From 0d588875a1f1899bb49d70513a0b77aa757fd4ca Mon Sep 17 00:00:00 2001 From: itismadness Date: Sat, 23 Nov 2019 16:39:24 +0000 Subject: [PATCH] dump to github --- .bin/phpcbf | 16 + .docker/mysql/mysqld_sql_mode.cnf | 3 + .docker/sphinxsearch/Dockerfile | 29 + .docker/sphinxsearch/crontab | 2 + .docker/sphinxsearch/entrypoint.sh | 28 + .docker/sphinxsearch/sphinx.conf | 453 +++ .docker/web/config.php | 283 ++ .docker/web/crontab | 2 + .docker/web/entrypoint.sh | 33 + .docker/web/gazelle-setup.sh | 131 + .docker/web/nginx.conf | 57 + .docker/web/php.ini | 1918 ++++++++++ .docker/web/www.conf | 411 ++ .docker/web/xdebug.ini | 6 + .dockerignore | 6 + .gitignore | 8 + .vagrant/config.php | 182 +- .vagrant/sphinx.conf | 411 +- Dockerfile | 71 + api.php | 98 + app/API/AbstractAPI.php | 19 + app/API/Artist.php | 25 + app/API/Collage.php | 28 + app/API/Forum.php | 33 + app/API/GenerateInvite.php | 68 + app/API/Request.php | 22 + app/API/Torrent.php | 81 + app/API/User.php | 176 + app/API/Wiki.php | 28 + app/Artist.php | 42 +- app/Bonus.php | 562 +-- app/BonusPool.php | 42 + app/Contest.php | 596 +++ app/DB.php | 123 +- app/Exception/InvalidAccessException.php | 8 +- app/Exception/RouterException.php | 8 +- app/Manager/Referral.php | 497 +++ app/Recovery.php | 614 +++ app/Report.php | 190 +- app/Router.php | 140 +- app/Top10/Donor.php | 22 + app/Top10/Tag.php | 81 + app/Top10/Torrent.php | 196 + app/Util/Arrays.php | 17 + app/Util/Crypto.php | 44 +- app/Util/Proxy.php | 39 + app/Util/Text.php | 71 +- app/Util/Time.php | 632 ++-- app/Util/Type.php | 98 +- boris | 71 + classes/NMA_API.php | 10 +- classes/ajax_start.php | 174 +- classes/applicant.class.php | 554 +-- classes/applicantrole.class.php | 328 +- classes/artist.class.php | 2 +- classes/artists.class.php | 504 +-- classes/artists_similar.class.php | 754 ++-- classes/autoenable.class.php | 707 ++-- classes/bencode.class.php | 154 +- classes/bencodedecode.class.php | 336 +- classes/bencodetorrent.class.php | 281 +- classes/bitcoinrpc.class.php | 52 +- classes/bookmarks.class.php | 182 +- classes/cache.class.php | 776 ++-- classes/calendar.class.php | 244 +- classes/calendarview.class.php | 200 +- classes/charts.class.php | 356 +- classes/classloader.php | 59 +- classes/collages.class.php | 170 +- classes/comments.class.php | 878 ++--- classes/commentsview.class.php | 176 +- classes/config.template | 242 +- classes/cookie.class.php | 60 +- classes/debug.class.php | 1282 +++---- classes/donations.class.php | 1536 ++++---- classes/donationsbitcoin.class.php | 356 +- classes/donationsview.class.php | 404 +- classes/feed.class.php | 134 +- classes/file_checker.class.php | 98 +- classes/fonts/README.TXT | 14 +- classes/format.class.php | 1132 +++--- classes/forums.class.php | 646 ++-- classes/g.class.php | 38 +- classes/google_authenticator.class.php | 480 +-- classes/image.class.php | 100 +- classes/imagetools.class.php | 474 +-- classes/inbox.class.php | 48 +- classes/invite_tree.class.php | 443 +-- classes/irc.class.php | 354 +- classes/irc.class.php.save | 352 +- classes/lastfm.class.php | 368 +- classes/lastfmview.class.php | 64 +- classes/lockedaccounts.class.php | 2 +- classes/mass_user_bookmarks_editor.class.php | 128 +- classes/mass_user_torrents_editor.class.php | 78 +- .../mass_user_torrents_table_view.class.php | 480 +-- classes/misc.class.php | 1026 ++--- classes/mysql.class.php | 1038 ++--- classes/notificationsmanager.class.php | 1586 ++++---- classes/notificationsmanagerview.class.php | 286 +- classes/paranoia.class.php | 110 +- classes/permissions.class.php | 214 +- classes/permissions_form.php | 542 +-- classes/proxies.class.php | 68 +- classes/pushserver.class.php | 346 +- classes/qr.class.php | 3086 +++++++-------- classes/regex.php | 2 +- classes/reports.class.php | 48 +- classes/requests.class.php | 492 +-- classes/revisionhistory.class.php | 54 +- classes/revisionhistoryview.class.php | 74 +- classes/rules.class.php | 470 +-- classes/script_start.php | 590 +-- classes/sitehistory.class.php | 502 +-- classes/sitehistoryview.class.php | 415 +- classes/siteoptions.class.php | 16 +- classes/sphinxql.class.php | 270 +- classes/sphinxqlquery.class.php | 726 ++-- classes/sphinxqlresult.class.php | 266 +- classes/subscriptions.class.php | 764 ++-- classes/tags.class.php | 452 +-- classes/text.class.php | 2252 +++++------ classes/textarea_preview.class.php | 362 +- classes/thread.class.php | 274 +- classes/time.class.php | 20 +- classes/tools.class.php | 584 +-- classes/top10.class.php | 2 +- classes/top10view.class.php | 170 +- classes/torrent.class.php | 546 +-- classes/torrent_32bit.class.php | 508 +-- classes/torrent_form.class.php | 1399 +++---- classes/torrents.class.php | 2403 ++++++------ classes/torrentsdl.class.php | 450 +-- classes/torrentsearch.class.php | 1534 ++++---- classes/tracker.class.php | 334 +- classes/userrank.class.php | 316 +- classes/users.class.php | 1805 ++++----- classes/util.php | 199 +- classes/validate.class.php | 725 ++-- classes/view.class.php | 242 +- classes/votes.class.php | 626 +-- classes/wiki.class.php | 186 +- classes/zip.class.php | 220 +- composer.json | 8 +- composer.lock | 249 +- db/data/data.sql | 2 +- db/migrations/20180104060449_tables.php | 2380 ++++++------ db/migrations/20180111234647_applicant.php | 116 +- .../20180214180452_platform_versions.php | 12 +- .../20180303180452_bonus_points_migration.php | 86 +- .../20180408161224_remove_library_contest.php | 6 +- db/migrations/20180410205600_vanity_house.php | 2 +- db/migrations/20180426104109_irc_channels.php | 17 + db/migrations/20180618045849_pm_on_delete.php | 16 +- db/migrations/20180714171241_referral.php | 41 + .../20180729085427_referral_tracking.php | 42 + db/migrations/20181219213631_release_type.php | 8 +- .../20181219220422_request_checksum.php | 16 +- .../20181229185143_delete_torrent.php | 14 +- .../20190212082403_bonus_point_pool.php | 59 + .../20190422111146_torrent_stats_tables.php | 71 + .../20190719073623_add_contest_payout.php | 40 + db/migrations/20190824010935_user_nav.php | 140 +- .../20190921082622_forum_edit_permissions.php | 53 + db/migrations/20190930092524_new_user_nav.php | 66 + db/recover.sql | 24 + db/seeds/InitialUserSeeder.php | 160 +- db/seeds/TorrentSeeder.php | 386 +- design/privatefooter.php | 87 +- design/privateheader.php | 1068 +++--- design/publicfooter.php | 6 +- design/publicheader.php | 53 +- design/views/generic/reply/quickreply.php | 254 +- design/views/generic/reply/staffpm.php | 49 +- design/views/generic/textarea/buttons.phtml | 2 +- design/views/generic/textarea/preview.phtml | 2 +- .../generic/textarea/script_factory.phtml | 2 +- design/views/generic/textarea/textarea.phtml | 2 +- docker-compose.yml | 35 + docs/CodingStandards.txt | 163 +- docs/INSTALL.txt | 18 +- feeds.php | 112 +- image.php | 140 +- index.php | 139 +- opensearch.php | 72 +- package-lock.json | 2122 +++++++++++ package.json | 33 + phinx.php | 45 +- phpcs.xml | 28 + phpunit.xml | 2 +- sections/ajax/announcements.php | 128 +- sections/ajax/artist.php | 550 +-- sections/ajax/better/index.php | 62 +- sections/ajax/better/single.php | 84 +- sections/ajax/better/transcode.php | 192 +- sections/ajax/bookmarks/artists.php | 90 +- sections/ajax/bookmarks/index.php | 46 +- sections/ajax/bookmarks/torrents.php | 134 +- sections/ajax/browse.php | 390 +- sections/ajax/clear_user_notification.php | 62 +- sections/ajax/collage.php | 286 +- sections/ajax/community_stats.php | 94 +- sections/ajax/forum/forum.php | 260 +- sections/ajax/forum/index.php | 44 +- sections/ajax/forum/main.php | 170 +- sections/ajax/forum/thread.php | 440 +-- sections/ajax/get_friends.php | 22 +- sections/ajax/get_user_notifications.php | 6 +- sections/ajax/inbox/inbox.php | 144 +- sections/ajax/inbox/index.php | 10 +- sections/ajax/inbox/viewconv.php | 128 +- sections/ajax/index.php | 316 +- sections/ajax/info.php | 156 +- sections/ajax/loadavg.php | 18 +- sections/ajax/loggy.php | 15 + sections/ajax/news_ajax.php | 42 +- sections/ajax/notifications.php | 150 +- sections/ajax/password_validate.php | 8 +- sections/ajax/preview.php | 10 +- sections/ajax/pushbullet_devices.php | 12 +- sections/ajax/raw_bbcode.php | 16 +- sections/ajax/request.php | 186 +- sections/ajax/requests.php | 596 +-- sections/ajax/send_recommendation.php | 70 +- sections/ajax/similar_artists.php | 54 +- sections/ajax/stats.php | 185 +- sections/ajax/stats/torrents.php | 72 +- sections/ajax/stats/users.php | 148 +- sections/ajax/subscriptions.php | 120 +- sections/ajax/takevote.php | 394 +- sections/ajax/tcomments.php | 54 +- sections/ajax/top10/index.php | 36 +- sections/ajax/top10/tags.php | 160 +- sections/ajax/top10/torrents.php | 294 +- sections/ajax/top10/users.php | 193 +- sections/ajax/torrent.php | 146 +- sections/ajax/torrentgroup.php | 160 +- sections/ajax/torrentgroupalbumart.php | 6 +- sections/ajax/upload.php | 50 +- sections/ajax/user.php | 488 +-- sections/ajax/user_recents.php | 106 +- sections/ajax/userhistory/index.php | 28 +- sections/ajax/userhistory/post_history.php | 300 +- sections/ajax/usersearch.php | 76 +- sections/ajax/wiki.php | 30 +- sections/api/index.php | 55 + sections/apply/admin.php | 223 +- sections/apply/apply.php | 189 +- sections/apply/index.php | 42 +- sections/apply/view.php | 329 +- sections/artist/add_alias.php | 110 +- sections/artist/add_similar.php | 90 +- sections/artist/artist.php | 1602 ++++---- sections/artist/autocomplete.php | 58 +- sections/artist/change_artistid.php | 328 +- sections/artist/concerts.php | 170 +- sections/artist/delete.php | 102 +- sections/artist/delete_alias.php | 48 +- sections/artist/delete_similar.php | 28 +- sections/artist/download.php | 309 +- sections/artist/edit.php | 262 +- sections/artist/editrequest.php | 88 +- sections/artist/history.php | 22 +- sections/artist/index.php | 222 +- sections/artist/notify.php | 72 +- sections/artist/notifyremove.php | 56 +- sections/artist/rename.php | 356 +- sections/artist/takeedit.php | 64 +- sections/artist/takeeditrequest.php | 172 +- sections/artist/vote_similar.php | 44 +- sections/better/index.php | 65 +- sections/better/missing.php | 363 ++ sections/better/single.php | 123 +- sections/better/transcode.php | 505 ++- sections/blog/blog_page.php | 218 +- sections/blog/dead_thread.php | 8 +- sections/blog/delete_blog.php | 8 +- sections/blog/index.php | 44 +- sections/blog/take_edit_blog.php | 66 +- sections/blog/take_new_blog.php | 46 +- sections/bonus/bprates.php | 310 +- sections/bonus/history.php | 128 +- sections/bonus/index.php | 101 +- sections/bonus/invite.php | 4 +- sections/bonus/store.php | 149 +- sections/bonus/title.php | 80 +- sections/bonus/tokens.php | 26 +- sections/bookmarks/add.php | 174 +- sections/bookmarks/artists.php | 151 +- sections/bookmarks/edit_torrents.php | 26 +- sections/bookmarks/index.php | 138 +- sections/bookmarks/mass_edit.php | 16 +- sections/bookmarks/remove.php | 46 +- sections/bookmarks/torrents.php | 686 ++-- sections/captcha/index.php | 68 +- sections/chat/index.php | 116 +- sections/collages/add_artist.php | 276 +- sections/collages/add_torrent.php | 278 +- sections/collages/all_comments.php | 68 +- sections/collages/artist_collage.php | 482 +-- sections/collages/browse.php | 505 +-- sections/collages/collage.php | 106 +- sections/collages/delete.php | 62 +- sections/collages/download.php | 219 +- sections/collages/edit.php | 163 +- sections/collages/edit_handle.php | 96 +- sections/collages/index.php | 194 +- sections/collages/manage.php | 231 +- sections/collages/manage_artists.php | 179 +- sections/collages/manage_artists_handle.php | 92 +- sections/collages/manage_handle.php | 92 +- sections/collages/new.php | 143 +- sections/collages/new_handle.php | 112 +- sections/collages/recover.php | 76 +- sections/collages/take_delete.php | 52 +- sections/collages/torrent_collage.php | 1085 +++--- sections/comments/comments.php | 403 +- sections/comments/get.php | 12 +- sections/comments/index.php | 66 +- sections/comments/jump.php | 6 +- sections/comments/take_delete.php | 6 +- sections/comments/take_edit.php | 6 +- sections/comments/take_post.php | 10 +- sections/comments/take_warn.php | 44 +- sections/comments/warn.php | 100 +- sections/contest/admin.php | 385 +- sections/contest/index.php | 21 +- sections/contest/intro.php | 20 +- sections/contest/leaderboard.php | 293 +- sections/donate/index.php | 92 +- sections/enable/index.php | 2 +- sections/error/403.php | 4 +- sections/error/404.php | 4 +- sections/error/413.php | 2 +- sections/error/504.php | 2 +- sections/error/index.php | 104 +- sections/feeds/index.php | 348 +- sections/forums/add_poll_option.php | 34 +- sections/forums/ajax_get_edit.php | 94 +- sections/forums/catchup.php | 36 +- sections/forums/change_vote.php | 38 +- sections/forums/delete.php | 200 +- sections/forums/delete_poll_option.php | 64 +- sections/forums/edit_rules.php | 118 +- sections/forums/forum.php | 416 +- sections/forums/get_post.php | 18 +- sections/forums/index.php | 4 +- sections/forums/main.php | 150 +- sections/forums/mod_thread.php | 720 ++-- sections/forums/newthread.php | 285 +- sections/forums/poll_mod.php | 80 +- sections/forums/poll_vote.php | 260 +- sections/forums/search.php | 552 +-- sections/forums/sticky_post.php | 58 +- sections/forums/take_new_thread.php | 254 +- sections/forums/take_reply.php | 414 +- sections/forums/take_topic_notes.php | 6 +- sections/forums/take_warn.php | 122 +- sections/forums/takeedit.php | 118 +- sections/forums/thread.php | 1128 +++--- sections/forums/warn.php | 106 +- sections/friends/add.php | 18 +- sections/friends/comment.php | 10 +- sections/friends/friends.php | 167 +- sections/friends/index.php | 42 +- sections/friends/remove.php | 8 +- sections/image/index.php | 234 +- sections/inbox/compose.php | 68 +- sections/inbox/conversation.php | 264 +- sections/inbox/forward.php | 64 +- sections/inbox/get_post.php | 14 +- sections/inbox/inbox.php | 274 +- sections/inbox/index.php | 70 +- sections/inbox/massdelete_handle.php | 74 +- sections/inbox/takecompose.php | 70 +- sections/inbox/takeedit.php | 62 +- sections/index/contest_leaderboard.php | 40 +- sections/index/feat_merch.php | 66 +- sections/index/index.php | 26 +- sections/index/month_album.php | 54 +- sections/index/private.php | 869 +++-- sections/index/public.php | 1 + sections/index/vanity_album.php | 48 +- sections/locked/default.php | 8 +- sections/locked/index.php | 2 +- sections/log/index.php | 396 +- sections/log/sphinx.php | 96 +- sections/log/sql.php | 38 +- sections/logchecker/index.php | 48 +- sections/logchecker/take_test.php | 72 +- sections/logchecker/take_upload.php | 132 +- sections/logchecker/test.php | 86 +- sections/logchecker/update.php | 174 +- sections/logchecker/upload.php | 174 +- sections/login/2fa.php | 82 +- sections/login/2fa_recovery.php | 94 +- sections/login/disabled.php | 116 +- sections/login/index.php | 1132 +++--- sections/login/login.php | 123 +- sections/login/recover_step1.php | 45 +- sections/login/recover_step2.php | 57 +- sections/logout/index.php | 2 +- sections/peerupdate/index.php | 141 +- sections/questions/ajax_get_answers.php | 38 +- sections/questions/answer_question.php | 72 +- sections/questions/answers.php | 59 +- sections/questions/ask_question.php | 36 +- sections/questions/edit.php | 48 +- sections/questions/index.php | 102 +- sections/questions/popular_questions.php | 111 +- sections/questions/questions.php | 159 +- sections/questions/take_answer_question.php | 38 +- sections/questions/take_ask_question.php | 14 +- sections/questions/take_edit_answer.php | 12 +- sections/questions/take_ignore_question.php | 14 +- sections/questions/take_remove_answer.php | 12 +- sections/questions/take_remove_question.php | 10 +- sections/questions/view_answers.php | 139 +- sections/random/index.php | 34 +- sections/randomcollage/index.php | 2 +- sections/recovery/admin.php | 135 + sections/recovery/browse.php | 105 + sections/recovery/closed.php | 2 + sections/recovery/form.php | 88 + sections/recovery/index.php | 33 + sections/recovery/pair.php | 158 + sections/recovery/recover.php | 45 + sections/recovery/save.php | 104 + sections/recovery/view.php | 172 + sections/referral/index.php | 161 +- sections/register/closed.php | 6 +- sections/register/code.php | 28 +- sections/register/index.php | 532 +-- sections/register/step1.php | 151 +- sections/register/step2.php | 8 +- sections/reports/ajax_add_notes.php | 30 +- sections/reports/ajax_claim_report.php | 60 +- sections/reports/ajax_resolve_report.php | 56 +- sections/reports/ajax_unclaim_report.php | 24 +- sections/reports/array.php | 154 +- sections/reports/compose.php | 276 +- sections/reports/index.php | 70 +- sections/reports/report.php | 528 +-- sections/reports/reports.php | 420 +-- sections/reports/stats.php | 334 +- sections/reports/takecompose.php | 72 +- sections/reports/takereport.php | 110 +- sections/reports/takeresolve.php | 48 +- sections/reportsv2/ajax_change_resolve.php | 30 +- sections/reportsv2/ajax_create_report.php | 76 +- sections/reportsv2/ajax_giveback_report.php | 20 +- sections/reportsv2/ajax_grab_report.php | 22 +- sections/reportsv2/ajax_new_report.php | 800 ++-- sections/reportsv2/ajax_report.php | 124 +- sections/reportsv2/ajax_take_pm.php | 56 +- sections/reportsv2/ajax_update_comment.php | 18 +- sections/reportsv2/ajax_update_resolve.php | 32 +- sections/reportsv2/array.php | 1738 ++++----- sections/reportsv2/header.php | 14 +- sections/reportsv2/index.php | 106 +- sections/reportsv2/report.php | 258 +- sections/reportsv2/reports.php | 18 +- sections/reportsv2/search.php | 283 +- sections/reportsv2/static.php | 1191 +++--- sections/reportsv2/takereport.php | 134 +- sections/reportsv2/takeresolve.php | 588 +-- sections/reportsv2/views.php | 511 +-- sections/requests/index.php | 70 +- sections/requests/interim.php | 66 +- sections/requests/new_edit.php | 760 ++-- sections/requests/request.php | 752 ++-- sections/requests/requests.php | 1118 +++--- sections/requests/take_delete.php | 52 +- sections/requests/take_fill.php | 196 +- sections/requests/take_new_edit.php | 624 +-- sections/requests/take_unfill.php | 78 +- sections/requests/take_vote.php | 68 +- sections/rules/chat.php | 30 +- sections/rules/clients.php | 72 +- sections/rules/collages.php | 100 +- sections/rules/index.php | 54 +- sections/rules/jump.php | 210 +- sections/rules/ratio.php | 396 +- sections/rules/requests.php | 50 +- sections/rules/rules.php | 22 +- sections/rules/tag.php | 20 +- sections/rules/upload.php | 1412 +++---- .../schedule/biweekly/cycle_auth_keys.php | 26 +- .../schedule/daily/delete_dead_torrents.php | 156 +- sections/schedule/daily/demote_users.php | 84 +- .../daily/disable_downloading_ratio_watch.php | 46 +- .../schedule/daily/disable_inactive_users.php | 54 +- .../daily/disable_unconfirmed_users.php | 34 +- sections/schedule/daily/lock_old_threads.php | 58 +- .../schedule/daily/ratio_requirements.php | 143 +- sections/schedule/daily/ratio_watch.php | 138 +- .../schedule/daily/update_daily_top10.php | 183 +- .../schedule/daily/user_stats_monthly.php | 19 +- sections/schedule/disabled/freeleech.php | 28 +- sections/schedule/disabled/rescore_eac_95.php | 36 +- .../disabled/reward_perfect_flac_uploads.php | 26 +- sections/schedule/disabled/update_geoip.php | 6 +- .../every/calculate_contest_leaderboard.php | 11 +- sections/schedule/every/delete_tags.php | 6 +- sections/schedule/every/expire_fl_tokens.php | 44 +- sections/schedule/every/recovery.php | 7 + sections/schedule/hourly/community_stats.php | 30 +- .../hourly/disable_leeching_ratio_watch.php | 40 +- sections/schedule/hourly/expire_invites.php | 10 +- sections/schedule/hourly/front_page_stats.php | 46 +- .../schedule/hourly/hide_old_requests.php | 8 +- .../schedule/hourly/lower_login_attempts.php | 10 +- sections/schedule/hourly/promote_users.php | 234 +- .../schedule/hourly/remove_dead_peers.php | 4 +- .../schedule/hourly/remove_dead_sessions.php | 22 +- .../hourly/remove_expired_warnings.php | 18 +- .../hourly/update_user_bonus_points.php | 44 +- .../hourly/update_user_torrent_history.php | 42 +- sections/schedule/hourly/user_stats_daily.php | 19 +- sections/schedule/index.php | 162 +- sections/schedule/weekly/donations.php | 2 +- .../schedule/weekly/resolve_staff_pms.php | 10 +- .../schedule/weekly/update_weekly_top10.php | 183 +- .../schedule/weekly/user_stats_yearly.php | 19 +- sections/schedule/weekly/warn_uploaders.php | 69 +- sections/sitehistory/edit.php | 29 +- sections/sitehistory/history.php | 44 +- sections/sitehistory/index.php | 50 +- sections/sitehistory/take_create.php | 6 +- sections/sitehistory/take_edit.php | 8 +- sections/staff/functions.php | 186 +- sections/staff/index.php | 156 +- sections/staffblog/index.php | 252 +- sections/staffpm/ajax_delete_response.php | 32 +- sections/staffpm/ajax_edit_response.php | 88 +- sections/staffpm/ajax_get_response.php | 44 +- sections/staffpm/ajax_preview_response.php | 4 +- sections/staffpm/assign.php | 162 +- sections/staffpm/common_responses.php | 162 +- sections/staffpm/get_post.php | 18 +- sections/staffpm/index.php | 122 +- sections/staffpm/makedonor.php | 50 +- sections/staffpm/multiresolve.php | 64 +- sections/staffpm/resolve.php | 44 +- sections/staffpm/scoreboard.php | 246 +- sections/staffpm/staff_inbox.php | 342 +- sections/staffpm/takepost.php | 140 +- sections/staffpm/unresolve.php | 58 +- sections/staffpm/user_inbox.php | 182 +- sections/staffpm/viewconv.php | 479 +-- sections/stats/index.php | 26 +- sections/stats/list.php | 4 +- sections/stats/torrents.php | 136 +- sections/stats/users.php | 276 +- sections/tools/data/database_specifics.php | 196 +- sections/tools/data/economic_stats.php | 242 +- sections/tools/data/invite_pool.php | 175 +- sections/tools/data/ocelot_info.php | 136 +- sections/tools/data/platform_usage.php | 46 +- sections/tools/data/registration_log.php | 235 +- sections/tools/data/special_users.php | 49 +- sections/tools/data/torrent_stats.php | 126 +- sections/tools/data/upscale_pool.php | 148 +- sections/tools/data/user_flow.php | 275 +- sections/tools/development/clear_cache.php | 197 +- sections/tools/development/process_info.php | 57 +- sections/tools/development/render_base.html | 646 ++-- .../tools/development/render_build_preview.js | 18 +- .../tools/development/rerender_gallery.php | 120 +- sections/tools/development/service_stats.php | 537 +-- sections/tools/development/site_info.php | 120 +- sections/tools/development/site_options.php | 60 +- sections/tools/development/update_geoip.php | 40 +- sections/tools/development/update_offsets.php | 90 +- sections/tools/finances/bitcoin_balance.php | 74 +- sections/tools/finances/bitcoin_unproc.php | 104 +- sections/tools/finances/btc_log.php | 248 +- sections/tools/finances/donation_log.php | 248 +- sections/tools/finances/donor_rewards.php | 150 +- sections/tools/index.php | 1069 +++--- .../managers/ajax_get_calendar_event.php | 239 +- .../managers/ajax_take_calendar_event.php | 12 +- .../managers/ajax_take_enable_request.php | 2 +- sections/tools/managers/bans.php | 240 +- sections/tools/managers/bonus_points.php | 60 +- sections/tools/managers/calendar.php | 20 +- sections/tools/managers/categories_alter.php | 70 +- sections/tools/managers/categories_list.php | 124 +- sections/tools/managers/change_log.php | 180 +- sections/tools/managers/db_key.php | 36 + sections/tools/managers/dnu_alter.php | 86 +- sections/tools/managers/dnu_list.php | 138 +- sections/tools/managers/email_blacklist.php | 143 +- .../tools/managers/email_blacklist_alter.php | 66 +- .../tools/managers/email_blacklist_search.php | 48 +- sections/tools/managers/enable_requests.php | 48 +- sections/tools/managers/featured_merch.php | 120 +- sections/tools/managers/forum_alter.php | 118 +- sections/tools/managers/forum_list.php | 324 +- .../managers/forum_transitions_alter.php | 55 + .../tools/managers/forum_transitions_list.php | 116 + .../tools/managers/global_notification.php | 94 +- sections/tools/managers/irc_alter.php | 46 + sections/tools/managers/irc_list.php | 89 + sections/tools/managers/label_aliases.php | 154 +- sections/tools/managers/login_watch.php | 146 +- sections/tools/managers/mass_pm.php | 68 +- .../tools/managers/multiple_freeleech.php | 14 +- sections/tools/managers/navigation_alter.php | 88 +- sections/tools/managers/navigation_list.php | 156 +- sections/tools/managers/news.php | 154 +- sections/tools/managers/ocelot.php | 82 +- sections/tools/managers/official_tags.php | 204 +- sections/tools/managers/payment_alter.php | 64 +- sections/tools/managers/payment_list.php | 108 +- sections/tools/managers/permissions_alter.php | 124 +- sections/tools/managers/permissions_list.php | 98 +- sections/tools/managers/recommend_add.php | 12 +- sections/tools/managers/recommend_alter.php | 24 +- sections/tools/managers/recommend_list.php | 105 +- sections/tools/managers/recommend_restore.php | 8 +- sections/tools/managers/referral_accounts.php | 128 + sections/tools/managers/referral_alter.php | 50 + sections/tools/managers/referral_users.php | 172 + .../tools/managers/staff_groups_alter.php | 50 +- sections/tools/managers/staff_groups_list.php | 110 +- sections/tools/managers/stylesheets_list.php | 94 +- sections/tools/managers/tag_aliases.php | 156 +- .../managers/take_global_notification.php | 10 +- sections/tools/managers/take_mass_pm.php | 10 +- sections/tools/managers/tokens.php | 154 +- sections/tools/managers/whitelist_alter.php | 96 +- sections/tools/managers/whitelist_list.php | 100 +- sections/tools/misc/album_of_month.php | 272 +- sections/tools/misc/analysis.php | 10 +- sections/tools/misc/create_user.php | 178 +- sections/tools/misc/dupe_ip.php | 124 +- sections/tools/misc/manipulate_tree.php | 207 +- sections/tools/misc/quick_ban.php | 32 +- sections/tools/misc/tags.php | 480 +-- sections/tools/misc/vanity_house.php | 268 +- .../sandboxes/artist_importance_sandbox.php | 50 +- sections/tools/sandboxes/bbcode_sandbox.php | 30 +- sections/tools/sandboxes/db_sandbox.php | 71 + sections/tools/sandboxes/referral_sandbox.php | 181 + sections/tools/services/get_cc.php | 10 +- sections/tools/services/get_host.php | 62 +- sections/tools/tools.php | 422 ++- sections/top10/donors.php | 145 +- sections/top10/history.php | 330 +- sections/top10/index.php | 62 +- sections/top10/lastfm.php | 72 +- sections/top10/tags.php | 224 +- sections/top10/torrents.php | 778 ++-- sections/top10/users.php | 265 +- sections/top10/votes.php | 646 ++-- sections/torrents/add_alias.php | 106 +- sections/torrents/add_cover_art.php | 46 +- sections/torrents/add_tag.php | 96 +- sections/torrents/autocomplete_tags.php | 44 +- sections/torrents/browse.php | 1230 +++--- sections/torrents/collector.php | 61 + sections/torrents/delete.php | 506 +-- sections/torrents/delete_alias.php | 62 +- sections/torrents/delete_log.php | 4 +- sections/torrents/delete_tag.php | 56 +- sections/torrents/details.php | 1387 +++---- sections/torrents/download.php | 306 +- sections/torrents/downloadlist.php | 116 +- sections/torrents/edit.php | 395 +- sections/torrents/edit_log.php | 240 +- sections/torrents/editgroup.php | 302 +- sections/torrents/editgroupid.php | 174 +- sections/torrents/editrequest.php | 90 +- sections/torrents/functions.php | 1050 +++--- sections/torrents/grouplog.php | 100 +- sections/torrents/history.php | 22 +- sections/torrents/index.php | 615 +-- sections/torrents/log_ajax.php | 110 +- sections/torrents/manage_artists.php | 142 +- sections/torrents/masspm.php | 110 +- sections/torrents/merge.php | 298 +- sections/torrents/nonwikiedit.php | 76 +- sections/torrents/notify.php | 539 +-- sections/torrents/notify_actions.php | 110 +- sections/torrents/peerlist.php | 98 +- sections/torrents/ranking_funcs.php | 156 +- sections/torrents/redownload.php | 140 +- sections/torrents/remove_cover_art.php | 22 +- sections/torrents/remove_logs.php | 2 +- sections/torrents/rename.php | 20 +- sections/torrents/rescore_log.php | 6 +- sections/torrents/reseed.php | 81 +- sections/torrents/snatchlist.php | 72 +- sections/torrents/take_edit_log.php | 72 +- sections/torrents/takechangecategory.php | 168 +- sections/torrents/takedelete.php | 77 +- sections/torrents/takeedit.php | 588 +-- sections/torrents/takeeditrequest.php | 172 +- sections/torrents/takegroupedit.php | 296 +- sections/torrents/takemasspm.php | 34 +- sections/torrents/takenewgroup.php | 150 +- sections/torrents/user.php | 886 ++--- sections/torrents/vote.php | 64 +- sections/torrents/vote_ranks.php | 62 +- sections/torrents/vote_tag.php | 52 +- sections/torrents/voter_picks.php | 99 +- sections/upload/index.php | 29 +- sections/upload/parse_html.php | 8 +- sections/upload/parse_json.php | 59 + sections/upload/upload.php | 243 +- sections/upload/upload_handle.php | 1644 ++++---- sections/user/2fa/complete.php | 22 +- sections/user/2fa/password_confirm.php | 64 +- sections/user/2fa/step1.php | 58 +- sections/user/2fa/step2.php | 66 +- sections/user/advancedsearch.php | 1696 ++++----- sections/user/community_stats.php | 418 +- sections/user/connchecker.php | 14 +- sections/user/delete_invite.php | 44 +- sections/user/donor_stats.php | 32 +- sections/user/edit.php | 1647 ++++---- sections/user/index.php | 438 +-- sections/user/invite.php | 379 +- sections/user/invitetree.php | 41 +- sections/user/lastfm.php | 2 +- sections/user/linkedfunctions.php | 449 +-- sections/user/manage_linked.php | 58 +- sections/user/notify_edit.php | 398 +- sections/user/notify_handle.php | 222 +- sections/user/permissions.php | 124 +- sections/user/search.php | 154 +- sections/user/sessions.php | 145 +- sections/user/take_edit.php | 441 +-- sections/user/take_invite.php | 125 +- sections/user/take_push.php | 4 +- sections/user/takemoderate.php | 1201 +++--- sections/user/user.php | 2600 ++++++------- sections/user/user_stats.php | 170 +- sections/userhistory/catchup.php | 52 +- sections/userhistory/catchup_collages.php | 6 +- sections/userhistory/collage_subscribe.php | 42 +- sections/userhistory/comments_subscribe.php | 6 +- sections/userhistory/copypaste.php | 106 +- sections/userhistory/email_history.php | 212 +- sections/userhistory/email_history2.php | 562 +-- sections/userhistory/index.php | 192 +- sections/userhistory/ip_history.php | 493 +-- sections/userhistory/ip_tracker_history.php | 78 +- sections/userhistory/passkey_history.php | 66 +- sections/userhistory/password_history.php | 54 +- sections/userhistory/post_history.php | 470 +-- sections/userhistory/quote_notifications.php | 264 +- sections/userhistory/subscribed_collages.php | 460 +-- sections/userhistory/subscriptions.php | 376 +- sections/userhistory/thread_subscribe.php | 16 +- sections/userhistory/token_history.php | 172 +- sections/userhistory/topic_history.php | 102 +- sections/wiki/add_alias.php | 10 +- sections/wiki/article.php | 218 +- sections/wiki/compare.php | 154 +- sections/wiki/create.php | 60 +- sections/wiki/delete.php | 16 +- sections/wiki/delete_alias.php | 4 +- sections/wiki/edit.php | 58 +- sections/wiki/index.php | 122 +- sections/wiki/revisions.php | 106 +- sections/wiki/search.php | 184 +- sections/wiki/splash.php | 86 +- sections/wiki/takecreate.php | 70 +- sections/wiki/takeedit.php | 70 +- sections/wiki/wiki_browse.php | 152 +- static/functions/ajax.class.js | 260 +- static/functions/artist_cloud.js | 138 +- static/functions/autocomplete.js | 58 +- static/functions/bbcode.js | 18 +- static/functions/bbcode_sandbox.js | 26 +- static/functions/bonus.js | 48 +- static/functions/browse.js | 524 +-- static/functions/calendar.js | 52 +- static/functions/captcha.js | 4 +- static/functions/collage.js | 268 +- static/functions/comments.js | 632 ++-- static/functions/cookie.class.js | 32 +- static/functions/countdown.class.js | 66 +- static/functions/cover_art.js | 110 +- static/functions/cssgallery.js | 90 +- static/functions/datetime_picker.js | 38 +- static/functions/dnu_list.js | 48 +- static/functions/donor_titles.js | 54 +- static/functions/edit_log.js | 88 +- static/functions/form_validate.js | 88 +- static/functions/forum_search.js | 26 +- static/functions/global.js | 226 +- static/functions/highcharts_custom.js | 130 +- static/functions/inbox.js | 18 +- static/functions/info_paster.js | 16 +- static/functions/iphone.js | 288 +- static/functions/jquery-ui.js | 2 +- .../jquery.countdown.js | 2 +- static/functions/jquery.countdown.min.js | 2 +- static/functions/jquery.datetimepicker.js | 1890 +++++----- static/functions/jquery.js | 2 +- static/functions/jquery.tablesorter.js | 3346 ++++++++--------- static/functions/jquery.validate.js | 2366 ++++++------ static/functions/lastfm.js | 736 ++-- static/functions/mosaic.js | 12 +- static/functions/multiformat_uploader.js | 170 +- static/functions/musicbrainz.js | 614 +-- static/functions/news_ajax.js | 106 +- static/functions/notifications.js | 70 +- static/functions/noty/layouts/bottom.js | 62 +- static/functions/noty/layouts/bottomCenter.js | 70 +- static/functions/noty/layouts/bottomLeft.js | 76 +- static/functions/noty/layouts/bottomRight.js | 76 +- static/functions/noty/layouts/center.js | 98 +- static/functions/noty/layouts/centerLeft.js | 104 +- static/functions/noty/layouts/centerRight.js | 104 +- static/functions/noty/layouts/inline.js | 56 +- static/functions/noty/layouts/top.js | 62 +- static/functions/noty/layouts/topCenter.js | 70 +- static/functions/noty/layouts/topLeft.js | 76 +- static/functions/noty/layouts/topRight.js | 76 +- static/functions/noty/noty.js | 24 +- static/functions/noty/themes/default.js | 290 +- static/functions/password_validate.js | 270 +- static/functions/preview_paranoia.js | 84 +- static/functions/questions.js | 118 +- static/functions/recommend.js | 138 +- static/functions/release_sort.js | 64 +- static/functions/reports.js | 126 +- static/functions/reportsv2.js | 336 +- static/functions/requests.js | 389 +- static/functions/rules.js | 62 +- static/functions/script_start.js | 632 ++-- static/functions/site_history.js | 32 +- static/functions/sort.js | 242 +- static/functions/staffpm.js | 184 +- static/functions/storage.class.js | 102 +- static/functions/subscriptions.js | 66 +- static/functions/testing.js | 52 +- static/functions/textareapreview.class.js | 158 +- static/functions/tiles.js | 38 +- static/functions/tooltipster.js | 2 +- static/functions/tooltipster_settings.js | 62 +- static/functions/top10.js | 56 +- static/functions/torrent.js | 570 +-- static/functions/transitions.js | 60 +- static/functions/upload.js | 261 +- static/functions/user.js | 386 +- static/functions/user_notifications.js | 186 +- static/functions/user_settings.js | 246 +- static/functions/valid_tags.js | 6 +- static/functions/validate.js | 190 +- static/functions/validate_upload.js | 50 +- static/functions/voting.js | 60 +- static/functions/wiki.js | 6 +- static/irc/IRCApplet.class | Bin 3585 -> 3610 bytes static/styles/80char/desaturate.svg | 2 +- static/styles/80char/style.css | 1042 ++--- static/styles/calendar/style.css | 64 +- static/styles/dark_ambient/style.css | 1624 ++++---- static/styles/datetime_picker/style.css | 330 +- static/styles/global.css | 638 ++-- static/styles/kuro/style.css | 772 ++-- static/styles/linohaze/style.css | 2156 +++++------ static/styles/linohaze/style.new.css | 2146 +++++------ static/styles/linohaze/style.old.css | 2200 +++++------ static/styles/log.css | 30 +- static/styles/minimal_mod_alt.css | 1242 +++--- static/styles/musicbrainz.css | 74 +- static/styles/opendyslexic/style.css | 4 +- static/styles/post_office/style.css | 2 +- static/styles/postmod/style.css | 908 ++--- static/styles/proton/style.css | 1598 ++++---- static/styles/public/style.css | 146 +- static/styles/tiles/style.css | 52 +- static/styles/tooltipster/custom.css | 38 +- static/styles/tooltipster/reset.css | 26 +- static/styles/tooltipster/style.css | 360 +- .../tooltipster/themes/tooltipster-light.css | 18 +- .../tooltipster/themes/tooltipster-noir.css | 18 +- .../tooltipster/themes/tooltipster-punk.css | 18 +- .../tooltipster/themes/tooltipster-shadow.css | 18 +- static/userscripts/gazelle-json-export.js | 37 +- templates/emails/enable_request_accepted.twig | 6 + templates/emails/enable_request_denied.twig | 8 + templates/emails/invite.twig | 14 + templates/emails/new_registration.twig | 8 + templates/emails/password_reset.twig | 11 + templates/emails/recovery.twig | 20 + templates/emails/referral.twig | 16 + templates/error.twig | 48 + tests/RouterTest.php | 192 +- tests/Util/ArraysTest.php | 19 + tests/Util/TextTest.php | 110 +- tests/Util/TimeTest.php | 106 +- tests/Util/TypeTest.php | 114 +- 898 files changed, 105857 insertions(+), 93267 deletions(-) create mode 100644 .bin/phpcbf create mode 100644 .docker/mysql/mysqld_sql_mode.cnf create mode 100644 .docker/sphinxsearch/Dockerfile create mode 100644 .docker/sphinxsearch/crontab create mode 100644 .docker/sphinxsearch/entrypoint.sh create mode 100644 .docker/sphinxsearch/sphinx.conf create mode 100644 .docker/web/config.php create mode 100644 .docker/web/crontab create mode 100644 .docker/web/entrypoint.sh create mode 100755 .docker/web/gazelle-setup.sh create mode 100644 .docker/web/nginx.conf create mode 100644 .docker/web/php.ini create mode 100644 .docker/web/www.conf create mode 100644 .docker/web/xdebug.ini create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 api.php create mode 100644 app/API/AbstractAPI.php create mode 100644 app/API/Artist.php create mode 100644 app/API/Collage.php create mode 100644 app/API/Forum.php create mode 100644 app/API/GenerateInvite.php create mode 100644 app/API/Request.php create mode 100644 app/API/Torrent.php create mode 100644 app/API/User.php create mode 100644 app/API/Wiki.php create mode 100644 app/BonusPool.php create mode 100644 app/Contest.php create mode 100644 app/Manager/Referral.php create mode 100644 app/Recovery.php create mode 100644 app/Top10/Donor.php create mode 100644 app/Top10/Tag.php create mode 100644 app/Top10/Torrent.php create mode 100644 app/Util/Arrays.php create mode 100644 app/Util/Proxy.php create mode 100755 boris create mode 100644 db/migrations/20180426104109_irc_channels.php create mode 100644 db/migrations/20180714171241_referral.php create mode 100644 db/migrations/20180729085427_referral_tracking.php create mode 100644 db/migrations/20190212082403_bonus_point_pool.php create mode 100644 db/migrations/20190422111146_torrent_stats_tables.php create mode 100644 db/migrations/20190719073623_add_contest_payout.php create mode 100644 db/migrations/20190921082622_forum_edit_permissions.php create mode 100644 db/migrations/20190930092524_new_user_nav.php create mode 100644 db/recover.sql create mode 100644 docker-compose.yml create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 phpcs.xml create mode 100644 sections/ajax/loggy.php create mode 100644 sections/api/index.php create mode 100644 sections/better/missing.php create mode 100644 sections/recovery/admin.php create mode 100644 sections/recovery/browse.php create mode 100644 sections/recovery/closed.php create mode 100644 sections/recovery/form.php create mode 100644 sections/recovery/index.php create mode 100644 sections/recovery/pair.php create mode 100644 sections/recovery/recover.php create mode 100644 sections/recovery/save.php create mode 100644 sections/recovery/view.php create mode 100644 sections/schedule/every/recovery.php create mode 100644 sections/tools/managers/db_key.php create mode 100644 sections/tools/managers/forum_transitions_alter.php create mode 100644 sections/tools/managers/forum_transitions_list.php create mode 100644 sections/tools/managers/irc_alter.php create mode 100644 sections/tools/managers/irc_list.php create mode 100644 sections/tools/managers/referral_accounts.php create mode 100644 sections/tools/managers/referral_alter.php create mode 100644 sections/tools/managers/referral_users.php create mode 100644 sections/tools/sandboxes/db_sandbox.php create mode 100644 sections/tools/sandboxes/referral_sandbox.php create mode 100644 sections/torrents/collector.php create mode 100644 sections/upload/parse_json.php create mode 100644 templates/emails/enable_request_accepted.twig create mode 100644 templates/emails/enable_request_denied.twig create mode 100644 templates/emails/invite.twig create mode 100644 templates/emails/new_registration.twig create mode 100644 templates/emails/password_reset.twig create mode 100644 templates/emails/recovery.twig create mode 100644 templates/emails/referral.twig create mode 100644 templates/error.twig create mode 100644 tests/Util/ArraysTest.php diff --git a/.bin/phpcbf b/.bin/phpcbf new file mode 100644 index 000000000..cda1b130e --- /dev/null +++ b/.bin/phpcbf @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +# This is a wrapper script around calling vendor/bin/phpcbf as it returns a exit 1 +# if it fixes anything, which does not mesh well with CI pipelines. +# See https://github.com/squizlabs/PHP_CodeSniffer/issues/1818#issuecomment-354420927 + +root=$( dirname $0 )/.. + +$root/vendor/bin/phpcbf $@ +exit=$? + +if [[ $exit == 1 ]]; then + exit=0 +fi + +exit $exit diff --git a/.docker/mysql/mysqld_sql_mode.cnf b/.docker/mysql/mysqld_sql_mode.cnf new file mode 100644 index 000000000..01a5a4324 --- /dev/null +++ b/.docker/mysql/mysqld_sql_mode.cnf @@ -0,0 +1,3 @@ +# this is only for the mysqld standalone daemon +[mysqld] +sql_mode = ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION diff --git a/.docker/sphinxsearch/Dockerfile b/.docker/sphinxsearch/Dockerfile new file mode 100644 index 000000000..6dbd20dce --- /dev/null +++ b/.docker/sphinxsearch/Dockerfile @@ -0,0 +1,29 @@ +FROM debian:buster-slim + +ENV SPHINX_VERSION 2.2.11 + +ENV SPHINX_FULL_STRING ${SPHINX_VERSION}-release + +RUN apt-get update && apt-get install -y --no-install-recommends \ + cron \ + curl \ + default-libmysqlclient-dev \ + mariadb-client \ + sphinxsearch \ + && rm -rf /var/lib/apt/lists/* + +RUN mkdir -pv /var/lib/sphinxsearch/data/ /var/lib/sphinxsearch/conf/ + +VOLUME /var/lib/sphinxsearch/data/ +VOLUME /var/lib/sphinxsearch/conf/ + +COPY crontab /var/lib/sphinxsearch/conf/ +COPY entrypoint.sh /var/lib/sphinxsearch/conf/ + +# redirect logs to stdout +RUN ln -sv /dev/stdout /var/log/sphinxsearch/query.log \ + && ln -sv /dev/stdout /var/log/sphinxsearch/searchd.log + +EXPOSE 36307 + +ENTRYPOINT [ "/bin/bash", "/var/lib/sphinxsearch/conf/entrypoint.sh" ] diff --git a/.docker/sphinxsearch/crontab b/.docker/sphinxsearch/crontab new file mode 100644 index 000000000..e92eb7598 --- /dev/null +++ b/.docker/sphinxsearch/crontab @@ -0,0 +1,2 @@ +* * * * * /usr/bin/indexer -c /var/lib/sphinxsearch/conf/sphinx.conf --rotate delta +5 * * * * /usr/bin/indexer -c /var/lib/sphinxsearch/conf/sphinx.conf --rotate --all diff --git a/.docker/sphinxsearch/entrypoint.sh b/.docker/sphinxsearch/entrypoint.sh new file mode 100644 index 000000000..0006a1d9c --- /dev/null +++ b/.docker/sphinxsearch/entrypoint.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +# Wait for MySQL... +counter=1 +while ! mysql -h mysql -ugazelle -ppassword -e "show databases;" > /dev/null 2>&1; do + sleep 1 + counter=`expr $counter + 1` + if [ $(($counter % 20)) -eq 0 ]; then + mysql -h mysql -ugazelle -ppassword -e "show databases;" + >&2 echo "Still waiting for MySQL (Count: ${counter})." + fi +done + +counter=1 +while ! curl --fail http://web > /dev/null 2>&1; do + sleep 1 + counter=`expr $counter + 1` + if [ $(($counter % 20)) -eq 0 ]; then + >&2 echo "Still waiting for Web (Count: ${counter})." + fi +done + +indexer -c /var/lib/sphinxsearch/conf/sphinx.conf --all + +service cron start +crontab /var/lib/sphinxsearch/conf/crontab + +searchd --nodetach --config /var/lib/sphinxsearch/conf/sphinx.conf diff --git a/.docker/sphinxsearch/sphinx.conf b/.docker/sphinxsearch/sphinx.conf new file mode 100644 index 000000000..bc1f9399a --- /dev/null +++ b/.docker/sphinxsearch/sphinx.conf @@ -0,0 +1,453 @@ +# Sphinx 2.2.9 +# WARNING: key 'enable_star' was permanently removed from Sphinx configuration. Refer to documentation for details. +# WARNING: key 'enable_star' was permanently removed from Sphinx configuration. Refer to documentation for details. +# WARNING: key 'max_matches' was permanently removed from Sphinx configuration. Refer to documentation for details. +# ERROR: unknown key name 'compat_sphinxql_magics' in /etc/sphinxsearch/sphinx.conf line 481 col 24. +# FATAL: failed to parse config file '/etc/sphinxsearch/sphinx.conf' +# the above error was fixed by commenting compat_sphinxql_magics +source connect { + type = mysql + sql_host = mysql + sql_user = gazelle + sql_pass = password + sql_db = gazelle + sql_port = 3306 +} + +source torrents_base : connect { + sql_attr_uint = groupid + sql_attr_uint = time + sql_attr_uint = categoryid + sql_attr_uint = releasetype + sql_attr_bigint = size + sql_attr_uint = snatched + sql_attr_uint = seeders + sql_attr_uint = leechers + sql_attr_uint = logscore + sql_attr_uint = year + sql_attr_bool = scene + sql_attr_bool = vanityhouse + sql_attr_bool = haslog + sql_attr_bool = hascue + sql_attr_uint = freetorrent +} +source torrents : torrents_base { + #By inheriting from torrents_base, we keep all the connection info + sql_query_pre = SET group_concat_max_len = 101400 + sql_query_pre = SET @starttime = NOW() + sql_query_pre = REPLACE INTO sphinx_index_last_pos VALUES ('torrents', UNIX_TIMESTAMP(@starttime)) + sql_query_pre = TRUNCATE sphinx_tg + sql_query_pre = INSERT INTO sphinx_tg \ + (id, name, tags, year, rlabel, cnumber, catid, reltype, \ + vanityhouse) \ + SELECT id, name, taglist, year, recordlabel, cataloguenumber, \ + categoryid, releasetype, vanityhouse \ + FROM torrents_group \ + WHERE time < @starttime + sql_query_pre = TRUNCATE sphinx_t + sql_query_pre = INSERT INTO sphinx_t \ + (id, gid, size, snatched, seeders, leechers, time, logscore, scene, \ + haslog, hascue, freetorrent, description, media, format, encoding, \ + remyear, remtitle, remrlabel, remcnumber, filelist, uid, remident) \ + SELECT t.ID, t.GroupID, t.Size, tls.Snatched, tls.Seeders, tls.Leechers, UNIX_TIMESTAMP(t.Time), \ + t.LogScore, CAST(t.Scene AS CHAR), CAST(t.HasLog AS CHAR), \ + CAST(t.HasCue AS CHAR), CAST(t.FreeTorrent AS CHAR), t.Description, \ + t.Media, t.Format, t.Encoding, t.RemasterYear, t.RemasterTitle, \ + t.RemasterRecordLabel, t.RemasterCatalogueNumber, t.FileList, t.UserID, \ + CRC32(CONCAT_WS(' ', t.Media, t.RemasterYear, t.RemasterTitle, \ + t.RemasterRecordLabel, t.RemasterCatalogueNumber)) \ + FROM torrents t \ + INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID) \ + WHERE t.Time < @starttime + sql_query_pre = TRUNCATE sphinx_a + sql_query_pre = INSERT INTO sphinx_a \ + (gid, aname) \ + SELECT GroupID, GROUP_CONCAT(aa.Name SEPARATOR ' ') \ + FROM torrents_artists AS ta \ + JOIN artists_alias AS aa USING(AliasID) \ + WHERE Importance IN ('1','3','4','5','6') \ + GROUP BY ta.groupid \ + ORDER BY NULL + sql_query = SELECT t.id, g.id AS groupid, g.name AS groupname, \ + tags AS taglist, year, year AS yearfulltext, \ + rlabel AS recordlabel, cnumber AS cataloguenumber, \ + catid AS categoryid, t.time, reltype AS releasetype, \ + size, snatched, seeders, leechers, logscore, \ + scene, vanityhouse, haslog, hascue, freetorrent, description, \ + media, format, encoding, remyear AS remasteryear, \ + remtitle AS remastertitle, remrlabel AS remasterrecordlabel, \ + remcnumber AS remastercataloguenumber, \ + REPLACE(filelist, '_', ' ') AS filelist \ + FROM sphinx_t AS t \ + JOIN sphinx_tg AS g ON t.gid = g.id + sql_joined_field = artistname from query; \ + SELECT t.id, aname FROM sphinx_a JOIN sphinx_t AS t USING(gid) ORDER BY t.id ASC; + sql_query_post_index = DELETE FROM sphinx_delta WHERE Time <= \ + (SELECT id FROM sphinx_index_last_pos WHERE type = 'torrents') +} +index torrents { + source = torrents + path = /var/lib/sphinxsearch/data/torrents + dict = keywords +# stopwords = /etc/sphinx/stopwords.txt # Path to file containing a space separated list of words not to index + preopen = 1 + morphology = none + min_word_len = 2 + phrase_boundary = U+F7 # This needs to the the same as the file delimiter in classes/torrents.class.php + phrase_boundary_step = 50 + infix_fields = taglist + min_infix_len = 3 + charset_table = \ + U+00C0->a, U+00C1->a, U+00C2->a, U+00C3->a, U+00C4->a, U+00C5->a, U+00E0->a, U+00E1->a, U+00E2->a, \ + U+00E3->a, U+00E4->a, U+00E5->a, U+0100->a, U+0101->a, U+0102->a, U+0103->a, U+010300->a, U+0104->a, U+0105->a, U+01CD->a, U+01CE->a, U+01DE->a, U+01DF->a, \ + U+01E0->a, U+01E1->a, U+01FA->a, U+01FB->a, U+0200->a, U+0201->a, U+0202->a, U+0203->a, U+0226->a, U+0227->a, U+023A->a, U+0250->a, U+04D0->a, U+04D1->a, U+1D2C->a, U+1D43->a, U+1D44->a, U+1D8F->a, U+1E00->a, U+1E01->a, U+1E9A->a, U+1EA0->a, U+1EA1->a, \ + U+1EA2->a, U+1EA3->a, U+1EA4->a, U+1EA5->a, U+1EA6->a, U+1EA7->a, U+1EA8->a, U+1EA9->a, U+1EAA->a, U+1EAB->a, U+1EAC->a, U+1EAD->a, U+1EAE->a, U+1EAF->a, U+1EB0->a, U+1EB1->a, U+1EB2->a, U+1EB3->a, U+1EB4->a, U+1EB5->a, U+1EB6->a, U+1EB7->a, U+2090->a, \ + U+2C65->a, U+0180->b, U+0181->b, U+0182->b, U+0183->b, U+0243->b, U+0253->b, U+0299->b, U+16D2->b, U+1D03->b, U+1D2E->b, U+1D2F->b, U+1D47->b, U+1D6C->b, U+1D80->b, U+1E02->b, U+1E03->b, U+1E04->b, U+1E05->b, U+1E06->b, U+1E07->b, U+00C7->c, U+00E7->c, \ + U+0106->c, U+0107->c, U+0108->c, U+0109->c, U+010A->c, U+010B->c, U+010C->c, U+010D->c, U+0187->c, U+0188->c, U+023B->c, U+023C->c, U+0255->c, U+0297->c, U+1D9C->c, U+1D9D->c, U+1E08->c, U+1E09->c, U+212D->c, U+2184->c, U+C487->c, U+010E->d, U+010F->d, U+0110->d, \ + U+0111->d, U+0189->d, U+018A->d, U+018B->d, U+018C->d, U+01C5->d, U+01F2->d, U+0221->d, U+0256->d, U+0257->d, U+1D05->d, U+1D30->d, U+1D48->d, U+1D6D->d, U+1D81->d, U+1D91->d, U+1E0A->d, U+1E0B->d, U+1E0C->d, U+1E0D->d, U+1E0E->d, U+1E0F->d, U+1E10->d, \ + U+1E11->d, U+1E12->d, U+1E13->d, U+00C8->e, U+00C9->e, U+00CA->e, U+00CB->e, U+00E8->e, U+00E9->e, U+00EA->e, U+00EB->e, U+0112->e, U+0113->e, U+0114->e, U+0115->e, U+0116->e, U+0117->e, U+0118->e, U+0119->e, U+011A->e, U+011B->e, U+018E->e, U+0190->e, \ + U+01DD->e, U+0204->e, U+0205->e, U+0206->e, U+0207->e, U+0228->e, U+0229->e, U+0246->e, U+0247->e, U+0258->e, U+025B->e, U+025C->e, U+025D->e, U+025E->e, U+029A->e, U+1D07->e, U+1D08->e, U+1D31->e, U+1D32->e, U+1D49->e, U+1D4B->e, U+1D4C->e, U+1D92->e, \ + U+1D93->e, U+1D94->e, U+1D9F->e, U+1E14->e, U+1E15->e, U+1E16->e, U+1E17->e, U+1E18->e, U+1E19->e, U+1E1A->e, U+1E1B->e, U+1E1C->e, U+1E1D->e, U+1EB8->e, U+1EB9->e, U+1EBA->e, U+1EBB->e, U+1EBC->e, U+1EBD->e, U+1EBE->e, U+1EBF->e, U+1EC0->e, U+1EC1->e, \ + U+1EC2->e, U+1EC3->e, U+1EC4->e, U+1EC5->e, U+1EC6->e, U+1EC7->e, U+2091->e, U+0191->f, U+0192->f, U+1D6E->f, U+1D82->f, U+1DA0->f, U+1E1E->f, U+1E1F->f, U+011C->g, U+011D->g, U+011E->g, U+011F->g, U+0120->g, U+0121->g, U+0122->g, U+0123->g, U+0193->g, \ + U+01E4->g, U+01E5->g, U+01E6->g, U+01E7->g, U+01F4->g, U+01F5->g, U+0260->g, U+0261->g, U+0262->g, U+029B->g, U+1D33->g, U+1D4D->g, U+1D77->g, U+1D79->g, U+1D83->g, U+1DA2->g, U+1E20->g, U+1E21->g, U+0124->h, U+0125->h, U+0126->h, U+0127->h, U+021E->h, \ + U+021F->h, U+0265->h, U+0266->h, U+029C->h, U+02AE->h, U+02AF->h, U+02B0->h, U+02B1->h, U+1D34->h, U+1DA3->h, U+1E22->h, U+1E23->h, U+1E24->h, U+1E25->h, U+1E26->h, U+1E27->h, U+1E28->h, U+1E29->h, U+1E2A->h, U+1E2B->h, U+1E96->h, U+210C->h, U+2C67->h, \ + U+2C68->h, U+2C75->h, U+2C76->h, U+00CC->i, U+00CD->i, U+00CE->i, U+00CF->i, U+00EC->i, U+00ED->i, U+00EE->i, U+00EF->i, U+010309->i, U+0128->i, U+0129->i, U+012A->i, U+012B->i, U+012C->i, U+012D->i, U+012E->i, U+012F->i, U+0130->i, U+0131->i, U+0197->i, \ + U+01CF->i, U+01D0->i, U+0208->i, U+0209->i, U+020A->i, U+020B->i, U+0268->i, U+026A->i, U+040D->i, U+0418->i, U+0419->i, U+0438->i, U+0439->i, U+0456->i, U+1D09->i, U+1D35->i, U+1D4E->i, U+1D62->i, U+1D7B->i, U+1D96->i, U+1DA4->i, U+1DA6->i, U+1DA7->i, \ + U+1E2C->i, U+1E2D->i, U+1E2E->i, U+1E2F->i, U+1EC8->i, U+1EC9->i, U+1ECA->i, U+1ECB->i, U+2071->i, U+2111->i, U+0134->j, U+0135->j, U+01C8->j, U+01CB->j, U+01F0->j, U+0237->j, U+0248->j, U+0249->j, U+025F->j, U+0284->j, U+029D->j, U+02B2->j, U+1D0A->j, \ + U+1D36->j, U+1DA1->j, U+1DA8->j, U+0136->k, U+0137->k, U+0198->k, U+0199->k, U+01E8->k, U+01E9->k, U+029E->k, U+1D0B->k, U+1D37->k, U+1D4F->k, U+1D84->k, U+1E30->k, U+1E31->k, U+1E32->k, U+1E33->k, U+1E34->k, U+1E35->k, U+2C69->k, U+2C6A->k, U+0139->l, \ + U+013A->l, U+013B->l, U+013C->l, U+013D->l, U+013E->l, U+013F->l, U+0140->l, U+0141->l, U+0142->l, U+019A->l, U+01C8->l, U+0234->l, U+023D->l, U+026B->l, U+026C->l, U+026D->l, U+029F->l, U+02E1->l, U+1D0C->l, U+1D38->l, U+1D85->l, U+1DA9->l, U+1DAA->l, \ + U+1DAB->l, U+1E36->l, U+1E37->l, U+1E38->l, U+1E39->l, U+1E3A->l, U+1E3B->l, U+1E3C->l, U+1E3D->l, U+2C60->l, U+2C61->l, U+2C62->l, U+019C->m, U+026F->m, U+0270->m, U+0271->m, U+1D0D->m, U+1D1F->m, U+1D39->m, U+1D50->m, U+1D5A->m, U+1D6F->m, U+1D86->m, \ + U+1DAC->m, U+1DAD->m, U+1E3E->m, U+1E3F->m, U+1E40->m, U+1E41->m, U+1E42->m, U+1E43->m, U+00D1->n, U+00F1->n, U+0143->n, U+0144->n, U+0145->n, U+0146->n, U+0147->n, U+0148->n, U+0149->n, U+019D->n, U+019E->n, U+01CB->n, U+01F8->n, U+01F9->n, U+0220->n, \ + U+0235->n, U+0272->n, U+0273->n, U+0274->n, U+1D0E->n, U+1D3A->n, U+1D3B->n, U+1D70->n, U+1D87->n, U+1DAE->n, U+1DAF->n, U+1DB0->n, U+1E44->n, U+1E45->n, U+1E46->n, U+1E47->n, U+1E48->n, U+1E49->n, U+1E4A->n, U+1E4B->n, U+207F->n, U+00D2->o, U+00D3->o, \ + U+00D4->o, U+00D5->o, U+00D6->o, U+00D8->o, U+00F2->o, U+00F3->o, U+00F4->o, U+00F5->o, U+00F6->o, U+00F8->o, U+01030F->o, U+014C->o, U+014D->o, U+014E->o, U+014F->o, U+0150->o, U+0151->o, U+0186->o, U+019F->o, U+01A0->o, U+01A1->o, U+01D1->o, U+01D2->o, \ + U+01EA->o, U+01EB->o, U+01EC->o, U+01ED->o, U+01FE->o, U+01FF->o, U+020C->o, U+020D->o, U+020E->o, U+020F->o, U+022A->o, U+022B->o, U+022C->o, U+022D->o, U+022E->o, U+022F->o, U+0230->o, U+0231->o, U+0254->o, U+0275->o, U+043E->o, U+04E6->o, U+04E7->o, \ + U+04E8->o, U+04E9->o, U+04EA->o, U+04EB->o, U+1D0F->o, U+1D10->o, U+1D11->o, U+1D12->o, U+1D13->o, U+1D16->o, U+1D17->o, U+1D3C->o, U+1D52->o, U+1D53->o, U+1D54->o, U+1D55->o, U+1D97->o, U+1DB1->o, U+1E4C->o, U+1E4D->o, U+1E4E->o, U+1E4F->o, U+1E50->o, \ + U+1E51->o, U+1E52->o, U+1E53->o, U+1ECC->o, U+1ECD->o, U+1ECE->o, U+1ECF->o, U+1ED0->o, U+1ED1->o, U+1ED2->o, U+1ED3->o, U+1ED4->o, U+1ED5->o, U+1ED6->o, U+1ED7->o, U+1ED8->o, U+1ED9->o, U+1EDA->o, U+1EDB->o, U+1EDC->o, U+1EDD->o, U+1EDE->o, U+1EDF->o, \ + U+1EE0->o, U+1EE1->o, U+1EE2->o, U+1EE3->o, U+2092->o, U+2C9E->o, U+2C9F->o, U+01A4->p, U+01A5->p, U+1D18->p, U+1D3E->p, U+1D56->p, U+1D71->p, U+1D7D->p, U+1D88->p, U+1E54->p, U+1E55->p, U+1E56->p, U+1E57->p, U+2C63->p, U+024A->q, U+024B->q, U+02A0->q, \ + U+0154->r, U+0155->r, U+0156->r, U+0157->r, U+0158->r, U+0159->r, U+0210->r, U+0211->r, U+0212->r, U+0213->r, U+024C->r, U+024D->r, U+0279->r, U+027A->r, U+027B->r, U+027C->r, U+027D->r, U+027E->r, U+027F->r, U+0280->r, U+0281->r, U+02B3->r, U+02B4->r, \ + U+02B5->r, U+02B6->r, U+1D19->r, U+1D1A->r, U+1D3F->r, U+1D63->r, U+1D72->r, U+1D73->r, U+1D89->r, U+1DCA->r, U+1E58->r, U+1E59->r, U+1E5A->r, U+1E5B->r, U+1E5C->r, U+1E5D->r, U+1E5E->r, U+1E5F->r, U+211C->r, U+2C64->r, U+00DF->s, U+015A->s, U+015B->s, \ + U+015C->s, U+015D->s, U+015E->s, U+015F->s, U+0160->s, U+0161->s, U+017F->s, U+0218->s, U+0219->s, U+023F->s, U+0282->s, U+02E2->s, U+1D74->s, U+1D8A->s, U+1DB3->s, U+1E60->s, U+1E61->s, U+1E62->s, U+1E63->s, U+1E64->s, U+1E65->s, U+1E66->s, U+1E67->s, \ + U+1E68->s, U+1E69->s, U+1E9B->s, U+0162->t, U+0163->t, U+0164->t, U+0165->t, U+0166->t, U+0167->t, U+01AB->t, U+01AC->t, U+01AD->t, U+01AE->t, U+021A->t, U+021B->t, U+0236->t, U+023E->t, U+0287->t, U+0288->t, U+1D1B->t, U+1D40->t, U+1D57->t, U+1D75->t, \ + U+1DB5->t, U+1E6A->t, U+1E6B->t, U+1E6C->t, U+1E6D->t, U+1E6E->t, U+1E6F->t, U+1E70->t, U+1E71->t, U+1E97->t, U+2C66->t, U+00D9->u, U+00DA->u, U+00DB->u, U+00DC->u, U+00F9->u, U+00FA->u, U+00FB->u, U+00FC->u, U+010316->u, U+0168->u, U+0169->u, U+016A->u, \ + U+016B->u, U+016C->u, U+016D->u, U+016E->u, U+016F->u, U+0170->u, U+0171->u, U+0172->u, U+0173->u, U+01AF->u, U+01B0->u, U+01D3->u, U+01D4->u, U+01D5->u, U+01D6->u, U+01D7->u, U+01D8->u, U+01D9->u, U+01DA->u, U+01DB->u, U+01DC->u, U+0214->u, U+0215->u, \ + U+0216->u, U+0217->u, U+0244->u, U+0289->u, U+1D1C->u, U+1D1D->u, U+1D1E->u, U+1D41->u, U+1D58->u, U+1D59->u, U+1D64->u, U+1D7E->u, U+1D99->u, U+1DB6->u, U+1DB8->u, U+1E72->u, U+1E73->u, U+1E74->u, U+1E75->u, U+1E76->u, U+1E77->u, U+1E78->u, U+1E79->u, \ + U+1E7A->u, U+1E7B->u, U+1EE4->u, U+1EE5->u, U+1EE6->u, U+1EE7->u, U+1EE8->u, U+1EE9->u, U+1EEA->u, U+1EEB->u, U+1EEC->u, U+1EED->u, U+1EEE->u, U+1EEF->u, U+1EF0->u, U+1EF1->u, U+01B2->v, U+0245->v, U+028B->v, U+028C->v, U+1D20->v, U+1D5B->v, U+1D65->v, \ + U+1D8C->v, U+1DB9->v, U+1DBA->v, U+1E7C->v, U+1E7D->v, U+1E7E->v, U+1E7F->v, U+2C74->v, U+0174->w, U+0175->w, U+028D->w, U+02B7->w, U+1D21->w, U+1D42->w, U+1E80->w, U+1E81->w, U+1E82->w, U+1E83->w, U+1E84->w, U+1E85->w, U+1E86->w, U+1E87->w, U+1E88->w, \ + U+1E89->w, U+1E98->w, U+02E3->x, U+1D8D->x, U+1E8A->x, U+1E8B->x, U+1E8C->x, U+1E8D->x, U+2093->x, U+00DD->y, U+00FD->y, U+00FF->y, U+0176->y, U+0177->y, U+0178->y, U+01B3->y, U+01B4->y, U+0232->y, U+0233->y, U+024E->y, U+024F->y, U+028E->y, U+028F->y, \ + U+02B8->y, U+1E8E->y, U+1E8F->y, U+1E99->y, U+1EF2->y, U+1EF3->y, U+1EF4->y, U+1EF5->y, U+1EF6->y, U+1EF7->y, U+1EF8->y, U+1EF9->y, U+0179->z, U+017A->z, U+017B->z, U+017C->z, U+017D->z, U+017E->z, U+01B5->z, U+01B6->z, U+0224->z, U+0225->z, U+0240->z, \ + U+0290->z, U+0291->z, U+1D22->z, U+1D76->z, U+1D8E->z, U+1DBB->z, U+1DBC->z, U+1DBD->z, U+1E90->z, U+1E91->z, U+1E92->z, U+1E93->z, U+1E94->z, U+1E95->z, U+2128->z, U+2C6B->z, U+2C6C->z, U+00C6->U+00E6, U+01E2->U+00E6, U+01E3->U+00E6, U+01FC->U+00E6, \ + U+01FD->U+00E6, U+1D01->U+00E6, U+1D02->U+00E6, U+1D2D->U+00E6, U+1D46->U+00E6, U+00E6, U+0622->U+0627, U+0623->U+0627, U+0624->U+0648, U+0625->U+0627, U+0626->U+064A, U+06C0->U+06D5, U+06C2->U+06C1, U+06D3->U+06D2, U+FB50->U+0671, U+FB51->U+0671, U+FB52->U+067B, \ + U+FB53->U+067B, U+FB54->U+067B, U+FB56->U+067E, U+FB57->U+067E, U+FB58->U+067E, U+FB5A->U+0680, U+FB5B->U+0680, U+FB5C->U+0680, U+FB5E->U+067A, U+FB5F->U+067A, U+FB60->U+067A, U+FB62->U+067F, U+FB63->U+067F, U+FB64->U+067F, U+FB66->U+0679, U+FB67->U+0679, \ + U+FB68->U+0679, U+FB6A->U+06A4, U+FB6B->U+06A4, U+FB6C->U+06A4, U+FB6E->U+06A6, U+FB6F->U+06A6, U+FB70->U+06A6, U+FB72->U+0684, U+FB73->U+0684, U+FB74->U+0684, U+FB76->U+0683, U+FB77->U+0683, U+FB78->U+0683, U+FB7A->U+0686, U+FB7B->U+0686, U+FB7C->U+0686, \ + U+FB7E->U+0687, U+FB7F->U+0687, U+FB80->U+0687, U+FB82->U+068D, U+FB83->U+068D, U+FB84->U+068C, U+FB85->U+068C, U+FB86->U+068E, U+FB87->U+068E, U+FB88->U+0688, U+FB89->U+0688, U+FB8A->U+0698, U+FB8B->U+0698, U+FB8C->U+0691, U+FB8D->U+0691, U+FB8E->U+06A9, \ + U+FB8F->U+06A9, U+FB90->U+06A9, U+FB92->U+06AF, U+FB93->U+06AF, U+FB94->U+06AF, U+FB96->U+06B3, U+FB97->U+06B3, U+FB98->U+06B3, U+FB9A->U+06B1, U+FB9B->U+06B1, U+FB9C->U+06B1, U+FB9E->U+06BA, U+FB9F->U+06BA, U+FBA0->U+06BB, U+FBA1->U+06BB, U+FBA2->U+06BB, \ + U+FBA4->U+06C0, U+FBA5->U+06C0, U+FBA6->U+06C1, U+FBA7->U+06C1, U+FBA8->U+06C1, U+FBAA->U+06BE, U+FBAB->U+06BE, U+FBAC->U+06BE, U+FBAE->U+06D2, U+FBAF->U+06D2, U+FBB0->U+06D3, U+FBB1->U+06D3, U+FBD3->U+06AD, U+FBD4->U+06AD, U+FBD5->U+06AD, U+FBD7->U+06C7, \ + U+FBD8->U+06C7, U+FBD9->U+06C6, U+FBDA->U+06C6, U+FBDB->U+06C8, U+FBDC->U+06C8, U+FBDD->U+0677, U+FBDE->U+06CB, U+FBDF->U+06CB, U+FBE0->U+06C5, U+FBE1->U+06C5, U+FBE2->U+06C9, U+FBE3->U+06C9, U+FBE4->U+06D0, U+FBE5->U+06D0, U+FBE6->U+06D0, U+FBE8->U+0649, \ + U+FBFC->U+06CC, U+FBFD->U+06CC, U+FBFE->U+06CC, U+0621, U+0627..U+063A, U+0641..U+064A, U+0660..U+0669, U+066E, U+066F, U+0671..U+06BF, U+06C1, U+06C3..U+06D2, U+06D5, U+06EE..U+06FC, U+06FF, U+0750..U+076D, U+FB55, U+FB59, U+FB5D, U+FB61, U+FB65, U+FB69, \ + U+FB6D, U+FB71, U+FB75, U+FB79, U+FB7D, U+FB81, U+FB91, U+FB95, U+FB99, U+FB9D, U+FBA3, U+FBA9, U+FBAD, U+FBD6, U+FBE7, U+FBE9, U+FBFF, U+0531..U+0556->U+0561..U+0586, U+0561..U+0586, U+0587, U+09DC->U+09A1, U+09DD->U+09A2, U+09DF->U+09AF, U+09F0->U+09AC, \ + U+09F1->U+09AC, U+0985..U+0990, U+0993..U+09B0, U+09B2, U+09B6..U+09B9, U+09CE, U+09E0, U+09E1, U+09E6..U+09EF, U+F900->U+8C48, U+F901->U+66F4, U+F902->U+8ECA, U+F903->U+8CC8, U+F904->U+6ED1, U+F905->U+4E32, U+F906->U+53E5, U+F907->U+9F9C, U+F908->U+9F9C, \ + U+F909->U+5951, U+F90A->U+91D1, U+F90B->U+5587, U+F90C->U+5948, U+F90D->U+61F6, U+F90E->U+7669, U+F90F->U+7F85, U+F910->U+863F, U+F911->U+87BA, U+F912->U+88F8, U+F913->U+908F, U+F914->U+6A02, U+F915->U+6D1B, U+F916->U+70D9, U+F917->U+73DE, U+F918->U+843D, \ + U+F919->U+916A, U+F91A->U+99F1, U+F91B->U+4E82, U+F91C->U+5375, U+F91D->U+6B04, U+F91E->U+721B, U+F91F->U+862D, U+F920->U+9E1E, U+F921->U+5D50, U+F922->U+6FEB, U+F923->U+85CD, U+F924->U+8964, U+F925->U+62C9, U+F926->U+81D8, U+F927->U+881F, U+F928->U+5ECA, \ + U+F929->U+6717, U+F92A->U+6D6A, U+F92B->U+72FC, U+F92C->U+90CE, U+F92D->U+4F86, U+F92E->U+51B7, U+F92F->U+52DE, U+F930->U+64C4, U+F931->U+6AD3, U+F932->U+7210, U+F933->U+76E7, U+F934->U+8001, U+F935->U+8606, U+F936->U+865C, U+F937->U+8DEF, U+F938->U+9732, \ + U+F939->U+9B6F, U+F93A->U+9DFA, U+F93B->U+788C, U+F93C->U+797F, U+F93D->U+7DA0, U+F93E->U+83C9, U+F93F->U+9304, U+F940->U+9E7F, U+F941->U+8AD6, U+F942->U+58DF, U+F943->U+5F04, U+F944->U+7C60, U+F945->U+807E, U+F946->U+7262, U+F947->U+78CA, U+F948->U+8CC2, \ + U+F949->U+96F7, U+F94A->U+58D8, U+F94B->U+5C62, U+F94C->U+6A13, U+F94D->U+6DDA, U+F94E->U+6F0F, U+F94F->U+7D2F, U+F950->U+7E37, U+F951->U+964B, U+F952->U+52D2, U+F953->U+808B, U+F954->U+51DC, U+F955->U+51CC, U+F956->U+7A1C, U+F957->U+7DBE, U+F958->U+83F1, \ + U+F959->U+9675, U+F95A->U+8B80, U+F95B->U+62CF, U+F95C->U+6A02, U+F95D->U+8AFE, U+F95E->U+4E39, U+F95F->U+5BE7, U+F960->U+6012, U+F961->U+7387, U+F962->U+7570, U+F963->U+5317, U+F964->U+78FB, U+F965->U+4FBF, U+F966->U+5FA9, U+F967->U+4E0D, U+F968->U+6CCC, \ + U+F969->U+6578, U+F96A->U+7D22, U+F96B->U+53C3, U+F96C->U+585E, U+F96D->U+7701, U+F96E->U+8449, U+F96F->U+8AAA, U+F970->U+6BBA, U+F971->U+8FB0, U+F972->U+6C88, U+F973->U+62FE, U+F974->U+82E5, U+F975->U+63A0, U+F976->U+7565, U+F977->U+4EAE, U+F978->U+5169, \ + U+F979->U+51C9, U+F97A->U+6881, U+F97B->U+7CE7, U+F97C->U+826F, U+F97D->U+8AD2, U+F97E->U+91CF, U+F97F->U+52F5, U+F980->U+5442, U+F981->U+5973, U+F982->U+5EEC, U+F983->U+65C5, U+F984->U+6FFE, U+F985->U+792A, U+F986->U+95AD, U+F987->U+9A6A, U+F988->U+9E97, \ + U+F989->U+9ECE, U+F98A->U+529B, U+F98B->U+66C6, U+F98C->U+6B77, U+F98D->U+8F62, U+F98E->U+5E74, U+F98F->U+6190, U+F990->U+6200, U+F991->U+649A, U+F992->U+6F23, U+F993->U+7149, U+F994->U+7489, U+F995->U+79CA, U+F996->U+7DF4, U+F997->U+806F, U+F998->U+8F26, \ + U+F999->U+84EE, U+F99A->U+9023, U+F99B->U+934A, U+F99C->U+5217, U+F99D->U+52A3, U+F99E->U+54BD, U+F99F->U+70C8, U+F9A0->U+88C2, U+F9A1->U+8AAA, U+F9A2->U+5EC9, U+F9A3->U+5FF5, U+F9A4->U+637B, U+F9A5->U+6BAE, U+F9A6->U+7C3E, U+F9A7->U+7375, U+F9A8->U+4EE4, \ + U+F9A9->U+56F9, U+F9AA->U+5BE7, U+F9AB->U+5DBA, U+F9AC->U+601C, U+F9AD->U+73B2, U+F9AE->U+7469, U+F9AF->U+7F9A, U+F9B0->U+8046, U+F9B1->U+9234, U+F9B2->U+96F6, U+F9B3->U+9748, U+F9B4->U+9818, U+F9B5->U+4F8B, U+F9B6->U+79AE, U+F9B7->U+91B4, U+F9B8->U+96B8, \ + U+F9B9->U+60E1, U+F9BA->U+4E86, U+F9BB->U+50DA, U+F9BC->U+5BEE, U+F9BD->U+5C3F, U+F9BE->U+6599, U+F9BF->U+6A02, U+F9C0->U+71CE, U+F9C1->U+7642, U+F9C2->U+84FC, U+F9C3->U+907C, U+F9C4->U+9F8D, U+F9C5->U+6688, U+F9C6->U+962E, U+F9C7->U+5289, U+F9C8->U+677B, \ + U+F9C9->U+67F3, U+F9CA->U+6D41, U+F9CB->U+6E9C, U+F9CC->U+7409, U+F9CD->U+7559, U+F9CE->U+786B, U+F9CF->U+7D10, U+F9D0->U+985E, U+F9D1->U+516D, U+F9D2->U+622E, U+F9D3->U+9678, U+F9D4->U+502B, U+F9D5->U+5D19, U+F9D6->U+6DEA, U+F9D7->U+8F2A, U+F9D8->U+5F8B, \ + U+F9D9->U+6144, U+F9DA->U+6817, U+F9DB->U+7387, U+F9DC->U+9686, U+F9DD->U+5229, U+F9DE->U+540F, U+F9DF->U+5C65, U+F9E0->U+6613, U+F9E1->U+674E, U+F9E2->U+68A8, U+F9E3->U+6CE5, U+F9E4->U+7406, U+F9E5->U+75E2, U+F9E6->U+7F79, U+F9E7->U+88CF, U+F9E8->U+88E1, \ + U+F9E9->U+91CC, U+F9EA->U+96E2, U+F9EB->U+533F, U+F9EC->U+6EBA, U+F9ED->U+541D, U+F9EE->U+71D0, U+F9EF->U+7498, U+F9F0->U+85FA, U+F9F1->U+96A3, U+F9F2->U+9C57, U+F9F3->U+9E9F, U+F9F4->U+6797, U+F9F5->U+6DCB, U+F9F6->U+81E8, U+F9F7->U+7ACB, U+F9F8->U+7B20, \ + U+F9F9->U+7C92, U+F9FA->U+72C0, U+F9FB->U+7099, U+F9FC->U+8B58, U+F9FD->U+4EC0, U+F9FE->U+8336, U+F9FF->U+523A, U+FA00->U+5207, U+FA01->U+5EA6, U+FA02->U+62D3, U+FA03->U+7CD6, U+FA04->U+5B85, U+FA05->U+6D1E, U+FA06->U+66B4, U+FA07->U+8F3B, U+FA08->U+884C, \ + U+FA09->U+964D, U+FA0A->U+898B, U+FA0B->U+5ED3, U+FA0C->U+5140, U+FA0D->U+55C0, U+FA10->U+585A, U+FA12->U+6674, U+FA15->U+51DE, U+FA16->U+732A, U+FA17->U+76CA, U+FA18->U+793C, U+FA19->U+795E, U+FA1A->U+7965, U+FA1B->U+798F, U+FA1C->U+9756, U+FA1D->U+7CBE, \ + U+FA1E->U+7FBD, U+FA20->U+8612, U+FA22->U+8AF8, U+FA25->U+9038, U+FA26->U+90FD, U+FA2A->U+98EF, U+FA2B->U+98FC, U+FA2C->U+9928, U+FA2D->U+9DB4, U+FA30->U+4FAE, U+FA31->U+50E7, U+FA32->U+514D, U+FA33->U+52C9, U+FA34->U+52E4, U+FA35->U+5351, U+FA36->U+559D, \ + U+FA37->U+5606, U+FA38->U+5668, U+FA39->U+5840, U+FA3A->U+58A8, U+FA3B->U+5C64, U+FA3C->U+5C6E, U+FA3D->U+6094, U+FA3E->U+6168, U+FA3F->U+618E, U+FA40->U+61F2, U+FA41->U+654F, U+FA42->U+65E2, U+FA43->U+6691, U+FA44->U+6885, U+FA45->U+6D77, U+FA46->U+6E1A, \ + U+FA47->U+6F22, U+FA48->U+716E, U+FA49->U+722B, U+FA4A->U+7422, U+FA4B->U+7891, U+FA4C->U+793E, U+FA4D->U+7949, U+FA4E->U+7948, U+FA4F->U+7950, U+FA50->U+7956, U+FA51->U+795D, U+FA52->U+798D, U+FA53->U+798E, U+FA54->U+7A40, U+FA55->U+7A81, U+FA56->U+7BC0, \ + U+FA57->U+7DF4, U+FA58->U+7E09, U+FA59->U+7E41, U+FA5A->U+7F72, U+FA5B->U+8005, U+FA5C->U+81ED, U+FA5D->U+8279, U+FA5E->U+8279, U+FA5F->U+8457, U+FA60->U+8910, U+FA61->U+8996, U+FA62->U+8B01, U+FA63->U+8B39, U+FA64->U+8CD3, U+FA65->U+8D08, U+FA66->U+8FB6, \ + U+FA67->U+9038, U+FA68->U+96E3, U+FA69->U+97FF, U+FA6A->U+983B, U+FA70->U+4E26, U+FA71->U+51B5, U+FA72->U+5168, U+FA73->U+4F80, U+FA74->U+5145, U+FA75->U+5180, U+FA76->U+52C7, U+FA77->U+52FA, U+FA78->U+559D, U+FA79->U+5555, U+FA7A->U+5599, U+FA7B->U+55E2, \ + U+FA7C->U+585A, U+FA7D->U+58B3, U+FA7E->U+5944, U+FA7F->U+5954, U+FA80->U+5A62, U+FA81->U+5B28, U+FA82->U+5ED2, U+FA83->U+5ED9, U+FA84->U+5F69, U+FA85->U+5FAD, U+FA86->U+60D8, U+FA87->U+614E, U+FA88->U+6108, U+FA89->U+618E, U+FA8A->U+6160, U+FA8B->U+61F2, \ + U+FA8C->U+6234, U+FA8D->U+63C4, U+FA8E->U+641C, U+FA8F->U+6452, U+FA90->U+6556, U+FA91->U+6674, U+FA92->U+6717, U+FA93->U+671B, U+FA94->U+6756, U+FA95->U+6B79, U+FA96->U+6BBA, U+FA97->U+6D41, U+FA98->U+6EDB, U+FA99->U+6ECB, U+FA9A->U+6F22, U+FA9B->U+701E, \ + U+FA9C->U+716E, U+FA9D->U+77A7, U+FA9E->U+7235, U+FA9F->U+72AF, U+FAA0->U+732A, U+FAA1->U+7471, U+FAA2->U+7506, U+FAA3->U+753B, U+FAA4->U+761D, U+FAA5->U+761F, U+FAA6->U+76CA, U+FAA7->U+76DB, U+FAA8->U+76F4, U+FAA9->U+774A, U+FAAA->U+7740, U+FAAB->U+78CC, \ + U+FAAC->U+7AB1, U+FAAD->U+7BC0, U+FAAE->U+7C7B, U+FAAF->U+7D5B, U+FAB0->U+7DF4, U+FAB1->U+7F3E, U+FAB2->U+8005, U+FAB3->U+8352, U+FAB4->U+83EF, U+FAB5->U+8779, U+FAB6->U+8941, U+FAB7->U+8986, U+FAB8->U+8996, U+FAB9->U+8ABF, U+FABA->U+8AF8, U+FABB->U+8ACB, \ + U+FABC->U+8B01, U+FABD->U+8AFE, U+FABE->U+8AED, U+FABF->U+8B39, U+FAC0->U+8B8A, U+FAC1->U+8D08, U+FAC2->U+8F38, U+FAC3->U+9072, U+FAC4->U+9199, U+FAC5->U+9276, U+FAC6->U+967C, U+FAC7->U+96E3, U+FAC8->U+9756, U+FAC9->U+97DB, U+FACA->U+97FF, U+FACB->U+980B, \ + U+FACC->U+983B, U+FACD->U+9B12, U+FACE->U+9F9C, U+FACF->U+2284A, U+FAD0->U+22844, U+FAD1->U+233D5, U+FAD2->U+3B9D, U+FAD3->U+4018, U+FAD4->U+4039, U+FAD5->U+25249, U+FAD6->U+25CD0, U+FAD7->U+27ED3, U+FAD8->U+9F43, U+FAD9->U+9F8E, U+2F800->U+4E3D, U+2F801->U+4E38, \ + U+2F802->U+4E41, U+2F803->U+20122, U+2F804->U+4F60, U+2F805->U+4FAE, U+2F806->U+4FBB, U+2F807->U+5002, U+2F808->U+507A, U+2F809->U+5099, U+2F80A->U+50E7, U+2F80B->U+50CF, U+2F80C->U+349E, U+2F80D->U+2063A, U+2F80E->U+514D, U+2F80F->U+5154, U+2F810->U+5164, \ + U+2F811->U+5177, U+2F812->U+2051C, U+2F813->U+34B9, U+2F814->U+5167, U+2F815->U+518D, U+2F816->U+2054B, U+2F817->U+5197, U+2F818->U+51A4, U+2F819->U+4ECC, U+2F81A->U+51AC, U+2F81B->U+51B5, U+2F81C->U+291DF, U+2F81D->U+51F5, U+2F81E->U+5203, U+2F81F->U+34DF, \ + U+2F820->U+523B, U+2F821->U+5246, U+2F822->U+5272, U+2F823->U+5277, U+2F824->U+3515, U+2F825->U+52C7, U+2F826->U+52C9, U+2F827->U+52E4, U+2F828->U+52FA, U+2F829->U+5305, U+2F82A->U+5306, U+2F82B->U+5317, U+2F82C->U+5349, U+2F82D->U+5351, U+2F82E->U+535A, \ + U+2F82F->U+5373, U+2F830->U+537D, U+2F831->U+537F, U+2F832->U+537F, U+2F833->U+537F, U+2F834->U+20A2C, U+2F835->U+7070, U+2F836->U+53CA, U+2F837->U+53DF, U+2F838->U+20B63, U+2F839->U+53EB, U+2F83A->U+53F1, U+2F83B->U+5406, U+2F83C->U+549E, U+2F83D->U+5438, \ + U+2F83E->U+5448, U+2F83F->U+5468, U+2F840->U+54A2, U+2F841->U+54F6, U+2F842->U+5510, U+2F843->U+5553, U+2F844->U+5563, U+2F845->U+5584, U+2F846->U+5584, U+2F847->U+5599, U+2F848->U+55AB, U+2F849->U+55B3, U+2F84A->U+55C2, U+2F84B->U+5716, U+2F84C->U+5606, \ + U+2F84D->U+5717, U+2F84E->U+5651, U+2F84F->U+5674, U+2F850->U+5207, U+2F851->U+58EE, U+2F852->U+57CE, U+2F853->U+57F4, U+2F854->U+580D, U+2F855->U+578B, U+2F856->U+5832, U+2F857->U+5831, U+2F858->U+58AC, U+2F859->U+214E4, U+2F85A->U+58F2, U+2F85B->U+58F7, \ + U+2F85C->U+5906, U+2F85D->U+591A, U+2F85E->U+5922, U+2F85F->U+5962, U+2F860->U+216A8, U+2F861->U+216EA, U+2F862->U+59EC, U+2F863->U+5A1B, U+2F864->U+5A27, U+2F865->U+59D8, U+2F866->U+5A66, U+2F867->U+36EE, U+2F868->U+36FC, U+2F869->U+5B08, U+2F86A->U+5B3E, \ + U+2F86B->U+5B3E, U+2F86C->U+219C8, U+2F86D->U+5BC3, U+2F86E->U+5BD8, U+2F86F->U+5BE7, U+2F870->U+5BF3, U+2F871->U+21B18, U+2F872->U+5BFF, U+2F873->U+5C06, U+2F874->U+5F53, U+2F875->U+5C22, U+2F876->U+3781, U+2F877->U+5C60, U+2F878->U+5C6E, U+2F879->U+5CC0, \ + U+2F87A->U+5C8D, U+2F87B->U+21DE4, U+2F87C->U+5D43, U+2F87D->U+21DE6, U+2F87E->U+5D6E, U+2F87F->U+5D6B, U+2F880->U+5D7C, U+2F881->U+5DE1, U+2F882->U+5DE2, U+2F883->U+382F, U+2F884->U+5DFD, U+2F885->U+5E28, U+2F886->U+5E3D, U+2F887->U+5E69, U+2F888->U+3862, \ + U+2F889->U+22183, U+2F88A->U+387C, U+2F88B->U+5EB0, U+2F88C->U+5EB3, U+2F88D->U+5EB6, U+2F88E->U+5ECA, U+2F88F->U+2A392, U+2F890->U+5EFE, U+2F891->U+22331, U+2F892->U+22331, U+2F893->U+8201, U+2F894->U+5F22, U+2F895->U+5F22, U+2F896->U+38C7, U+2F897->U+232B8, \ + U+2F898->U+261DA, U+2F899->U+5F62, U+2F89A->U+5F6B, U+2F89B->U+38E3, U+2F89C->U+5F9A, U+2F89D->U+5FCD, U+2F89E->U+5FD7, U+2F89F->U+5FF9, U+2F8A0->U+6081, U+2F8A1->U+393A, U+2F8A2->U+391C, U+2F8A3->U+6094, U+2F8A4->U+226D4, U+2F8A5->U+60C7, U+2F8A6->U+6148, \ + U+2F8A7->U+614C, U+2F8A8->U+614E, U+2F8A9->U+614C, U+2F8AA->U+617A, U+2F8AB->U+618E, U+2F8AC->U+61B2, U+2F8AD->U+61A4, U+2F8AE->U+61AF, U+2F8AF->U+61DE, U+2F8B0->U+61F2, U+2F8B1->U+61F6, U+2F8B2->U+6210, U+2F8B3->U+621B, U+2F8B4->U+625D, U+2F8B5->U+62B1, \ + U+2F8B6->U+62D4, U+2F8B7->U+6350, U+2F8B8->U+22B0C, U+2F8B9->U+633D, U+2F8BA->U+62FC, U+2F8BB->U+6368, U+2F8BC->U+6383, U+2F8BD->U+63E4, U+2F8BE->U+22BF1, U+2F8BF->U+6422, U+2F8C0->U+63C5, U+2F8C1->U+63A9, U+2F8C2->U+3A2E, U+2F8C3->U+6469, U+2F8C4->U+647E, \ + U+2F8C5->U+649D, U+2F8C6->U+6477, U+2F8C7->U+3A6C, U+2F8C8->U+654F, U+2F8C9->U+656C, U+2F8CA->U+2300A, U+2F8CB->U+65E3, U+2F8CC->U+66F8, U+2F8CD->U+6649, U+2F8CE->U+3B19, U+2F8CF->U+6691, U+2F8D0->U+3B08, U+2F8D1->U+3AE4, U+2F8D2->U+5192, U+2F8D3->U+5195, \ + U+2F8D4->U+6700, U+2F8D5->U+669C, U+2F8D6->U+80AD, U+2F8D7->U+43D9, U+2F8D8->U+6717, U+2F8D9->U+671B, U+2F8DA->U+6721, U+2F8DB->U+675E, U+2F8DC->U+6753, U+2F8DD->U+233C3, U+2F8DE->U+3B49, U+2F8DF->U+67FA, U+2F8E0->U+6785, U+2F8E1->U+6852, U+2F8E2->U+6885, \ + U+2F8E3->U+2346D, U+2F8E4->U+688E, U+2F8E5->U+681F, U+2F8E6->U+6914, U+2F8E7->U+3B9D, U+2F8E8->U+6942, U+2F8E9->U+69A3, U+2F8EA->U+69EA, U+2F8EB->U+6AA8, U+2F8EC->U+236A3, U+2F8ED->U+6ADB, U+2F8EE->U+3C18, U+2F8EF->U+6B21, U+2F8F0->U+238A7, U+2F8F1->U+6B54, \ + U+2F8F2->U+3C4E, U+2F8F3->U+6B72, U+2F8F4->U+6B9F, U+2F8F5->U+6BBA, U+2F8F6->U+6BBB, U+2F8F7->U+23A8D, U+2F8F8->U+21D0B, U+2F8F9->U+23AFA, U+2F8FA->U+6C4E, U+2F8FB->U+23CBC, U+2F8FC->U+6CBF, U+2F8FD->U+6CCD, U+2F8FE->U+6C67, U+2F8FF->U+6D16, U+2F900->U+6D3E, \ + U+2F901->U+6D77, U+2F902->U+6D41, U+2F903->U+6D69, U+2F904->U+6D78, U+2F905->U+6D85, U+2F906->U+23D1E, U+2F907->U+6D34, U+2F908->U+6E2F, U+2F909->U+6E6E, U+2F90A->U+3D33, U+2F90B->U+6ECB, U+2F90C->U+6EC7, U+2F90D->U+23ED1, U+2F90E->U+6DF9, U+2F90F->U+6F6E, \ + U+2F910->U+23F5E, U+2F911->U+23F8E, U+2F912->U+6FC6, U+2F913->U+7039, U+2F914->U+701E, U+2F915->U+701B, U+2F916->U+3D96, U+2F917->U+704A, U+2F918->U+707D, U+2F919->U+7077, U+2F91A->U+70AD, U+2F91B->U+20525, U+2F91C->U+7145, U+2F91D->U+24263, U+2F91E->U+719C, \ + U+2F91F->U+243AB, U+2F920->U+7228, U+2F921->U+7235, U+2F922->U+7250, U+2F923->U+24608, U+2F924->U+7280, U+2F925->U+7295, U+2F926->U+24735, U+2F927->U+24814, U+2F928->U+737A, U+2F929->U+738B, U+2F92A->U+3EAC, U+2F92B->U+73A5, U+2F92C->U+3EB8, U+2F92D->U+3EB8, \ + U+2F92E->U+7447, U+2F92F->U+745C, U+2F930->U+7471, U+2F931->U+7485, U+2F932->U+74CA, U+2F933->U+3F1B, U+2F934->U+7524, U+2F935->U+24C36, U+2F936->U+753E, U+2F937->U+24C92, U+2F938->U+7570, U+2F939->U+2219F, U+2F93A->U+7610, U+2F93B->U+24FA1, U+2F93C->U+24FB8, \ + U+2F93D->U+25044, U+2F93E->U+3FFC, U+2F93F->U+4008, U+2F940->U+76F4, U+2F941->U+250F3, U+2F942->U+250F2, U+2F943->U+25119, U+2F944->U+25133, U+2F945->U+771E, U+2F946->U+771F, U+2F947->U+771F, U+2F948->U+774A, U+2F949->U+4039, U+2F94A->U+778B, U+2F94B->U+4046, \ + U+2F94C->U+4096, U+2F94D->U+2541D, U+2F94E->U+784E, U+2F94F->U+788C, U+2F950->U+78CC, U+2F951->U+40E3, U+2F952->U+25626, U+2F953->U+7956, U+2F954->U+2569A, U+2F955->U+256C5, U+2F956->U+798F, U+2F957->U+79EB, U+2F958->U+412F, U+2F959->U+7A40, U+2F95A->U+7A4A, \ + U+2F95B->U+7A4F, U+2F95C->U+2597C, U+2F95D->U+25AA7, U+2F95E->U+25AA7, U+2F95F->U+7AEE, U+2F960->U+4202, U+2F961->U+25BAB, U+2F962->U+7BC6, U+2F963->U+7BC9, U+2F964->U+4227, U+2F965->U+25C80, U+2F966->U+7CD2, U+2F967->U+42A0, U+2F968->U+7CE8, U+2F969->U+7CE3, \ + U+2F96A->U+7D00, U+2F96B->U+25F86, U+2F96C->U+7D63, U+2F96D->U+4301, U+2F96E->U+7DC7, U+2F96F->U+7E02, U+2F970->U+7E45, U+2F971->U+4334, U+2F972->U+26228, U+2F973->U+26247, U+2F974->U+4359, U+2F975->U+262D9, U+2F976->U+7F7A, U+2F977->U+2633E, U+2F978->U+7F95, \ + U+2F979->U+7FFA, U+2F97A->U+8005, U+2F97B->U+264DA, U+2F97C->U+26523, U+2F97D->U+8060, U+2F97E->U+265A8, U+2F97F->U+8070, U+2F980->U+2335F, U+2F981->U+43D5, U+2F982->U+80B2, U+2F983->U+8103, U+2F984->U+440B, U+2F985->U+813E, U+2F986->U+5AB5, U+2F987->U+267A7, \ + U+2F988->U+267B5, U+2F989->U+23393, U+2F98A->U+2339C, U+2F98B->U+8201, U+2F98C->U+8204, U+2F98D->U+8F9E, U+2F98E->U+446B, U+2F98F->U+8291, U+2F990->U+828B, U+2F991->U+829D, U+2F992->U+52B3, U+2F993->U+82B1, U+2F994->U+82B3, U+2F995->U+82BD, U+2F996->U+82E6, \ + U+2F997->U+26B3C, U+2F998->U+82E5, U+2F999->U+831D, U+2F99A->U+8363, U+2F99B->U+83AD, U+2F99C->U+8323, U+2F99D->U+83BD, U+2F99E->U+83E7, U+2F99F->U+8457, U+2F9A0->U+8353, U+2F9A1->U+83CA, U+2F9A2->U+83CC, U+2F9A3->U+83DC, U+2F9A4->U+26C36, U+2F9A5->U+26D6B, \ + U+2F9A6->U+26CD5, U+2F9A7->U+452B, U+2F9A8->U+84F1, U+2F9A9->U+84F3, U+2F9AA->U+8516, U+2F9AB->U+273CA, U+2F9AC->U+8564, U+2F9AD->U+26F2C, U+2F9AE->U+455D, U+2F9AF->U+4561, U+2F9B0->U+26FB1, U+2F9B1->U+270D2, U+2F9B2->U+456B, U+2F9B3->U+8650, U+2F9B4->U+865C, \ + U+2F9B5->U+8667, U+2F9B6->U+8669, U+2F9B7->U+86A9, U+2F9B8->U+8688, U+2F9B9->U+870E, U+2F9BA->U+86E2, U+2F9BB->U+8779, U+2F9BC->U+8728, U+2F9BD->U+876B, U+2F9BE->U+8786, U+2F9BF->U+45D7, U+2F9C0->U+87E1, U+2F9C1->U+8801, U+2F9C2->U+45F9, U+2F9C3->U+8860, \ + U+2F9C4->U+8863, U+2F9C5->U+27667, U+2F9C6->U+88D7, U+2F9C7->U+88DE, U+2F9C8->U+4635, U+2F9C9->U+88FA, U+2F9CA->U+34BB, U+2F9CB->U+278AE, U+2F9CC->U+27966, U+2F9CD->U+46BE, U+2F9CE->U+46C7, U+2F9CF->U+8AA0, U+2F9D0->U+8AED, U+2F9D1->U+8B8A, U+2F9D2->U+8C55, \ + U+2F9D3->U+27CA8, U+2F9D4->U+8CAB, U+2F9D5->U+8CC1, U+2F9D6->U+8D1B, U+2F9D7->U+8D77, U+2F9D8->U+27F2F, U+2F9D9->U+20804, U+2F9DA->U+8DCB, U+2F9DB->U+8DBC, U+2F9DC->U+8DF0, U+2F9DD->U+208DE, U+2F9DE->U+8ED4, U+2F9DF->U+8F38, U+2F9E0->U+285D2, U+2F9E1->U+285ED, \ + U+2F9E2->U+9094, U+2F9E3->U+90F1, U+2F9E4->U+9111, U+2F9E5->U+2872E, U+2F9E6->U+911B, U+2F9E7->U+9238, U+2F9E8->U+92D7, U+2F9E9->U+92D8, U+2F9EA->U+927C, U+2F9EB->U+93F9, U+2F9EC->U+9415, U+2F9ED->U+28BFA, U+2F9EE->U+958B, U+2F9EF->U+4995, U+2F9F0->U+95B7, \ + U+2F9F1->U+28D77, U+2F9F2->U+49E6, U+2F9F3->U+96C3, U+2F9F4->U+5DB2, U+2F9F5->U+9723, U+2F9F6->U+29145, U+2F9F7->U+2921A, U+2F9F8->U+4A6E, U+2F9F9->U+4A76, U+2F9FA->U+97E0, U+2F9FB->U+2940A, U+2F9FC->U+4AB2, U+2F9FD->U+29496, U+2F9FE->U+980B, U+2F9FF->U+980B, \ + U+2FA00->U+9829, U+2FA01->U+295B6, U+2FA02->U+98E2, U+2FA03->U+4B33, U+2FA04->U+9929, U+2FA05->U+99A7, U+2FA06->U+99C2, U+2FA07->U+99FE, U+2FA08->U+4BCE, U+2FA09->U+29B30, U+2FA0A->U+9B12, U+2FA0B->U+9C40, U+2FA0C->U+9CFD, U+2FA0D->U+4CCE, U+2FA0E->U+4CED, \ + U+2FA0F->U+9D67, U+2FA10->U+2A0CE, U+2FA11->U+4CF8, U+2FA12->U+2A105, U+2FA13->U+2A20E, U+2FA14->U+2A291, U+2FA15->U+9EBB, U+2FA16->U+4D56, U+2FA17->U+9EF9, U+2FA18->U+9EFE, U+2FA19->U+9F05, U+2FA1A->U+9F0F, U+2FA1B->U+9F16, U+2FA1C->U+9F3B, U+2FA1D->U+2A600, \ + U+2F00->U+4E00, U+2F01->U+4E28, U+2F02->U+4E36, U+2F03->U+4E3F, U+2F04->U+4E59, U+2F05->U+4E85, U+2F06->U+4E8C, U+2F07->U+4EA0, U+2F08->U+4EBA, U+2F09->U+513F, U+2F0A->U+5165, U+2F0B->U+516B, U+2F0C->U+5182, U+2F0D->U+5196, U+2F0E->U+51AB, U+2F0F->U+51E0, \ + U+2F10->U+51F5, U+2F11->U+5200, U+2F12->U+529B, U+2F13->U+52F9, U+2F14->U+5315, U+2F15->U+531A, U+2F16->U+5338, U+2F17->U+5341, U+2F18->U+535C, U+2F19->U+5369, U+2F1A->U+5382, U+2F1B->U+53B6, U+2F1C->U+53C8, U+2F1D->U+53E3, U+2F1E->U+56D7, U+2F1F->U+571F, \ + U+2F20->U+58EB, U+2F21->U+5902, U+2F22->U+590A, U+2F23->U+5915, U+2F24->U+5927, U+2F25->U+5973, U+2F26->U+5B50, U+2F27->U+5B80, U+2F28->U+5BF8, U+2F29->U+5C0F, U+2F2A->U+5C22, U+2F2B->U+5C38, U+2F2C->U+5C6E, U+2F2D->U+5C71, U+2F2E->U+5DDB, U+2F2F->U+5DE5, \ + U+2F30->U+5DF1, U+2F31->U+5DFE, U+2F32->U+5E72, U+2F33->U+5E7A, U+2F34->U+5E7F, U+2F35->U+5EF4, U+2F36->U+5EFE, U+2F37->U+5F0B, U+2F38->U+5F13, U+2F39->U+5F50, U+2F3A->U+5F61, U+2F3B->U+5F73, U+2F3C->U+5FC3, U+2F3D->U+6208, U+2F3E->U+6236, U+2F3F->U+624B, \ + U+2F40->U+652F, U+2F41->U+6534, U+2F42->U+6587, U+2F43->U+6597, U+2F44->U+65A4, U+2F45->U+65B9, U+2F46->U+65E0, U+2F47->U+65E5, U+2F48->U+66F0, U+2F49->U+6708, U+2F4A->U+6728, U+2F4B->U+6B20, U+2F4C->U+6B62, U+2F4D->U+6B79, U+2F4E->U+6BB3, U+2F4F->U+6BCB, \ + U+2F50->U+6BD4, U+2F51->U+6BDB, U+2F52->U+6C0F, U+2F53->U+6C14, U+2F54->U+6C34, U+2F55->U+706B, U+2F56->U+722A, U+2F57->U+7236, U+2F58->U+723B, U+2F59->U+723F, U+2F5A->U+7247, U+2F5B->U+7259, U+2F5C->U+725B, U+2F5D->U+72AC, U+2F5E->U+7384, U+2F5F->U+7389, \ + U+2F60->U+74DC, U+2F61->U+74E6, U+2F62->U+7518, U+2F63->U+751F, U+2F64->U+7528, U+2F65->U+7530, U+2F66->U+758B, U+2F67->U+7592, U+2F68->U+7676, U+2F69->U+767D, U+2F6A->U+76AE, U+2F6B->U+76BF, U+2F6C->U+76EE, U+2F6D->U+77DB, U+2F6E->U+77E2, U+2F6F->U+77F3, \ + U+2F70->U+793A, U+2F71->U+79B8, U+2F72->U+79BE, U+2F73->U+7A74, U+2F74->U+7ACB, U+2F75->U+7AF9, U+2F76->U+7C73, U+2F77->U+7CF8, U+2F78->U+7F36, U+2F79->U+7F51, U+2F7A->U+7F8A, U+2F7B->U+7FBD, U+2F7C->U+8001, U+2F7D->U+800C, U+2F7E->U+8012, U+2F7F->U+8033, \ + U+2F80->U+807F, U+2F81->U+8089, U+2F82->U+81E3, U+2F83->U+81EA, U+2F84->U+81F3, U+2F85->U+81FC, U+2F86->U+820C, U+2F87->U+821B, U+2F88->U+821F, U+2F89->U+826E, U+2F8A->U+8272, U+2F8B->U+8278, U+2F8C->U+864D, U+2F8D->U+866B, U+2F8E->U+8840, U+2F8F->U+884C, \ + U+2F90->U+8863, U+2F91->U+897E, U+2F92->U+898B, U+2F93->U+89D2, U+2F94->U+8A00, U+2F95->U+8C37, U+2F96->U+8C46, U+2F97->U+8C55, U+2F98->U+8C78, U+2F99->U+8C9D, U+2F9A->U+8D64, U+2F9B->U+8D70, U+2F9C->U+8DB3, U+2F9D->U+8EAB, U+2F9E->U+8ECA, U+2F9F->U+8F9B, \ + U+2FA0->U+8FB0, U+2FA1->U+8FB5, U+2FA2->U+9091, U+2FA3->U+9149, U+2FA4->U+91C6, U+2FA5->U+91CC, U+2FA6->U+91D1, U+2FA7->U+9577, U+2FA8->U+9580, U+2FA9->U+961C, U+2FAA->U+96B6, U+2FAB->U+96B9, U+2FAC->U+96E8, U+2FAD->U+9751, U+2FAE->U+975E, U+2FAF->U+9762, \ + U+2FB0->U+9769, U+2FB1->U+97CB, U+2FB2->U+97ED, U+2FB3->U+97F3, U+2FB4->U+9801, U+2FB5->U+98A8, U+2FB6->U+98DB, U+2FB7->U+98DF, U+2FB8->U+9996, U+2FB9->U+9999, U+2FBA->U+99AC, U+2FBB->U+9AA8, U+2FBC->U+9AD8, U+2FBD->U+9ADF, U+2FBE->U+9B25, U+2FBF->U+9B2F, \ + U+2FC0->U+9B32, U+2FC1->U+9B3C, U+2FC2->U+9B5A, U+2FC3->U+9CE5, U+2FC4->U+9E75, U+2FC5->U+9E7F, U+2FC6->U+9EA5, U+2FC7->U+9EBB, U+2FC8->U+9EC3, U+2FC9->U+9ECD, U+2FCA->U+9ED1, U+2FCB->U+9EF9, U+2FCC->U+9EFD, U+2FCD->U+9F0E, U+2FCE->U+9F13, U+2FCF->U+9F20, \ + U+2FD0->U+9F3B, U+2FD1->U+9F4A, U+2FD2->U+9F52, U+2FD3->U+9F8D, U+2FD4->U+9F9C, U+2FD5->U+9FA0, U+3042->U+3041, U+3044->U+3043, U+3046->U+3045, U+3048->U+3047, U+304A->U+3049, U+304C->U+304B, U+304E->U+304D, U+3050->U+304F, U+3052->U+3051, U+3054->U+3053, \ + U+3056->U+3055, U+3058->U+3057, U+305A->U+3059, U+305C->U+305B, U+305E->U+305D, U+3060->U+305F, U+3062->U+3061, U+3064->U+3063, U+3065->U+3063, U+3067->U+3066, U+3069->U+3068, U+3070->U+306F, U+3071->U+306F, U+3073->U+3072, U+3074->U+3072, U+3076->U+3075, \ + U+3077->U+3075, U+3079->U+3078, U+307A->U+3078, U+307C->U+307B, U+307D->U+307B, U+3084->U+3083, U+3086->U+3085, U+3088->U+3087, U+308F->U+308E, U+3094->U+3046, U+3095->U+304B, U+3096->U+3051, U+30A2->U+30A1, U+30A4->U+30A3, U+30A6->U+30A5, U+30A8->U+30A7, \ + U+30AA->U+30A9, U+30AC->U+30AB, U+30AE->U+30AD, U+30B0->U+30AF, U+30B2->U+30B1, U+30B4->U+30B3, U+30B6->U+30B5, U+30B8->U+30B7, U+30BA->U+30B9, U+30BC->U+30BB, U+30BE->U+30BD, U+30C0->U+30BF, U+30C2->U+30C1, U+30C5->U+30C4, U+30C7->U+30C6, U+30C9->U+30C8, \ + U+30D0->U+30CF, U+30D1->U+30CF, U+30D3->U+30D2, U+30D4->U+30D2, U+30D6->U+30D5, U+30D7->U+30D5, U+30D9->U+30D8, U+30DA->U+30D8, U+30DC->U+30DB, U+30DD->U+30DB, U+30E4->U+30E3, U+30E6->U+30E5, U+30E8->U+30E7, U+30EF->U+30EE, U+30F4->U+30A6, U+30AB->U+30F5, \ + U+30B1->U+30F6, U+30F7->U+30EF, U+30F8->U+30F0, U+30F9->U+30F1, U+30FA->U+30F2, U+30AF->U+31F0, U+30B7->U+31F1, U+30B9->U+31F2, U+30C8->U+31F3, U+30CC->U+31F4, U+30CF->U+31F5, U+30D2->U+31F6, U+30D5->U+31F7, U+30D8->U+31F8, U+30DB->U+31F9, U+30E0->U+31FA, \ + U+30E9->U+31FB, U+30EA->U+31FC, U+30EB->U+31FD, U+30EC->U+31FE, U+30ED->U+31FF, U+FF66->U+30F2, U+FF67->U+30A1, U+FF68->U+30A3, U+FF69->U+30A5, U+FF6A->U+30A7, U+FF6B->U+30A9, U+FF6C->U+30E3, U+FF6D->U+30E5, U+FF6E->U+30E7, U+FF6F->U+30C3, U+FF71->U+30A1, \ + U+FF72->U+30A3, U+FF73->U+30A5, U+FF74->U+30A7, U+FF75->U+30A9, U+FF76->U+30AB, U+FF77->U+30AD, U+FF78->U+30AF, U+FF79->U+30B1, U+FF7A->U+30B3, U+FF7B->U+30B5, U+FF7C->U+30B7, U+FF7D->U+30B9, U+FF7E->U+30BB, U+FF7F->U+30BD, U+FF80->U+30BF, U+FF81->U+30C1, \ + U+FF82->U+30C3, U+FF83->U+30C6, U+FF84->U+30C8, U+FF85->U+30CA, U+FF86->U+30CB, U+FF87->U+30CC, U+FF88->U+30CD, U+FF89->U+30CE, U+FF8A->U+30CF, U+FF8B->U+30D2, U+FF8C->U+30D5, U+FF8D->U+30D8, U+FF8E->U+30DB, U+FF8F->U+30DE, U+FF90->U+30DF, U+FF91->U+30E0, \ + U+FF92->U+30E1, U+FF93->U+30E2, U+FF94->U+30E3, U+FF95->U+30E5, U+FF96->U+30E7, U+FF97->U+30E9, U+FF98->U+30EA, U+FF99->U+30EB, U+FF9A->U+30EC, U+FF9B->U+30ED, U+FF9C->U+30EF, U+FF9D->U+30F3, U+FFA0->U+3164, U+FFA1->U+3131, U+FFA2->U+3132, U+FFA3->U+3133, \ + U+FFA4->U+3134, U+FFA5->U+3135, U+FFA6->U+3136, U+FFA7->U+3137, U+FFA8->U+3138, U+FFA9->U+3139, U+FFAA->U+313A, U+FFAB->U+313B, U+FFAC->U+313C, U+FFAD->U+313D, U+FFAE->U+313E, U+FFAF->U+313F, U+FFB0->U+3140, U+FFB1->U+3141, U+FFB2->U+3142, U+FFB3->U+3143, \ + U+FFB4->U+3144, U+FFB5->U+3145, U+FFB6->U+3146, U+FFB7->U+3147, U+FFB8->U+3148, U+FFB9->U+3149, U+FFBA->U+314A, U+FFBB->U+314B, U+FFBC->U+314C, U+FFBD->U+314D, U+FFBE->U+314E, U+FFC2->U+314F, U+FFC3->U+3150, U+FFC4->U+3151, U+FFC5->U+3152, U+FFC6->U+3153, \ + U+FFC7->U+3154, U+FFCA->U+3155, U+FFCB->U+3156, U+FFCC->U+3157, U+FFCD->U+3158, U+FFCE->U+3159, U+FFCF->U+315A, U+FFD2->U+315B, U+FFD3->U+315C, U+FFD4->U+315D, U+FFD5->U+315E, U+FFD6->U+315F, U+FFD7->U+3160, U+FFDA->U+3161, U+FFDB->U+3162, U+FFDC->U+3163, \ + U+3131->U+1100, U+3132->U+1101, U+3133->U+11AA, U+3134->U+1102, U+3135->U+11AC, U+3136->U+11AD, U+3137->U+1103, U+3138->U+1104, U+3139->U+1105, U+313A->U+11B0, U+313B->U+11B1, U+313C->U+11B2, U+313D->U+11B3, U+313E->U+11B4, U+313F->U+11B5, U+3140->U+111A, \ + U+3141->U+1106, U+3142->U+1107, U+3143->U+1108, U+3144->U+1121, U+3145->U+1109, U+3146->U+110A, U+3147->U+110B, U+3148->U+110C, U+3149->U+110D, U+314A->U+110E, U+314B->U+110F, U+314C->U+1110, U+314D->U+1111, U+314E->U+1112, U+314F->U+1161, U+3150->U+1162, \ + U+3151->U+1163, U+3152->U+1164, U+3153->U+1165, U+3154->U+1166, U+3155->U+1167, U+3156->U+1168, U+3157->U+1169, U+3158->U+116A, U+3159->U+116B, U+315A->U+116C, U+315B->U+116D, U+315C->U+116E, U+315D->U+116F, U+315E->U+1170, U+315F->U+1171, U+3160->U+1172, \ + U+3161->U+1173, U+3162->U+1174, U+3163->U+1175, U+3165->U+1114, U+3166->U+1115, U+3167->U+11C7, U+3168->U+11C8, U+3169->U+11CC, U+316A->U+11CE, U+316B->U+11D3, U+316C->U+11D7, U+316D->U+11D9, U+316E->U+111C, U+316F->U+11DD, U+3170->U+11DF, U+3171->U+111D, \ + U+3172->U+111E, U+3173->U+1120, U+3174->U+1122, U+3175->U+1123, U+3176->U+1127, U+3177->U+1129, U+3178->U+112B, U+3179->U+112C, U+317A->U+112D, U+317B->U+112E, U+317C->U+112F, U+317D->U+1132, U+317E->U+1136, U+317F->U+1140, U+3180->U+1147, U+3181->U+114C, \ + U+3182->U+11F1, U+3183->U+11F2, U+3184->U+1157, U+3185->U+1158, U+3186->U+1159, U+3187->U+1184, U+3188->U+1185, U+3189->U+1188, U+318A->U+1191, U+318B->U+1192, U+318C->U+1194, U+318D->U+119E, U+318E->U+11A1, U+A490->U+A408, U+A491->U+A1B9, U+4E00..U+9FBB, \ + U+3400..U+4DB5, U+20000..U+2A6D6, U+FA0E, U+FA0F, U+FA11, U+FA13, U+FA14, U+FA1F, U+FA21, U+FA23, U+FA24, U+FA27, U+FA28, U+FA29, U+3105..U+312C, U+31A0..U+31B7, U+3041, U+3043, U+3045, U+3047, U+3049, U+304B, U+304D, U+304F, U+3051, U+3053, U+3055, U+3057, \ + U+3059, U+305B, U+305D, U+305F, U+3061, U+3063, U+3066, U+3068, U+306A..U+306F, U+3072, U+3075, U+3078, U+307B, U+307E..U+3083, U+3085, U+3087, U+3089..U+308E, U+3090..U+3093, U+30A1, U+30A3, U+30A5, U+30A7, U+30A9, U+30AD, U+30AF, U+30B3, U+30B5, U+30BB, \ + U+30BD, U+30BF, U+30C1, U+30C3, U+30C4, U+30C6, U+30CA, U+30CB, U+30CD, U+30CE, U+30DE, U+30DF, U+30E1, U+30E2, U+30E3, U+30E5, U+30E7, U+30EE, U+30F0..U+30F3, U+30F5, U+30F6, U+31F0, U+31F1, U+31F2, U+31F3, U+31F4, U+31F5, U+31F6, U+31F7, U+31F8, U+31F9, \ + U+31FA, U+31FB, U+31FC, U+31FD, U+31FE, U+31FF, U+AC00..U+D7A3, U+1100..U+1159, U+1161..U+11A2, U+11A8..U+11F9, U+A000..U+A48C, U+A492..U+A4C6, U+2C80->U+2C81, U+2C81, U+2C82->U+2C83, U+2C83, U+2C84->U+2C85, U+2C85, U+2C86->U+2C87, U+2C87, U+2C88->U+2C89, \ + U+2C89, U+2C8A->U+2C8B, U+2C8B, U+2C8C->U+2C8D, U+2C8D, U+2C8E->U+2C8F, U+2C8F, U+2C90->U+2C91, U+2C91, U+2C92->U+2C93, U+2C93, U+2C94->U+2C95, U+2C95, U+2C96->U+2C97, U+2C97, U+2C98->U+2C99, U+2C99, U+2C9A->U+2C9B, U+2C9B, U+2C9C->U+2C9D, U+2C9D, U+2C9E->U+2C9F, \ + U+2C9F, U+2CA0->U+2CA1, U+2CA1, U+2CA2->U+2CA3, U+2CA3, U+2CA4->U+2CA5, U+2CA5, U+2CA6->U+2CA7, U+2CA7, U+2CA8->U+2CA9, U+2CA9, U+2CAA->U+2CAB, U+2CAB, U+2CAC->U+2CAD, U+2CAD, U+2CAE->U+2CAF, U+2CAF, U+2CB0->U+2CB1, U+2CB1, U+2CB2->U+2CB3, U+2CB3, U+2CB4->U+2CB5, \ + U+2CB5, U+2CB6->U+2CB7, U+2CB7, U+2CB8->U+2CB9, U+2CB9, U+2CBA->U+2CBB, U+2CBB, U+2CBC->U+2CBD, U+2CBD, U+2CBE->U+2CBF, U+2CBF, U+2CC0->U+2CC1, U+2CC1, U+2CC2->U+2CC3, U+2CC3, U+2CC4->U+2CC5, U+2CC5, U+2CC6->U+2CC7, U+2CC7, U+2CC8->U+2CC9, U+2CC9, U+2CCA->U+2CCB, \ + U+2CCB, U+2CCC->U+2CCD, U+2CCD, U+2CCE->U+2CCF, U+2CCF, U+2CD0->U+2CD1, U+2CD1, U+2CD2->U+2CD3, U+2CD3, U+2CD4->U+2CD5, U+2CD5, U+2CD6->U+2CD7, U+2CD7, U+2CD8->U+2CD9, U+2CD9, U+2CDA->U+2CDB, U+2CDB, U+2CDC->U+2CDD, U+2CDD, U+2CDE->U+2CDF, U+2CDF, U+2CE0->U+2CE1, \ + U+2CE1, U+2CE2->U+2CE3, U+2CE3, U+0400->U+0435, U+0401->U+0435, U+0402->U+0452, U+0452, U+0403->U+0433, U+0404->U+0454, U+0454, U+0405->U+0455, U+0455, U+0406->U+0456, U+0407->U+0456, U+0457->U+0456, U+0456, U+0408..U+040B->U+0458..U+045B, U+0458..U+045B, \ + U+040C->U+043A, U+040D->U+0438, U+040E->U+0443, U+040F->U+045F, U+045F, U+0450->U+0435, U+0451->U+0435, U+0453->U+0433, U+045C->U+043A, U+045D->U+0438, U+045E->U+0443, U+0460->U+0461, U+0461, U+0462->U+0463, U+0463, U+0464->U+0465, U+0465, U+0466->U+0467, \ + U+0467, U+0468->U+0469, U+0469, U+046A->U+046B, U+046B, U+046C->U+046D, U+046D, U+046E->U+046F, U+046F, U+0470->U+0471, U+0471, U+0472->U+0473, U+0473, U+0474->U+0475, U+0476->U+0475, U+0477->U+0475, U+0475, U+0478->U+0479, U+0479, U+047A->U+047B, U+047B, \ + U+047C->U+047D, U+047D, U+047E->U+047F, U+047F, U+0480->U+0481, U+0481, U+048A->U+0438, U+048B->U+0438, U+048C->U+044C, U+048D->U+044C, U+048E->U+0440, U+048F->U+0440, U+0490->U+0433, U+0491->U+0433, U+0490->U+0433, U+0491->U+0433, U+0492->U+0433, U+0493->U+0433, \ + U+0494->U+0433, U+0495->U+0433, U+0496->U+0436, U+0497->U+0436, U+0498->U+0437, U+0499->U+0437, U+049A->U+043A, U+049B->U+043A, U+049C->U+043A, U+049D->U+043A, U+049E->U+043A, U+049F->U+043A, U+04A0->U+043A, U+04A1->U+043A, U+04A2->U+043D, U+04A3->U+043D, \ + U+04A4->U+043D, U+04A5->U+043D, U+04A6->U+043F, U+04A7->U+043F, U+04A8->U+04A9, U+04A9, U+04AA->U+0441, U+04AB->U+0441, U+04AC->U+0442, U+04AD->U+0442, U+04AE->U+0443, U+04AF->U+0443, U+04B0->U+0443, U+04B1->U+0443, U+04B2->U+0445, U+04B3->U+0445, U+04B4->U+04B5, \ + U+04B5, U+04B6->U+0447, U+04B7->U+0447, U+04B8->U+0447, U+04B9->U+0447, U+04BA->U+04BB, U+04BB, U+04BC->U+04BD, U+04BE->U+04BD, U+04BF->U+04BD, U+04BD, U+04C0->U+04CF, U+04CF, U+04C1->U+0436, U+04C2->U+0436, U+04C3->U+043A, U+04C4->U+043A, U+04C5->U+043B, \ + U+04C6->U+043B, U+04C7->U+043D, U+04C8->U+043D, U+04C9->U+043D, U+04CA->U+043D, U+04CB->U+0447, U+04CC->U+0447, U+04CD->U+043C, U+04CE->U+043C, U+04D0->U+0430, U+04D1->U+0430, U+04D2->U+0430, U+04D3->U+0430, U+04D4->U+00E6, U+04D5->U+00E6, U+04D6->U+0435, \ + U+04D7->U+0435, U+04D8->U+04D9, U+04DA->U+04D9, U+04DB->U+04D9, U+04D9, U+04DC->U+0436, U+04DD->U+0436, U+04DE->U+0437, U+04DF->U+0437, U+04E0->U+04E1, U+04E1, U+04E2->U+0438, U+04E3->U+0438, U+04E4->U+0438, U+04E5->U+0438, U+04E6->U+043E, U+04E7->U+043E, \ + U+04E8->U+043E, U+04E9->U+043E, U+04EA->U+043E, U+04EB->U+043E, U+04EC->U+044D, U+04ED->U+044D, U+04EE->U+0443, U+04EF->U+0443, U+04F0->U+0443, U+04F1->U+0443, U+04F2->U+0443, U+04F3->U+0443, U+04F4->U+0447, U+04F5->U+0447, U+04F6->U+0433, U+04F7->U+0433, \ + U+04F8->U+044B, U+04F9->U+044B, U+04FA->U+0433, U+04FB->U+0433, U+04FC->U+0445, U+04FD->U+0445, U+04FE->U+0445, U+04FF->U+0445, U+0410..U+0418->U+0430..U+0438, U+0419->U+0438, U+0430..U+0438, U+041A..U+042F->U+043A..U+044F, U+043A..U+044F, U+0929->U+0928, \ + U+0931->U+0930, U+0934->U+0933, U+0958->U+0915, U+0959->U+0916, U+095A->U+0917, U+095B->U+091C, U+095C->U+0921, U+095D->U+0922, U+095E->U+092B, U+095F->U+092F, U+0904..U+0928, U+092A..U+0930, U+0932, U+0933, U+0935..U+0939, U+0960, U+0961, U+0966..U+096F, \ + U+097B..U+097F, U+10FC->U+10DC, U+10D0..U+10FA, U+10A0..U+10C5->U+2D00..U+2D25, U+2D00..U+2D25, U+0386->U+03B1, U+0388->U+03B5, U+0389->U+03B7, U+038A->U+03B9, U+038C->U+03BF, U+038E->U+03C5, U+038F->U+03C9, U+0390->U+03B9, U+03AA->U+03B9, U+03AB->U+03C5, \ + U+03AC->U+03B1, U+03AD->U+03B5, U+03AE->U+03B7, U+03AF->U+03B9, U+03B0->U+03C5, U+03CA->U+03B9, U+03CB->U+03C5, U+03CC->U+03BF, U+03CD->U+03C5, U+03CE->U+03C9, U+03D0->U+03B2, U+03D1->U+03B8, U+03D2->U+03C5, U+03D3->U+03C5, U+03D4->U+03C5, U+03D5->U+03C6, \ + U+03D6->U+03C0, U+03D8->U+03D9, U+03DA->U+03DB, U+03DC->U+03DD, U+03DE->U+03DF, U+03E0->U+03E1, U+03E2->U+03E3, U+03E4->U+03E5, U+03E6->U+03E7, U+03E8->U+03E9, U+03EA->U+03EB, U+03EC->U+03ED, U+03EE->U+03EF, U+03F0->U+03BA, U+03F1->U+03C1, U+03F2->U+03C3, \ + U+03F4->U+03B8, U+03F5->U+03B5, U+03F6->U+03B5, U+03F7->U+03F8, U+03F9->U+03C3, U+03FA->U+03FB, U+1F00->U+03B1, U+1F01->U+03B1, U+1F02->U+03B1, U+1F03->U+03B1, U+1F04->U+03B1, U+1F05->U+03B1, U+1F06->U+03B1, U+1F07->U+03B1, U+1F08->U+03B1, U+1F09->U+03B1, \ + U+1F0A->U+03B1, U+1F0B->U+03B1, U+1F0C->U+03B1, U+1F0D->U+03B1, U+1F0E->U+03B1, U+1F0F->U+03B1, U+1F10->U+03B5, U+1F11->U+03B5, U+1F12->U+03B5, U+1F13->U+03B5, U+1F14->U+03B5, U+1F15->U+03B5, U+1F18->U+03B5, U+1F19->U+03B5, U+1F1A->U+03B5, U+1F1B->U+03B5, \ + U+1F1C->U+03B5, U+1F1D->U+03B5, U+1F20->U+03B7, U+1F21->U+03B7, U+1F22->U+03B7, U+1F23->U+03B7, U+1F24->U+03B7, U+1F25->U+03B7, U+1F26->U+03B7, U+1F27->U+03B7, U+1F28->U+03B7, U+1F29->U+03B7, U+1F2A->U+03B7, U+1F2B->U+03B7, U+1F2C->U+03B7, U+1F2D->U+03B7, \ + U+1F2E->U+03B7, U+1F2F->U+03B7, U+1F30->U+03B9, U+1F31->U+03B9, U+1F32->U+03B9, U+1F33->U+03B9, U+1F34->U+03B9, U+1F35->U+03B9, U+1F36->U+03B9, U+1F37->U+03B9, U+1F38->U+03B9, U+1F39->U+03B9, U+1F3A->U+03B9, U+1F3B->U+03B9, U+1F3C->U+03B9, U+1F3D->U+03B9, \ + U+1F3E->U+03B9, U+1F3F->U+03B9, U+1F40->U+03BF, U+1F41->U+03BF, U+1F42->U+03BF, U+1F43->U+03BF, U+1F44->U+03BF, U+1F45->U+03BF, U+1F48->U+03BF, U+1F49->U+03BF, U+1F4A->U+03BF, U+1F4B->U+03BF, U+1F4C->U+03BF, U+1F4D->U+03BF, U+1F50->U+03C5, U+1F51->U+03C5, \ + U+1F52->U+03C5, U+1F53->U+03C5, U+1F54->U+03C5, U+1F55->U+03C5, U+1F56->U+03C5, U+1F57->U+03C5, U+1F59->U+03C5, U+1F5B->U+03C5, U+1F5D->U+03C5, U+1F5F->U+03C5, U+1F60->U+03C9, U+1F61->U+03C9, U+1F62->U+03C9, U+1F63->U+03C9, U+1F64->U+03C9, U+1F65->U+03C9, \ + U+1F66->U+03C9, U+1F67->U+03C9, U+1F68->U+03C9, U+1F69->U+03C9, U+1F6A->U+03C9, U+1F6B->U+03C9, U+1F6C->U+03C9, U+1F6D->U+03C9, U+1F6E->U+03C9, U+1F6F->U+03C9, U+1F70->U+03B1, U+1F71->U+03B1, U+1F72->U+03B5, U+1F73->U+03B5, U+1F74->U+03B7, U+1F75->U+03B7, \ + U+1F76->U+03B9, U+1F77->U+03B9, U+1F78->U+03BF, U+1F79->U+03BF, U+1F7A->U+03C5, U+1F7B->U+03C5, U+1F7C->U+03C9, U+1F7D->U+03C9, U+1F80->U+03B1, U+1F81->U+03B1, U+1F82->U+03B1, U+1F83->U+03B1, U+1F84->U+03B1, U+1F85->U+03B1, U+1F86->U+03B1, U+1F87->U+03B1, \ + U+1F88->U+03B1, U+1F89->U+03B1, U+1F8A->U+03B1, U+1F8B->U+03B1, U+1F8C->U+03B1, U+1F8D->U+03B1, U+1F8E->U+03B1, U+1F8F->U+03B1, U+1F90->U+03B7, U+1F91->U+03B7, U+1F92->U+03B7, U+1F93->U+03B7, U+1F94->U+03B7, U+1F95->U+03B7, U+1F96->U+03B7, U+1F97->U+03B7, \ + U+1F98->U+03B7, U+1F99->U+03B7, U+1F9A->U+03B7, U+1F9B->U+03B7, U+1F9C->U+03B7, U+1F9D->U+03B7, U+1F9E->U+03B7, U+1F9F->U+03B7, U+1FA0->U+03C9, U+1FA1->U+03C9, U+1FA2->U+03C9, U+1FA3->U+03C9, U+1FA4->U+03C9, U+1FA5->U+03C9, U+1FA6->U+03C9, U+1FA7->U+03C9, \ + U+1FA8->U+03C9, U+1FA9->U+03C9, U+1FAA->U+03C9, U+1FAB->U+03C9, U+1FAC->U+03C9, U+1FAD->U+03C9, U+1FAE->U+03C9, U+1FAF->U+03C9, U+1FB0->U+03B1, U+1FB1->U+03B1, U+1FB2->U+03B1, U+1FB3->U+03B1, U+1FB4->U+03B1, U+1FB6->U+03B1, U+1FB7->U+03B1, U+1FB8->U+03B1, \ + U+1FB9->U+03B1, U+1FBA->U+03B1, U+1FBB->U+03B1, U+1FBC->U+03B1, U+1FC2->U+03B7, U+1FC3->U+03B7, U+1FC4->U+03B7, U+1FC6->U+03B7, U+1FC7->U+03B7, U+1FC8->U+03B5, U+1FC9->U+03B5, U+1FCA->U+03B7, U+1FCB->U+03B7, U+1FCC->U+03B7, U+1FD0->U+03B9, U+1FD1->U+03B9, \ + U+1FD2->U+03B9, U+1FD3->U+03B9, U+1FD6->U+03B9, U+1FD7->U+03B9, U+1FD8->U+03B9, U+1FD9->U+03B9, U+1FDA->U+03B9, U+1FDB->U+03B9, U+1FE0->U+03C5, U+1FE1->U+03C5, U+1FE2->U+03C5, U+1FE3->U+03C5, U+1FE4->U+03C1, U+1FE5->U+03C1, U+1FE6->U+03C5, U+1FE7->U+03C5, \ + U+1FE8->U+03C5, U+1FE9->U+03C5, U+1FEA->U+03C5, U+1FEB->U+03C5, U+1FEC->U+03C1, U+1FF2->U+03C9, U+1FF3->U+03C9, U+1FF4->U+03C9, U+1FF6->U+03C9, U+1FF7->U+03C9, U+1FF8->U+03BF, U+1FF9->U+03BF, U+1FFA->U+03C9, U+1FFB->U+03C9, U+1FFC->U+03C9, U+0391..U+03A1->U+03B1..U+03C1, \ + U+03B1..U+03C1, U+03A3..U+03A9->U+03C3..U+03C9, U+03C3..U+03C9, U+03C2, U+03D9, U+03DB, U+03DD, U+03DF, U+03E1, U+03E3, U+03E5, U+03E7, U+03E9, U+03EB, U+03ED, U+03EF, U+03F3, U+03F8, U+03FB, U+0A85..U+0A8C, U+0A8F, U+0A90, U+0A93..U+0AB0, U+0AB2, U+0AB3, \ + U+0AB5..U+0AB9, U+0AE0, U+0AE1, U+0AE6..U+0AEF, U+0A33->U+0A32, U+0A36->U+0A38, U+0A59->U+0A16, U+0A5A->U+0A17, U+0A5B->U+0A1C, U+0A5E->U+0A2B, U+0A05..U+0A0A, U+0A0F, U+0A10, U+0A13..U+0A28, U+0A2A..U+0A30, U+0A32, U+0A35, U+0A38, U+0A39, U+0A5C, U+0A66..U+0A6F, \ + U+FB1D->U+05D9, U+FB1F->U+05F2, U+FB20->U+05E2, U+FB21->U+05D0, U+FB22->U+05D3, U+FB23->U+05D4, U+FB24->U+05DB, U+FB25->U+05DC, U+FB26->U+05DD, U+FB27->U+05E8, U+FB28->U+05EA, U+FB2A->U+05E9, U+FB2B->U+05E9, U+FB2C->U+05E9, U+FB2D->U+05E9, U+FB2E->U+05D0, \ + U+FB2F->U+05D0, U+FB30->U+05D0, U+FB31->U+05D1, U+FB32->U+05D2, U+FB33->U+05D3, U+FB34->U+05D4, U+FB35->U+05D5, U+FB36->U+05D6, U+FB38->U+05D8, U+FB39->U+05D9, U+FB3A->U+05DA, U+FB3B->U+05DB, U+FB3C->U+05DC, U+FB3E->U+05DE, U+FB40->U+05E0, U+FB41->U+05E1, \ + U+FB43->U+05E3, U+FB44->U+05E4, U+FB46->U+05E6, U+FB47->U+05E7, U+FB48->U+05E8, U+FB49->U+05E9, U+FB4A->U+05EA, U+FB4B->U+05D5, U+FB4C->U+05D1, U+FB4D->U+05DB, U+FB4E->U+05E4, U+FB4F->U+05D0, U+05D0..U+05F2, U+0C85..U+0C8C, U+0C8E..U+0C90, U+0C92..U+0CA8, \ + U+0CAA..U+0CB3, U+0CB5..U+0CB9, U+0CE0, U+0CE1, U+0CE6..U+0CEF, U+1900..U+191C, U+1930..U+1938, U+1946..U+194F, U+0D05..U+0D0C, U+0D0E..U+0D10, U+0D12..U+0D28, U+0D2A..U+0D39, U+0D60, U+0D61, U+0D66..U+0D6F, U+0B94->U+0B92, U+0B85..U+0B8A, U+0B8E..U+0B90, \ + U+0B92, U+0B93, U+0B95, U+0B99, U+0B9A, U+0B9C, U+0B9E, U+0B9F, U+0BA3, U+0BA4, U+0BA8..U+0BAA, U+0BAE..U+0BB9, U+0BE6..U+0BEF, U+0E01..U+0E30, U+0E32, U+0E33, U+0E40..U+0E46, U+0E50..U+0E5B, U+FF10..U+FF19->0..9, U+FF21..U+FF3A->a..z, U+FF41..U+FF5A->a..z, \ + 0..9, A..Z->a..z, a..z, _ + blend_chars = !, ", U+23, $, %, &, ', (, ), *, +, U+2C, -, ., /, :, U+3B, <, =, >, ?, @, U+5B, U+5C, U+5D, ^, U+60, U+7C, U+7E, U+A1..U+BF + blend_mode = trim_none, trim_head, trim_tail, trim_both +} +source delta : torrents_base { + sql_query = SELECT *, Year AS yearfulltext FROM sphinx_delta WHERE Size > 0; + sql_query_killlist = SELECT ID FROM sphinx_delta +} +index delta : torrents { + source = delta + path = /var/lib/sphinxsearch/data/delta +} +source requests_base : connect { + sql_attr_uint = UserID + sql_attr_uint = TimeAdded + sql_attr_uint = LastVote + sql_attr_uint = CategoryID + sql_attr_uint = Year + sql_attr_uint = ReleaseType + sql_attr_uint = FillerID + sql_attr_uint = TorrentID + sql_attr_uint = TimeFilled + sql_attr_uint = Visible + sql_attr_uint = Votes + sql_attr_uint = Bounty +} +source requests : requests_base { + sql_query_pre = TRUNCATE TABLE sphinx_requests + sql_query_pre = SET group_concat_max_len = 10140 + sql_query_pre = SET @starttime = NOW() + sql_query_pre = REPLACE INTO sphinx_index_last_pos VALUES ('requests', UNIX_TIMESTAMP(@starttime)) + sql_query_pre = INSERT INTO sphinx_requests ( \ + ID, UserID, TimeAdded, LastVote, CategoryID, Title, \ + Year, ReleaseType, RecordLabel, CatalogueNumber, \ + BitrateList, FormatList, MediaList, LogCue, FillerID, \ + TorrentID, TimeFilled, Visible, Votes, Bounty ) \ + SELECT \ + r.ID, r.UserID, UNIX_TIMESTAMP(TimeAdded), \ + UNIX_TIMESTAMP(LastVote), CategoryID, Title, Year, \ + ReleaseType, RecordLabel, CatalogueNumber, BitrateList, \ + FormatList, MediaList, LogCue, FillerID, TorrentID, \ + UNIX_TIMESTAMP(TimeFilled), Visible, \ + COUNT(rv.RequestID), SUM(rv.Bounty) >> 10 \ + FROM requests AS r \ + JOIN requests_votes AS rv ON rv.RequestID = r.ID \ + GROUP BY rv.RequestID + sql_query_pre = INSERT INTO sphinx_requests ( \ + ID, ArtistList ) \ + SELECT \ + RequestID, \ + GROUP_CONCAT(aa.Name SEPARATOR ' ') \ + FROM requests_artists AS ra \ + JOIN artists_alias AS aa ON aa.AliasID = ra.AliasID \ + JOIN requests AS r ON r.ID = ra.RequestID \ + WHERE TimeAdded <= @starttime \ + GROUP BY r.ID \ + ON DUPLICATE KEY UPDATE ArtistList = VALUES(ArtistList) + sql_query = SELECT ID, UserID, TimeAdded, LastVote, CategoryID, Title, \ + Year, ArtistList, ReleaseType, RecordLabel, CatalogueNumber, \ + BitrateList, FormatList, MediaList, LogCue, FillerID, \ + TorrentID, TimeFilled, Visible, Votes, Bounty, \ + Year AS YearFullText \ + FROM sphinx_requests + sql_joined_field = taglist from query; \ + SELECT rt.RequestID, REPLACE(t.Name, '.', '_') \ + FROM requests_tags AS rt \ + JOIN tags AS t ON TagID = ID \ + ORDER BY requestid ASC; + sql_attr_multi = uint Voter from query; \ + SELECT RequestID AS ID, UserID FROM requests_votes + sql_attr_multi = uint Bookmarker from query; \ + SELECT RequestID AS ID, UserID FROM bookmarks_requests + sql_query_post_index = DELETE FROM sphinx_requests_delta WHERE TimeAdded <= \ + (SELECT ID FROM sphinx_index_last_pos WHERE type = 'requests') +} +source requests_delta : requests_base { + sql_query = SELECT ID, UserID, TimeAdded, LastVote, CategoryID, Title, TagList, \ + Year, ArtistList, ReleaseType, RecordLabel, CatalogueNumber, \ + BitrateList, FormatList, MediaList, LogCue, FillerID, \ + TorrentID, TimeFilled, Visible, Votes, Bounty, \ + Year AS YearFullText \ + FROM sphinx_requests_delta + sql_query_killlist = SELECT ID FROM sphinx_requests_delta + sql_attr_multi = uint Voter from query; \ + SELECT v.RequestID, v.UserID FROM requests_votes AS v \ + JOIN sphinx_requests_delta AS d ON d.ID = v.RequestID + sql_attr_multi = uint Bookmarker from query; \ + SELECT b.RequestID, b.UserID FROM bookmarks_requests AS b \ + JOIN sphinx_requests_delta AS d ON d.ID = b.RequestID +} +index requests : torrents { + source = requests + path = /var/lib/sphinxsearch/data/requests + infix_fields = taglist + min_infix_len = 3 +} +index requests_delta : requests { + source = requests_delta + path = /var/lib/sphinxsearch/data/requests_delta +} +source log : connect { + sql_attr_uint = Time + sql_query = SELECT ID, UNIX_TIMESTAMP(Time) AS Time, Message FROM log + sql_query_post_index = REPLACE INTO sphinx_index_last_pos VALUES ('log', $maxid) +} +source log_delta : log { + sql_query_pre = SELECT ID FROM sphinx_index_last_pos WHERE type = 'log' INTO @lastid + sql_query = SELECT ID, UNIX_TIMESTAMP(Time) AS Time, Message FROM log WHERE ID > @lastid + sql_query_post_index = SET @nothing = 0 +} +index log : torrents { + source = log + path = /var/lib/sphinxsearch/data/log + min_word_len = 1 + min_infix_len = 0 + infix_fields = +} +index log_delta : log { + source = log_delta + path = /var/lib/sphinxsearch/data/log_delta +} +source better_transcode : connect { + sql_attr_uint = logscore + sql_attr_uint = groupid + sql_attr_uint = uploader + sql_query = SELECT t.id, groupid, logscore, format, encoding, \ + name AS groupname, tags AS taglist, year, uploader, remident \ + FROM (SELECT t.id, gid AS groupid, uid uploader, remident, \ + IF(media='cd', MAX(t.logscore), 100) AS logscore, \ + GROUP_CONCAT(DISTINCT format SEPARATOR ' ') \ + AS format, \ + GROUP_CONCAT(DISTINCT encoding SEPARATOR ' ') \ + AS encoding \ + FROM sphinx_t AS t \ + WHERE format IN ('MP3', 'FLAC') \ + GROUP BY gid, remident) AS t \ + JOIN sphinx_tg AS g ON g.id = groupid \ + WHERE catid = 1 + sql_joined_field = artistname from query; \ + SELECT t.id, a.aname \ + FROM sphinx_a AS a\ + JOIN sphinx_t AS t USING(gid) \ + ORDER BY t.id ASC +} +index better_transcode : torrents { + source = better_transcode + path = /var/lib/sphinxsearch/data/better_transcode + phrase_boundary = + min_infix_len = 0 + infix_fields = +} + +indexer { + mem_limit = 128M +} + +searchd { + listen = 9312 + listen = 9306:mysql41 + log = /var/log/sphinxsearch/searchd.log + query_log = /var/log/sphinxsearch/query.log + pid_file = /var/run/sphinxsearch/searchd.pid + mva_updates_pool = 1M + #compat_sphinxql_magics = 0 +} diff --git a/.docker/web/config.php b/.docker/web/config.php new file mode 100644 index 000000000..dfa695b61 --- /dev/null +++ b/.docker/web/config.php @@ -0,0 +1,283 @@ + '/var/run/memcached.sock', 'port' => 0), + ['host' => 'memcached', 'port' => 11211] +); + +// Sphinx details +define('SPHINX_HOST', 'sphinxsearch'); +define('SPHINX_PORT', 9312); +define('SPHINXQL_HOST', 'sphinxsearch'); +define('SPHINXQL_PORT', 9306); +define('SPHINXQL_SOCK', false); +define('SPHINX_MAX_MATCHES', 1000); // Must be <= the server's max_matches variable (default 1000) +define('SPHINX_INDEX', 'torrents'); + +// Ocelot details +define('DISABLE_TRACKER', true); +define('TRACKER_HOST', 'localhost'); +define('TRACKER_PORT', 34000); +define('TRACKER_SECRET', '1737853d77069dc24824916a8d0e501e'); // Must be 32 characters and match site_password in Ocelot's config.cpp +define('TRACKER_REPORTKEY', '1737853d77069dc24824916a8d0e501e'); // Must be 32 characters and match report_password in Ocelot's config.cpp + + +// This is special to the vagrant setup as we are not using the default 80 port and at the same time cannot use SSL +define('SITE_URL', NONSSL_SITE_URL); +define('STATIC_SERVER', NONSSL_STATIC_SERVER); + +/*if (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 80) { + define('SITE_URL', NONSSL_SITE_URL); + define('STATIC_SERVER', NONSSL_STATIC_SERVER); +} else { + define('SITE_URL', SSL_SITE_URL); + define('STATIC_SERVER', SSL_STATIC_SERVER); +}*/ + +// Site settings +define('CRYPT_HASH_PREFIX', '$2y$07$'); +define('DEBUG_MODE', true); //Set to false if you dont want everyone to see debug information, can be overriden with 'site_debug' +define('DEBUG_WARNINGS', true); //Set to true if you want to see PHP warnings in the footer +define('SHOW_PUBLIC_INDEX', true); // Show the public index.php landing page +define('OPEN_REGISTRATION', true); //Set to false to disable open regirstration, true to allow anyone to register +define('USER_LIMIT', 40000); //The maximum number of users the site can have, 0 for no limit +define('STARTING_INVITES', 0); //# of invites to give to newly registered users +define('STARTING_UPLOAD', 3221225472); //Upload given to newly registered users, in bytes using IEC standard (1024 bytes per KiB) +define('REQUEST_TAX', 0.0); //Percentage Tax (0 - 1) to charge users on making requests +define('BLOCK_TOR', false); //Set to true to block Tor users +define('BLOCK_OPERA_MINI', false); //Set to true to block Opera Mini proxy +define('DONOR_INVITES', 2); +define('SYSTEM_USER_ID', 1); +define('TRASH_FORUM_ID', 4); +define('EDITING_FORUM_ID', 34); +define('EDITING_TRASH_FORUM_ID', 48); + + +if (!defined('FEATURE_EMAIL_REENABLE')) { + define('FEATURE_EMAIL_REENABLE', true); +} + +// User class IDs needed for automatic promotions. Found in the 'permissions' table +// Name of class Class ID (NOT level) +define('ADMIN', '40'); +define('USER', '2'); +define('MEMBER', '3'); +define('POWER', '4'); +define('ELITE', '5'); +define('VIP', '26'); +define('TORRENT_MASTER','25'); +define('LEGEND', '27'); +define('CELEB', '31'); +define('MOD', '11'); +define('CODER', '24'); +define('LEAD_DEV', '43'); +define('SYSOP', '15'); +define('ARTIST', '19'); +define('DONOR', '20'); +define('FLS_TEAM', '23'); +define('POWER_TM', '29'); +define('ELITE_TM', '28'); +define('FORUM_MOD', '21'); +define('TORRENT_MOD', '22'); +define('INTERVIEWER', '30'); +define('DESIGNER', '32'); +define('SECURITY', '33'); +define('IRC', '34'); +define('SHADOW', '35'); +define('ALPHA', '36'); +define('BRAVO', '37'); +define('CHARLIE', '38'); +define('DELTA', '39'); +define('RECRUITER', '41'); + +// Pagination +define('TORRENT_COMMENTS_PER_PAGE', 10); +define('POSTS_PER_PAGE', 25); +define('TOPICS_PER_PAGE', 50); +define('TORRENTS_PER_PAGE', 50); +define('REQUESTS_PER_PAGE', 25); +define('MESSAGES_PER_PAGE', 25); +define('LOG_ENTRIES_PER_PAGE', 50); + +// Cache catalogues +define('THREAD_CATALOGUE', 500); // Limit to THREAD_CATALOGUE posts per cache key. + +// IRC settings +define('DISABLE_IRC', true); +define('BOT_NICK', ''); +define('BOT_SERVER', ''); // IRC server address. Used for onsite chat tool. +define('BOT_PORT', 6667); +define('BOT_CHAN', '#'.NONSSL_SITE_URL); +define('BOT_ANNOUNCE_CHAN', '#'); +define('BOT_STAFF_CHAN', '#'); +define('BOT_DISABLED_CHAN', '#'); // Channel to refer disabled users to. +define('BOT_HELP_CHAN', '#'); +define('BOT_DEBUG_CHAN', '#'); +define('BOT_REPORT_CHAN', '#'); +define('BOT_NICKSERV_PASS', ''); +define('BOT_INVITE_CHAN',BOT_CHAN.'-invites'); // Channel for non-members seeking an interview +define('BOT_INTERVIEW_CHAN',BOT_CHAN.'-interview'); // Channel for the interviews +define('BOT_INTERVIEW_NUM',5); +define('BOT_INTERVIEW_STAFF',BOT_CHAN.'-interviewers'); // Channel for the interviewers +define('SOCKET_LISTEN_PORT', 51010); +define('SOCKET_LISTEN_ADDRESS', 'localhost'); +define('ADMIN_CHAN', '#'); +define('LAB_CHAN', '#'); +define('STATUS_CHAN', '#'); + +// Miscellaneous values +define('RANK_ONE_COST', 5); +define('RANK_TWO_COST', 10); +define('RANK_THREE_COST', 15); +define('RANK_FOUR_COST', 20); +define('RANK_FIVE_COST', 30); +define('MAX_RANK', 6); +define('MAX_EXTRA_RANK', 8); +define('DONOR_FORUM_RANK', 6); +define('DONOR_FORUM', 70); +define('MAX_SPECIAL_RANK', 3); + +$ForumsRevealVoters = array(); +$ForumsDoublePost = array(); + +$Categories = array('Music', 'Applications', 'E-Books', 'Audiobooks', 'E-Learning Videos', 'Comedy', 'Comics'); +$GroupedCategories = array_intersect(array('Music'), $Categories); +$CategoryIcons = array('music.png', 'apps.png', 'ebook.png', 'audiobook.png', 'elearning.png', 'comedy.png', 'comics.png'); + +$Formats = array('MP3', 'FLAC', 'AAC', 'AC3', 'DTS'); +$Bitrates = array('192', 'APS (VBR)', 'V2 (VBR)', 'V1 (VBR)', '256', 'APX (VBR)', 'V0 (VBR)', 'q8.x (VBR)', '320', 'Lossless', '24bit Lossless', 'Other'); +$Media = array('CD', 'DVD', 'Vinyl', 'Soundboard', 'SACD', 'DAT', 'Cassette', 'WEB'); + +$CollageCats = array(0=>'Personal', 1=>'Theme', 2=>'Genre introduction', 3=>'Discography', 4=>'Label', 5=>'Staff picks', 6=>'Charts', 7=>'Artists'); + +$ReleaseTypes = array(1=>'Album', 3=>'Soundtrack', 5=>'EP', 6=>'Anthology', 7=>'Compilation', 9=>'Single', 11=>'Live album', 13=>'Remix', 14=>'Bootleg', 15=>'Interview', 16=>'Mixtape', 21=>'Unknown'); +//$ForumCats = array(1=>'Site', 5=>'Community', 10=>'Help', 8=>'Music', 20=>'Trash'); //No longer needed + +$ZIPGroups = array( + 0 => 'MP3 (VBR) - High Quality', + 1 => 'MP3 (VBR) - Low Quality', + 2 => 'MP3 (CBR)', + 3 => 'FLAC - Lossless', + 4 => 'Others' +); + +//3D array of attributes, OptionGroup, OptionNumber, Name +$ZIPOptions = array( + '00' => array(0,0,'V0'), + '01' => array(0,1,'APX'), + '02' => array(0,2,'256'), + '03' => array(0,3,'V1'), + '10' => array(1,0,'224'), + '11' => array(1,1,'V2'), + '12' => array(1,2,'APS'), + '13' => array(1,3,'192'), + '20' => array(2,0,'320'), + '21' => array(2,1,'256'), + '22' => array(2,2,'224'), + '23' => array(2,3,'192'), + '30' => array(3,0,'FLAC / 24bit / Vinyl'), + '31' => array(3,1,'FLAC / 24bit / DVD'), + '32' => array(3,2,'FLAC / 24bit / SACD'), + '33' => array(3,3,'FLAC / Log (100) / Cue'), + '34' => array(3,4,'FLAC / Log (100)'), + '35' => array(3,5,'FLAC / Log'), + '36' => array(3,6,'FLAC'), + '40' => array(4,0,'DTS'), + '41' => array(4,1,'Ogg Vorbis'), + '42' => array(4,2,'AAC - 320'), + '43' => array(4,3,'AAC - 256'), + '44' => array(4,4,'AAC - q5.5'), + '45' => array(4,5,'AAC - q5'), + '46' => array(4,6,'AAC - 192') +); + +// Ratio requirements, in descending order +// Columns: Download amount, required ratio, grace period +$RatioRequirements = array( + array(50 * 1024 * 1024 * 1024, 0.60, date('Y-m-d H:i:s')), + array(40 * 1024 * 1024 * 1024, 0.50, date('Y-m-d H:i:s')), + array(30 * 1024 * 1024 * 1024, 0.40, date('Y-m-d H:i:s')), + array(20 * 1024 * 1024 * 1024, 0.30, date('Y-m-d H:i:s')), + array(10 * 1024 * 1024 * 1024, 0.20, date('Y-m-d H:i:s')), + array(5 * 1024 * 1024 * 1024, 0.15, date('Y-m-d H:i:s', time() - (60 * 60 * 24 * 14))) +); + +//Captcha fonts should be located in /classes/fonts +$CaptchaFonts = array( + 'ARIBLK.TTF', + 'IMPACT.TTF', + 'TREBUC.TTF', + 'TREBUCBD.TTF', + 'TREBUCBI.TTF', + 'TREBUCIT.TTF', + 'VERDANA.TTF', + 'VERDANAB.TTF', + 'VERDANAI.TTF', + 'VERDANAZ.TTF'); +//Captcha images should be located in /captcha +$CaptchaBGs = array( + 'captcha1.png', + 'captcha2.png', + 'captcha3.png', + 'captcha4.png', + 'captcha5.png', + 'captcha6.png', + 'captcha7.png', + 'captcha8.png', + 'captcha9.png'); + +// Special characters, and what they should be converted to +// Used for torrent searching +$SpecialChars = array( + '&' => 'and' +); + +// Deny cache access to keys without specified permission +$CachePermissions = [ + 'api_apps' => 'site_debug', + 'catalogue' => 'site_debug' +]; diff --git a/.docker/web/crontab b/.docker/web/crontab new file mode 100644 index 000000000..c68d2a2a1 --- /dev/null +++ b/.docker/web/crontab @@ -0,0 +1,2 @@ +*/15 * * * * /usr/bin/php /var/www/schedule.php OL9n0m2JxhBxYyMvXWJg >> /tmp/schedule.log +10-59/15 * * * * /usr/bin/php /var/www/peerupdate.php OL9n0m2JxhBxYyMvXWJg >> /tmp/peerupdate.log diff --git a/.docker/web/entrypoint.sh b/.docker/web/entrypoint.sh new file mode 100644 index 000000000..178b6d8be --- /dev/null +++ b/.docker/web/entrypoint.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +# Wait for MySQL... +counter=1 +while ! mysql -h mysql -ugazelle -ppassword -e "show databases;" > /dev/null 2>&1; do + sleep 1 + counter=`expr $counter + 1` + if [ $(($counter % 20)) -eq 0 ]; then + mysql -h mysql -ugazelle -ppassword -e "show databases;" + >&2 echo "Still waiting for MySQL (Count: ${counter})." + fi; +done + +if [ ! -f /srv/www/classes/config.php ]; then + cp /var/www/.docker/web/config.php /var/www/classes/config.php +fi + +echo "Run migrate..." +/var/www/vendor/bin/phinx migrate + +if [ ! -f /srv/gazelle.txt ]; then + touch /srv/gazelle.txt + /var/www/vendor/bin/phinx seed:run -s InitialUserSeeder +fi + +echo "Start services..." +service cron start +service nginx start +service php7.3-fpm start + +crontab /var/www/.docker/web/crontab + +tail -f /var/log/nginx/access.log diff --git a/.docker/web/gazelle-setup.sh b/.docker/web/gazelle-setup.sh new file mode 100755 index 000000000..a35552595 --- /dev/null +++ b/.docker/web/gazelle-setup.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash + +####### +# This script, and the contents of this directory, are to be used primarily for setting +# up and building a Vagrant environment in Debian Jessie. While you could use this script +# as a starting point for a production server, you must be aware that you need to edit +# the files here, in at least the following ways (but also probably others): +# 1) Don't use eatmydata before any apt-gets if you care about the server's files +# 2) READ ALL PARTS OF config.php TO KNOW WHAT IT DOES AND CONTROLS!! +# 3) Make sure to edit config.php to have unique values for the passwords and secrets +# 4) Turn off DEBUG_MODE, and set DISABLE_TRACKER and DISABLE_IRC to false if using them +# 5) Modify your server's php.ini file to what you want, you don't need the one in .vagrant +# +# And no, config.php is not the config.php that we run on the live Orpheus site. +####### + + +# This script is intended to be run as root. +if [[ $EUID -ne 0 ]]; then + echo "This script must be run as root" + exit +fi + +if [ -f ~/.runonce ] +then + echo "Gazelle setup already run, skipping..." + exit +fi +touch ~/.runonce + +export DEBIAN_FRONTEND=noninteractive +echo "deb http://ftp.debian.org/debian stretch-backports main" >> /etc/apt/sources.list + +# Don't do this in production +apt-get install -y eatmydata + +eatmydata apt-get update + +# install basic stuff that we need for potential later operations +eatmydata apt-get install -y software-properties-common build-essential make libssl-dev zlib1g-dev libbz2-dev libsqlite3-dev libboost-dev libtcmalloc-minimal4 unzip wget curl netcat-openbsd imagemagick +curl -sL https://deb.nodesource.com/setup_10.x | bash - +eatmydata apt-get install -y nodejs + +# TODO: Remove default mailer, install nullmailer and PostOffice +# We can remove the default MTA (exim4) as it doesn't do anything that really helps us on our sever. However, we do +# want to install sendmail (but not let it run) so that PHP can send mail. +# eatmydata apt-get remove -y exim4 exim4-base exim4-config exim4-daemon-light +# rm -rf /var/log/exim4 + +eatmydata apt-get install -y git nginx memcached nodejs +eatmydata apt-get install -y python3 +wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py +python3 /tmp/get-pip.py +rm -f /tmp/get-pip.py +pip3 install -U pip +pip3 install chardet eac-logchecker xld-logchecker + +eatmydata apt-get install -y sphinxsearch +eatmydata apt-get install -y php7.0 php7.0-fpm php7.0-mysql php7.0-cli php7.0-gd php7.0-curl php7.0-mbstring php7.0-xml php7.0-zip php-memcached php-xdebug php-apcu + +debconf-set-selections <<< 'mariadb-server mysql-server/root_password password em%G9Lrey4^N' +debconf-set-selections <<< 'mariadb-server mysql-server/root_password_again password em%G9Lrey4^N' +eatmydata apt-get install -y mariadb-server mariadb-client + +npm install --global --unsafe-perm puppeteer +chmod -R o+rx /usr/lib/node_modules/puppeteer/.local-chromium + +pushd /var/www/sections/tools/development +npm link puppeteer +popd + +rm -r /etc/nginx/sites-enabled/default +rm -f /etc/nginx/sites-available/default +cp /var/www/.vagrant/nginx.conf /etc/nginx/sites-available/gazelle.conf +cp /var/www/.vagrant/php.ini /etc/php/7.0/cli/php.ini +cp /var/www/.vagrant/php.ini /etc/php/7.0/fpm/php.ini +cp /var/www/.vagrant/xdebug.ini /etc/php/7.0/mods-available/xdebug.ini +cp /var/www/.vagrant/www.conf /etc/php/7.0/fpm/pool.d/www.conf +cp /var/www/.vagrant/config.php /var/www/classes/config.php + +ln -s /etc/nginx/sites-available/gazelle.conf /etc/nginx/sites-enabled/gazelle.conf + +#mysql -uroot -pem%G9Lrey4^N < /var/www/.vagrant/gazelle.sql +mysql -uroot -pem%G9Lrey4^N -e "CREATE DATABASE gazelle CHARACTER SET utf8 COLLATE utf8_swedish_ci;" +mysql -uroot -pem%G9Lrey4^N -e "CREATE USER 'gazelle'@'%' IDENTIFIED BY 'password';" +mysql -uroot -pem%G9Lrey4^N -e "GRANT ALL ON *.* TO 'gazelle'@'%';" +mysql -uroot -pem%G9Lrey4^N -e "FLUSH PRIVILEGES;" +sed -i "s/^bind-address/\# bind-address/" /etc/mysql/my.cnf +sed -i "s/^skip-external-locking/\# skip-external-locking/" /etc/mysql/my.cnf + +# Setup composer +php -r "copy('https://getcomposer.org/installer', '/tmp/composer-setup.php');" +EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig) +ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', '/tmp/composer-setup.php');") +if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]; then + >&2 echo 'ERROR: Invalid installer signature' +else + php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer + su vagrant -c "composer --version" + pushd /var/www + su vagrant -c "composer install" + su vagrant -c "composer dump-autoload" + su vagrant -c "vendor/bin/phinx migrate" + su vagrant -c "vendor/bin/phinx seed:run -s InitialUserSeeder" + popd +fi +rm -f /tmp/composer-setup.php + +echo "START=yes" | tee /etc/default/sphinxsearch > /dev/null +cp /var/www/.vagrant/sphinx.conf /etc/sphinxsearch/sphinx.conf +indexer -c /etc/sphinxsearch/sphinx.conf --all + +cp /var/www/.vagrant/init.d/* /etc/init.d +chmod +x /etc/init.d/memcached.sock +update-rc.d memcached.sock defaults + +crontab /var/www/.vagrant/crontab + +if [ -d /var/ocelot ]; then + cp /var/www/.vagrant/config.cpp /var/ocelot/config.cpp + #cd /var/ocelot + #./configure + #make + #screen -S ocelot -dm /var/ocelot/ocelot +fi; + +service mysql restart +service memcached.sock restart +service sphinxsearch restart +service php7.0-fpm restart +service nginx restart diff --git a/.docker/web/nginx.conf b/.docker/web/nginx.conf new file mode 100644 index 000000000..9960f2db5 --- /dev/null +++ b/.docker/web/nginx.conf @@ -0,0 +1,57 @@ +## +# You should look at the following URL's in order to grasp a solid understanding +# of Nginx configuration files in order to fully unleash the power of Nginx. +# http://wiki.nginx.org/Pitfalls +# http://wiki.nginx.org/QuickStart +# http://wiki.nginx.org/Configuration +# +# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples. +## + +server { + listen 80; ## listen for ipv4; this line is default and implied + listen [::]:80 default_server ipv6only=on; ## listen for ipv6 + + root /var/www; + index index.html index.htm index.php; + + server_name localhost; + + location / { + try_files $uri $uri/ /index.php; + } + + location /logs/ { + add_header Content-Type text/plain; + } + + location /static/userscripts/ { + add_header Content-Type text/plain; + } + + location ~ \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + try_files $fastcgi_script_name @missing; + set $path_info $fastcgi_path_info; + + fastcgi_param PATH_INFO $path_info; + fastcgi_pass unix:/var/run/php/php-fpm.sock; + fastcgi_index index.php; + include fastcgi.conf; + } + + location @missing { + fastcgi_pass unix:/var/run/php/php-fpm.sock; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + set $path_info $fastcgi_path_info; + fastcgi_param PATH_INFO $path_info; + + fastcgi_index index.php; + include fastcgi.conf; + fastcgi_param SCRIPT_FILENAME "${document_root}/index.php"; + } + + location ~* ^.+\.(js|css)$ { + expires -1; + } +} diff --git a/.docker/web/php.ini b/.docker/web/php.ini new file mode 100644 index 000000000..4f0c652bf --- /dev/null +++ b/.docker/web/php.ini @@ -0,0 +1,1918 @@ +[PHP] + +;;;;;;;;;;;;;;;;;;; +; About php.ini ; +;;;;;;;;;;;;;;;;;;; +; PHP's initialization file, generally called php.ini, is responsible for +; configuring many of the aspects of PHP's behavior. + +; PHP attempts to find and load this configuration from a number of locations. +; The following is a summary of its search order: +; 1. SAPI module specific location. +; 2. The PHPRC environment variable. (As of PHP 5.2.0) +; 3. A number of predefined registry keys on Windows (As of PHP 5.2.0) +; 4. Current working directory (except CLI) +; 5. The web server's directory (for SAPI modules), or directory of PHP +; (otherwise in Windows) +; 6. The directory from the --with-config-file-path compile time option, or the +; Windows directory (C:\windows or C:\winnt) +; See the PHP docs for more specific information. +; http://php.net/configuration.file + +; The syntax of the file is extremely simple. Whitespace and lines +; beginning with a semicolon are silently ignored (as you probably guessed). +; Section headers (e.g. [Foo]) are also silently ignored, even though +; they might mean something in the future. + +; Directives following the section heading [PATH=/www/mysite] only +; apply to PHP files in the /www/mysite directory. Directives +; following the section heading [HOST=www.example.com] only apply to +; PHP files served from www.example.com. Directives set in these +; special sections cannot be overridden by user-defined INI files or +; at runtime. Currently, [PATH=] and [HOST=] sections only work under +; CGI/FastCGI. +; http://php.net/ini.sections + +; Directives are specified using the following syntax: +; directive = value +; Directive names are *case sensitive* - foo=bar is different from FOO=bar. +; Directives are variables used to configure PHP or PHP extensions. +; There is no name validation. If PHP can't find an expected +; directive because it is not set or is mistyped, a default value will be used. + +; The value can be a string, a number, a PHP constant (e.g. E_ALL or M_PI), one +; of the INI constants (On, Off, True, False, Yes, No and None) or an expression +; (e.g. E_ALL & ~E_NOTICE), a quoted string ("bar"), or a reference to a +; previously set variable or directive (e.g. ${foo}) + +; Expressions in the INI file are limited to bitwise operators and parentheses: +; | bitwise OR +; ^ bitwise XOR +; & bitwise AND +; ~ bitwise NOT +; ! boolean NOT + +; Boolean flags can be turned on using the values 1, On, True or Yes. +; They can be turned off using the values 0, Off, False or No. + +; An empty string can be denoted by simply not writing anything after the equal +; sign, or by using the None keyword: + +; foo = ; sets foo to an empty string +; foo = None ; sets foo to an empty string +; foo = "None" ; sets foo to the string 'None' + +; If you use constants in your value, and these constants belong to a +; dynamically loaded extension (either a PHP extension or a Zend extension), +; you may only use these constants *after* the line that loads the extension. + +;;;;;;;;;;;;;;;;;;; +; About this file ; +;;;;;;;;;;;;;;;;;;; +; PHP comes packaged with two INI files. One that is recommended to be used +; in production environments and one that is recommended to be used in +; development environments. + +; php.ini-production contains settings which hold security, performance and +; best practices at its core. But please be aware, these settings may break +; compatibility with older or less security conscience applications. We +; recommending using the production ini in production and testing environments. + +; php.ini-development is very similar to its production variant, except it is +; much more verbose when it comes to errors. We recommend using the +; development version only in development environments, as errors shown to +; application users can inadvertently leak otherwise secure information. + +; This is php.ini-production INI file. + +;;;;;;;;;;;;;;;;;;; +; Quick Reference ; +;;;;;;;;;;;;;;;;;;; +; The following are all the settings which are different in either the production +; or development versions of the INIs with respect to PHP's default behavior. +; Please see the actual settings later in the document for more details as to why +; we recommend these changes in PHP's behavior. + +; display_errors +; Default Value: On +; Development Value: On +; Production Value: Off + +; display_startup_errors +; Default Value: Off +; Development Value: On +; Production Value: Off + +; error_reporting +; Default Value: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED +; Development Value: E_ALL +; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT + +; html_errors +; Default Value: On +; Development Value: On +; Production value: On + +; log_errors +; Default Value: Off +; Development Value: On +; Production Value: On + +; max_input_time +; Default Value: -1 (Unlimited) +; Development Value: 60 (60 seconds) +; Production Value: 60 (60 seconds) + +; output_buffering +; Default Value: Off +; Development Value: 4096 +; Production Value: 4096 + +; register_argc_argv +; Default Value: On +; Development Value: Off +; Production Value: Off + +; request_order +; Default Value: None +; Development Value: "GP" +; Production Value: "GP" + +; session.gc_divisor +; Default Value: 100 +; Development Value: 1000 +; Production Value: 1000 + +; session.hash_bits_per_character +; Default Value: 4 +; Development Value: 5 +; Production Value: 5 + +; short_open_tag +; Default Value: On +; Development Value: Off +; Production Value: Off + +; track_errors +; Default Value: Off +; Development Value: On +; Production Value: Off + +; url_rewriter.tags +; Default Value: "a=href,area=href,frame=src,form=,fieldset=" +; Development Value: "a=href,area=href,frame=src,input=src,form=fakeentry" +; Production Value: "a=href,area=href,frame=src,input=src,form=fakeentry" + +; variables_order +; Default Value: "EGPCS" +; Development Value: "GPCS" +; Production Value: "GPCS" + +;;;;;;;;;;;;;;;;;;;; +; php.ini Options ; +;;;;;;;;;;;;;;;;;;;; +; Name for user-defined php.ini (.htaccess) files. Default is ".user.ini" +;user_ini.filename = ".user.ini" + +; To disable this feature set this option to empty value +;user_ini.filename = + +; TTL for user-defined php.ini files (time-to-live) in seconds. Default is 300 seconds (5 minutes) +;user_ini.cache_ttl = 300 + +;;;;;;;;;;;;;;;;;;;; +; Language Options ; +;;;;;;;;;;;;;;;;;;;; + +; Enable the PHP scripting language engine under Apache. +; http://php.net/engine +engine = On + +; This directive determines whether or not PHP will recognize code between +; tags as PHP source which should be processed as such. It is +; generally recommended that should be used and that this feature +; should be disabled, as enabling it may result in issues when generating XML +; documents, however this remains supported for backward compatibility reasons. +; Note that this directive does not control the would work. +; http://php.net/syntax-highlighting +;highlight.string = #DD0000 +;highlight.comment = #FF9900 +;highlight.keyword = #007700 +;highlight.default = #0000BB +;highlight.html = #000000 + +; If enabled, the request will be allowed to complete even if the user aborts +; the request. Consider enabling it if executing long requests, which may end up +; being interrupted by the user or a browser timing out. PHP's default behavior +; is to disable this feature. +; http://php.net/ignore-user-abort +;ignore_user_abort = On + +; Determines the size of the realpath cache to be used by PHP. This value should +; be increased on systems where PHP opens many files to reflect the quantity of +; the file operations performed. +; http://php.net/realpath-cache-size +;realpath_cache_size = 4096k + +; Duration of time, in seconds for which to cache realpath information for a given +; file or directory. For systems with rarely changing files, consider increasing this +; value. +; http://php.net/realpath-cache-ttl +;realpath_cache_ttl = 120 + +; Enables or disables the circular reference collector. +; http://php.net/zend.enable-gc +zend.enable_gc = On + +; If enabled, scripts may be written in encodings that are incompatible with +; the scanner. CP936, Big5, CP949 and Shift_JIS are the examples of such +; encodings. To use this feature, mbstring extension must be enabled. +; Default: Off +;zend.multibyte = Off + +; Allows to set the default encoding for the scripts. This value will be used +; unless "declare(encoding=...)" directive appears at the top of the script. +; Only affects if zend.multibyte is set. +; Default: "" +;zend.script_encoding = + +;;;;;;;;;;;;;;;;; +; Miscellaneous ; +;;;;;;;;;;;;;;;;; + +; Decides whether PHP may expose the fact that it is installed on the server +; (e.g. by adding its signature to the Web server header). It is no security +; threat in any way, but it makes it possible to determine whether you use PHP +; on your server or not. +; http://php.net/expose-php +expose_php = Off + +;;;;;;;;;;;;;;;;;;; +; Resource Limits ; +;;;;;;;;;;;;;;;;;;; + +; Maximum execution time of each script, in seconds +; http://php.net/max-execution-time +; Note: This directive is hardcoded to 0 for the CLI SAPI +max_execution_time = 30 + +; Maximum amount of time each script may spend parsing request data. It's a good +; idea to limit this time on productions servers in order to eliminate unexpectedly +; long running scripts. +; Note: This directive is hardcoded to -1 for the CLI SAPI +; Default Value: -1 (Unlimited) +; Development Value: 60 (60 seconds) +; Production Value: 60 (60 seconds) +; http://php.net/max-input-time +max_input_time = 60 + +; Maximum input variable nesting level +; http://php.net/max-input-nesting-level +;max_input_nesting_level = 64 + +; How many GET/POST/COOKIE input variables may be accepted +; max_input_vars = 1000 + +; Maximum amount of memory a script may consume (128MB) +; http://php.net/memory-limit +memory_limit = 128M + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Error handling and logging ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; This directive informs PHP of which errors, warnings and notices you would like +; it to take action for. The recommended way of setting values for this +; directive is through the use of the error level constants and bitwise +; operators. The error level constants are below here for convenience as well as +; some common settings and their meanings. +; By default, PHP is set to take action on all errors, notices and warnings EXCEPT +; those related to E_NOTICE and E_STRICT, which together cover best practices and +; recommended coding standards in PHP. For performance reasons, this is the +; recommend error reporting setting. Your production server shouldn't be wasting +; resources complaining about best practices and coding standards. That's what +; development servers and development settings are for. +; Note: The php.ini-development file has this setting as E_ALL. This +; means it pretty much reports everything which is exactly what you want during +; development and early testing. +; +; Error Level Constants: +; E_ALL - All errors and warnings (includes E_STRICT as of PHP 5.4.0) +; E_ERROR - fatal run-time errors +; E_RECOVERABLE_ERROR - almost fatal run-time errors +; E_WARNING - run-time warnings (non-fatal errors) +; E_PARSE - compile-time parse errors +; E_NOTICE - run-time notices (these are warnings which often result +; from a bug in your code, but it's possible that it was +; intentional (e.g., using an uninitialized variable and +; relying on the fact it is automatically initialized to an +; empty string) +; E_STRICT - run-time notices, enable to have PHP suggest changes +; to your code which will ensure the best interoperability +; and forward compatibility of your code +; E_CORE_ERROR - fatal errors that occur during PHP's initial startup +; E_CORE_WARNING - warnings (non-fatal errors) that occur during PHP's +; initial startup +; E_COMPILE_ERROR - fatal compile-time errors +; E_COMPILE_WARNING - compile-time warnings (non-fatal errors) +; E_USER_ERROR - user-generated error message +; E_USER_WARNING - user-generated warning message +; E_USER_NOTICE - user-generated notice message +; E_DEPRECATED - warn about code that will not work in future versions +; of PHP +; E_USER_DEPRECATED - user-generated deprecation warnings +; +; Common Values: +; E_ALL (Show all errors, warnings and notices including coding standards.) +; E_ALL & ~E_NOTICE (Show all errors, except for notices) +; E_ALL & ~E_NOTICE & ~E_STRICT (Show all errors, except for notices and coding standards warnings.) +; E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR (Show only errors) +; Default Value: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED +; Development Value: E_ALL +; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT +; http://php.net/error-reporting +error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT + +; This directive controls whether or not and where PHP will output errors, +; notices and warnings too. Error output is very useful during development, but +; it could be very dangerous in production environments. Depending on the code +; which is triggering the error, sensitive information could potentially leak +; out of your application such as database usernames and passwords or worse. +; For production environments, we recommend logging errors rather than +; sending them to STDOUT. +; Possible Values: +; Off = Do not display any errors +; stderr = Display errors to STDERR (affects only CGI/CLI binaries!) +; On or stdout = Display errors to STDOUT +; Default Value: On +; Development Value: On +; Production Value: Off +; http://php.net/display-errors +display_errors = On + +; The display of errors which occur during PHP's startup sequence are handled +; separately from display_errors. PHP's default behavior is to suppress those +; errors from clients. Turning the display of startup errors on can be useful in +; debugging configuration problems. We strongly recommend you +; set this to 'off' for production servers. +; Default Value: Off +; Development Value: On +; Production Value: Off +; http://php.net/display-startup-errors +display_startup_errors = Off + +; Besides displaying errors, PHP can also log errors to locations such as a +; server-specific log, STDERR, or a location specified by the error_log +; directive found below. While errors should not be displayed on productions +; servers they should still be monitored and logging is a great way to do that. +; Default Value: Off +; Development Value: On +; Production Value: On +; http://php.net/log-errors +log_errors = On + +; Set maximum length of log_errors. In error_log information about the source is +; added. The default is 1024 and 0 allows to not apply any maximum length at all. +; http://php.net/log-errors-max-len +log_errors_max_len = 1024 + +; Do not log repeated messages. Repeated errors must occur in same file on same +; line unless ignore_repeated_source is set true. +; http://php.net/ignore-repeated-errors +ignore_repeated_errors = Off + +; Ignore source of message when ignoring repeated messages. When this setting +; is On you will not log errors with repeated messages from different files or +; source lines. +; http://php.net/ignore-repeated-source +ignore_repeated_source = Off + +; If this parameter is set to Off, then memory leaks will not be shown (on +; stdout or in the log). This has only effect in a debug compile, and if +; error reporting includes E_WARNING in the allowed list +; http://php.net/report-memleaks +report_memleaks = On + +; This setting is on by default. +;report_zend_debug = 0 + +; Store the last error/warning message in $php_errormsg (boolean). Setting this value +; to On can assist in debugging and is appropriate for development servers. It should +; however be disabled on production servers. +; Default Value: Off +; Development Value: On +; Production Value: Off +; http://php.net/track-errors +track_errors = Off + +; Turn off normal error reporting and emit XML-RPC error XML +; http://php.net/xmlrpc-errors +;xmlrpc_errors = 0 + +; An XML-RPC faultCode +;xmlrpc_error_number = 0 + +; When PHP displays or logs an error, it has the capability of formatting the +; error message as HTML for easier reading. This directive controls whether +; the error message is formatted as HTML or not. +; Note: This directive is hardcoded to Off for the CLI SAPI +; Default Value: On +; Development Value: On +; Production value: On +; http://php.net/html-errors +html_errors = On + +; If html_errors is set to On *and* docref_root is not empty, then PHP +; produces clickable error messages that direct to a page describing the error +; or function causing the error in detail. +; You can download a copy of the PHP manual from http://php.net/docs +; and change docref_root to the base URL of your local copy including the +; leading '/'. You must also specify the file extension being used including +; the dot. PHP's default behavior is to leave these settings empty, in which +; case no links to documentation are generated. +; Note: Never use this feature for production boxes. +; http://php.net/docref-root +; Examples +;docref_root = "/phpmanual/" + +; http://php.net/docref-ext +;docref_ext = .html + +; String to output before an error message. PHP's default behavior is to leave +; this setting blank. +; http://php.net/error-prepend-string +; Example: +;error_prepend_string = "" + +; String to output after an error message. PHP's default behavior is to leave +; this setting blank. +; http://php.net/error-append-string +; Example: +;error_append_string = "" + +; Log errors to specified file. PHP's default behavior is to leave this value +; empty. +; http://php.net/error-log +; Example: +;error_log = php_errors.log +; Log errors to syslog (Event Log on Windows). +;error_log = syslog + +;windows.show_crt_warning +; Default value: 0 +; Development value: 0 +; Production value: 0 + +;;;;;;;;;;;;;;;;; +; Data Handling ; +;;;;;;;;;;;;;;;;; + +; The separator used in PHP generated URLs to separate arguments. +; PHP's default setting is "&". +; http://php.net/arg-separator.output +; Example: +;arg_separator.output = "&" + +; List of separator(s) used by PHP to parse input URLs into variables. +; PHP's default setting is "&". +; NOTE: Every character in this directive is considered as separator! +; http://php.net/arg-separator.input +; Example: +;arg_separator.input = ";&" + +; This directive determines which super global arrays are registered when PHP +; starts up. G,P,C,E & S are abbreviations for the following respective super +; globals: GET, POST, COOKIE, ENV and SERVER. There is a performance penalty +; paid for the registration of these arrays and because ENV is not as commonly +; used as the others, ENV is not recommended on productions servers. You +; can still get access to the environment variables through getenv() should you +; need to. +; Default Value: "EGPCS" +; Development Value: "GPCS" +; Production Value: "GPCS"; +; http://php.net/variables-order +variables_order = "GPCS" + +; This directive determines which super global data (G,P & C) should be +; registered into the super global array REQUEST. If so, it also determines +; the order in which that data is registered. The values for this directive +; are specified in the same manner as the variables_order directive, +; EXCEPT one. Leaving this value empty will cause PHP to use the value set +; in the variables_order directive. It does not mean it will leave the super +; globals array REQUEST empty. +; Default Value: None +; Development Value: "GP" +; Production Value: "GP" +; http://php.net/request-order +request_order = "GP" + +; This directive determines whether PHP registers $argv & $argc each time it +; runs. $argv contains an array of all the arguments passed to PHP when a script +; is invoked. $argc contains an integer representing the number of arguments +; that were passed when the script was invoked. These arrays are extremely +; useful when running scripts from the command line. When this directive is +; enabled, registering these variables consumes CPU cycles and memory each time +; a script is executed. For performance reasons, this feature should be disabled +; on production servers. +; Note: This directive is hardcoded to On for the CLI SAPI +; Default Value: On +; Development Value: Off +; Production Value: Off +; http://php.net/register-argc-argv +register_argc_argv = Off + +; When enabled, the ENV, REQUEST and SERVER variables are created when they're +; first used (Just In Time) instead of when the script starts. If these +; variables are not used within a script, having this directive on will result +; in a performance gain. The PHP directive register_argc_argv must be disabled +; for this directive to have any affect. +; http://php.net/auto-globals-jit +auto_globals_jit = On + +; Whether PHP will read the POST data. +; This option is enabled by default. +; Most likely, you won't want to disable this option globally. It causes $_POST +; and $_FILES to always be empty; the only way you will be able to read the +; POST data will be through the php://input stream wrapper. This can be useful +; to proxy requests or to process the POST data in a memory efficient fashion. +; http://php.net/enable-post-data-reading +;enable_post_data_reading = Off + +; Maximum size of POST data that PHP will accept. +; Its value may be 0 to disable the limit. It is ignored if POST data reading +; is disabled through enable_post_data_reading. +; http://php.net/post-max-size +post_max_size = 8M + +; Automatically add files before PHP document. +; http://php.net/auto-prepend-file +auto_prepend_file = + +; Automatically add files after PHP document. +; http://php.net/auto-append-file +auto_append_file = + +; By default, PHP will output a media type using the Content-Type header. To +; disable this, simply set it to be empty. +; +; PHP's built-in default media type is set to text/html. +; http://php.net/default-mimetype +default_mimetype = "text/html" + +; PHP's default character set is set to UTF-8. +; http://php.net/default-charset +default_charset = "UTF-8" + +; PHP internal character encoding is set to empty. +; If empty, default_charset is used. +; http://php.net/internal-encoding +;internal_encoding = + +; PHP input character encoding is set to empty. +; If empty, default_charset is used. +; http://php.net/input-encoding +;input_encoding = + +; PHP output character encoding is set to empty. +; If empty, default_charset is used. +; See also output_buffer. +; http://php.net/output-encoding +;output_encoding = + +;;;;;;;;;;;;;;;;;;;;;;;;; +; Paths and Directories ; +;;;;;;;;;;;;;;;;;;;;;;;;; + +; UNIX: "/path1:/path2" +;include_path = ".:/usr/share/php" +; +; Windows: "\path1;\path2" +;include_path = ".;c:\php\includes" +; +; PHP's default setting for include_path is ".;/path/to/php/pear" +; http://php.net/include-path + +; The root of the PHP pages, used only if nonempty. +; if PHP was not compiled with FORCE_REDIRECT, you SHOULD set doc_root +; if you are running php as a CGI under any web server (other than IIS) +; see documentation for security issues. The alternate is to use the +; cgi.force_redirect configuration below +; http://php.net/doc-root +doc_root = + +; The directory under which PHP opens the script using /~username used only +; if nonempty. +; http://php.net/user-dir +user_dir = + +; Directory in which the loadable extensions (modules) reside. +; http://php.net/extension-dir +; extension_dir = "./" +; On windows: +; extension_dir = "ext" + +; Directory where the temporary files should be placed. +; Defaults to the system default (see sys_get_temp_dir) +; sys_temp_dir = "/tmp" + +; Whether or not to enable the dl() function. The dl() function does NOT work +; properly in multithreaded servers, such as IIS or Zeus, and is automatically +; disabled on them. +; http://php.net/enable-dl +enable_dl = Off + +; cgi.force_redirect is necessary to provide security running PHP as a CGI under +; most web servers. Left undefined, PHP turns this on by default. You can +; turn it off here AT YOUR OWN RISK +; **You CAN safely turn this off for IIS, in fact, you MUST.** +; http://php.net/cgi.force-redirect +;cgi.force_redirect = 1 + +; if cgi.nph is enabled it will force cgi to always sent Status: 200 with +; every request. PHP's default behavior is to disable this feature. +;cgi.nph = 1 + +; if cgi.force_redirect is turned on, and you are not running under Apache or Netscape +; (iPlanet) web servers, you MAY need to set an environment variable name that PHP +; will look for to know it is OK to continue execution. Setting this variable MAY +; cause security issues, KNOW WHAT YOU ARE DOING FIRST. +; http://php.net/cgi.redirect-status-env +;cgi.redirect_status_env = + +; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI. PHP's +; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok +; what PATH_INFO is. For more information on PATH_INFO, see the cgi specs. Setting +; this to 1 will cause PHP CGI to fix its paths to conform to the spec. A setting +; of zero causes PHP to behave as before. Default is 1. You should fix your scripts +; to use SCRIPT_FILENAME rather than PATH_TRANSLATED. +; http://php.net/cgi.fix-pathinfo +;cgi.fix_pathinfo=1 + +; if cgi.discard_path is enabled, the PHP CGI binary can safely be placed outside +; of the web tree and people will not be able to circumvent .htaccess security. +; http://php.net/cgi.dicard-path +;cgi.discard_path=1 + +; FastCGI under IIS (on WINNT based OS) supports the ability to impersonate +; security tokens of the calling client. This allows IIS to define the +; security context that the request runs under. mod_fastcgi under Apache +; does not currently support this feature (03/17/2002) +; Set to 1 if running under IIS. Default is zero. +; http://php.net/fastcgi.impersonate +;fastcgi.impersonate = 1 + +; Disable logging through FastCGI connection. PHP's default behavior is to enable +; this feature. +;fastcgi.logging = 0 + +; cgi.rfc2616_headers configuration option tells PHP what type of headers to +; use when sending HTTP response code. If set to 0, PHP sends Status: header that +; is supported by Apache. When this option is set to 1, PHP will send +; RFC2616 compliant header. +; Default is zero. +; http://php.net/cgi.rfc2616-headers +;cgi.rfc2616_headers = 0 + +; cgi.check_shebang_line controls whether CGI PHP checks for line starting with #! +; (shebang) at the top of the running script. This line might be needed if the +; script support running both as stand-alone script and via PHP CGI<. PHP in CGI +; mode skips this line and ignores its content if this directive is turned on. +; http://php.net/cgi.check-shebang-line +;cgi.check_shebang_line=1 + +;;;;;;;;;;;;;;;; +; File Uploads ; +;;;;;;;;;;;;;;;; + +; Whether to allow HTTP file uploads. +; http://php.net/file-uploads +file_uploads = On + +; Temporary directory for HTTP uploaded files (will use system default if not +; specified). +; http://php.net/upload-tmp-dir +;upload_tmp_dir = + +; Maximum allowed size for uploaded files. +; http://php.net/upload-max-filesize +upload_max_filesize = 2M + +; Maximum number of files that can be uploaded via a single request +max_file_uploads = 20 + +;;;;;;;;;;;;;;;;;; +; Fopen wrappers ; +;;;;;;;;;;;;;;;;;; + +; Whether to allow the treatment of URLs (like http:// or ftp://) as files. +; http://php.net/allow-url-fopen +allow_url_fopen = On + +; Whether to allow include/require to open URLs (like http:// or ftp://) as files. +; http://php.net/allow-url-include +allow_url_include = Off + +; Define the anonymous ftp password (your email address). PHP's default setting +; for this is empty. +; http://php.net/from +;from="john@doe.com" + +; Define the User-Agent string. PHP's default setting for this is empty. +; http://php.net/user-agent +;user_agent="PHP" + +; Default timeout for socket based streams (seconds) +; http://php.net/default-socket-timeout +default_socket_timeout = 60 + +; If your scripts have to deal with files from Macintosh systems, +; or you are running on a Mac and need to deal with files from +; unix or win32 systems, setting this flag will cause PHP to +; automatically detect the EOL character in those files so that +; fgets() and file() will work regardless of the source of the file. +; http://php.net/auto-detect-line-endings +;auto_detect_line_endings = Off + +;;;;;;;;;;;;;;;;;;;;;; +; Dynamic Extensions ; +;;;;;;;;;;;;;;;;;;;;;; + +; If you wish to have an extension loaded automatically, use the following +; syntax: +; +; extension=modulename.extension +; +; For example, on Windows: +; +; extension=msql.dll +; +; ... or under UNIX: +; +; extension=msql.so +; +; ... or with a path: +; +; extension=/path/to/extension/msql.so +; +; If you only provide the name of the extension, PHP will look for it in its +; default extension directory. +; +; Windows Extensions +; Note that ODBC support is built in, so no dll is needed for it. +; Note that many DLL files are located in the extensions/ (PHP 4) ext/ (PHP 5+) +; extension folders as well as the separate PECL DLL download (PHP 5+). +; Be sure to appropriately set the extension_dir directive. +; +;extension=php_bz2.dll +;extension=php_curl.dll +;extension=php_fileinfo.dll +;extension=php_ftp.dll +;extension=php_gd2.dll +;extension=php_gettext.dll +;extension=php_gmp.dll +;extension=php_intl.dll +;extension=php_imap.dll +;extension=php_interbase.dll +;extension=php_ldap.dll +;extension=php_mbstring.dll +;extension=php_exif.dll ; Must be after mbstring as it depends on it +;extension=php_mysqli.dll +;extension=php_oci8_12c.dll ; Use with Oracle Database 12c Instant Client +;extension=php_openssl.dll +;extension=php_pdo_firebird.dll +;extension=php_pdo_mysql.dll +;extension=php_pdo_oci.dll +;extension=php_pdo_odbc.dll +;extension=php_pdo_pgsql.dll +;extension=php_pdo_sqlite.dll +;extension=php_pgsql.dll +;extension=php_shmop.dll + +; The MIBS data available in the PHP distribution must be installed. +; See http://www.php.net/manual/en/snmp.installation.php +;extension=php_snmp.dll + +;extension=php_soap.dll +;extension=php_sockets.dll +;extension=php_sqlite3.dll +;extension=php_tidy.dll +;extension=php_xmlrpc.dll +;extension=php_xsl.dll + +;;;;;;;;;;;;;;;;;;; +; Module Settings ; +;;;;;;;;;;;;;;;;;;; + +[CLI Server] +; Whether the CLI web server uses ANSI color coding in its terminal output. +cli_server.color = On + +[Date] +; Defines the default timezone used by the date functions +; http://php.net/date.timezone +;date.timezone = + +; http://php.net/date.default-latitude +;date.default_latitude = 31.7667 + +; http://php.net/date.default-longitude +;date.default_longitude = 35.2333 + +; http://php.net/date.sunrise-zenith +;date.sunrise_zenith = 90.583333 + +; http://php.net/date.sunset-zenith +;date.sunset_zenith = 90.583333 + +[filter] +; http://php.net/filter.default +;filter.default = unsafe_raw + +; http://php.net/filter.default-flags +;filter.default_flags = + +[iconv] +; Use of this INI entry is deprecated, use global input_encoding instead. +; If empty, default_charset or input_encoding or iconv.input_encoding is used. +; The precedence is: default_charset < intput_encoding < iconv.input_encoding +;iconv.input_encoding = + +; Use of this INI entry is deprecated, use global internal_encoding instead. +; If empty, default_charset or internal_encoding or iconv.internal_encoding is used. +; The precedence is: default_charset < internal_encoding < iconv.internal_encoding +;iconv.internal_encoding = + +; Use of this INI entry is deprecated, use global output_encoding instead. +; If empty, default_charset or output_encoding or iconv.output_encoding is used. +; The precedence is: default_charset < output_encoding < iconv.output_encoding +; To use an output encoding conversion, iconv's output handler must be set +; otherwise output encoding conversion cannot be performed. +;iconv.output_encoding = + +[intl] +;intl.default_locale = +; This directive allows you to produce PHP errors when some error +; happens within intl functions. The value is the level of the error produced. +; Default is 0, which does not produce any errors. +;intl.error_level = E_WARNING +;intl.use_exceptions = 0 + +[sqlite3] +;sqlite3.extension_dir = + +[Pcre] +;PCRE library backtracking limit. +; http://php.net/pcre.backtrack-limit +;pcre.backtrack_limit=100000 + +;PCRE library recursion limit. +;Please note that if you set this value to a high number you may consume all +;the available process stack and eventually crash PHP (due to reaching the +;stack size limit imposed by the Operating System). +; http://php.net/pcre.recursion-limit +;pcre.recursion_limit=100000 + +;Enables or disables JIT compilation of patterns. This requires the PCRE +;library to be compiled with JIT support. +;pcre.jit=1 + +[Pdo] +; Whether to pool ODBC connections. Can be one of "strict", "relaxed" or "off" +; http://php.net/pdo-odbc.connection-pooling +;pdo_odbc.connection_pooling=strict + +;pdo_odbc.db2_instance_name + +[Pdo_mysql] +; If mysqlnd is used: Number of cache slots for the internal result set cache +; http://php.net/pdo_mysql.cache_size +pdo_mysql.cache_size = 2000 + +; Default socket name for local MySQL connects. If empty, uses the built-in +; MySQL defaults. +; http://php.net/pdo_mysql.default-socket +pdo_mysql.default_socket= + +[Phar] +; http://php.net/phar.readonly +;phar.readonly = On + +; http://php.net/phar.require-hash +;phar.require_hash = On + +;phar.cache_list = + +[mail function] +; For Win32 only. +; http://php.net/smtp +SMTP = localhost +; http://php.net/smtp-port +smtp_port = 25 + +; For Win32 only. +; http://php.net/sendmail-from +;sendmail_from = me@example.com + +; For Unix only. You may supply arguments as well (default: "sendmail -t -i"). +; http://php.net/sendmail-path +;sendmail_path = + +; Force the addition of the specified parameters to be passed as extra parameters +; to the sendmail binary. These parameters will always replace the value of +; the 5th parameter to mail(). +;mail.force_extra_parameters = + +; Add X-PHP-Originating-Script: that will include uid of the script followed by the filename +mail.add_x_header = On + +; The path to a log file that will log all mail() calls. Log entries include +; the full path of the script, line number, To address and headers. +;mail.log = +; Log mail to syslog (Event Log on Windows). +;mail.log = syslog + +[SQL] +; http://php.net/sql.safe-mode +sql.safe_mode = Off + +[ODBC] +; http://php.net/odbc.default-db +;odbc.default_db = Not yet implemented + +; http://php.net/odbc.default-user +;odbc.default_user = Not yet implemented + +; http://php.net/odbc.default-pw +;odbc.default_pw = Not yet implemented + +; Controls the ODBC cursor model. +; Default: SQL_CURSOR_STATIC (default). +;odbc.default_cursortype + +; Allow or prevent persistent links. +; http://php.net/odbc.allow-persistent +odbc.allow_persistent = On + +; Check that a connection is still valid before reuse. +; http://php.net/odbc.check-persistent +odbc.check_persistent = On + +; Maximum number of persistent links. -1 means no limit. +; http://php.net/odbc.max-persistent +odbc.max_persistent = -1 + +; Maximum number of links (persistent + non-persistent). -1 means no limit. +; http://php.net/odbc.max-links +odbc.max_links = -1 + +; Handling of LONG fields. Returns number of bytes to variables. 0 means +; passthru. +; http://php.net/odbc.defaultlrl +odbc.defaultlrl = 4096 + +; Handling of binary data. 0 means passthru, 1 return as is, 2 convert to char. +; See the documentation on odbc_binmode and odbc_longreadlen for an explanation +; of odbc.defaultlrl and odbc.defaultbinmode +; http://php.net/odbc.defaultbinmode +odbc.defaultbinmode = 1 + +;birdstep.max_links = -1 + +[Interbase] +; Allow or prevent persistent links. +ibase.allow_persistent = 1 + +; Maximum number of persistent links. -1 means no limit. +ibase.max_persistent = -1 + +; Maximum number of links (persistent + non-persistent). -1 means no limit. +ibase.max_links = -1 + +; Default database name for ibase_connect(). +;ibase.default_db = + +; Default username for ibase_connect(). +;ibase.default_user = + +; Default password for ibase_connect(). +;ibase.default_password = + +; Default charset for ibase_connect(). +;ibase.default_charset = + +; Default timestamp format. +ibase.timestampformat = "%Y-%m-%d %H:%M:%S" + +; Default date format. +ibase.dateformat = "%Y-%m-%d" + +; Default time format. +ibase.timeformat = "%H:%M:%S" + +[MySQLi] + +; Maximum number of persistent links. -1 means no limit. +; http://php.net/mysqli.max-persistent +mysqli.max_persistent = -1 + +; Allow accessing, from PHP's perspective, local files with LOAD DATA statements +; http://php.net/mysqli.allow_local_infile +;mysqli.allow_local_infile = On + +; Allow or prevent persistent links. +; http://php.net/mysqli.allow-persistent +mysqli.allow_persistent = On + +; Maximum number of links. -1 means no limit. +; http://php.net/mysqli.max-links +mysqli.max_links = -1 + +; If mysqlnd is used: Number of cache slots for the internal result set cache +; http://php.net/mysqli.cache_size +mysqli.cache_size = 2000 + +; Default port number for mysqli_connect(). If unset, mysqli_connect() will use +; the $MYSQL_TCP_PORT or the mysql-tcp entry in /etc/services or the +; compile-time value defined MYSQL_PORT (in that order). Win32 will only look +; at MYSQL_PORT. +; http://php.net/mysqli.default-port +mysqli.default_port = 3306 + +; Default socket name for local MySQL connects. If empty, uses the built-in +; MySQL defaults. +; http://php.net/mysqli.default-socket +mysqli.default_socket = + +; Default host for mysql_connect() (doesn't apply in safe mode). +; http://php.net/mysqli.default-host +mysqli.default_host = + +; Default user for mysql_connect() (doesn't apply in safe mode). +; http://php.net/mysqli.default-user +mysqli.default_user = + +; Default password for mysqli_connect() (doesn't apply in safe mode). +; Note that this is generally a *bad* idea to store passwords in this file. +; *Any* user with PHP access can run 'echo get_cfg_var("mysqli.default_pw") +; and reveal this password! And of course, any users with read access to this +; file will be able to reveal the password as well. +; http://php.net/mysqli.default-pw +mysqli.default_pw = + +; Allow or prevent reconnect +mysqli.reconnect = Off + +[mysqlnd] +; Enable / Disable collection of general statistics by mysqlnd which can be +; used to tune and monitor MySQL operations. +; http://php.net/mysqlnd.collect_statistics +mysqlnd.collect_statistics = On + +; Enable / Disable collection of memory usage statistics by mysqlnd which can be +; used to tune and monitor MySQL operations. +; http://php.net/mysqlnd.collect_memory_statistics +mysqlnd.collect_memory_statistics = Off + +; Records communication from all extensions using mysqlnd to the specified log +; file. +; http://php.net/mysqlnd.debug +;mysqlnd.debug = + +; Defines which queries will be logged. +; http://php.net/mysqlnd.log_mask +;mysqlnd.log_mask = 0 + +; Default size of the mysqlnd memory pool, which is used by result sets. +; http://php.net/mysqlnd.mempool_default_size +;mysqlnd.mempool_default_size = 16000 + +; Size of a pre-allocated buffer used when sending commands to MySQL in bytes. +; http://php.net/mysqlnd.net_cmd_buffer_size +;mysqlnd.net_cmd_buffer_size = 2048 + +; Size of a pre-allocated buffer used for reading data sent by the server in +; bytes. +; http://php.net/mysqlnd.net_read_buffer_size +;mysqlnd.net_read_buffer_size = 32768 + +; Timeout for network requests in seconds. +; http://php.net/mysqlnd.net_read_timeout +;mysqlnd.net_read_timeout = 31536000 + +; SHA-256 Authentication Plugin related. File with the MySQL server public RSA +; key. +; http://php.net/mysqlnd.sha256_server_public_key +;mysqlnd.sha256_server_public_key = + +[OCI8] + +; Connection: Enables privileged connections using external +; credentials (OCI_SYSOPER, OCI_SYSDBA) +; http://php.net/oci8.privileged-connect +;oci8.privileged_connect = Off + +; Connection: The maximum number of persistent OCI8 connections per +; process. Using -1 means no limit. +; http://php.net/oci8.max-persistent +;oci8.max_persistent = -1 + +; Connection: The maximum number of seconds a process is allowed to +; maintain an idle persistent connection. Using -1 means idle +; persistent connections will be maintained forever. +; http://php.net/oci8.persistent-timeout +;oci8.persistent_timeout = -1 + +; Connection: The number of seconds that must pass before issuing a +; ping during oci_pconnect() to check the connection validity. When +; set to 0, each oci_pconnect() will cause a ping. Using -1 disables +; pings completely. +; http://php.net/oci8.ping-interval +;oci8.ping_interval = 60 + +; Connection: Set this to a user chosen connection class to be used +; for all pooled server requests with Oracle 11g Database Resident +; Connection Pooling (DRCP). To use DRCP, this value should be set to +; the same string for all web servers running the same application, +; the database pool must be configured, and the connection string must +; specify to use a pooled server. +;oci8.connection_class = + +; High Availability: Using On lets PHP receive Fast Application +; Notification (FAN) events generated when a database node fails. The +; database must also be configured to post FAN events. +;oci8.events = Off + +; Tuning: This option enables statement caching, and specifies how +; many statements to cache. Using 0 disables statement caching. +; http://php.net/oci8.statement-cache-size +;oci8.statement_cache_size = 20 + +; Tuning: Enables statement prefetching and sets the default number of +; rows that will be fetched automatically after statement execution. +; http://php.net/oci8.default-prefetch +;oci8.default_prefetch = 100 + +; Compatibility. Using On means oci_close() will not close +; oci_connect() and oci_new_connect() connections. +; http://php.net/oci8.old-oci-close-semantics +;oci8.old_oci_close_semantics = Off + +[PostgreSQL] +; Allow or prevent persistent links. +; http://php.net/pgsql.allow-persistent +pgsql.allow_persistent = On + +; Detect broken persistent links always with pg_pconnect(). +; Auto reset feature requires a little overheads. +; http://php.net/pgsql.auto-reset-persistent +pgsql.auto_reset_persistent = Off + +; Maximum number of persistent links. -1 means no limit. +; http://php.net/pgsql.max-persistent +pgsql.max_persistent = -1 + +; Maximum number of links (persistent+non persistent). -1 means no limit. +; http://php.net/pgsql.max-links +pgsql.max_links = -1 + +; Ignore PostgreSQL backends Notice message or not. +; Notice message logging require a little overheads. +; http://php.net/pgsql.ignore-notice +pgsql.ignore_notice = 0 + +; Log PostgreSQL backends Notice message or not. +; Unless pgsql.ignore_notice=0, module cannot log notice message. +; http://php.net/pgsql.log-notice +pgsql.log_notice = 0 + +[bcmath] +; Number of decimal digits for all bcmath functions. +; http://php.net/bcmath.scale +bcmath.scale = 0 + +[browscap] +; http://php.net/browscap +;browscap = extra/browscap.ini + +[Session] +; Handler used to store/retrieve data. +; http://php.net/session.save-handler +session.save_handler = files + +; Argument passed to save_handler. In the case of files, this is the path +; where data files are stored. Note: Windows users have to change this +; variable in order to use PHP's session functions. +; +; The path can be defined as: +; +; session.save_path = "N;/path" +; +; where N is an integer. Instead of storing all the session files in +; /path, what this will do is use subdirectories N-levels deep, and +; store the session data in those directories. This is useful if +; your OS has problems with many files in one directory, and is +; a more efficient layout for servers that handle many sessions. +; +; NOTE 1: PHP will not create this directory structure automatically. +; You can use the script in the ext/session dir for that purpose. +; NOTE 2: See the section on garbage collection below if you choose to +; use subdirectories for session storage +; +; The file storage module creates files using mode 600 by default. +; You can change that by using +; +; session.save_path = "N;MODE;/path" +; +; where MODE is the octal representation of the mode. Note that this +; does not overwrite the process's umask. +; http://php.net/session.save-path +;session.save_path = "/var/lib/php/sessions" + +; Whether to use strict session mode. +; Strict session mode does not accept uninitialized session ID and regenerate +; session ID if browser sends uninitialized session ID. Strict mode protects +; applications from session fixation via session adoption vulnerability. It is +; disabled by default for maximum compatibility, but enabling it is encouraged. +; https://wiki.php.net/rfc/strict_sessions +session.use_strict_mode = 0 + +; Whether to use cookies. +; http://php.net/session.use-cookies +session.use_cookies = 1 + +; http://php.net/session.cookie-secure +;session.cookie_secure = + +; This option forces PHP to fetch and use a cookie for storing and maintaining +; the session id. We encourage this operation as it's very helpful in combating +; session hijacking when not specifying and managing your own session id. It is +; not the be-all and end-all of session hijacking defense, but it's a good start. +; http://php.net/session.use-only-cookies +session.use_only_cookies = 1 + +; Name of the session (used as cookie name). +; http://php.net/session.name +session.name = PHPSESSID + +; Initialize session on request startup. +; http://php.net/session.auto-start +session.auto_start = 0 + +; Lifetime in seconds of cookie or, if 0, until browser is restarted. +; http://php.net/session.cookie-lifetime +session.cookie_lifetime = 0 + +; The path for which the cookie is valid. +; http://php.net/session.cookie-path +session.cookie_path = / + +; The domain for which the cookie is valid. +; http://php.net/session.cookie-domain +session.cookie_domain = + +; Whether or not to add the httpOnly flag to the cookie, which makes it inaccessible to browser scripting languages such as JavaScript. +; http://php.net/session.cookie-httponly +session.cookie_httponly = + +; Handler used to serialize data. php is the standard serializer of PHP. +; http://php.net/session.serialize-handler +session.serialize_handler = php + +; Defines the probability that the 'garbage collection' process is started +; on every session initialization. The probability is calculated by using +; gc_probability/gc_divisor. Where session.gc_probability is the numerator +; and gc_divisor is the denominator in the equation. Setting this value to 1 +; when the session.gc_divisor value is 100 will give you approximately a 1% chance +; the gc will run on any give request. +; Default Value: 1 +; Development Value: 1 +; Production Value: 1 +; http://php.net/session.gc-probability +session.gc_probability = 0 + +; Defines the probability that the 'garbage collection' process is started on every +; session initialization. The probability is calculated by using the following equation: +; gc_probability/gc_divisor. Where session.gc_probability is the numerator and +; session.gc_divisor is the denominator in the equation. Setting this value to 1 +; when the session.gc_divisor value is 100 will give you approximately a 1% chance +; the gc will run on any give request. Increasing this value to 1000 will give you +; a 0.1% chance the gc will run on any give request. For high volume production servers, +; this is a more efficient approach. +; Default Value: 100 +; Development Value: 1000 +; Production Value: 1000 +; http://php.net/session.gc-divisor +session.gc_divisor = 1000 + +; After this number of seconds, stored data will be seen as 'garbage' and +; cleaned up by the garbage collection process. +; http://php.net/session.gc-maxlifetime +session.gc_maxlifetime = 1440 + +; NOTE: If you are using the subdirectory option for storing session files +; (see session.save_path above), then garbage collection does *not* +; happen automatically. You will need to do your own garbage +; collection through a shell script, cron entry, or some other method. +; For example, the following script would is the equivalent of +; setting session.gc_maxlifetime to 1440 (1440 seconds = 24 minutes): +; find /path/to/sessions -cmin +24 -type f | xargs rm + +; Check HTTP Referer to invalidate externally stored URLs containing ids. +; HTTP_REFERER has to contain this substring for the session to be +; considered as valid. +; http://php.net/session.referer-check +session.referer_check = + +; How many bytes to read from the file. +; http://php.net/session.entropy-length +;session.entropy_length = 32 + +; Specified here to create the session id. +; http://php.net/session.entropy-file +; Defaults to /dev/urandom +; On systems that don't have /dev/urandom but do have /dev/arandom, this will default to /dev/arandom +; If neither are found at compile time, the default is no entropy file. +; On windows, setting the entropy_length setting will activate the +; Windows random source (using the CryptoAPI) +;session.entropy_file = /dev/urandom + +; Set to {nocache,private,public,} to determine HTTP caching aspects +; or leave this empty to avoid sending anti-caching headers. +; http://php.net/session.cache-limiter +session.cache_limiter = nocache + +; Document expires after n minutes. +; http://php.net/session.cache-expire +session.cache_expire = 180 + +; trans sid support is disabled by default. +; Use of trans sid may risk your users' security. +; Use this option with caution. +; - User may send URL contains active session ID +; to other person via. email/irc/etc. +; - URL that contains active session ID may be stored +; in publicly accessible computer. +; - User may access your site with the same session ID +; always using URL stored in browser's history or bookmarks. +; http://php.net/session.use-trans-sid +session.use_trans_sid = 0 + +; Select a hash function for use in generating session ids. +; Possible Values +; 0 (MD5 128 bits) +; 1 (SHA-1 160 bits) +; This option may also be set to the name of any hash function supported by +; the hash extension. A list of available hashes is returned by the hash_algos() +; function. +; http://php.net/session.hash-function +session.hash_function = 0 + +; Define how many bits are stored in each character when converting +; the binary hash data to something readable. +; Possible values: +; 4 (4 bits: 0-9, a-f) +; 5 (5 bits: 0-9, a-v) +; 6 (6 bits: 0-9, a-z, A-Z, "-", ",") +; Default Value: 4 +; Development Value: 5 +; Production Value: 5 +; http://php.net/session.hash-bits-per-character +session.hash_bits_per_character = 5 + +; The URL rewriter will look for URLs in a defined set of HTML tags. +; form/fieldset are special; if you include them here, the rewriter will +; add a hidden field with the info which is otherwise appended +; to URLs. If you want XHTML conformity, remove the form entry. +; Note that all valid entries require a "=", even if no value follows. +; Default Value: "a=href,area=href,frame=src,form=,fieldset=" +; Development Value: "a=href,area=href,frame=src,input=src,form=fakeentry" +; Production Value: "a=href,area=href,frame=src,input=src,form=fakeentry" +; http://php.net/url-rewriter.tags +url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=fakeentry" + +; Enable upload progress tracking in $_SESSION +; Default Value: On +; Development Value: On +; Production Value: On +; http://php.net/session.upload-progress.enabled +;session.upload_progress.enabled = On + +; Cleanup the progress information as soon as all POST data has been read +; (i.e. upload completed). +; Default Value: On +; Development Value: On +; Production Value: On +; http://php.net/session.upload-progress.cleanup +;session.upload_progress.cleanup = On + +; A prefix used for the upload progress key in $_SESSION +; Default Value: "upload_progress_" +; Development Value: "upload_progress_" +; Production Value: "upload_progress_" +; http://php.net/session.upload-progress.prefix +;session.upload_progress.prefix = "upload_progress_" + +; The index name (concatenated with the prefix) in $_SESSION +; containing the upload progress information +; Default Value: "PHP_SESSION_UPLOAD_PROGRESS" +; Development Value: "PHP_SESSION_UPLOAD_PROGRESS" +; Production Value: "PHP_SESSION_UPLOAD_PROGRESS" +; http://php.net/session.upload-progress.name +;session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS" + +; How frequently the upload progress should be updated. +; Given either in percentages (per-file), or in bytes +; Default Value: "1%" +; Development Value: "1%" +; Production Value: "1%" +; http://php.net/session.upload-progress.freq +;session.upload_progress.freq = "1%" + +; The minimum delay between updates, in seconds +; Default Value: 1 +; Development Value: 1 +; Production Value: 1 +; http://php.net/session.upload-progress.min-freq +;session.upload_progress.min_freq = "1" + +; Only write session data when session data is changed. Enabled by default. +; http://php.net/session.lazy-write +;session.lazy_write = On + +[Assertion] +; Switch whether to compile assertions at all (to have no overhead at run-time) +; -1: Do not compile at all +; 0: Jump over assertion at run-time +; 1: Execute assertions +; Changing from or to a negative value is only possible in php.ini! (For turning assertions on and off at run-time, see assert.active, when zend.assertions = 1) +; Default Value: 1 +; Development Value: 1 +; Production Value: -1 +; http://php.net/zend.assertions +zend.assertions = -1 + +; Assert(expr); active by default. +; http://php.net/assert.active +;assert.active = On + +; Throw an AssertationException on failed assertions +; http://php.net/assert.exception +;assert.exception = On + +; Issue a PHP warning for each failed assertion. (Overridden by assert.exception if active) +; http://php.net/assert.warning +;assert.warning = On + +; Don't bail out by default. +; http://php.net/assert.bail +;assert.bail = Off + +; User-function to be called if an assertion fails. +; http://php.net/assert.callback +;assert.callback = 0 + +; Eval the expression with current error_reporting(). Set to true if you want +; error_reporting(0) around the eval(). +; http://php.net/assert.quiet-eval +;assert.quiet_eval = 0 + +[COM] +; path to a file containing GUIDs, IIDs or filenames of files with TypeLibs +; http://php.net/com.typelib-file +;com.typelib_file = + +; allow Distributed-COM calls +; http://php.net/com.allow-dcom +;com.allow_dcom = true + +; autoregister constants of a components typlib on com_load() +; http://php.net/com.autoregister-typelib +;com.autoregister_typelib = true + +; register constants casesensitive +; http://php.net/com.autoregister-casesensitive +;com.autoregister_casesensitive = false + +; show warnings on duplicate constant registrations +; http://php.net/com.autoregister-verbose +;com.autoregister_verbose = true + +; The default character set code-page to use when passing strings to and from COM objects. +; Default: system ANSI code page +;com.code_page= + +[mbstring] +; language for internal character representation. +; This affects mb_send_mail() and mbstring.detect_order. +; http://php.net/mbstring.language +;mbstring.language = Japanese + +; Use of this INI entry is deprecated, use global internal_encoding instead. +; internal/script encoding. +; Some encoding cannot work as internal encoding. (e.g. SJIS, BIG5, ISO-2022-*) +; If empty, default_charset or internal_encoding or iconv.internal_encoding is used. +; The precedence is: default_charset < internal_encoding < iconv.internal_encoding +;mbstring.internal_encoding = + +; Use of this INI entry is deprecated, use global input_encoding instead. +; http input encoding. +; mbstring.encoding_traslation = On is needed to use this setting. +; If empty, default_charset or input_encoding or mbstring.input is used. +; The precedence is: default_charset < intput_encoding < mbsting.http_input +; http://php.net/mbstring.http-input +;mbstring.http_input = + +; Use of this INI entry is deprecated, use global output_encoding instead. +; http output encoding. +; mb_output_handler must be registered as output buffer to function. +; If empty, default_charset or output_encoding or mbstring.http_output is used. +; The precedence is: default_charset < output_encoding < mbstring.http_output +; To use an output encoding conversion, mbstring's output handler must be set +; otherwise output encoding conversion cannot be performed. +; http://php.net/mbstring.http-output +;mbstring.http_output = + +; enable automatic encoding translation according to +; mbstring.internal_encoding setting. Input chars are +; converted to internal encoding by setting this to On. +; Note: Do _not_ use automatic encoding translation for +; portable libs/applications. +; http://php.net/mbstring.encoding-translation +;mbstring.encoding_translation = Off + +; automatic encoding detection order. +; "auto" detect order is changed according to mbstring.language +; http://php.net/mbstring.detect-order +;mbstring.detect_order = auto + +; substitute_character used when character cannot be converted +; one from another +; http://php.net/mbstring.substitute-character +;mbstring.substitute_character = none + +; overload(replace) single byte functions by mbstring functions. +; mail(), ereg(), etc are overloaded by mb_send_mail(), mb_ereg(), +; etc. Possible values are 0,1,2,4 or combination of them. +; For example, 7 for overload everything. +; 0: No overload +; 1: Overload mail() function +; 2: Overload str*() functions +; 4: Overload ereg*() functions +; http://php.net/mbstring.func-overload +;mbstring.func_overload = 0 + +; enable strict encoding detection. +; Default: Off +;mbstring.strict_detection = On + +; This directive specifies the regex pattern of content types for which mb_output_handler() +; is activated. +; Default: mbstring.http_output_conv_mimetype=^(text/|application/xhtml\+xml) +;mbstring.http_output_conv_mimetype= + +[gd] +; Tell the jpeg decode to ignore warnings and try to create +; a gd image. The warning will then be displayed as notices +; disabled by default +; http://php.net/gd.jpeg-ignore-warning +;gd.jpeg_ignore_warning = 0 + +[exif] +; Exif UNICODE user comments are handled as UCS-2BE/UCS-2LE and JIS as JIS. +; With mbstring support this will automatically be converted into the encoding +; given by corresponding encode setting. When empty mbstring.internal_encoding +; is used. For the decode settings you can distinguish between motorola and +; intel byte order. A decode setting cannot be empty. +; http://php.net/exif.encode-unicode +;exif.encode_unicode = ISO-8859-15 + +; http://php.net/exif.decode-unicode-motorola +;exif.decode_unicode_motorola = UCS-2BE + +; http://php.net/exif.decode-unicode-intel +;exif.decode_unicode_intel = UCS-2LE + +; http://php.net/exif.encode-jis +;exif.encode_jis = + +; http://php.net/exif.decode-jis-motorola +;exif.decode_jis_motorola = JIS + +; http://php.net/exif.decode-jis-intel +;exif.decode_jis_intel = JIS + +[Tidy] +; The path to a default tidy configuration file to use when using tidy +; http://php.net/tidy.default-config +;tidy.default_config = /usr/local/lib/php/default.tcfg + +; Should tidy clean and repair output automatically? +; WARNING: Do not use this option if you are generating non-html content +; such as dynamic images +; http://php.net/tidy.clean-output +tidy.clean_output = Off + +[soap] +; Enables or disables WSDL caching feature. +; http://php.net/soap.wsdl-cache-enabled +soap.wsdl_cache_enabled=1 + +; Sets the directory name where SOAP extension will put cache files. +; http://php.net/soap.wsdl-cache-dir +soap.wsdl_cache_dir="/tmp" + +; (time to live) Sets the number of second while cached file will be used +; instead of original one. +; http://php.net/soap.wsdl-cache-ttl +soap.wsdl_cache_ttl=86400 + +; Sets the size of the cache limit. (Max. number of WSDL files to cache) +soap.wsdl_cache_limit = 5 + +[sysvshm] +; A default size of the shared memory segment +;sysvshm.init_mem = 10000 + +[ldap] +; Sets the maximum number of open links or -1 for unlimited. +ldap.max_links = -1 + +[mcrypt] +; For more information about mcrypt settings see http://php.net/mcrypt-module-open + +; Directory where to load mcrypt algorithms +; Default: Compiled in into libmcrypt (usually /usr/local/lib/libmcrypt) +;mcrypt.algorithms_dir= + +; Directory where to load mcrypt modes +; Default: Compiled in into libmcrypt (usually /usr/local/lib/libmcrypt) +;mcrypt.modes_dir= + +[dba] +;dba.default_handler= + +[opcache] +; Determines if Zend OPCache is enabled +;opcache.enable=0 + +; Determines if Zend OPCache is enabled for the CLI version of PHP +;opcache.enable_cli=0 + +; The OPcache shared memory storage size. +;opcache.memory_consumption=64 + +; The amount of memory for interned strings in Mbytes. +;opcache.interned_strings_buffer=4 + +; The maximum number of keys (scripts) in the OPcache hash table. +; Only numbers between 200 and 1000000 are allowed. +;opcache.max_accelerated_files=2000 + +; The maximum percentage of "wasted" memory until a restart is scheduled. +;opcache.max_wasted_percentage=5 + +; When this directive is enabled, the OPcache appends the current working +; directory to the script key, thus eliminating possible collisions between +; files with the same name (basename). Disabling the directive improves +; performance, but may break existing applications. +;opcache.use_cwd=1 + +; When disabled, you must reset the OPcache manually or restart the +; webserver for changes to the filesystem to take effect. +;opcache.validate_timestamps=1 + +; How often (in seconds) to check file timestamps for changes to the shared +; memory storage allocation. ("1" means validate once per second, but only +; once per request. "0" means always validate) +;opcache.revalidate_freq=2 + +; Enables or disables file search in include_path optimization +;opcache.revalidate_path=0 + +; If disabled, all PHPDoc comments are dropped from the code to reduce the +; size of the optimized code. +;opcache.save_comments=1 + +; If enabled, a fast shutdown sequence is used for the accelerated code +; Depending on the used Memory Manager this may cause some incompatibilities. +;opcache.fast_shutdown=0 + +; Allow file existence override (file_exists, etc.) performance feature. +;opcache.enable_file_override=0 + +; A bitmask, where each bit enables or disables the appropriate OPcache +; passes +;opcache.optimization_level=0xffffffff + +;opcache.inherited_hack=1 +;opcache.dups_fix=0 + +; The location of the OPcache blacklist file (wildcards allowed). +; Each OPcache blacklist file is a text file that holds the names of files +; that should not be accelerated. The file format is to add each filename +; to a new line. The filename may be a full path or just a file prefix +; (i.e., /var/www/x blacklists all the files and directories in /var/www +; that start with 'x'). Line starting with a ; are ignored (comments). +;opcache.blacklist_filename= + +; Allows exclusion of large files from being cached. By default all files +; are cached. +;opcache.max_file_size=0 + +; Check the cache checksum each N requests. +; The default value of "0" means that the checks are disabled. +;opcache.consistency_checks=0 + +; How long to wait (in seconds) for a scheduled restart to begin if the cache +; is not being accessed. +;opcache.force_restart_timeout=180 + +; OPcache error_log file name. Empty string assumes "stderr". +;opcache.error_log= + +; All OPcache errors go to the Web server log. +; By default, only fatal errors (level 0) or errors (level 1) are logged. +; You can also enable warnings (level 2), info messages (level 3) or +; debug messages (level 4). +;opcache.log_verbosity_level=1 + +; Preferred Shared Memory back-end. Leave empty and let the system decide. +;opcache.preferred_memory_model= + +; Protect the shared memory from unexpected writing during script execution. +; Useful for internal debugging only. +;opcache.protect_memory=0 + +; Allows calling OPcache API functions only from PHP scripts which path is +; started from specified string. The default "" means no restriction +;opcache.restrict_api= + +; Mapping base of shared memory segments (for Windows only). All the PHP +; processes have to map shared memory into the same address space. This +; directive allows to manually fix the "Unable to reattach to base address" +; errors. +;opcache.mmap_base= + +; Enables and sets the second level cache directory. +; It should improve performance when SHM memory is full, at server restart or +; SHM reset. The default "" disables file based caching. +;opcache.file_cache= + +; Enables or disables opcode caching in shared memory. +;opcache.file_cache_only=0 + +; Enables or disables checksum validation when script loaded from file cache. +;opcache.file_cache_consistency_checks=1 + +; Implies opcache.file_cache_only=1 for a certain process that failed to +; reattach to the shared memory (for Windows only). Explicitly enabled file +; cache is required. +;opcache.file_cache_fallback=1 + +; Enables or disables copying of PHP code (text segment) into HUGE PAGES. +; This should improve performance, but requires appropriate OS configuration. +;opcache.huge_code_pages=1 + +; Validate cached file permissions. +; opcache.validate_permission=0 + +; Prevent name collisions in chroot'ed environment. +; opcache.validate_root=0 + +[curl] +; A default value for the CURLOPT_CAINFO option. This is required to be an +; absolute path. +;curl.cainfo = + +[openssl] +; The location of a Certificate Authority (CA) file on the local filesystem +; to use when verifying the identity of SSL/TLS peers. Most users should +; not specify a value for this directive as PHP will attempt to use the +; OS-managed cert stores in its absence. If specified, this value may still +; be overridden on a per-stream basis via the "cafile" SSL stream context +; option. +;openssl.cafile= + +; If openssl.cafile is not specified or if the CA file is not found, the +; directory pointed to by openssl.capath is searched for a suitable +; certificate. This value must be a correctly hashed certificate directory. +; Most users should not specify a value for this directive as PHP will +; attempt to use the OS-managed cert stores in its absence. If specified, +; this value may still be overridden on a per-stream basis via the "capath" +; SSL stream context option. +;openssl.capath= + +; Local Variables: +; tab-width: 4 +; End: diff --git a/.docker/web/www.conf b/.docker/web/www.conf new file mode 100644 index 000000000..65c2634fc --- /dev/null +++ b/.docker/web/www.conf @@ -0,0 +1,411 @@ +; Start a new pool named 'www'. +; the variable $pool can we used in any directive and will be replaced by the +; pool name ('www' here) +[www] + +; Per pool prefix +; It only applies on the following directives: +; - 'access.log' +; - 'slowlog' +; - 'listen' (unixsocket) +; - 'chroot' +; - 'chdir' +; - 'php_values' +; - 'php_admin_values' +; When not set, the global prefix (or /usr) applies instead. +; Note: This directive can also be relative to the global prefix. +; Default Value: none +;prefix = /path/to/pools/$pool + +; Unix user/group of processes +; Note: The user is mandatory. If the group is not set, the default user's group +; will be used. +user = gazelle +group = gazelle + +; The address on which to accept FastCGI requests. +; Valid syntaxes are: +; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on +; a specific port; +; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on +; a specific port; +; 'port' - to listen on a TCP socket to all IPv4 addresses on a +; specific port; +; '[::]:port' - to listen on a TCP socket to all addresses +; (IPv6 and IPv4-mapped) on a specific port; +; '/path/to/unix/socket' - to listen on a unix socket. +; Note: This value is mandatory. +listen = /var/run/php/php-fpm.sock + +; Set listen(2) backlog. +; Default Value: 65535 (-1 on FreeBSD and OpenBSD) +;listen.backlog = 65535 + +; Set permissions for unix socket, if one is used. In Linux, read/write +; permissions must be set in order to allow connections from a web server. Many +; BSD-derived systems allow connections regardless of permissions. +; Default Values: user and group are set as the running user +; mode is set to 0660 +listen.owner = www-data +listen.group = www-data +;listen.mode = 0660 +; When POSIX Access Control Lists are supported you can set them using +; these options, value is a comma separated list of user/group names. +; When set, listen.owner and listen.group are ignored +;listen.acl_users = +;listen.acl_groups = + +; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect. +; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original +; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address +; must be separated by a comma. If this value is left blank, connections will be +; accepted from any ip address. +; Default Value: any +;listen.allowed_clients = 127.0.0.1 + +; Specify the nice(2) priority to apply to the pool processes (only if set) +; The value can vary from -19 (highest priority) to 20 (lower priority) +; Note: - It will only work if the FPM master process is launched as root +; - The pool processes will inherit the master process priority +; unless it specified otherwise +; Default Value: no set +; process.priority = -19 + +; Choose how the process manager will control the number of child processes. +; Possible Values: +; static - a fixed number (pm.max_children) of child processes; +; dynamic - the number of child processes are set dynamically based on the +; following directives. With this process management, there will be +; always at least 1 children. +; pm.max_children - the maximum number of children that can +; be alive at the same time. +; pm.start_servers - the number of children created on startup. +; pm.min_spare_servers - the minimum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is less than this +; number then some children will be created. +; pm.max_spare_servers - the maximum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is greater than this +; number then some children will be killed. +; ondemand - no children are created at startup. Children will be forked when +; new requests will connect. The following parameter are used: +; pm.max_children - the maximum number of children that +; can be alive at the same time. +; pm.process_idle_timeout - The number of seconds after which +; an idle process will be killed. +; Note: This value is mandatory. +pm = dynamic + +; The number of child processes to be created when pm is set to 'static' and the +; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. +; This value sets the limit on the number of simultaneous requests that will be +; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. +; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP +; CGI. The below defaults are based on a server without much resources. Don't +; forget to tweak pm.* to fit your needs. +; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' +; Note: This value is mandatory. +pm.max_children = 5 + +; The number of child processes created on startup. +; Note: Used only when pm is set to 'dynamic' +; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 +pm.start_servers = 2 + +; The desired minimum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.min_spare_servers = 1 + +; The desired maximum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.max_spare_servers = 3 + +; The number of seconds after which an idle process will be killed. +; Note: Used only when pm is set to 'ondemand' +; Default Value: 10s +;pm.process_idle_timeout = 10s; + +; The number of requests each child process should execute before respawning. +; This can be useful to work around memory leaks in 3rd party libraries. For +; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. +; Default Value: 0 +;pm.max_requests = 500 + +; The URI to view the FPM status page. If this value is not set, no URI will be +; recognized as a status page. It shows the following informations: +; pool - the name of the pool; +; process manager - static, dynamic or ondemand; +; start time - the date and time FPM has started; +; start since - number of seconds since FPM has started; +; accepted conn - the number of request accepted by the pool; +; listen queue - the number of request in the queue of pending +; connections (see backlog in listen(2)); +; max listen queue - the maximum number of requests in the queue +; of pending connections since FPM has started; +; listen queue len - the size of the socket queue of pending connections; +; idle processes - the number of idle processes; +; active processes - the number of active processes; +; total processes - the number of idle + active processes; +; max active processes - the maximum number of active processes since FPM +; has started; +; max children reached - number of times, the process limit has been reached, +; when pm tries to start more children (works only for +; pm 'dynamic' and 'ondemand'); +; Value are updated in real time. +; Example output: +; pool: www +; process manager: static +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 62636 +; accepted conn: 190460 +; listen queue: 0 +; max listen queue: 1 +; listen queue len: 42 +; idle processes: 4 +; active processes: 11 +; total processes: 15 +; max active processes: 12 +; max children reached: 0 +; +; By default the status page output is formatted as text/plain. Passing either +; 'html', 'xml' or 'json' in the query string will return the corresponding +; output syntax. Example: +; http://www.foo.bar/status +; http://www.foo.bar/status?json +; http://www.foo.bar/status?html +; http://www.foo.bar/status?xml +; +; By default the status page only outputs short status. Passing 'full' in the +; query string will also return status for each pool process. +; Example: +; http://www.foo.bar/status?full +; http://www.foo.bar/status?json&full +; http://www.foo.bar/status?html&full +; http://www.foo.bar/status?xml&full +; The Full status returns for each process: +; pid - the PID of the process; +; state - the state of the process (Idle, Running, ...); +; start time - the date and time the process has started; +; start since - the number of seconds since the process has started; +; requests - the number of requests the process has served; +; request duration - the duration in µs of the requests; +; request method - the request method (GET, POST, ...); +; request URI - the request URI with the query string; +; content length - the content length of the request (only with POST); +; user - the user (PHP_AUTH_USER) (or '-' if not set); +; script - the main script called (or '-' if not set); +; last request cpu - the %cpu the last request consumed +; it's always 0 if the process is not in Idle state +; because CPU calculation is done when the request +; processing has terminated; +; last request memory - the max amount of memory the last request consumed +; it's always 0 if the process is not in Idle state +; because memory calculation is done when the request +; processing has terminated; +; If the process is in Idle state, then informations are related to the +; last request the process has served. Otherwise informations are related to +; the current request being served. +; Example output: +; ************************ +; pid: 31330 +; state: Running +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 63087 +; requests: 12808 +; request duration: 1250261 +; request method: GET +; request URI: /test_mem.php?N=10000 +; content length: 0 +; user: - +; script: /home/fat/web/docs/php/test_mem.php +; last request cpu: 0.00 +; last request memory: 0 +; +; Note: There is a real-time FPM status monitoring sample web page available +; It's available in: /usr/share/php5/fpm/status.html +; +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;pm.status_path = /status + +; The ping URI to call the monitoring page of FPM. If this value is not set, no +; URI will be recognized as a ping page. This could be used to test from outside +; that FPM is alive and responding, or to +; - create a graph of FPM availability (rrd or such); +; - remove a server from a group if it is not responding (load balancing); +; - trigger alerts for the operating team (24/7). +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;ping.path = /ping + +; This directive may be used to customize the response of a ping request. The +; response is formatted as text/plain with a 200 response code. +; Default Value: pong +;ping.response = pong + +; The access log file +; Default: not set +;access.log = log/$pool.access.log + +; The access log format. +; The following syntax is allowed +; %%: the '%' character +; %C: %CPU used by the request +; it can accept the following format: +; - %{user}C for user CPU only +; - %{system}C for system CPU only +; - %{total}C for user + system CPU (default) +; %d: time taken to serve the request +; it can accept the following format: +; - %{seconds}d (default) +; - %{miliseconds}d +; - %{mili}d +; - %{microseconds}d +; - %{micro}d +; %e: an environment variable (same as $_ENV or $_SERVER) +; it must be associated with embraces to specify the name of the env +; variable. Some exemples: +; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e +; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e +; %f: script filename +; %l: content-length of the request (for POST request only) +; %m: request method +; %M: peak of memory allocated by PHP +; it can accept the following format: +; - %{bytes}M (default) +; - %{kilobytes}M +; - %{kilo}M +; - %{megabytes}M +; - %{mega}M +; %n: pool name +; %o: output header +; it must be associated with embraces to specify the name of the header: +; - %{Content-Type}o +; - %{X-Powered-By}o +; - %{Transfert-Encoding}o +; - .... +; %p: PID of the child that serviced the request +; %P: PID of the parent of the child that serviced the request +; %q: the query string +; %Q: the '?' character if query string exists +; %r: the request URI (without the query string, see %q and %Q) +; %R: remote IP address +; %s: status (response code) +; %t: server time the request was received +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; %T: time the log has been written (the request has finished) +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; %u: remote user +; +; Default: "%R - %u %t \"%m %r\" %s" +;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%" + +; The log file for slow requests +; Default Value: not set +; Note: slowlog is mandatory if request_slowlog_timeout is set +;slowlog = log/$pool.log.slow + +; The timeout for serving a single request after which a PHP backtrace will be +; dumped to the 'slowlog' file. A value of '0s' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +;request_slowlog_timeout = 0 + +; The timeout for serving a single request after which the worker process will +; be killed. This option should be used when the 'max_execution_time' ini option +; does not stop script execution for some reason. A value of '0' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +;request_terminate_timeout = 0 + +; Set open file descriptor rlimit. +; Default Value: system defined value +;rlimit_files = 1024 + +; Set max core size rlimit. +; Possible Values: 'unlimited' or an integer greater or equal to 0 +; Default Value: system defined value +;rlimit_core = 0 + +; Chroot to this directory at the start. This value must be defined as an +; absolute path. When this value is not set, chroot is not used. +; Note: you can prefix with '$prefix' to chroot to the pool prefix or one +; of its subdirectories. If the pool prefix is not set, the global prefix +; will be used instead. +; Note: chrooting is a great security feature and should be used whenever +; possible. However, all PHP paths will be relative to the chroot +; (error_log, sessions.save_path, ...). +; Default Value: not set +;chroot = + +; Chdir to this directory at the start. +; Note: relative path can be used. +; Default Value: current directory or / when chroot +chdir = / + +; Redirect worker stdout and stderr into main error log. If not set, stdout and +; stderr will be redirected to /dev/null according to FastCGI specs. +; Note: on highloaded environement, this can cause some delay in the page +; process time (several ms). +; Default Value: no +;catch_workers_output = yes + +; Clear environment in FPM workers +; Prevents arbitrary environment variables from reaching FPM worker processes +; by clearing the environment in workers before env vars specified in this +; pool configuration are added. +; Setting to "no" will make all environment variables available to PHP code +; via getenv(), $_ENV and $_SERVER. +; Default Value: yes +;clear_env = no + +; Limits the extensions of the main script FPM will allow to parse. This can +; prevent configuration mistakes on the web server side. You should only limit +; FPM to .php extensions to prevent malicious users to use other extensions to +; exectute php code. +; Note: set an empty value to allow all extensions. +; Default Value: .php +;security.limit_extensions = .php .php3 .php4 .php5 + +; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from +; the current environment. +; Default Value: clean env +;env[HOSTNAME] = $HOSTNAME +;env[PATH] = /usr/local/bin:/usr/bin:/bin +;env[TMP] = /tmp +;env[TMPDIR] = /tmp +;env[TEMP] = /tmp + +; Additional php.ini defines, specific to this pool of workers. These settings +; overwrite the values previously defined in the php.ini. The directives are the +; same as the PHP SAPI: +; php_value/php_flag - you can set classic ini defines which can +; be overwritten from PHP call 'ini_set'. +; php_admin_value/php_admin_flag - these directives won't be overwritten by +; PHP call 'ini_set' +; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no. + +; Defining 'extension' will load the corresponding shared extension from +; extension_dir. Defining 'disable_functions' or 'disable_classes' will not +; overwrite previously defined php.ini values, but will append the new value +; instead. + +; Note: path INI options can be relative and will be expanded with the prefix +; (pool, global or /usr) + +; Default Value: nothing is defined by default except the values in php.ini and +; specified at startup with the -d argument +;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com +;php_flag[display_errors] = off +;php_admin_value[error_log] = /var/log/fpm-php.www.log +;php_admin_flag[log_errors] = on +;php_admin_value[memory_limit] = 32M diff --git a/.docker/web/xdebug.ini b/.docker/web/xdebug.ini new file mode 100644 index 000000000..726c9691f --- /dev/null +++ b/.docker/web/xdebug.ini @@ -0,0 +1,6 @@ +zend_extension=xdebug.so +xdebug.remote_port=9000 +xdebug.remote_handler="dbgp" +xdebug.remote_enable=On +xdebug.remote_connect_back=On +xdebug.remote_log=/var/log/xdebug.log diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..83f52e6cb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +vendor/ +node_modules/ +Vagrantfile +.vagrant/ +html/ +.docker/data diff --git a/.gitignore b/.gitignore index f77656ca1..c59c5b935 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,12 @@ html/ /classes/config.php /sphinx.conf .DS_Store +__MACOSX/ logs/ !logs/README.txt +static/local/ + static/similar/ !static/similar/.gitkeep @@ -14,6 +17,7 @@ static/stylespreview/ sections/tools/development/node_modules/ /vendor/ +node_modules/ static/userscripts/gazelle-json-export.js.txt @@ -31,3 +35,7 @@ static/userscripts/gazelle-json-export.js.txt report/ .vagrant/machines/ .vagrant/rgloader/ +*.swp + +.docker/data +cache/ diff --git a/.vagrant/config.php b/.vagrant/config.php index c4c116751..87d0bb03d 100644 --- a/.vagrant/config.php +++ b/.vagrant/config.php @@ -1,6 +1,6 @@ '/var/run/memcached.sock', 'port' => 0), + // unix sockets are fast, and other people can't telnet into them + array('host' => '/var/run/memcached.sock', 'port' => 0), ); // Sphinx details @@ -63,11 +68,11 @@ define('STATIC_SERVER', NONSSL_STATIC_SERVER); /*if (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 80) { - define('SITE_URL', NONSSL_SITE_URL); - define('STATIC_SERVER', NONSSL_STATIC_SERVER); + define('SITE_URL', NONSSL_SITE_URL); + define('STATIC_SERVER', NONSSL_STATIC_SERVER); } else { - define('SITE_URL', SSL_SITE_URL); - define('STATIC_SERVER', SSL_STATIC_SERVER); + define('SITE_URL', SSL_SITE_URL); + define('STATIC_SERVER', SSL_STATIC_SERVER); }*/ // Site settings @@ -90,30 +95,30 @@ if (!defined('FEATURE_EMAIL_REENABLE')) { - define('FEATURE_EMAIL_REENABLE', true); + define('FEATURE_EMAIL_REENABLE', true); } // User class IDs needed for automatic promotions. Found in the 'permissions' table -// Name of class Class ID (NOT level) -define('ADMIN', '40'); -define('USER', '2'); -define('MEMBER', '3'); -define('POWER', '4'); -define('ELITE', '5'); -define('VIP', '26'); +// Name of class Class ID (NOT level) +define('ADMIN', '40'); +define('USER', '2'); +define('MEMBER', '3'); +define('POWER', '4'); +define('ELITE', '5'); +define('VIP', '26'); define('TORRENT_MASTER','25'); -define('LEGEND', '27'); -define('CELEB', '31'); -define('MOD', '11'); -define('CODER', '24'); +define('LEGEND', '27'); +define('CELEB', '31'); +define('MOD', '11'); +define('CODER', '24'); define('LEAD_DEV', '43'); -define('SYSOP', '15'); -define('ARTIST', '19'); -define('DONOR', '20'); -define('FLS_TEAM', '23'); -define('POWER_TM', '29'); -define('ELITE_TM', '28'); -define('FORUM_MOD', '21'); +define('SYSOP', '15'); +define('ARTIST', '19'); +define('DONOR', '20'); +define('FLS_TEAM', '23'); +define('POWER_TM', '29'); +define('ELITE_TM', '28'); +define('FORUM_MOD', '21'); define('TORRENT_MOD', '22'); define('INTERVIEWER', '30'); define('DESIGNER', '32'); @@ -173,8 +178,8 @@ define('DONOR_FORUM', 70); define('MAX_SPECIAL_RANK', 3); -$ForumsRevealVoters = array(); -$ForumsDoublePost = array(); +$ForumsRevealVoters = []; +$ForumsDoublePost = []; $Categories = array('Music', 'Applications', 'E-Books', 'Audiobooks', 'E-Learning Videos', 'Comedy', 'Comics'); $GroupedCategories = array_intersect(array('Music'), $Categories); @@ -190,80 +195,87 @@ //$ForumCats = array(1=>'Site', 5=>'Community', 10=>'Help', 8=>'Music', 20=>'Trash'); //No longer needed $ZIPGroups = array( - 0 => 'MP3 (VBR) - High Quality', - 1 => 'MP3 (VBR) - Low Quality', - 2 => 'MP3 (CBR)', - 3 => 'FLAC - Lossless', - 4 => 'Others' + 0 => 'MP3 (VBR) - High Quality', + 1 => 'MP3 (VBR) - Low Quality', + 2 => 'MP3 (CBR)', + 3 => 'FLAC - Lossless', + 4 => 'Others' ); //3D array of attributes, OptionGroup, OptionNumber, Name $ZIPOptions = array( - '00' => array(0,0,'V0'), - '01' => array(0,1,'APX'), - '02' => array(0,2,'256'), - '03' => array(0,3,'V1'), - '10' => array(1,0,'224'), - '11' => array(1,1,'V2'), - '12' => array(1,2,'APS'), - '13' => array(1,3,'192'), - '20' => array(2,0,'320'), - '21' => array(2,1,'256'), - '22' => array(2,2,'224'), - '23' => array(2,3,'192'), - '30' => array(3,0,'FLAC / 24bit / Vinyl'), - '31' => array(3,1,'FLAC / 24bit / DVD'), - '32' => array(3,2,'FLAC / 24bit / SACD'), - '33' => array(3,3,'FLAC / Log (100) / Cue'), - '34' => array(3,4,'FLAC / Log (100)'), - '35' => array(3,5,'FLAC / Log'), - '36' => array(3,6,'FLAC'), - '40' => array(4,0,'DTS'), - '41' => array(4,1,'Ogg Vorbis'), - '42' => array(4,2,'AAC - 320'), - '43' => array(4,3,'AAC - 256'), - '44' => array(4,4,'AAC - q5.5'), - '45' => array(4,5,'AAC - q5'), - '46' => array(4,6,'AAC - 192') + '00' => array(0,0,'V0'), + '01' => array(0,1,'APX'), + '02' => array(0,2,'256'), + '03' => array(0,3,'V1'), + '10' => array(1,0,'224'), + '11' => array(1,1,'V2'), + '12' => array(1,2,'APS'), + '13' => array(1,3,'192'), + '20' => array(2,0,'320'), + '21' => array(2,1,'256'), + '22' => array(2,2,'224'), + '23' => array(2,3,'192'), + '30' => array(3,0,'FLAC / 24bit / Vinyl'), + '31' => array(3,1,'FLAC / 24bit / DVD'), + '32' => array(3,2,'FLAC / 24bit / SACD'), + '33' => array(3,3,'FLAC / Log (100) / Cue'), + '34' => array(3,4,'FLAC / Log (100)'), + '35' => array(3,5,'FLAC / Log'), + '36' => array(3,6,'FLAC'), + '40' => array(4,0,'DTS'), + '41' => array(4,1,'Ogg Vorbis'), + '42' => array(4,2,'AAC - 320'), + '43' => array(4,3,'AAC - 256'), + '44' => array(4,4,'AAC - q5.5'), + '45' => array(4,5,'AAC - q5'), + '46' => array(4,6,'AAC - 192') ); // Ratio requirements, in descending order // Columns: Download amount, required ratio, grace period $RatioRequirements = array( - array(50 * 1024 * 1024 * 1024, 0.60, date('Y-m-d H:i:s')), - array(40 * 1024 * 1024 * 1024, 0.50, date('Y-m-d H:i:s')), - array(30 * 1024 * 1024 * 1024, 0.40, date('Y-m-d H:i:s')), - array(20 * 1024 * 1024 * 1024, 0.30, date('Y-m-d H:i:s')), - array(10 * 1024 * 1024 * 1024, 0.20, date('Y-m-d H:i:s')), - array(5 * 1024 * 1024 * 1024, 0.15, date('Y-m-d H:i:s', time() - (60 * 60 * 24 * 14))) + array(50 * 1024 * 1024 * 1024, 0.60, date('Y-m-d H:i:s')), + array(40 * 1024 * 1024 * 1024, 0.50, date('Y-m-d H:i:s')), + array(30 * 1024 * 1024 * 1024, 0.40, date('Y-m-d H:i:s')), + array(20 * 1024 * 1024 * 1024, 0.30, date('Y-m-d H:i:s')), + array(10 * 1024 * 1024 * 1024, 0.20, date('Y-m-d H:i:s')), + array(5 * 1024 * 1024 * 1024, 0.15, date('Y-m-d H:i:s', time() - (60 * 60 * 24 * 14))) ); //Captcha fonts should be located in /classes/fonts $CaptchaFonts = array( - 'ARIBLK.TTF', - 'IMPACT.TTF', - 'TREBUC.TTF', - 'TREBUCBD.TTF', - 'TREBUCBI.TTF', - 'TREBUCIT.TTF', - 'VERDANA.TTF', - 'VERDANAB.TTF', - 'VERDANAI.TTF', - 'VERDANAZ.TTF'); + 'ARIBLK.TTF', + 'IMPACT.TTF', + 'TREBUC.TTF', + 'TREBUCBD.TTF', + 'TREBUCBI.TTF', + 'TREBUCIT.TTF', + 'VERDANA.TTF', + 'VERDANAB.TTF', + 'VERDANAI.TTF', + 'VERDANAZ.TTF'); //Captcha images should be located in /captcha $CaptchaBGs = array( - 'captcha1.png', - 'captcha2.png', - 'captcha3.png', - 'captcha4.png', - 'captcha5.png', - 'captcha6.png', - 'captcha7.png', - 'captcha8.png', - 'captcha9.png'); + 'captcha1.png', + 'captcha2.png', + 'captcha3.png', + 'captcha4.png', + 'captcha5.png', + 'captcha6.png', + 'captcha7.png', + 'captcha8.png', + 'captcha9.png'); // Special characters, and what they should be converted to // Used for torrent searching $SpecialChars = array( - '&' => 'and' + '&' => 'and' ); + +// Deny cache access to keys without specified permission +$CachePermissions = [ + 'api_apps' => 'site_debug', + 'catalogue' => 'site_debug' +]; + diff --git a/.vagrant/sphinx.conf b/.vagrant/sphinx.conf index 37c88f6dc..961abe821 100644 --- a/.vagrant/sphinx.conf +++ b/.vagrant/sphinx.conf @@ -8,96 +8,97 @@ source connect { type = mysql sql_host = localhost - sql_user = root - sql_pass = em%G9Lrey4^N + sql_user = gazelle + sql_pass = password sql_db = gazelle sql_port = 3306 sql_sock = /var/run/mysqld/mysqld.sock } source torrents_base : connect { - sql_attr_uint = groupid - sql_attr_uint = time - sql_attr_uint = categoryid - sql_attr_uint = releasetype - sql_attr_bigint = size - sql_attr_uint = snatched - sql_attr_uint = seeders - sql_attr_uint = leechers - sql_attr_uint = logscore - sql_attr_uint = year - sql_attr_bool = scene - sql_attr_bool = vanityhouse - sql_attr_bool = haslog - sql_attr_bool = hascue - sql_attr_uint = freetorrent + sql_attr_uint = groupid + sql_attr_uint = time + sql_attr_uint = categoryid + sql_attr_uint = releasetype + sql_attr_bigint = size + sql_attr_uint = snatched + sql_attr_uint = seeders + sql_attr_uint = leechers + sql_attr_uint = logscore + sql_attr_uint = year + sql_attr_bool = scene + sql_attr_bool = vanityhouse + sql_attr_bool = haslog + sql_attr_bool = hascue + sql_attr_uint = freetorrent } source torrents : torrents_base { - #By inheriting from torrents_base, we keep all the connection info - sql_query_pre = SET group_concat_max_len = 101400 - sql_query_pre = SET @starttime = NOW() - sql_query_pre = REPLACE INTO sphinx_index_last_pos VALUES ('torrents', UNIX_TIMESTAMP(@starttime)) - sql_query_pre = TRUNCATE sphinx_tg - sql_query_pre = INSERT INTO sphinx_tg \ - (id, name, tags, year, rlabel, cnumber, catid, reltype, \ - vanityhouse) \ - SELECT id, name, taglist, year, recordlabel, cataloguenumber, \ - categoryid, releasetype, vanityhouse \ - FROM torrents_group \ - WHERE time < @starttime - sql_query_pre = TRUNCATE sphinx_t - sql_query_pre = INSERT INTO sphinx_t \ - (id, gid, size, snatched, seeders, leechers, time, logscore, scene, \ - haslog, hascue, freetorrent, description, media, format, encoding, \ - remyear, remtitle, remrlabel, remcnumber, filelist, uid, remident) \ - SELECT ID, GroupID, Size, Snatched, Seeders, Leechers, UNIX_TIMESTAMP(Time), \ - LogScore, CAST(Scene AS CHAR), CAST(HasLog AS CHAR), \ - CAST(HasCue AS CHAR), CAST(FreeTorrent AS CHAR), Description, \ - Media, Format, Encoding, RemasterYear, RemasterTitle, \ - RemasterRecordLabel, RemasterCatalogueNumber, FileList, UserID, \ - CRC32(CONCAT_WS(' ', Media, RemasterYear, RemasterTitle, \ - RemasterRecordLabel, RemasterCatalogueNumber)) \ - FROM torrents \ - WHERE Time < @starttime - sql_query_pre = TRUNCATE sphinx_a - sql_query_pre = INSERT INTO sphinx_a \ - (gid, aname) \ - SELECT GroupID, GROUP_CONCAT(aa.Name SEPARATOR ' ') \ - FROM torrents_artists AS ta \ - JOIN artists_alias AS aa USING(AliasID) \ - WHERE Importance IN ('1','3','4','5','6') \ - GROUP BY ta.groupid \ - ORDER BY NULL - sql_query = SELECT t.id, g.id AS groupid, g.name AS groupname, \ - tags AS taglist, year, year AS yearfulltext, \ - rlabel AS recordlabel, cnumber AS cataloguenumber, \ - catid AS categoryid, t.time, reltype AS releasetype, \ - size, snatched, seeders, leechers, logscore, \ - scene, vanityhouse, haslog, hascue, freetorrent, description, \ - media, format, encoding, remyear AS remasteryear, \ - remtitle AS remastertitle, remrlabel AS remasterrecordlabel, \ - remcnumber AS remastercataloguenumber, \ - REPLACE(filelist, '_', ' ') AS filelist \ - FROM sphinx_t AS t \ - JOIN sphinx_tg AS g ON t.gid = g.id - sql_joined_field = artistname from query; \ - SELECT t.id, aname FROM sphinx_a JOIN sphinx_t AS t USING(gid) ORDER BY t.id ASC; - sql_query_post_index = DELETE FROM sphinx_delta WHERE Time <= \ - (SELECT id FROM sphinx_index_last_pos WHERE type = 'torrents') + #By inheriting from torrents_base, we keep all the connection info + sql_query_pre = SET group_concat_max_len = 101400 + sql_query_pre = SET @starttime = NOW() + sql_query_pre = REPLACE INTO sphinx_index_last_pos VALUES ('torrents', UNIX_TIMESTAMP(@starttime)) + sql_query_pre = TRUNCATE sphinx_tg + sql_query_pre = INSERT INTO sphinx_tg \ + (id, name, tags, year, rlabel, cnumber, catid, reltype, \ + vanityhouse) \ + SELECT id, name, taglist, year, recordlabel, cataloguenumber, \ + categoryid, releasetype, vanityhouse \ + FROM torrents_group \ + WHERE time < @starttime + sql_query_pre = TRUNCATE sphinx_t + sql_query_pre = INSERT INTO sphinx_t \ + (id, gid, size, snatched, seeders, leechers, time, logscore, scene, \ + haslog, hascue, freetorrent, description, media, format, encoding, \ + remyear, remtitle, remrlabel, remcnumber, filelist, uid, remident) \ + SELECT t.ID, t.GroupID, t.Size, tls.Snatched, tls.Seeders, tls.Leechers, UNIX_TIMESTAMP(t.Time), \ + t.LogScore, CAST(t.Scene AS CHAR), CAST(t.HasLog AS CHAR), \ + CAST(t.HasCue AS CHAR), CAST(t.FreeTorrent AS CHAR), t.Description, \ + t.Media, t.Format, t.Encoding, t.RemasterYear, t.RemasterTitle, \ + t.RemasterRecordLabel, t.RemasterCatalogueNumber, t.FileList, t.UserID, \ + CRC32(CONCAT_WS(' ', t.Media, t.RemasterYear, t.RemasterTitle, \ + t.RemasterRecordLabel, t.RemasterCatalogueNumber)) \ + FROM torrents t \ + INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID) \ + WHERE t.Time < @starttime + sql_query_pre = TRUNCATE sphinx_a + sql_query_pre = INSERT INTO sphinx_a \ + (gid, aname) \ + SELECT GroupID, GROUP_CONCAT(aa.Name SEPARATOR ' ') \ + FROM torrents_artists AS ta \ + JOIN artists_alias AS aa USING(AliasID) \ + WHERE Importance IN ('1','3','4','5','6') \ + GROUP BY ta.groupid \ + ORDER BY NULL + sql_query = SELECT t.id, g.id AS groupid, g.name AS groupname, \ + tags AS taglist, year, year AS yearfulltext, \ + rlabel AS recordlabel, cnumber AS cataloguenumber, \ + catid AS categoryid, t.time, reltype AS releasetype, \ + size, snatched, seeders, leechers, logscore, \ + scene, vanityhouse, haslog, hascue, freetorrent, description, \ + media, format, encoding, remyear AS remasteryear, \ + remtitle AS remastertitle, remrlabel AS remasterrecordlabel, \ + remcnumber AS remastercataloguenumber, \ + REPLACE(filelist, '_', ' ') AS filelist \ + FROM sphinx_t AS t \ + JOIN sphinx_tg AS g ON t.gid = g.id + sql_joined_field = artistname from query; \ + SELECT t.id, aname FROM sphinx_a JOIN sphinx_t AS t USING(gid) ORDER BY t.id ASC; + sql_query_post_index = DELETE FROM sphinx_delta WHERE Time <= \ + (SELECT id FROM sphinx_index_last_pos WHERE type = 'torrents') } index torrents { - source = torrents - path = /var/lib/sphinxsearch/data/torrents - dict = keywords -# stopwords = /etc/sphinx/stopwords.txt # Path to file containing a space separated list of words not to index - preopen = 1 - morphology = none - min_word_len = 2 - phrase_boundary = U+F7 # This needs to the the same as the file delimiter in classes/torrents.class.php - phrase_boundary_step = 50 - infix_fields = taglist - min_infix_len = 3 - charset_table = \ + source = torrents + path = /var/lib/sphinxsearch/data/torrents + dict = keywords +# stopwords = /etc/sphinx/stopwords.txt # Path to file containing a space separated list of words not to index + preopen = 1 + morphology = none + min_word_len = 2 + phrase_boundary = U+F7 # This needs to the the same as the file delimiter in classes/torrents.class.php + phrase_boundary_step = 50 + infix_fields = taglist + min_infix_len = 3 + charset_table = \ U+00C0->a, U+00C1->a, U+00C2->a, U+00C3->a, U+00C4->a, U+00C5->a, U+00E0->a, U+00E1->a, U+00E2->a, \ U+00E3->a, U+00E4->a, U+00E5->a, U+0100->a, U+0101->a, U+0102->a, U+0103->a, U+010300->a, U+0104->a, U+0105->a, U+01CD->a, U+01CE->a, U+01DE->a, U+01DF->a, \ U+01E0->a, U+01E1->a, U+01FA->a, U+01FB->a, U+0200->a, U+0201->a, U+0202->a, U+0203->a, U+0226->a, U+0227->a, U+023A->a, U+0250->a, U+04D0->a, U+04D1->a, U+1D2C->a, U+1D43->a, U+1D44->a, U+1D8F->a, U+1E00->a, U+1E01->a, U+1E9A->a, U+1EA0->a, U+1EA1->a, \ @@ -287,159 +288,159 @@ index torrents { U+0CAA..U+0CB3, U+0CB5..U+0CB9, U+0CE0, U+0CE1, U+0CE6..U+0CEF, U+1900..U+191C, U+1930..U+1938, U+1946..U+194F, U+0D05..U+0D0C, U+0D0E..U+0D10, U+0D12..U+0D28, U+0D2A..U+0D39, U+0D60, U+0D61, U+0D66..U+0D6F, U+0B94->U+0B92, U+0B85..U+0B8A, U+0B8E..U+0B90, \ U+0B92, U+0B93, U+0B95, U+0B99, U+0B9A, U+0B9C, U+0B9E, U+0B9F, U+0BA3, U+0BA4, U+0BA8..U+0BAA, U+0BAE..U+0BB9, U+0BE6..U+0BEF, U+0E01..U+0E30, U+0E32, U+0E33, U+0E40..U+0E46, U+0E50..U+0E5B, U+FF10..U+FF19->0..9, U+FF21..U+FF3A->a..z, U+FF41..U+FF5A->a..z, \ 0..9, A..Z->a..z, a..z, _ - blend_chars = !, ", U+23, $, %, &, ', (, ), *, +, U+2C, -, ., /, :, U+3B, <, =, >, ?, @, U+5B, U+5C, U+5D, ^, U+60, U+7C, U+7E, U+A1..U+BF - blend_mode = trim_none, trim_head, trim_tail, trim_both + blend_chars = !, ", U+23, $, %, &, ', (, ), *, +, U+2C, -, ., /, :, U+3B, <, =, >, ?, @, U+5B, U+5C, U+5D, ^, U+60, U+7C, U+7E, U+A1..U+BF + blend_mode = trim_none, trim_head, trim_tail, trim_both } source delta : torrents_base { - sql_query = SELECT *, Year AS yearfulltext FROM sphinx_delta WHERE Size > 0; - sql_query_killlist = SELECT ID FROM sphinx_delta + sql_query = SELECT *, Year AS yearfulltext FROM sphinx_delta WHERE Size > 0; + sql_query_killlist = SELECT ID FROM sphinx_delta } index delta : torrents { - source = delta - path = /var/lib/sphinxsearch/data/delta + source = delta + path = /var/lib/sphinxsearch/data/delta } source requests_base : connect { - sql_attr_uint = UserID - sql_attr_uint = TimeAdded - sql_attr_uint = LastVote - sql_attr_uint = CategoryID - sql_attr_uint = Year - sql_attr_uint = ReleaseType - sql_attr_uint = FillerID - sql_attr_uint = TorrentID - sql_attr_uint = TimeFilled - sql_attr_uint = Visible - sql_attr_uint = Votes - sql_attr_uint = Bounty + sql_attr_uint = UserID + sql_attr_uint = TimeAdded + sql_attr_uint = LastVote + sql_attr_uint = CategoryID + sql_attr_uint = Year + sql_attr_uint = ReleaseType + sql_attr_uint = FillerID + sql_attr_uint = TorrentID + sql_attr_uint = TimeFilled + sql_attr_uint = Visible + sql_attr_uint = Votes + sql_attr_uint = Bounty } source requests : requests_base { - sql_query_pre = TRUNCATE TABLE sphinx_requests - sql_query_pre = SET group_concat_max_len = 10140 - sql_query_pre = SET @starttime = NOW() - sql_query_pre = REPLACE INTO sphinx_index_last_pos VALUES ('requests', UNIX_TIMESTAMP(@starttime)) - sql_query_pre = INSERT INTO sphinx_requests ( \ - ID, UserID, TimeAdded, LastVote, CategoryID, Title, \ - Year, ReleaseType, RecordLabel, CatalogueNumber, \ - BitrateList, FormatList, MediaList, LogCue, FillerID, \ - TorrentID, TimeFilled, Visible, Votes, Bounty ) \ - SELECT \ - r.ID, r.UserID, UNIX_TIMESTAMP(TimeAdded), \ - UNIX_TIMESTAMP(LastVote), CategoryID, Title, Year, \ - ReleaseType, RecordLabel, CatalogueNumber, BitrateList, \ - FormatList, MediaList, LogCue, FillerID, TorrentID, \ - UNIX_TIMESTAMP(TimeFilled), Visible, \ - COUNT(rv.RequestID), SUM(rv.Bounty) >> 10 \ - FROM requests AS r \ - JOIN requests_votes AS rv ON rv.RequestID = r.ID \ - GROUP BY rv.RequestID - sql_query_pre = INSERT INTO sphinx_requests ( \ - ID, ArtistList ) \ - SELECT \ - RequestID, \ - GROUP_CONCAT(aa.Name SEPARATOR ' ') \ - FROM requests_artists AS ra \ - JOIN artists_alias AS aa ON aa.AliasID = ra.AliasID \ - JOIN requests AS r ON r.ID = ra.RequestID \ - WHERE TimeAdded <= @starttime \ - GROUP BY r.ID \ - ON DUPLICATE KEY UPDATE ArtistList = VALUES(ArtistList) - sql_query = SELECT ID, UserID, TimeAdded, LastVote, CategoryID, Title, \ - Year, ArtistList, ReleaseType, RecordLabel, CatalogueNumber, \ - BitrateList, FormatList, MediaList, LogCue, FillerID, \ - TorrentID, TimeFilled, Visible, Votes, Bounty, \ - Year AS YearFullText \ - FROM sphinx_requests - sql_joined_field = taglist from query; \ - SELECT rt.RequestID, REPLACE(t.Name, '.', '_') \ - FROM requests_tags AS rt \ - JOIN tags AS t ON TagID = ID \ - ORDER BY requestid ASC; - sql_attr_multi = uint Voter from query; \ - SELECT RequestID AS ID, UserID FROM requests_votes - sql_attr_multi = uint Bookmarker from query; \ - SELECT RequestID AS ID, UserID FROM bookmarks_requests - sql_query_post_index = DELETE FROM sphinx_requests_delta WHERE TimeAdded <= \ - (SELECT ID FROM sphinx_index_last_pos WHERE type = 'requests') + sql_query_pre = TRUNCATE TABLE sphinx_requests + sql_query_pre = SET group_concat_max_len = 10140 + sql_query_pre = SET @starttime = NOW() + sql_query_pre = REPLACE INTO sphinx_index_last_pos VALUES ('requests', UNIX_TIMESTAMP(@starttime)) + sql_query_pre = INSERT INTO sphinx_requests ( \ + ID, UserID, TimeAdded, LastVote, CategoryID, Title, \ + Year, ReleaseType, RecordLabel, CatalogueNumber, \ + BitrateList, FormatList, MediaList, LogCue, FillerID, \ + TorrentID, TimeFilled, Visible, Votes, Bounty ) \ + SELECT \ + r.ID, r.UserID, UNIX_TIMESTAMP(TimeAdded), \ + UNIX_TIMESTAMP(LastVote), CategoryID, Title, Year, \ + ReleaseType, RecordLabel, CatalogueNumber, BitrateList, \ + FormatList, MediaList, LogCue, FillerID, TorrentID, \ + UNIX_TIMESTAMP(TimeFilled), Visible, \ + COUNT(rv.RequestID), SUM(rv.Bounty) >> 10 \ + FROM requests AS r \ + JOIN requests_votes AS rv ON rv.RequestID = r.ID \ + GROUP BY rv.RequestID + sql_query_pre = INSERT INTO sphinx_requests ( \ + ID, ArtistList ) \ + SELECT \ + RequestID, \ + GROUP_CONCAT(aa.Name SEPARATOR ' ') \ + FROM requests_artists AS ra \ + JOIN artists_alias AS aa ON aa.AliasID = ra.AliasID \ + JOIN requests AS r ON r.ID = ra.RequestID \ + WHERE TimeAdded <= @starttime \ + GROUP BY r.ID \ + ON DUPLICATE KEY UPDATE ArtistList = VALUES(ArtistList) + sql_query = SELECT ID, UserID, TimeAdded, LastVote, CategoryID, Title, \ + Year, ArtistList, ReleaseType, RecordLabel, CatalogueNumber, \ + BitrateList, FormatList, MediaList, LogCue, FillerID, \ + TorrentID, TimeFilled, Visible, Votes, Bounty, \ + Year AS YearFullText \ + FROM sphinx_requests + sql_joined_field = taglist from query; \ + SELECT rt.RequestID, REPLACE(t.Name, '.', '_') \ + FROM requests_tags AS rt \ + JOIN tags AS t ON TagID = ID \ + ORDER BY requestid ASC; + sql_attr_multi = uint Voter from query; \ + SELECT RequestID AS ID, UserID FROM requests_votes + sql_attr_multi = uint Bookmarker from query; \ + SELECT RequestID AS ID, UserID FROM bookmarks_requests + sql_query_post_index = DELETE FROM sphinx_requests_delta WHERE TimeAdded <= \ + (SELECT ID FROM sphinx_index_last_pos WHERE type = 'requests') } source requests_delta : requests_base { - sql_query = SELECT ID, UserID, TimeAdded, LastVote, CategoryID, Title, TagList, \ - Year, ArtistList, ReleaseType, RecordLabel, CatalogueNumber, \ - BitrateList, FormatList, MediaList, LogCue, FillerID, \ - TorrentID, TimeFilled, Visible, Votes, Bounty, \ - Year AS YearFullText \ - FROM sphinx_requests_delta - sql_query_killlist = SELECT ID FROM sphinx_requests_delta - sql_attr_multi = uint Voter from query; \ - SELECT v.RequestID, v.UserID FROM requests_votes AS v \ - JOIN sphinx_requests_delta AS d ON d.ID = v.RequestID - sql_attr_multi = uint Bookmarker from query; \ - SELECT b.RequestID, b.UserID FROM bookmarks_requests AS b \ - JOIN sphinx_requests_delta AS d ON d.ID = b.RequestID + sql_query = SELECT ID, UserID, TimeAdded, LastVote, CategoryID, Title, TagList, \ + Year, ArtistList, ReleaseType, RecordLabel, CatalogueNumber, \ + BitrateList, FormatList, MediaList, LogCue, FillerID, \ + TorrentID, TimeFilled, Visible, Votes, Bounty, \ + Year AS YearFullText \ + FROM sphinx_requests_delta + sql_query_killlist = SELECT ID FROM sphinx_requests_delta + sql_attr_multi = uint Voter from query; \ + SELECT v.RequestID, v.UserID FROM requests_votes AS v \ + JOIN sphinx_requests_delta AS d ON d.ID = v.RequestID + sql_attr_multi = uint Bookmarker from query; \ + SELECT b.RequestID, b.UserID FROM bookmarks_requests AS b \ + JOIN sphinx_requests_delta AS d ON d.ID = b.RequestID } index requests : torrents { - source = requests - path = /var/lib/sphinxsearch/data/requests - infix_fields = taglist - min_infix_len = 3 + source = requests + path = /var/lib/sphinxsearch/data/requests + infix_fields = taglist + min_infix_len = 3 } index requests_delta : requests { - source = requests_delta - path = /var/lib/sphinxsearch/data/requests_delta + source = requests_delta + path = /var/lib/sphinxsearch/data/requests_delta } source log : connect { - sql_attr_uint = Time - sql_query = SELECT ID, UNIX_TIMESTAMP(Time) AS Time, Message FROM log - sql_query_post_index = REPLACE INTO sphinx_index_last_pos VALUES ('log', $maxid) + sql_attr_uint = Time + sql_query = SELECT ID, UNIX_TIMESTAMP(Time) AS Time, Message FROM log + sql_query_post_index = REPLACE INTO sphinx_index_last_pos VALUES ('log', $maxid) } source log_delta : log { - sql_query_pre = SELECT ID FROM sphinx_index_last_pos WHERE type = 'log' INTO @lastid - sql_query = SELECT ID, UNIX_TIMESTAMP(Time) AS Time, Message FROM log WHERE ID > @lastid - sql_query_post_index = SET @nothing = 0 + sql_query_pre = SELECT ID FROM sphinx_index_last_pos WHERE type = 'log' INTO @lastid + sql_query = SELECT ID, UNIX_TIMESTAMP(Time) AS Time, Message FROM log WHERE ID > @lastid + sql_query_post_index = SET @nothing = 0 } index log : torrents { - source = log - path = /var/lib/sphinxsearch/data/log - min_word_len = 1 - min_infix_len = 0 - infix_fields = + source = log + path = /var/lib/sphinxsearch/data/log + min_word_len = 1 + min_infix_len = 0 + infix_fields = } index log_delta : log { - source = log_delta - path = /var/lib/sphinxsearch/data/log_delta + source = log_delta + path = /var/lib/sphinxsearch/data/log_delta } source better_transcode : connect { - sql_attr_uint = logscore - sql_attr_uint = groupid - sql_attr_uint = uploader - sql_query = SELECT t.id, groupid, logscore, format, encoding, \ - name AS groupname, tags AS taglist, year, uploader, remident \ - FROM (SELECT t.id, gid AS groupid, uid uploader, remident, \ - IF(media='cd', MAX(t.logscore), 100) AS logscore, \ - GROUP_CONCAT(DISTINCT format SEPARATOR ' ') \ - AS format, \ - GROUP_CONCAT(DISTINCT encoding SEPARATOR ' ') \ - AS encoding \ - FROM sphinx_t AS t \ - WHERE format IN ('MP3', 'FLAC') \ - GROUP BY gid, remident) AS t \ - JOIN sphinx_tg AS g ON g.id = groupid \ - WHERE catid = 1 - sql_joined_field = artistname from query; \ - SELECT t.id, a.aname \ - FROM sphinx_a AS a\ - JOIN sphinx_t AS t USING(gid) \ - ORDER BY t.id ASC + sql_attr_uint = logscore + sql_attr_uint = groupid + sql_attr_uint = uploader + sql_query = SELECT t.id, groupid, logscore, format, encoding, \ + name AS groupname, tags AS taglist, year, uploader, remident \ + FROM (SELECT t.id, gid AS groupid, uid uploader, remident, \ + IF(media='cd', MAX(t.logscore), 100) AS logscore, \ + GROUP_CONCAT(DISTINCT format SEPARATOR ' ') \ + AS format, \ + GROUP_CONCAT(DISTINCT encoding SEPARATOR ' ') \ + AS encoding \ + FROM sphinx_t AS t \ + WHERE format IN ('MP3', 'FLAC') \ + GROUP BY gid, remident) AS t \ + JOIN sphinx_tg AS g ON g.id = groupid \ + WHERE catid = 1 + sql_joined_field = artistname from query; \ + SELECT t.id, a.aname \ + FROM sphinx_a AS a\ + JOIN sphinx_t AS t USING(gid) \ + ORDER BY t.id ASC } index better_transcode : torrents { - source = better_transcode - path = /var/lib/sphinxsearch/data/better_transcode - phrase_boundary = - min_infix_len = 0 - infix_fields = + source = better_transcode + path = /var/lib/sphinxsearch/data/better_transcode + phrase_boundary = + min_infix_len = 0 + infix_fields = } indexer { - mem_limit = 128M + mem_limit = 128M } searchd { @@ -450,4 +451,4 @@ searchd { pid_file = /var/run/sphinxsearch/searchd.pid mva_updates_pool = 1M #compat_sphinxql_magics = 0 -} \ No newline at end of file +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..fa8959e7b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,71 @@ +FROM debian:buster-slim + +WORKDIR /var/www + +RUN useradd -ms /bin/bash gazelle \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + software-properties-common \ + wget + +RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \ + && apt-get install -y --no-install-recommends \ + cron \ + git \ + imagemagick \ + libboost-dev \ + libbz2-dev \ + libssl-dev \ + libsqlite3-dev \ + libtcmalloc-minimal4 \ + make \ + mariadb-client \ + nodejs \ + nginx \ + python3 \ + python3-pip \ + python3-setuptools \ + python3-wheel \ + netcat-openbsd \ + unzip \ + zlib1g-dev + +RUN apt-get install -y --no-install-recommends \ + php7.3-cli \ + php7.3-curl \ + php7.3-fpm \ + php7.3-gd \ + php7.3-mbstring \ + php7.3-mysql \ + php7.3-xml \ + php7.3-zip \ + php-apcu \ + php-memcached \ + php-xdebug \ + composer + +RUN pip3 install chardet eac-logchecker xld-logchecker + +COPY . /var/www + +RUN chown -R gazelle:gazelle /var/www \ + && cp /var/www/.docker/web/php.ini /etc/php/7.3/cli/php.ini \ + && cp /var/www/.docker/web/php.ini /etc/php/7.3/fpm/php.ini \ + && cp /var/www/.docker/web/xdebug.ini /etc/php/7.3/mods-available/xdebug.ini \ + && cp /var/www/.docker/web/www.conf /etc/php/7.3/fpm/pool.d/www.conf \ + && cp /var/www/.docker/web/nginx.conf /etc/nginx/sites-available/gazelle.conf \ + && ln -s /etc/nginx/sites-available/gazelle.conf /etc/nginx/sites-enabled/gazelle.conf \ + && rm -f /etc/nginx/sites-enabled/default + +USER gazelle + +RUN composer --version \ + && composer install --no-dev --optimize-autoloader --no-suggest + +USER root + +EXPOSE 80 +CMD ["/bin/bash", "/var/www/.docker/web/entrypoint.sh"] diff --git a/api.php b/api.php new file mode 100644 index 000000000..d191f080a --- /dev/null +++ b/api.php @@ -0,0 +1,98 @@ + __DIR__.'/cache/twig'] +); +$Debug->handle_errors(); + +G::initialize(); + +function json_error($Code) { + echo json_encode(array('status' => 400, 'error' => $Code, 'response' => [])); + die(); +} + +function make_secret($Length = 32) { + $NumBytes = (int) round($Length / 2); + $Secret = bin2hex(openssl_random_pseudo_bytes($NumBytes)); + return substr($Secret, 0, $Length); +} + +function make_utf8($Str) { + if ($Str != '') { + if (is_utf8($Str)) { + $Encoding = 'UTF-8'; + } + if (empty($Encoding)) { + $Encoding = mb_detect_encoding($Str, 'UTF-8, ISO-8859-1'); + } + if (empty($Encoding)) { + $Encoding = 'ISO-8859-1'; + } + if ($Encoding == 'UTF-8') { + return $Str; + } else { + return @mb_convert_encoding($Str, 'UTF-8', $Encoding); + } + } +} + +function is_utf8($Str) { + return preg_match('%^(?: + [\x09\x0A\x0D\x20-\x7E] // ASCII + | [\xC2-\xDF][\x80-\xBF] // non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] // excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} // straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] // excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} // planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} // planes 4-15 + | \xF4[\x80-\x8F][\x80-\xBF]{2} // plane 16 + )*$%xs', $Str + ); +} + +function display_array($Array, $Escape = []) { + foreach ($Array as $Key => $Val) { + if ((!is_array($Escape) && $Escape == true) || !in_array($Key, $Escape)) { + $Array[$Key] = display_str($Val); + } + } + return $Array; +} + +header('Expires: '.date('D, d M Y H:i:s', time() + (2 * 60 * 60)).' GMT'); +header('Last-Modified: '.date('D, d M Y H:i:s').' GMT'); +header('Content-type: application/json'); +require_once(SERVER_ROOT.'/sections/api/index.php'); diff --git a/app/API/AbstractAPI.php b/app/API/AbstractAPI.php new file mode 100644 index 000000000..9ca90721a --- /dev/null +++ b/app/API/AbstractAPI.php @@ -0,0 +1,19 @@ +db = $db; + $this->cache = $cache; + $this->twig = $twig; + $this->config = $config; + } + + abstract public function run(); +} diff --git a/app/API/Artist.php b/app/API/Artist.php new file mode 100644 index 000000000..2a80069f0 --- /dev/null +++ b/app/API/Artist.php @@ -0,0 +1,25 @@ +db->prepared_query(" + SELECT + ArtistID, + Name + FROM + artists_group + WHERE + ArtistID = ?", $_GET['artist_id']); + if (!$this->db->has_results()) { + json_error('Artist not found'); + } + $artist = $this->db->next_record(MYSQLI_ASSOC, false); + return $artist; + } +} diff --git a/app/API/Collage.php b/app/API/Collage.php new file mode 100644 index 000000000..19a93a191 --- /dev/null +++ b/app/API/Collage.php @@ -0,0 +1,28 @@ +db->prepared_query(" + SELECT + ID, + Name, + CategoryID + FROM + collages + WHERE + ID = ?", $_GET['collage_id']); + if (!$this->db->has_results()) { + json_error('Collage not found'); + } + $collage = $this->db->next_record(MYSQLI_ASSOC, false); + $collage['Category'] = $this->config['CollageCats'][$collage['CategoryID']]; + + return $collage; + } +} diff --git a/app/API/Forum.php b/app/API/Forum.php new file mode 100644 index 000000000..9f5b9a374 --- /dev/null +++ b/app/API/Forum.php @@ -0,0 +1,33 @@ +db->prepared_query(" + SELECT + ft.ID, + ft.Title, + um.Username AS Author, + f.Name AS Forum, + f.MinClassRead + FROM + forums_topics AS ft + INNER JOIN users_main AS um ON um.ID = ft.AuthorID + INNER JOIN forums AS f ON f.ID = ft.ForumID + WHERE + ft.ID = ?", $_GET['topic_id']); + if (!$this->db->has_results()) { + json_error('Topic not found'); + } + $thread = $this->db->next_record(MYSQLI_ASSOC, false); + return $thread; + } +} diff --git a/app/API/GenerateInvite.php b/app/API/GenerateInvite.php new file mode 100644 index 000000000..1295f5463 --- /dev/null +++ b/app/API/GenerateInvite.php @@ -0,0 +1,68 @@ +db->query("SELECT ID, Username FROM users_main WHERE {$where}"); + if ($this->db->record_count() === 0) { + json_error("Could not find interviewer"); + } + $user = $this->db->next_record(); + $interviewer_id = $user['ID']; + $interviewer_name = $user['Username']; + + $email = (!empty($_GET['email'])) ? db_string($_GET['email']) : ""; + $expires = time_plus(60 * 60 * 24 * 3); // 3 days + $key = db_string(make_secret()); + $reason = "Passed Interview"; + + if (!empty($_GET['email'])) { + $this->db->query("SELECT ID, Username FROM users_main WHERE Email='{$email}'"); + if ($this->db->record_count() > 0) { + json_error("Email address already in use"); + } + + $this->db->query("SELECT * FROM invites WHERE Email='{$email}'"); + if ($this->db->record_count() > 0) { + $key = $this->db->next_record(); + json_error("Invite code already generated for this email address"); + } + } + + $this->db->query(" +INSERT INTO invites (InviterID, InviteKey, Email, Expires, Reason) +VALUES ('{$interviewer_id}', '{$key}', '{$email}', '{$expires}', '{$reason}')"); + $site_url = "http"; + if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != "") { + $site_url .= "s"; + } + $site_url .= "://" . SITE_URL . "/register.php?invite={$key}"; + + if (!empty($_GET['email'])) { + $body = $this->twig->render('emails/invite.twig', [ + 'InviterName' => $interviewer_name, + 'InviteKey' => $key, + 'Email' => $_GET['email'], + 'SITE_NAME' => SITE_NAME, + 'SITE_URL' => SITE_URL, + 'IRC_SERVER' => BOT_SERVER, + 'DISABLED_CHAN' => BOT_DISABLED_CHAN + ]); + + Misc::send_email($_GET['email'], 'New account confirmation at '.SITE_NAME, $body, 'noreply'); + } + + return array("key" => $key, "invite_url" => $site_url); + } +} diff --git a/app/API/Request.php b/app/API/Request.php new file mode 100644 index 000000000..27685314e --- /dev/null +++ b/app/API/Request.php @@ -0,0 +1,22 @@ +config['Categories'][$request['CategoryID'] - 1]; + + return $request; + } +} diff --git a/app/API/Torrent.php b/app/API/Torrent.php new file mode 100644 index 000000000..acbb1cee0 --- /dev/null +++ b/app/API/Torrent.php @@ -0,0 +1,81 @@ +getGroup(); + break; + default: + case 'torrent': + return $this->getTorrent(); + break; + } + } + + private function getTorrent() { + if (!isset($_GET['torrent_id'])) { + json_error('Missing torrent id'); + } + + $this->db->prepared_query(" + SELECT + tg.ID, + tg.Name, + tg.Year, + tg.ReleaseType AS ReleaseTypeID, + t.Media, + t.Format, + t.HasLog, + t.HasLogDB, + t.LogScore, + tls.Snatched, + tls.Seeders, + tls.Leechers + FROM + torrents AS t + INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID) + INNER JOIN torrents_group AS tg ON (tg.ID = t.GroupID) + WHERE + t.ID = ?", $_GET['torrent_id']); + if (!$this->db->has_results()) { + json_error('Torrent not found'); + } + $torrent = $this->db->next_record(MYSQLI_ASSOC, false); + $torrent['ReleaseType'] = $this->config['ReleaseTypes'][$torrent['ReleaseTypeID']]; + $artists = \Artists::get_artist($torrent['ID']); + $torrent['Artists'] = $artists; + $torrent['DisplayArtists'] = \Artists::display_artists($artists, + false, false, false); + return $torrent; + } + + private function getGroup() { + if (!isset($_GET['group_id'])) { + json_error('Missing group id'); + } + + $this->db->prepared_query(" + SELECT + ID, + Name, + Year, + ReleaseType AS ReleaseTypeID + FROM + torrents_group + WHERE + ID = ?", $_GET['group_id']); + if (!$this->db->has_results()) { + json_error('Group not found'); + } + $group = $this->db->next_record(MYSQLI_ASSOC, false); + $group['ReleaseType'] = $this->config['ReleaseTypes'][$group['ReleaseTypeID']]; + $artists = \Artists::get_artist($group['ID']); + $group['Artists'] = $artists; + $group['DisplayArtists'] = \Artists::display_artists($artists, + false, false, false); + return $group; + } +} diff --git a/app/API/User.php b/app/API/User.php new file mode 100644 index 000000000..741227e31 --- /dev/null +++ b/app/API/User.php @@ -0,0 +1,176 @@ +id = intval($_GET['user_id']); + } + else if (isset($_GET['username'])) { + $this->username = $_GET['username']; + } + else { + json_error("Need to supply either user_id or username"); + } + + if (isset($_GET['clear_tokens'])) { + $this->clear_tokens = true; + } + + switch ($_GET['req']) { + case 'enable': + return $this->enableUser(); + break; + case 'disable': + return $this->disableUser(); + break; + default: + case 'stats': + return $this->getUser(); + break; + } + } + + private function getUser() { + $where = ($this->id !== null) ? "um.ID = ?" : "um.Username = ?"; + $this->db->prepared_query(" + SELECT + um.ID, + um.Username, + um.Enabled, + um.IRCKey, + uls.Uploaded, + uls.Downloaded, + um.PermissionID AS Class, + um.Paranoia, + um.BonusPoints, + ui.DisableIRC, + p.Name as ClassName, + p.Level, + GROUP_CONCAT(ul.PermissionID SEPARATOR ',') AS SecondaryClasses + FROM + users_main AS um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + INNER JOIN users_info AS ui ON (ui.UserID = um.ID) + INNER JOIN permissions AS p ON (p.ID = um.PermissionID) + LEFT JOIN users_levels AS ul ON (ul.UserID = um.ID) + WHERE + {$where}", ($this->id !== null) ? $this->id : $this->username); + + $user = $this->db->next_record(MYSQLI_ASSOC, array('IRCKey', 'Paranoia')); + if (empty($user['Username'])) { + json_error("User not found"); + } + + $user['SecondaryClasses'] = array_map("intval", explode(",", $user['SecondaryClasses'])); + foreach (array('ID', 'Uploaded', 'Downloaded', 'Class', 'Level') as $key) { + $user[$key] = intval($user[$key]); + } + $user['Paranoia'] = unserialize_array($user['Paranoia']); + + $user['Ratio'] = \Format::get_ratio($user['Uploaded'], $user['Downloaded']); + $user['DisplayStats'] = array( + 'Downloaded' => \Format::get_size($user['Downloaded']), + 'Uploaded' => \Format::get_size($user['Uploaded']), + 'Ratio' => $user['Ratio'] + ); + foreach (array('Downloaded', 'Uploaded', 'Ratio') as $key) { + if (in_array(strtolower($key), $user['Paranoia'])) { + $user['DisplayStats'][$key] = "Hidden"; + } + } + $user['UserPage'] = site_url() . "user.php?id={$user['ID']}"; + + return $user; + } + + private function disableUser() { + if ($this->id === null) { + $this->db->prepared_query("SELECT ID FROM users_main WHERE Username = ?", + $this->username); + if ($this->db->has_results()) { + $user = $this->db->next_record(MYSQLI_ASSOC, false); + $this->id = $user['ID']; + } else { + json_error("No user found with username {$this->username}"); + } + } + + \Tools::disable_users($this->id, 'Disabled via API', 1); + return array('disabled' => true, 'user_id' => $this->id, 'username' => $this->username); + } + + private function enableUser() { + $where = ($this->id !== null) ? "um.ID = ?" : "um.Username = ?"; + $this->db->prepared_query(" + SELECT + um.ID, + um.Username, + um.IP, + um.Enabled, + uls.Uploaded, + uls.Downloaded, + um.Visible, + ui.AdminComment, + um.torrent_pass, + um.RequiredRatio, + ui.RatioWatchEnds + FROM + users_main AS um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + INNER JOIN users_info AS ui ON (ui.UserID = um.ID) + WHERE + {$where}", ($this->id !== null) ? $this->id : $this->username); + + // TODO: merge this and the version in takemoderate.php + $UpdateSet = []; + $Cur = $this->db->next_record(MYSQLI_ASSOC, false); + $Comment = 'Enabled via API'; + + if ($this->clear_tokens) { + $UpdateSet[] = "um.Invites = '0'"; + $UpdateSet[] = "um.FLTokens = '0'"; + $Comment = 'Tokens and invites reset, enabled via API'; + } + + $this->cache->increment('stats_user_count'); + $VisibleTrIp = $Cur['Visible'] && $Cur['IP'] != '127.0.0.1' ? '1' : '0'; + \Tracker::update_tracker('add_user', array('id' => $this->id, + 'passkey' => $Cur['torrent_pass'], 'visible' => $VisibleTrIp)); + if (($Cur['Downloaded'] == 0) || ($Cur['Uploaded'] / $Cur['Downloaded'] >= + $Cur['RequiredRatio'])) { + $UpdateSet[] = "ui.RatioWatchEnds = '0000-00-00 00:00:00'"; + $UpdateSet[] = "um.can_leech = '1'"; + $UpdateSet[] = "ui.RatioWatchDownload = '0'"; + } else { + if ($Cur['RatioWatchEnds'] != '0000-00-00 00:00:00') { + $UpdateSet[] = "ui.RatioWatchEnds = NOW()"; + $UpdateSet[] = "ui.RatioWatchDownload = um.Downloaded"; + $Comment .= ' (Ratio: '.\Format::get_ratio_html($Cur['Uploaded'], + $Cur['Downloaded'], false).', RR: '.number_format($Cur['RequiredRatio'], 2).')'; + } + \Tracker::update_tracker('update_user', array('passkey' => $Cur['torrent_pass'], + 'can_leech' => 0)); + } + $UpdateSet[] = "ui.BanReason = '0'"; + $UpdateSet[] = "um.Enabled = '1'"; + + $set = implode(', ', $UpdateSet); + + $this->db->prepared_query(" + UPDATE users_main AS um + JOIN users_info AS ui ON um.ID = ui.UserID + SET + {$set}, + ui.AdminComment = CONCAT('".sqltime()." - ".$Comment."\n\n', ui.AdminComment) + WHERE + um.ID = ?", $Cur['ID']); + + return array('enabled' => true, 'user_id' => $Cur['ID'], 'username' => $Cur['Username']); + } +} diff --git a/app/API/Wiki.php b/app/API/Wiki.php new file mode 100644 index 000000000..2c6e255f3 --- /dev/null +++ b/app/API/Wiki.php @@ -0,0 +1,28 @@ +db->prepared_query(" + SELECT + wa.Title, + wa.MinClassRead, + um.Username AS Author, + wa.Date + FROM + wiki_articles AS wa + INNER JOIN users_main AS um ON um.ID = wa.Author + WHERE + wa.ID = ?", $_GET['wiki_id']); + if (!$this->db->has_results()) { + json_error('Wiki article not found'); + } + $article = $this->db->next_record(MYSQLI_ASSOC, false); + return $article; + } +} diff --git a/app/Artist.php b/app/Artist.php index 0253c9994..2f901186a 100644 --- a/app/Artist.php +++ b/app/Artist.php @@ -3,29 +3,29 @@ namespace Gazelle; class Artist { - /** @var \DB_MYSQL */ - private $db; + /** @var \DB_MYSQL */ + private $db; - /** @var \CACHE */ - private $cache; + /** @var \CACHE */ + private $cache; - const CACHE_ALIAS = 'artist_alias_%d_%s'; + const CACHE_ALIAS = 'artist_alias_%d_%s'; - public function __construct ($db, $cache) { - $this->db = $db; - $this->cache = $cache; - } + public function __construct (\DB_MYSQL $db, \CACHE $cache) { + $this->db = $db; + $this->cache = $cache; + } - public function get_alias($id, $name) { - $key = sprintf(self::CACHE_ALIAS, $id, $name); // use this later on - $this->db->prepared_query(' - SELECT AliasID - FROM artists_alias - WHERE ArtistID = ? - AND ArtistID != AliasID - AND Name = ?', - $id, $name); - list($alias) = $this->db->next_record(); - return empty($alias) ? $id : $alias; - } + public function get_alias($id, $name) { + $key = sprintf(self::CACHE_ALIAS, $id, $name); // use this later on + $this->db->prepared_query(' + SELECT AliasID + FROM artists_alias + WHERE ArtistID = ? + AND ArtistID != AliasID + AND Name = ?', + $id, $name); + list($alias) = $this->db->next_record(); + return empty($alias) ? $id : $alias; + } } diff --git a/app/Bonus.php b/app/Bonus.php index 8646c92a5..62b8bc5c8 100644 --- a/app/Bonus.php +++ b/app/Bonus.php @@ -3,260 +3,326 @@ namespace Gazelle; class Bonus { - private $items; - /** @var \DB_MYSQL */ - private $db; - /** @var \CACHE */ - private $cache; + private $items; + /** @var \DB_MYSQL */ + private $db; + /** @var \CACHE */ + private $cache; - const CACHE_ITEM = 'bonus_item'; - const CACHE_SUMMARY = 'bonus_summary.'; - const CACHE_HISTORY = 'bonus_history.'; + const CACHE_ITEM = 'bonus_item'; + const CACHE_OPEN_POOL = 'bonus_pool'; + const CACHE_SUMMARY = 'bonus_summary.'; + const CACHE_HISTORY = 'bonus_history.'; + const CACHE_POOL_HISTORY = 'bonus_pool_history.'; - public function __construct ($db, $cache) { - $this->db = $db; - $this->cache = $cache; - $this->items = $this->cache->get_value(self::CACHE_ITEM); - if ($this->items === false) { - $this->db->query("SELECT ID, Price, Amount, MinClass, FreeClass, Label, Title FROM bonus_item"); - $this->items = $this->db->has_results() ? $this->db->to_array('Label') : []; - $this->cache->cache_value(self::CACHE_ITEM, $this->items, 86400 * 30); - } - } + public function __construct (\DB_MYSQL $db, \CACHE $cache) { + $this->db = $db; + $this->cache = $cache; + $this->items = $this->cache->get_value(self::CACHE_ITEM); + if ($this->items === false) { + $this->db->query("SELECT ID, Price, Amount, MinClass, FreeClass, Label, Title FROM bonus_item"); + $this->items = $this->db->has_results() ? $this->db->to_array('Label') : []; + $this->cache->cache_value(self::CACHE_ITEM, $this->items, 86400 * 30); + } + } - public function getList() { - return $this->items; - } + public function getList() { + return $this->items; + } - public function getItem($label) { - return array_key_exists($label, $this->items) ? $this->items[$label] : null; - } + public function getItem($label) { + return array_key_exists($label, $this->items) ? $this->items[$label] : null; + } - public function getTorrentValue($format, $media, $encoding, $haslogdb = 0, $logscore = 0, $logchecksum = 0) { - if ($format == 'FLAC') { - if ($media == 'CD' && $haslogdb && $logscore === 100 && $logchecksum == 1) { - return BONUS_AWARD_FLAC_PERFECT; - } - elseif (in_array($media, ['Vinyl', 'WEB', 'DVD', 'Soundboard', 'Cassette', 'SACD', 'Blu-ray', 'DAT'])) { - return BONUS_AWARD_FLAC_PERFECT; - } - else { - return BONUS_AWARD_FLAC; - } - } - elseif ($format == 'MP3' && in_array($encoding, ['V2 (VBR)', 'V0 (VBR)', '320'])) { - return BONUS_AWARD_MP3; - } - return BONUS_AWARD_OTHER; - } + public function getTorrentValue($format, $media, $encoding, $haslogdb = 0, $logscore = 0, $logchecksum = 0) { + if ($format == 'FLAC') { + if ($media == 'CD' && $haslogdb && $logscore === 100 && $logchecksum == 1) { + return BONUS_AWARD_FLAC_PERFECT; + } + elseif (in_array($media, ['Vinyl', 'WEB', 'DVD', 'Soundboard', 'Cassette', 'SACD', 'Blu-ray', 'DAT'])) { + return BONUS_AWARD_FLAC_PERFECT; + } + else { + return BONUS_AWARD_FLAC; + } + } + elseif ($format == 'MP3' && in_array($encoding, ['V2 (VBR)', 'V0 (VBR)', '320'])) { + return BONUS_AWARD_MP3; + } + return BONUS_AWARD_OTHER; + } - public function getEffectivePrice($label, $effective_class) { - $item = $this->items[$label]; - return $effective_class >= $item['FreeClass'] ? 0 : $item['Price']; - } + public function getEffectivePrice($label, $effective_class) { + $item = $this->items[$label]; + return $effective_class >= $item['FreeClass'] ? 0 : $item['Price']; + } - public function getListOther($balance) { - $list_other = []; - foreach ($this->items as $label => $item) { - if (preg_match('/^other-\d$/', $label) && $balance >= $item['Price']) { - $list_other[] = [ - 'Label' => $item['Label'], - 'Name' => $item['Title'], - 'Price' => $item['Price'], - 'After' => $balance - $item['Price'], - ]; - } - } - return $list_other; - } + public function getListOther($balance) { + $list_other = []; + foreach ($this->items as $label => $item) { + if (preg_match('/^other-\d$/', $label) && $balance >= $item['Price']) { + $list_other[] = [ + 'Label' => $item['Label'], + 'Name' => $item['Title'], + 'Price' => $item['Price'], + 'After' => $balance - $item['Price'], + ]; + } + } + return $list_other; + } - public function getUserSummary($user_id) { - $key = self::CACHE_SUMMARY . $user_id; - $summary = $this->cache->get_value($key); - if ($summary === false) { - $this->db->prepared_query('SELECT count(*) AS nr, sum(price) AS total FROM bonus_history WHERE UserID = ?', $user_id); - $summary = $this->db->has_results() ? $this->db->next_record(MYSQLI_ASSOC) : ['nr' => 0, 'total' => 0]; - $this->cache->cache_value($key, $summary, 86400 * 7); - } - return $summary; - } + public function getOpenPool() { + $key = self::CACHE_OPEN_POOL; + $pool = $this->cache->get_value($key); + if ($pool === false) { + $this->db->prepared_query('SELECT Id, Name, Total FROM bonus_pool WHERE now() BETWEEN SinceDate AND UntilDate'); + $pool = $this->db->next_record(); + $this->cache->cache_value($key, $pool, 3600); + } + return $pool; + } - public function getUserHistory($user_id, $page, $items_per_page) { - $key = self::CACHE_HISTORY . "{$user_id}.{$page}"; - $history = $this->cache->get_value($key); - if ($history === false) { - $this->db->prepared_query(' - SELECT i.Title, h.Price, h.PurchaseDate, h.OtherUserID - FROM bonus_history h - INNER JOIN bonus_item i ON i.ID = h.ItemID - WHERE h.UserID = ? - ORDER BY PurchaseDate DESC - LIMIT ? OFFSET ? - ', $user_id, $items_per_page, $items_per_page * ($page-1) - ); - $history = $this->db->has_results() ? $this->db->to_array() : null; - $this->cache->cache_value($key, $history, 86400 * 3); - /* since we had to fetch this page, invalidate the next one */ - $this->cache->delete_value(self::CACHE_HISTORY . "{$user_id}." . ($page+1)); - } - return $history; - } + public function donate($pool_id, $value, $user_id, $effective_class) { + if ($effective_class < 250) { + $taxed_value = $value * BONUS_POOL_TAX_STD; + } + elseif($effective_class == 250 /* Elite */) { + $taxed_value = $value * BONUS_POOL_TAX_ELITE; + } + elseif($effective_class <= 500 /* EliteTM */) { + $taxed_value = $value * BONUS_POOL_TAX_TM; + } + else { + $taxed_value = $value * BONUS_POOL_TAX_STAFF; + } - public function purchaseInvite($user_id) { - $item = $this->items['invite']; - if (!\Users::canPurchaseInvite($user_id, $item['MinClass'])) { - return false; - } + $this->db->begin_transaction(); + $this->db->prepared_query( + "UPDATE users_main SET BonusPoints = BonusPoints - ? WHERE BonusPoints >= ? AND ID = ?", + $value, $value, $user_id + ); + if ($this->db->affected_rows() != 1) { + $this->db->rollback(); + return false; + } + $pool = new \Gazelle\BonusPool($this->db, $this->cache, $pool_id); + $pool->contribute($user_id, $value, $taxed_value); + $this->db->commit(); + $this->cache->delete_value(self::CACHE_OPEN_POOL); + $this->cache->delete_value(self::CACHE_POOL_HISTORY . $user_id); + $this->cache->delete_value('user_stats_' . $user_id); + $this->cache->delete_value('user_info_heavy_' . $user_id); + return true; + } - $this->db->begin_transaction(); - $this->db->prepared_query( - "UPDATE users_main SET Invites = Invites + 1, BonusPoints = BonusPoints - ? WHERE BonusPoints >= ? AND ID = ?", - $item['Price'], $item['Price'], $user_id - ); - if ($this->db->affected_rows() != 1) { - $this->db->rollback(); - return false; - } + public function getUserSummary($user_id) { + $key = self::CACHE_SUMMARY . $user_id; + $summary = $this->cache->get_value($key); + if ($summary === false) { + $this->db->prepared_query('SELECT count(*) AS nr, sum(price) AS total FROM bonus_history WHERE UserID = ?', $user_id); + $summary = $this->db->has_results() ? $this->db->next_record(MYSQLI_ASSOC) : ['nr' => 0, 'total' => 0]; + $this->cache->cache_value($key, $summary, 86400 * 7); + } + return $summary; + } - $this->addPurchaseHistory($item['ID'], $user_id, $item['Price']); - $this->db->commit(); - $this->cache->delete_value('user_stats_' . $user_id); - $this->cache->delete_value('user_info_heavy_' . $user_id); - return true; - } + public function getUserHistory($user_id, $page, $items_per_page) { + $key = self::CACHE_HISTORY . "{$user_id}.{$page}"; + $history = $this->cache->get_value($key); + if ($history === false) { + $this->db->prepared_query(' + SELECT i.Title, h.Price, h.PurchaseDate, h.OtherUserID + FROM bonus_history h + INNER JOIN bonus_item i ON i.ID = h.ItemID + WHERE h.UserID = ? + ORDER BY PurchaseDate DESC + LIMIT ? OFFSET ? + ', $user_id, $items_per_page, $items_per_page * ($page-1) + ); + $history = $this->db->has_results() ? $this->db->to_array() : null; + $this->cache->cache_value($key, $history, 86400 * 3); + /* since we had to fetch this page, invalidate the next one */ + $this->cache->delete_value(self::CACHE_HISTORY . "{$user_id}." . ($page+1)); + } + return $history; + } - public function purchaseTitle($user_id, $label, $title, $effective_class) { - $item = $this->items[$label]; - $title = $label === 'title-bb-y' ? \Text::full_format($title) : \Text::strip_bbcode($title); - $price = $this->getEffectivePrice($label, $effective_class); - $stats = \Users::user_stats($user_id, true); - if ($stats['BonusPoints'] < $price) { - return false; - } + public function getUserPoolHistory($user_id) { + $key = self::CACHE_POOL_HISTORY . $user_id; + $history = $this->cache->get_value($key); + if ($history === false) { + $this->db->prepared_query(' + SELECT sum(c.amountrecv) AS Total, p.UntilDate, p.Name + FROM bonus_pool_contrib c + INNER JOIN bonus_pool p ON (p.ID = c.BonusPoolID) + WHERE c.UserID = ? + GROUP BY p.UntilDate, p.Name + ORDER BY p.UntilDate, p.Name + ', $user_id + ); + $history = $this->db->has_results() ? $this->db->to_array() : null; + $this->cache->cache_value($key, $history, 86400 * 3); + /* since we had to fetch this page, invalidate the next one */ + } + return $history; + } - $this->db->begin_transaction(); - if ($price > 0) { - /* if the price is 0, nothing changes so avoid hitting the db */ - $this->db->prepared_query( - 'UPDATE users_main SET BonusPoints = BonusPoints - ? WHERE BonusPoints >= ? AND ID = ?', - $price, $price, $user_id - ); - if ($this->db->affected_rows() != 1) { - $this->db->rollback(); - return false; - } - // Sanity check - $new_stats = \Users::user_stats($user_id, true); - if (!($new_stats['BonusPoints'] >= 0 && $new_stats['BonusPoints'] < $stats['BonusPoints'])) { - $this->db->rollback(); - return false; - } - } - if (!\Users::setCustomTitle($user_id, $title)) { - $this->db->rollback(); - return false; - } - $this->addPurchaseHistory($item['ID'], $user_id, $price); - $this->db->commit(); - $this->cache->delete_value('user_info_heavy_' . $user_id); - return true; - } + public function purchaseInvite($user_id) { + $item = $this->items['invite']; + if (!\Users::canPurchaseInvite($user_id, $item['MinClass'])) { + return false; + } - public function purchaseToken($user_id, $label) { - if (!array_key_exists($label, $this->items)) { - return false; - } - $item = $this->items[$label]; - $amount = $item['Amount']; - $price = $item['Price']; - $stats = \Users::user_stats($user_id, true); - if ($stats['BonusPoints'] < $price) { - return false; - } - $this->db->begin_transaction(); - $this->db->prepared_query( - 'UPDATE users_main SET FLTokens = FLTokens + ?, BonusPoints = BonusPoints - ? WHERE BonusPoints >= ? AND ID = ?', - $amount, $price, $price, $user_id - ); - if ($this->db->affected_rows() != 1) { - $this->db->rollback(); - return false; - } - $new_stats = \Users::user_stats($user_id, true); - if (!($new_stats['BonusPoints'] >= 0 && $new_stats['BonusPoints'] < $stats['BonusPoints'])) { - $this->db->rollback(); - return false; - } - $this->addPurchaseHistory($item['ID'], $user_id, $price); - $this->db->commit(); - $this->cache->delete_value('user_info_heavy_' . $user_id); - return true; - } + $this->db->begin_transaction(); + $this->db->prepared_query( + "UPDATE users_main SET Invites = Invites + 1, BonusPoints = BonusPoints - ? WHERE BonusPoints >= ? AND ID = ?", + $item['Price'], $item['Price'], $user_id + ); + if ($this->db->affected_rows() != 1) { + $this->db->rollback(); + return false; + } - public function purchaseTokenOther($fromID, $toID, $label, &$logged_user) { - if ($fromID === $toID) { - return 0; - } - if (!array_key_exists($label, $this->items)) { - return 0; - } - $item = $this->items[$label]; - $amount = $item['Amount']; - $price = $item['Price']; - if (!isset($price) and !($price > 0)) { - return 0; - } - $From = \Users::user_info($fromID); - $To = \Users::user_info($toID); - if ($From['Enabled'] != 1 || $To['Enabled'] != 1) { - return 0; - } - // get the bonus points of the giver from the database - // verify they could be legally spent, and then update the receiver - $stats = \Users::user_stats($fromID, true); - if ($stats['BonusPoints'] < $price) { - return 0; - } - $this->db->begin_transaction(); - $this->db->prepared_query('UPDATE users_main SET BonusPoints = BonusPoints - ? WHERE BonusPoints >= 0 AND ID = ?', $price, $fromID); - if ($this->db->affected_rows() != 1) { - $this->db->rollback(); - return 0; - } - $new_stats = \Users::user_stats($fromID, true); - if (!($new_stats['BonusPoints'] >= 0 && $new_stats['BonusPoints'] < $stats['BonusPoints'])) { - $this->db->rollback(); - return 0; - } - $this->db->prepared_query("UPDATE users_main SET FLTokens = FLTokens + ? WHERE ID=?", $amount, $toID); - if ($this->db->affected_rows() != 1) { - $this->db->rollback(); - return 0; - } - $this->addPurchaseHistory($item['ID'], $fromID, $price, $toID); - $this->db->commit(); + $this->addPurchaseHistory($item['ID'], $user_id, $item['Price']); + $this->db->commit(); + $this->cache->delete_value('user_stats_' . $user_id); + $this->cache->delete_value('user_info_heavy_' . $user_id); + return true; + } - $this->cache->delete_value("user_info_heavy_{$fromID}"); - $this->cache->delete_value("user_info_heavy_{$toID}"); - // the calling code may not know this has been invalidated, so we cheat - $logged_user['BonusPoints'] = $new_stats['BonusPoints']; - self::sendPmToOther($From['Username'], $toID, $amount); + public function purchaseTitle($user_id, $label, $title, $effective_class) { + $item = $this->items[$label]; + $title = $label === 'title-bb-y' ? \Text::full_format($title) : \Text::strip_bbcode($title); + $price = $this->getEffectivePrice($label, $effective_class); + $stats = \Users::user_stats($user_id, true); + if ($stats['BonusPoints'] < $price) { + return false; + } - return $amount; - } + $this->db->begin_transaction(); + if ($price > 0) { + /* if the price is 0, nothing changes so avoid hitting the db */ + $this->db->prepared_query( + 'UPDATE users_main SET BonusPoints = BonusPoints - ? WHERE BonusPoints >= ? AND ID = ?', + $price, $price, $user_id + ); + if ($this->db->affected_rows() != 1) { + $this->db->rollback(); + return false; + } + // Sanity check + $new_stats = \Users::user_stats($user_id, true); + if (!($new_stats['BonusPoints'] >= 0 && $new_stats['BonusPoints'] < $stats['BonusPoints'])) { + $this->db->rollback(); + return false; + } + } + if (!\Users::setCustomTitle($user_id, $title)) { + $this->db->rollback(); + return false; + } + $this->addPurchaseHistory($item['ID'], $user_id, $price); + $this->db->commit(); + $this->cache->delete_value('user_info_heavy_' . $user_id); + return true; + } - public function sendPmToOther($from, $toID, $amount) { - if ($amount > 1) { - $is_are = 'are'; - $s = 's'; - } - else { - $is_are = 'is'; - $s = ''; - } - $to = \Users::user_info($toID); - $Body = "Hello {$to['Username']}, + public function purchaseToken($user_id, $label) { + if (!array_key_exists($label, $this->items)) { + return false; + } + $item = $this->items[$label]; + $amount = $item['Amount']; + $price = $item['Price']; + $stats = \Users::user_stats($user_id, true); + if ($stats['BonusPoints'] < $price) { + return false; + } + $this->db->begin_transaction(); + $this->db->prepared_query( + 'UPDATE users_main SET FLTokens = FLTokens + ?, BonusPoints = BonusPoints - ? WHERE BonusPoints >= ? AND ID = ?', + $amount, $price, $price, $user_id + ); + if ($this->db->affected_rows() != 1) { + $this->db->rollback(); + return false; + } + $new_stats = \Users::user_stats($user_id, true); + if (!($new_stats['BonusPoints'] >= 0 && $new_stats['BonusPoints'] < $stats['BonusPoints'])) { + $this->db->rollback(); + return false; + } + $this->addPurchaseHistory($item['ID'], $user_id, $price); + $this->db->commit(); + $this->cache->delete_value('user_info_heavy_' . $user_id); + return true; + } + + public function purchaseTokenOther($fromID, $toID, $label, &$logged_user) { + if ($fromID === $toID) { + return 0; + } + if (!array_key_exists($label, $this->items)) { + return 0; + } + $item = $this->items[$label]; + $amount = $item['Amount']; + $price = $item['Price']; + if (!isset($price) and !($price > 0)) { + return 0; + } + $From = \Users::user_info($fromID); + $To = \Users::user_info($toID); + if ($From['Enabled'] != 1 || $To['Enabled'] != 1) { + return 0; + } + // get the bonus points of the giver from the database + // verify they could be legally spent, and then update the receiver + $stats = \Users::user_stats($fromID, true); + if ($stats['BonusPoints'] < $price) { + return 0; + } + $this->db->begin_transaction(); + $this->db->prepared_query('UPDATE users_main SET BonusPoints = BonusPoints - ? WHERE BonusPoints >= 0 AND ID = ?', $price, $fromID); + if ($this->db->affected_rows() != 1) { + $this->db->rollback(); + return 0; + } + $new_stats = \Users::user_stats($fromID, true); + if (!($new_stats['BonusPoints'] >= 0 && $new_stats['BonusPoints'] < $stats['BonusPoints'])) { + $this->db->rollback(); + return 0; + } + $this->db->prepared_query("UPDATE users_main SET FLTokens = FLTokens + ? WHERE ID=?", $amount, $toID); + if ($this->db->affected_rows() != 1) { + $this->db->rollback(); + return 0; + } + $this->addPurchaseHistory($item['ID'], $fromID, $price, $toID); + $this->db->commit(); + + $this->cache->delete_value("user_info_heavy_{$fromID}"); + $this->cache->delete_value("user_info_heavy_{$toID}"); + // the calling code may not know this has been invalidated, so we cheat + $logged_user['BonusPoints'] = $new_stats['BonusPoints']; + self::sendPmToOther($From['Username'], $toID, $amount); + + return $amount; + } + + public function sendPmToOther($from, $toID, $amount) { + if ($amount > 1) { + $is_are = 'are'; + $s = 's'; + } + else { + $is_are = 'is'; + $s = ''; + } + $to = \Users::user_info($toID); + $Body = "Hello {$to['Username']}, {$from} has sent you {$amount} freeleech token{$s} for you to use! " . "You can use them to download torrents without getting charged any download. " . @@ -264,16 +330,22 @@ public function sendPmToOther($from, $toID, $amount) { "[url=".site_url()."wiki.php?action=article&id=57]the wiki[/url]. Enjoy!"; - \Misc::send_pm($toID, 0, "Here {$is_are} {$amount} freeleech token{$s}!", trim($Body)); - } + \Misc::send_pm($toID, 0, "Here {$is_are} {$amount} freeleech token{$s}!", trim($Body)); + } + + private function addPurchaseHistory($item_id, $user_id, $price, $other_user_id = null) { + $this->cache->delete_value(self::CACHE_SUMMARY . $user_id); + $this->cache->delete_value(self::CACHE_HISTORY . $user_id . ".1"); + $this->db->prepared_query( + 'INSERT INTO bonus_history (ItemID, UserID, price, OtherUserID) VALUES (?, ?, ?, ?)', + $item_id, $user_id, $price, $other_user_id + ); + return $this->db->affected_rows(); + } - private function addPurchaseHistory($item_id, $user_id, $price, $other_user_id = null) { - $this->cache->delete_value(self::CACHE_SUMMARY . $user_id); - $this->cache->delete_value(self::CACHE_HISTORY . $user_id . ".1"); - $this->db->prepared_query( - 'INSERT INTO bonus_history (ItemID, UserID, price, OtherUserID) VALUES (?, ?, ?, ?)', - $item_id, $user_id, $price, $other_user_id - ); - return $this->db->affected_rows(); - } + public function addPoints ($user_id, $amount) { + $this->db->prepared_query('UPDATE users_main SET BonusPoints = BonusPoints + ? WHERE ID = ?', $amount, $user_id); + $this->cache->delete_value("user_info_heavy_{$user_id}"); + $this->cache->delete_value("user_stats_{$user_id}"); + } } diff --git a/app/BonusPool.php b/app/BonusPool.php new file mode 100644 index 000000000..730241e2d --- /dev/null +++ b/app/BonusPool.php @@ -0,0 +1,42 @@ +db = $db; + $this->cache = $cache; + $this->id = $id; + } + + public function contribute($user_id, $value_recv, $value_sent) { + $this->db->prepared_query( + 'INSERT INTO bonus_pool_contrib (BonusPoolID, UserID, AmountRecv, AmountSent) VALUES (?, ?, ?, ?)', + $this->id, $user_id, $value_recv, $value_sent + ); + $this->db->prepared_query( + 'UPDATE bonus_pool SET Total = Total + ? WHERE ID = ?', + $value_sent, $this->id + ); + $this->cache->delete_value(sprintf(self::CACHE_SENT, $this->id)); + } + + public function getTotalSent() { + $key = sprintf(self::CACHE_SENT, $this->id); + $total = $this->cache->get_value($key); + if ($total == false) { + $this->db->prepared_query('SELECT Total FROM bonus_pool WHERE ID = ?', $this->id); + $total = $this->db->has_results() ? $this->db->next_record()[0] : 0; + $this->cache->cache_value($key, $total, 6 * 3600); + } + return $total; + } +} diff --git a/app/Contest.php b/app/Contest.php new file mode 100644 index 000000000..93edb15ad --- /dev/null +++ b/app/Contest.php @@ -0,0 +1,596 @@ +db = $db; + $this->cache = $cache; + $this->type = $this->cache->get_value(self::CACHE_CONTEST_TYPE); + if ($this->type === false) { + $this->db->query("SELECT ID, Name FROM contest_type ORDER BY ID"); + $this->type = $this->db->to_array('ID'); + $this->cache->cache_value(self::CACHE_CONTEST_TYPE, $this->type, 86400 * 7); + } + } + + public function get_type () { + return $this->type; + } + + public function get_list () { + $this->db->query(" + SELECT c.ID, c.Name, c.DateBegin, c.DateEnd, t.ID AS ContestType, (cbp.BonusPoolID IS NOT NULL) AS BonusPool, cbp.Status AS BonusStatus + FROM contest c + INNER JOIN contest_type t ON (t.ID = c.ContestTypeID) + LEFT JOIN contest_has_bonus_pool cbp ON (cbp.ContestID = c.ID) + ORDER BY c.DateBegin DESC + "); + return $this->db->to_array(); + } + + public function get_contest ($Id) { + $key = sprintf(self::CACHE_CONTEST, $Id); + $Contest = $this->cache->get_value($key); + if ($Contest === false) { + $this->db->prepared_query(' + SELECT c.ID, t.Name AS ContestType, c.Name, c.Banner, c.WikiText, c.Display, c.MaxTracked, c.DateBegin, c.DateEnd, + coalesce(cbp.BonusPoolID, 0) AS BonusPool, + cbp.Status AS BonusStatus, + CASE WHEN now() BETWEEN c.DateBegin AND c.DateEnd THEN 1 ELSE 0 END AS is_open, + CASE WHEN cbp.BonusPoolID IS NOT NULL AND cbp.Status = ? AND now() > c.DateEnd THEN 1 ELSE 0 END AS payout_ready + FROM contest c + INNER JOIN contest_type t ON (t.ID = c.ContestTypeID) + LEFT JOIN contest_has_bonus_pool cbp ON (cbp.ContestID = c.ID) + WHERE c.ID = ? + ', 'open', $Id + ); + if ($this->db->has_results()) { + $Contest = $this->db->next_record(MYSQLI_ASSOC); + $this->cache->cache_value($key, $Contest, 86400 * 3); + } + } + return $Contest; + } + + public function get_current_contest () { + $Contest = $this->cache->get_value(self::CACHE_CURRENT); + if ($Contest === false) { + $this->db->prepared_query(' + SELECT c.ID, t.Name AS ContestType, c.Name, c.Banner, c.WikiText, c.Display, c.MaxTracked, c.DateBegin, c.DateEnd, + coalesce(cbp.BonusPoolID, 0) AS BonusPool, + cbp.Status AS BonusStatus, + CASE WHEN now() BETWEEN c.DateBegin AND c.DateEnd THEN 1 ELSE 0 END AS is_open, + CASE WHEN cbp.BonusPoolID IS NOT NULL AND cbp.Status = ? AND now() > c.DateEnd THEN 1 ELSE 0 END AS payout_ready + FROM contest c + INNER JOIN contest_type t ON (t.ID = c.ContestTypeID) + LEFT JOIN contest_has_bonus_pool cbp ON (cbp.ContestID = c.ID) + WHERE c.DateEnd = (select max(DateEnd) from contest) + ', 'open'); + if ($this->db->has_results()) { + $Contest = $this->db->next_record(MYSQLI_ASSOC); + // Cache this for three days + $this->cache->cache_value(sprintf(self::CACHE_CONTEST, $Contest['ID']), $Contest, 86400 * 3); + $this->cache->cache_value(self::CACHE_CURRENT, $Contest, 86400 * 3); + } + } + return $Contest; + } + + public function get_prior_contests () { + $Prior = $this->cache->get_value('contest_prior'); + if ($Prior === false) { + $this->db->query(" + SELECT c.ID + FROM contest c + WHERE c.DateBegin < NOW() + /* AND ... we may want to think about excluding certain past contests */ + ORDER BY c.DateBegin ASC + "); + if ($this->db->has_results()) { + $Prior = $this->db->to_array(false, MYSQLI_BOTH); + $this->cache->cache_value('contest_prior', $Prior, 86400 * 3); + } + } + return $Prior; + } + + private function leaderboard_query ($Contest) { + /* only called from schedule, don't need to worry about caching this */ + switch ($Contest['ContestType']) { + case 'upload_flac': + /* how many 100% flacs uploaded? */ + $sql = " + SELECT u.ID AS userid, + count(*) AS nr, + max(t.ID) AS last_torrent + FROM users_main u + INNER JOIN torrents t ON (t.Userid = u.ID) + WHERE t.Format = 'FLAC' + AND t.Time BETWEEN ? AND ? + AND ( + t.Media IN ('Vinyl', 'WEB') + OR (t.Media = 'CD' + AND t.HasLog = '1' + AND t.HasCue = '1' + AND t.LogScore = 100 + AND t.LogChecksum = '1' + ) + ) + GROUP By u.ID + "; + $args = [ + $Contest['DateBegin'], + $Contest['DateEnd'] + ]; + break; + + case 'upload_flac_no_single': + /* how many non-Single 100% flacs uploaded? */ + $sql = " + SELECT u.ID AS userid, + count(*) AS nr, + max(t.ID) AS last_torrent + FROM users_main u + INNER JOIN torrents t ON (t.Userid = u.ID) + INNER JOIN torrents_group g ON (t.GroupID = g.ID) + INNER JOIN release_type r ON (g.ReleaseType = r.ID) + WHERE r.Name != 'Single' + AND t.Format = 'FLAC' + AND t.Time BETWEEN ? AND ? + AND ( + t.Media IN ('Vinyl', 'WEB', 'SACD') + OR (t.Media = 'CD' + AND t.HasLog = '1' + AND t.HasCue = '1' + AND t.LogScore = 100 + AND t.LogChecksum = '1' + ) + ) + GROUP By u.ID + "; + $args = [ + $Contest['DateBegin'], + $Contest['DateEnd'] + ]; + break; + + case 'request_fill': + /* how many requests filled */ + $sql = " + SELECT r.FillerID AS userid, + count(*) AS nr, + max(if(r.TimeFilled = LAST.TimeFilled AND r.TimeAdded < ?, TorrentID, NULL)) AS last_torrent + FROM requests r + INNER JOIN ( + SELECT r.FillerID, + MAX(r.TimeFilled) AS TimeFilled + FROM requests r + INNER JOIN users_main u ON (r.FillerID = u.ID) + INNER JOIN torrents t ON (r.TorrentID = t.ID) + WHERE r.TimeFilled BETWEEN ? AND ? + AND r.FIllerId != r.UserID + AND r.TimeAdded < ? + GROUP BY r.FillerID + ) LAST USING (FillerID) + WHERE r.TimeFilled BETWEEN ? AND ? + AND r.FIllerId != r.UserID + AND r.TimeAdded < ? + GROUP BY r.FillerID + "; + $args = [ + $Contest['DateBegin'], + $Contest['DateBegin'], + $Contest['DateEnd'], + $Contest['DateBegin'], + $Contest['DateBegin'], + $Contest['DateEnd'], + $Contest['DateBegin'] + ]; + break; + default: + $sql = null; + $args = []; + break; + } + return [$sql, $args]; + } + + public function calculate_leaderboard () { + $this->db->query(" + SELECT c.ID + FROM contest c + INNER JOIN contest_type t ON (t.ID = c.ContestTypeID) + WHERE c.DateEnd > now() - INTERVAL 1 MONTH + ORDER BY c.DateEnd DESC + "); + $contest_id = []; + while ($this->db->has_results()) { + $c = $this->db->next_record(); + if (isset($c['ID'])) { + $contest_id[] = $c['ID']; + } + } + foreach ($contest_id as $id) { + $Contest = $this->get_contest($id); + list($subquery, $args) = $this->leaderboard_query($Contest); + array_unshift($args, $id); + if ($subquery) { + $this->db->query("BEGIN"); + $this->db->prepared_query('DELETE FROM contest_leaderboard WHERE ContestID = ?', $id); + $this->db->prepared_query_array(" + INSERT INTO contest_leaderboard + SELECT ?, LADDER.userid, + LADDER.nr, + T.ID, + TG.Name, + group_concat(TA.ArtistID), + group_concat(AG.Name order by AG.Name separator 0x1), + T.Time + FROM torrents_group TG + LEFT JOIN torrents_artists TA ON (TA.GroupID = TG.ID) + LEFT JOIN artists_group AG ON (AG.ArtistID = TA.ArtistID) + INNER JOIN torrents T ON (T.GroupID = TG.ID) + INNER JOIN ( + $subquery + ) LADDER on (LADDER.last_torrent = T.ID) + GROUP BY + LADDER.nr, + T.ID, + TG.Name, + T.Time + ", $args); + $this->db->query("COMMIT"); + $this->cache->delete_value('contest_leaderboard_' . $id); + switch ($Contest['ContestType']) { + case 'upload_flac': + $this->db->prepared_query(" + SELECT count(*) AS nr + FROM torrents t + WHERE t.Format = 'FLAC' + AND t.Time BETWEEN ? AND ? + AND ( + t.Media IN ('Vinyl', 'WEB') + OR (t.Media = 'CD' + AND t.HasLog = '1' + AND t.HasCue = '1' + AND t.LogScore = 100 + AND t.LogChecksum = '1' + ) + ) + ", $Contest['DateBegin'], $Contest['DateEnd'] + ); + break; + case 'upload_flac_no_single': + $this->db->prepared_query(" + SELECT count(*) AS nr + FROM torrents t + INNER JOIN torrents_group g ON (t.GroupID = g.ID) + INNER JOIN release_type r ON (g.ReleaseType = r.ID) + WHERE r.Name != 'Single' + AND t.Format = 'FLAC' + AND t.Time BETWEEN ? AND ? + AND ( + t.Media IN ('Vinyl', 'WEB', 'SACD') + OR (t.Media = 'CD' + AND t.HasLog = '1' + AND t.HasCue = '1' + AND t.LogScore = 100 + AND t.LogChecksum = '1' + ) + ) + ", $Contest['DateBegin'], $Contest['DateEnd'] + ); + break; + case 'request_fill': + $this->db->prepared_query(" + SELECT + count(*) AS nr + FROM requests r + INNER JOIN users_main u ON (r.FillerID = u.ID) + WHERE r.TimeFilled BETWEEN ? AND ? + AND r.FIllerId != r.UserID + AND r.TimeAdded < ? + ", $Contest['DateBegin'], $Contest['DateEnd'], $Contest['DateBegin'] + ); + break; + default: + $this->db->prepared_query("SELECT 0"); + break; + } + $this->cache->cache_value( + "contest_leaderboard_total_{$Contest['ID']}", + $this->db->has_results() ? $this->db->next_record()[0] : 0, + 3600 * 6 + ); + $this->get_leaderboard($id, false); + } + } + } + + public function get_leaderboard ($Id, $UseCache = true) { + $Contest = $this->get_contest($Id); + $Key = "contest_leaderboard_{$Contest['ID']}"; + $Leaderboard = $this->cache->get_value($Key); + if (!$UseCache || $Leaderboard === false) { + $this->db->query(" + SELECT + l.UserID, + l.FlacCount, + l.LastTorrentID, + l.LastTorrentNAme, + l.ArtistList, + l.ArtistNames, + l.LastUpload + FROM contest_leaderboard l + WHERE l.ContestID = {$Contest['ID']} + ORDER BY l.FlacCount DESC, l.LastUpload ASC, l.UserID ASC + LIMIT {$Contest['MaxTracked']}"); + $Leaderboard = $this->db->to_array(false, MYSQLI_BOTH); + $this->cache->cache_value($Key, $Leaderboard, 60 * 20); + } + return $Leaderboard; + } + + public function calculate_request_pairs () { + $Contest = $this->get_current_contest(); + if ($Contest['ContestType'] != 'request_fill') { + $Pairs = []; + } + else { + $this->db->query(" + SELECT r.FillerID, r.UserID, count(*) AS nr + FROM requests r + WHERE r.TimeFilled BETWEEN '{$Contest['DateBegin']}' AND '{$Contest['DateEnd']}' + GROUP BY + r.FillerID, r.UserId + HAVING count(*) > 1 + ORDER BY + count(*) DESC, r.FillerID ASC + LIMIT 100 + "); + $Pairs = $this->db->to_array(false, MYSQLI_BOTH); + } + $this->cache->cache_value('contest_pairs_' . $Contest['ID'], $Pairs, 60 * 20); + } + + public function get_request_pairs ($UseCache = true) { + $Contest = $this->get_current_contest(); + $Key = "contest_pairs_{$Contest['ID']}"; + if (($Pairs = $this->cache->get_value($Key)) === false) { + $this->calculate_request_pairs(); + $Pairs = $this->cache->get_value($Key); + } + return $Pairs; + } + + public function calculate_pool_payout ($id) { + list($total_torrents, $total_users) = $this->get_upload_stats($id); + $bonuspool = new \Gazelle\BonusPool($this->db, $this->cache, $this->get_contest($id)['BonusPool']); + return [ + 'torrent' => $total_torrents, + 'user' => $total_users, + 'bonus' => $bonuspool->getTotalSent() + ]; + } + + public function get_upload_stats ($id) { + $this->db->prepared_query(" + SELECT count(*) AS nr_torrents, count(DISTINCT um.ID) AS nr_users + FROM contest c, + users_main um + INNER JOIN users_info ui ON (ui.UserID = um.ID) + INNER JOIN torrents t ON (t.Userid = um.ID) + INNER JOIN torrents_group g ON (t.GroupID = g.ID) + INNER JOIN release_type r ON (g.ReleaseType = r.ID) + WHERE + c.ID = ? + AND um.Enabled = '1' + AND ui.JoinDate <= c.DateEnd + AND r.Name != 'Single' + AND t.Format = 'FLAC' + AND t.Time BETWEEN c.DateBegin AND c.DateEnd + AND ( + t.Media IN ('Vinyl', 'WEB', 'SACD') + OR (t.Media = 'CD' + AND t.HasLog = '1' + AND t.HasCue = '1' + AND t.LogScore = 100 + AND t.LogChecksum = '1' + ) + ) + ", $id); + return $this->db->next_record(); + } + + public function schedule_payout () { + $this->db->prepared_query(' + SELECT c.ID + FROM contest c + INNER JOIN contest_has_bonus_pool cbp ON (cbp.ContestID = c.ID) + WHERE c.DateEnd < now() + AND cbp.Status = ? + ', 'ready' + ); + $Contests = $this->db->to_array(false, MYSQLI_NUM); + $total_participants = 0; + foreach ($Contests as $c) { + $total_participants += $this->do_payout($c[0]); + $this->set_payment_closed($c[0]); + } + return $total_participants; + } + + protected function do_payout ($id) { + $total = $this->calculate_pool_payout($id); + $bonus = $total['bonus']; + $enabled_user_bonus = $bonus * 0.05 / \Users::get_enabled_users_count(); + $contest_participation = $bonus * 0.1 / $total['user']; + $per_entry_bonus = $bonus * 0.85 / $total['torrent']; + + $enabled_user_bonus_fmt = number_format($enabled_user_bonus, 2); + $contest_participation_fmt = number_format($contest_participation, 2); + $per_entry_bonus_fmt = number_format($per_entry_bonus, 2); + + $this->db->prepared_query(" + SELECT um.ID, um.Username, count(t.ID) AS nr_entries, ? AS enabled_bonus, + CASE WHEN count(t.ID) > 0 THEN ? ELSE 0 END AS participated_bonus, + count(t.ID) * ? AS entries_bonus + FROM contest c, + users_main um + INNER JOIN users_info ui ON (ui.UserID = um.ID) + LEFT JOIN torrents t ON (t.UserID = um.ID) + LEFT JOIN torrents_group g ON (t.GroupID = g.ID) + LEFT JOIN release_type r ON (g.ReleaseType = r.ID) + WHERE + c.ID = ? + AND um.Enabled = '1' + AND ui.JoinDate <= c.DateEnd + AND + (t.ID IS NULL + OR ( + r.Name != 'Single' + AND t.Format = 'FLAC' + AND t.Time BETWEEN c.DateBegin AND c.DateEnd + AND ( + t.Media IN ('Vinyl', 'WEB', 'SACD') + OR (t.Media = 'CD' + AND t.HasLog = '1' + AND t.HasCue = '1' + AND t.LogScore = 100 + AND t.LogChecksum = '1' + ) + ) + ) + ) + GROUP BY um.ID + ", $enabled_user_bonus, $contest_participation, $per_entry_bonus, $id + ); + $participants = $this->db->to_array('ID', MYSQLI_ASSOC); + $contest = $this->get_contest($id); + $bonus = new \Gazelle\Bonus($this->db, $this->cache); + + $report = fopen(TMPDIR . "/payout-contest-$id.txt", 'a'); + foreach ($participants as $p) { + if ($p['nr_entries'] == 0) { + $total_gain = $enabled_user_bonus; + $total_gain_fmt = number_format($enabled_user_bonus, 2); + $msg = <<addPoints($p['ID'], $total_gain); + \Misc::send_pm($p['ID'], 0, "You have received {$total_gain_fmt} bonus points!", $msg); + $this->db->prepared_query(" + UPDATE users_info + SET AdminComment = CONCAT(?, AdminComment) + WHERE UserID = ? + ", sqltime() . " - {$total_gain_fmt} BP added for {$p['nr_entries']} entries in {$contest['Name']}\n\n", $p['ID'] + ); + fwrite($report, sqltime() . " {$p['Username']} ({$p['ID']}) n={$p['nr_entries']} t={$total_gain_fmt}\n"); + fflush($report); + } + fclose($report); + return count($participants); + } + + public function set_payment_ready ($id) { + $this->db->prepared_query(' + UPDATE contest_has_bonus_pool + SET Status = ? + WHERE ContestID = ? + ', 'ready', $id + ); + $this->cache->delete_value(sprintf(self::CACHE_CONTEST, $id)); + return $this->db->affected_rows(); + } + + public function set_payment_closed ($id) { + $this->db->prepared_query(' + UPDATE contest_has_bonus_pool + SET Status = ? + WHERE ContestID = ? + ', 'paid', $id + ); + $this->cache->delete_value(sprintf(self::CACHE_CONTEST, $id)); + return $this->db->affected_rows(); + } + + public function save ($params) { + if (isset($params['cid'])) { + $contest_id = $params['cid']; + $this->db->prepared_query(" + UPDATE contest SET + Name = ?, Display = ?, MaxTracked = ?, DateBegin = ?, DateEnd = ?, + ContestTypeID = ?, Banner = ?, WikiText = ? + WHERE ID = ? + ", $params['name'], $params['display'], $params['maxtrack'], $params['date_begin'], $params['date_end'], + $params['type'], $params['banner'], $params['intro'], + $contest_id + ); + if (isset($params['payment'])) { + $this->set_payment_ready($contest_id); + } + $this->cache->delete_value(self::CACHE_CURRENT); + $this->cache->delete_value(sprintf(self::CACHE_CONTEST, $contest_id)); + } + elseif (isset($params['new'])) { + $this->db->prepared_query(" + INSERT INTO contest (Name, Display, MaxTracked, DateBegin, DateEnd, ContestTypeID, Banner, WikiText) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ", + $params['name'], $params['display'], $params['maxtrack'], $params['date_begin'], $params['date_end'], + $params['type'], $params['banner'], $params['intro'] + ); + $contest_id = $this->db->inserted_id(); + + if (array_key_exists('pool', $params)) { + $this->db->prepared_query("INSERT INTO bonus_pool (Name, SinceDate, UntilDate) VALUES (?, ?, ?)", + $params['name'], $params['date_begin'], $params['date_end'] + ); + $pool_id = $this->db->inserted_id(); + $this->db->prepared_query("INSERT INTO contest_has_bonus_pool (ContestID, BonusPoolID) VALUES (?, ?)", + $contest_id, $pool_id + ); + } + } + return $contest_id; + } +} diff --git a/app/DB.php b/app/DB.php index 6eb2f13d2..3c0268ad6 100644 --- a/app/DB.php +++ b/app/DB.php @@ -3,71 +3,76 @@ namespace Gazelle; class DB { - /** @var \DB_MYSQL */ - private $db; + /** @var \DB_MYSQL */ + private $db; - /** @var \CACHE */ - private $cache; + /** @var \CACHE */ + private $cache; - public function __construct ($db, $cache) { - $this->db = $db; - $this->cache = $cache; - } + public function __construct (\DB_MYSQL $db, \CACHE $cache) { + $this->db = $db; + $this->cache = $cache; + } - /** - * Soft delete a row from a table by inserting it into deleted_ and then delete from - * @param string $schema the schema name - * @param string $table the table name - * @param array $condition Must be an array of arrays, e.g. [[column_name, column_value]] or [[col1, val1], [col2, val2]] - * Will be used to identify the row (or rows) to delete - * @return array 2 elements, true/false and message if false - */ - public function soft_delete($schema, $table, array $condition) { - $sql = 'SELECT column_name, column_type FROM information_schema.columns WHERE table_schema = ? AND table_name = ? ORDER BY 1'; - $this->db->prepared_query($sql, $schema, $table); - $t1 = $this->db->to_array(); - $n1 = count($t1); + /** + * Soft delete a row from a table by inserting it into deleted_ and then delete from + * @param string $schema the schema name + * @param string $table the table name + * @param array $condition Must be an array of arrays, e.g. [[column_name, column_value]] or [[col1, val1], [col2, val2]] + * Will be used to identify the row (or rows) to delete + * @param boolean $delete whether to delete the matched rows + * @return array 2 elements, true/false and message if false + */ + public function soft_delete($schema, $table, array $condition, $delete = true) { + $sql = 'SELECT column_name, column_type FROM information_schema.columns WHERE table_schema = ? AND table_name = ? ORDER BY 1'; + $this->db->prepared_query($sql, $schema, $table); + $t1 = $this->db->to_array(); + $n1 = count($t1); - $soft_delete_table = 'deleted_' . $table; - $this->db->prepared_query($sql, $schema, $soft_delete_table); - $t2 = $this->db->to_array(); - $n2 = count($t2); + $soft_delete_table = 'deleted_' . $table; + $this->db->prepared_query($sql, $schema, $soft_delete_table); + $t2 = $this->db->to_array(); + $n2 = count($t2); - if (!$n1) { - return [false, "No such table $table"]; - } - elseif (!$n2) { - return [false, "No such table $soft_delete_table"]; - } - elseif ($n1 != $n2) { - // tables do not have the same number of columns - return [false, "$table and $soft_delete_table column count mismatch ($n1 != $n2)"]; - } + if (!$n1) { + return [false, "No such table $table"]; + } + elseif (!$n2) { + return [false, "No such table $soft_delete_table"]; + } + elseif ($n1 != $n2) { + // tables do not have the same number of columns + return [false, "$table and $soft_delete_table column count mismatch ($n1 != $n2)"]; + } - $column = []; - for ($i = 0; $i < $n1; ++$i) { - // a column does not have the same name or datatype - if (strtolower($t1[$i][0]) != strtolower($t2[$i][0]) || $t1[$i][1] != $t2[$i][1]) { - return [false, "column {$t1[$i][0]} name or datatype mismatch {$t1[$i][0]}:{$t2[$i][0]} {$t1[$i][1]}:{$t2[$i][1]}"]; - } - $column[] = $t1[$i][0]; - } - $column_list = implode(', ', $column); - $condition_list = implode(' AND ', array_map(function ($c) {return "{$c[0]} = ?";}, $condition)); - $arg_list = array_map(function ($c) {return $c[1];}, $condition); + $column = []; + for ($i = 0; $i < $n1; ++$i) { + // a column does not have the same name or datatype + if (strtolower($t1[$i][0]) != strtolower($t2[$i][0]) || $t1[$i][1] != $t2[$i][1]) { + return [false, "{$table}: column {$t1[$i][0]} name or datatype mismatch {$t1[$i][0]}:{$t2[$i][0]} {$t1[$i][1]}:{$t2[$i][1]}"]; + } + $column[] = $t1[$i][0]; + } + $column_list = implode(', ', $column); + $condition_list = implode(' AND ', array_map(function ($c) {return "{$c[0]} = ?";}, $condition)); + $arg_list = array_map(function ($c) {return $c[1];}, $condition); - $sql = "INSERT INTO $soft_delete_table - ($column_list) - SELECT $column_list - FROM $table - WHERE $condition_list"; - $this->db->prepared_query_array($sql, $arg_list); - if ($this->db->affected_rows() == 0) { - return [false, "condition selected 0 rows"]; - } + $sql = "INSERT INTO $soft_delete_table + ($column_list) + SELECT $column_list + FROM $table + WHERE $condition_list"; + $this->db->prepared_query_array($sql, $arg_list); + if ($this->db->affected_rows() == 0) { + return [false, "condition selected 0 rows"]; + } - $sql = "DELETE FROM $table WHERE $condition_list"; - $this->db->prepared_query_array($sql, $arg_list); - return [true, "rows deleted: " . $this->db->affected_rows()]; - } + if (!$delete) { + return [true, "rows affected: " . $this->db->affected_rows()]; + } + + $sql = "DELETE FROM $table WHERE $condition_list"; + $this->db->prepared_query_array($sql, $arg_list); + return [true, "rows deleted: " . $this->db->affected_rows()]; + } } diff --git a/app/Exception/InvalidAccessException.php b/app/Exception/InvalidAccessException.php index 9c0cefd61..da975fbfa 100644 --- a/app/Exception/InvalidAccessException.php +++ b/app/Exception/InvalidAccessException.php @@ -5,7 +5,7 @@ use Throwable; class InvalidAccessException extends \RuntimeException { - public function __construct(string $message = 'You are not authorized to access this action', int $code = 0, Throwable $previous = null) { - parent::__construct($message, $code, $previous); - } -} \ No newline at end of file + public function __construct(string $message = 'You are not authorized to access this action', int $code = 0, Throwable $previous = null) { + parent::__construct($message, $code, $previous); + } +} diff --git a/app/Exception/RouterException.php b/app/Exception/RouterException.php index 3143d6e7e..8b4873625 100644 --- a/app/Exception/RouterException.php +++ b/app/Exception/RouterException.php @@ -5,7 +5,7 @@ use Throwable; class RouterException extends \RuntimeException { - public function __construct(string $message = "The route you tried to access is not available.", int $code = 0, Throwable $previous = null) { - parent::__construct($message, $code, $previous); - } -} \ No newline at end of file + public function __construct(string $message = "The route you tried to access is not available.", int $code = 0, Throwable $previous = null) { + parent::__construct($message, $code, $previous); + } +} diff --git a/app/Manager/Referral.php b/app/Manager/Referral.php new file mode 100644 index 000000000..869e9e521 --- /dev/null +++ b/app/Manager/Referral.php @@ -0,0 +1,497 @@ +db = $db; + $this->cache = $cache; + $this->accounts = $this->cache->get_value(self::CACHE_ACCOUNTS); + $this->proxy = new \Gazelle\Util\Proxy(REFERRAL_KEY, REFERRAL_BOUNCER); + + if ($this->accounts === false) { + $this->db->query("SELECT ID, Site, Active, Type FROM referral_accounts"); + $this->accounts = $this->db->has_results() ? $this->db->to_array('ID') : []; + foreach ($this->accounts as &$acc) { + $acc["UserIsId"] = in_array($acc["Type"], self::ID_TYPES); + unset($acc); + } + $this->cache->cache_value(self::CACHE_ACCOUNTS, $this->accounts, 86400 * 30); + } + + $this->readOnly = !apcu_exists('DB_KEY'); + } + + public function generateToken() { + return 'OPS|' . \Users::make_secret(64) . '|OPS'; + } + + public function getTypes() { + return self::ACCOUNT_TYPES; + } + + public function getAccounts() { + return $this->accounts; + } + + public function getActiveAccounts() { + return array_filter($this->accounts, + function ($i) { return $i['Active'] == '1' && !$this->readOnly; }); + } + + public function getAccount($id) { + return array_key_exists($id, $this->accounts) ? $this->accounts[$id] : null; + } + + public function getFullAccount($id) { + $this->db->prepared_query(" + SELECT ID, Site, URL, User, Password, Active, Type, Cookie + FROM referral_accounts + WHERE ID = ?", $id); + + if ($this->db->has_results()) { + $account = $this->db->next_record(); + foreach (array('URL', 'User', 'Password', 'Cookie') as $key) { + if (array_key_exists($key, $account)) { + $account[$key] = \Gazelle\Util\Crypto::dbDecrypt($account[$key]); + } + } + $account["Cookie"] = json_decode($account["Cookie"], true); + $account["UserIsId"] = in_array($account["Type"], self::ID_TYPES); + return $account; + } else { + return null; + } + } + + public function getFullAccounts() { + $this->db->prepared_query(" + SELECT ID, Site, URL, User, Password, Active, Type, Cookie + FROM referral_accounts"); + + if ($this->db->has_results()) { + $accounts = $this->db->to_array('ID', MYSQLI_ASSOC); + foreach ($accounts as &$account) { + foreach (array('URL', 'User', 'Password', 'Cookie') as $key) { + if (array_key_exists($key, $account)) { + $account[$key] = \Gazelle\Util\Crypto::dbDecrypt($account[$key]); + } + } + $account["Cookie"] = json_decode($account["Cookie"], true); + $account["UserIsId"] = in_array($account["Type"], self::ID_TYPES); + } + return $accounts; + } + return []; + } + + public function createAccount($site, $url, $user, $password, $active, $type, $cookie) { + if (!$this->readOnly) { + if (strlen($cookie) < 2) { + $cookie = '[]'; + } + json_decode($cookie); + if (json_last_error() != JSON_ERROR_NONE) { + $cookie = '[]'; + } + $this->db->prepared_query(" + INSERT INTO referral_accounts + (Site, URL, User, Password, Active, Type, Cookie) + VALUES + (?, ?, ?, ?, ?, ?, ?)", $site, \Gazelle\Util\Crypto::dbEncrypt($url), + \Gazelle\Util\Crypto::dbEncrypt($user), \Gazelle\Util\Crypto::dbEncrypt($password), + $active, $type, \Gazelle\Util\Crypto::dbEncrypt($cookie)); + + $this->cache->delete_value(self::CACHE_ACCOUNTS); + } + } + + private function updateCookie($id, $cookie) { + if (!$this->readOnly) { + $this->db->prepared_query(" + UPDATE referral_accounts SET + Cookie = ? + WHERE ID = ?", \Gazelle\Util\Crypto::dbEncrypt(json_encode($cookie)), $id); + } + } + + public function updateAccount($id, $site, $url, $user, $password, $active, $type, $cookie) { + if (!$this->readOnly) { + $account = $this->getFullAccount($id); + if (strlen($cookie) < 2) { + $cookie = '[]'; + } + json_decode($cookie); + if (json_last_error() != JSON_ERROR_NONE) { + $cookie = '[]'; + } + if ($cookie == '[]') { + $cookie = json_encode($account["Cookie"]); + } + if (strlen($password) == 0) { + $password = $account["Password"]; + } + $this->db->prepared_query(" + UPDATE referral_accounts SET + Site = ?, + URL = ?, + User = ?, + Password = ?, + Active = ?, + Type = ?, + Cookie = ? + WHERE ID = ?", $site, \Gazelle\Util\Crypto::dbEncrypt($url), + \Gazelle\Util\Crypto::dbEncrypt($user), \Gazelle\Util\Crypto::dbEncrypt($password), + $active, $type, \Gazelle\Util\Crypto::dbEncrypt($cookie), $id); + + $this->cache->delete_value(self::CACHE_ACCOUNTS); + } + } + + public function deleteAccount($id) { + $this->db->prepared_query("DELETE FROM referral_accounts WHERE ID = ?", $id); + + $this->cache->delete_value(self::CACHE_ACCOUNTS); + } + + public function getReferredUsers($startDate, $endDate, $site, $username, $invite, $limit, $view) { + if ($startDate == NULL) { + $startDate = \Gazelle\Util\Time::timeOffset(-(3600 * 24 * 30), true); + } + + if ($endDate == NULL) { + $endDate = \Gazelle\Util\Time::sqlTime(); + } + + $Filter = ['ru.Created BETWEEN ? AND ?']; + $Params = [$startDate, $endDate]; + + if ($view === 'pending') { + $Filter[] = 'ru.Active = 0'; + } else if ($view === 'processed') { + $Filter[] = 'ru.Active = 1'; + } + + if ($site != NULL) { + $Filter[] = 'ru.Site LIKE ?'; + $Params[] = $site; + } + + if ($username != NULL) { + $Filter[] = '(ru.Username LIKE ? OR um.Username LIKE ?)'; + $Params[] = $username; + $Params[] = $username; + } + + if ($invite != NULL) { + $Filter[] = 'ru.InviteKey LIKE ?'; + $Params[] = $invite; + } + + $Filter = implode(' AND ', $Filter); + + $qId = $this->db->prepared_query(" + SELECT SQL_CALC_FOUND_ROWS ru.ID, ru.UserID, ru.Site, ru.Username, ru.Created, ru.Joined, ru.IP, ru.Active, ru.InviteKey + FROM referral_users ru + LEFT JOIN users_main um ON um.ID = ru.UserID + WHERE $Filter + ORDER BY ru.Created DESC + LIMIT $limit", ...$Params); + $this->db->prepared_query("SELECT FOUND_ROWS()"); + list($Results) = $this->db->next_record(); + $this->db->set_query_id($qId); + + $Users = $Results > 0 ? $this->db->to_array('ID', MYSQLI_ASSOC) : []; + + return array("Results" => $Results, "Users" => $Users); + } + + public function deleteUserReferral($id) { + $this->db->prepared_query(" + DELETE FROM referral_users + WHERE ID = ?", + $id); + } + + public function validateCookie($acc) { + switch ($acc["Type"]) { + case 0: + return $this->validateGazelleCookie($acc); + break; + case 1: + return true; + break; + case 2: + return false; + break; + case 3: + case 4: + case 5: + return $this->validateLuminanceCookie($acc); + break; + } + return false; + } + + private function validateGazelleCookie($acc) { + $url = $acc["URL"] . 'ajax.php'; + + $result = $this->proxy->fetch($url, array("action" => "index"), $acc["Cookie"], false); + $json = json_decode($result["response"], true); + + return $json["status"] === 'success'; + } + + private function validateLuminanceCookie($acc) { + $url = $acc["URL"]; + + $result = $this->proxy->fetch($url, [], $acc["Cookie"], false); + $match = strpos($result["response"], "authkey"); + + return $match !== false; + } + + public function loginAccount(&$acc) { + switch ($acc["Type"]) { + case 0: + return $this->loginGazelleAccount($acc); + break; + case 1: + return true; + break; + case 2: + return false; + case 3: + return $this->loginLuminanceAccount($acc); + break; + case 4: + return $this->loginGazelleHTMLAccount($acc); + break; + case 5: + return false; + } + return false; + } + + private function loginGazelleAccount(&$acc) { + if ($this->validateGazelleCookie($acc)) { + return true; + } + + $url = $acc["URL"] . "login.php"; + + $result = $this->proxy->fetch($url, array("username" => $acc["User"], + "password" => $acc["Password"], "keeplogged" => "1"), [], true); + + if ($result["status"] == 200) { + $acc["Cookie"] = $result["cookies"]; + $this->updateCookie($acc["ID"], $acc["Cookie"]); + } + + return $result["status"] == 200; + } + + private function loginLuminanceAccount(&$acc) { + if ($this->validateLuminanceCookie($acc)) { + return true; + } + + $url = $acc["URL"] . "login"; + + $result = $this->proxy->fetch($url, [], [], false); + $doc = new \DOMDocument(); + libxml_use_internal_errors(true); + @$doc->loadHTML($result["response"]); + $xpath = new \DOMXPath($doc); + $token = $xpath->evaluate("string(//input[@name='token']/@value)"); + + $result = $this->proxy->fetch($url, array("username" => $acc["User"], + "password" => $acc["Password"], "keeploggedin" => "1", + "token" => $token, "cinfo" => "1024|768|24|0", + "iplocked" => "1"), $result["cookies"], true); + + if ($result["status"] == 200) { + $acc["Cookie"] = $result["cookies"]; + $this->updateCookie($acc["ID"], $acc["Cookie"]); + } + + return $result["status"] == 200; + } + + private function loginGazelleHTMLAccount(&$acc) { + if ($this->validateLuminanceCookie($acc)) { + return true; + } + + $url = $acc["URL"] . "login.php"; + + $result = $this->proxy->fetch($url, array("username" => $acc["User"], + "password" => $acc["Password"], "keeplogged" => "1"), [], true); + + if ($result["status"] == 200) { + $acc["Cookie"] = $result["cookies"]; + $this->updateCookie($acc["ID"], $acc["Cookie"]); + } + + return $result["status"] == 200; + } + + public function verifyAccount($acc, $user, $key) { + switch ($acc["Type"]) { + case 0: + return $this->verifyGazelleAccount($acc, $user, $key); + break; + case 1: + return false; + case 2: + return false; + case 3: + return $this->verifyLuminanceAccount($acc, $user, $key); + break; + case 4: + return $this->verifyGazelleHTMLAccount($acc, $user, $key); + break; + case 5: + return false; + } + return "Unrecognised account type"; + } + + private function verifyGazelleAccount($acc, $user, $key) { + if (!$this->loginGazelleAccount($acc)) { + return "Internal error"; + } + + $url = $acc["URL"] . 'ajax.php'; + + $result = $this->proxy->fetch($url, array("action" => "usersearch", "search" => $user), + $acc["Cookie"], false); + $json = json_decode($result["response"], true); + + if ($json["status"] === 'success') { + $match = false; + foreach ($json["response"]["results"] as $userResult) { + if ($userResult["username"] == $user) { + $match = true; + $userId = $userResult["userId"]; + break; + } + } + + if ($match) { + $result = $this->proxy->fetch($url, array("action" => "user", "id" => $userId), + $acc["Cookie"], false); + $json = json_decode($result["response"], true); + + $profile = $json["response"]["profileText"]; + $match = strpos($profile, $key); + + if ($match !== false) { + return true; + } else { + return "Token not found. Please try again."; + } + } + } + + return "Token not found. Please try again."; + } + + private function verifyLuminanceAccount($acc, $user, $key) { + if (!$this->loginLuminanceAccount($acc)) { + return "Internal error"; + } + + $url = $acc["URL"] . 'user.php'; + + $result = $this->proxy->fetch($url, array("id" => $user), $acc["Cookie"], false); + + $profile = $result["response"]; + $match = strpos($profile, $key); + + if ($match !== false) { + return true; + } else { + return "Token not found. Please try again."; + } + } + + private function verifyGazelleHTMLAccount($acc, $user, $key) { + if (!$this->loginGazelleHTMLAccount($acc)) { + return "Internal error"; + } + + $url = $acc["URL"] . 'user.php'; + + $result = $this->proxy->fetch($url, array("id" => $user), + $acc["Cookie"], false); + + $profile = $result["response"]; + $match = strpos($profile, $key); + + if ($match !== false) { + return true; + } else { + return "Token not found. Please try again."; + } + } + + public function generateInvite($acc, $username, $email, $twig) { + $this->db->prepared_query(" + SELECT Username + FROM referral_users + WHERE Username = ? AND Site = ?", + $username, $acc["Site"]); + if ($this->db->has_results()) { + return [false, "Account already used for referral, join " . BOT_DISABLED_CHAN . " on " . BOT_SERVER . " for help."]; + } + + $InviteExpires = time_plus(60 * 60 * 24 * 3); // 3 days + $InviteReason = 'This user was referred from their account on ' . $acc["Site"] . '.'; + $InviteKey = \Users::make_secret(); + + // save invite to DB + $this->db->prepared_query(" + INSERT INTO invites + (InviterID, InviteKey, Email, Expires, Reason) + VALUES + (?, ?, ?, ?, ?)", + 0, $InviteKey, $email, $InviteExpires, $InviteReason); + + // save to referral history + $this->db->prepared_query(" + INSERT INTO referral_users + (Username, Site, IP, InviteKey) + VALUES + (?, ?, ?, ?)", + $username, $acc["Site"], $_SERVER["REMOTE_ADDR"], $InviteKey); + + if (defined('REFERRAL_SEND_EMAIL') && REFERRAL_SEND_EMAIL) { + $message = $twig->render('emails/referral.twig', [ + 'Email' => $email, + 'InviteKey' => $InviteKey, + 'DISABLED_CHAN' => BOT_DISABLED_CHAN, + 'IRC_SERVER' => BOT_SERVER, + 'SITE_NAME' => SITE_NAME, + 'SITE_URL' => SITE_URL + ]); + // send email + \Misc::send_email($email, 'You have been invited to ' . SITE_NAME, $message, 'noreply', 'text/plain'); + } + + return [true, $InviteKey]; + } +} diff --git a/app/Recovery.php b/app/Recovery.php new file mode 100644 index 000000000..edd39c921 --- /dev/null +++ b/app/Recovery.php @@ -0,0 +1,614 @@ + 10 * 1024 * 1024) { + return [false, "File was too large, please make sure it is less than 10MB in size."]; + } + $filename = sha1(RECOVERY_SALT . mt_rand(0, 10000000). sha1_file($file['tmp_name'])); + $destination = sprintf('%s/%s/%s/%s/%s', + RECOVERY_PATH, substr($filename, 0, 1), substr($filename, 1, 1), substr($filename, 2, 1), $filename + ); + if (!move_uploaded_file($file['tmp_name'], $destination)) { + return [false, "Unable to persist your upload."]; + } + return [true, $filename]; + } + + static function check_password($user, $pw, $db) { + $db->prepared_query('SELECT PassHash FROM ' . RECOVERY_DB . '.users_main WHERE Username = ?', $user); + if (!$db->has_results()) { + return false; + } + list($prevhash) = $db->next_record(); + return password_verify($pw, $prevhash); + } + + static function persist($info, $db) { + $db->prepared_query( + "INSERT INTO recovery (token, ipaddr, username, password_ok, email, email_clean, announce, screenshot, invite, info, state ) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'PENDING')", + $info['token'], + $info['ipaddr'], + $info['username'], + $info['password_ok'], + $info['email'], + $info['email_clean'], + $info['announce'], + $info['screenshot'], + $info['invite'], + $info['info'] + ); + return $db->affected_rows(); + } + + static function get_total($state, $admin_id, $db) { + $state = strtoupper($state); + switch ($state) { + case 'CLAIMED': + $db->prepared_query("SELECT count(*) FROM recovery WHERE state = ? and admin_user_id = ?", $state, $admin_id); + break; + case 'PENDING': + $db->prepared_query("SELECT count(*) FROM recovery WHERE state = ? and (admin_user_id is null or admin_user_id != ?)", $state, $admin_id); + break; + default: + $db->prepared_query("SELECT count(*) FROM recovery WHERE state = ?", strtoupper($state)); + break; + } + list($total) = $db->next_record(); + return $total; + } + + static function get_list($limit, $offset, $state, $admin_id, $db) { + $state = strtoupper($state); + $sql_header = 'SELECT recovery_id, username, token, email, announce, created_dt, updated_dt, state FROM recovery'; + $sql_footer = 'ORDER BY updated_dt DESC LIMIT ? OFFSET ?'; + + switch ($state) { + case 'CLAIMED': + $db->prepared_query("$sql_header + WHERE admin_user_id = ? + $sql_footer + ", $admin_id, $limit, $offset + ); + break; + case 'PENDING': + $db->prepared_query("$sql_header + WHERE admin_user_id IS NULL + AND state = ? + $sql_footer + ", $state, $limit, $offset + ); + break; + default: + $db->prepared_query("$sql_header + WHERE state = ? + $sql_footer + ", $state, $limit, $offset + ); + break; + } + return $db->to_array(); + } + + static function validate_pending($db) { + $db->prepared_query("SELECT recovery_id + FROM recovery r + INNER JOIN " . RECOVERY_DB . ".users_main m ON (m.torrent_pass = r.announce) + WHERE r.state = 'PENDING' AND r.admin_user_id IS NULL AND char_length(r.announce) = 32 + LIMIT ? + ", RECOVERY_AUTOVALIDATE_LIMIT); + $recover = $db->to_array(); + foreach ($recover as $r) { + self::accept($r['recovery_id'], RECOVERY_ADMIN_ID, RECOVERY_ADMIN_NAME, $db); + } + + $db->prepared_query("SELECT recovery_id + FROM recovery r + INNER JOIN " . RECOVERY_DB . ".users_main m ON (m.Email = r.email) + WHERE r.state = 'PENDING' AND r.admin_user_id IS NULL AND locate('@', r.email) > 1 + LIMIT ? + ", RECOVERY_AUTOVALIDATE_LIMIT); + $recover = $db->to_array(); + foreach ($recover as $r) { + self::accept($r['recovery_id'], RECOVERY_ADMIN_ID, RECOVERY_ADMIN_NAME, $db); + } + } + + static function claim ($id, $admin_id, $admin_username, $db) { + $db->prepared_query(" + UPDATE recovery + SET admin_user_id = ?, + updated_dt = now(), + log = concat(coalesce(log, ''), ?) + WHERE recovery_id = ? + AND (admin_user_id IS NULL OR admin_user_id != ?) + ", $admin_id, + ("\r\n" . Date('Y-m-d H:i') . " claimed by $admin_username"), + $id, $admin_id + ); + return $db->affected_rows(); + } + + static function unclaim ($id, $admin_username, $db) { + $db->prepared_query(" + UPDATE recovery + SET admin_user_id = NULL, + state = 'PENDING', + updated_dt = now(), + log = concat(coalesce(log, ''), ?) + WHERE recovery_id = ? + ", ("\r\n" . Date('Y-m-d H:i') . " unclaimed by $admin_username"), + $id + ); + return $db->affected_rows(); + } + + static function deny ($id, $admin_id, $admin_username, $db) { + $db->prepared_query(" + UPDATE recovery + SET state = 'DENIED', + updated_dt = now(), + log = concat(coalesce(log, ''), ?) + WHERE recovery_id = ? + ", ("\r\n" . Date('Y-m-d H:i') . " recovery denied by $admin_username"), + $id + ); + return $db->affected_rows(); + } + + static function accept_fail($id, $reason, $db) { + $db->prepared_query(" + UPDATE recovery + SET state = 'PENDING', + updated_dt = now(), + log = concat(coalesce(log, ''), ?) + WHERE recovery_id = ? + ", ("\r\n" . Date('Y-m-d H:i') . " recovery failed: $reason"), + $id + ); + } + + static function accept ($id, $admin_id, $admin_username, $db) { + $db->prepared_query(" + SELECT username, email_clean + FROM recovery WHERE state != 'DENIED' AND recovery_id = ? + ", $id + ); + if (!$db->has_results()) { + return false; + } + list ($username, $email) = $db->next_record(); + + $db->prepared_query('select 1 from users_main where email = ?', $email); + if ($db->record_count() > 0) { + self::accept_fail($id, "an existing user $username already registered with $email", $db); + return false; + } + + $db->prepared_query('select InviteKey from invites where email = ?', $email); + if ($db->record_count() > 0) { + list($key) = $db->next_record(); + self::accept_fail($id, "invite key $key already issued to $email", $db); + return false; + } + + $key = db_string(\Users::make_secret()); + $db->prepared_query(" + INSERT INTO invites (InviterID, InviteKey, Email, Reason, Expires) + VALUES (?, ?, ?, ?, now() + interval 1 week) + ", $admin_id, $key, $email, "Account recovery id={$id} key={$key}" + ); + + $SITE_URL = SITE_URL; + $SITE_NAME = SITE_NAME; + $mail = <<. + +The information you provided was sufficient proof for confirm that you +did have in fact have an account, and consequently you have been given +an invitation. + +Please note that selling invites, trading invites, and giving invites +away publicly (e.g. on a forum) is strictly forbidden. If you do any of +these things with this invitation, do not bother signing up - you will +be banned, the person who used the invite will be banned and you and they +lose your chances of ever signing up in the future. + +To confirm your invite, click on the following link: + +https://$SITE_URL/register.php?invite=$key + +After you register, you will be able to use your account. Please take note +that if you do not use this invite in the next 3 days, it will expire. We +urge you to read the RULES and the wiki immediately after you join. + +MOST IMPORTANT OF ALL: + +You should read the following article: https://$SITE_URL/wiki.php?action=article&id=114 + +This will help you understand what you need to do to begin reseeding +your old torrents (and avoid downloading them all over again by accident, +thereby destroying your buffer). + +Thank you, + Staff +END_EMAIL; + + \Misc::send_email($email, 'Account recovery confirmation at '.SITE_NAME, $mail, 'noreply'); + + $db->prepared_query(" + UPDATE recovery + SET state = ?, + updated_dt = now(), + log = concat(coalesce(log, ''), ?) + WHERE recovery_id = ? + ", ($admin_id == RECOVERY_ADMIN_ID ? 'VALIDATED' : 'ACCEPTED'), + ("\r\n" . Date('Y-m-d H:i') . " recovery accepted by $admin_username invite=$key"), + $id + ); + return true; + } + + static function get_details ($id, $db) { + $db->prepared_query(" + SELECT + recovery_id, state, admin_user_id, created_dt, updated_dt, + token, username, ipaddr, password_ok, email, email_clean, + announce, screenshot, invite, info, log + FROM recovery + WHERE recovery_id = ? + ", $id + ); + return $db->next_record(); + } + + static function search ($terms, $db) { + $cond = []; + $args = []; + foreach ($terms as $t) { + foreach ($t as $field => $value) { + $cond[] = "$field = ?"; + $args[] = $value; + } + } + + $condition = implode(' AND ', $cond); + $db->prepared_query_array(" + SELECT + recovery_id, state, admin_user_id, created_dt, updated_dt, + token, username, ipaddr, password_ok, email, email_clean, + announce, screenshot, invite, info, log + FROM recovery + WHERE $condition + ", $args + ); + return $db->next_record(); + } + + static private function get_candidate_sql () { + return sprintf(' + SELECT m.Username, m.torrent_pass, m.Email, m.Uploaded, m.Downloaded, m.Enabled, m.PermissionID, m.ID as UserID, + (SELECT count(t.ID) FROM %s.torrents t WHERE m.ID = t.UserID) as nr_torrents, + group_concat(DISTINCT(h.IP) ORDER BY h.ip) as ips + FROM %s.users_main m LEFT JOIN %s.users_history_ips h ON (m.ID = h.UserID) + ', RECOVERY_DB, RECOVERY_DB, RECOVERY_DB + ); + } + + static function get_candidate ($username, $db) { + $db->prepared_query(self::get_candidate_sql() . " + WHERE m.Username LIKE ? GROUP BY m.Username + ", $username + ); + return $db->next_record(); + } + + static function get_candidate_by_username ($username, $db) { + $db->prepared_query(self::get_candidate_sql() . " + WHERE m.Username LIKE ? GROUP BY m.Username + ", $username + ); + return $db->to_array(); + } + + static function get_candidate_by_announce ($announce, $db) { + $db->prepared_query(self::get_candidate_sql() . " + WHERE m.torrent_pass LIKE ? GROUP BY m.torrent_pass + ", $announce + ); + return $db->to_array(); + } + + static function get_candidate_by_email ($email, $db) { + $db->prepared_query(self::get_candidate_sql() . " + WHERE m.Email LIKE ? GROUP BY m.Email + ", $email + ); + return $db->to_array(); + } + + static function get_candidate_by_id ($id, $db) { + $db->prepared_query(self::get_candidate_sql() . " + WHERE m.ID = ? GROUP BY m.ID + ", $id + ); + return $db->to_array(); + } + + static private function get_user_details_sql ($schema = null) { + if ($schema) { + $permission_t = "$schema.permissions"; + $users_main_t = "$schema.users_main"; + $torrents_t = "$schema.torrents"; + } + else { + $permission_t = 'permissions'; + $users_main_t = 'users_main'; + $torrents_t = 'torrents'; + } + return " + SELECT u.ID, u.Username, u.Email, u.torrent_pass, p.Name as UserClass, count(t.ID) as nr_torrents + FROM $users_main_t u + INNER JOIN $permission_t p ON (p.ID = u.PermissionID) + LEFT JOIN $torrents_t t ON (t.UserID = u.ID) + WHERE u.ID = ? + GROUP BY u.ID, u.Username, u.Email, u.torrent_pass, p.Name + "; + } + + static public function get_pair_confirmation($prev_id, $curr_id, $db) { + $db->prepared_query(self::get_user_details_sql(RECOVERY_DB), $prev_id); + $prev = $db->next_record(); + $db->prepared_query(self::get_user_details_sql(), $curr_id); + $curr = $db->next_record(); + return [$prev, $curr]; + } + + public static function is_mapped($ID, $db) { + $db->prepared_query(sprintf("SELECT MappedID AS ID FROM %s.%s WHERE UserID = ?", RECOVERY_DB, RECOVERY_MAPPING_TABLE), $ID); + return $db->to_array(); + } + + public static function is_mapped_local($ID, $db) { + $db->prepared_query(sprintf("SELECT UserID AS ID FROM %s.%s WHERE MappedID = ?", RECOVERY_DB, RECOVERY_MAPPING_TABLE), $ID); + return $db->to_array(); + } + + public static function map_to_previous($ops_user_id, $prev_user_id, $admin_username, $db) { + $db->prepared_query( + sprintf("INSERT INTO %s.%s (UserID, MappedID) VALUES (?, ?)", RECOVERY_DB, RECOVERY_MAPPING_TABLE), + $prev_user_id, $ops_user_id + ); + if ($db->affected_rows() != 1) { + return false; + } + + /* staff note */ + $db->prepared_query(" + UPDATE users_info + SET AdminComment = CONCAT(?, AdminComment) + WHERE UserID = ? + ", sqltime() . " mapped to previous id $prev_user_id by $admin_username\n\n", $ops_user_id + ); + return true; + } + + static function boost_upload ($db, $cache) { + $sql = sprintf(" + SELECT HIST.Username, HIST.MappedID, HIST.UserID, HIST.Uploaded, HIST.Downloaded, HIST.Bounty, HIST.nr_torrents, HIST.userclass, + round( + CASE + WHEN HIST.nr_torrents >= 500 THEN (1.5 * (500 + ((HIST.nr_torrents - 500) * 0.5)) - 3) * pow(1024, 3) + WHEN HIST.nr_torrents >= 50 AND HIST.nr_torrents < 500 THEN (1.5 * (100 + ((HIST.nr_torrents - 50) * 0.8)) - 3) * pow(1024, 3) + WHEN HIST.nr_torrents >= 5 AND HIST.nr_torrents < 50 THEN (1.5 * (25 + ((HIST.nr_torrents - 5) * 0.5)) - 3) * pow(1024, 3) + WHEN HIST.nr_torrents >= 1 AND HIST.nr_torrents < 5 THEN (1.5 * (5 + HIST.nr_torrents) - 3) * pow(1024, 3) + ELSE 0.0 + END + (HIST.Downloaded + HIST.bounty) * 0.5, + 0 + ) as new_up + FROM ( + SELECT uam.MappedID, uam.UserID, + u.Username, + u.Uploaded, + u.Downloaded, + lower(irc.userclass) as userclass, + coalesce(r.Bounty, 0) as Bounty, + count(t.ID) AS nr_torrents + FROM %s.users_main u + INNER JOIN %s.users_info ui ON (ui.UserID = u.ID) + LEFT JOIN %s.torrents t ON ( t.UserID = u.ID) + LEFT JOIN ( + SELECT UserID, sum(bounty) as Bounty + FROM %s.requests_votes + GROUP BY UserID + ) r ON (r.UserID = u.ID) + INNER JOIN %s.%s uam ON (uam.UserID = u.ID) + LEFT JOIN %s.%s irc ON (irc.UserID = u.ID) + LEFT JOIN users_buffer_log ubl ON (ubl.aplid = u.ID) + WHERE NOT uam.buffer + AND uam.UserID > 1 + AND ubl.aplid IS NULL + GROUP BY u.id + ) HIST + LIMIT ? + ", RECOVERY_DB, + RECOVERY_DB, + RECOVERY_DB, + RECOVERY_DB, + RECOVERY_DB, RECOVERY_MAPPING_TABLE, + RECOVERY_DB, RECOVERY_IRC_TABLE + ); + $db->prepared_query($sql, RECOVERY_BUFFER_REASSIGN_LIMIT); + + $rescale = [ + 'member' => 10.0 * pow(1024, 3), + 'poweruser' => 25.0 * pow(1024, 3), + 'elite' => 100.0 * pow(1024, 3), + 'torrentmaster' => 500.0 * pow(1024, 3), + 'powertm' => 500.0 * pow(1024, 3), + 'elitetm' => 500.0 * pow(1024, 3) + ]; + + $results = $db->to_array(); + foreach ($results as $r) { + list($username, $ops_user_id, $apl_user_id, $uploaded, $downloaded, $bounty, $nr_torrents, $irc_userclass, $final) = $r; + + /* close the gate */ + $db->prepared_query(sprintf(" + UPDATE %s.users_apl_mapping + SET buffer = true + WHERE MappedID = ? + ", RECOVERY_DB + ) + , $ops_user_id + ); + + /* upscale from IRC activity */ + $irc_change = ''; + if (array_key_exists($irc_userclass, $rescale)) { + $rescale_uploaded = 0.0 + $rescale[$irc_userclass]; + if ($rescale_uploaded > $final) { + $irc_message = "Upscaled from $uploaded to $rescale_uploaded from final irc userclass $irc_userclass"; + $final += 1.5 * ($rescale_uploaded - $final); + $irc_change = "\n\nThe above buffer calculation takes into account your final recorded userclass on IRC '$irc_userclass'"; + } + else { + $irc_message = "No change from logged irc userclass $irc_userclass"; + } + } + else { + $irc_message = 'never joined #APOLLO'; + } + + $uploaded_fmt = \Format::get_size($uploaded); + $downloaded_fmt = \Format::get_size($downloaded); + $bounty_fmt = \Format::get_size($bounty); + $final_fmt = \Format::get_size($final); + + $admin_comment = sprintf("%s - Upload stats recovery raw: Up=%d Down=%d Bounty=%d Torrents=%d IRC=%s" + . "\nformatted: U=%s D=%s B=%s Final=%s (%d) APL_ID=%d RESCALE=%s\n\n", + $username, $uploaded, $downloaded, $bounty, $nr_torrents, $irc_userclass, + $uploaded_fmt, $downloaded_fmt, $bounty_fmt, $final_fmt, $final, $apl_user_id, $irc_message + ); + + /* no buffer for you if < 1MB */ + if ($final >= 1.0) { + + $to = \Users::user_info($ops_user_id); + + $Body = <<prepared_query(" + INSERT INTO users_buffer_log + (opsid, aplid, uploaded, downloaded, bounty, nr_torrents, userclass, final) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ", $ops_user_id, $apl_user_id, $uploaded, $downloaded, $bounty, $nr_torrents, $irc_userclass, $final + ); + + /* staff note */ + $db->prepared_query(" + UPDATE users_info + SET AdminComment = CONCAT(?, AdminComment) + WHERE UserID = ? + ", $admin_comment, $ops_user_id + ); + + /* buffer */ + $db->prepared_query(" + UPDATE users_leech_stats + SET Uploaded = Uploaded + ? + WHERE ID = ? + ", $final, $ops_user_id + ); + + $cache->delete_value('user_stats_' . $ops_user_id); + } + } +} diff --git a/app/Report.php b/app/Report.php index da08f6b7d..83910d054 100644 --- a/app/Report.php +++ b/app/Report.php @@ -3,100 +3,100 @@ namespace Gazelle; class Report { - public static function search($db, array $filter) { - $cond = []; - $args = []; - $delcond = []; - $delargs = []; - if (array_key_exists('reporter', $filter) && $filter['reporter']) { - $cond[] = 'r.ReporterID = ?'; - $args[] = self::username2id($db, $filter['reporter']); - } - if (array_key_exists('handler', $filter) && $filter['handler']) { - $cond[] = 'r.ResolverID = ?'; - $args[] = self::username2id($db, $filter['handler']); - } - if (array_key_exists('report-type', $filter)) { - $cond[] = 'r.Type in (' . implode(', ', array_fill(0, count($filter['report-type']), '?')) . ')'; - $args = array_merge($args, $filter['report-type']); - } - if (array_key_exists('dt-from', $filter)) { - $cond[] = 'r.ReportedTime >= ?'; - $args[] = $filter['dt-from']; - } - if (array_key_exists('dt-until', $filter)) { - $rpt_cond[] = 'r.ReportedTime <= ? + INTERVAL 1 DAY'; - $rpt_args[] = $filter['dt-until']; - } - if (array_key_exists('torrent', $filter)) { - $rpt_cond[] = 'r.TorrentID = ?'; - $rpt_args[] = $filter['torrent']; - } - if (array_key_exists('uploader', $filter) && $filter['uploader']) { - $cond[] = 't.UserID = ?'; - $args[] = self::username2id($db, $filter['uploader']); - $delcond[] = 'dt.UserID = ?'; - $delargs[] = self::username2id($db, $filter['uploader']); - } - if (array_key_exists('group', $filter)) { - $cond[] = 't.GroupID = ?'; - $args[] = $filter['group']; - $delcond[] = 'dt.GroupID = ?'; - $delargs[] = $filter['group']; - } - if (count($cond) == 0 && count($delcond) == 0) { - $cond = ['1 = 1']; - } - $conds = implode(' AND ', $cond); - /* The construct below is pretty sick: we alias the group_log table to t - * which means that t.GroupID in a condition refers to the same thing in - * the `torrents` table as well. I am not certain this is entirely sane. - */ - $sql_where = implode("\n\t\tAND ", array_merge($cond, $delcond)); - $sql = " - SELECT SQL_CALC_FOUND_ROWS - r.ID, r.ReporterID, r.ResolverID, r.TorrentID, - coalesce(t.UserID, dt.UserID) as UserID, - coalesce(t.GroupID, dt.GroupID) as GroupID, - coalesce(t.Media, dt.Media) as Media, - coalesce(t.Format, dt.Format) as Format, - coalesce(t.Encoding, dt.Encoding) as Encoding, - coalesce(g.Name, gl.Info) as Name, g.Year, r.Type, r.ReportedTime - FROM reportsv2 r - LEFT JOIN torrents t ON (t.ID = r.TorrentID) - LEFT JOIN deleted_torrents dt ON (dt.ID = r.TorrentID) - LEFT JOIN torrents_group g on (g.ID = t.GroupID) - LEFT JOIN ( - SELECT max(t.ID) AS ID, t.TorrentID - FROM group_log t - INNER JOIN reportsv2 r using (TorrentID) - WHERE t.Info NOT LIKE 'uploaded%' - AND $conds - GROUP BY t.TorrentID - ) LASTLOG USING (TorrentID) - LEFT JOIN group_log gl ON (gl.ID = LASTLOG.ID) - WHERE $sql_where - ORDER BY r.ReportedTime DESC LIMIT ? OFFSET ? - "; - $args = array_merge( - $args, - $args, - $delargs, - [ - TORRENTS_PER_PAGE, // LIMIT - TORRENTS_PER_PAGE * (max($filter['page'], 1) - 1), // OFFSET - ] - ); - $db->prepared_query_array($sql, $args); - $result = $db->to_array(); - $db->query('SELECT FOUND_ROWS()'); - list($count) = $db->next_record(); - return [$result, $count]; - } + public static function search(\DB_MYSQL $db, array $filter) { + $cond = []; + $args = []; + $delcond = []; + $delargs = []; + if (array_key_exists('reporter', $filter) && $filter['reporter']) { + $cond[] = 'r.ReporterID = ?'; + $args[] = self::username2id($db, $filter['reporter']); + } + if (array_key_exists('handler', $filter) && $filter['handler']) { + $cond[] = 'r.ResolverID = ?'; + $args[] = self::username2id($db, $filter['handler']); + } + if (array_key_exists('report-type', $filter)) { + $cond[] = 'r.Type in (' . implode(', ', array_fill(0, count($filter['report-type']), '?')) . ')'; + $args = array_merge($args, $filter['report-type']); + } + if (array_key_exists('dt-from', $filter)) { + $cond[] = 'r.ReportedTime >= ?'; + $args[] = $filter['dt-from']; + } + if (array_key_exists('dt-until', $filter)) { + $delcond[] = 'r.ReportedTime <= ? + INTERVAL 1 DAY'; + $delargs[] = $filter['dt-until']; + } + if (array_key_exists('torrent', $filter)) { + $delcond[] = 'r.TorrentID = ?'; + $delargs[] = $filter['torrent']; + } + if (array_key_exists('uploader', $filter) && $filter['uploader']) { + $cond[] = 't.UserID = ?'; + $args[] = self::username2id($db, $filter['uploader']); + $delcond[] = 'dt.UserID = ?'; + $delargs[] = self::username2id($db, $filter['uploader']); + } + if (array_key_exists('group', $filter)) { + $cond[] = 't.GroupID = ?'; + $args[] = $filter['group']; + $delcond[] = 'dt.GroupID = ?'; + $delargs[] = $filter['group']; + } + if (count($cond) == 0 && count($delcond) == 0) { + $cond = ['1 = 1']; + } + $conds = implode(' AND ', $cond); + /* The construct below is pretty sick: we alias the group_log table to t + * which means that t.GroupID in a condition refers to the same thing in + * the `torrents` table as well. I am not certain this is entirely sane. + */ + $sql_where = implode("\n\t\tAND ", array_merge($cond, $delcond)); + $sql = " + SELECT SQL_CALC_FOUND_ROWS + r.ID, r.ReporterID, r.ResolverID, r.TorrentID, + coalesce(t.UserID, dt.UserID) as UserID, + coalesce(t.GroupID, dt.GroupID) as GroupID, + coalesce(t.Media, dt.Media) as Media, + coalesce(t.Format, dt.Format) as Format, + coalesce(t.Encoding, dt.Encoding) as Encoding, + coalesce(g.Name, gl.Info) as Name, g.Year, r.Type, r.ReportedTime + FROM reportsv2 r + LEFT JOIN torrents t ON (t.ID = r.TorrentID) + LEFT JOIN deleted_torrents dt ON (dt.ID = r.TorrentID) + LEFT JOIN torrents_group g on (g.ID = t.GroupID) + LEFT JOIN ( + SELECT max(t.ID) AS ID, t.TorrentID + FROM group_log t + INNER JOIN reportsv2 r using (TorrentID) + WHERE t.Info NOT LIKE 'uploaded%' + AND $conds + GROUP BY t.TorrentID + ) LASTLOG USING (TorrentID) + LEFT JOIN group_log gl ON (gl.ID = LASTLOG.ID) + WHERE $sql_where + ORDER BY r.ReportedTime DESC LIMIT ? OFFSET ? + "; + $args = array_merge( + $args, + $args, + $delargs, + [ + TORRENTS_PER_PAGE, // LIMIT + TORRENTS_PER_PAGE * (max($filter['page'], 1) - 1), // OFFSET + ] + ); + $db->prepared_query($sql, ...$args); + $result = $db->to_array(); + $db->query('SELECT FOUND_ROWS()'); + list($count) = $db->next_record(); + return [$result, $count]; + } - private static function username2id ($db, $name) { - $db->prepared_query('SELECT ID FROM users_main WHERE Username = ?', $name); - $user = $db->next_record(); - return $user['ID']; - } + private static function username2id (\DB_MYSQL $db, $name) { + $db->prepared_query('SELECT ID FROM users_main WHERE Username = ?', $name); + $user = $db->next_record(); + return $user['ID']; + } } diff --git a/app/Router.php b/app/Router.php index bb93f31d0..666eec078 100644 --- a/app/Router.php +++ b/app/Router.php @@ -17,83 +17,83 @@ * GET request will not. */ class Router { - private $authorize = ['GET' => false, 'POST' => true]; - private $routes = ['GET' => [], 'POST' => []]; - private $auth_key = null; + private $authorize = ['GET' => false, 'POST' => true]; + private $routes = ['GET' => [], 'POST' => []]; + private $auth_key = null; - /** - * Router constructor. - * @param string $auth_key Authorization key for a user - */ - public function __construct($auth_key = '') { - $this->auth_key = $auth_key; - } + /** + * Router constructor. + * @param string $auth_key Authorization key for a user + */ + public function __construct($auth_key = '') { + $this->auth_key = $auth_key; + } - /** - * @param string|array $methods - * @param string $action - * @param string $path - * @param bool $authorize - */ - public function addRoute($methods=['GET'], string $action, string $path, bool $authorize = false) { - if (is_array($methods)) { - foreach ($methods as $method) { - $this->addRoute($method, $action, $path, $authorize); - } - } - else { - if (strtoupper($methods) === 'GET') { - $this->addGet($action, $path, $authorize); - } - elseif (strtoupper($methods) === 'POST') { - $this->addPost($action, $path, $authorize); - } - } - } + /** + * @param string|array $methods + * @param string $action + * @param string $path + * @param bool $authorize + */ + public function addRoute($methods=['GET'], string $action, string $path, bool $authorize = false) { + if (is_array($methods)) { + foreach ($methods as $method) { + $this->addRoute($method, $action, $path, $authorize); + } + } + else { + if (strtoupper($methods) === 'GET') { + $this->addGet($action, $path, $authorize); + } + elseif (strtoupper($methods) === 'POST') { + $this->addPost($action, $path, $authorize); + } + } + } - public function addGet(string $action, string $file, bool $authorize = false) { - $this->routes['GET'][$action] = ['file' => $file, 'authorize' => $authorize]; - } + public function addGet(string $action, string $file, bool $authorize = false) { + $this->routes['GET'][$action] = ['file' => $file, 'authorize' => $authorize]; + } - public function addPost(string $action, string $file, bool $authorize = true) { - $this->routes['POST'][$action] = ['file' => $file, 'authorize' => $authorize]; - } + public function addPost(string $action, string $file, bool $authorize = true) { + $this->routes['POST'][$action] = ['file' => $file, 'authorize' => $authorize]; + } - public function authorizeGet(bool $authorize = true) { - $this->authorize['GET'] = $authorize; - } + public function authorizeGet(bool $authorize = true) { + $this->authorize['GET'] = $authorize; + } - public function authorizePost(bool $authorize = true) { - $this->authorize['POST'] = $authorize; - } + public function authorizePost(bool $authorize = true) { + $this->authorize['POST'] = $authorize; + } - public function authorized() { - return !empty($_REQUEST['auth']) && $_REQUEST['auth'] === $this->auth_key; - } + public function authorized() { + return !empty($_REQUEST['auth']) && $_REQUEST['auth'] === $this->auth_key; + } - public function hasRoutes() { - return array_sum(array_map("count", $this->routes)) > 0; - } + public function hasRoutes() { + return array_sum(array_map("count", $this->routes)) > 0; + } - /** - * @param string $action - * @return string path to file to load - * @throws RouterException - */ - public function getRoute(string $action) { - $request_method = strtoupper(empty($_SERVER['REQUEST_METHOD']) ? 'GET' : $_SERVER['REQUEST_METHOD']); - if (isset($this->routes[$request_method]) && isset($this->routes[$request_method][$action])) { - $method = $this->routes[$request_method][$action]; - } - else { - throw new RouterException("Invalid action for '${request_method}' request method"); - } + /** + * @param string $action + * @return string path to file to load + * @throws RouterException + */ + public function getRoute(string $action) { + $request_method = strtoupper(empty($_SERVER['REQUEST_METHOD']) ? 'GET' : $_SERVER['REQUEST_METHOD']); + if (isset($this->routes[$request_method]) && isset($this->routes[$request_method][$action])) { + $method = $this->routes[$request_method][$action]; + } + else { + throw new RouterException("Invalid action for '${request_method}' request method"); + } - if (($this->authorize[$request_method] || $method['authorize']) && !$this->authorized()) { - throw new InvalidAccessException(); - } - else { - return $method['file']; - } - } -} \ No newline at end of file + if (($this->authorize[$request_method] || $method['authorize']) && !$this->authorized()) { + throw new InvalidAccessException(); + } + else { + return $method['file']; + } + } +} diff --git a/app/Top10/Donor.php b/app/Top10/Donor.php new file mode 100644 index 000000000..f085d7784 --- /dev/null +++ b/app/Top10/Donor.php @@ -0,0 +1,22 @@ +db = $db; + } + + public function getTopDonors($limit) { + return $this->db->prepared_query(' + SELECT UserID, TotalRank, Rank, SpecialRank, DonationTime, Hidden + FROM users_donor_ranks + WHERE TotalRank > 0 + ORDER BY TotalRank DESC + LIMIT ?', + $limit); + } +} diff --git a/app/Top10/Tag.php b/app/Top10/Tag.php new file mode 100644 index 000000000..7bfa6edf8 --- /dev/null +++ b/app/Top10/Tag.php @@ -0,0 +1,81 @@ +db = $db; + $this->cache = $cache; + } + + public function getTopUsedTags($limit) { + if (!$topUsedTags = $this->cache->get_value('topusedtag_' . $limit)) { + $topUsedTags = $this->db->prepared_query(" + SELECT + t.ID, + t.Name, + COUNT(tt.GroupID) AS Uses, + SUM(tt.PositiveVotes - 1) AS PositiveVotes, + SUM(tt.NegativeVotes - 1) AS NegativeVotes + FROM tags AS t + JOIN torrents_tags AS tt ON (tt.TagID = t.ID) + GROUP BY tt.TagID + ORDER BY Uses DESC, t.name + LIMIT ?", $limit); + + $topUsedTags = $this->db->to_array(); + $this->cache->cache_value('topusedtag_' . $limit, $topUsedTags, 3600 * 12); + } + + return $topUsedTags; + } + + public function getTopRequestTags($limit) { + if (!$topRequestTags = $this->cache->get_value('toprequesttag_' . $limit)) { + $this->db->prepared_query(" + SELECT + t.ID, + t.Name, + COUNT(r.RequestID) AS Uses, + '','' + FROM tags AS t + JOIN requests_tags AS r ON (r.TagID = t.ID) + GROUP BY r.TagID + ORDER BY Uses DESC, t.name + LIMIT ?", $limit); + + $topRequestTags = $this->db->to_array(); + $this->cache->cache_value('toprequesttag_' . $limit, $topRequestTags, 3600 * 12); + } + + return $topRequestTags; + } + + public function getTopVotedTags($limit) { + if (!$topVotedTags = $this->cache->get_value('topvotedtag_' . $limit)) { + $topVotedTags = $this->db->prepared_query(" + SELECT + t.ID, + t.Name, + COUNT(tt.GroupID) AS Uses, + SUM(tt.PositiveVotes - 1) AS PositiveVotes, + SUM(tt.NegativeVotes - 1) AS NegativeVotes + FROM tags AS t + JOIN torrents_tags AS tt ON (tt.TagID = t.ID) + GROUP BY tt.TagID + ORDER BY PositiveVotes DESC, t.name + LIMIT ?", $limit); + + $topVotedTags = $this->db->to_array(); + $this->cache->cache_value('topvotedtag_' . $limit, $topVotedTags, 3600 * 12); + } + + return $topVotedTags; + } +} diff --git a/app/Top10/Torrent.php b/app/Top10/Torrent.php new file mode 100644 index 000000000..c6f090a89 --- /dev/null +++ b/app/Top10/Torrent.php @@ -0,0 +1,196 @@ +db = $db; + $this->cache = $cache; + $this->formats = $formats; + $this->currentUser = $currentUser; + } + + function getTopTorrents($getParameters, $details = 'all', $limit = 10) { + $cacheKey = 'top10_' . $details . '_' . md5(implode($getParameters,'')) . '_' . $limit; + $topTorrents = $this->cache->get_value($cacheKey); + + if ($topTorrents !== false) return $topTorrents; + if (!$this->cache->get_query_lock($cacheKey)) return false; + + $where = []; + $anyTags = isset($tagParameter['anyall']) && $tagParameter['anyall'] == 'any'; + if (isset($getParameters['tags'])) $where[] = $this->tagWhere($getParameters['tags'], $anyTags); + if (isset($getParameters['format'])) $where[] = $this->formatWhere($getParameters['format']); + if (isset($getParameters['freeleech'])) $where[] = $this->freeleechWhere($getParameters['freeleech']); + $where[] = $this->detailsWhere($details); + + $where[] = ["parameters" => null, "where" => "tls.Seeders > 0"]; + + $whereFilter = function($value){ + if (!isset($value["where"])) { + return null; + }; + return $value["where"]; + }; + + $parameterFilter = function($value){ + if (!isset($value["parameters"])) { + return null; + }; + return $value["parameters"]; + }; + + $filteredWhere = array_filter(array_map($whereFilter, $where)); + $parameters = $this->flatten(array_filter(array_map($parameterFilter, $where))); + + $query = $this->baseQuery . ' WHERE ' . implode(" AND ", $filteredWhere); + $query = $query . (isset($getParameters['groups']) && $getParameters['groups'] == 'show' ? ' GROUP BY g.ID ' : ''); + $query = $query . ' ORDER BY ' . $this->orderBy($details) . ' DESC'; + $query = $query . " LIMIT $limit"; + + $this->db->prepared_query($query, ...$parameters); + $topTorrents = $this->db->to_array(); + + $this->cache->cache_value($cacheKey, $topTorrents, 3600 * 6); + $this->cache->clear_query_lock($cacheKey); + return $topTorrents; + } + + function showFreeleechTorrents($freeleechParameters) { + if (isset($freeleechParameters)) { + return $freeleechParameters == 'hide' ? 1 : 0; + } else if (isset($this->currentUser['DisableFreeTorrentTop10'])) { + return $this->currentUser['DisableFreeTorrentTop10']; + } else { + return 0; + } + } + + private function orderBy($details) { + switch($details) { + case 'snatched': + return 'tls.Snatched'; + break; + case 'seeded': + return 'tls.Seeders'; + break; + case 'data': + return 'Data'; + break; + default: + return '(tls.Seeders + tls.Leechers)'; + break; + } + } + + private function detailsWhere($detailsParameters) { + switch($detailsParameters) { + case 'day': + return ["parameters" => null, "where" => "t.Time > now() - INTERVAL 1 DAY"]; + break; + case 'week': + return ["parameters" => null, "where" => "t.Time > now() - INTERVAL 1 WEEK"]; + break; + case 'month': + return ["parameters" => null, "where" => "t.Time > now() - INTERVAL 1 MONTH"]; + break; + case 'year': + return ["parameters" => null, "where" => "t.Time > now() - INTERVAL 1 YEAR"]; + break; + default: + return []; + break; + } + } + + private function formatWhere($formatParameters) { + if (isset($formatParameters)) { + if (in_array($formatParameters, $this->formats)) { + return ["parameters" => $formatParameters, "where" => "t.Format = '?'"]; + } + } + + return []; + } + + private function freeleechWhere($freeleechParameters) { + $disableFreeTorrentTop10 = isset($this->currentUser['DisableFreeTorrentTop10']) ? $this->currentUser['DisableFreeTorrentTop10'] : 0; + + if (isset($freeleechParameters)) { + $disableFreeTorrentTop10 = ($freeleechParameters == 'hide' ? 1 : 0); + } + + if ($disableFreeTorrentTop10) { + return ["parameters" => null, "where" => "t.FreeTorrent = 0"]; + } + + return []; + } + + private function tagWhere($tagParameter, $any = false) { + if (isset($tagParameter) && !empty($tagParameter)) { + $tags = explode(',', str_replace('.', '_', trim($tagParameter))); + $replace = function($tag) { return preg_replace('/[^a-z0-9_]/', '', $tag); }; + $tags = array_map($replace, $tags); + $tags = array_filter($tags); + + # This is to make the prepared query work. + $likePrepare = function($tag) { return "%{$tag}%"; }; + $tags = array_map($likePrepare, $tags); + + $whereKeyword = $any ? 'OR' : 'AND'; + $filler = array_fill(0, count($tags), "g.TagList LIKE ? "); + $where = '(' . implode(" $whereKeyword ", $filler) . ')'; + return ["parameters" => $tags, "where" => $where]; + } + + return []; + } + + private function flatten(array $array) { + $return = []; + array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; }); + return $return; + } + + private $baseQuery = ' + SELECT + t.ID, + g.ID, + g.Name, + g.CategoryID, + g.wikiImage, + g.TagList, + t.Format, + t.Encoding, + t.Media, + t.Scene, + t.HasLog, + t.HasCue, + t.HasLogDB, + t.LogScore, + t.LogChecksum, + t.RemasterYear, + g.Year, + t.RemasterTitle, + tls.Snatched, + tls.Seeders, + tls.Leechers, + ((t.Size * tls.Snatched) + (t.Size * 0.5 * tls.Leechers)) AS Data, + g.ReleaseType, + t.Size + FROM torrents AS t + INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID) + INNER JOIN torrents_group AS g ON (g.ID = t.GroupID)'; +} diff --git a/app/Util/Arrays.php b/app/Util/Arrays.php new file mode 100644 index 000000000..1de9850eb --- /dev/null +++ b/app/Util/Arrays.php @@ -0,0 +1,17 @@ + $value) { + if (is_string($value)) { + $array[$key] = trim($value); + } + } + return $array; + } +} diff --git a/app/Util/Crypto.php b/app/Util/Crypto.php index b045d4b5c..b746d2006 100644 --- a/app/Util/Crypto.php +++ b/app/Util/Crypto.php @@ -3,30 +3,30 @@ namespace Gazelle\Util; class Crypto { - public static function encrypt($plaintext, $key) { - $iv_size = openssl_cipher_iv_length('AES-128-CBC'); - $iv = openssl_random_pseudo_bytes($iv_size); - return base64_encode($iv.openssl_encrypt($plaintext, 'AES-128-CBC', $key, - OPENSSL_RAW_DATA, $iv)); - } + public static function encrypt($plaintext, $key) { + $iv_size = openssl_cipher_iv_length('AES-128-CBC'); + $iv = openssl_random_pseudo_bytes($iv_size); + return base64_encode($iv.openssl_encrypt($plaintext, 'AES-128-CBC', $key, + OPENSSL_RAW_DATA, $iv)); + } - public static function dbEncrypt($plaintext) { - return apcu_exists('DB_KEY') ? Crypto::encrypt($plaintext, apcu_fetch('DB_KEY')) : false; - } + public static function dbEncrypt($plaintext) { + return apcu_exists('DB_KEY') ? Crypto::encrypt($plaintext, apcu_fetch('DB_KEY')) : false; + } - public static function decrypt($ciphertext, $key) { - if (empty($ciphertext)) { - return ''; - } + public static function decrypt($ciphertext, $key) { + if (empty($ciphertext)) { + return ''; + } - $data = base64_decode($ciphertext); - $iv_size = openssl_cipher_iv_length('AES-128-CBC'); - $iv = substr($data, 0, $iv_size); - return openssl_decrypt(substr($data, $iv_size), 'AES-128-CBC', $key, - OPENSSL_RAW_DATA, $iv); - } + $data = base64_decode($ciphertext); + $iv_size = openssl_cipher_iv_length('AES-128-CBC'); + $iv = substr($data, 0, $iv_size); + return openssl_decrypt(substr($data, $iv_size), 'AES-128-CBC', $key, + OPENSSL_RAW_DATA, $iv); + } - public static function dbDecrypt($ciphertext) { - return apcu_exists('DB_KEY') ? Crypto::decrypt($ciphertext, apcu_fetch('DB_KEY')) : false; - } + public static function dbDecrypt($ciphertext) { + return apcu_exists('DB_KEY') ? Crypto::decrypt($ciphertext, apcu_fetch('DB_KEY')) : false; + } } diff --git a/app/Util/Proxy.php b/app/Util/Proxy.php new file mode 100644 index 000000000..585e6a766 --- /dev/null +++ b/app/Util/Proxy.php @@ -0,0 +1,39 @@ +key = $key; + $this->bouncer = $bouncer; + } + + public function fetch($url, $params, $cookies, $post, $headers = []) { + $data = Crypto::encrypt(json_encode(array('url' => $url, 'params' => $params, + 'cookies' => $cookies, 'post' => $post, 'action' => 'fetch', 'headers' => $headers), + JSON_UNESCAPED_SLASHES), $this->key); + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_TIMEOUT, 30); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_URL, $this->bouncer); + curl_setopt($curl, CURLOPT_POST, true); + curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded')); + curl_setopt($curl, CURLOPT_POSTFIELDS, self::urlEncode($data)); + $result = curl_exec($curl); + $json = json_decode(Crypto::decrypt(self::urlDecode($result), $this->key), true); + + return $json; + } + + public static function urlEncode($data) { + return rtrim(strtr($data, '+/', '-_'), '='); + } + + public static function urlDecode($data) { + return str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT); + } +} diff --git a/app/Util/Text.php b/app/Util/Text.php index b0afab338..02fe3ef8b 100644 --- a/app/Util/Text.php +++ b/app/Util/Text.php @@ -3,35 +3,44 @@ namespace Gazelle\Util; class Text { - /** - * Determine if the $haystack starts with $needle - * - * @param string $haystack String to search in - * @param string $needle String to search for - * @param boolean $case_insensitive flag to ignore case of the $haystack and $needle - * @return boolean True if $Needle is a prefix of $Haystack - */ - public static function startsWith($haystack, $needle, $case_insensitive = false) { - if($case_insensitive) { - $haystack = strtolower($haystack); - $needle = strtolower($needle); - } - return substr($haystack, 0, strlen($needle)) === $needle; - } + /** + * Determine if the $haystack starts with $needle + * + * @param string $haystack String to search in + * @param string $needle String to search for + * @param boolean $case_insensitive flag to ignore case of the $haystack and $needle + * @return boolean True if $Needle is a prefix of $Haystack + */ + public static function startsWith($haystack, $needle, $case_insensitive = false) { + if($case_insensitive) { + $haystack = strtolower($haystack); + $needle = strtolower($needle); + } + return substr($haystack, 0, strlen($needle)) === $needle; + } - /** - * Determine if the $haystack ends with $needle - * - * @param string $haystack String to search in - * @param string $needle String to search for - * @param boolean $case_insensitive flag to ignore case of the $haystack and $needle - * @return boolean True if $Needle is a suffix of $Haystack - */ - public static function endsWith($haystack, $needle, $case_insensitive = false) { - if($case_insensitive) { - $haystack = strtolower($haystack); - $needle = strtolower($needle); - } - return ($needle !== null && strlen($needle) === 0) || substr($haystack, -strlen($needle)) === $needle; - } -} \ No newline at end of file + /** + * Determine if the $haystack ends with $needle + * + * @param string $haystack String to search in + * @param string $needle String to search for + * @param boolean $case_insensitive flag to ignore case of the $haystack and $needle + * @return boolean True if $Needle is a suffix of $Haystack + */ + public static function endsWith($haystack, $needle, $case_insensitive = false) { + if($case_insensitive) { + $haystack = strtolower($haystack); + $needle = strtolower($needle); + } + return ($needle !== null && strlen($needle) === 0) || substr($haystack, -strlen($needle)) === $needle; + } + + public static function base64UrlEncode($data) { + return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); + } + + public static function base64UrlDecode($data) { + return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', + STR_PAD_RIGHT)); + } +} diff --git a/app/Util/Time.php b/app/Util/Time.php index 7bac4e344..1fe92b7b0 100644 --- a/app/Util/Time.php +++ b/app/Util/Time.php @@ -3,320 +3,320 @@ namespace Gazelle\Util; class Time { - /** - * Returns the number of seconds between now() and the inputed timestamp. If the timestamp - * is an integer, we assume that's the nubmer of seconds you wish to subtract, otherwise - * it's a string of a timestamp that we convert to a UNIX timestamp and then do a subtraction. - * If the passed in $timestamp does not convert properly or is null, return false (error). - * - * @param string|int $timestamp - * @return false|int - */ - public static function timeAgo($timestamp) { - if ($timestamp === null) { - return false; - } - - if (($filter = filter_var($timestamp, FILTER_VALIDATE_INT)) === false) { - if ($timestamp == '0000-00-00 00:00:00') { - return false; - } - $timestamp = strtotime($timestamp); - if ($timestamp === false) { - return false; - } - return time() - $timestamp; - } - else { - return $filter; - } - } - - public static function timeDiff($timestamp, $levels = 2, $span = true, $lowercase = false, $starttime = false) { - $starttime = ($starttime === false) ? time() : strtotime($starttime); - - if (!Type::isInteger($timestamp)) { // Assume that $timestamp is SQL timestamp - if ($timestamp == '0000-00-00 00:00:00') { - return 'Never'; - } - $timestamp = strtotime($timestamp); - } - if ($timestamp == 0) { - return 'Never'; - } - $time = $starttime - $timestamp; - - // If the time is negative, then it expires in the future. - if ($time < 0) { - $time = -$time; - $HideAgo = true; - } - - $years = floor($time / 31556926); // seconds in one year - $remain = $time - $years * 31556926; - - $months = floor($remain / 2629744); // seconds in one month - $remain = $remain - $months * 2629744; - - $weeks = floor($remain / 604800); // seconds in one week - $remain = $remain - $weeks * 604800; - - $days = floor($remain / 86400); // seconds in one day - $remain = $remain - $days * 86400; - - $hours=floor($remain / 3600); // seconds in one hour - $remain = $remain - $hours * 3600; - - $minutes = floor($remain / 60); // seconds in one minute - $remain = $remain - $minutes * 60; - - $seconds = $remain; - - $return = ''; - - if ($years > 0 && $levels > 0) { - if ($years > 1) { - $return .= "$years years"; - } else { - $return .= "$years year"; - } - $levels--; - } - - if ($months > 0 && $levels > 0) { - if ($return != '') { - $return .= ', '; - } - if ($months > 1) { - $return .= "$months months"; - } else { - $return .= "$months month"; - } - $levels--; - } - - if ($weeks > 0 && $levels > 0) { - if ($return != '') { - $return .= ', '; - } - if ($weeks > 1) { - $return .= "$weeks weeks"; - } else { - $return .= "$weeks week"; - } - $levels--; - } - - if ($days > 0 && $levels > 0) { - if ($return != '') { - $return .= ', '; - } - if ($days > 1) { - $return .= "$days days"; - } else { - $return .= "$days day"; - } - $levels--; - } - - if ($hours > 0 && $levels > 0) { - if ($return != '') { - $return .= ', '; - } - if ($hours > 1) { - $return .= "$hours hours"; - } else { - $return .= "$hours hour"; - } - $levels--; - } - - if ($minutes > 0 && $levels > 0) { - if ($return != '') { - $return .= ' and '; - } - if ($minutes > 1) { - $return .= "$minutes mins"; - } else { - $return .= "$minutes min"; - } - } - - if ($return == '') { - $return = 'Just now'; - } elseif (!isset($HideAgo)) { - $return .= ' ago'; - } - - if ($lowercase) { - $return = strtolower($return); - } - - if ($span) { - return ''.$return.''; - } else { - return $return; - } - } - - /** - * Converts a numeric amount of hours (though we round down via floor for all levels) into a more human readeable - * string representing the number of years, months, weeks, days, and hours that make up that numeric amount. The - * function then either surrounds the amount with a span or just returns the string. Giving a less than or equal - * 0 hours to the function will return the string 'Never'. - * - * @param $hours - * @param int $levels - * @param bool $span - * @return string - */ - public static function convertHours($hours, $levels = 2, $span = true) { - if ($hours <= 0) { - return 'Never'; - } - - $years = floor($hours/8760); // hours in a year - $remain = $hours - $years*8760; - - $months = floor($remain/730); // hours in a month - $remain = $remain - $months*730; - - $weeks = floor($remain/168); // hours in a week - $remain = $remain - $weeks*168; - - $days = floor($remain/24); // hours in a day - $remain = $remain - $days*24; - - $hours = floor($remain); - - $return = ''; - - if ($years > 0 && $levels > 0) { - $return .= $years.'y'; - $levels--; - } - - if ($months > 0 && $levels > 0) { - $return .= $months.'mo'; - $levels--; - } - - if ($weeks > 0 && $levels > 0) { - $return .= $weeks.'w'; - $levels--; - } - - if ($days > 0 && $levels > 0) { - $return .= $days.'d'; - $levels--; - } - - if ($hours > 0 && $levels > 0) { - $return .= $hours.'h'; - } - - if ($span) { - return ''.$return.''; - } - else { - return $return; - } - } - - /** - * Utility function to generate a timestamp to insert into the database, given some offset and - * whether or not we will be 'fuzzy' (midnight for time) with the timestamp. - * - * @param int $offset - * @param bool $fuzzy - * @return false|string - */ - public static function timeOffset($offset = 0, $fuzzy = false) { - if ($fuzzy) { - return date('Y-m-d 00:00:00', time() + $offset); - } - else { - return date('Y-m-d H:i:s', time() + $offset); - } - } - - /** - * Legacy function from classes/time.class.php. - * - * @see Time::timeOffset() - * @deprecated Use Time::timeOffset() instead. - * - * @param int $offset - * @return false|string - */ - public static function timePlus($offset = 0) { - return static::timeOffset($offset); - } - - /** - * Legacy function from classes/time.class.php. - * - * @see Time::timeOffset() - * @deprecated Use Time::timeOffset() instead. - * - * @param int $offset - * @param bool $fuzzy - * @return false|string - */ - public static function timeMinus($offset = 0, $fuzzy = false) { - return static::timeOffset(-$offset, $fuzzy); - } - - public static function sqlTime($timestamp = false) { - if ($timestamp === false) { - $timestamp = time(); - } - return date('Y-m-d H:i:s', $timestamp); - } - - public static function validDate($date_string) { - $date_time = explode(' ', $date_string); - if (count($date_time) != 2) { - return false; - } - list($date, $time) = $date_time; - $split_time = explode(':', $time); - if (count($split_time) != 3) { - return false; - } - list($hour, $minute, $second) = $split_time; - if ($hour != 0 && !(is_number($hour) && $hour < 24 && $hour >= 0)) { - return false; - } - if ($minute != 0 && !(is_number($minute) && $minute < 60 && $minute >= 0)) { - return false; - } - if ($second != 0 && !(is_number($second) && $second < 60 && $second >= 0)) { - return false; - } - $split_date = explode('-', $date); - if (count($split_date) != 3) { - return false; - } - list($year, $month, $day) = $split_date; - return checkDate($month, $day, $year); - } - - public static function isValidDate($date) { - return static::isValidDateTime($date, 'Y-m-d'); - } - - public static function isDate($date) { - list($year, $month, $day) = explode('-', $date); - return checkdate($month, $day, $year); - } - - public static function isValidTime($time) { - return static::isValidDateTime($time, 'H:i'); - } - - public static function isValidDateTime($date_time, $format = 'Y-m-d H:i') { - $formatted_date_time = \DateTime::createFromFormat($format, $date_time); - return $formatted_date_time && $formatted_date_time->format($format) == $date_time; - } + /** + * Returns the number of seconds between now() and the inputed timestamp. If the timestamp + * is an integer, we assume that's the nubmer of seconds you wish to subtract, otherwise + * it's a string of a timestamp that we convert to a UNIX timestamp and then do a subtraction. + * If the passed in $timestamp does not convert properly or is null, return false (error). + * + * @param string|int $timestamp + * @return false|int + */ + public static function timeAgo($timestamp) { + if ($timestamp === null) { + return false; + } + + if (($filter = filter_var($timestamp, FILTER_VALIDATE_INT)) === false) { + if ($timestamp == '0000-00-00 00:00:00') { + return false; + } + $timestamp = strtotime($timestamp); + if ($timestamp === false) { + return false; + } + return time() - $timestamp; + } + else { + return $filter; + } + } + + public static function timeDiff($timestamp, $levels = 2, $span = true, $lowercase = false, $starttime = false) { + $starttime = ($starttime === false) ? time() : strtotime($starttime); + + if (!Type::isInteger($timestamp)) { // Assume that $timestamp is SQL timestamp + if ($timestamp == '0000-00-00 00:00:00') { + return 'Never'; + } + $timestamp = strtotime($timestamp); + } + if ($timestamp == 0) { + return 'Never'; + } + $time = $starttime - $timestamp; + + // If the time is negative, then it expires in the future. + if ($time < 0) { + $time = -$time; + $HideAgo = true; + } + + $years = floor($time / 31556926); // seconds in one year + $remain = $time - $years * 31556926; + + $months = floor($remain / 2629744); // seconds in one month + $remain = $remain - $months * 2629744; + + $weeks = floor($remain / 604800); // seconds in one week + $remain = $remain - $weeks * 604800; + + $days = floor($remain / 86400); // seconds in one day + $remain = $remain - $days * 86400; + + $hours=floor($remain / 3600); // seconds in one hour + $remain = $remain - $hours * 3600; + + $minutes = floor($remain / 60); // seconds in one minute + $remain = $remain - $minutes * 60; + + $seconds = $remain; + + $return = ''; + + if ($years > 0 && $levels > 0) { + if ($years > 1) { + $return .= "$years years"; + } else { + $return .= "$years year"; + } + $levels--; + } + + if ($months > 0 && $levels > 0) { + if ($return != '') { + $return .= ', '; + } + if ($months > 1) { + $return .= "$months months"; + } else { + $return .= "$months month"; + } + $levels--; + } + + if ($weeks > 0 && $levels > 0) { + if ($return != '') { + $return .= ', '; + } + if ($weeks > 1) { + $return .= "$weeks weeks"; + } else { + $return .= "$weeks week"; + } + $levels--; + } + + if ($days > 0 && $levels > 0) { + if ($return != '') { + $return .= ', '; + } + if ($days > 1) { + $return .= "$days days"; + } else { + $return .= "$days day"; + } + $levels--; + } + + if ($hours > 0 && $levels > 0) { + if ($return != '') { + $return .= ', '; + } + if ($hours > 1) { + $return .= "$hours hours"; + } else { + $return .= "$hours hour"; + } + $levels--; + } + + if ($minutes > 0 && $levels > 0) { + if ($return != '') { + $return .= ' and '; + } + if ($minutes > 1) { + $return .= "$minutes mins"; + } else { + $return .= "$minutes min"; + } + } + + if ($return == '') { + $return = 'Just now'; + } elseif (!isset($HideAgo)) { + $return .= ' ago'; + } + + if ($lowercase) { + $return = strtolower($return); + } + + if ($span) { + return ''.$return.''; + } else { + return $return; + } + } + + /** + * Converts a numeric amount of hours (though we round down via floor for all levels) into a more human readeable + * string representing the number of years, months, weeks, days, and hours that make up that numeric amount. The + * function then either surrounds the amount with a span or just returns the string. Giving a less than or equal + * 0 hours to the function will return the string 'Never'. + * + * @param $hours + * @param int $levels + * @param bool $span + * @return string + */ + public static function convertHours($hours, $levels = 2, $span = true) { + if ($hours <= 0) { + return 'Never'; + } + + $years = floor($hours/8760); // hours in a year + $remain = $hours - $years*8760; + + $months = floor($remain/730); // hours in a month + $remain = $remain - $months*730; + + $weeks = floor($remain/168); // hours in a week + $remain = $remain - $weeks*168; + + $days = floor($remain/24); // hours in a day + $remain = $remain - $days*24; + + $hours = floor($remain); + + $return = ''; + + if ($years > 0 && $levels > 0) { + $return .= $years.'y'; + $levels--; + } + + if ($months > 0 && $levels > 0) { + $return .= $months.'mo'; + $levels--; + } + + if ($weeks > 0 && $levels > 0) { + $return .= $weeks.'w'; + $levels--; + } + + if ($days > 0 && $levels > 0) { + $return .= $days.'d'; + $levels--; + } + + if ($hours > 0 && $levels > 0) { + $return .= $hours.'h'; + } + + if ($span) { + return ''.$return.''; + } + else { + return $return; + } + } + + /** + * Utility function to generate a timestamp to insert into the database, given some offset and + * whether or not we will be 'fuzzy' (midnight for time) with the timestamp. + * + * @param int $offset + * @param bool $fuzzy + * @return false|string + */ + public static function timeOffset($offset = 0, $fuzzy = false) { + if ($fuzzy) { + return date('Y-m-d 00:00:00', time() + $offset); + } + else { + return date('Y-m-d H:i:s', time() + $offset); + } + } + + /** + * Legacy function from classes/time.class.php. + * + * @see Time::timeOffset() + * @deprecated Use Time::timeOffset() instead. + * + * @param int $offset + * @return false|string + */ + public static function timePlus($offset = 0) { + return static::timeOffset($offset); + } + + /** + * Legacy function from classes/time.class.php. + * + * @see Time::timeOffset() + * @deprecated Use Time::timeOffset() instead. + * + * @param int $offset + * @param bool $fuzzy + * @return false|string + */ + public static function timeMinus($offset = 0, $fuzzy = false) { + return static::timeOffset(-$offset, $fuzzy); + } + + public static function sqlTime($timestamp = false) { + if ($timestamp === false) { + $timestamp = time(); + } + return date('Y-m-d H:i:s', $timestamp); + } + + public static function validDate($date_string) { + $date_time = explode(' ', $date_string); + if (count($date_time) != 2) { + return false; + } + list($date, $time) = $date_time; + $split_time = explode(':', $time); + if (count($split_time) != 3) { + return false; + } + list($hour, $minute, $second) = $split_time; + if ($hour != 0 && !(is_number($hour) && $hour < 24 && $hour >= 0)) { + return false; + } + if ($minute != 0 && !(is_number($minute) && $minute < 60 && $minute >= 0)) { + return false; + } + if ($second != 0 && !(is_number($second) && $second < 60 && $second >= 0)) { + return false; + } + $split_date = explode('-', $date); + if (count($split_date) != 3) { + return false; + } + list($year, $month, $day) = $split_date; + return checkDate($month, $day, $year); + } + + public static function isValidDate($date) { + return static::isValidDateTime($date, 'Y-m-d'); + } + + public static function isDate($date) { + list($year, $month, $day) = explode('-', $date); + return checkdate($month, $day, $year); + } + + public static function isValidTime($time) { + return static::isValidDateTime($time, 'H:i'); + } + + public static function isValidDateTime($date_time, $format = 'Y-m-d H:i') { + $formatted_date_time = \DateTime::createFromFormat($format, $date_time); + return $formatted_date_time && $formatted_date_time->format($format) == $date_time; + } } diff --git a/app/Util/Type.php b/app/Util/Type.php index a2f88d897..3e41fda8f 100644 --- a/app/Util/Type.php +++ b/app/Util/Type.php @@ -3,53 +3,53 @@ namespace Gazelle\Util; class Type { - /** - * Given a variable, which could be a string or numeric, test if that variable - * is an integer (string or otherwise). - * - * TODO: replace current method with filter_var($variable, FILTER_VALIDATE_INT) !== false; - * @param mixed $variable variable to test - * @return bool does the variable represent an integer - */ - public static function isInteger($variable) { - return $variable == strval(intval($variable)); - } + /** + * Given a variable, which could be a string or numeric, test if that variable + * is an integer (string or otherwise). + * + * TODO: replace current method with filter_var($variable, FILTER_VALIDATE_INT) !== false; + * @param mixed $variable variable to test + * @return bool does the variable represent an integer + */ + public static function isInteger($variable) { + return $variable == strval(intval($variable)); + } - /** - * Given some value, test to see if it represents a boolean true/false value. This - * covers booleans, strings that represent booleans (using the php.ini classification - * of booleans with allowing string '0' and '1'), or the 0/1 integers. If the given - * value does not fit any of the above criteria, then return null. - * - * @param mixed $value - * @return bool|null - */ - public static function isBoolValue($value) { - if (is_bool($value)) { - return $value; - } - elseif (is_string($value)) { - switch (strtolower($value)) { - case 'true': - case 'yes': - case 'on': - case '1': - return true; - case 'false': - case 'no': - case 'off': - case '0': - return false; - } - } - elseif (is_numeric($value)) { - if ($value == 1) { - return true; - } - elseif ($value == 0) { - return false; - } - } - return null; - } -} \ No newline at end of file + /** + * Given some value, test to see if it represents a boolean true/false value. This + * covers booleans, strings that represent booleans (using the php.ini classification + * of booleans with allowing string '0' and '1'), or the 0/1 integers. If the given + * value does not fit any of the above criteria, then return null. + * + * @param mixed $value + * @return bool|null + */ + public static function isBoolValue($value) { + if (is_bool($value)) { + return $value; + } + elseif (is_string($value)) { + switch (strtolower($value)) { + case 'true': + case 'yes': + case 'on': + case '1': + return true; + case 'false': + case 'no': + case 'off': + case '0': + return false; + } + } + elseif (is_numeric($value)) { + if ($value == 1) { + return true; + } + elseif ($value == 0) { + return false; + } + } + return null; + } +} diff --git a/boris b/boris new file mode 100755 index 000000000..4e0d3a2ec --- /dev/null +++ b/boris @@ -0,0 +1,71 @@ +#! /usr/bin/env php +handle_errors(); + +G::init($DB, $Cache); + +define('BORIS', 1); +require_once(SERVER_ROOT.'/vendor/d11wtq/boris/lib/autoload.php'); +$b = new \Boris\Boris(SITE_NAME . '> '); +$b->setLocal([ + 'cache' => $Cache, + 'db' => $DB, + 'artist' => new \Gazelle\Artist($DB, $Cache), + 'bonus' => new \Gazelle\Bonus($DB, $Cache) +]); + +$b->start(); + +/* + * Sample usage: + * + vagrant@contrib-jessie:/var/www$ ./boris + [1] ops> $db->query('select count(*) from torrents'); + // object(mysqli_result)( + // 'current_field' => NULL, + // 'field_count' => NULL, + // 'lengths' => NULL, + // 'num_rows' => NULL, + // 'type' => NULL + // ) + [2] ops> $db->to_array(); + // array( + // 0 => array( + // 0 => '14', + // 'count(*)' => '14' + // ) + // ) +*/ diff --git a/classes/NMA_API.php b/classes/NMA_API.php index 3e7bc5b2a..173f9c0e4 100644 --- a/classes/NMA_API.php +++ b/classes/NMA_API.php @@ -53,7 +53,7 @@ class NMA_API /** * @param array $options */ - function __construct($options = array()) + function __construct($options = []) { if (!isset($options['apikey'])) { return $this->error('You must supply a API Key'); @@ -82,7 +82,7 @@ function __construct($options = array()) public function verify($key = false) { - $options = array(); + $options = []; if ($key !== false) { $options['apikey'] = $key; @@ -119,9 +119,9 @@ public function notify($application = '', $event = '', $description = '', $url = 'description' => substr($description, 0, 10000), 'priority' => $priority ); - if (!empty($url)) { - $post['url'] = substr($url, 0, 2000); - } + if (!empty($url)) { + $post['url'] = substr($url, 0, 2000); + } if ($this->devKey) { $post['developerkey'] = $this->devKey; } diff --git a/classes/ajax_start.php b/classes/ajax_start.php index 58292f115..fcbcbbd68 100644 --- a/classes/ajax_start.php +++ b/classes/ajax_start.php @@ -1,4 +1,4 @@ -get_value("enabled_$UserID")) { - require(SERVER_ROOT.'/classes/mysql.class.php'); //Require the database wrapper - $DB = NEW DB_MYSQL; //Load the database wrapper - $DB->query(" - SELECT Enabled - FROM users_main - WHERE ID = '$UserID'"); - list($Enabled) = $DB->next_record(); - $Cache->cache_value("enabled_$UserID", $Enabled, 0); - } + list($SessionID, $UserID) = explode("|~|", Crypto::decrypt($LoginCookie, ENCKEY)); + + if (!$UserID || !$SessionID) { + die('Not logged in!'); + } + + if (!$Enabled = $Cache->get_value("enabled_$UserID")) { + require(SERVER_ROOT.'/classes/mysql.class.php'); //Require the database wrapper + $DB = NEW DB_MYSQL; //Load the database wrapper + $DB->query(" + SELECT Enabled + FROM users_main + WHERE ID = '$UserID'"); + list($Enabled) = $DB->next_record(); + $Cache->cache_value("enabled_$UserID", $Enabled, 0); + } } else { - die('Not logged in!'); + die('Not logged in!'); } function error($Error) { - die($Error); + die($Error); } function is_number($Str) { - if ($Str < 0) { - return false; - } - // We're converting input to a int, then string and comparing to original - return ($Str == strval(intval($Str)) ? true : false); + if ($Str < 0) { + return false; + } + // We're converting input to a int, then string and comparing to original + return ($Str == strval(intval($Str)) ? true : false); } function display_str($Str) { - if ($Str != '') { - $Str = make_utf8($Str); - $Str = mb_convert_encoding($Str, 'HTML-ENTITIES', 'UTF-8'); - $Str = preg_replace("/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,5};)/m", '&', $Str); - - $Replace = array( - "'",'"',"<",">", - '€','‚','ƒ','„','…','†','‡','ˆ','‰','Š','‹','Œ','Ž','‘','’','“','”','•','–','—','˜','™','š','›','œ','ž','Ÿ' - ); - - $With = array( - ''','"','<','>', - '€','‚','ƒ','„','…','†','‡','ˆ','‰','Š','‹','Œ','Ž','‘','’','“','”','•','–','—','˜','™','š','›','œ','ž','Ÿ' - ); - - $Str = str_replace($Replace, $With, $Str); - } - return $Str; + if ($Str != '') { + $Str = make_utf8($Str); + $Str = mb_convert_encoding($Str, 'HTML-ENTITIES', 'UTF-8'); + $Str = preg_replace("/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,5};)/m", '&', $Str); + + $Replace = array( + "'",'"',"<",">", + '€','‚','ƒ','„','…','†','‡','ˆ','‰','Š','‹','Œ','Ž','‘','’','“','”','•','–','—','˜','™','š','›','œ','ž','Ÿ' + ); + + $With = array( + ''','"','<','>', + '€','‚','ƒ','„','…','†','‡','ˆ','‰','Š','‹','Œ','Ž','‘','’','“','”','•','–','—','˜','™','š','›','œ','ž','Ÿ' + ); + + $Str = str_replace($Replace, $With, $Str); + } + return $Str; } function make_utf8($Str) { - if ($Str != '') { - if (is_utf8($Str)) { - $Encoding = 'UTF-8'; - } - if (empty($Encoding)) { - $Encoding = mb_detect_encoding($Str, 'UTF-8, ISO-8859-1'); - } - if (empty($Encoding)) { - $Encoding = 'ISO-8859-1'; - } - if ($Encoding == 'UTF-8') { - return $Str; - } else { - return @mb_convert_encoding($Str, 'UTF-8', $Encoding); - } - } + if ($Str != '') { + if (is_utf8($Str)) { + $Encoding = 'UTF-8'; + } + if (empty($Encoding)) { + $Encoding = mb_detect_encoding($Str, 'UTF-8, ISO-8859-1'); + } + if (empty($Encoding)) { + $Encoding = 'ISO-8859-1'; + } + if ($Encoding == 'UTF-8') { + return $Str; + } else { + return @mb_convert_encoding($Str, 'UTF-8', $Encoding); + } + } } function is_utf8($Str) { - return preg_match('%^(?: - [\x09\x0A\x0D\x20-\x7E] // ASCII - | [\xC2-\xDF][\x80-\xBF] // non-overlong 2-byte - | \xE0[\xA0-\xBF][\x80-\xBF] // excluding overlongs - | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} // straight 3-byte - | \xED[\x80-\x9F][\x80-\xBF] // excluding surrogates - | \xF0[\x90-\xBF][\x80-\xBF]{2} // planes 1-3 - | [\xF1-\xF3][\x80-\xBF]{3} // planes 4-15 - | \xF4[\x80-\x8F][\x80-\xBF]{2} // plane 16 - )*$%xs', $Str - ); + return preg_match('%^(?: + [\x09\x0A\x0D\x20-\x7E] // ASCII + | [\xC2-\xDF][\x80-\xBF] // non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] // excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} // straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] // excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} // planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} // planes 4-15 + | \xF4[\x80-\x8F][\x80-\xBF]{2} // plane 16 + )*$%xs', $Str + ); } -function display_array($Array, $DontEscape = array()) { - foreach ($Array as $Key => $Val) { - if (!in_array($Key, $DontEscape)) { - $Array[$Key] = display_str($Val); - } - } - return $Array; +function display_array($Array, $DontEscape = []) { + foreach ($Array as $Key => $Val) { + if (!in_array($Key, $DontEscape)) { + $Array[$Key] = display_str($Val); + } + } + return $Array; } function make_secret($Length = 32) { - $NumBytes = (int) round($Length / 2); - $Secret = bin2hex(openssl_random_pseudo_bytes($NumBytes)); - return substr($Secret, 0, $Length); + $NumBytes = (int) round($Length / 2); + $Secret = bin2hex(openssl_random_pseudo_bytes($NumBytes)); + return substr($Secret, 0, $Length); } // Send a message to an IRC bot listening on SOCKET_LISTEN_PORT function send_irc($Raw) { - if (defined('DISABLE_IRC') && DISABLE_IRC === true) { - return; - } - $IRCSocket = fsockopen(SOCKET_LISTEN_ADDRESS, SOCKET_LISTEN_PORT); - $Raw = str_replace(array("\n", "\r"), '', $Raw); - fwrite($IRCSocket, $Raw); - fclose($IRCSocket); + if (defined('DISABLE_IRC') && DISABLE_IRC === true) { + return; + } + $IRCSocket = fsockopen(SOCKET_LISTEN_ADDRESS, SOCKET_LISTEN_PORT); + $Raw = str_replace(array("\n", "\r"), '', $Raw); + fwrite($IRCSocket, $Raw); + fclose($IRCSocket); } diff --git a/classes/applicant.class.php b/classes/applicant.class.php index 0c994314b..4641bf3c0 100644 --- a/classes/applicant.class.php +++ b/classes/applicant.class.php @@ -1,136 +1,136 @@ user_id = $user_id; - $this->thread = new Thread('staff-role'); - $this->role_id = ApplicantRole::get_id($role); - $this->body = $body; - $this->resovled = false; - G::$DB->prepared_query(" - INSERT INTO applicant (RoleID, UserID, ThreadID, Body) - VALUES (?, ?, ?, ?) - ", $this->role_id, $this->user_id, $this->thread->id(), $this->body - ); - $this->id = G::$DB->inserted_id(); - G::$Cache->delete_value(self::CACHE_KEY_NEW_COUNT); - $this->flush_applicant_list_cache(); - } + /** + * persistent Applicant constructor + * @param int $user_id id of applicant in users_main table + * @param string $role The name of the role the applicant is applying for + * @param string $body Their application request + */ + public function __construct ($user_id = null, $role = null, $body = null) { + if (!isset($user_id)) { + return; + } + $this->user_id = $user_id; + $this->thread = new Thread('staff-role'); + $this->role_id = ApplicantRole::get_id($role); + $this->body = $body; + $this->resovled = false; + G::$DB->prepared_query(" + INSERT INTO applicant (RoleID, UserID, ThreadID, Body) + VALUES (?, ?, ?, ?) + ", $this->role_id, $this->user_id, $this->thread->id(), $this->body + ); + $this->id = G::$DB->inserted_id(); + G::$Cache->delete_value(self::CACHE_KEY_NEW_COUNT); + $this->flush_applicant_list_cache(); + } - private function flush_applicant_list_cache() { - G::$Cache->delete_value('user_is_applicant.' . $this->user_id); - for ($page = 1; $page; ++$page) { - $hit = 0; - $cache_key = [ - sprintf(self::CACHE_KEY_OPEN, $page), - sprintf(self::CACHE_KEY_RESOLVED, $page), - sprintf(self::CACHE_KEY_OPEN . ".$this->user_id", $page), - sprintf(self::CACHE_KEY_RESOLVED . ".$this->user_id", $page) - ]; - foreach ($cache_key as $key) { - if (G::$Cache->get_value($key) !== false) { - ++$hit; - G::$Cache->delete_value($key); - } - } - if (!$hit) { - break; - } - } - return $this; - } + private function flush_applicant_list_cache() { + G::$Cache->delete_value('user_is_applicant.' . $this->user_id); + for ($page = 1; $page; ++$page) { + $hit = 0; + $cache_key = [ + sprintf(self::CACHE_KEY_OPEN, $page), + sprintf(self::CACHE_KEY_RESOLVED, $page), + sprintf(self::CACHE_KEY_OPEN . ".$this->user_id", $page), + sprintf(self::CACHE_KEY_RESOLVED . ".$this->user_id", $page) + ]; + foreach ($cache_key as $key) { + if (G::$Cache->get_value($key) !== false) { + ++$hit; + G::$Cache->delete_value($key); + } + } + if (!$hit) { + break; + } + } + return $this; + } - public function id() { - return $this->id; - } + public function id() { + return $this->id; + } - public function user_id() { - return $this->user_id; - } + public function user_id() { + return $this->user_id; + } - public function thread() { - return $this->thread; - } + public function thread() { + return $this->thread; + } - public function thread_id() { - return $this->thread->id(); - } + public function thread_id() { + return $this->thread->id(); + } - public function role_title() { - return ApplicantRole::get_title($this->role_id); - } + public function role_title() { + return ApplicantRole::get_title($this->role_id); + } - public function body() { - return $this->body; - } + public function body() { + return $this->body; + } - public function created() { - return $this->created; - } + public function created() { + return $this->created; + } - public function resolve($resolved = true) { - $this->resolved = $resolved; - G::$DB->prepared_query(" - UPDATE applicant - SET Resolved = ? - WHERE ID = ? - ", $this->resolved, $this->id); - $key = sprintf(self::CACHE_KEY, $this->id); - $data = G::$Cache->get_value($key); - if ($data !== false) { - $data['Resolved'] = $this->resolved; - G::$Cache->replace_value($key, $data, 86400); - } - G::$Cache->delete_value('user_is_applicant.' . $this->user_id); - return $this->flush_applicant_list_cache(); - } + public function resolve($resolved = true) { + $this->resolved = $resolved; + G::$DB->prepared_query(" + UPDATE applicant + SET Resolved = ? + WHERE ID = ? + ", $this->resolved, $this->id); + $key = sprintf(self::CACHE_KEY, $this->id); + $data = G::$Cache->get_value($key); + if ($data !== false) { + $data['Resolved'] = $this->resolved; + G::$Cache->replace_value($key, $data, 86400); + } + G::$Cache->delete_value('user_is_applicant.' . $this->user_id); + return $this->flush_applicant_list_cache(); + } - public function is_resolved() { - return $this->resolved; - } + public function is_resolved() { + return $this->resolved; + } - // DELEGATES + // DELEGATES - /** - * Save the applicant thread note (see Thread class) - */ - public function save_note($poster_id, $body, $visibility) { - $this->thread->save_note($poster_id, $body, $visibility); - G::$Cache->delete_value(sprintf(self::CACHE_KEY, $this->id)); - G::$Cache->delete_value(self::CACHE_KEY_NEW_REPLY); - G::$Cache->delete_value(self::CACHE_KEY_NEW_COUNT); - if ($visibility == 'public' && Permissions::has_permission($poster_id, 'admin_manage_applicants')) { - $staff = Users::user_info($poster_id); - $user = Users::user_info($this->user_id()); - Misc::send_pm( - $this->user_id(), - 0, - sprintf('You have a reply to your %s application', $this->role_title()), - sprintf(<<thread->save_note($poster_id, $body, $visibility); + G::$Cache->delete_value(sprintf(self::CACHE_KEY, $this->id)); + G::$Cache->delete_value(self::CACHE_KEY_NEW_REPLY); + G::$Cache->delete_value(self::CACHE_KEY_NEW_COUNT); + if ($visibility == 'public' && Permissions::has_permission($poster_id, 'admin_manage_applicants')) { + $staff = Users::user_info($poster_id); + $user = Users::user_info($this->user_id()); + Misc::send_pm( + $this->user_id(), + 0, + sprintf('You have a reply to your %s application', $this->role_title()), + sprintf(<<role_title() - , site_url() . '/apply.php?action=view&id=' . $this->id() - , SITE_NAME - ) - ); - } - return $this->flush_applicant_list_cache(); - } + , $user['Username'] + , $staff['Username'] + , $this->role_title() + , site_url() . '/apply.php?action=view&id=' . $this->id() + , SITE_NAME + ) + ); + } + return $this->flush_applicant_list_cache(); + } - public function delete_note($note_id) { - G::$Cache->delete_value(self::CACHE_KEY_NEW_REPLY); - $this->thread()->delete_note($note_id); - return $this->flush_applicant_list_cache(); - } + public function delete_note($note_id) { + G::$Cache->delete_value(self::CACHE_KEY_NEW_REPLY); + $this->thread()->delete_note($note_id); + return $this->flush_applicant_list_cache(); + } - /** - * Get the applicant thread story (see Thread class) - */ - public function get_story() { - return $this->thread->get_story(); - } + /** + * Get the applicant thread story (see Thread class) + */ + public function get_story() { + return $this->thread->get_story(); + } - // FACTORY METHODS + // FACTORY METHODS - /** - * Instantiate an instance of an Applicant from an id - * @param $id int The id of an Applicant - * @return an Applicant object - */ - static public function factory($id) { - $applicant = new self(); - $key = sprintf(self::CACHE_KEY, $id); - $data = G::$Cache->get_value($key); - if ($data === false) { - G::$DB->prepared_query(" - SELECT a.RoleID, a.UserID, a.ThreadID, a.Body, a.Resolved, a.Created, a.Modified - FROM applicant a - WHERE a.ID = ? - ", $id); - if (G::$DB->has_results()) { - $data = G::$DB->next_record(); - G::$Cache->cache_value($key, $data, 86400); - } - } - $applicant->id = $id; - $applicant->role_id = $data['RoleID']; - $applicant->user_id = $data['UserID']; - $applicant->thread = Thread::factory($data['ThreadID']); - $applicant->body = $data['Body']; - $applicant->resolved = $data['Resolved']; - $applicant->created = $data['Created']; - $applicant->modified = $data['Modified']; - return $applicant; - } + /** + * Instantiate an instance of an Applicant from an id + * @param $id int The id of an Applicant + * @return an Applicant object + */ + static public function factory($id) { + $applicant = new self(); + $key = sprintf(self::CACHE_KEY, $id); + $data = G::$Cache->get_value($key); + if ($data === false) { + G::$DB->prepared_query(" + SELECT a.RoleID, a.UserID, a.ThreadID, a.Body, a.Resolved, a.Created, a.Modified + FROM applicant a + WHERE a.ID = ? + ", $id); + if (G::$DB->has_results()) { + $data = G::$DB->next_record(); + G::$Cache->cache_value($key, $data, 86400); + } + } + $applicant->id = $id; + $applicant->role_id = $data['RoleID']; + $applicant->user_id = $data['UserID']; + $applicant->thread = Thread::factory($data['ThreadID']); + $applicant->body = $data['Body']; + $applicant->resolved = $data['Resolved']; + $applicant->created = $data['Created']; + $applicant->modified = $data['Modified']; + return $applicant; + } - /** - * Get an array of applicant entries (e.g. for an index/table of contents page). - * Each array element is a hash with the following keys: - * ID - the Applicant id (in the applicant table) - * RoleID - the role the applicant is applying for - * UserID - the id of the member making the application - * UserName - the name of the member making the application - * ThreadID - the thread story associated with this application - * Created - when this application was created - * Modified - when this application was last modified - * nr_notes - the number of notes (0 or more) in the story - * last_UserID - user ID of the most recent person to comment in the thread - * last_Username - username of the most recent person to comment in the thread - * last_Created - the timestamp of the most recent comment - * @param $page int The page number to fetch (50 entries) defaults to 1 - * @param $resolved int Should resolved applications be included (defaults to no). - * @param $user_id int If non-zero, return applications of this user_id - * @return a list of Applicant information - */ - static public function get_list($page = 1, $resolved = false, $user_id = 0) { - $key = sprintf($resolved ? self::CACHE_KEY_RESOLVED : self::CACHE_KEY_OPEN, $page); - if ($user_id) { - $key .= ".$user_id"; - } - $list = G::$Cache->get_value($key); - if ($list === false) { - $user_condition = $user_id ? 'a.UserID = ?' : '0 = ? /* manager */'; - G::$DB->prepared_query($sql = <<get_value($key); + if ($list === false) { + $user_condition = $user_id ? 'a.UserID = ?' : '0 = ? /* manager */'; + G::$DB->prepared_query($sql = <<has_results() ? G::$DB->to_array() : []; - G::$Cache->cache_value($key, $list, 86400); - } - return $list; - } + , $resolved ? 1 : 0, $user_id, self::ENTRIES_PER_PAGE, ($page-1) * self::ENTRIES_PER_PAGE); + $list = G::$DB->has_results() ? G::$DB->to_array() : []; + G::$Cache->cache_value($key, $list, 86400); + } + return $list; + } - public static function user_is_applicant($user_id) { - $key = 'user_is_applicant.' . $user_id; - $has_application = G::$Cache->get_value($key); - if ($has_application === false) { - $has_application = -1; - G::$DB->prepared_query('SELECT 1 FROM applicant WHERE UserID = ? LIMIT 1', $user_id); - if (G::$DB->has_results()) { - $data = G::$DB->next_record(); - if ($data[0] == 1) { - $has_application = 1; - } - } - G::$Cache->cache_value($key, $has_application, 86400); - } - return $has_application > 0; - } + public static function user_is_applicant($user_id) { + $key = 'user_is_applicant.' . $user_id; + $has_application = G::$Cache->get_value($key); + if ($has_application === false) { + $has_application = -1; + G::$DB->prepared_query('SELECT 1 FROM applicant WHERE UserID = ? LIMIT 1', $user_id); + if (G::$DB->has_results()) { + $data = G::$DB->next_record(); + if ($data[0] == 1) { + $has_application = 1; + } + } + G::$Cache->cache_value($key, $has_application, 86400); + } + return $has_application > 0; + } - public static function new_applicant_count() { - $key = self::CACHE_KEY_NEW_COUNT; - $applicant_count = G::$Cache->get_value($key); - if ($applicant_count === false) { - G::$DB->prepared_query(" - SELECT count(a.ID) as nr - FROM applicant a - INNER JOIN thread t ON (a.ThreadID = t.ID - AND t.ThreadTypeID = (SELECT ID FROM thread_type WHERE Name = ?) - ) - LEFT JOIN thread_note n ON (n.threadid = t.id) + public static function new_applicant_count() { + $key = self::CACHE_KEY_NEW_COUNT; + $applicant_count = G::$Cache->get_value($key); + if ($applicant_count === false) { + G::$DB->prepared_query(" + SELECT count(a.ID) as nr + FROM applicant a + INNER JOIN thread t ON (a.ThreadID = t.ID + AND t.ThreadTypeID = (SELECT ID FROM thread_type WHERE Name = ?) + ) + LEFT JOIN thread_note n ON (n.threadid = t.id) WHERE a.Resolved = 0 AND n.id IS NULL - ", 'staff-role' - ); - if (G::$DB->has_results()) { - $data = G::$DB->next_record(); - $applicant_count = $data['nr']; - } - G::$Cache->cache_value($key, $applicant_count, 3600); - } - return $applicant_count; - } + ", 'staff-role' + ); + if (G::$DB->has_results()) { + $data = G::$DB->next_record(); + $applicant_count = $data['nr']; + } + G::$Cache->cache_value($key, $applicant_count, 3600); + } + return $applicant_count; + } - public static function new_reply_count() { - $key = self::CACHE_KEY_NEW_REPLY; - $reply_count = G::$Cache->get_value($key); - if ($reply_count === false) { - G::$DB->prepared_query(" - SELECT count(*) AS nr - FROM applicant a - INNER JOIN thread_note n USING (ThreadID) - INNER JOIN ( - /* find the last person to comment in an applicant thread */ - SELECT a.ID, max(n.ID) AS noteid - FROM applicant a - INNER JOIN thread t ON (a.threadid = t.id - AND t.ThreadTypeID = (SELECT ID FROM thread_type WHERE Name = ?) - ) - INNER JOIN thread_note n ON (n.ThreadID = a.ThreadID) + public static function new_reply_count() { + $key = self::CACHE_KEY_NEW_REPLY; + $reply_count = G::$Cache->get_value($key); + if ($reply_count === false) { + G::$DB->prepared_query(" + SELECT count(*) AS nr + FROM applicant a + INNER JOIN thread_note n USING (ThreadID) + INNER JOIN ( + /* find the last person to comment in an applicant thread */ + SELECT a.ID, max(n.ID) AS noteid + FROM applicant a + INNER JOIN thread t ON (a.threadid = t.id + AND t.ThreadTypeID = (SELECT ID FROM thread_type WHERE Name = ?) + ) + INNER JOIN thread_note n ON (n.ThreadID = a.ThreadID) WHERE a.Resolved = 0 - GROUP BY a.ID - ) X ON (n.ID = X.noteid) - WHERE a.UserID = n.UserID /* if they are the applicant: not a staff response Q.E.D. */ - ", 'staff-role' - ); - if (G::$DB->has_results()) { - $data = G::$DB->next_record(); - $reply_count = $data['nr']; - } - G::$Cache->cache_value($key, $reply_count, 3600); - } - return $reply_count; - } + GROUP BY a.ID + ) X ON (n.ID = X.noteid) + WHERE a.UserID = n.UserID /* if they are the applicant: not a staff response Q.E.D. */ + ", 'staff-role' + ); + if (G::$DB->has_results()) { + $data = G::$DB->next_record(); + $reply_count = $data['nr']; + } + G::$Cache->cache_value($key, $reply_count, 3600); + } + return $reply_count; + } } diff --git a/classes/applicantrole.class.php b/classes/applicantrole.class.php index 5e919497c..88d095956 100644 --- a/classes/applicantrole.class.php +++ b/classes/applicantrole.class.php @@ -1,168 +1,168 @@ title = $title; - $this->description = $description; - $this->published = $published ? 1 : 0; - $this->user_id = $user_id; - $this->created = strftime('%Y-%m-%d %H:%M:%S', time()); - $this->modified = $this->created; - G::$DB->prepared_query(" - INSERT INTO applicant_role (Title, Description, Published, UserID, Created, Modified) - VALUES (?, ?, ?, ?, ?, ?) - ", $this->title, $this->description, $this->published, $this->user_id, $this->created, $this->modified); - $this->id = G::$DB->inserted_id(); - G::$Cache->delete_value(self::CACHE_KEY_ALL); - G::$Cache->delete_value(self::CACHE_KEY_PUBLISHED); - G::$Cache->cache_value(sprintf(self::CACHE_KEY, $this->id), - [ - 'Title' => $this->title, - 'Published' => $this->published, - 'Description' => $this->description, - 'UserID' => $this->user_id, - 'Created' => $this->created, - 'Modified' => $this->modified - ] - ); - } - - public function id() { - return $this->id; - } - - public function title() { - return $this->title; - } - - public function description() { - return $this->description; - } - - public function is_published() { - return $this->published; - } - - public function user_id() { - return $this->user_id; - } - - public function created() { - return $this->created; - } - - public function modified() { - return $this->modified; - } - - public function update($title, $description, $published) { - $this->title = $title; - $this->description = $description; - $this->published = $published ? 1 : 0; - $this->modified = strftime('%Y-%m-%d %H:%M:%S', time()); - - G::$DB->prepared_query(" - UPDATE applicant_role - SET Title = ?, Published = ?, Description = ?, Modified = ? - WHERE ID = ? - ", $this->title, $this->published, $this->description, $this->modified, - $this->id); - G::$Cache->delete_value(self::CACHE_KEY_ALL); - G::$Cache->delete_value(self::CACHE_KEY_PUBLISHED); - G::$Cache->replace_value(sprintf(self::CACHE_KEY, $this->id), - [ - 'Title' => $this->title, - 'Published' => $this->published, - 'Description' => $this->description, - 'UserID' => $this->user_id, - 'Created' => $this->created, - 'Modified' => $this->modified - ] - ); - return $this; - } - - // FACTORY METHODS - - static public function factory($id) { - $approle = new self(); - $key = sprintf(self::CACHE_KEY, $id); - $data = G::$Cache->get_value($key); - if ($data === false) { - G::$DB->prepared_query(" - SELECT Title, Published, Description, UserID, Created, Modified - FROM applicant_role - WHERE ID = ? - ", $id); - if (G::$DB->has_results()) { - $data = G::$DB->next_record(MYSQLI_ASSOC); - G::$Cache->cache_value($key, $data, 86400); - } - } - $approle->id = $id; - $approle->title = $data['Title']; - $approle->published = $data['Published'] ? 1 : 0; - $approle->description = $data['Description']; - $approle->user_id = $data['UserID']; - $approle->created = $data['Created']; - $approle->modified = $data['Modified']; - return $approle; - } - - static public function get_id($role) { - $list = self::get_list(true); - return $list[$role]['id']; - } - - static public function get_title($id) { - $list = self::get_list(true); - foreach ($list as $role => $data) { - if ($data['id'] == $id) { - return $role; - } - } - return null; - } - - static public function get_list($all = false) { - $key = $all ? self::CACHE_KEY_ALL : self::CACHE_KEY_PUBLISHED; - $list = G::$Cache->get_value($key); - if ($list === false) { - $where = $all ? '/* all */' : 'WHERE r.Published = 1'; - G::$DB->query(" - SELECT r.ID as role_id, r.Title as role, r.Published, r.Description, r.UserID, r.Created, r.Modified - FROM applicant_role r - $where - ORDER BY r.Title - "); - $list = []; - while (($row = G::$DB->next_record(MYSQLI_ASSOC))) { - $list[$row['role']] = [ - 'id' => $row['role_id'], - 'published' => $row['Published'] ? 1 : 0, - 'description' => $row['Description'], - 'user_id' => $row['UserID'], - 'created' => $row['Created'], - 'modified' => $row['Modified'] - ]; - } - G::$Cache->cache_value($key, $list, 86400 * 10); - } - return $list; - } + private $id; + private $title; + private $published; + private $description; + private $user_id; + private $created; + private $modified; + + const CACHE_KEY = 'approle_%d'; + const CACHE_KEY_ALL = 'approle_list_all'; + const CACHE_KEY_PUBLISHED = 'approle_list_published'; + + public function __construct ($title = null, $description = null, $published = null, $user_id = null) { + if (!isset($title)) { + return; + } + $this->title = $title; + $this->description = $description; + $this->published = $published ? 1 : 0; + $this->user_id = $user_id; + $this->created = strftime('%Y-%m-%d %H:%M:%S', time()); + $this->modified = $this->created; + G::$DB->prepared_query(" + INSERT INTO applicant_role (Title, Description, Published, UserID, Created, Modified) + VALUES (?, ?, ?, ?, ?, ?) + ", $this->title, $this->description, $this->published, $this->user_id, $this->created, $this->modified); + $this->id = G::$DB->inserted_id(); + G::$Cache->delete_value(self::CACHE_KEY_ALL); + G::$Cache->delete_value(self::CACHE_KEY_PUBLISHED); + G::$Cache->cache_value(sprintf(self::CACHE_KEY, $this->id), + [ + 'Title' => $this->title, + 'Published' => $this->published, + 'Description' => $this->description, + 'UserID' => $this->user_id, + 'Created' => $this->created, + 'Modified' => $this->modified + ] + ); + } + + public function id() { + return $this->id; + } + + public function title() { + return $this->title; + } + + public function description() { + return $this->description; + } + + public function is_published() { + return $this->published; + } + + public function user_id() { + return $this->user_id; + } + + public function created() { + return $this->created; + } + + public function modified() { + return $this->modified; + } + + public function update($title, $description, $published) { + $this->title = $title; + $this->description = $description; + $this->published = $published ? 1 : 0; + $this->modified = strftime('%Y-%m-%d %H:%M:%S', time()); + + G::$DB->prepared_query(" + UPDATE applicant_role + SET Title = ?, Published = ?, Description = ?, Modified = ? + WHERE ID = ? + ", $this->title, $this->published, $this->description, $this->modified, + $this->id); + G::$Cache->delete_value(self::CACHE_KEY_ALL); + G::$Cache->delete_value(self::CACHE_KEY_PUBLISHED); + G::$Cache->replace_value(sprintf(self::CACHE_KEY, $this->id), + [ + 'Title' => $this->title, + 'Published' => $this->published, + 'Description' => $this->description, + 'UserID' => $this->user_id, + 'Created' => $this->created, + 'Modified' => $this->modified + ] + ); + return $this; + } + + // FACTORY METHODS + + static public function factory($id) { + $approle = new self(); + $key = sprintf(self::CACHE_KEY, $id); + $data = G::$Cache->get_value($key); + if ($data === false) { + G::$DB->prepared_query(" + SELECT Title, Published, Description, UserID, Created, Modified + FROM applicant_role + WHERE ID = ? + ", $id); + if (G::$DB->has_results()) { + $data = G::$DB->next_record(MYSQLI_ASSOC); + G::$Cache->cache_value($key, $data, 86400); + } + } + $approle->id = $id; + $approle->title = $data['Title']; + $approle->published = $data['Published'] ? 1 : 0; + $approle->description = $data['Description']; + $approle->user_id = $data['UserID']; + $approle->created = $data['Created']; + $approle->modified = $data['Modified']; + return $approle; + } + + static public function get_id($role) { + $list = self::get_list(true); + return $list[$role]['id']; + } + + static public function get_title($id) { + $list = self::get_list(true); + foreach ($list as $role => $data) { + if ($data['id'] == $id) { + return $role; + } + } + return null; + } + + static public function get_list($all = false) { + $key = $all ? self::CACHE_KEY_ALL : self::CACHE_KEY_PUBLISHED; + $list = G::$Cache->get_value($key); + if ($list === false) { + $where = $all ? '/* all */' : 'WHERE r.Published = 1'; + G::$DB->query(" + SELECT r.ID as role_id, r.Title as role, r.Published, r.Description, r.UserID, r.Created, r.Modified + FROM applicant_role r + $where + ORDER BY r.Title + "); + $list = []; + while (($row = G::$DB->next_record(MYSQLI_ASSOC))) { + $list[$row['role']] = [ + 'id' => $row['role_id'], + 'published' => $row['Published'] ? 1 : 0, + 'description' => $row['Description'], + 'user_id' => $row['UserID'], + 'created' => $row['Created'], + 'modified' => $row['Modified'] + ]; + } + G::$Cache->cache_value($key, $list, 86400 * 10); + } + return $list; + } } diff --git a/classes/artist.class.php b/classes/artist.class.php index a31986bb2..b761cb809 100644 --- a/classes/artist.class.php +++ b/classes/artist.class.php @@ -1,3 +1,3 @@ - diff --git a/classes/artists.class.php b/classes/artists.class.php index 6ebdebea8..0b7580526 100644 --- a/classes/artists.class.php +++ b/classes/artists.class.php @@ -1,280 +1,280 @@ - { - * [ArtistType] => { - * id, name, aliasid - * } - * } - * ArtistType is an int. It can be: - * 1 => Main artist - * 2 => Guest artist - * 4 => Composer - * 5 => Conductor - * 6 => DJ - */ - public static function get_artists($GroupIDs) { - $Results = array(); - $DBs = array(); - foreach ($GroupIDs as $GroupID) { - if (!is_number($GroupID)) { - continue; - } - $Artists = G::$Cache->get_value('groups_artists_'.$GroupID); - if (is_array($Artists)) { - $Results[$GroupID] = $Artists; - } else { - $DBs[] = $GroupID; - } - } - if (count($DBs) > 0) { - $IDs = implode(',', $DBs); - if (empty($IDs)) { - $IDs = "null"; - } - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT ta.GroupID, - ta.ArtistID, - aa.Name, - ta.Importance, - ta.AliasID - FROM torrents_artists AS ta - JOIN artists_alias AS aa ON ta.AliasID = aa.AliasID - WHERE ta.GroupID IN ($IDs) - ORDER BY ta.GroupID ASC, - ta.Importance ASC, - aa.Name ASC;"); - while (list($GroupID, $ArtistID, $ArtistName, $ArtistImportance, $AliasID) = G::$DB->next_record(MYSQLI_BOTH, false)) { - $Results[$GroupID][$ArtistImportance][] = array('id' => $ArtistID, 'name' => $ArtistName, 'aliasid' => $AliasID); - $New[$GroupID][$ArtistImportance][] = array('id' => $ArtistID, 'name' => $ArtistName, 'aliasid' => $AliasID); - } - G::$DB->set_query_id($QueryID); - foreach ($DBs as $GroupID) { - if (isset($New[$GroupID])) { - G::$Cache->cache_value('groups_artists_'.$GroupID, $New[$GroupID]); - } - else { - G::$Cache->cache_value('groups_artists_'.$GroupID, array()); - } - } - $Missing = array_diff($GroupIDs, array_keys($Results)); - if (!empty($Missing)) { - $Results += array_fill_keys($Missing, array()); - } - } - return $Results; - } + /** + * Given an array of GroupIDs, return their associated artists. + * + * @param array $GroupIDs + * @return an array of the following form: + * GroupID => { + * [ArtistType] => { + * id, name, aliasid + * } + * } + * ArtistType is an int. It can be: + * 1 => Main artist + * 2 => Guest artist + * 4 => Composer + * 5 => Conductor + * 6 => DJ + */ + public static function get_artists($GroupIDs) { + $Results = []; + $DBs = []; + foreach ($GroupIDs as $GroupID) { + if (!is_number($GroupID)) { + continue; + } + $Artists = G::$Cache->get_value('groups_artists_'.$GroupID); + if (is_array($Artists)) { + $Results[$GroupID] = $Artists; + } else { + $DBs[] = $GroupID; + } + } + if (count($DBs) > 0) { + $IDs = implode(',', $DBs); + if (empty($IDs)) { + $IDs = "null"; + } + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT ta.GroupID, + ta.ArtistID, + aa.Name, + ta.Importance, + ta.AliasID + FROM torrents_artists AS ta + JOIN artists_alias AS aa ON ta.AliasID = aa.AliasID + WHERE ta.GroupID IN ($IDs) + ORDER BY ta.GroupID ASC, + ta.Importance ASC, + aa.Name ASC;"); + while (list($GroupID, $ArtistID, $ArtistName, $ArtistImportance, $AliasID) = G::$DB->next_record(MYSQLI_BOTH, false)) { + $Results[$GroupID][$ArtistImportance][] = array('id' => $ArtistID, 'name' => $ArtistName, 'aliasid' => $AliasID); + $New[$GroupID][$ArtistImportance][] = array('id' => $ArtistID, 'name' => $ArtistName, 'aliasid' => $AliasID); + } + G::$DB->set_query_id($QueryID); + foreach ($DBs as $GroupID) { + if (isset($New[$GroupID])) { + G::$Cache->cache_value('groups_artists_'.$GroupID, $New[$GroupID]); + } + else { + G::$Cache->cache_value('groups_artists_'.$GroupID, []); + } + } + $Missing = array_diff($GroupIDs, array_keys($Results)); + if (!empty($Missing)) { + $Results += array_fill_keys($Missing, []); + } + } + return $Results; + } - /** - * Convenience function for get_artists, when you just need one group. - * - * @param int $GroupID - * @return array - see get_artists - */ - public static function get_artist($GroupID) { - $Results = Artists::get_artists(array($GroupID)); - return $Results[$GroupID]; - } + /** + * Convenience function for get_artists, when you just need one group. + * + * @param int $GroupID + * @return array - see get_artists + */ + public static function get_artist($GroupID) { + $Results = Artists::get_artists(array($GroupID)); + return $Results[$GroupID]; + } - /** - * Format an array of artists for display. - * TODO: Revisit the logic of this, see if we can helper-function the copypasta. - * - * @param array Artists an array of the form output by get_artists - * @param boolean $MakeLink if true, the artists will be links, if false, they will be text. - * @param boolean $IncludeHyphen if true, appends " - " to the end. - * @param $Escape if true, output will be escaped. Think carefully before setting it false. - */ - public static function display_artists($Artists, $MakeLink = true, $IncludeHyphen = true, $Escape = true) { - if (!empty($Artists)) { - $ampersand = ($Escape) ? ' & ' : ' & '; - $link = ''; + /** + * Format an array of artists for display. + * TODO: Revisit the logic of this, see if we can helper-function the copypasta. + * + * @param array Artists an array of the form output by get_artists + * @param boolean $MakeLink if true, the artists will be links, if false, they will be text. + * @param boolean $IncludeHyphen if true, appends " - " to the end. + * @param $Escape if true, output will be escaped. Think carefully before setting it false. + */ + public static function display_artists($Artists, $MakeLink = true, $IncludeHyphen = true, $Escape = true) { + if (!empty($Artists)) { + $ampersand = ($Escape) ? ' & ' : ' & '; + $link = ''; - $MainArtists = isset($Artists[1]) ? $Artists[1] : null; - $Guests = isset($Artists[2]) ? $Artists[2] : null; - $Composers = isset($Artists[4]) ? $Artists[4] : null; - $Conductors = isset($Artists[5]) ? $Artists[5] : null; - $DJs = isset($Artists[6]) ? $Artists[6] : null; + $MainArtists = isset($Artists[1]) ? $Artists[1] : null; + $Guests = isset($Artists[2]) ? $Artists[2] : null; + $Composers = isset($Artists[4]) ? $Artists[4] : null; + $Conductors = isset($Artists[5]) ? $Artists[5] : null; + $DJs = isset($Artists[6]) ? $Artists[6] : null; - $MainArtistCount = $MainArtists == null ? 0 : count($MainArtists); - $GuestCount = $Guests == null ? 0 : count($Guests); - $ComposerCount = $Composers == null ? 0 : count($Composers); - $ConductorCount = $Conductors == null ? 0 : count($Conductors); - $DJCount = $DJs == null ? 0 : count($DJs); + $MainArtistCount = $MainArtists == null ? 0 : count($MainArtists); + $GuestCount = $Guests == null ? 0 : count($Guests); + $ComposerCount = $Composers == null ? 0 : count($Composers); + $ConductorCount = $Conductors == null ? 0 : count($Conductors); + $DJCount = $DJs == null ? 0 : count($DJs); - if (($MainArtistCount + $ConductorCount + $DJCount == 0) && ($ComposerCount == 0)) { - return ''; - } - // Various Composers is not needed and is ugly and should die - switch ($ComposerCount) { - case 0: - break; - case 1: - $link .= Artists::display_artist($Composers[0], $MakeLink, $Escape); - break; - case 2: - $link .= Artists::display_artist($Composers[0], $MakeLink, $Escape).$ampersand.Artists::display_artist($Composers[1], $MakeLink, $Escape); - break; - } + if (($MainArtistCount + $ConductorCount + $DJCount == 0) && ($ComposerCount == 0)) { + return ''; + } + // Various Composers is not needed and is ugly and should die + switch ($ComposerCount) { + case 0: + break; + case 1: + $link .= Artists::display_artist($Composers[0], $MakeLink, $Escape); + break; + case 2: + $link .= Artists::display_artist($Composers[0], $MakeLink, $Escape).$ampersand.Artists::display_artist($Composers[1], $MakeLink, $Escape); + break; + } - if (($ComposerCount > 0) && ($ComposerCount < 3) && ($MainArtistCount > 0)) { - $link .= ' performed by '; - } + if (($ComposerCount > 0) && ($ComposerCount < 3) && ($MainArtistCount > 0)) { + $link .= ' performed by '; + } - $ComposerStr = $link; + $ComposerStr = $link; - switch ($MainArtistCount) { - case 0: - break; - case 1: - $link .= Artists::display_artist($MainArtists[0], $MakeLink, $Escape); - break; - case 2: - $link .= Artists::display_artist($MainArtists[0], $MakeLink, $Escape).$ampersand.Artists::display_artist($MainArtists[1], $MakeLink, $Escape); - break; - default: - $link .= 'Various Artists'; - } + switch ($MainArtistCount) { + case 0: + break; + case 1: + $link .= Artists::display_artist($MainArtists[0], $MakeLink, $Escape); + break; + case 2: + $link .= Artists::display_artist($MainArtists[0], $MakeLink, $Escape).$ampersand.Artists::display_artist($MainArtists[1], $MakeLink, $Escape); + break; + default: + $link .= 'Various Artists'; + } - if (($ConductorCount > 0) && ($MainArtistCount + $ComposerCount > 0) && ($ComposerCount < 3 || $MainArtistCount > 0)) { - $link .= ' under '; - } - switch ($ConductorCount) { - case 0: - break; - case 1: - $link .= Artists::display_artist($Conductors[0], $MakeLink, $Escape); - break; - case 2: - $link .= Artists::display_artist($Conductors[0], $MakeLink, $Escape).$ampersand.Artists::display_artist($Conductors[1], $MakeLink, $Escape); - break; - default: - $link .= ' Various Conductors'; - } + if (($ConductorCount > 0) && ($MainArtistCount + $ComposerCount > 0) && ($ComposerCount < 3 || $MainArtistCount > 0)) { + $link .= ' under '; + } + switch ($ConductorCount) { + case 0: + break; + case 1: + $link .= Artists::display_artist($Conductors[0], $MakeLink, $Escape); + break; + case 2: + $link .= Artists::display_artist($Conductors[0], $MakeLink, $Escape).$ampersand.Artists::display_artist($Conductors[1], $MakeLink, $Escape); + break; + default: + $link .= ' Various Conductors'; + } - if (($ComposerCount > 0) && ($MainArtistCount + $ConductorCount > 3) && ($MainArtistCount > 1) && ($ConductorCount > 1)) { - $link = $ComposerStr . 'Various Artists'; - } elseif (($ComposerCount > 2) && ($MainArtistCount + $ConductorCount == 0)) { - $link = 'Various Composers'; - } + if (($ComposerCount > 0) && ($MainArtistCount + $ConductorCount > 3) && ($MainArtistCount > 1) && ($ConductorCount > 1)) { + $link = $ComposerStr . 'Various Artists'; + } elseif (($ComposerCount > 2) && ($MainArtistCount + $ConductorCount == 0)) { + $link = 'Various Composers'; + } - // DJs override everything else - switch ($DJCount) { - case 0: - break; - case 1: - $link = Artists::display_artist($DJs[0], $MakeLink, $Escape); - break; - case 2: - $link = Artists::display_artist($DJs[0], $MakeLink, $Escape).$ampersand.Artists::display_artist($DJs[1], $MakeLink, $Escape); - break; - default: - $link = 'Various DJs'; - } - return $link.($IncludeHyphen?' - ':''); - } else { - return ''; - } - } + // DJs override everything else + switch ($DJCount) { + case 0: + break; + case 1: + $link = Artists::display_artist($DJs[0], $MakeLink, $Escape); + break; + case 2: + $link = Artists::display_artist($DJs[0], $MakeLink, $Escape).$ampersand.Artists::display_artist($DJs[1], $MakeLink, $Escape); + break; + default: + $link = 'Various DJs'; + } + return $link.($IncludeHyphen?' - ':''); + } else { + return ''; + } + } - /** - * Formats a single artist name. - * - * @param array $Artist an array of the form ('id'=>ID, 'name'=>Name) - * @param boolean $MakeLink If true, links to the artist page. - * @param boolean $Escape If false and $MakeLink is false, returns the unescaped, unadorned artist name. - * @return string Formatted artist name. - */ - public static function display_artist($Artist, $MakeLink = true, $Escape = true) { - if ($MakeLink && !$Escape) { - error('Invalid parameters to Artists::display_artist()'); - } elseif ($MakeLink) { - return ''.display_str($Artist['name']).''; - } elseif ($Escape) { - return display_str($Artist['name']); - } else { - return $Artist['name']; - } - } + /** + * Formats a single artist name. + * + * @param array $Artist an array of the form ('id'=>ID, 'name'=>Name) + * @param boolean $MakeLink If true, links to the artist page. + * @param boolean $Escape If false and $MakeLink is false, returns the unescaped, unadorned artist name. + * @return string Formatted artist name. + */ + public static function display_artist($Artist, $MakeLink = true, $Escape = true) { + if ($MakeLink && !$Escape) { + error('Invalid parameters to Artists::display_artist()'); + } elseif ($MakeLink) { + return ''.display_str($Artist['name']).''; + } elseif ($Escape) { + return display_str($Artist['name']); + } else { + return $Artist['name']; + } + } - /** - * Deletes an artist and their requests, wiki, and tags. - * Does NOT delete their torrents. - * - * @param int $ArtistID - */ - public static function delete_artist($ArtistID) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT Name - FROM artists_group - WHERE ArtistID = ".$ArtistID); - list($Name) = G::$DB->next_record(MYSQLI_NUM, false); + /** + * Deletes an artist and their requests, wiki, and tags. + * Does NOT delete their torrents. + * + * @param int $ArtistID + */ + public static function delete_artist($ArtistID) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT Name + FROM artists_group + WHERE ArtistID = ".$ArtistID); + list($Name) = G::$DB->next_record(MYSQLI_NUM, false); - // Delete requests - G::$DB->query(" - SELECT RequestID - FROM requests_artists - WHERE ArtistID = $ArtistID - AND ArtistID != 0"); - $Requests = G::$DB->to_array(); - foreach ($Requests AS $Request) { - list($RequestID) = $Request; - G::$DB->query('DELETE FROM requests WHERE ID='.$RequestID); - G::$DB->query('DELETE FROM requests_votes WHERE RequestID='.$RequestID); - G::$DB->query('DELETE FROM requests_tags WHERE RequestID='.$RequestID); - G::$DB->query('DELETE FROM requests_artists WHERE RequestID='.$RequestID); - } + // Delete requests + G::$DB->query(" + SELECT RequestID + FROM requests_artists + WHERE ArtistID = $ArtistID + AND ArtistID != 0"); + $Requests = G::$DB->to_array(); + foreach ($Requests AS $Request) { + list($RequestID) = $Request; + G::$DB->query('DELETE FROM requests WHERE ID='.$RequestID); + G::$DB->query('DELETE FROM requests_votes WHERE RequestID='.$RequestID); + G::$DB->query('DELETE FROM requests_tags WHERE RequestID='.$RequestID); + G::$DB->query('DELETE FROM requests_artists WHERE RequestID='.$RequestID); + } - // Delete artist - G::$DB->query('DELETE FROM artists_group WHERE ArtistID='.$ArtistID); - G::$DB->query('DELETE FROM artists_alias WHERE ArtistID='.$ArtistID); - G::$Cache->decrement('stats_artist_count'); + // Delete artist + G::$DB->query('DELETE FROM artists_group WHERE ArtistID='.$ArtistID); + G::$DB->query('DELETE FROM artists_alias WHERE ArtistID='.$ArtistID); + G::$Cache->decrement('stats_artist_count'); - // Delete wiki revisions - G::$DB->query('DELETE FROM wiki_artists WHERE PageID='.$ArtistID); + // Delete wiki revisions + G::$DB->query('DELETE FROM wiki_artists WHERE PageID='.$ArtistID); - // Delete tags - G::$DB->query('DELETE FROM artists_tags WHERE ArtistID='.$ArtistID); + // Delete tags + G::$DB->query('DELETE FROM artists_tags WHERE ArtistID='.$ArtistID); - // Delete artist comments, subscriptions and quote notifications - Comments::delete_page('artist', $ArtistID); + // Delete artist comments, subscriptions and quote notifications + Comments::delete_page('artist', $ArtistID); - G::$Cache->delete_value('artist_'.$ArtistID); - G::$Cache->delete_value('artist_groups_'.$ArtistID); - // Record in log + G::$Cache->delete_value('artist_'.$ArtistID); + G::$Cache->delete_value('artist_groups_'.$ArtistID); + // Record in log - if (!empty(G::$LoggedUser['Username'])) { - $Username = G::$LoggedUser['Username']; - } else { - $Username = 'System'; - } - Misc::write_log("Artist $ArtistID ($Name) was deleted by $Username"); - G::$DB->set_query_id($QueryID); - } + if (!empty(G::$LoggedUser['Username'])) { + $Username = G::$LoggedUser['Username']; + } else { + $Username = 'System'; + } + Misc::write_log("Artist $ArtistID ($Name) was deleted by $Username"); + G::$DB->set_query_id($QueryID); + } - /** - * Remove LRM (left-right-marker) and trims, because people copypaste carelessly. - * If we don't do this, we get seemingly duplicate artist names. - * TODO: make stricter, e.g. on all whitespace characters or Unicode normalisation - * - * @param string $ArtistName - */ - public static function normalise_artist_name($ArtistName) { - // \u200e is ‎ - $ArtistName = trim($ArtistName); - $ArtistName = preg_replace('/^(\xE2\x80\x8E)+/', '', $ArtistName); - $ArtistName = preg_replace('/(\xE2\x80\x8E)+$/', '', $ArtistName); - return trim(preg_replace('/ +/', ' ', $ArtistName)); - } + /** + * Remove LRM (left-right-marker) and trims, because people copypaste carelessly. + * If we don't do this, we get seemingly duplicate artist names. + * TODO: make stricter, e.g. on all whitespace characters or Unicode normalisation + * + * @param string $ArtistName + */ + public static function normalise_artist_name($ArtistName) { + // \u200e is ‎ + $ArtistName = trim($ArtistName); + $ArtistName = preg_replace('/^(\xE2\x80\x8E)+/', '', $ArtistName); + $ArtistName = preg_replace('/(\xE2\x80\x8E)+$/', '', $ArtistName); + return trim(preg_replace('/ +/', ' ', $ArtistName)); + } } ?> diff --git a/classes/artists_similar.class.php b/classes/artists_similar.class.php index 1ac5716f3..e74ff6f10 100644 --- a/classes/artists_similar.class.php +++ b/classes/artists_similar.class.php @@ -1,385 +1,385 @@ -ID = $ID; - $this->NameLength = mb_strlen($Name, 'utf8'); - $this->Name = display_str($Name); - } + var $ID = 0; + var $Name = 0; + var $NameLength = 0; + var $SimilarID = 0; + var $Displayed = false; + var $x = 0; + var $y = 0; + var $Similar = []; + + function __construct($ID = '', $Name = '') { + $this->ID = $ID; + $this->NameLength = mb_strlen($Name, 'utf8'); + $this->Name = display_str($Name); + } } class ARTISTS_SIMILAR extends ARTIST{ - var $Artists = array(); - var $TotalScore = 0; - - var $xValues = array(WIDTH=>1); - var $yValues = array(HEIGHT=>1); - - var $LargestDecimal = 0; - var $LowestDecimal = 1; - - - - function dump_data() { - return serialize(array(time(), $this->Name, $this->x, $this->y, serialize($this->Artists), serialize($this->Similar))); - } - - function load_data($Data) { - list($LastUpdated, $this->Name, $this->x, $this->y, $this->Artists, $this->Similar) = unserialize($Data); - $this->Artists = unserialize($this->Artists); - $this->Similar = unserialize($this->Similar); - } - - function set_up() { - $QueryID = G::$DB->get_query_id(); - - $this->x = ceil(WIDTH / 2); - $this->y = ceil(HEIGHT / 2); - - $this->xValues[$this->x] = $this->ID; - $this->yValues[$this->y] = $this->ID; - - - // Get artists that are directly similar to the artist - $ArtistIDs = array(); - G::$DB->query(" - SELECT - s2.ArtistID, - ag.Name, - ass.Score - FROM artists_similar AS s1 - JOIN artists_similar AS s2 ON s1.SimilarID=s2.SimilarID AND s1.ArtistID!=s2.ArtistID - JOIN artists_similar_scores AS ass ON ass.SimilarID=s1.SimilarID - JOIN artists_group AS ag ON ag.ArtistID=s2.ArtistID - WHERE s1.ArtistID=".$this->ID." - ORDER BY ass.Score DESC - LIMIT 14"); - - if (!G::$DB->has_results()) { - return; - } - - // Build into array. Each artist is its own object in $this->Artists - while (list($ArtistID, $Name, $Score) = G::$DB->next_record(MYSQLI_NUM, false)) { - if ($Score < 0) { - continue; - } - $this->Artists[$ArtistID] = new ARTIST($ArtistID, $Name); - $this->Similar[$ArtistID] = array('ID' => $ArtistID, 'Score' => $Score); - $this->TotalScore += $Score; - $ArtistIDs[] = $ArtistID; - } - - // Get similarities between artists on the map - G::$DB->query(" - SELECT - s1.ArtistID, - s2.ArtistID - FROM artists_similar AS s1 - JOIN artists_similar AS s2 ON s1.SimilarID=s2.SimilarID AND s1.ArtistID!=s2.ArtistID - JOIN artists_similar_scores AS ass ON ass.SimilarID=s1.SimilarID - JOIN artists_group AS a ON a.ArtistID=s2.ArtistID - WHERE s1.ArtistID IN(".implode(',', $ArtistIDs).') - AND s2.ArtistID IN('.implode(',', $ArtistIDs).')'); - - // Build into array - while (list($Artist1ID, $Artist2ID) = G::$DB->next_record()) { - $this->Artists[$Artist1ID]->Similar[$Artist2ID] = array('ID'=>$Artist2ID); - } - - // Calculate decimal point scores between artists - foreach ($this->Similar as $SimilarArtist) { - list($ArtistID, $Score) = array_values($SimilarArtist); - $this->Similar[$ArtistID]['Decimal'] = $this->similarity($Score, $this->TotalScore); - - if ($this->Similar[$ArtistID]['Decimal'] < $this->LowestDecimal) { - $this->LowestDecimal = $this->Similar[$ArtistID]['Decimal']; - } - if ($this->Similar[$ArtistID]['Decimal'] > $this->LargestDecimal) { - $this->LargestDecimal = $this->Similar[$ArtistID]['Decimal']; - } - } - reset($this->Artists); - - G::$DB->set_query_id($QueryID); - } - - function set_positions() { - $xValues = array(); // Possible x values - $Root = ceil(WIDTH / 4); // Half-way into half of the image - $Offset = 4; // Distance from the root (a quarter of the way into the image) to the x value - - // The number of artists placed in the top or the bottom - $NumTop = 0; - $NumBottom = 0; - - // The number of artists placed in the left or the right - $NumLeft = 0; - $NumRight = 0; - - $Multiplier = 0; - - // Build up an impressive list of possible x values - // We later iterate through these, and pick out the ones we want - - // These x values are all below WIDTH/2 (all on the left) - // The script later chooses which side to put them on - - // We create more very low x values because they're more likely to be skipped - for ($i = 0; $i <= count($this->Artists) * 4; $i++) { - if ($Offset >= ((WIDTH / 4))) { - $Offset = $Offset % (WIDTH / 4); - } - $Plus = $Root + $Offset; // Point on the right of the root - $Minus = abs($Root - $Offset); // Point on the left of the root - - $xValues[$Plus] = $Plus; - - $xValues[$Minus] = $Minus; - - // Throw in an extra x value closer to the edge, because they're more likely to be skipped - - if ($Minus > 30) { - // $xValues[$Minus - 30] = $Minus - 30; - } - - $Offset = $Offset + rand(5, 20); // Increase offset, and go again - } - - foreach ($this->Artists as $Artist) { - $ArtistID = $Artist->ID; - if ($Artist->Displayed == true) { - continue; - } - $this->Similar[$ArtistID]['Decimal'] = $this->Similar[$ArtistID]['Decimal'] * (1 / ($this->LargestDecimal)) - 0.1; - // Calculate the distance away from the center, based on similarity - $IdealDistance = $this->calculate_distance($this->Similar[$ArtistID]['Decimal'], $this->x, $this->y); - - $this->Similar[$ArtistID]['Distance'] = $IdealDistance; - - // 1 = left, 2 = right - $Horizontal = 0; - $Vertical = 0; - - // See if any similar artists have been placed yet. If so, place artist in that half - // (provided that there are enough in the other half to visually balance out) - reset($Artist->Similar); - foreach ($Artist->Similar as $SimilarArtist) { - list($Artist2ID) = array_values($SimilarArtist); - if ($this->Artists[$Artist2ID]) { - if ($this->Artists[$Artist2ID]->x > (WIDTH / 2) && ($NumRight-$NumLeft) < 1) { - $Horizontal = 2; - } elseif ($NumLeft - $NumRight < 1) { - $Horizontal = 1; - } - break; - } - } - - shuffle($xValues); - - while ($xValue = array_shift($xValues)) { - if (abs($this->x - $xValue) <= $IdealDistance) { - if (hypot(abs($this->x - $xValue), ($this->y - 50)) > $IdealDistance - || ceil(sqrt(pow($IdealDistance, 2) - pow($this->x - $xValue, 2))) > (HEIGHT / 2)) { - $xValue = $this->x - ceil(sqrt(pow($IdealDistance, 2) - pow($IdealDistance * 0.1 * rand(5,9), 2))); - //echo "Had to change x value for ".$Artist->Name." to ".$xValue."\n"; - } - // Found a match (Is close enough to the center to satisfy $IdealDistance), - // Now it's time to choose which half to put it on - if (!$Horizontal) { - // No similar artists displayed - $Horizontal = ($NumLeft < $NumRight) ? 1 : 2; - } - if ($Horizontal == 2) { - $xValue = WIDTH - $xValue; - $NumRight++; - } else { - $NumLeft++; - } - - $Artist->x = $xValue; - $this->xValues[$xValue] = $ArtistID; - unset($xValues[$xValue]); - - break; - } - } - if (!$xValue) { // Uh-oh, we were unable to choose an x value. - $xValue = ceil(sqrt(pow($IdealDistance, 2) / 2)); - $xValue = (WIDTH / 2) - $xValue; - $Artist->x = $xValue; - $this->xValues[$xValue] = $ArtistID; - unset($xValues[$xValue]); - } - - - // Pythagoras. $yValue is the vertical distance from the center to the y value - $yValue = sqrt(pow($IdealDistance, 2) - pow(abs($this->x - $Artist->x), 2)); - - - // Now we pick if it should go on the top or bottom - - if ($NumTop > $NumBottom) { // Send it to the bottom half - $yValue = (HEIGHT / 2) + $yValue; - $NumBottom++; - } else { - $yValue=(HEIGHT / 2) - $yValue; - $NumTop++; - } - - $yValue = ceil($yValue); - - // $yValue is now a proper y coordinate - // Now time to do some spacing out - - if ($yValue < 10) { - $yValue += (10 + abs($yValue)) + rand(10,20); - } - - if ($yValue > (HEIGHT - 10)) { - $yValue -= ((HEIGHT / 2) - rand(10,20)); - } - - $i = 1; - while ($Conflict = $this->scan_array_range($this->yValues, abs($yValue - 13), $yValue + 13)) { - if ($i > 10) { - break; - } - if (!$this->scan_array_range($this->yValues, abs($yValue - 5), $yValue - 20)) { - $yValue -= 20; - } - - $yValue = $Conflict + rand(10, 20); - if ($yValue > HEIGHT - 10) { - $yValue -= ceil(HEIGHT / 2.5); - } elseif ($yValue < 10) { - $yValue += ceil(HEIGHT / 2.5); - } - $i++; - } - - $Artist->y = $yValue; - $this->yValues[$yValue] = $ArtistID; - } - reset($this->Artists); - reset($this->xValues); - reset($this->yValues); - - } - - // Calculate the ideal distance from the center point ($Rootx, $Rooty) to the artist's point on the board - // Pythagoras as fun! - function calculate_distance($SimilarityCoefficient, $Rootx, $Rooty) { - $MaxWidth = WIDTH - $Rootx; - $MaxHeight = HEIGHT - $Rooty; - $x = $MaxWidth - ($SimilarityCoefficient * $MaxWidth * 0.01); // Possible x value - $y = $MaxHeight - ($SimilarityCoefficient * $MaxHeight); // Possible y value - $Hypot = hypot($Rootx - $x, $Rooty - $y); - return $MaxWidth - $Hypot; - - } - - function similarity($Score, $TotalArtistScore) { - return (pow(($Score / ($TotalArtistScore + 1)), (1 / 1))); - } - - function scan_array_range($Array, $Start, $Finish) { - if ($Start < 0) { - die($Start); - } - for ($i = $Start; $i <= $Finish; $i++) { - if (isset($Array[$i])) { - return $i; - } - } - return false; - } - - function write_artists() { + var $Artists = []; + var $TotalScore = 0; + + var $xValues = array(WIDTH=>1); + var $yValues = array(HEIGHT=>1); + + var $LargestDecimal = 0; + var $LowestDecimal = 1; + + + + function dump_data() { + return serialize(array(time(), $this->Name, $this->x, $this->y, serialize($this->Artists), serialize($this->Similar))); + } + + function load_data($Data) { + list($LastUpdated, $this->Name, $this->x, $this->y, $this->Artists, $this->Similar) = unserialize($Data); + $this->Artists = unserialize($this->Artists); + $this->Similar = unserialize($this->Similar); + } + + function set_up() { + $QueryID = G::$DB->get_query_id(); + + $this->x = ceil(WIDTH / 2); + $this->y = ceil(HEIGHT / 2); + + $this->xValues[$this->x] = $this->ID; + $this->yValues[$this->y] = $this->ID; + + + // Get artists that are directly similar to the artist + $ArtistIDs = []; + G::$DB->query(" + SELECT + s2.ArtistID, + ag.Name, + ass.Score + FROM artists_similar AS s1 + JOIN artists_similar AS s2 ON s1.SimilarID=s2.SimilarID AND s1.ArtistID!=s2.ArtistID + JOIN artists_similar_scores AS ass ON ass.SimilarID=s1.SimilarID + JOIN artists_group AS ag ON ag.ArtistID=s2.ArtistID + WHERE s1.ArtistID=".$this->ID." + ORDER BY ass.Score DESC + LIMIT 14"); + + if (!G::$DB->has_results()) { + return; + } + + // Build into array. Each artist is its own object in $this->Artists + while (list($ArtistID, $Name, $Score) = G::$DB->next_record(MYSQLI_NUM, false)) { + if ($Score < 0) { + continue; + } + $this->Artists[$ArtistID] = new ARTIST($ArtistID, $Name); + $this->Similar[$ArtistID] = array('ID' => $ArtistID, 'Score' => $Score); + $this->TotalScore += $Score; + $ArtistIDs[] = $ArtistID; + } + + // Get similarities between artists on the map + G::$DB->query(" + SELECT + s1.ArtistID, + s2.ArtistID + FROM artists_similar AS s1 + JOIN artists_similar AS s2 ON s1.SimilarID=s2.SimilarID AND s1.ArtistID!=s2.ArtistID + JOIN artists_similar_scores AS ass ON ass.SimilarID=s1.SimilarID + JOIN artists_group AS a ON a.ArtistID=s2.ArtistID + WHERE s1.ArtistID IN(".implode(',', $ArtistIDs).') + AND s2.ArtistID IN('.implode(',', $ArtistIDs).')'); + + // Build into array + while (list($Artist1ID, $Artist2ID) = G::$DB->next_record()) { + $this->Artists[$Artist1ID]->Similar[$Artist2ID] = array('ID'=>$Artist2ID); + } + + // Calculate decimal point scores between artists + foreach ($this->Similar as $SimilarArtist) { + list($ArtistID, $Score) = array_values($SimilarArtist); + $this->Similar[$ArtistID]['Decimal'] = $this->similarity($Score, $this->TotalScore); + + if ($this->Similar[$ArtistID]['Decimal'] < $this->LowestDecimal) { + $this->LowestDecimal = $this->Similar[$ArtistID]['Decimal']; + } + if ($this->Similar[$ArtistID]['Decimal'] > $this->LargestDecimal) { + $this->LargestDecimal = $this->Similar[$ArtistID]['Decimal']; + } + } + reset($this->Artists); + + G::$DB->set_query_id($QueryID); + } + + function set_positions() { + $xValues = []; // Possible x values + $Root = ceil(WIDTH / 4); // Half-way into half of the image + $Offset = 4; // Distance from the root (a quarter of the way into the image) to the x value + + // The number of artists placed in the top or the bottom + $NumTop = 0; + $NumBottom = 0; + + // The number of artists placed in the left or the right + $NumLeft = 0; + $NumRight = 0; + + $Multiplier = 0; + + // Build up an impressive list of possible x values + // We later iterate through these, and pick out the ones we want + + // These x values are all below WIDTH/2 (all on the left) + // The script later chooses which side to put them on + + // We create more very low x values because they're more likely to be skipped + for ($i = 0; $i <= count($this->Artists) * 4; $i++) { + if ($Offset >= ((WIDTH / 4))) { + $Offset = $Offset % (WIDTH / 4); + } + $Plus = $Root + $Offset; // Point on the right of the root + $Minus = abs($Root - $Offset); // Point on the left of the root + + $xValues[$Plus] = $Plus; + + $xValues[$Minus] = $Minus; + + // Throw in an extra x value closer to the edge, because they're more likely to be skipped + + if ($Minus > 30) { + // $xValues[$Minus - 30] = $Minus - 30; + } + + $Offset = $Offset + rand(5, 20); // Increase offset, and go again + } + + foreach ($this->Artists as $Artist) { + $ArtistID = $Artist->ID; + if ($Artist->Displayed == true) { + continue; + } + $this->Similar[$ArtistID]['Decimal'] = $this->Similar[$ArtistID]['Decimal'] * (1 / ($this->LargestDecimal)) - 0.1; + // Calculate the distance away from the center, based on similarity + $IdealDistance = $this->calculate_distance($this->Similar[$ArtistID]['Decimal'], $this->x, $this->y); + + $this->Similar[$ArtistID]['Distance'] = $IdealDistance; + + // 1 = left, 2 = right + $Horizontal = 0; + $Vertical = 0; + + // See if any similar artists have been placed yet. If so, place artist in that half + // (provided that there are enough in the other half to visually balance out) + reset($Artist->Similar); + foreach ($Artist->Similar as $SimilarArtist) { + list($Artist2ID) = array_values($SimilarArtist); + if ($this->Artists[$Artist2ID]) { + if ($this->Artists[$Artist2ID]->x > (WIDTH / 2) && ($NumRight-$NumLeft) < 1) { + $Horizontal = 2; + } elseif ($NumLeft - $NumRight < 1) { + $Horizontal = 1; + } + break; + } + } + + shuffle($xValues); + + while ($xValue = array_shift($xValues)) { + if (abs($this->x - $xValue) <= $IdealDistance) { + if (hypot(abs($this->x - $xValue), ($this->y - 50)) > $IdealDistance + || ceil(sqrt(pow($IdealDistance, 2) - pow($this->x - $xValue, 2))) > (HEIGHT / 2)) { + $xValue = $this->x - ceil(sqrt(pow($IdealDistance, 2) - pow($IdealDistance * 0.1 * rand(5,9), 2))); + //echo "Had to change x value for ".$Artist->Name." to ".$xValue."\n"; + } + // Found a match (Is close enough to the center to satisfy $IdealDistance), + // Now it's time to choose which half to put it on + if (!$Horizontal) { + // No similar artists displayed + $Horizontal = ($NumLeft < $NumRight) ? 1 : 2; + } + if ($Horizontal == 2) { + $xValue = WIDTH - $xValue; + $NumRight++; + } else { + $NumLeft++; + } + + $Artist->x = $xValue; + $this->xValues[$xValue] = $ArtistID; + unset($xValues[$xValue]); + + break; + } + } + if (!$xValue) { // Uh-oh, we were unable to choose an x value. + $xValue = ceil(sqrt(pow($IdealDistance, 2) / 2)); + $xValue = (WIDTH / 2) - $xValue; + $Artist->x = $xValue; + $this->xValues[$xValue] = $ArtistID; + unset($xValues[$xValue]); + } + + + // Pythagoras. $yValue is the vertical distance from the center to the y value + $yValue = sqrt(pow($IdealDistance, 2) - pow(abs($this->x - $Artist->x), 2)); + + + // Now we pick if it should go on the top or bottom + + if ($NumTop > $NumBottom) { // Send it to the bottom half + $yValue = (HEIGHT / 2) + $yValue; + $NumBottom++; + } else { + $yValue=(HEIGHT / 2) - $yValue; + $NumTop++; + } + + $yValue = ceil($yValue); + + // $yValue is now a proper y coordinate + // Now time to do some spacing out + + if ($yValue < 10) { + $yValue += (10 + abs($yValue)) + rand(10,20); + } + + if ($yValue > (HEIGHT - 10)) { + $yValue -= ((HEIGHT / 2) - rand(10,20)); + } + + $i = 1; + while ($Conflict = $this->scan_array_range($this->yValues, abs($yValue - 13), $yValue + 13)) { + if ($i > 10) { + break; + } + if (!$this->scan_array_range($this->yValues, abs($yValue - 5), $yValue - 20)) { + $yValue -= 20; + } + + $yValue = $Conflict + rand(10, 20); + if ($yValue > HEIGHT - 10) { + $yValue -= ceil(HEIGHT / 2.5); + } elseif ($yValue < 10) { + $yValue += ceil(HEIGHT / 2.5); + } + $i++; + } + + $Artist->y = $yValue; + $this->yValues[$yValue] = $ArtistID; + } + reset($this->Artists); + reset($this->xValues); + reset($this->yValues); + + } + + // Calculate the ideal distance from the center point ($Rootx, $Rooty) to the artist's point on the board + // Pythagoras as fun! + function calculate_distance($SimilarityCoefficient, $Rootx, $Rooty) { + $MaxWidth = WIDTH - $Rootx; + $MaxHeight = HEIGHT - $Rooty; + $x = $MaxWidth - ($SimilarityCoefficient * $MaxWidth * 0.01); // Possible x value + $y = $MaxHeight - ($SimilarityCoefficient * $MaxHeight); // Possible y value + $Hypot = hypot($Rootx - $x, $Rooty - $y); + return $MaxWidth - $Hypot; + + } + + function similarity($Score, $TotalArtistScore) { + return (pow(($Score / ($TotalArtistScore + 1)), (1 / 1))); + } + + function scan_array_range($Array, $Start, $Finish) { + if ($Start < 0) { + die($Start); + } + for ($i = $Start; $i <= $Finish; $i++) { + if (isset($Array[$i])) { + return $i; + } + } + return false; + } + + function write_artists() { ?> -
- Name)?> -
-Artists as $Artist) { - if ($Artist->ID == $this->ID) { - continue; - } - $xPosition = $Artist->x - $Artist->NameLength * 4; - if ($xPosition < 0) { - $xPosition = 3; - $Artist->x = $xPosition; - - } - $Decimal = $this->Similar[$Artist->ID]['Decimal']; - - if ($Decimal < 0.2) { - $FontSize = 8; - } elseif ($Decimal < 0.3) { - $FontSize = 9; - } elseif ($Decimal < 0.4) { - $FontSize = 10; - } else { - $FontSize = 12; - } +
+ Name)?> +
+Artists as $Artist) { + if ($Artist->ID == $this->ID) { + continue; + } + $xPosition = $Artist->x - $Artist->NameLength * 4; + if ($xPosition < 0) { + $xPosition = 3; + $Artist->x = $xPosition; + + } + $Decimal = $this->Similar[$Artist->ID]['Decimal']; + + if ($Decimal < 0.2) { + $FontSize = 8; + } elseif ($Decimal < 0.3) { + $FontSize = 9; + } elseif ($Decimal < 0.4) { + $FontSize = 10; + } else { + $FontSize = 12; + } ?> -
- Name)?> -
-Artists); - } - - function background_image() { - global $Img; - reset($this->Similar); - foreach ($this->Similar as $SimilarArtist) { - list($ArtistID, $Val) = array_values($SimilarArtist); - $Artist = $this->Artists[$ArtistID]; - $Decimal = $this->Similar[$ArtistID]['Decimal']; - $Width = ceil($Decimal * 4) + 1; - - $Img->line($this->x, $this->y, $Artist->x, $Artist->y, $Img->color(199, 218, 255), $Width); - - unset($Artist->Similar[$this->ID]); - reset($Artist->Similar); - foreach ($Artist->Similar as $SimilarArtist2) { - list($Artist2ID) = array_values($SimilarArtist2); - if ($this->Artists[$Artist2ID]) { - $Artist2 = $this->Artists[$Artist2ID]; - $Img->line($Artist->x, $Artist->y, $Artist2->x, $Artist2->y, $Img->color(173, 201, 255)); - unset($Artist2->Similar[$ArtistID]); - } - } - reset($this->xValues); - } - $Img->make_png(SERVER_ROOT.'/static/similar/'.$this->ID.'.png'); - } - - function dump() { - echo "Similarities:\n"; - foreach ($this->Artists as $Artist) { - echo $Artist->ID; - echo ' - '; - echo $Artist->Name; - echo "\n"; - echo 'x - ' . $Artist->x . "\n"; - echo 'y - ' . $Artist->y . "\n"; - print_r($this->Similar[$Artist->ID]); - //print_r($Artist->Similar); - echo "\n\n---\n\n"; - } - - } +
+ Name)?> +
+Artists); + } + + function background_image() { + global $Img; + reset($this->Similar); + foreach ($this->Similar as $SimilarArtist) { + list($ArtistID, $Val) = array_values($SimilarArtist); + $Artist = $this->Artists[$ArtistID]; + $Decimal = $this->Similar[$ArtistID]['Decimal']; + $Width = ceil($Decimal * 4) + 1; + + $Img->line($this->x, $this->y, $Artist->x, $Artist->y, $Img->color(199, 218, 255), $Width); + + unset($Artist->Similar[$this->ID]); + reset($Artist->Similar); + foreach ($Artist->Similar as $SimilarArtist2) { + list($Artist2ID) = array_values($SimilarArtist2); + if ($this->Artists[$Artist2ID]) { + $Artist2 = $this->Artists[$Artist2ID]; + $Img->line($Artist->x, $Artist->y, $Artist2->x, $Artist2->y, $Img->color(173, 201, 255)); + unset($Artist2->Similar[$ArtistID]); + } + } + reset($this->xValues); + } + $Img->make_png(SERVER_ROOT.'/static/similar/'.$this->ID.'.png'); + } + + function dump() { + echo "Similarities:\n"; + foreach ($this->Artists as $Artist) { + echo $Artist->ID; + echo ' - '; + echo $Artist->Name; + echo "\n"; + echo 'x - ' . $Artist->x . "\n"; + echo 'y - ' . $Artist->y . "\n"; + print_r($this->Similar[$Artist->ID]); + //print_r($Artist->Similar); + echo "\n\n---\n\n"; + } + + } } ?> diff --git a/classes/autoenable.class.php b/classes/autoenable.class.php index 5af9bba90..5b2b772af 100644 --- a/classes/autoenable.class.php +++ b/classes/autoenable.class.php @@ -1,358 +1,357 @@ -This may be because a request is already pending for your username, or because a recent request was denied.

You are encouraged to discuss this with staff by visiting %s on %s"; - - // The default request received message - const RECEIVED_MESSAGE = "Your request to re-enable your account has been received. You can expect a reply message in your email within 48 hours.
If you do not receive an email after 48 hours have passed, please visit us on IRC for assistance."; - - /** - * Handle a new enable request - * - * @param string $Username The user's username - * @param string $Email The user's email address - * @return string The output - */ - public static function new_request($Username, $Email) { - if (empty($Username)) { - header("Location: login.php"); - die(); - } - - // Get the user's ID - G::$DB->query(" - SELECT um.ID - FROM users_main AS um - JOIN users_info ui ON ui.UserID = um.ID - WHERE um.Username = '$Username' - AND um.Enabled = '2'"); - - if (G::$DB->has_results()) { - // Make sure the user can make another request - list($UserID) = G::$DB->next_record(); - G::$DB->query(" - SELECT 1 FROM users_enable_requests - WHERE UserID = '$UserID' - AND ( - ( - Timestamp > NOW() - INTERVAL 1 WEEK - AND HandledTimestamp IS NULL - ) - OR - ( - Timestamp > NOW() - INTERVAL 2 MONTH - AND - (Outcome = '".self::DENIED."' - OR Outcome = '".self::DISCARDED."') - ) - )"); - } - - $IP = $_SERVER['REMOTE_ADDR']; - - if (G::$DB->has_results() || !isset($UserID)) { - // User already has/had a pending activation request or username is invalid - $Output = sprintf(self::REJECTED_MESSAGE, BOT_DISABLED_CHAN, BOT_SERVER); - if (isset($UserID)) { - Tools::update_user_notes($UserID, sqltime() . " - Enable request rejected from $IP\n\n"); - } - } else { - // New disable activation request - $UserAgent = db_string($_SERVER['HTTP_USER_AGENT']); - - G::$DB->query(" - INSERT INTO users_enable_requests - (UserID, Email, IP, UserAgent, Timestamp) - VALUES ('$UserID', '$Email', '$IP', '$UserAgent', '".sqltime()."')"); - - // Cache the number of requests for the modbar - G::$Cache->increment_value(self::CACHE_KEY_NAME); - setcookie('username', '', time() - 60 * 60, '/', '', false); - $Output = self::RECEIVED_MESSAGE; - Tools::update_user_notes($UserID, sqltime() . " - Enable request " . G::$DB->inserted_id() . " received from $IP\n\n"); - } - - return $Output; - } - - /* - * Handle requests - * - * @param int|int[] $IDs An array of IDs, or a single ID - * @param int $Status The status to mark the requests as - * @param string $Comment The staff member comment - */ - public static function handle_requests($IDs, $Status, $Comment) { - if ($Status != self::APPROVED && $Status != self::DENIED && $Status != self::DISCARDED) { - error(404); - } - - $UserInfo = array(); - $IDs = (!is_array($IDs)) ? [$IDs] : $IDs; - - if (count($IDs) == 0) { - error(404); - } - - foreach ($IDs as $ID) { - if (!is_number($ID)) { - error(404); - } - } - - G::$DB->query("SELECT Email, ID, UserID - FROM users_enable_requests - WHERE ID IN (".implode(',', $IDs).") - AND Outcome IS NULL"); - $Results = G::$DB->to_array(false, MYSQLI_NUM); - - if ($Status != self::DISCARDED) { - // Prepare email - require(SERVER_ROOT . '/classes/templates.class.php'); - $TPL = NEW TEMPLATE; - if ($Status == self::APPROVED) { - $TPL->open(SERVER_ROOT . '/templates/enable_request_accepted.tpl'); - $TPL->set('SITE_URL', NONSSL_SITE_URL); - } else { - $TPL->open(SERVER_ROOT . '/templates/enable_request_denied.tpl'); - } - - $TPL->set('SITE_NAME', SITE_NAME); - - foreach ($Results as $Result) { - list($Email, $ID, $UserID) = $Result; - $UserInfo[] = array($ID, $UserID); - - if ($Status == self::APPROVED) { - // Generate token - $Token = db_string(Users::make_secret()); - G::$DB->query(" - UPDATE users_enable_requests - SET Token = '$Token' - WHERE ID = '$ID'"); - $TPL->set('TOKEN', $Token); - } - - // Send email - $Subject = "Your enable request for " . SITE_NAME . " has been "; - $Subject .= ($Status == self::APPROVED) ? 'approved' : 'denied'; - - Misc::send_email($Email, $Subject, $TPL->get(), 'noreply'); - } - } else { - foreach ($Results as $Result) { - list(, $ID, $UserID) = $Result; - $UserInfo[] = array($ID, $UserID); - } - } - - // User notes stuff - G::$DB->query(" - SELECT Username - FROM users_main - WHERE ID = '" . G::$LoggedUser['ID'] . "'"); - list($StaffUser) = G::$DB->next_record(); - - foreach ($UserInfo as $User) { - list($ID, $UserID) = $User; - $BaseComment = sqltime() . " - Enable request $ID " . strtolower(self::get_outcome_string($Status)) . ' by [user]'.$StaffUser.'[/user]'; - $BaseComment .= (!empty($Comment)) ? "\nReason: $Comment\n\n" : "\n\n"; - Tools::update_user_notes($UserID, $BaseComment); - } - - // Update database values and decrement cache - G::$DB->query(" - UPDATE users_enable_requests - SET HandledTimestamp = '".sqltime()."', - CheckedBy = '".G::$LoggedUser['ID']."', - Outcome = '$Status' - WHERE ID IN (".implode(',', $IDs).")"); - G::$Cache->decrement_value(self::CACHE_KEY_NAME, count($IDs)); - } - - /** - * Unresolve a discarded request - * - * @param int $ID The request ID - */ - public static function unresolve_request($ID) { - $ID = (int) $ID; - - if (empty($ID)) { - error(404); - } - - G::$DB->query(" - SELECT UserID - FROM users_enable_requests - WHERE Outcome = '" . self::DISCARDED . "' - AND ID = '$ID'"); - - if (!G::$DB->has_results()) { - error(404); - } else { - list($UserID) = G::$DB->next_record(); - } - - G::$DB->query(" - SELECT Username - FROM users_main - WHERE ID = '" . G::$LoggedUser['ID'] . "'"); - list($StaffUser) = G::$DB->next_record(); - - Tools::update_user_notes($UserID, sqltime() . " - Enable request $ID unresolved by [user]" . $StaffUser . '[/user]' . "\n\n"); - G::$DB->query(" - UPDATE users_enable_requests - SET Outcome = NULL, HandledTimestamp = NULL, CheckedBy = NULL - WHERE ID = '$ID'"); - G::$Cache->increment_value(self::CACHE_KEY_NAME); - } - - /** - * Get the corresponding outcome string for a numerical value - * - * @param int $Outcome The outcome integer - * @return string The formatted output string - */ - public static function get_outcome_string($Outcome) { - if ($Outcome == self::APPROVED) { - $String = "Approved"; - } else if ($Outcome == self::DENIED) { - $String = "Rejected"; - } else if ($Outcome == self::DISCARDED) { - $String = "Discarded"; - } else { - $String = "---"; - } - - return $String; - } - - /** - * Handle a user's request to enable an account - * - * @param string $Token The token - * @return string The error output, or an empty string - */ - public static function handle_token($Token) { - $Token = db_string($Token); - G::$DB->query(" - SELECT UserID, HandledTimestamp - FROM users_enable_requests - WHERE Token = '$Token'"); - - if (G::$DB->has_results()) { - list($UserID, $Timestamp) = G::$DB->next_record(); - G::$DB->query("UPDATE users_enable_requests SET Token = NULL WHERE Token = '$Token'"); - if ($Timestamp < time_minus(3600 * 48)) { - // Old request - Tools::update_user_notes($UserID, sqltime() . " - Tried to use an expired enable token from ".$_SERVER['REMOTE_ADDR']."\n\n"); - $Err = "Token has expired. Please visit ".BOT_DISABLED_CHAN." on ".BOT_SERVER." to discuss this with staff."; - } else { - // Good request, decrement cache value and enable account - G::$Cache->decrement_value(AutoEnable::CACHE_KEY_NAME); - G::$DB->query("UPDATE users_main SET Enabled = '1', can_leech = '1' WHERE ID = '$UserID'"); - G::$DB->query("UPDATE users_info SET BanReason = '0' WHERE UserID = '$UserID'"); - G::$DB->query("SELECT torrent_pass FROM users_main WHERE ID='{$UserID}'"); - list($TorrentPass) = G::$DB->next_record(); - Tracker::update_tracker('add_user', array('id' => $UserID, 'passkey' => $TorrentPass)); - $Err = "Your account has been enabled. You may now log in."; - } - } else { - $Err = "Invalid token."; - } - - return $Err; - } - - /** - * Build the search query, from the searchbox inputs - * - * @param int $UserID The user ID - * @param string $IP The IP - * @param string $SubmittedTimestamp The timestamp representing when the request was submitted - * @param int $HandledUserID The ID of the user that handled the request - * @param string $HandledTimestamp The timestamp representing when the request was handled - * @param int $OutcomeSearch The outcome of the request - * @param boolean $Checked Should checked requests be included? - * @return array The WHERE conditions for the query - */ - public static function build_search_query($Username, $IP, $SubmittedBetween, $SubmittedTimestamp1, $SubmittedTimestamp2, $HandledUsername, $HandledBetween, $HandledTimestamp1, $HandledTimestamp2, $OutcomeSearch, $Checked) { - $Where = array(); - - if (!empty($Username)) { - $Where[] = "um1.Username = '$Username'"; - } - - if (!empty($IP)) { - $Where[] = "uer.IP = '$IP'"; - } - - if (!empty($SubmittedTimestamp1)) { - switch($SubmittedBetween) { - case 'on': - $Where[] = "DATE(uer.Timestamp) = DATE('$SubmittedTimestamp1')"; - break; - case 'before': - $Where[] = "DATE(uer.Timestamp) < DATE('$SubmittedTimestamp1')"; - break; - case 'after': - $Where[] = "DATE(uer.Timestamp) > DATE('$SubmittedTimestamp1')"; - break; - case 'between': - if (!empty($SubmittedTimestamp2)) { - $Where[] = "DATE(uer.Timestamp) BETWEEN DATE('$SubmittedTimestamp1') AND DATE('$SubmittedTimestamp2')"; - } - break; - default: - break; - } - } - - if (!empty($HandledTimestamp1)) { - switch($HandledBetween) { - case 'on': - $Where[] = "DATE(uer.HandledTimestamp) = DATE('$HandledTimestamp1')"; - break; - case 'before': - $Where[] = "DATE(uer.HandledTimestamp) < DATE('$HandledTimestamp1')"; - break; - case 'after': - $Where[] = "DATE(uer.HandledTimestamp) > DATE('$HandledTimestamp1')"; - break; - case 'between': - if (!empty($HandledTimestamp2)) { - $Where[] = "DATE(uer.HandledTimestamp) BETWEEN DATE('$HandledTimestamp1') AND DATE('$HandledTimestamp2')"; - } - break; - default: - break; - } - } - - if (!empty($HandledUsername)) { - $Where[] = "um2.Username = '$HandledUsername'"; - } - - if (!empty($OutcomeSearch)) { - $Where[] = "uer.Outcome = '$OutcomeSearch'"; - } - - if ($Checked) { - // This is to skip the if statement in enable_requests.php - $Where[] = "(uer.Outcome IS NULL OR uer.Outcome IS NOT NULL)"; - } - - return $Where; - } + // Constants for database values + const APPROVED = 1; + const DENIED = 2; + const DISCARDED = 3; + + // Cache key to store the number of enable requests + const CACHE_KEY_NAME = 'num_enable_requests'; + + // The default request rejected message + const REJECTED_MESSAGE = "Your request to re-enable your account has been rejected.
This may be because a request is already pending for your username, or because a recent request was denied.

You are encouraged to discuss this with staff by visiting %s on %s"; + + // The default request received message + const RECEIVED_MESSAGE = "Your request to re-enable your account has been received. You can expect a reply message in your email within 48 hours.
If you do not receive an email after 48 hours have passed, please visit us on IRC for assistance."; + + /** + * Handle a new enable request + * + * @param string $Username The user's username + * @param string $Email The user's email address + * @return string The output + */ + public static function new_request($Username, $Email) { + if (empty($Username)) { + header("Location: login.php"); + die(); + } + + // Get the user's ID + G::$DB->query(" + SELECT um.ID + FROM users_main AS um + JOIN users_info ui ON ui.UserID = um.ID + WHERE um.Username = '$Username' + AND um.Enabled = '2'"); + + if (G::$DB->has_results()) { + // Make sure the user can make another request + list($UserID) = G::$DB->next_record(); + G::$DB->query(" + SELECT 1 FROM users_enable_requests + WHERE UserID = '$UserID' + AND ( + ( + Timestamp > NOW() - INTERVAL 1 WEEK + AND HandledTimestamp IS NULL + ) + OR + ( + Timestamp > NOW() - INTERVAL 2 MONTH + AND + (Outcome = '".self::DENIED."' + OR Outcome = '".self::DISCARDED."') + ) + )"); + } + + $IP = $_SERVER['REMOTE_ADDR']; + + if (G::$DB->has_results() || !isset($UserID)) { + // User already has/had a pending activation request or username is invalid + $Output = sprintf(self::REJECTED_MESSAGE, BOT_DISABLED_CHAN, BOT_SERVER); + if (isset($UserID)) { + Tools::update_user_notes($UserID, sqltime() . " - Enable request rejected from $IP\n\n"); + } + } else { + // New disable activation request + $UserAgent = db_string($_SERVER['HTTP_USER_AGENT']); + + G::$DB->query(" + INSERT INTO users_enable_requests + (UserID, Email, IP, UserAgent, Timestamp) + VALUES ('$UserID', '$Email', '$IP', '$UserAgent', '".sqltime()."')"); + + // Cache the number of requests for the modbar + G::$Cache->increment_value(self::CACHE_KEY_NAME); + setcookie('username', '', time() - 60 * 60, '/', '', false); + $Output = self::RECEIVED_MESSAGE; + Tools::update_user_notes($UserID, sqltime() . " - Enable request " . G::$DB->inserted_id() . " received from $IP\n\n"); + } + + return $Output; + } + + /* + * Handle requests + * + * @param int|int[] $IDs An array of IDs, or a single ID + * @param int $Status The status to mark the requests as + * @param string $Comment The staff member comment + */ + public static function handle_requests($IDs, $Status, $Comment) { + if ($Status != self::APPROVED && $Status != self::DENIED && $Status != self::DISCARDED) { + error(404); + } + + $UserInfo = []; + $IDs = (!is_array($IDs)) ? [$IDs] : $IDs; + + if (count($IDs) == 0) { + error(404); + } + + foreach ($IDs as $ID) { + if (!is_number($ID)) { + error(404); + } + } + + G::$DB->query("SELECT Email, ID, UserID + FROM users_enable_requests + WHERE ID IN (".implode(',', $IDs).") + AND Outcome IS NULL"); + $Results = G::$DB->to_array(false, MYSQLI_NUM); + + if ($Status != self::DISCARDED) { + // Prepare email + $context = []; + if ($Status == self::APPROVED) { + $template = 'enable_request_accepted.twig'; + $context['SITE_URL'] = SITE_URL; + } else { + $template = 'enable_request_denied.twig'; + } + + $context['SITE_NAME'] = SITE_NAME; + + foreach ($Results as $Result) { + list($Email, $ID, $UserID) = $Result; + $UserInfo[] = array($ID, $UserID); + + if ($Status == self::APPROVED) { + // Generate token + $Token = Users::make_secret(); + G::$DB->prepared_query(" + UPDATE users_enable_requests + SET Token = ? + WHERE ID = ?", $Token, $ID); + $context['TOKEN'] = $Token; + } + + // Send email + $Subject = "Your enable request for " . SITE_NAME . " has been "; + $Subject .= ($Status == self::APPROVED) ? 'approved' : 'denied'; + + Misc::send_email($Email, $Subject, G::$Twig->render("emails/".template, $context), 'noreply'); + } + } else { + foreach ($Results as $Result) { + list(, $ID, $UserID) = $Result; + $UserInfo[] = array($ID, $UserID); + } + } + + // User notes stuff + G::$DB->query(" + SELECT Username + FROM users_main + WHERE ID = '" . G::$LoggedUser['ID'] . "'"); + list($StaffUser) = G::$DB->next_record(); + + foreach ($UserInfo as $User) { + list($ID, $UserID) = $User; + $BaseComment = sqltime() . " - Enable request $ID " . strtolower(self::get_outcome_string($Status)) . ' by [user]'.$StaffUser.'[/user]'; + $BaseComment .= (!empty($Comment)) ? "\nReason: $Comment\n\n" : "\n\n"; + Tools::update_user_notes($UserID, $BaseComment); + } + + // Update database values and decrement cache + G::$DB->query(" + UPDATE users_enable_requests + SET HandledTimestamp = '".sqltime()."', + CheckedBy = '".G::$LoggedUser['ID']."', + Outcome = '$Status' + WHERE ID IN (".implode(',', $IDs).")"); + G::$Cache->decrement_value(self::CACHE_KEY_NAME, count($IDs)); + } + + /** + * Unresolve a discarded request + * + * @param int $ID The request ID + */ + public static function unresolve_request($ID) { + $ID = (int) $ID; + + if (empty($ID)) { + error(404); + } + + G::$DB->query(" + SELECT UserID + FROM users_enable_requests + WHERE Outcome = '" . self::DISCARDED . "' + AND ID = '$ID'"); + + if (!G::$DB->has_results()) { + error(404); + } else { + list($UserID) = G::$DB->next_record(); + } + + G::$DB->query(" + SELECT Username + FROM users_main + WHERE ID = '" . G::$LoggedUser['ID'] . "'"); + list($StaffUser) = G::$DB->next_record(); + + Tools::update_user_notes($UserID, sqltime() . " - Enable request $ID unresolved by [user]" . $StaffUser . '[/user]' . "\n\n"); + G::$DB->query(" + UPDATE users_enable_requests + SET Outcome = NULL, HandledTimestamp = NULL, CheckedBy = NULL + WHERE ID = '$ID'"); + G::$Cache->increment_value(self::CACHE_KEY_NAME); + } + + /** + * Get the corresponding outcome string for a numerical value + * + * @param int $Outcome The outcome integer + * @return string The formatted output string + */ + public static function get_outcome_string($Outcome) { + if ($Outcome == self::APPROVED) { + $String = "Approved"; + } else if ($Outcome == self::DENIED) { + $String = "Rejected"; + } else if ($Outcome == self::DISCARDED) { + $String = "Discarded"; + } else { + $String = "---"; + } + + return $String; + } + + /** + * Handle a user's request to enable an account + * + * @param string $Token The token + * @return string The error output, or an empty string + */ + public static function handle_token($Token) { + $Token = db_string($Token); + G::$DB->query(" + SELECT UserID, HandledTimestamp + FROM users_enable_requests + WHERE Token = '$Token'"); + + if (G::$DB->has_results()) { + list($UserID, $Timestamp) = G::$DB->next_record(); + G::$DB->query("UPDATE users_enable_requests SET Token = NULL WHERE Token = '$Token'"); + if ($Timestamp < time_minus(3600 * 48)) { + // Old request + Tools::update_user_notes($UserID, sqltime() . " - Tried to use an expired enable token from ".$_SERVER['REMOTE_ADDR']."\n\n"); + $Err = "Token has expired. Please visit ".BOT_DISABLED_CHAN." on ".BOT_SERVER." to discuss this with staff."; + } else { + // Good request, decrement cache value and enable account + G::$Cache->decrement_value(AutoEnable::CACHE_KEY_NAME); + G::$DB->query("UPDATE users_main SET Enabled = '1', can_leech = '1' WHERE ID = '$UserID'"); + G::$DB->query("UPDATE users_info SET BanReason = '0' WHERE UserID = '$UserID'"); + G::$DB->query("SELECT torrent_pass FROM users_main WHERE ID='{$UserID}'"); + list($TorrentPass) = G::$DB->next_record(); + Tracker::update_tracker('add_user', array('id' => $UserID, 'passkey' => $TorrentPass)); + $Err = "Your account has been enabled. You may now log in."; + } + } else { + $Err = "Invalid token."; + } + + return $Err; + } + + /** + * Build the search query, from the searchbox inputs + * + * @param int $UserID The user ID + * @param string $IP The IP + * @param string $SubmittedTimestamp The timestamp representing when the request was submitted + * @param int $HandledUserID The ID of the user that handled the request + * @param string $HandledTimestamp The timestamp representing when the request was handled + * @param int $OutcomeSearch The outcome of the request + * @param boolean $Checked Should checked requests be included? + * @return array The WHERE conditions for the query + */ + public static function build_search_query($Username, $IP, $SubmittedBetween, $SubmittedTimestamp1, $SubmittedTimestamp2, $HandledUsername, $HandledBetween, $HandledTimestamp1, $HandledTimestamp2, $OutcomeSearch, $Checked) { + $Where = []; + + if (!empty($Username)) { + $Where[] = "um1.Username = '$Username'"; + } + + if (!empty($IP)) { + $Where[] = "uer.IP = '$IP'"; + } + + if (!empty($SubmittedTimestamp1)) { + switch($SubmittedBetween) { + case 'on': + $Where[] = "DATE(uer.Timestamp) = DATE('$SubmittedTimestamp1')"; + break; + case 'before': + $Where[] = "DATE(uer.Timestamp) < DATE('$SubmittedTimestamp1')"; + break; + case 'after': + $Where[] = "DATE(uer.Timestamp) > DATE('$SubmittedTimestamp1')"; + break; + case 'between': + if (!empty($SubmittedTimestamp2)) { + $Where[] = "DATE(uer.Timestamp) BETWEEN DATE('$SubmittedTimestamp1') AND DATE('$SubmittedTimestamp2')"; + } + break; + default: + break; + } + } + + if (!empty($HandledTimestamp1)) { + switch($HandledBetween) { + case 'on': + $Where[] = "DATE(uer.HandledTimestamp) = DATE('$HandledTimestamp1')"; + break; + case 'before': + $Where[] = "DATE(uer.HandledTimestamp) < DATE('$HandledTimestamp1')"; + break; + case 'after': + $Where[] = "DATE(uer.HandledTimestamp) > DATE('$HandledTimestamp1')"; + break; + case 'between': + if (!empty($HandledTimestamp2)) { + $Where[] = "DATE(uer.HandledTimestamp) BETWEEN DATE('$HandledTimestamp1') AND DATE('$HandledTimestamp2')"; + } + break; + default: + break; + } + } + + if (!empty($HandledUsername)) { + $Where[] = "um2.Username = '$HandledUsername'"; + } + + if (!empty($OutcomeSearch)) { + $Where[] = "uer.Outcome = '$OutcomeSearch'"; + } + + if ($Checked) { + // This is to skip the if statement in enable_requests.php + $Where[] = "(uer.Outcome IS NULL OR uer.Outcome IS NOT NULL)"; + } + + return $Where; + } } diff --git a/classes/bencode.class.php b/classes/bencode.class.php index 295700964..4a6737b27 100644 --- a/classes/bencode.class.php +++ b/classes/bencode.class.php @@ -1,26 +1,26 @@ -Num = $Val; - } + public function __construct($Val) { + $this->Num = $Val; + } - public static function make($Val) { - return PHP_INT_SIZE === 4 ? new Int64($Val) : (int)$Val; - } + public static function make($Val) { + return PHP_INT_SIZE === 4 ? new Int64($Val) : (int)$Val; + } - public static function get($Val) { - return PHP_INT_SIZE === 4 ? $Val->Num : $Val; - } + public static function get($Val) { + return PHP_INT_SIZE === 4 ? $Val->Num : $Val; + } - public static function is_int($Val) { - return is_int($Val) || (is_object($Val) && get_class($Val) === 'Int64'); - } + public static function is_int($Val) { + return is_int($Val) || (is_object($Val) && get_class($Val) === 'Int64'); + } } /** @@ -28,69 +28,69 @@ public static function is_int($Val) { * note is that empty dictionaries are represented by boolean trues */ class Bencode { - private $DefaultKeys = array( // Get rid of everything except these keys to save some space - 'created by', 'creation date', 'encoding', 'info'); - private $Data; - public $Enc; + private $DefaultKeys = array( // Get rid of everything except these keys to save some space + 'created by', 'creation date', 'encoding', 'info'); + private $Data; + public $Enc; - /** - * Encode an arbitrary array (usually one that's just been decoded) - * - * @param array $Arg the thing to encode - * @param mixed $Keys string or array with keys in the input array to encode or true to encode everything - * @return bencoded string representing the content of the input array - */ - public function encode($Arg = false, $Keys = false) { - if ($Arg === false) { - $Data =& $this->Dec; - } else { - $Data =& $Arg; - } - if ($Keys === true) { - $this->Data = $Data; - } elseif ($Keys === false) { - $this->Data = array_intersect_key($Data, array_flip($this->DefaultKeys)); - } elseif (is_array($Keys)) { - $this->Data = array_intersect_key($Data, array_flip($Keys)); - } else { - $this->Data = isset($Data[$Keys]) ? $Data[$Keys] : false; - } - if (!$this->Data) { - return false; - } - $this->Enc = $this->_benc(); - return $this->Enc; - } + /** + * Encode an arbitrary array (usually one that's just been decoded) + * + * @param array $Arg the thing to encode + * @param mixed $Keys string or array with keys in the input array to encode or true to encode everything + * @return bencoded string representing the content of the input array + */ + public function encode($Arg = false, $Keys = false) { + if ($Arg === false) { + $Data =& $this->Dec; + } else { + $Data =& $Arg; + } + if ($Keys === true) { + $this->Data = $Data; + } elseif ($Keys === false) { + $this->Data = array_intersect_key($Data, array_flip($this->DefaultKeys)); + } elseif (is_array($Keys)) { + $this->Data = array_intersect_key($Data, array_flip($Keys)); + } else { + $this->Data = isset($Data[$Keys]) ? $Data[$Keys] : false; + } + if (!$this->Data) { + return false; + } + $this->Enc = $this->_benc(); + return $this->Enc; + } - /** - * Internal encoding function that does the actual job - * - * @return bencoded string - */ - private function _benc() { - if (!is_array($this->Data)) { - if (Int64::is_int($this->Data)) { // Integer - return 'i'.Int64::get($this->Data).'e'; - } - if ($this->Data === true) { // Empty dictionary - return 'de'; - } - return strlen($this->Data).':'.$this->Data; // String - } - if (empty($this->Data) || Int64::is_int(key($this->Data))) { - $IsDict = false; - } else { - $IsDict = true; - ksort($this->Data); // Dictionaries must be sorted - } - $Ret = $IsDict ? 'd' : 'l'; - foreach ($this->Data as $Key => $Value) { - if ($IsDict) { - $Ret .= strlen($Key).':'.$Key; - } - $this->Data = $Value; - $Ret .= $this->_benc(); - } - return $Ret.'e'; - } + /** + * Internal encoding function that does the actual job + * + * @return bencoded string + */ + private function _benc() { + if (!is_array($this->Data)) { + if (Int64::is_int($this->Data)) { // Integer + return 'i'.Int64::get($this->Data).'e'; + } + if ($this->Data === true) { // Empty dictionary + return 'de'; + } + return strlen($this->Data).':'.$this->Data; // String + } + if (empty($this->Data) || Int64::is_int(key($this->Data))) { + $IsDict = false; + } else { + $IsDict = true; + ksort($this->Data); // Dictionaries must be sorted + } + $Ret = $IsDict ? 'd' : 'l'; + foreach ($this->Data as $Key => $Value) { + if ($IsDict) { + $Ret .= strlen($Key).':'.$Key; + } + $this->Data = $Value; + $Ret .= $this->_benc(); + } + return $Ret.'e'; + } } diff --git a/classes/bencodedecode.class.php b/classes/bencodedecode.class.php index a1bc30464..693f04280 100644 --- a/classes/bencodedecode.class.php +++ b/classes/bencodedecode.class.php @@ -1,184 +1,184 @@ -Enc)) { - return false; - } - } else { - if ($IsPath === true) { - return $this->bdec_file($Arg); - } - $this->Data = $Arg; - } - return $this->decode(); - } + /** + * Decode prepararations + * + * @param string $Arg bencoded string or path to bencoded file to decode + * @param bool $IsPath needs to be true if $Arg is a path + * @return decoded data with a suitable structure + */ + function __construct($Arg = false, $IsPath = false) { + if ($Arg === false) { + if (empty($this->Enc)) { + return false; + } + } else { + if ($IsPath === true) { + return $this->bdec_file($Arg); + } + $this->Data = $Arg; + } + return $this->decode(); + } - /** - * Decodes a bencoded file - * - * @param $Path path to bencoded file to decode - * @return decoded data with a suitable structure - */ - public function bdec_file($Path = false) { - if (empty($Path)) { - return false; - } - if (!$this->Data = @file_get_contents($Path, FILE_BINARY)) { - return $this->error("Error: file '$Path' could not be opened.\n"); - } - return $this->decode(); - } + /** + * Decodes a bencoded file + * + * @param $Path path to bencoded file to decode + * @return decoded data with a suitable structure + */ + public function bdec_file($Path = false) { + if (empty($Path)) { + return false; + } + if (!$this->Data = @file_get_contents($Path, FILE_BINARY)) { + return $this->error("Error: file '$Path' could not be opened.\n"); + } + return $this->decode(); + } - /** - * Decodes a string with bencoded data - * - * @param mixed $Arg bencoded data or false to decode the content of $this->Data - * @return decoded data with a suitable structure - */ - public function decode($Arg = false) { - if ($Arg !== false) { - $this->Data = $Arg; - } elseif (!$this->Data) { - $this->Data = $this->Enc; - } - if (!$this->Data) { - return false; - } - $this->Length = strlen($this->Data); - $this->Pos = 0; - $this->Dec = $this->_bdec(); - if ($this->Pos < $this->Length) { - // Not really necessary, but if the torrent is invalid, it's better to warn than to silently truncate it - return $this->error(); - } - return $this->Dec; - } + /** + * Decodes a string with bencoded data + * + * @param mixed $Arg bencoded data or false to decode the content of $this->Data + * @return decoded data with a suitable structure + */ + public function decode($Arg = false) { + if ($Arg !== false) { + $this->Data = $Arg; + } elseif (!$this->Data) { + $this->Data = $this->Enc; + } + if (!$this->Data) { + return false; + } + $this->Length = strlen($this->Data); + $this->Pos = 0; + $this->Dec = $this->_bdec(); + if ($this->Pos < $this->Length) { + // Not really necessary, but if the torrent is invalid, it's better to warn than to silently truncate it + return $this->error(); + } + return $this->Dec; + } - /** - * Internal decoding function that does the actual job - * - * @return decoded data with a suitable structure - */ - private function _bdec() { - switch ($this->Data[$this->Pos]) { + /** + * Internal decoding function that does the actual job + * + * @return decoded data with a suitable structure + */ + private function _bdec() { + switch ($this->Data[$this->Pos]) { - case 'i': - $this->Pos++; - $Value = substr($this->Data, $this->Pos, strpos($this->Data, 'e', $this->Pos) - $this->Pos); - if (!ctype_digit($Value) && !($Value[0] == '-' && ctype_digit(substr($Value, 1)))) { - return $this->error(); - } - $this->Pos += strlen($Value) + 1; - return Int64::make($Value); + case 'i': + $this->Pos++; + $Value = substr($this->Data, $this->Pos, strpos($this->Data, 'e', $this->Pos) - $this->Pos); + if (!ctype_digit($Value) && !($Value[0] == '-' && ctype_digit(substr($Value, 1)))) { + return $this->error(); + } + $this->Pos += strlen($Value) + 1; + return Int64::make($Value); - case 'l': - $Value = array(); - $this->Pos++; - while ($this->Data[$this->Pos] != 'e') { - if ($this->Pos >= $this->Length) { - return $this->error(); - } - $Value[] = $this->_bdec(); - } - $this->Pos++; - return $Value; + case 'l': + $Value = []; + $this->Pos++; + while ($this->Data[$this->Pos] != 'e') { + if ($this->Pos >= $this->Length) { + return $this->error(); + } + $Value[] = $this->_bdec(); + } + $this->Pos++; + return $Value; - case 'd': - $Value = array(); - $this->Pos++; - while ($this->Data[$this->Pos] != 'e') { - $Length = substr($this->Data, $this->Pos, strpos($this->Data, ':', $this->Pos) - $this->Pos); - if (!ctype_digit($Length)) { - return $this->error(); - } - $this->Pos += strlen($Length) + $Length + 1; - $Key = substr($this->Data, $this->Pos - $Length, $Length); - if ($this->Pos >= $this->Length) { - return $this->error(); - } - $Value[$Key] = $this->_bdec(); - } - $this->Pos++; - // Use boolean true to keep track of empty dictionaries - return empty($Value) ? true : $Value; + case 'd': + $Value = []; + $this->Pos++; + while ($this->Data[$this->Pos] != 'e') { + $Length = substr($this->Data, $this->Pos, strpos($this->Data, ':', $this->Pos) - $this->Pos); + if (!ctype_digit($Length)) { + return $this->error(); + } + $this->Pos += strlen($Length) + $Length + 1; + $Key = substr($this->Data, $this->Pos - $Length, $Length); + if ($this->Pos >= $this->Length) { + return $this->error(); + } + $Value[$Key] = $this->_bdec(); + } + $this->Pos++; + // Use boolean true to keep track of empty dictionaries + return empty($Value) ? true : $Value; - default: - $Length = substr($this->Data, $this->Pos, strpos($this->Data, ':', $this->Pos) - $this->Pos); - if (!ctype_digit($Length)) { - return $this->error(); // Even if the string is likely to be decoded correctly without this check, it's malformed - } - $this->Pos += strlen($Length) + $Length + 1; - return substr($this->Data, $this->Pos - $Length, $Length); - } - } + default: + $Length = substr($this->Data, $this->Pos, strpos($this->Data, ':', $this->Pos) - $this->Pos); + if (!ctype_digit($Length)) { + return $this->error(); // Even if the string is likely to be decoded correctly without this check, it's malformed + } + $this->Pos += strlen($Length) + $Length + 1; + return substr($this->Data, $this->Pos - $Length, $Length); + } + } - /** - * Convert everything to the correct data types and optionally escape strings - * - * @param bool $Escape whether to escape the textual data - * @param mixed $Data decoded data or false to use the $Dec property - * @return decoded data with more useful data types - */ - public function dump($Escape = true, $Data = false) { - if ($Data === false) { - $Data = $this->Dec; - } - if (Int64::is_int($Data)) { - return Int64::get($Data); - } - if (is_bool($Data)) { - return array(); - } - if (is_array($Data)) { - $Output = array(); - foreach ($Data as $Key => $Val) { - $Output[$Key] = $this->dump($Escape, $Val); - } - return $Output; - } - return $Escape ? htmlentities($Data) : $Data; - } + /** + * Convert everything to the correct data types and optionally escape strings + * + * @param bool $Escape whether to escape the textual data + * @param mixed $Data decoded data or false to use the $Dec property + * @return decoded data with more useful data types + */ + public function dump($Escape = true, $Data = false) { + if ($Data === false) { + $Data = $this->Dec; + } + if (Int64::is_int($Data)) { + return Int64::get($Data); + } + if (is_bool($Data)) { + return []; + } + if (is_array($Data)) { + $Output = []; + foreach ($Data as $Key => $Val) { + $Output[$Key] = $this->dump($Escape, $Val); + } + return $Output; + } + return $Escape ? htmlentities($Data) : $Data; + } - /** - * Display an error and halt the operation unless the $ExitOnError property is false - * - * @param string $ErrMsg the error message to display - */ - private function error($ErrMsg = false) { - static $ErrorPos; - if ($this->Pos === $ErrorPos) { - // The recursive nature of the class requires this to avoid duplicate error messages - return false; - } - if ($ErrMsg === false) { - printf("Malformed string. Invalid character at pos 0x%X: %s\n", - $this->Pos, str_replace(array("\r","\n"), array('',' '), htmlentities(substr($this->Data, $this->Pos, self::SnipLength)))); - } else { - echo $ErrMsg; - } - if ($this->ExitOnError) { - exit(); - } - $ErrorPos = $this->Pos; - return false; - } + /** + * Display an error and halt the operation unless the $ExitOnError property is false + * + * @param string $ErrMsg the error message to display + */ + private function error($ErrMsg = false) { + static $ErrorPos; + if ($this->Pos === $ErrorPos) { + // The recursive nature of the class requires this to avoid duplicate error messages + return false; + } + if ($ErrMsg === false) { + printf("Malformed string. Invalid character at pos 0x%X: %s\n", + $this->Pos, str_replace(array("\r","\n"), array('',' '), htmlentities(substr($this->Data, $this->Pos, self::SnipLength)))); + } else { + echo $ErrMsg; + } + if ($this->ExitOnError) { + exit(); + } + $ErrorPos = $this->Pos; + return false; + } } diff --git a/classes/bencodetorrent.class.php b/classes/bencodetorrent.class.php index a6c79e729..441b4b133 100644 --- a/classes/bencodetorrent.class.php +++ b/classes/bencodetorrent.class.php @@ -1,146 +1,163 @@ -Dec)) { - return false; - } - $InfoDict =& $this->Dec['info']; - if (!isset($InfoDict['files'])) { - // Single-file torrent - $this->Size = (Int64::is_int($InfoDict['length']) - ? Int64::get($InfoDict['length']) - : $InfoDict['length']); - $Name = (isset($InfoDict['name.utf-8']) - ? $InfoDict['name.utf-8'] - : $InfoDict['name']); - $this->Files[] = array($this->Size, $Name); - } else { - if (isset($InfoDict['path.utf-8']['files'][0])) { - $this->PathKey = 'path.utf-8'; - } - foreach ($InfoDict['files'] as $File) { - $TmpPath = array(); - foreach ($File[$this->PathKey] as $SubPath) { - $TmpPath[] = $SubPath; - } - $CurSize = (Int64::is_int($File['length']) - ? Int64::get($File['length']) - : $File['length']); - $this->Files[] = array($CurSize, implode('/', $TmpPath)); - $this->Size += $CurSize; - } - uasort($this->Files, function($a, $b) { - return strnatcasecmp($a[1], $b[1]); - }); - } - return array($this->Size, $this->Files); - } + /** + * Create a list of the files in the torrent and their sizes as well as the total torrent size + * + * @return array with a list of files and file sizes + */ + public function file_list() { + if (empty($this->Dec)) { + return false; + } + $InfoDict =& $this->Dec['info']; + if (!isset($InfoDict['files'])) { + // Single-file torrent + $this->Size = (Int64::is_int($InfoDict['length']) + ? Int64::get($InfoDict['length']) + : $InfoDict['length']); + $Name = (isset($InfoDict['name.utf-8']) + ? $InfoDict['name.utf-8'] + : $InfoDict['name']); + $this->Files[] = array($this->Size, $Name); + } else { + if (isset($InfoDict['path.utf-8']['files'][0])) { + $this->PathKey = 'path.utf-8'; + } + foreach ($InfoDict['files'] as $File) { + $TmpPath = []; + foreach ($File[$this->PathKey] as $SubPath) { + $TmpPath[] = $SubPath; + } + $CurSize = (Int64::is_int($File['length']) + ? Int64::get($File['length']) + : $File['length']); + $this->Files[] = array($CurSize, implode('/', $TmpPath)); + $this->Size += $CurSize; + } + uasort($this->Files, function($a, $b) { + return strnatcasecmp($a[1], $b[1]); + }); + } + return array($this->Size, $this->Files); + } - /** - * Find out the name of the torrent - * - * @return string torrent name - */ - public function get_name() { - if (empty($this->Dec)) { - return false; - } - if (isset($this->Dec['info']['name.utf-8'])) { - return $this->Dec['info']['name.utf-8']; - } - return $this->Dec['info']['name']; - } + /** + * Find out the name of the torrent + * + * @return string torrent name + */ + public function get_name() { + if (empty($this->Dec)) { + return false; + } + if (isset($this->Dec['info']['name.utf-8'])) { + return $this->Dec['info']['name.utf-8']; + } + return $this->Dec['info']['name']; + } - /** - * Find out the total size of the torrent - * - * @return string torrent size - */ - public function get_size() { - if (empty($this->Files)) { - if (empty($this->Dec)) { - return false; - } - $FileList = $this->file_list(); - } - return $FileList[0]; - } + /** + * Find out the total size of the torrent + * + * @return string torrent size + */ + public function get_size() { + if (empty($this->Files)) { + if (empty($this->Dec)) { + return false; + } + $FileList = $this->file_list(); + } + return $FileList[0]; + } - /** - * Checks if the "private" flag is present in the torrent - * - * @return true if the "private" flag is set - */ - public function is_private() { - if (empty($this->Dec)) { - return false; - } - return isset($this->Dec['info']['private']) && Int64::get($this->Dec['info']['private']) == 1; - } - /** - * Add the "private" flag to the torrent - * - * @return true if a change was required - */ - public function make_private() { - if (empty($this->Dec)) { - return false; - } - if ($this->is_private()) { - return false; - } - $this->Dec['info']['private'] = Int64::make(1); - ksort($this->Dec['info']); - return true; - } + /** + * Checks if the "private" flag is present in the torrent + * + * @return true if the "private" flag is set + */ + public function is_private() { + if (empty($this->Dec)) { + return false; + } + return isset($this->Dec['info']['private']) && Int64::get($this->Dec['info']['private']) == 1; + } + /** + * Add the "private" flag to the torrent + * + * @return true if a change was required + */ + public function make_private() { + if (empty($this->Dec)) { + return false; + } + if ($this->is_private()) { + return false; + } + $this->Dec['info']['private'] = Int64::make(1); + ksort($this->Dec['info']); + return true; + } - /** - * Adds thet "source" flag to the torrent to allow for easier cross-seeding - * - * @return bool false if the source flag was already set to APL, else true - */ - function set_source() { - if (empty($this->Dec)) { - return false; - } + /** + * Adds thet "source" flag to the torrent to allow for easier cross-seeding + * + * @return bool false if the source flag was already set to OPS, else true + */ + function set_source() { + if (empty($this->Dec)) { + return false; + } - if (isset($this->Dec['info']['source']) && $this->Dec['info']['source'] === 'APL') { - return false; - } + if (isset($this->Dec['info']['source']) && ($this->Dec['info']['source'] === 'OPS')) { + return false; + } - $this->Dec['info']['source'] = 'APL'; - ksort($this->Dec['info']); - return true; - } + if (isset($this->Dec['info']['source']) && ($this->Dec['info']['source'] === 'APL')) { + return false; + } - /** - * Calculate the torrent's info hash - * - * @return info hash in hexadecimal form - */ - public function info_hash() { - if (empty($this->Dec) || !isset($this->Dec['info'])) { - return false; - } - return sha1($this->encode(false, 'info')); - } + // Grandfather APL torrents + if (isset($this->Dec['info']['source']) && isset($this->Dec['creation date']) && + ($this->Dec['creation date'] <= GRANDFATHER_OLD_SOURCE) && + ($this->Dec['info']['source'] === 'APL')) { + return false; + } - /** - * Add the announce URL to a torrent - */ - public static function add_announce_url($Data, $Url) { - return 'd8:announce'.strlen($Url).':'.$Url . substr($Data, 1); - } + // Grandfather torrents before source flag was enforced + if (isset($this->Dec['creation date']) && !isset($this->Dec['info']['source']) && + ($this->Dec['creation date'] <= GRANDFATHER_NO_SOURCE)) { + return false; + } + + $this->Dec['info']['source'] = 'OPS'; + ksort($this->Dec['info']); + return true; + } + + /** + * Calculate the torrent's info hash + * + * @return info hash in hexadecimal form + */ + public function info_hash() { + if (empty($this->Dec) || !isset($this->Dec['info'])) { + return false; + } + return sha1($this->encode(false, 'info')); + } + + /** + * Add the announce URL to a torrent + */ + public static function add_announce_url($Data, $Url) { + return 'd8:announce'.strlen($Url).':'.$Url . substr($Data, 1); + } } diff --git a/classes/bitcoinrpc.class.php b/classes/bitcoinrpc.class.php index 8951fe467..00b796e31 100644 --- a/classes/bitcoinrpc.class.php +++ b/classes/bitcoinrpc.class.php @@ -1,32 +1,32 @@ $Method, - 'params' => $Args, - 'id' => $MessageID) - ); + public static function __callStatic($Method, $Args) { + if (!defined('BITCOIN_RPC_URL')) { + return false; + } + $MessageID = mt_rand(); + $Params = json_encode(array( + 'method' => $Method, + 'params' => $Args, + 'id' => $MessageID) + ); - $Request = array( - 'http' => array( - 'method' => 'POST', - 'header' => 'Content-type: application/json', - 'content' => $Params - ) - ); + $Request = array( + 'http' => array( + 'method' => 'POST', + 'header' => 'Content-type: application/json', + 'content' => $Params + ) + ); - if (!$Response = file_get_contents(BITCOIN_RPC_URL, false, stream_context_create($Request))) { - return false; - } - $Response = json_decode($Response); - if ($Response->id != $MessageID || !empty($Response->error) || empty($Response->result)) { - return false; - } - return $Response->result; - } + if (!$Response = file_get_contents(BITCOIN_RPC_URL, false, stream_context_create($Request))) { + return false; + } + $Response = json_decode($Response); + if ($Response->id != $MessageID || !empty($Response->error) || empty($Response->result)) { + return false; + } + return $Response->result; + } } diff --git a/classes/bookmarks.class.php b/classes/bookmarks.class.php index 6a8c99fb4..e6c331b64 100644 --- a/classes/bookmarks.class.php +++ b/classes/bookmarks.class.php @@ -1,98 +1,98 @@ get_value($CacheKey)) === false) { - list ($Table, $Col) = self::bookmark_schema($Type); - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT $Col - FROM $Table - WHERE UserID = '$UserID'"); - $Bookmarks = G::$DB->collect($Col); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value($CacheKey, $Bookmarks, 0); - } - return $Bookmarks; - } + /** + * Fetch all bookmarks of a certain type for a user. + * If UserID is false than defaults to G::$LoggedUser['ID'] + * + * @param string $Type + * type of bookmarks to fetch + * @param int $UserID + * userid whose bookmarks to get + * @return array the bookmarks + */ + public static function all_bookmarks($Type, $UserID = false) { + if ($UserID === false) { + $UserID = G::$LoggedUser['ID']; + } + $CacheKey = 'bookmarks_' . $Type . '_' . $UserID; + if (($Bookmarks = G::$Cache->get_value($CacheKey)) === false) { + list ($Table, $Col) = self::bookmark_schema($Type); + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT $Col + FROM $Table + WHERE UserID = '$UserID'"); + $Bookmarks = G::$DB->collect($Col); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value($CacheKey, $Bookmarks, 0); + } + return $Bookmarks; + } } diff --git a/classes/cache.class.php b/classes/cache.class.php index c77515949..f98def72a 100644 --- a/classes/cache.class.php +++ b/classes/cache.class.php @@ -1,4 +1,4 @@ -Servers = $Servers; - $ServerList = array(); - foreach ($this->getServerList() as $Server) { - $ServerList["{$Server['host']}:{$Server['port']}"] = true; - } - foreach ($this->Servers as $Server) { - $ServerCheck = isset($ServerList["{$Server['host']}:{$Server['port']}"]); - if ($Server['port'] == 0) { - $ServerCheck = $ServerCheck || isset($ServerList["{$Server['host']}:11211"]); - } - if (!$ServerCheck) { - $Weight = (isset($Server['weight'])) ? $Server['weight'] : 0; - $this->addServer($Server['host'], $Server['port'], $Weight); - } - } - } - - //---------- Caching functions ----------// - - // Allows us to set an expiration on otherwise permanently cached values - // Useful for disabled users, locked threads, basically reducing ram usage - public function expire_value($Key, $Duration = 2592000) { - $StartTime = microtime(true); - $this->set($Key, $this->get($Key), $Duration); - $this->Time += (microtime(true) - $StartTime) * 1000; - } - - // Wrapper for Memcache::set, with the zlib option removed and default duration of 30 days - public function cache_value($Key, $Value, $Duration = 2592000) { - $StartTime = microtime(true); - if (empty($Key)) { - trigger_error("Cache insert failed for empty key"); - } - if (!$this->set($Key, $Value, $Duration)) { - trigger_error("Cache insert failed for key $Key:" . $this->getResultMessage()); - } - if ($this->InternalCache && array_key_exists($Key, $this->CacheHits)) { - $this->CacheHits[$Key] = $Value; - } - $this->Time += (microtime(true) - $StartTime) * 1000; - } - - // Wrapper for Memcache::add, with the zlib option removed and default duration of 30 days - public function add_value($Key, $Value, $Duration = 2592000) { - $StartTime = microtime(true); - $Added = $this->add($Key, $Value, $Duration); - $this->Time += (microtime(true) - $StartTime) * 1000; - return $Added; - } - - public function replace_value($Key, $Value, $Duration = 2592000) { - $StartTime = microtime(true); - $this->replace($Key, $Value, $Duration); - if ($this->InternalCache && array_key_exists($Key, $this->CacheHits)) { - $this->CacheHits[$Key] = $Value; - } - $this->Time += (microtime(true) - $StartTime) * 1000; - } - - public function get_value($Key, $NoCache = false) { - if (!$this->InternalCache) { - $NoCache = true; - } - $StartTime = microtime(true); - if (empty($Key)) { - trigger_error('Cache retrieval failed for empty key'); - } - - if (!empty($_GET['clearcache']) && $this->CanClear && !isset($this->ClearedKeys[$Key]) && !Misc::in_array_partial($Key, $this->PersistentKeys)) { - if ($_GET['clearcache'] === '1') { - // Because check_perms() isn't true until LoggedUser is pulled from the cache, we have to remove the entries loaded before the LoggedUser data - // Because of this, not user cache data will require a secondary pageload following the clearcache to update - if (count($this->CacheHits) > 0) { - foreach (array_keys($this->CacheHits) as $HitKey) { - if (!isset($this->ClearedKeys[$HitKey]) && !Misc::in_array_partial($HitKey, $this->PersistentKeys)) { - $this->delete($HitKey); - unset($this->CacheHits[$HitKey]); - $this->ClearedKeys[$HitKey] = true; - } - } - } - $this->delete($Key); - $this->Time += (microtime(true) - $StartTime) * 1000; - return false; - } elseif ($_GET['clearcache'] == $Key) { - $this->delete($Key); - $this->Time += (microtime(true) - $StartTime) * 1000; - return false; - } elseif (substr($_GET['clearcache'], -1) === '*') { - $Prefix = substr($_GET['clearcache'], 0, -1); - if ($Prefix === '' || $Prefix === substr($Key, 0, strlen($Prefix))) { - $this->delete($Key); - $this->Time += (microtime(true) - $StartTime) * 1000; - return false; - } - } - $this->ClearedKeys[$Key] = true; - } - - // For cases like the forums, if a key is already loaded, grab the existing pointer - if (isset($this->CacheHits[$Key]) && !$NoCache) { - $this->Time += (microtime(true) - $StartTime) * 1000; - return $this->CacheHits[$Key]; - } - - $Return = $this->get($Key); - if ($Return !== false) { - $this->CacheHits[$Key] = $NoCache ? null : $Return; - } - $this->Time += (microtime(true) - $StartTime) * 1000; - return $Return; - } - - // Wrapper for Memcache::delete. For a reason, see above. - public function delete_value($Key) { - $StartTime = microtime(true); - if (empty($Key)) { - trigger_error('Cache deletion failed for empty key'); - } - $this->delete($Key); - unset($this->CacheHits[$Key]); - $this->Time += (microtime(true) - $StartTime) * 1000; - } - - public function increment_value($Key, $Value = 1) { - $StartTime = microtime(true); - $NewVal = $this->increment($Key, $Value); - if (isset($this->CacheHits[$Key])) { - $this->CacheHits[$Key] = $NewVal; - } - $this->Time += (microtime(true) - $StartTime) * 1000; - } - - public function decrement_value($Key, $Value = 1) { - $StartTime = microtime(true); - $NewVal = $this->decrement($Key, $Value); - if (isset($this->CacheHits[$Key])) { - $this->CacheHits[$Key] = $NewVal; - } - $this->Time += (microtime(true) - $StartTime) * 1000; - } - - //---------- memcachedb functions ----------// - - public function begin_transaction($Key) { - $Value = $this->get($Key); - if (!is_array($Value)) { - $this->InTransaction = false; - $this->MemcacheDBKey = array(); - $this->MemcacheDBKey = ''; - return false; - } - $this->MemcacheDBArray = $Value; - $this->MemcacheDBKey = $Key; - $this->InTransaction = true; - return true; - } - - public function cancel_transaction() { - $this->InTransaction = false; - $this->MemcacheDBKey = array(); - $this->MemcacheDBKey = ''; - } - - public function commit_transaction($Time = 2592000) { - if (!$this->InTransaction) { - return false; - } - $this->cache_value($this->MemcacheDBKey, $this->MemcacheDBArray, $Time); - $this->InTransaction = false; - } - - // Updates multiple rows in an array - public function update_transaction($Rows, $Values) { - if (!$this->InTransaction) { - return false; - } - $Array = $this->MemcacheDBArray; - if (is_array($Rows)) { - $i = 0; - $Keys = $Rows[0]; - $Property = $Rows[1]; - foreach ($Keys as $Row) { - $Array[$Row][$Property] = $Values[$i]; - $i++; - } - } else { - $Array[$Rows] = $Values; - } - $this->MemcacheDBArray = $Array; - } - - // Updates multiple values in a single row in an array - // $Values must be an associative array with key:value pairs like in the array we're updating - public function update_row($Row, $Values) { - if (!$this->InTransaction) { - return false; - } - if ($Row === false) { - $UpdateArray = $this->MemcacheDBArray; - } else { - $UpdateArray = $this->MemcacheDBArray[$Row]; - } - foreach ($Values as $Key => $Value) { - if (!array_key_exists($Key, $UpdateArray)) { - trigger_error('Bad transaction key ('.$Key.') for cache '.$this->MemcacheDBKey); - } - if ($Value === '+1') { - if (!is_number($UpdateArray[$Key])) { - trigger_error('Tried to increment non-number ('.$Key.') for cache '.$this->MemcacheDBKey); - } - ++$UpdateArray[$Key]; // Increment value - } elseif ($Value === '-1') { - if (!is_number($UpdateArray[$Key])) { - trigger_error('Tried to decrement non-number ('.$Key.') for cache '.$this->MemcacheDBKey); - } - --$UpdateArray[$Key]; // Decrement value - } else { - $UpdateArray[$Key] = $Value; // Otherwise, just alter value - } - } - if ($Row === false) { - $this->MemcacheDBArray = $UpdateArray; - } else { - $this->MemcacheDBArray[$Row] = $UpdateArray; - } - } - - // Increments multiple values in a single row in an array - // $Values must be an associative array with key:value pairs like in the array we're updating - public function increment_row($Row, $Values) { - if (!$this->InTransaction) { - return false; - } - if ($Row === false) { - $UpdateArray = $this->MemcacheDBArray; - } else { - $UpdateArray = $this->MemcacheDBArray[$Row]; - } - foreach ($Values as $Key => $Value) { - if (!array_key_exists($Key, $UpdateArray)) { - trigger_error("Bad transaction key ($Key) for cache ".$this->MemcacheDBKey); - } - if (!is_number($Value)) { - trigger_error("Tried to increment with non-number ($Key) for cache ".$this->MemcacheDBKey); - } - $UpdateArray[$Key] += $Value; // Increment value - } - if ($Row === false) { - $this->MemcacheDBArray = $UpdateArray; - } else { - $this->MemcacheDBArray[$Row] = $UpdateArray; - } - } - - // Insert a value at the beginning of the array - public function insert_front($Key, $Value) { - if (!$this->InTransaction) { - return false; - } - if ($Key === '') { - array_unshift($this->MemcacheDBArray, $Value); - } else { - $this->MemcacheDBArray = array($Key=>$Value) + $this->MemcacheDBArray; - } - } - - // Insert a value at the end of the array - public function insert_back($Key, $Value) { - if (!$this->InTransaction) { - return false; - } - if ($Key === '') { - array_push($this->MemcacheDBArray, $Value); - } else { - $this->MemcacheDBArray = $this->MemcacheDBArray + array($Key=>$Value); - } - - } - - public function insert($Key, $Value) { - if (!$this->InTransaction) { - return false; - } - if ($Key === '') { - $this->MemcacheDBArray[] = $Value; - } else { - $this->MemcacheDBArray[$Key] = $Value; - } - } - - public function delete_row($Row) { - if (!$this->InTransaction) { - return false; - } - if (!isset($this->MemcacheDBArray[$Row])) { - trigger_error("Tried to delete non-existent row ($Row) for cache ".$this->MemcacheDBKey); - } - unset($this->MemcacheDBArray[$Row]); - } - - public function update($Key, $Rows, $Values, $Time = 2592000) { - if (!$this->InTransaction) { - $this->begin_transaction($Key); - $this->update_transaction($Rows, $Values); - $this->commit_transaction($Time); - } else { - $this->update_transaction($Rows, $Values); - } - } - - /** - * Tries to set a lock. Expiry time is one hour to avoid indefinite locks - * - * @param string $LockName name on the lock - * @return true if lock was acquired - */ - public function get_query_lock($LockName) { - return $this->add_value('query_lock_'.$LockName, 1, 3600); - } - - /** - * Remove lock - * - * @param string $LockName name on the lock - */ - public function clear_query_lock($LockName) { - $this->delete_value('query_lock_'.$LockName); - } - - /** - * Get cache server status - * - * @return array (host => bool status, ...) - */ - public function server_status() { - /*$Status = array(); - foreach ($this->Servers as $Server) { - $Status["$Server[host]:$Server[port]"] = $this->getServerStatus($Server['host'], $Server['port']); - }*/ - return $this->getStats(); - } + /** + * Torrent Group cache version + */ + const GROUP_VERSION = 5; + + public $CacheHits = []; + public $MemcacheDBArray = []; + public $MemcacheDBKey = ''; + protected $InTransaction = false; + public $Time = 0; + private $Servers = []; + private $PersistentKeys = array( + 'ajax_requests_*', + 'query_lock_*', + 'stats_*', + 'top10tor_*', + 'top10votes_*', + 'users_snatched_*', + + // Cache-based features + 'global_notification', + 'notifications_one_reads_*', + ); + private $ClearedKeys = []; + + public $CanClear = false; + public $InternalCache = true; + + /** + * CACHE constructor. Takes a array of $Servers with a host, port, and optionally a weight. + * We then add each of the servers in the array to our memcached pool assuming we haven't + * already connected to it before (cross-checking against the pool's server list). If you want + * to connect to a socket, you need to use port 0, though internally in the pool it'll have + * port 11211, so if using a server with port 0, we also need to check for port 11211 in + * the $ServerList as Memcached really doesn't like the same server being added hundreds of time + * with the same weight. + * + * @see Memcached::getServerList() + * + * @param $Servers + * @param string $PersistantID + */ + function __construct($Servers, $PersistantID = 'apl') { + parent::__construct($PersistantID); + $this->Servers = $Servers; + $ServerList = []; + foreach ($this->getServerList() as $Server) { + $ServerList["{$Server['host']}:{$Server['port']}"] = true; + } + foreach ($this->Servers as $Server) { + $ServerCheck = isset($ServerList["{$Server['host']}:{$Server['port']}"]); + if ($Server['port'] == 0) { + $ServerCheck = $ServerCheck || isset($ServerList["{$Server['host']}:11211"]); + } + if (!$ServerCheck) { + $Weight = (isset($Server['weight'])) ? $Server['weight'] : 0; + $this->addServer($Server['host'], $Server['port'], $Weight); + } + } + } + + //---------- Caching functions ----------// + + // Allows us to set an expiration on otherwise permanently cached values + // Useful for disabled users, locked threads, basically reducing ram usage + public function expire_value($Key, $Duration = 2592000) { + $StartTime = microtime(true); + $this->set($Key, $this->get($Key), $Duration); + $this->Time += (microtime(true) - $StartTime) * 1000; + } + + // Wrapper for Memcache::set, with the zlib option removed and default duration of 30 days + public function cache_value($Key, $Value, $Duration = 2592000) { + $StartTime = microtime(true); + if (empty($Key)) { + trigger_error("Cache insert failed for empty key"); + } + if (!$this->set($Key, $Value, $Duration)) { + trigger_error("Cache insert failed for key $Key:" . $this->getResultMessage()); + } + if ($this->InternalCache && array_key_exists($Key, $this->CacheHits)) { + $this->CacheHits[$Key] = $Value; + } + $this->Time += (microtime(true) - $StartTime) * 1000; + } + + // Wrapper for Memcache::add, with the zlib option removed and default duration of 30 days + public function add_value($Key, $Value, $Duration = 2592000) { + $StartTime = microtime(true); + $Added = $this->add($Key, $Value, $Duration); + $this->Time += (microtime(true) - $StartTime) * 1000; + return $Added; + } + + public function replace_value($Key, $Value, $Duration = 2592000) { + $StartTime = microtime(true); + $this->replace($Key, $Value, $Duration); + if ($this->InternalCache && array_key_exists($Key, $this->CacheHits)) { + $this->CacheHits[$Key] = $Value; + } + $this->Time += (microtime(true) - $StartTime) * 1000; + } + + public function get_value($Key, $NoCache = false) { + if (!$this->InternalCache) { + $NoCache = true; + } + $StartTime = microtime(true); + if (empty($Key)) { + trigger_error('Cache retrieval failed for empty key'); + } + + if (!empty($_GET['clearcache']) && $this->CanClear && !isset($this->ClearedKeys[$Key]) && !Misc::in_array_partial($Key, $this->PersistentKeys)) { + if ($_GET['clearcache'] === '1') { + // Because check_perms() isn't true until LoggedUser is pulled from the cache, we have to remove the entries loaded before the LoggedUser data + // Because of this, not user cache data will require a secondary pageload following the clearcache to update + if (count($this->CacheHits) > 0) { + foreach (array_keys($this->CacheHits) as $HitKey) { + if (!isset($this->ClearedKeys[$HitKey]) && !Misc::in_array_partial($HitKey, $this->PersistentKeys)) { + $this->delete($HitKey); + unset($this->CacheHits[$HitKey]); + $this->ClearedKeys[$HitKey] = true; + } + } + } + $this->delete($Key); + $this->Time += (microtime(true) - $StartTime) * 1000; + return false; + } elseif ($_GET['clearcache'] == $Key) { + $this->delete($Key); + $this->Time += (microtime(true) - $StartTime) * 1000; + return false; + } elseif (substr($_GET['clearcache'], -1) === '*') { + $Prefix = substr($_GET['clearcache'], 0, -1); + if ($Prefix === '' || $Prefix === substr($Key, 0, strlen($Prefix))) { + $this->delete($Key); + $this->Time += (microtime(true) - $StartTime) * 1000; + return false; + } + } + $this->ClearedKeys[$Key] = true; + } + + // For cases like the forums, if a key is already loaded, grab the existing pointer + if (isset($this->CacheHits[$Key]) && !$NoCache) { + $this->Time += (microtime(true) - $StartTime) * 1000; + return $this->CacheHits[$Key]; + } + + $Return = $this->get($Key); + if ($Return !== false) { + $this->CacheHits[$Key] = $NoCache ? null : $Return; + } + $this->Time += (microtime(true) - $StartTime) * 1000; + return $Return; + } + + // Wrapper for Memcache::delete. For a reason, see above. + public function delete_value($Key) { + $StartTime = microtime(true); + if (empty($Key)) { + trigger_error('Cache deletion failed for empty key'); + } + $this->delete($Key); + unset($this->CacheHits[$Key]); + $this->Time += (microtime(true) - $StartTime) * 1000; + } + + public function increment_value($Key, $Value = 1) { + $StartTime = microtime(true); + $NewVal = $this->increment($Key, $Value); + if (isset($this->CacheHits[$Key])) { + $this->CacheHits[$Key] = $NewVal; + } + $this->Time += (microtime(true) - $StartTime) * 1000; + } + + public function decrement_value($Key, $Value = 1) { + $StartTime = microtime(true); + $NewVal = $this->decrement($Key, $Value); + if (isset($this->CacheHits[$Key])) { + $this->CacheHits[$Key] = $NewVal; + } + $this->Time += (microtime(true) - $StartTime) * 1000; + } + + //---------- memcachedb functions ----------// + + public function begin_transaction($Key) { + $Value = $this->get($Key); + if (!is_array($Value)) { + $this->InTransaction = false; + $this->MemcacheDBKey = []; + $this->MemcacheDBKey = ''; + return false; + } + $this->MemcacheDBArray = $Value; + $this->MemcacheDBKey = $Key; + $this->InTransaction = true; + return true; + } + + public function cancel_transaction() { + $this->InTransaction = false; + $this->MemcacheDBKey = []; + $this->MemcacheDBKey = ''; + } + + public function commit_transaction($Time = 2592000) { + if (!$this->InTransaction) { + return false; + } + $this->cache_value($this->MemcacheDBKey, $this->MemcacheDBArray, $Time); + $this->InTransaction = false; + } + + // Updates multiple rows in an array + public function update_transaction($Rows, $Values) { + if (!$this->InTransaction) { + return false; + } + $Array = $this->MemcacheDBArray; + if (is_array($Rows)) { + $i = 0; + $Keys = $Rows[0]; + $Property = $Rows[1]; + foreach ($Keys as $Row) { + $Array[$Row][$Property] = $Values[$i]; + $i++; + } + } else { + $Array[$Rows] = $Values; + } + $this->MemcacheDBArray = $Array; + } + + // Updates multiple values in a single row in an array + // $Values must be an associative array with key:value pairs like in the array we're updating + public function update_row($Row, $Values) { + if (!$this->InTransaction) { + return false; + } + if ($Row === false) { + $UpdateArray = $this->MemcacheDBArray; + } else { + $UpdateArray = $this->MemcacheDBArray[$Row]; + } + foreach ($Values as $Key => $Value) { + if (!array_key_exists($Key, $UpdateArray)) { + trigger_error('Bad transaction key ('.$Key.') for cache '.$this->MemcacheDBKey); + } + if ($Value === '+1') { + if (!is_number($UpdateArray[$Key])) { + trigger_error('Tried to increment non-number ('.$Key.') for cache '.$this->MemcacheDBKey); + } + ++$UpdateArray[$Key]; // Increment value + } elseif ($Value === '-1') { + if (!is_number($UpdateArray[$Key])) { + trigger_error('Tried to decrement non-number ('.$Key.') for cache '.$this->MemcacheDBKey); + } + --$UpdateArray[$Key]; // Decrement value + } else { + $UpdateArray[$Key] = $Value; // Otherwise, just alter value + } + } + if ($Row === false) { + $this->MemcacheDBArray = $UpdateArray; + } else { + $this->MemcacheDBArray[$Row] = $UpdateArray; + } + } + + // Increments multiple values in a single row in an array + // $Values must be an associative array with key:value pairs like in the array we're updating + public function increment_row($Row, $Values) { + if (!$this->InTransaction) { + return false; + } + if ($Row === false) { + $UpdateArray = $this->MemcacheDBArray; + } else { + $UpdateArray = $this->MemcacheDBArray[$Row]; + } + foreach ($Values as $Key => $Value) { + if (!array_key_exists($Key, $UpdateArray)) { + trigger_error("Bad transaction key ($Key) for cache ".$this->MemcacheDBKey); + } + if (!is_number($Value)) { + trigger_error("Tried to increment with non-number ($Key) for cache ".$this->MemcacheDBKey); + } + $UpdateArray[$Key] += $Value; // Increment value + } + if ($Row === false) { + $this->MemcacheDBArray = $UpdateArray; + } else { + $this->MemcacheDBArray[$Row] = $UpdateArray; + } + } + + // Insert a value at the beginning of the array + public function insert_front($Key, $Value) { + if (!$this->InTransaction) { + return false; + } + if ($Key === '') { + array_unshift($this->MemcacheDBArray, $Value); + } else { + $this->MemcacheDBArray = array($Key=>$Value) + $this->MemcacheDBArray; + } + } + + // Insert a value at the end of the array + public function insert_back($Key, $Value) { + if (!$this->InTransaction) { + return false; + } + if ($Key === '') { + array_push($this->MemcacheDBArray, $Value); + } else { + $this->MemcacheDBArray = $this->MemcacheDBArray + array($Key=>$Value); + } + + } + + public function insert($Key, $Value) { + if (!$this->InTransaction) { + return false; + } + if ($Key === '') { + $this->MemcacheDBArray[] = $Value; + } else { + $this->MemcacheDBArray[$Key] = $Value; + } + } + + public function delete_row($Row) { + if (!$this->InTransaction) { + return false; + } + if (!isset($this->MemcacheDBArray[$Row])) { + trigger_error("Tried to delete non-existent row ($Row) for cache ".$this->MemcacheDBKey); + } + unset($this->MemcacheDBArray[$Row]); + } + + public function update($Key, $Rows, $Values, $Time = 2592000) { + if (!$this->InTransaction) { + $this->begin_transaction($Key); + $this->update_transaction($Rows, $Values); + $this->commit_transaction($Time); + } else { + $this->update_transaction($Rows, $Values); + } + } + + /** + * Tries to set a lock. Expiry time is one hour to avoid indefinite locks + * + * @param string $LockName name on the lock + * @return true if lock was acquired + */ + public function get_query_lock($LockName) { + return $this->add_value('query_lock_'.$LockName, 1, 3600); + } + + /** + * Remove lock + * + * @param string $LockName name on the lock + */ + public function clear_query_lock($LockName) { + $this->delete_value('query_lock_'.$LockName); + } + + /** + * Get cache server status + * + * @return array (host => bool status, ...) + */ + public function server_status() { + /*$Status = []; + foreach ($this->Servers as $Server) { + $Status["$Server[host]:$Server[port]"] = $this->getServerStatus($Server['host'], $Server['port']); + }*/ + return $this->getStats(); + } } diff --git a/classes/calendar.class.php b/classes/calendar.class.php index 853f812ad..70d8b27a5 100644 --- a/classes/calendar.class.php +++ b/classes/calendar.class.php @@ -1,138 +1,138 @@ - "IRC Meeting", "IRC Brainstorm", "Poll Deadline", "Feature Release", "Blog Post", "Announcement", "Featured Album", "Product Release", "Staff Picks", "Forum Brainstorm", "Forum Discussion", "Promotion", "Absence", "Task"); - public static $Importances = array(1 => "Critical", "Important", "Average", "Meh"); - public static $Colors = array( - "Critical" => "red", - "Important" => "yellow", - "Average" => "green", - "Meh" => "blue"); + public static $Categories = array(1 => "IRC Meeting", "IRC Brainstorm", "Poll Deadline", "Feature Release", "Blog Post", "Announcement", "Featured Album", "Product Release", "Staff Picks", "Forum Brainstorm", "Forum Discussion", "Promotion", "Absence", "Task"); + public static $Importances = array(1 => "Critical", "Important", "Average", "Meh"); + public static $Colors = array( + "Critical" => "red", + "Important" => "yellow", + "Average" => "green", + "Meh" => "blue"); - public static $Teams = array( - 0 => "Everyone", - 1 => "Staff" + public static $Teams = array( + 0 => "Everyone", + 1 => "Staff" - ); + ); - public static function can_view() { - return check_perms('users_mod'); - } + public static function can_view() { + return check_perms('users_mod'); + } - private static function get_teams_query() { - $Teams = array(0); - $IsMod = check_perms("users_mod"); - if ($IsMod) { - $Teams[] = 1; - } + private static function get_teams_query() { + $Teams = array(0); + $IsMod = check_perms("users_mod"); + if ($IsMod) { + $Teams[] = 1; + } - return "Team IN (" . implode(",", $Teams) . ") "; - } + return "Team IN (" . implode(",", $Teams) . ") "; + } - public static function get_events($Month, $Year) { - if (empty($Month) || empty($Year)) { - $Date = getdate(); - $Month = $Date['mon']; - $Year = $Date['year']; - } - $Month = (int)$Month; - $Year = (int)$Year; + public static function get_events($Month, $Year) { + if (empty($Month) || empty($Year)) { + $Date = getdate(); + $Month = $Date['mon']; + $Year = $Date['year']; + } + $Month = (int)$Month; + $Year = (int)$Year; - $TeamsSQL = self::get_teams_query(); + $TeamsSQL = self::get_teams_query(); - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - ID, Team, Title, Category, Importance, DAY(StartDate) AS StartDay, DAY(EndDate) AS EndDay - FROM calendar - WHERE - MONTH(StartDate) = '$Month' - AND - YEAR(StartDate) = '$Year' - AND - $TeamsSQL"); - $Events = G::$DB->to_array(); - G::$DB->set_query_id($QueryID); - return $Events; - } + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + ID, Team, Title, Category, Importance, DAY(StartDate) AS StartDay, DAY(EndDate) AS EndDay + FROM calendar + WHERE + MONTH(StartDate) = '$Month' + AND + YEAR(StartDate) = '$Year' + AND + $TeamsSQL"); + $Events = G::$DB->to_array(); + G::$DB->set_query_id($QueryID); + return $Events; + } - public static function get_event($ID) { - $ID = (int)$ID; - if (empty($ID)) { - error("Invalid ID"); - } - $TeamsSQL = self::get_teams_query(); - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - ID, Team, Title, Body, Category, Importance, AddedBy, StartDate, EndDate - FROM calendar - WHERE - ID = '$ID' - AND - $TeamsSQL"); - $Event = G::$DB->next_record(MYSQLI_ASSOC); - G::$DB->set_query_id($QueryID); - return $Event; - } + public static function get_event($ID) { + $ID = (int)$ID; + if (empty($ID)) { + error("Invalid ID"); + } + $TeamsSQL = self::get_teams_query(); + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + ID, Team, Title, Body, Category, Importance, AddedBy, StartDate, EndDate + FROM calendar + WHERE + ID = '$ID' + AND + $TeamsSQL"); + $Event = G::$DB->next_record(MYSQLI_ASSOC); + G::$DB->set_query_id($QueryID); + return $Event; + } - public static function create_event($Title, $Body, $Category, $Importance, $Team, $UserID, $StartDate, $EndDate = null) { - if (empty($Title) || empty($Body) || !is_number($Category) || !is_number($Importance) || !is_number($Team) || empty($StartDate)) { - error("Error adding event"); - } - $Title = db_string($Title); - $Body = db_string($Body); - $Category = (int)$Category; - $Importance = (int)$Importance; - $UserID = (int)$UserID; - $Team = (int)$Team; - $StartDate = db_string($StartDate); - $EndDate = db_string($EndDate); + public static function create_event($Title, $Body, $Category, $Importance, $Team, $UserID, $StartDate, $EndDate = null) { + if (empty($Title) || empty($Body) || !is_number($Category) || !is_number($Importance) || !is_number($Team) || empty($StartDate)) { + error("Error adding event"); + } + $Title = db_string($Title); + $Body = db_string($Body); + $Category = (int)$Category; + $Importance = (int)$Importance; + $UserID = (int)$UserID; + $Team = (int)$Team; + $StartDate = db_string($StartDate); + $EndDate = db_string($EndDate); - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - INSERT INTO calendar - (Title, Body, Category, Importance, Team, StartDate, EndDate, AddedBy) - VALUES - ('$Title', '$Body', '$Category', '$Importance', '$Team', '$StartDate', '$EndDate', '$UserID')"); - G::$DB->set_query_id($QueryID); - send_irc("PRIVMSG " . ADMIN_CHAN . " :!mod New calendar event created! Event: $Title; Starts: $StartDate; Ends: $EndDate."); - } + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + INSERT INTO calendar + (Title, Body, Category, Importance, Team, StartDate, EndDate, AddedBy) + VALUES + ('$Title', '$Body', '$Category', '$Importance', '$Team', '$StartDate', '$EndDate', '$UserID')"); + G::$DB->set_query_id($QueryID); + send_irc("PRIVMSG " . MOD_CHAN . " :!mod New calendar event created! Event: $Title; Starts: $StartDate; Ends: $EndDate."); + } - public static function update_event($ID, $Title, $Body, $Category, $Importance, $Team, $StartDate, $EndDate = null) { - if (!is_number($ID) || empty($Title) || empty($Body) || !is_number($Category) || !is_number($Importance) || !is_number($Team) || empty($StartDate)) { - error("Error updating event"); - } - $ID = (int)$ID; - $Title = db_string($Title); - $Body = db_string($Body); - $Category = (int)$Category; - $Importance = (int)$Importance; - $Team = (int)$Team; - $StartDate = db_string($StartDate); - $EndDate = db_string($EndDate); - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - UPDATE calendar - SET - Title = '$Title', - Body = '$Body', - Category = '$Category', - Importance = '$Importance', - Team = '$Team', - StartDate = '$StartDate', - EndDate = '$EndDate' - WHERE - ID = '$ID'"); - G::$DB->set_query_id($QueryID); - } + public static function update_event($ID, $Title, $Body, $Category, $Importance, $Team, $StartDate, $EndDate = null) { + if (!is_number($ID) || empty($Title) || empty($Body) || !is_number($Category) || !is_number($Importance) || !is_number($Team) || empty($StartDate)) { + error("Error updating event"); + } + $ID = (int)$ID; + $Title = db_string($Title); + $Body = db_string($Body); + $Category = (int)$Category; + $Importance = (int)$Importance; + $Team = (int)$Team; + $StartDate = db_string($StartDate); + $EndDate = db_string($EndDate); + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + UPDATE calendar + SET + Title = '$Title', + Body = '$Body', + Category = '$Category', + Importance = '$Importance', + Team = '$Team', + StartDate = '$StartDate', + EndDate = '$EndDate' + WHERE + ID = '$ID'"); + G::$DB->set_query_id($QueryID); + } - public static function remove_event($ID) { - $ID = (int)$ID; - if (!empty($ID)) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query("DELETE FROM calendar WHERE ID = '$ID'"); - G::$DB->set_query_id($QueryID); - } - } + public static function remove_event($ID) { + $ID = (int)$ID; + if (!empty($ID)) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query("DELETE FROM calendar WHERE ID = '$ID'"); + G::$DB->set_query_id($QueryID); + } + } } diff --git a/classes/calendarview.class.php b/classes/calendarview.class.php index ecce5a4b8..2fba1d1d0 100644 --- a/classes/calendarview.class.php +++ b/classes/calendarview.class.php @@ -1,120 +1,120 @@ - -

- < - - > -

- - - + < + + > + + + += $Day)) { - $Results[] = $Event; - } - } - return $Results; - } + private static function get_events_on($Day, $Events) { + // Linear search, Lol. + $Results = []; + foreach ($Events as $Event) { + if ($Event['StartDay'] == $Day || ($Event['StartDay'] <= $Day && $Event['EndDay'] >= $Day)) { + $Results[] = $Event; + } + } + return $Results; + } - private static function render_events_day($Day, $Events) { - $Events = self::get_events_on($Day, $Events); - foreach ($Events as $Event) { - $Color = Calendar::$Colors[Calendar::$Importances[$Event['Importance']]]; - $Category = Calendar::$Categories[$Event['Category']]; - $Tooltip = $Event['Title'] . " - " . Calendar::$Categories[$Event['Category']] . " - " . Calendar::$Importances[$Event['Importance']]; + private static function render_events_day($Day, $Events) { + $Events = self::get_events_on($Day, $Events); + foreach ($Events as $Event) { + $Color = Calendar::$Colors[Calendar::$Importances[$Event['Importance']]]; + $Category = Calendar::$Categories[$Event['Category']]; + $Tooltip = $Event['Title'] . " - " . Calendar::$Categories[$Event['Category']] . " - " . Calendar::$Importances[$Event['Importance']]; ?> -

-

+ - - - - - - - +
- -
+ + + + + + - - - + + - - - - - - +
+ +
+
+ +
+ + + + + + - - + - + -
+ +
-
- -
-
- -
-
- + 300000 || $Height > 1000 || $Width > 1000) { - trigger_error('Tried to make chart too large.'); - } - $this->URL .= "?cht=$Type&chs={$Width}x$Height"; - $this->Options = $Options; - } - - protected function encode($Number) { - if ($Number == -1) { - return '__'; - } - $CharKey = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.'; - return $CharKey[floor($Number / 64)].$CharKey[floor($Number % 64)]; - } - - public function color($Colors) { - $this->URL .= '&chco='.$Colors; - } - - public function lines($Thickness, $Solid = 1, $Blank = 0) { - $this->URL .= "&chls=$Thickness,$Solid,$Blank"; - } - - public function title($Title, $Color = '', $Size = '') { - $this->URL .= '&chtt='.str_replace(array(' ', "\n"), array('+', '|'), $Title); - if (!empty($Color)) { - $this->URL .= '&chts='.$Color; - } - if (!empty($Size)) { - $this->URL .= ','.$Size; - } - } - - public function legend($Items, $Placement = '') { - $this->URL .= '&chdl='.str_replace(' ', '+', implode('|', $Items)); - if (!empty($Placement)) { - if (!in_array($Placement, array('b', 't', 'r', 'l', 'bv', 'tv'))) { - trigger_error('Invalid legend placement.'); - } - $this->URL .= '&chdlp='.$Placement; - } - } - - public function add($Label, $Data) { - if ($Label !== false) { - $this->Labels[] = $Label; - } - $this->Data[] = $Data; - } - - public function grid_lines($SpacingX = 0, $SpacingY = -1, $Solid = 1, $Blank = 1) { - //Can take 2 more parameters for offset, but we're not bothering with that right now - $this->URL .= "&chg=$SpacingX,$SpacingY,$Solid,$Blank"; - } - - public function transparent() { - $this->URL .= '&chf=bg,s,FFFFFF00'; - } - - - public function url() { - return $this->URL; - } + protected $URL = 'https://chart.googleapis.com/chart'; + protected $Labels = array(); + protected $Data = array(); + protected $Options = array(); + + public function __construct($Type, $Width, $Height, $Options) { + if ($Width * $Height > 300000 || $Height > 1000 || $Width > 1000) { + trigger_error('Tried to make chart too large.'); + } + $this->URL .= "?cht=$Type&chs={$Width}x$Height"; + $this->Options = $Options; + } + + protected function encode($Number) { + if ($Number == -1) { + return '__'; + } + $CharKey = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.'; + return $CharKey[floor($Number / 64)].$CharKey[floor($Number % 64)]; + } + + public function color($Colors) { + $this->URL .= '&chco='.$Colors; + } + + public function lines($Thickness, $Solid = 1, $Blank = 0) { + $this->URL .= "&chls=$Thickness,$Solid,$Blank"; + } + + public function title($Title, $Color = '', $Size = '') { + $this->URL .= '&chtt='.str_replace(array(' ', "\n"), array('+', '|'), $Title); + if (!empty($Color)) { + $this->URL .= '&chts='.$Color; + } + if (!empty($Size)) { + $this->URL .= ','.$Size; + } + } + + public function legend($Items, $Placement = '') { + $this->URL .= '&chdl='.str_replace(' ', '+', implode('|', $Items)); + if (!empty($Placement)) { + if (!in_array($Placement, array('b', 't', 'r', 'l', 'bv', 'tv'))) { + trigger_error('Invalid legend placement.'); + } + $this->URL .= '&chdlp='.$Placement; + } + } + + public function add($Label, $Data) { + if ($Label !== false) { + $this->Labels[] = $Label; + } + $this->Data[] = $Data; + } + + public function grid_lines($SpacingX = 0, $SpacingY = -1, $Solid = 1, $Blank = 1) { + //Can take 2 more parameters for offset, but we're not bothering with that right now + $this->URL .= "&chg=$SpacingX,$SpacingY,$Solid,$Blank"; + } + + public function transparent() { + $this->URL .= '&chf=bg,s,FFFFFF00'; + } + + + public function url() { + return $this->URL; + } } class AREA_GRAPH extends GOOGLE_CHARTS { - public function __construct ($Width, $Height, $Options = array()) { - parent::__construct('lc', $Width, $Height, $Options); - } - - public function color ($Color) { - $this->URL .= '&chco='.$Color.'&chm=B,'.$Color.'50,0,0,0'; - } - - public function generate() { - $Max = max($this->Data); - $Min = ((isset($this->Options['Break'])) ? $Min = min($this->Data) : 0); - $Data = array(); - foreach ($this->Data as $Value) { - $Data[] = $this->encode((($Value - $Min) / ($Max - $Min)) * 4095); - } - $this->URL .= "&chxt=y,x&chxs=0,h&chxl=1:|".implode('|', $this->Labels).'&chxr=0,'.$Min.','.($Max - $Min).'&chd=e:'.implode('', $Data); - } + public function __construct ($Width, $Height, $Options = array()) { + parent::__construct('lc', $Width, $Height, $Options); + } + + public function color ($Color) { + $this->URL .= '&chco='.$Color.'&chm=B,'.$Color.'50,0,0,0'; + } + + public function generate() { + $Max = max($this->Data); + $Min = ((isset($this->Options['Break'])) ? $Min = min($this->Data) : 0); + $Data = array(); + foreach ($this->Data as $Value) { + $Data[] = $this->encode((($Value - $Min) / ($Max - $Min)) * 4095); + } + $this->URL .= "&chxt=y,x&chxs=0,h&chxl=1:|".implode('|', $this->Labels).'&chxr=0,'.$Min.','.($Max - $Min).'&chd=e:'.implode('', $Data); + } } class PIE_CHART extends GOOGLE_CHARTS { - public function __construct ($Width, $Height, $Options = array()) { - $Type = ((isset($this->Options['3D'])) ? 'p3' : 'p'); - parent::__construct($Type, $Width, $Height, $Options); - } - - public function generate() { - $Sum = array_sum($this->Data); - $Other = isset($this->Options['Other']); - $Sort = isset($this->Options['Sort']); - $LabelPercent = isset($this->Options['Percentage']); - - if ($Sort && !empty($this->Labels)) { - array_multisort($this->Data, SORT_DESC, $this->Labels); - } elseif ($Sort) { - sort($this->Data); - $this->Data = array_reverse($this->Data); - } - - $Data = array(); - $Labels = $this->Labels; - $OtherPercentage = 0.00; - $OtherData = 0; - - foreach ($this->Data as $Key => $Value) { - $ThisPercentage = number_format(($Value / $Sum) * 100, 2); - $ThisData = ($Value / $Sum) * 4095; - if ($Other && $ThisPercentage < 1) { - $OtherPercentage += $ThisPercentage; - $OtherData += $ThisData; - unset($Data[$Key]); - unset($Labels[$Key]); - continue; - } - if ($LabelPercent) { - $Labels[$Key] .= ' ('.$ThisPercentage.'%)'; - } - $Data[] = $this->encode($ThisData); - } - if ($OtherPercentage > 0) { - $OtherLabel = 'Other'; - if ($LabelPercent) { - $OtherLabel .= ' ('.$OtherPercentage.'%)'; - } - $Labels[] = $OtherLabel; - $Data[] = $this->encode($OtherData); - } - $this->URL .= "&chl=".implode('|', $Labels).'&chd=e:'.implode('', $Data); - } + public function __construct ($Width, $Height, $Options = array()) { + $Type = ((isset($this->Options['3D'])) ? 'p3' : 'p'); + parent::__construct($Type, $Width, $Height, $Options); + } + + public function generate() { + $Sum = array_sum($this->Data); + $Other = isset($this->Options['Other']); + $Sort = isset($this->Options['Sort']); + $LabelPercent = isset($this->Options['Percentage']); + + if ($Sort && !empty($this->Labels)) { + array_multisort($this->Data, SORT_DESC, $this->Labels); + } elseif ($Sort) { + sort($this->Data); + $this->Data = array_reverse($this->Data); + } + + $Data = array(); + $Labels = $this->Labels; + $OtherPercentage = 0.00; + $OtherData = 0; + + foreach ($this->Data as $Key => $Value) { + $ThisPercentage = number_format(($Value / $Sum) * 100, 2); + $ThisData = ($Value / $Sum) * 4095; + if ($Other && $ThisPercentage < 1) { + $OtherPercentage += $ThisPercentage; + $OtherData += $ThisData; + unset($Data[$Key]); + unset($Labels[$Key]); + continue; + } + if ($LabelPercent) { + $Labels[$Key] .= ' ('.$ThisPercentage.'%)'; + } + $Data[] = $this->encode($ThisData); + } + if ($OtherPercentage > 0) { + $OtherLabel = 'Other'; + if ($LabelPercent) { + $OtherLabel .= ' ('.$OtherPercentage.'%)'; + } + $Labels[] = $OtherLabel; + $Data[] = $this->encode($OtherData); + } + $this->URL .= "&chl=".implode('|', $Labels).'&chd=e:'.implode('', $Data); + } } class LOG_BAR_GRAPH extends GOOGLE_CHARTS { - //TODO: Finish. - public function __construct ($Base, $Width, $Height, $Options = array()) { - parent::__construct('lc', $Width, $Height, $Options); - } - - public function color ($Color) { - $this->URL .= '&chco='.$Color.'&chm=B,'.$Color.'50,0,0,0'; - } - - public function generate() { - $Max = max($this->Data); - $Min = ((isset($this->Options['Break'])) ? $Min = min($this->Data) : 0); - $Data = array(); - foreach ($this->Data as $Value) { - $Data[] = $this->encode((($Value - $Min) / ($Max - $Min)) * 4095); - } - $this->URL .= "&chxt=y,x&chxs=0,h&chxl=1:|".implode('|', $this->Labels).'&chxr=0,'.$Min.','.($Max-$Min).'&chd=e:'.implode('', $Data); - } + //TODO: Finish. + public function __construct ($Base, $Width, $Height, $Options = array()) { + parent::__construct('lc', $Width, $Height, $Options); + } + + public function color ($Color) { + $this->URL .= '&chco='.$Color.'&chm=B,'.$Color.'50,0,0,0'; + } + + public function generate() { + $Max = max($this->Data); + $Min = ((isset($this->Options['Break'])) ? $Min = min($this->Data) : 0); + $Data = array(); + foreach ($this->Data as $Value) { + $Data[] = $this->encode((($Value - $Min) / ($Max - $Min)) * 4095); + } + $this->URL .= "&chxt=y,x&chxs=0,h&chxl=1:|".implode('|', $this->Labels).'&chxr=0,'.$Min.','.($Max-$Min).'&chd=e:'.implode('', $Data); + } } class POLL_GRAPH extends GOOGLE_CHARTS { - public function __construct () { - $this->URL .= '?cht=bhg'; - } - - public function add($Label, $Data) { - if ($Label !== false) { - $this->Labels[] = Format::cut_string($Label, 35); - } - $this->Data[] = $Data; - } - - public function generate() { - $Count = count($this->Data); - $Height = (30 * $Count) + 20; - $Max = max($this->Data); - $Sum = array_sum($this->Data); - $Increment = ($Max / $Sum) * 25; // * 100% / 4divisions - $Data = array(); - $Labels = array(); - foreach ($this->Data as $Key => $Value) { - $Data[] = $this->encode(($Value / $Max) * 4095); - $Labels[] = '@t'.str_replace(array(' ', ','),array('+', '\,'), $this->Labels[$Key]).',000000,1,'.round((($Key + 1) / $Count) - (12 / $Height), 2).':0,12'; - } - $this->URL .= "&chbh=25,0,5&chs=214x$Height&chl=0%|".round($Increment, 1)."%|".round($Increment * 2, 1)."%|".round($Increment * 3, 1)."%|".round($Increment * 4, 1)."%&chm=".implode('|', $Labels).'&chd=e:'.implode('', $Data); - } + public function __construct () { + $this->URL .= '?cht=bhg'; + } + + public function add($Label, $Data) { + if ($Label !== false) { + $this->Labels[] = Format::cut_string($Label, 35); + } + $this->Data[] = $Data; + } + + public function generate() { + $Count = count($this->Data); + $Height = (30 * $Count) + 20; + $Max = max($this->Data); + $Sum = array_sum($this->Data); + $Increment = ($Max / $Sum) * 25; // * 100% / 4divisions + $Data = array(); + $Labels = array(); + foreach ($this->Data as $Key => $Value) { + $Data[] = $this->encode(($Value / $Max) * 4095); + $Labels[] = '@t'.str_replace(array(' ', ','),array('+', '\,'), $this->Labels[$Key]).',000000,1,'.round((($Key + 1) / $Count) - (12 / $Height), 2).':0,12'; + } + $this->URL .= "&chbh=25,0,5&chs=214x$Height&chl=0%|".round($Increment, 1)."%|".round($Increment * 2, 1)."%|".round($Increment * 3, 1)."%|".round($Increment * 4, 1)."%&chm=".implode('|', $Labels).'&chd=e:'.implode('', $Data); + } } diff --git a/classes/classloader.php b/classes/classloader.php index 0cca8472d..b03cd0d1f 100644 --- a/classes/classloader.php +++ b/classes/classloader.php @@ -6,33 +6,36 @@ * @param string $ClassName class name */ spl_autoload_register(function ($ClassName) { - $FilePath = SERVER_ROOT . '/classes/' . strtolower($ClassName) . '.class.php'; - if (!file_exists($FilePath)) { - // TODO: Rename the following classes to conform with the code guidelines - switch ($ClassName) { - case 'MASS_USER_BOOKMARKS_EDITOR': - $FileName = 'mass_user_bookmarks_editor.class'; - break; - case 'MASS_USER_TORRENTS_EDITOR': - $FileName = 'mass_user_torrents_editor.class'; - break; - case 'MASS_USER_TORRENTS_TABLE_VIEW': - $FileName = 'mass_user_torrents_table_view.class'; - break; - case 'TEXTAREA_PREVIEW': - $FileName = 'textarea_preview.class'; - break; - case 'TORRENT': - case 'BENCODE_DICT': - case 'BENCODE_LIST': - $FileName = 'torrent.class'; - break; - default: - die("Couldn't import class $ClassName"); - } - $FilePath = SERVER_ROOT . "/classes/$FileName.php"; - } - require_once($FilePath); + $FilePath = SERVER_ROOT . '/classes/' . strtolower($ClassName) . '.class.php'; + if (!file_exists($FilePath)) { + // TODO: Rename the following classes to conform with the code guidelines + switch ($ClassName) { + case 'MASS_USER_BOOKMARKS_EDITOR': + $FileName = 'mass_user_bookmarks_editor.class'; + break; + case 'MASS_USER_TORRENTS_EDITOR': + $FileName = 'mass_user_torrents_editor.class'; + break; + case 'MASS_USER_TORRENTS_TABLE_VIEW': + $FileName = 'mass_user_torrents_table_view.class'; + break; + case 'TEXTAREA_PREVIEW': + $FileName = 'textarea_preview.class'; + break; + case 'DB_MYSQL': + $FileName = 'mysql.class'; + break; + case 'TORRENT': + case 'BENCODE_DICT': + case 'BENCODE_LIST': + $FileName = 'torrent.class'; + break; + default: + die("Couldn't import class $ClassName"); + } + $FilePath = SERVER_ROOT . "/classes/$FileName.php"; + } + require_once($FilePath); }); -require(__DIR__.'/../vendor/autoload.php'); \ No newline at end of file +require(__DIR__.'/../vendor/autoload.php'); diff --git a/classes/collages.class.php b/classes/collages.class.php index f398c73f4..88354c413 100644 --- a/classes/collages.class.php +++ b/classes/collages.class.php @@ -1,92 +1,92 @@ -get_query_id(); - G::$DB->query(" - UPDATE collages - SET Subscribers = Subscribers + 1 - WHERE ID = '$CollageID'"); - G::$DB->set_query_id($QueryID); - } + public static function increase_subscriptions($CollageID) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + UPDATE collages + SET Subscribers = Subscribers + 1 + WHERE ID = '$CollageID'"); + G::$DB->set_query_id($QueryID); + } - public static function decrease_subscriptions($CollageID) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - UPDATE collages - SET Subscribers = IF(Subscribers < 1, 0, Subscribers - 1) - WHERE ID = '$CollageID'"); - G::$DB->set_query_id($QueryID); - } + public static function decrease_subscriptions($CollageID) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + UPDATE collages + SET Subscribers = IF(Subscribers < 1, 0, Subscribers - 1) + WHERE ID = '$CollageID'"); + G::$DB->set_query_id($QueryID); + } - public static function create_personal_collage() { - G::$DB->query(" - SELECT - COUNT(ID) - FROM collages - WHERE UserID = '" . G::$LoggedUser['ID'] . "' - AND CategoryID = '0' - AND Deleted = '0'"); - list($CollageCount) = G::$DB->next_record(); + public static function create_personal_collage() { + G::$DB->query(" + SELECT + COUNT(ID) + FROM collages + WHERE UserID = '" . G::$LoggedUser['ID'] . "' + AND CategoryID = '0' + AND Deleted = '0'"); + list($CollageCount) = G::$DB->next_record(); - if ($CollageCount >= G::$LoggedUser['Permissions']['MaxCollages']) { - // TODO: fix this, the query was for COUNT(ID), so I highly doubt that this works... - Y - list($CollageID) = G::$DB->next_record(); - header('Location: collage.php?id='.$CollageID); - die(); - } - $NameStr = db_string(G::$LoggedUser['Username'] . "'s personal collage" . ($CollageCount > 0 ? ' no. ' . ($CollageCount + 1) : '')); - $Description = db_string('Personal collage for ' . G::$LoggedUser['Username'] . '. The first 5 albums will appear on his or her [url=' . site_url() . 'user.php?id= ' . G::$LoggedUser['ID'] . ']profile[/url].'); - G::$DB->query(" - INSERT INTO collages - (Name, Description, CategoryID, UserID) - VALUES - ('$NameStr', '$Description', '0', " . G::$LoggedUser['ID'] . ")"); - $CollageID = G::$DB->inserted_id(); - header('Location: collage.php?id='.$CollageID); - die(); - } + if ($CollageCount >= G::$LoggedUser['Permissions']['MaxCollages']) { + // TODO: fix this, the query was for COUNT(ID), so I highly doubt that this works... - Y + list($CollageID) = G::$DB->next_record(); + header('Location: collage.php?id='.$CollageID); + die(); + } + $NameStr = db_string(G::$LoggedUser['Username'] . "'s personal collage" . ($CollageCount > 0 ? ' no. ' . ($CollageCount + 1) : '')); + $Description = db_string('Personal collage for ' . G::$LoggedUser['Username'] . '. The first 5 albums will appear on his or her [url=' . site_url() . 'user.php?id= ' . G::$LoggedUser['ID'] . ']profile[/url].'); + G::$DB->query(" + INSERT INTO collages + (Name, Description, CategoryID, UserID) + VALUES + ('$NameStr', '$Description', '0', " . G::$LoggedUser['ID'] . ")"); + $CollageID = G::$DB->inserted_id(); + header('Location: collage.php?id='.$CollageID); + die(); + } - public static function collage_cover_row($Group) { - extract(Torrents::array_group($Group)); - /** - * @var int $GroupID - * @var string $GroupName - * @var string $GroupYear - * @var int $GroupCategoryID - * @var string $GroupRecordLabel - * @var array $Artists - * @var array $ExtendedArtists - * @var string $TagList - * @var string $WikiImage - */ + public static function collage_cover_row($Group) { + extract(Torrents::array_group($Group)); + /** + * @var int $GroupID + * @var string $GroupName + * @var string $GroupYear + * @var int $GroupCategoryID + * @var string $GroupRecordLabel + * @var array $Artists + * @var array $ExtendedArtists + * @var string $TagList + * @var string $WikiImage + */ - $DisplayName = ''; - if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5])|| !empty($ExtendedArtists[6])) { - unset($ExtendedArtists[2]); - unset($ExtendedArtists[3]); - $DisplayName .= Artists::display_artists($ExtendedArtists, false); - } elseif (count($Artists) > 0) { - $DisplayName .= Artists::display_artists(array('1' => $Artists), false); - } - $DisplayName .= $GroupName; - if ($GroupYear > 0) { - $DisplayName = "$DisplayName [$GroupYear]"; - } - $TorrentTags = new Tags($TagList); - $Tags = display_str($TorrentTags->format()); - $PlainTags = implode(', ', $TorrentTags->get_tags()); - ob_start(); - ?> -
  • - - - <?=$DisplayName?>" width="118" /> - -
    - -
    -
  • - 0) { + $DisplayName .= Artists::display_artists(array('1' => $Artists), false); + } + $DisplayName .= $GroupName; + if ($GroupYear > 0) { + $DisplayName = "$DisplayName [$GroupYear]"; + } + $TorrentTags = new Tags($TagList); + $Tags = display_str($TorrentTags->format()); + $PlainTags = implode(', ', $TorrentTags->get_tags()); + ob_start(); + ?> +
  • + + + <?=$DisplayName?>" width="118" /> + +
    + +
    +
  • + get_query_id(); - G::$DB->query(" - SELECT - CEIL( - ( - SELECT COUNT(ID) + 1 - FROM comments - WHERE Page = '$Page' - AND PageID = $PageID - ) / " . TORRENT_COMMENTS_PER_PAGE . " - ) AS Pages"); - list($Pages) = G::$DB->next_record(); - - G::$DB->query(" - INSERT INTO comments (Page, PageID, AuthorID, AddedTime, Body) - VALUES ('$Page', $PageID, " . G::$LoggedUser['ID'] . ", '" . sqltime() . "', '" . db_string($Body) . "')"); - $PostID = G::$DB->inserted_id(); - - $CatalogueID = floor((TORRENT_COMMENTS_PER_PAGE * $Pages - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE); - G::$Cache->delete_value($Page.'_comments_'.$PageID.'_catalogue_'.$CatalogueID); - G::$Cache->delete_value($Page.'_comments_'.$PageID); - - Subscriptions::flush_subscriptions($Page, $PageID); - Subscriptions::quote_notify($Body, $PostID, $Page, $PageID); - - G::$DB->set_query_id($QueryID); - - return $PostID; - } - - /** - * Edit a comment - * @param int $PostID - * @param string $NewBody - * @param bool $SendPM If true, send a PM to the author of the comment informing him about the edit - * @todo move permission check out of here/remove hardcoded error(404) - */ - public static function edit($PostID, $NewBody, $SendPM = false) { - $QueryID = G::$DB->get_query_id(); - - G::$DB->query(" - SELECT - Body, - AuthorID, - Page, - PageID, - AddedTime - FROM comments - WHERE ID = $PostID"); - if (!G::$DB->has_results()) { - return false; - } - list($OldBody, $AuthorID, $Page, $PageID, $AddedTime) = G::$DB->next_record(); - - if (G::$LoggedUser['ID'] != $AuthorID && !check_perms('site_moderate_forums')) { - return false; - } - - G::$DB->query(" - SELECT CEIL(COUNT(ID) / " . TORRENT_COMMENTS_PER_PAGE . ") AS Page - FROM comments - WHERE Page = '$Page' - AND PageID = $PageID - AND ID <= $PostID"); - list($CommPage) = G::$DB->next_record(); - - // Perform the update - G::$DB->query(" - UPDATE comments - SET - Body = '" . db_string($NewBody) . "', - EditedUserID = " . G::$LoggedUser['ID'] . ", - EditedTime = '" . sqltime() . "' - WHERE ID = $PostID"); - - // Update the cache - $CatalogueID = floor((TORRENT_COMMENTS_PER_PAGE * $CommPage - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE); - G::$Cache->delete_value($Page . '_comments_' . $PageID . '_catalogue_' . $CatalogueID); - - if ($Page == 'collages') { - // On collages, we also need to clear the collage key (collage_$CollageID), because it has the comments in it... (why??) - G::$Cache->delete_value('collage_' . $PageID); - } - - G::$DB->query(" - INSERT INTO comments_edits (Page, PostID, EditUser, EditTime, Body) - VALUES ('$Page', $PostID, " . G::$LoggedUser['ID'] . ", '" . sqltime() . "', '" . db_string($OldBody) . "')"); - - G::$DB->set_query_id($QueryID); - - if ($SendPM && G::$LoggedUser['ID'] != $AuthorID) { - // Send a PM to the user to notify them of the edit - $PMSubject = "Your comment #$PostID has been edited"; - $PMurl = site_url()."comments.php?action=jump&postid=$PostID"; - $ProfLink = '[url='.site_url().'user.php?id='.G::$LoggedUser['ID'].']'.G::$LoggedUser['Username'].'[/url]'; - $PMBody = "One of your comments has been edited by $ProfLink: [url]{$PMurl}[/url]"; - Misc::send_pm($AuthorID, 0, $PMSubject, $PMBody); - } - - return true; // TODO: this should reflect whether or not the update was actually successful, e.g. by checking G::$DB->affected_rows after the UPDATE query - } - - /** - * Delete a comment - * @param int $PostID - */ - public static function delete($PostID) { - $QueryID = G::$DB->get_query_id(); - // Get page, pageid - G::$DB->query("SELECT Page, PageID FROM comments WHERE ID = $PostID"); - if (!G::$DB->has_results()) { - // no such comment? - G::$DB->set_query_id($QueryID); - return false; - } - list ($Page, $PageID) = G::$DB->next_record(); - // get number of pages - G::$DB->query(" - SELECT - CEIL(COUNT(ID) / " . TORRENT_COMMENTS_PER_PAGE . ") AS Pages, - CEIL(SUM(IF(ID <= $PostID, 1, 0)) / " . TORRENT_COMMENTS_PER_PAGE . ") AS Page - FROM comments - WHERE Page = '$Page' - AND PageID = $PageID - GROUP BY PageID"); - if (!G::$DB->has_results()) { - // the comment $PostID was probably not posted on $Page - G::$DB->set_query_id($QueryID); - return false; - } - list($CommPages, $CommPage) = G::$DB->next_record(); - - // $CommPages = number of pages in the thread - // $CommPage = which page the post is on - // These are set for cache clearing. - - G::$DB->query(" - DELETE FROM comments - WHERE ID = $PostID"); - G::$DB->query(" - DELETE FROM comments_edits - WHERE Page = '$Page' - AND PostID = $PostID"); - - G::$DB->query(" - DELETE FROM users_notify_quoted - WHERE Page = '$Page' - AND PostID = $PostID"); - - Subscriptions::flush_subscriptions($Page, $PageID); - Subscriptions::flush_quote_notifications($Page, $PageID); - - //We need to clear all subsequential catalogues as they've all been bumped with the absence of this post - $ThisCatalogue = floor((TORRENT_COMMENTS_PER_PAGE * $CommPage - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE); - $LastCatalogue = floor((TORRENT_COMMENTS_PER_PAGE * $CommPages - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE); - for ($i = $ThisCatalogue; $i <= $LastCatalogue; ++$i) { - G::$Cache->delete_value($Page . '_comments_' . $PageID . '_catalogue_' . $i); - } - - G::$Cache->delete_value($Page . '_comments_' . $PageID); - - if ($Page == 'collages') { - // On collages, we also need to clear the collage key (collage_$CollageID), because it has the comments in it... (why??) - G::$Cache->delete_value("collage_$PageID"); - } - - G::$DB->set_query_id($QueryID); - - return true; - } - - /** - * Get the URL to a comment, already knowing the Page and PostID - * @param string $Page - * @param int $PageID - * @param int $PostID - * @return string|bool The URL to the comment or false on error - */ - public static function get_url($Page, $PageID, $PostID = null) { - $Post = (!empty($PostID) ? "&postid=$PostID#post$PostID" : ''); - switch ($Page) { - case 'artist': - return "artist.php?id=$PageID$Post"; - case 'collages': - return "collages.php?action=comments&collageid=$PageID$Post"; - case 'requests': - return "requests.php?action=view&id=$PageID$Post"; - case 'torrents': - return "torrents.php?id=$PageID$Post"; - default: - return false; - } - } - - /** - * Get the URL to a comment - * @param int $PostID - * @return string|bool The URL to the comment or false on error - */ - public static function get_url_query($PostID) { - $QueryID = G::$DB->get_query_id(); - - G::$DB->query(" - SELECT Page, PageID - FROM comments - WHERE ID = $PostID"); - if (!G::$DB->has_results()) { - error(404); - } - list($Page, $PageID) = G::$DB->next_record(); - - G::$DB->set_query_id($QueryID); - - return self::get_url($Page, $PageID, $PostID); - } - - /** - * Load a page's comments. This takes care of `postid` and (indirectly) `page` parameters passed in $_GET. - * Quote notifications and last read are also handled here, unless $HandleSubscriptions = false is passed. - * @param string $Page - * @param int $PageID - * @param bool $HandleSubscriptions Whether or not to handle subscriptions (last read & quote notifications) - * @return array ($NumComments, $Page, $Thread, $LastRead) - * $NumComments: the total number of comments on this artist/request/torrent group - * $Page: the page we're currently on - * $Thread: an array of all posts on this page - * $LastRead: ID of the last comment read by the current user in this thread; - * will be false if $HandleSubscriptions == false or if there are no comments on this page - */ - public static function load($Page, $PageID, $HandleSubscriptions = true) { - $QueryID = G::$DB->get_query_id(); - - // Get the total number of comments - $NumComments = G::$Cache->get_value($Page."_comments_$PageID"); - if ($NumComments === false) { - G::$DB->query(" - SELECT COUNT(ID) - FROM comments - WHERE Page = '$Page' - AND PageID = $PageID"); - list($NumComments) = G::$DB->next_record(); - G::$Cache->cache_value($Page."_comments_$PageID", $NumComments, 0); - } - - // If a postid was passed, we need to determine which page that comment is on. - // Format::page_limit handles a potential $_GET['page'] - if (isset($_GET['postid']) && is_number($_GET['postid']) && $NumComments > TORRENT_COMMENTS_PER_PAGE) { - G::$DB->query(" - SELECT COUNT(ID) - FROM comments - WHERE Page = '$Page' - AND PageID = $PageID - AND ID <= $_GET[postid]"); - list($PostNum) = G::$DB->next_record(); - list($CommPage, $Limit) = Format::page_limit(TORRENT_COMMENTS_PER_PAGE, $PostNum); - } else { - list($CommPage, $Limit) = Format::page_limit(TORRENT_COMMENTS_PER_PAGE, $NumComments); - } - - // Get the cache catalogue - $CatalogueID = floor((TORRENT_COMMENTS_PER_PAGE * $CommPage - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE); - - // Cache catalogue from which the page is selected, allows block caches and future ability to specify posts per page - $Catalogue = G::$Cache->get_value($Page.'_comments_'.$PageID.'_catalogue_'.$CatalogueID); - if ($Catalogue === false) { - $CatalogueLimit = $CatalogueID * THREAD_CATALOGUE . ', ' . THREAD_CATALOGUE; - G::$DB->query(" - SELECT - c.ID, - c.AuthorID, - c.AddedTime, - c.Body, - c.EditedUserID, - c.EditedTime, - u.Username - FROM comments AS c - LEFT JOIN users_main AS u ON u.ID = c.EditedUserID - WHERE c.Page = '$Page' - AND c.PageID = $PageID - ORDER BY c.ID - LIMIT $CatalogueLimit"); - $Catalogue = G::$DB->to_array(false, MYSQLI_ASSOC); - G::$Cache->cache_value($Page.'_comments_'.$PageID.'_catalogue_'.$CatalogueID, $Catalogue, 0); - } - - //This is a hybrid to reduce the catalogue down to the page elements: We use the page limit % catalogue - $Thread = array_slice($Catalogue, ((TORRENT_COMMENTS_PER_PAGE * $CommPage - TORRENT_COMMENTS_PER_PAGE) % THREAD_CATALOGUE), TORRENT_COMMENTS_PER_PAGE, true); - - if ($HandleSubscriptions && count($Thread) > 0) { - // quote notifications - $LastPost = end($Thread); - $LastPost = $LastPost['ID']; - $FirstPost = reset($Thread); - $FirstPost = $FirstPost['ID']; - G::$DB->query(" - UPDATE users_notify_quoted - SET UnRead = false - WHERE UserID = " . G::$LoggedUser['ID'] . " - AND Page = '$Page' - AND PageID = $PageID - AND PostID >= $FirstPost - AND PostID <= $LastPost"); - if (G::$DB->affected_rows()) { - G::$Cache->delete_value('notify_quoted_' . G::$LoggedUser['ID']); - } - - // last read - G::$DB->query(" - SELECT PostID - FROM users_comments_last_read - WHERE UserID = " . G::$LoggedUser['ID'] . " - AND Page = '$Page' - AND PageID = $PageID"); - list($LastRead) = G::$DB->next_record(); - if ($LastRead < $LastPost) { - G::$DB->query(" - INSERT INTO users_comments_last_read - (UserID, Page, PageID, PostID) - VALUES - (" . G::$LoggedUser['ID'] . ", '$Page', $PageID, $LastPost) - ON DUPLICATE KEY UPDATE - PostID = $LastPost"); - G::$Cache->delete_value('subscriptions_user_new_' . G::$LoggedUser['ID']); - } - } else { - $LastRead = false; - } - - G::$DB->set_query_id($QueryID); - - return array($NumComments, $CommPage, $Thread, $LastRead); - } - - /** - * Merges all comments from $Page/$PageID into $Page/$TargetPageID. This also takes care of quote notifications, subscriptions and cache. - * @param type $Page - * @param type $PageID - * @param type $TargetPageID - */ - public static function merge($Page, $PageID, $TargetPageID) { - $QueryID = G::$DB->get_query_id(); - - G::$DB->query(" - UPDATE comments - SET PageID = $TargetPageID - WHERE Page = '$Page' - AND PageID = $PageID"); - - // quote notifications - G::$DB->query(" - UPDATE users_notify_quoted - SET PageID = $TargetPageID - WHERE Page = '$Page' - AND PageID = $PageID"); - - // comment subscriptions - Subscriptions::move_subscriptions($Page, $PageID, $TargetPageID); - - // cache (we need to clear all comment catalogues) - G::$DB->query(" - SELECT - CEIL(COUNT(ID) / " . TORRENT_COMMENTS_PER_PAGE . ") AS Pages - FROM comments - WHERE Page = '$Page' - AND PageID = $TargetPageID - GROUP BY PageID"); - list($CommPages) = G::$DB->next_record(); - $LastCatalogue = floor((TORRENT_COMMENTS_PER_PAGE * $CommPages - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE); - for ($i = 0; $i <= $LastCatalogue; ++$i) { - G::$Cache->delete_value($Page . "_comments_$TargetPageID" . "_catalogue_$i"); - } - G::$Cache->delete_value($Page."_comments_$TargetPageID"); - G::$DB->set_query_id($QueryID); - } - - /** - * Delete all comments on $Page/$PageID (deals with quote notifications and subscriptions as well) - * @param string $Page - * @param int $PageID - * @return boolean - */ - public static function delete_page($Page, $PageID) { - $QueryID = G::$DB->get_query_id(); - - // get number of pages - G::$DB->query(" - SELECT - CEIL(COUNT(ID) / " . TORRENT_COMMENTS_PER_PAGE . ") AS Pages - FROM comments - WHERE Page = '$Page' - AND PageID = $PageID - GROUP BY PageID"); - if (!G::$DB->has_results()) { - return false; - } - list($CommPages) = G::$DB->next_record(); - - // Delete comments - G::$DB->query(" - DELETE FROM comments - WHERE Page = '$Page' - AND PageID = $PageID"); - - // Delete quote notifications - Subscriptions::flush_quote_notifications($Page, $PageID); - G::$DB->query(" - DELETE FROM users_notify_quoted - WHERE Page = '$Page' - AND PageID = $PageID"); - - // Deal with subscriptions - Subscriptions::move_subscriptions($Page, $PageID, null); - - // Clear cache - $LastCatalogue = floor((TORRENT_COMMENTS_PER_PAGE * $CommPages - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE); - for ($i = 0; $i <= $LastCatalogue; ++$i) { - G::$Cache->delete_value($Page . '_comments_' . $PageID . '_catalogue_' . $i); - } - G::$Cache->delete_value($Page.'_comments_'.$PageID); - - G::$DB->set_query_id($QueryID); - - return true; - } + /* + * For all functions: + * $Page = 'artist', 'collages', 'requests' or 'torrents' + * $PageID = ArtistID, CollageID, RequestID or GroupID, respectively + */ + + /** + * Post a comment on an artist, request or torrent page. + * @param string $Page + * @param int $PageID + * @param string $Body + * @return int ID of the new comment + */ + public static function post($Page, $PageID, $Body) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + CEIL( + ( + SELECT COUNT(ID) + 1 + FROM comments + WHERE Page = '$Page' + AND PageID = $PageID + ) / " . TORRENT_COMMENTS_PER_PAGE . " + ) AS Pages"); + list($Pages) = G::$DB->next_record(); + + G::$DB->query(" + INSERT INTO comments (Page, PageID, AuthorID, AddedTime, Body) + VALUES ('$Page', $PageID, " . G::$LoggedUser['ID'] . ", '" . sqltime() . "', '" . db_string($Body) . "')"); + $PostID = G::$DB->inserted_id(); + + $CatalogueID = floor((TORRENT_COMMENTS_PER_PAGE * $Pages - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE); + G::$Cache->delete_value($Page.'_comments_'.$PageID.'_catalogue_'.$CatalogueID); + G::$Cache->delete_value($Page.'_comments_'.$PageID); + + Subscriptions::flush_subscriptions($Page, $PageID); + Subscriptions::quote_notify($Body, $PostID, $Page, $PageID); + + G::$DB->set_query_id($QueryID); + + return $PostID; + } + + /** + * Edit a comment + * @param int $PostID + * @param string $NewBody + * @param bool $SendPM If true, send a PM to the author of the comment informing him about the edit + * @todo move permission check out of here/remove hardcoded error(404) + */ + public static function edit($PostID, $NewBody, $SendPM = false) { + $QueryID = G::$DB->get_query_id(); + + G::$DB->query(" + SELECT + Body, + AuthorID, + Page, + PageID, + AddedTime + FROM comments + WHERE ID = $PostID"); + if (!G::$DB->has_results()) { + return false; + } + list($OldBody, $AuthorID, $Page, $PageID, $AddedTime) = G::$DB->next_record(); + + if (G::$LoggedUser['ID'] != $AuthorID && !check_perms('site_moderate_forums')) { + return false; + } + + G::$DB->query(" + SELECT CEIL(COUNT(ID) / " . TORRENT_COMMENTS_PER_PAGE . ") AS Page + FROM comments + WHERE Page = '$Page' + AND PageID = $PageID + AND ID <= $PostID"); + list($CommPage) = G::$DB->next_record(); + + // Perform the update + G::$DB->query(" + UPDATE comments + SET + Body = '" . db_string($NewBody) . "', + EditedUserID = " . G::$LoggedUser['ID'] . ", + EditedTime = '" . sqltime() . "' + WHERE ID = $PostID"); + + // Update the cache + $CatalogueID = floor((TORRENT_COMMENTS_PER_PAGE * $CommPage - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE); + G::$Cache->delete_value($Page . '_comments_' . $PageID . '_catalogue_' . $CatalogueID); + + if ($Page == 'collages') { + // On collages, we also need to clear the collage key (collage_$CollageID), because it has the comments in it... (why??) + G::$Cache->delete_value('collage_' . $PageID); + } + + G::$DB->query(" + INSERT INTO comments_edits (Page, PostID, EditUser, EditTime, Body) + VALUES ('$Page', $PostID, " . G::$LoggedUser['ID'] . ", '" . sqltime() . "', '" . db_string($OldBody) . "')"); + + G::$DB->set_query_id($QueryID); + + if ($SendPM && G::$LoggedUser['ID'] != $AuthorID) { + // Send a PM to the user to notify them of the edit + $PMSubject = "Your comment #$PostID has been edited"; + $PMurl = site_url()."comments.php?action=jump&postid=$PostID"; + $ProfLink = '[url='.site_url().'user.php?id='.G::$LoggedUser['ID'].']'.G::$LoggedUser['Username'].'[/url]'; + $PMBody = "One of your comments has been edited by $ProfLink: [url]{$PMurl}[/url]"; + Misc::send_pm($AuthorID, 0, $PMSubject, $PMBody); + } + + return true; // TODO: this should reflect whether or not the update was actually successful, e.g. by checking G::$DB->affected_rows after the UPDATE query + } + + /** + * Delete a comment + * @param int $PostID + */ + public static function delete($PostID) { + $QueryID = G::$DB->get_query_id(); + // Get page, pageid + G::$DB->query("SELECT Page, PageID FROM comments WHERE ID = $PostID"); + if (!G::$DB->has_results()) { + // no such comment? + G::$DB->set_query_id($QueryID); + return false; + } + list ($Page, $PageID) = G::$DB->next_record(); + // get number of pages + G::$DB->query(" + SELECT + CEIL(COUNT(ID) / " . TORRENT_COMMENTS_PER_PAGE . ") AS Pages, + CEIL(SUM(IF(ID <= $PostID, 1, 0)) / " . TORRENT_COMMENTS_PER_PAGE . ") AS Page + FROM comments + WHERE Page = '$Page' + AND PageID = $PageID + GROUP BY PageID"); + if (!G::$DB->has_results()) { + // the comment $PostID was probably not posted on $Page + G::$DB->set_query_id($QueryID); + return false; + } + list($CommPages, $CommPage) = G::$DB->next_record(); + + // $CommPages = number of pages in the thread + // $CommPage = which page the post is on + // These are set for cache clearing. + + G::$DB->query(" + DELETE FROM comments + WHERE ID = $PostID"); + G::$DB->query(" + DELETE FROM comments_edits + WHERE Page = '$Page' + AND PostID = $PostID"); + + G::$DB->query(" + DELETE FROM users_notify_quoted + WHERE Page = '$Page' + AND PostID = $PostID"); + + Subscriptions::flush_subscriptions($Page, $PageID); + Subscriptions::flush_quote_notifications($Page, $PageID); + + //We need to clear all subsequential catalogues as they've all been bumped with the absence of this post + $ThisCatalogue = floor((TORRENT_COMMENTS_PER_PAGE * $CommPage - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE); + $LastCatalogue = floor((TORRENT_COMMENTS_PER_PAGE * $CommPages - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE); + for ($i = $ThisCatalogue; $i <= $LastCatalogue; ++$i) { + G::$Cache->delete_value($Page . '_comments_' . $PageID . '_catalogue_' . $i); + } + + G::$Cache->delete_value($Page . '_comments_' . $PageID); + + if ($Page == 'collages') { + // On collages, we also need to clear the collage key (collage_$CollageID), because it has the comments in it... (why??) + G::$Cache->delete_value("collage_$PageID"); + } + + G::$DB->set_query_id($QueryID); + + return true; + } + + /** + * Get the URL to a comment, already knowing the Page and PostID + * @param string $Page + * @param int $PageID + * @param int $PostID + * @return string|bool The URL to the comment or false on error + */ + public static function get_url($Page, $PageID, $PostID = null) { + $Post = (!empty($PostID) ? "&postid=$PostID#post$PostID" : ''); + switch ($Page) { + case 'artist': + return "artist.php?id=$PageID$Post"; + case 'collages': + return "collages.php?action=comments&collageid=$PageID$Post"; + case 'requests': + return "requests.php?action=view&id=$PageID$Post"; + case 'torrents': + return "torrents.php?id=$PageID$Post"; + default: + return false; + } + } + + /** + * Get the URL to a comment + * @param int $PostID + * @return string|bool The URL to the comment or false on error + */ + public static function get_url_query($PostID) { + $QueryID = G::$DB->get_query_id(); + + G::$DB->query(" + SELECT Page, PageID + FROM comments + WHERE ID = $PostID"); + if (!G::$DB->has_results()) { + error(404); + } + list($Page, $PageID) = G::$DB->next_record(); + + G::$DB->set_query_id($QueryID); + + return self::get_url($Page, $PageID, $PostID); + } + + /** + * Load a page's comments. This takes care of `postid` and (indirectly) `page` parameters passed in $_GET. + * Quote notifications and last read are also handled here, unless $HandleSubscriptions = false is passed. + * @param string $Page + * @param int $PageID + * @param bool $HandleSubscriptions Whether or not to handle subscriptions (last read & quote notifications) + * @return array ($NumComments, $Page, $Thread, $LastRead) + * $NumComments: the total number of comments on this artist/request/torrent group + * $Page: the page we're currently on + * $Thread: an array of all posts on this page + * $LastRead: ID of the last comment read by the current user in this thread; + * will be false if $HandleSubscriptions == false or if there are no comments on this page + */ + public static function load($Page, $PageID, $HandleSubscriptions = true) { + $QueryID = G::$DB->get_query_id(); + + // Get the total number of comments + $NumComments = G::$Cache->get_value($Page."_comments_$PageID"); + if ($NumComments === false) { + G::$DB->query(" + SELECT COUNT(ID) + FROM comments + WHERE Page = '$Page' + AND PageID = $PageID"); + list($NumComments) = G::$DB->next_record(); + G::$Cache->cache_value($Page."_comments_$PageID", $NumComments, 0); + } + + // If a postid was passed, we need to determine which page that comment is on. + // Format::page_limit handles a potential $_GET['page'] + if (isset($_GET['postid']) && is_number($_GET['postid']) && $NumComments > TORRENT_COMMENTS_PER_PAGE) { + G::$DB->query(" + SELECT COUNT(ID) + FROM comments + WHERE Page = '$Page' + AND PageID = $PageID + AND ID <= $_GET[postid]"); + list($PostNum) = G::$DB->next_record(); + list($CommPage, $Limit) = Format::page_limit(TORRENT_COMMENTS_PER_PAGE, $PostNum); + } else { + list($CommPage, $Limit) = Format::page_limit(TORRENT_COMMENTS_PER_PAGE, $NumComments); + } + + // Get the cache catalogue + $CatalogueID = floor((TORRENT_COMMENTS_PER_PAGE * $CommPage - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE); + + // Cache catalogue from which the page is selected, allows block caches and future ability to specify posts per page + $Catalogue = G::$Cache->get_value($Page.'_comments_'.$PageID.'_catalogue_'.$CatalogueID); + if ($Catalogue === false) { + $CatalogueLimit = $CatalogueID * THREAD_CATALOGUE . ', ' . THREAD_CATALOGUE; + G::$DB->query(" + SELECT + c.ID, + c.AuthorID, + c.AddedTime, + c.Body, + c.EditedUserID, + c.EditedTime, + u.Username + FROM comments AS c + LEFT JOIN users_main AS u ON u.ID = c.EditedUserID + WHERE c.Page = '$Page' + AND c.PageID = $PageID + ORDER BY c.ID + LIMIT $CatalogueLimit"); + $Catalogue = G::$DB->to_array(false, MYSQLI_ASSOC); + G::$Cache->cache_value($Page.'_comments_'.$PageID.'_catalogue_'.$CatalogueID, $Catalogue, 0); + } + + //This is a hybrid to reduce the catalogue down to the page elements: We use the page limit % catalogue + $Thread = array_slice($Catalogue, ((TORRENT_COMMENTS_PER_PAGE * $CommPage - TORRENT_COMMENTS_PER_PAGE) % THREAD_CATALOGUE), TORRENT_COMMENTS_PER_PAGE, true); + + if ($HandleSubscriptions && count($Thread) > 0) { + // quote notifications + $LastPost = end($Thread); + $LastPost = $LastPost['ID']; + $FirstPost = reset($Thread); + $FirstPost = $FirstPost['ID']; + G::$DB->query(" + UPDATE users_notify_quoted + SET UnRead = false + WHERE UserID = " . G::$LoggedUser['ID'] . " + AND Page = '$Page' + AND PageID = $PageID + AND PostID >= $FirstPost + AND PostID <= $LastPost"); + if (G::$DB->affected_rows()) { + G::$Cache->delete_value('notify_quoted_' . G::$LoggedUser['ID']); + } + + // last read + G::$DB->query(" + SELECT PostID + FROM users_comments_last_read + WHERE UserID = " . G::$LoggedUser['ID'] . " + AND Page = '$Page' + AND PageID = $PageID"); + list($LastRead) = G::$DB->next_record(); + if ($LastRead < $LastPost) { + G::$DB->query(" + INSERT INTO users_comments_last_read + (UserID, Page, PageID, PostID) + VALUES + (" . G::$LoggedUser['ID'] . ", '$Page', $PageID, $LastPost) + ON DUPLICATE KEY UPDATE + PostID = $LastPost"); + G::$Cache->delete_value('subscriptions_user_new_' . G::$LoggedUser['ID']); + } + } else { + $LastRead = false; + } + + G::$DB->set_query_id($QueryID); + + return array($NumComments, $CommPage, $Thread, $LastRead); + } + + /** + * Merges all comments from $Page/$PageID into $Page/$TargetPageID. This also takes care of quote notifications, subscriptions and cache. + * @param type $Page + * @param type $PageID + * @param type $TargetPageID + */ + public static function merge($Page, $PageID, $TargetPageID) { + $QueryID = G::$DB->get_query_id(); + + G::$DB->query(" + UPDATE comments + SET PageID = $TargetPageID + WHERE Page = '$Page' + AND PageID = $PageID"); + + // quote notifications + G::$DB->query(" + UPDATE users_notify_quoted + SET PageID = $TargetPageID + WHERE Page = '$Page' + AND PageID = $PageID"); + + // comment subscriptions + Subscriptions::move_subscriptions($Page, $PageID, $TargetPageID); + + // cache (we need to clear all comment catalogues) + G::$DB->query(" + SELECT + CEIL(COUNT(ID) / " . TORRENT_COMMENTS_PER_PAGE . ") AS Pages + FROM comments + WHERE Page = '$Page' + AND PageID = $TargetPageID + GROUP BY PageID"); + list($CommPages) = G::$DB->next_record(); + $LastCatalogue = floor((TORRENT_COMMENTS_PER_PAGE * $CommPages - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE); + for ($i = 0; $i <= $LastCatalogue; ++$i) { + G::$Cache->delete_value($Page . "_comments_$TargetPageID" . "_catalogue_$i"); + } + G::$Cache->delete_value($Page."_comments_$TargetPageID"); + G::$DB->set_query_id($QueryID); + } + + /** + * Delete all comments on $Page/$PageID (deals with quote notifications and subscriptions as well) + * @param string $Page + * @param int $PageID + * @return boolean + */ + public static function delete_page($Page, $PageID) { + $QueryID = G::$DB->get_query_id(); + + // get number of pages + G::$DB->query(" + SELECT + CEIL(COUNT(ID) / " . TORRENT_COMMENTS_PER_PAGE . ") AS Pages + FROM comments + WHERE Page = '$Page' + AND PageID = $PageID + GROUP BY PageID"); + if (!G::$DB->has_results()) { + return false; + } + list($CommPages) = G::$DB->next_record(); + + // Delete comments + G::$DB->query(" + DELETE FROM comments + WHERE Page = '$Page' + AND PageID = $PageID"); + + // Delete quote notifications + Subscriptions::flush_quote_notifications($Page, $PageID); + G::$DB->query(" + DELETE FROM users_notify_quoted + WHERE Page = '$Page' + AND PageID = $PageID"); + + // Deal with subscriptions + Subscriptions::move_subscriptions($Page, $PageID, null); + + // Clear cache + $LastCatalogue = floor((TORRENT_COMMENTS_PER_PAGE * $CommPages - TORRENT_COMMENTS_PER_PAGE) / THREAD_CATALOGUE); + for ($i = 0; $i <= $LastCatalogue; ++$i) { + G::$Cache->delete_value($Page . '_comments_' . $PageID . '_catalogue_' . $i); + } + G::$Cache->delete_value($Page.'_comments_'.$PageID); + + G::$DB->set_query_id($QueryID); + + return true; + } } diff --git a/classes/commentsview.class.php b/classes/commentsview.class.php index df5cc9ac4..e846e60a3 100644 --- a/classes/commentsview.class.php +++ b/classes/commentsview.class.php @@ -1,95 +1,95 @@ - $LastRead)); - } - } + /** + * Render a thread of comments + * @param array $Thread An array as returned by Comments::load + * @param int $LastRead PostID of the last read post + * @param string $Baselink Link to the site these comments are on + */ + public static function render_comments($Thread, $LastRead, $Baselink) { + foreach ($Thread as $Post) { + list($PostID, $AuthorID, $AddedTime, $CommentBody, $EditedUserID, $EditedTime, $EditedUsername) = array_values($Post); + self::render_comment($AuthorID, $PostID, $CommentBody, $AddedTime, $EditedUserID, $EditedTime, $Baselink . "&postid=$PostID#post$PostID", ($PostID > $LastRead)); + } + } - /** - * Render one comment - * @param int $AuthorID - * @param int $PostID - * @param string $Body - * @param string $AddedTime - * @param int $EditedUserID - * @param string $EditedTime - * @param string $Link The link to the post elsewhere on the site - * @param string $Header The header used in the post - * @param bool $Tools Whether or not to show [Edit], [Report] etc. - * @todo Find a better way to pass the page (artist, collages, requests, torrents) to this function than extracting it from $Link - */ - function render_comment($AuthorID, $PostID, $Body, $AddedTime, $EditedUserID, $EditedTime, $Link, $Unread = false, $Header = '', $Tools = true) { - $UserInfo = Users::user_info($AuthorID); - $Header = '' . Users::format_username($AuthorID, true, true, true, true, false) . ' ' . time_diff($AddedTime) . $Header; + /** + * Render one comment + * @param int $AuthorID + * @param int $PostID + * @param string $Body + * @param string $AddedTime + * @param int $EditedUserID + * @param string $EditedTime + * @param string $Link The link to the post elsewhere on the site + * @param string $Header The header used in the post + * @param bool $Tools Whether or not to show [Edit], [Report] etc. + * @todo Find a better way to pass the page (artist, collages, requests, torrents) to this function than extracting it from $Link + */ + function render_comment($AuthorID, $PostID, $Body, $AddedTime, $EditedUserID, $EditedTime, $Link, $Unread = false, $Header = '', $Tools = true) { + $UserInfo = Users::user_info($AuthorID); + $Header = '' . Users::format_username($AuthorID, true, true, true, true, false) . ' ' . time_diff($AddedTime) . $Header; ?> - - - - - - - - - + +
    -
    # - - - - Quote - - - Edit - - - Delete - -
    -
    - Report -= $UserInfo['Class']) { + + + + + + + + + - - - - - - + + + + + + - -
    +
    # + + + - Quote + + - Edit + + - Delete + +
    +
    + Report += $UserInfo['Class']) { ?> - - - Warn - -   - - -
    -
    - - -
    - - -
    -
    + + - Warn + +   + + +
    +
    + + +
    + + +
    +
    - - « - - Last edited by - + + « + + Last edited by + - -
    -
    - +
    +
    + 'unix:///var/run/memcached.sock', 'port' => 0, 'buckets' => 1), + // unix sockets are fast, and other people can't telnet into them + array('host' => 'unix:///var/run/memcached.sock', 'port' => 0, 'buckets' => 1), ); // Sphinx details @@ -56,11 +66,11 @@ define('TRACKER_SECRET', ''); // Must be 32 characters and match site_password i define('TRACKER_REPORTKEY', ''); // Must be 32 characters and match report_password in Ocelot's config.cpp if (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 80) { - define('SITE_URL', NONSSL_SITE_URL); - define('STATIC_SERVER', NONSSL_STATIC_SERVER); + define('SITE_URL', NONSSL_SITE_URL); + define('STATIC_SERVER', NONSSL_STATIC_SERVER); } else { - define('SITE_URL', SSL_SITE_URL); - define('STATIC_SERVER', SSL_STATIC_SERVER); + define('SITE_URL', SSL_SITE_URL); + define('STATIC_SERVER', SSL_STATIC_SERVER); } // Site settings @@ -71,8 +81,8 @@ define('SHOW_PUBLIC_INDEX', true); // Show the public index.php landing page define('OPEN_REGISTRATION', true); //Set to false to disable open registration, true to allow anyone to register define('OPEN_EXTERNAL_REFERRALS', true); //Set to false to disable external tracker referrals, true to allow them define('USER_LIMIT', 5000); //The maximum number of users the site can have, 0 for no limit -define('REQUEST_TAX', 0.0); //Percentage Tax (0 - 1) to charge users on making requests define('STARTING_UPLOAD', 3221225472); //Upload given to newly registered users, in bytes using IEC standard (1024 bytes per KiB) +define('REQUEST_TAX', 0.0); //Percentage Tax (0 - 1) to charge users on making requests define('STARTING_INVITES', 0); //# of invites to give to newly registered users define('BLOCK_TOR', false); //Set to true to block Tor users define('BLOCK_OPERA_MINI', false); //Set to true to block Opera Mini proxy @@ -87,30 +97,30 @@ define('BUGS_FORUM_ID', 6); // ID of bug reports forum define('BUGS_RESOLVED_FORUM_ID', 14); // ID of forum to send resolved bug reports when resolve button is pressed in BUGS_FORUM_ID if (!defined('FEATURE_EMAIL_REENABLE')) { - define('FEATURE_EMAIL_REENABLE', true); + define('FEATURE_EMAIL_REENABLE', true); } // User class IDs needed for automatic promotions. Found in the 'permissions' table -// Name of class Class ID (NOT level) -define('ADMIN', '1'); -define('USER', '2'); -define('MEMBER', '3'); -define('POWER', '4'); -define('ELITE', '5'); -define('VIP', '6'); +// Name of class Class ID (NOT level) +define('ADMIN', '1'); +define('USER', '2'); +define('MEMBER', '3'); +define('POWER', '4'); +define('ELITE', '5'); +define('VIP', '6'); define('TORRENT_MASTER','7'); -define('LEGEND', '8'); -define('CELEB', '9'); -define('MOD', '11'); -define('DESIGNER', '13'); -define('CODER', '14'); -define('SYSOP', '15'); -define('ARTIST', '19'); -define('DONOR', '20'); -define('FLS_TEAM', '21'); -define('POWER_TM', '22'); -define('ELITE_TM', '23'); -define('FORUM_MOD', '28'); +define('LEGEND', '8'); +define('CELEB', '9'); +define('MOD', '11'); +define('DESIGNER', '13'); +define('CODER', '14'); +define('SYSOP', '15'); +define('ARTIST', '19'); +define('DONOR', '20'); +define('FLS_TEAM', '21'); +define('POWER_TM', '22'); +define('ELITE_TM', '23'); +define('FORUM_MOD', '28'); // Pagination define('TORRENT_COMMENTS_PER_PAGE', 10); @@ -164,8 +174,15 @@ define('BONUS_AWARD_FLAC', 30); define('BONUS_AWARD_MP3', 30); define('BONUS_AWARD_OTHER', 10); -$ForumsRevealVoters = array(); -$ForumsDoublePost = array(); +define('BONUS_POOL_TAX_STD', 0.9); +define('BONUS_POOL_TAX_ELITE', 0.8); +define('BONUS_POOL_TAX_TM', 0.7); +define('BONUS_POOL_TAX_STAFF', 0.5); + +define('TMPDIR', '/tmp'); + +$ForumsRevealVoters = []; +$ForumsDoublePost = []; $Categories = ['Music', 'Applications', 'E-Books', 'Audiobooks', 'E-Learning Videos', 'Comedy', 'Comics']; $GroupedCategories = array_intersect(['Music'], $Categories); @@ -183,81 +200,130 @@ $ReleaseTypes = array(1=>'Album', 3=>'Soundtrack', 5=>'EP', 6=>'Anthology', 7=>' //$ForumCats = array(1=>'Site', 5=>'Community', 10=>'Help', 8=>'Music', 20=>'Trash'); //No longer needed $ZIPGroups = array( - 0 => 'MP3 (VBR) - High Quality', - 1 => 'MP3 (VBR) - Low Quality', - 2 => 'MP3 (CBR)', - 3 => 'FLAC - Lossless', - 4 => 'Others' + 0 => 'MP3 (VBR) - High Quality', + 1 => 'MP3 (VBR) - Low Quality', + 2 => 'MP3 (CBR)', + 3 => 'FLAC - Lossless', + 4 => 'Others' ); //3D array of attributes, OptionGroup, OptionNumber, Name $ZIPOptions = array( - '00' => array(0, 0, 'V0'), - '01' => array(0, 1, 'APX'), - '02' => array(0, 2, '256'), - '03' => array(0, 3, 'V1'), - '10' => array(1, 0, '224'), - '11' => array(1, 1, 'V2'), - '12' => array(1, 2, 'APS'), - '13' => array(1, 3, '192'), - '20' => array(2, 0, '320'), - '21' => array(2, 1, '256'), - '22' => array(2, 2, '224'), - '23' => array(2, 3, '192'), - '30' => array(3, 0, 'FLAC / 24bit / Vinyl'), - '31' => array(3, 1, 'FLAC / 24bit / DVD'), - '32' => array(3, 2, 'FLAC / 24bit / SACD'), - '33' => array(3, 3, 'FLAC / 24bit / WEB'), - '34' => array(3, 4, 'FLAC / Log (100) / Cue'), - '35' => array(3, 5, 'FLAC / Log (100)'), - '36' => array(3, 6, 'FLAC / Log'), - '37' => array(3, 7, 'FLAC'), - '40' => array(4, 0, 'DTS'), - '41' => array(4, 1, 'Ogg Vorbis'), - '42' => array(4, 2, 'AAC - 320'), - '43' => array(4, 3, 'AAC - 256'), - '44' => array(4, 4, 'AAC - q5.5'), - '45' => array(4, 5, 'AAC - q5'), - '46' => array(4, 6, 'AAC - 192') + '00' => array(0, 0, 'V0'), + '01' => array(0, 1, 'APX'), + '02' => array(0, 2, '256'), + '03' => array(0, 3, 'V1'), + '10' => array(1, 0, '224'), + '11' => array(1, 1, 'V2'), + '12' => array(1, 2, 'APS'), + '13' => array(1, 3, '192'), + '20' => array(2, 0, '320'), + '21' => array(2, 1, '256'), + '22' => array(2, 2, '224'), + '23' => array(2, 3, '192'), + '30' => array(3, 0, 'FLAC / 24bit / Vinyl'), + '31' => array(3, 1, 'FLAC / 24bit / DVD'), + '32' => array(3, 2, 'FLAC / 24bit / SACD'), + '33' => array(3, 3, 'FLAC / 24bit / WEB'), + '34' => array(3, 4, 'FLAC / Log (100) / Cue'), + '35' => array(3, 5, 'FLAC / Log (100)'), + '36' => array(3, 6, 'FLAC / Log'), + '37' => array(3, 7, 'FLAC'), + '40' => array(4, 0, 'DTS'), + '41' => array(4, 1, 'Ogg Vorbis'), + '42' => array(4, 2, 'AAC - 320'), + '43' => array(4, 3, 'AAC - 256'), + '44' => array(4, 4, 'AAC - q5.5'), + '45' => array(4, 5, 'AAC - q5'), + '46' => array(4, 6, 'AAC - 192') ); // Ratio requirements, in descending order // Columns: Download amount, required ratio, grace period $RatioRequirements = array( - array(50 * 1024 * 1024 * 1024, 0.60, date('Y-m-d H:i:s')), - array(40 * 1024 * 1024 * 1024, 0.50, date('Y-m-d H:i:s')), - array(30 * 1024 * 1024 * 1024, 0.40, date('Y-m-d H:i:s')), - array(20 * 1024 * 1024 * 1024, 0.30, date('Y-m-d H:i:s')), - array(10 * 1024 * 1024 * 1024, 0.20, date('Y-m-d H:i:s')), - array(5 * 1024 * 1024 * 1024, 0.15, date('Y-m-d H:i:s', time() - (60 * 60 * 24 * 14))) + array(50 * 1024 * 1024 * 1024, 0.60, date('Y-m-d H:i:s')), + array(40 * 1024 * 1024 * 1024, 0.50, date('Y-m-d H:i:s')), + array(30 * 1024 * 1024 * 1024, 0.40, date('Y-m-d H:i:s')), + array(20 * 1024 * 1024 * 1024, 0.30, date('Y-m-d H:i:s')), + array(10 * 1024 * 1024 * 1024, 0.20, date('Y-m-d H:i:s')), + array(5 * 1024 * 1024 * 1024, 0.15, date('Y-m-d H:i:s', time() - (60 * 60 * 24 * 14))) ); //Captcha fonts should be located in /classes/fonts $CaptchaFonts = array( - 'ARIBLK.TTF', - 'IMPACT.TTF', - 'TREBUC.TTF', - 'TREBUCBD.TTF', - 'TREBUCBI.TTF', - 'TREBUCIT.TTF', - 'VERDANA.TTF', - 'VERDANAB.TTF', - 'VERDANAI.TTF', - 'VERDANAZ.TTF'); + 'ARIBLK.TTF', + 'IMPACT.TTF', + 'TREBUC.TTF', + 'TREBUCBD.TTF', + 'TREBUCBI.TTF', + 'TREBUCIT.TTF', + 'VERDANA.TTF', + 'VERDANAB.TTF', + 'VERDANAI.TTF', + 'VERDANAZ.TTF'); //Captcha images should be located in /captcha $CaptchaBGs = array( - 'captcha1.png', - 'captcha2.png', - 'captcha3.png', - 'captcha4.png', - 'captcha5.png', - 'captcha6.png', - 'captcha7.png', - 'captcha8.png', - 'captcha9.png'); + 'captcha1.png', + 'captcha2.png', + 'captcha3.png', + 'captcha4.png', + 'captcha5.png', + 'captcha6.png', + 'captcha7.png', + 'captcha8.png', + 'captcha9.png'); // Special characters, and what they should be converted to // Used for torrent searching $SpecialChars = array( - '&' => 'and' + '&' => 'and' ); + +// Deny cache access to keys without specified permission +$CachePermissions = [ + 'api_apps' => 'site_debug', + 'catalogue' => 'site_debug' +]; + +// array to store external site credentials and API URIs, stored in cache to keep user sessions alive +$ExternalServicesConfig = [ + "Orpheus" => [ + 'type' => 'gazelle', + 'inviter_id' => 1, + 'base_url' => 'https://orpheus.network/', + 'api_path' => 'ajax.php?action=', + 'login_path' => 'login.php', + 'username' => 'foo', + 'password' => 'bar', + 'cookie' => '', + 'cookie_expiry' => 0, + 'status' => TRUE + ], + "VagrantGazelle" => [ + 'type' => 'gazelle', + 'inviter_id' => 1, + 'base_url' => 'http://localhost:80/', + 'api_path' => 'ajax.php?action=', + 'login_path' => 'login.php', + 'username' => 'foo', + 'password' => 'bar', + 'cookie' => '', + 'cookie_expiry' => 0, + 'status' => TRUE + ], + "PassThePopcorn" => [ + 'type' => 'gazelle', + 'inviter_id' => 1, + 'base_url' => 'https://passthepopcorn.me/', + 'api_path' => 'ajax.php?action=', + 'login_path' => 'login.php', + 'username' => 'foo', + 'password' => 'bar', + 'cookie' => '', + 'cookie_expiry' => 0, + 'status' => TRUE + ]]; + +define('TOP10_ALL_TIME_THRESHOLD', 150); +define('TOP10_YEAR_THRESHOLD', 80); +define('TOP10_DATA_THRESHOLD', 50); diff --git a/classes/cookie.class.php b/classes/cookie.class.php index ccc3a66b3..761b6225d 100644 --- a/classes/cookie.class.php +++ b/classes/cookie.class.php @@ -1,4 +1,4 @@ -del($Cookie); - } - } + const LIMIT_ACCESS = true; //If true, blocks JS cookie API access by default (can be overridden case by case) + const PREFIX = ''; //In some cases you may desire to prefix your cookies + + public function get($Key) { + if (!isset($_COOKIE[SELF::PREFIX.$Key])) { + return false; + } + return $_COOKIE[SELF::PREFIX.$Key]; + } + + //Pass the 4th optional param as false to allow JS access to the cookie + public function set($Key, $Value, $Seconds = 86400, $LimitAccess = SELF::LIMIT_ACCESS) { + setcookie(SELF::PREFIX.$Key, $Value, time() + $Seconds, '/', SITE_URL, $_SERVER['SERVER_PORT'] === '443', $LimitAccess, false); + } + + public function del($Key) { + setcookie(SELF::PREFIX.$Key, '', time() - 24 * 3600); //3600 vs 1 second to account for potential clock desyncs + } + + public function flush() { + $Cookies = array_keys($_COOKIE); + foreach ($Cookies as $Cookie) { + $this->del($Cookie); + } + } } diff --git a/classes/debug.class.php b/classes/debug.class.php index 075323e3c..543100e46 100644 --- a/classes/debug.class.php +++ b/classes/debug.class.php @@ -1,4 +1,4 @@ - MAX_TIME && !defined('TIME_EXCEPTION')) { - $Reason[] = number_format($Micro, 3).' ms'; - } - - $Errors = count($this->get_errors()); - if ($Errors > MAX_ERRORS && !defined('ERROR_EXCEPTION')) { - $Reason[] = $Errors.' PHP errors'; - } - /* - $Queries = count($this->get_queries()); - if ($Queries > MAX_QUERIES && !defined('QUERY_EXCEPTION')) { - $Reason[] = $Queries.' Queries'; - } - */ - $Ram = memory_get_usage(true); - if ($Ram > MAX_MEMORY && !defined('MEMORY_EXCEPTION')) { - $Reason[] = Format::get_size($Ram).' RAM used'; - } - - G::$DB->warnings(); // see comment in MYSQL::query - /*$Queries = $this->get_queries(); - $DBWarningCount = 0; - foreach ($Queries as $Query) { - if (!empty($Query[2])) { - $DBWarningCount += count($Query[2]); - } - } - if ($DBWarningCount) { - $Reason[] = $DBWarningCount . ' DB warning(s)'; - }*/ - - $CacheStatus = G::$Cache->server_status(); - if (in_array(0, $CacheStatus) && !G::$Cache->get_value('cache_fail_reported')) { - // Limit to max one report every 15 minutes to avoid massive debug spam - G::$Cache->cache_value('cache_fail_reported', true, 900); - $Reason[] = "Cache server error"; - } - - if (isset($_REQUEST['profile'])) { - $Reason[] = 'Requested by ' . G::$LoggedUser['Username']; - } - - $this->Perf['Memory usage'] = (($Ram>>10) / 1024).' MB'; - $this->Perf['Page process time'] = number_format($Micro / 1000, 3).' s'; - $this->Perf['CPU time'] = number_format($this->get_cpu_time() / 1000000, 3).' s'; - - if (isset($Reason[0])) { - $this->log_var($CacheStatus, 'Cache server status'); - $this->analysis(implode(', ', $Reason)); - return true; - } - - return false; - } - - public function analysis($Message, $Report = '', $Time = 43200) { - global $Document; - if (empty($Report)) { - $Report = $Message; - } - $Identifier = Users::make_secret(5); - G::$Cache->cache_value( - 'analysis_'.$Identifier, - array( - 'url' => $_SERVER['REQUEST_URI'], - 'message' => $Report, - 'errors' => $this->get_errors(true), - 'queries' => $this->get_queries(), - 'flags' => $this->get_flags(), - 'includes' => $this->get_includes(), - 'cache' => $this->get_cache_keys(), - 'vars' => $this->get_logged_vars(), - 'perf' => $this->get_perf(), - 'ocelot' => $this->get_ocelot_requests() - ), - $Time - ); - $RequestURI = !empty($_SERVER['REQUEST_URI']) ? substr($_SERVER['REQUEST_URI'], 1) : ''; - send_irc('PRIVMSG '.LAB_CHAN." :{$Message} $Document ".site_url()."tools.php?action=analysis&case=$Identifier ".site_url().$RequestURI); - } - - public function get_cpu_time() { - if (!defined('PHP_WINDOWS_VERSION_MAJOR')) { - global $CPUTimeStart; - $RUsage = getrusage(); - $CPUTime = $RUsage['ru_utime.tv_sec'] * 1000000 + $RUsage['ru_utime.tv_usec'] - $CPUTimeStart; - return $CPUTime; - } - return false; - } - - public function log_var($Var, $VarName = false) { - $BackTrace = debug_backtrace(); - $ID = Users::make_secret(5); - if (!$VarName) { - $VarName = $ID; - } - $File = array('path' => substr($BackTrace[0]['file'], strlen(SERVER_ROOT)), 'line' => $BackTrace[0]['line']); - $this->LoggedVars[$ID] = array($VarName => array('bt' => $File, 'data' => $Var)); - } - - public function set_flag($Event) { - global $ScriptStartTime; - $this->Flags[] = array($Event, (microtime(true) - $ScriptStartTime) * 1000, memory_get_usage(true), $this->get_cpu_time()); - } - - //This isn't in the constructor because $this is not available, and the function cannot be made static - public function handle_errors() { - //error_reporting(E_ALL ^ E_STRICT | E_WARNING | E_DEPRECATED | E_ERROR | E_PARSE); //E_STRICT disabled - error_reporting(E_WARNING | E_ERROR | E_PARSE); - set_error_handler(array($this, 'php_error_handler')); - } - - protected function format_args($Array) { - $LastKey = -1; - $Return = []; - foreach ($Array as $Key => $Val) { - $Return[$Key] = ''; - if (!is_numeric($Key) || !is_numeric($LastKey) || $Key != $LastKey + 1) { - $Return[$Key] .= "'$Key' => "; - } - if ($Val === true) { - $Return[$Key] .= 'true'; - } elseif ($Val === false) { - $Return[$Key] .= 'false'; - } elseif (is_numeric($Val)) { - $Return[$Key] .= $Val; - } elseif (is_string($Val)) { - $Return[$Key] .= "'$Val'"; - } elseif (is_object($Val)) { - $Return[$Key] .= get_class($Val); - } elseif (is_array($Val)) { - $Return[$Key] .= '['.$this->format_args($Val).']'; - } - $LastKey = $Key; - } - return implode(', ', $Return); - } - - public function php_error_handler($Level, $Error, $File, $Line) { - //Who added this, it's still something to pay attention to... - if (stripos('Undefined index', $Error) !== false) { - //return true; - } - - $Steps = 1; //Steps to go up in backtrace, default one - $Call = ''; - $Args = ''; - $Tracer = debug_backtrace(); - - //This is in case something in this function goes wrong and we get stuck with an infinite loop - if (isset($Tracer[$Steps]['function'], $Tracer[$Steps]['class']) && $Tracer[$Steps]['function'] == 'php_error_handler' && $Tracer[$Steps]['class'] == 'DEBUG') { - return true; - } - - //If this error was thrown, we return the function which threw it - if (isset($Tracer[$Steps]['function']) && $Tracer[$Steps]['function'] == 'trigger_error') { - $Steps++; - $File = $Tracer[$Steps]['file']; - $Line = $Tracer[$Steps]['line']; - } - - //At this time ONLY Array strict typing is fully supported. - //Allow us to abuse strict typing (IE: function test(Array)) - if (preg_match('/^Argument (\d+) passed to \S+ must be an (array), (array|string|integer|double|object) given, called in (\S+) on line (\d+) and defined$/', $Error, $Matches)) { - $Error = 'Type hinting failed on arg '.$Matches[1]. ', expected '.$Matches[2].' but found '.$Matches[3]; - $File = $Matches[4]; - $Line = $Matches[5]; - } - - //Lets not be repetative - if (($Tracer[$Steps]['function'] == 'include' || $Tracer[$Steps]['function'] == 'require' ) && isset($Tracer[$Steps]['args'][0]) && $Tracer[$Steps]['args'][0] == $File) { - unset($Tracer[$Steps]['args']); - } - - //Class - if (isset($Tracer[$Steps]['class'])) { - $Call .= $Tracer[$Steps]['class'].'::'; - } - - //Function & args - if (isset($Tracer[$Steps]['function'])) { - $Call .= $Tracer[$Steps]['function']; - if (isset($Tracer[$Steps]['args'][0])) { - $Args = $this->format_args($Tracer[$Steps]['args']); - } - } - - //Shorten the path & we're done - $File = str_replace(SERVER_ROOT, '', $File); - $Error = str_replace(SERVER_ROOT, '', $Error); - - if (DEBUG_WARNINGS) { - $this->Errors[] = array($Error, $File.':'.$Line, $Call, $Args); - } - return true; - } - - /* Data wrappers */ - - public function get_perf() { - if (empty($this->Perf)) { - global $ScriptStartTime; - $PageTime = (microtime(true) - $ScriptStartTime); - $CPUTime = $this->get_cpu_time(); - $Perf = array( - 'Memory usage' => Format::get_size(memory_get_usage(true)), - 'Page process time' => number_format($PageTime, 3).' s'); - if ($CPUTime) { - $Perf['CPU time'] = number_format($CPUTime / 1000000, 3).' s'; - } - return $Perf; - } - return $this->Perf; - } - - public function get_flags() { - return $this->Flags; - } - - public function get_errors($Light = false) { - //Because the cache can't take some of these variables - if ($Light) { - foreach ($this->Errors as $Key => $Value) { - $this->Errors[$Key][3] = ''; - } - } - return $this->Errors; - } - - public function get_constants() { - return get_defined_constants(true); - } - - public function get_classes() { - foreach (get_declared_classes() as $Class) { - $Classes[$Class]['Vars'] = get_class_vars($Class); - $Classes[$Class]['Functions'] = get_class_methods($Class); - } - return $Classes; - } - - public function get_extensions() { - foreach (get_loaded_extensions() as $Extension) { - $Extensions[$Extension]['Functions'] = get_extension_funcs($Extension); - } - return $Extensions; - } - - public function get_includes() { - return get_included_files(); - } - - public function get_cache_time() { - return G::$Cache->Time; - } - - public function get_cache_keys() { - return array_keys(G::$Cache->CacheHits); - } - - public function get_sphinxql_queries() { - if (class_exists('Sphinxql')) { - return Sphinxql::$Queries; - } - } - - public function get_sphinxql_time() { - if (class_exists('Sphinxql')) { - return Sphinxql::$Time; - } - } - - public function get_queries() { - return G::$DB->Queries; - } - - public function get_query_time() { - return G::$DB->Time; - } - - public function get_logged_vars() { - return $this->LoggedVars; - } - - public function get_ocelot_requests() { - if (class_exists('Tracker')) { - return Tracker::$Requests; - } - } - - /* Output Formatting */ - - public function perf_table($Perf = false) { - if (!is_array($Perf)) { - $Perf = $this->get_perf(); - } - if (empty($Perf)) { - return; - } + public $Errors = []; + public $Flags = []; + public $Perf = []; + private $LoggedVars = []; + + public function profile($Automatic = '') { + global $ScriptStartTime; + $Reason = []; + + if (!empty($Automatic)) { + $Reason[] = $Automatic; + } + + $Micro = (microtime(true) - $ScriptStartTime) * 1000; + if ($Micro > MAX_TIME && !defined('TIME_EXCEPTION')) { + $Reason[] = number_format($Micro, 3).' ms'; + } + + $Errors = count($this->get_errors()); + if ($Errors > MAX_ERRORS && !defined('ERROR_EXCEPTION')) { + $Reason[] = $Errors.' PHP errors'; + } + /* + $Queries = count($this->get_queries()); + if ($Queries > MAX_QUERIES && !defined('QUERY_EXCEPTION')) { + $Reason[] = $Queries.' Queries'; + } + */ + $Ram = memory_get_usage(true); + if ($Ram > MAX_MEMORY && !defined('MEMORY_EXCEPTION')) { + $Reason[] = Format::get_size($Ram).' RAM used'; + } + + G::$DB->warnings(); // see comment in MYSQL::query + /*$Queries = $this->get_queries(); + $DBWarningCount = 0; + foreach ($Queries as $Query) { + if (!empty($Query[2])) { + $DBWarningCount += count($Query[2]); + } + } + if ($DBWarningCount) { + $Reason[] = $DBWarningCount . ' DB warning(s)'; + }*/ + + $CacheStatus = G::$Cache->server_status(); + if (in_array(0, $CacheStatus) && !G::$Cache->get_value('cache_fail_reported')) { + // Limit to max one report every 15 minutes to avoid massive debug spam + G::$Cache->cache_value('cache_fail_reported', true, 900); + $Reason[] = "Cache server error"; + } + + if (isset($_REQUEST['profile'])) { + $Reason[] = 'Requested by ' . G::$LoggedUser['Username']; + } + + $this->Perf['Memory usage'] = (($Ram>>10) / 1024).' MB'; + $this->Perf['Page process time'] = number_format($Micro / 1000, 3).' s'; + $this->Perf['CPU time'] = number_format($this->get_cpu_time() / 1000000, 3).' s'; + + if (isset($Reason[0])) { + $this->log_var($CacheStatus, 'Cache server status'); + $this->analysis(implode(', ', $Reason)); + return true; + } + + return false; + } + + public function analysis($Message, $Report = '', $Time = 43200) { + global $Document; + if (empty($Report)) { + $Report = $Message; + } + $Identifier = Users::make_secret(5); + G::$Cache->cache_value( + 'analysis_'.$Identifier, + array( + 'url' => $_SERVER['REQUEST_URI'], + 'message' => $Report, + 'errors' => $this->get_errors(true), + 'queries' => $this->get_queries(), + 'flags' => $this->get_flags(), + 'includes' => $this->get_includes(), + 'cache' => $this->get_cache_keys(), + 'vars' => $this->get_logged_vars(), + 'perf' => $this->get_perf(), + 'ocelot' => $this->get_ocelot_requests() + ), + $Time + ); + $RequestURI = !empty($_SERVER['REQUEST_URI']) ? substr($_SERVER['REQUEST_URI'], 1) : ''; + send_irc('PRIVMSG '.LAB_CHAN." :{$Message} $Document ".site_url()."tools.php?action=analysis&case=$Identifier ".site_url().$RequestURI); + } + + public function get_cpu_time() { + if (!defined('PHP_WINDOWS_VERSION_MAJOR')) { + global $CPUTimeStart; + $RUsage = getrusage(); + $CPUTime = $RUsage['ru_utime.tv_sec'] * 1000000 + $RUsage['ru_utime.tv_usec'] - $CPUTimeStart; + return $CPUTime; + } + return false; + } + + public function log_var($Var, $VarName = false) { + $BackTrace = debug_backtrace(); + $ID = Users::make_secret(5); + if (!$VarName) { + $VarName = $ID; + } + $File = array('path' => substr($BackTrace[0]['file'], strlen(SERVER_ROOT)), 'line' => $BackTrace[0]['line']); + $this->LoggedVars[$ID] = array($VarName => array('bt' => $File, 'data' => $Var)); + } + + public function set_flag($Event) { + global $ScriptStartTime; + $this->Flags[] = array($Event, (microtime(true) - $ScriptStartTime) * 1000, memory_get_usage(true), $this->get_cpu_time()); + } + + //This isn't in the constructor because $this is not available, and the function cannot be made static + public function handle_errors() { + //error_reporting(E_ALL ^ E_STRICT | E_WARNING | E_DEPRECATED | E_ERROR | E_PARSE); //E_STRICT disabled + error_reporting(E_WARNING | E_ERROR | E_PARSE); + set_error_handler(array($this, 'php_error_handler')); + } + + protected function format_args($Array) { + $LastKey = -1; + $Return = []; + foreach ($Array as $Key => $Val) { + $Return[$Key] = ''; + if (!is_numeric($Key) || !is_numeric($LastKey) || $Key != $LastKey + 1) { + $Return[$Key] .= "'$Key' => "; + } + if ($Val === true) { + $Return[$Key] .= 'true'; + } elseif ($Val === false) { + $Return[$Key] .= 'false'; + } elseif (is_numeric($Val)) { + $Return[$Key] .= $Val; + } elseif (is_string($Val)) { + $Return[$Key] .= "'$Val'"; + } elseif (is_object($Val)) { + $Return[$Key] .= get_class($Val); + } elseif (is_array($Val)) { + $Return[$Key] .= '['.$this->format_args($Val).']'; + } + $LastKey = $Key; + } + return implode(', ', $Return); + } + + public function php_error_handler($Level, $Error, $File, $Line) { + //Who added this, it's still something to pay attention to... + if (stripos('Undefined index', $Error) !== false) { + //return true; + } + + $Steps = 1; //Steps to go up in backtrace, default one + $Call = ''; + $Args = ''; + $Tracer = debug_backtrace(); + + //This is in case something in this function goes wrong and we get stuck with an infinite loop + if (isset($Tracer[$Steps]['function'], $Tracer[$Steps]['class']) && $Tracer[$Steps]['function'] == 'php_error_handler' && $Tracer[$Steps]['class'] == 'DEBUG') { + return true; + } + + //If this error was thrown, we return the function which threw it + if (isset($Tracer[$Steps]['function']) && $Tracer[$Steps]['function'] == 'trigger_error') { + $Steps++; + $File = $Tracer[$Steps]['file']; + $Line = $Tracer[$Steps]['line']; + } + + //At this time ONLY Array strict typing is fully supported. + //Allow us to abuse strict typing (IE: function test(Array)) + if (preg_match('/^Argument (\d+) passed to \S+ must be an (array), (array|string|integer|double|object) given, called in (\S+) on line (\d+) and defined$/', $Error, $Matches)) { + $Error = 'Type hinting failed on arg '.$Matches[1]. ', expected '.$Matches[2].' but found '.$Matches[3]; + $File = $Matches[4]; + $Line = $Matches[5]; + } + + //Lets not be repetative + if (($Tracer[$Steps]['function'] == 'include' || $Tracer[$Steps]['function'] == 'require' ) && isset($Tracer[$Steps]['args'][0]) && $Tracer[$Steps]['args'][0] == $File) { + unset($Tracer[$Steps]['args']); + } + + //Class + if (isset($Tracer[$Steps]['class'])) { + $Call .= $Tracer[$Steps]['class'].'::'; + } + + //Function & args + if (isset($Tracer[$Steps]['function'])) { + $Call .= $Tracer[$Steps]['function']; + if (isset($Tracer[$Steps]['args'][0])) { + $Args = $this->format_args($Tracer[$Steps]['args']); + } + } + + //Shorten the path & we're done + $File = str_replace(SERVER_ROOT, '', $File); + $Error = str_replace(SERVER_ROOT, '', $Error); + + if (DEBUG_WARNINGS) { + $this->Errors[] = array($Error, $File.':'.$Line, $Call, $Args); + } + return true; + } + + /* Data wrappers */ + + public function get_perf() { + if (empty($this->Perf)) { + global $ScriptStartTime; + $PageTime = (microtime(true) - $ScriptStartTime); + $CPUTime = $this->get_cpu_time(); + $Perf = array( + 'Memory usage' => Format::get_size(memory_get_usage(true)), + 'Page process time' => number_format($PageTime, 3).' s'); + if ($CPUTime) { + $Perf['CPU time'] = number_format($CPUTime / 1000000, 3).' s'; + } + return $Perf; + } + return $this->Perf; + } + + public function get_flags() { + return $this->Flags; + } + + public function get_errors($Light = false) { + //Because the cache can't take some of these variables + if ($Light) { + foreach ($this->Errors as $Key => $Value) { + $this->Errors[$Key][3] = ''; + } + } + return $this->Errors; + } + + public function get_constants() { + return get_defined_constants(true); + } + + public function get_classes() { + foreach (get_declared_classes() as $Class) { + $Classes[$Class]['Vars'] = get_class_vars($Class); + $Classes[$Class]['Functions'] = get_class_methods($Class); + } + return $Classes; + } + + public function get_extensions() { + foreach (get_loaded_extensions() as $Extension) { + $Extensions[$Extension]['Functions'] = get_extension_funcs($Extension); + } + return $Extensions; + } + + public function get_includes() { + return get_included_files(); + } + + public function get_cache_time() { + return G::$Cache->Time; + } + + public function get_cache_keys() { + return array_keys(G::$Cache->CacheHits); + } + + public function get_sphinxql_queries() { + if (class_exists('Sphinxql')) { + return Sphinxql::$Queries; + } + } + + public function get_sphinxql_time() { + if (class_exists('Sphinxql')) { + return Sphinxql::$Time; + } + } + + public function get_queries() { + return G::$DB->Queries; + } + + public function get_query_time() { + return G::$DB->Time; + } + + public function get_logged_vars() { + return $this->LoggedVars; + } + + public function get_ocelot_requests() { + if (class_exists('Tracker')) { + return Tracker::$Requests; + } + } + + /* Output Formatting */ + + public function perf_table($Perf = false) { + if (!is_array($Perf)) { + $Perf = $this->get_perf(); + } + if (empty($Perf)) { + return; + } ?> - - - - -
    View Performance Statistics:
    - - $Value) { + + + + +
    View Performance Statistics:
    + + $Value) { ?> - - - - - + + + + - -get_includes(); - } + +get_includes(); + } ?> - - - - -
    View Includes:
    - - + + + + + + - - - - + + + - -get_classes(); - } + +get_classes(); + } ?> - - - - -
    View Classes:
    - - - - - - + + View Classes: + + + + + + + + - - - - -
    View Extensions:
    - - - - - -get_flags(); - } - if (empty($Flags)) { - return; - } + + + + +
    View Extensions:
    + + + + + +get_flags(); + } + if (empty($Flags)) { + return; + } ?> - - - - -
    View Flags:
    - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - -get_constants(); - } + + + ms + + ms + + + + + +get_constants(); + } ?> - - - - -
    View Constants:
    - - - - - -get_ocelot_requests(); - } - if (empty($OcelotRequests)) { - return; - } + + + + +
    View Constants:
    + + + + + +get_ocelot_requests(); + } + if (empty($OcelotRequests)) { + return; + } ?> - - - - -
    View Ocelot requests:
    - - $Request) { ?> - - - - - - - -get_cache_keys(); - $Header .= ' ('.number_format($this->get_cache_time(), 5).' ms)'; - } - if (empty($CacheKeys)) { - return; - } - $Header = ' '.number_format(count($CacheKeys))." $Header:"; + + + + +
    View Ocelot requests:
    + + $Request) { ?> + + + + + + + +get_cache_keys(); + $Header .= ' ('.number_format($this->get_cache_time(), 5).' ms)'; + } + if (empty($CacheKeys)) { + return; + } + $Header = ' '.number_format(count($CacheKeys))." $Header:"; ?> - - - - -
    View
    - - - - - - - - -get_errors(); - } - if (empty($Errors)) { - return; - } + + + + +
    View
    + + + + + + + + +get_errors(); + } + if (empty($Errors)) { + return; + } ?> - - - - -
    View Errors:
    - - + + + + + + - - - - - - - -get_queries(); - $Header .= ' ('.number_format($this->get_query_time(), 5).' ms)'; - } - if (empty($Queries)) { - return; - } - $Header = ' '.number_format(count($Queries))." $Header:"; + + + () + + + + + + + + + + +get_queries(); + $Header .= ' ('.number_format($this->get_query_time(), 5).' ms)'; + } + if (empty($Queries)) { + return; + } + $Header = ' '.number_format(count($Queries))." $Header:"; ?> - - - - -
    View
    - -', $Warnings); - } + + + + +
    View
    + +', $Warnings); + } ?> - - - - - - - -get_sphinxql_queries(); - $Header .= ' ('.number_format($this->get_sphinxql_time(), 5).' ms)'; - } - if (empty($Queries)) { - return; - } - $Header = ' '.number_format(count($Queries))." $Header:"; + +
    + ms + + + + +get_sphinxql_queries(); + $Header .= ' ('.number_format($this->get_sphinxql_time(), 5).' ms)'; + } + if (empty($Queries)) { + return; + } + $Header = ' '.number_format(count($Queries))." $Header:"; ?> - - - - -
    View
    - - + + + + + + - - - - - - -LoggedVars)) { - return; - } - $Vars = $this->LoggedVars; - } - $Header = ' '.number_format(count($Vars))." $Header:"; + +
    + ms + + + +LoggedVars)) { + return; + } + $Vars = $this->LoggedVars; + } + $Header = ' '.number_format(count($Vars))." $Header:"; ?> - - - - -
    View
    - - $Var) { - list($Key, $Data) = each($Var); - $Size = count($Data['data']); + + + + +
    View
    + + $Var) { + list($Key, $Data) = each($Var); + $Size = count($Data['data']); ?> - - - - - - - + + () +
    + + + + + + + + $Source, - "Price" => $DonationAmount, - "Currency" => $Currency, - "Source" => $Source, - "Reason" => $Reason, - "SendPM" => true)); - } - - public static function donate($UserID, $Args) { - $UserID = (int)$UserID; - $QueryID = G::$DB->get_query_id(); - - G::$DB->query(" - SELECT 1 - FROM users_main - WHERE ID = '$UserID' - LIMIT 1"); - if (G::$DB->has_results()) { - G::$Cache->InternalCache = false; - foreach ($Args as &$Arg) { - $Arg = db_string($Arg); - } - extract($Args); - - // We don't always get a date passed in. - if (empty($Date)) { - $Date = sqltime(); - } - - // Get the ID of the staff member making the edit - $AddedBy = 0; - if (!self::$IsSchedule) { - $AddedBy = G::$LoggedUser['ID']; - } - - // Legacy donor, should remove at some point - G::$DB->query(" - UPDATE users_info - SET Donor = '1' - WHERE UserID = '$UserID'"); - // Give them the extra invite - $ExtraInvite = G::$DB->affected_rows(); - - // A staff member is directly manipulating donor points - if (isset($Manipulation) && $Manipulation === "Direct") { - $DonorPoints = $Rank; - $AdjustedRank = $Rank >= MAX_EXTRA_RANK ? MAX_EXTRA_RANK : $Rank; - G::$DB->query(" - INSERT INTO users_donor_ranks - (UserID, Rank, TotalRank, DonationTime, RankExpirationTime) - VALUES - ('$UserID', '$AdjustedRank', '$TotalRank', '$Date', NOW()) - ON DUPLICATE KEY UPDATE - Rank = '$AdjustedRank', - TotalRank = '$TotalRank', - DonationTime = '$Date', - RankExpirationTime = NOW()"); - } else { - // Donations from the store get donor points directly, no need to calculate them - if ($Source == "Store Parser") { - $ConvertedPrice = self::currency_exchange($Amount * $Price, $Currency); - } else { - $ConvertedPrice = self::currency_exchange($Price, $Currency); - $DonorPoints = self::calculate_rank($ConvertedPrice); - } - $IncreaseRank = $DonorPoints; - - // Rank is the same thing as DonorPoints - $CurrentRank = self::get_rank($UserID); - // A user's donor rank can never exceed MAX_EXTRA_RANK - // If the amount they donated causes it to overflow, chnage it to MAX_EXTRA_RANK - // The total rank isn't affected by this, so their original donor point value is added to it - if (($CurrentRank + $DonorPoints) >= MAX_EXTRA_RANK) { - $AdjustedRank = MAX_EXTRA_RANK; - } else { - $AdjustedRank = $CurrentRank + $DonorPoints; - } - G::$DB->query(" - INSERT INTO users_donor_ranks - (UserID, Rank, TotalRank, DonationTime, RankExpirationTime) - VALUES - ('$UserID', '$AdjustedRank', '$DonorPoints', '$Date', NOW()) - ON DUPLICATE KEY UPDATE - Rank = '$AdjustedRank', - TotalRank = TotalRank + '$DonorPoints', - DonationTime = '$Date', - RankExpirationTime = NOW()"); - } - // Donor cache key is outdated - G::$Cache->delete_value("donor_info_$UserID"); - - // Get their rank - $Rank = self::get_rank($UserID); - $TotalRank = self::get_total_rank($UserID); - - // Now that their rank and total rank has been set, we can calculate their special rank - self::calculate_special_rank($UserID); - - // Hand out invites - G::$DB->query(" - SELECT InvitesRecievedRank - FROM users_donor_ranks - WHERE UserID = '$UserID'"); - list($InvitesRecievedRank) = G::$DB->next_record(); - $AdjustedRank = $Rank >= MAX_RANK ? (MAX_RANK - 1) : $Rank; - $InviteRank = $AdjustedRank - $InvitesRecievedRank; - if ($InviteRank > 0) { - $Invites = $ExtraInvite ? ($InviteRank + 1) : $InviteRank; - G::$DB->query(" - UPDATE users_main - SET Invites = Invites + '$Invites' - WHERE ID = $UserID"); - G::$DB->query(" - UPDATE users_donor_ranks - SET InvitesRecievedRank = '$AdjustedRank' - WHERE UserID = '$UserID'"); - } - - // Send them a thank you PM - if ($SendPM) { - $Subject = "Your contribution has been received and credited. Thank you!"; - $Body = self::get_pm_body($Source, $Currency, $Price, $IncreaseRank, $Rank); - Misc::send_pm($UserID, 0, $Subject, $Body); - } - - // Lastly, add this donation to our history - G::$DB->query(" - INSERT INTO donations - (UserID, Amount, Source, Reason, Currency, Email, Time, AddedBy, Rank, TotalRank) - VALUES - ('$UserID', '$ConvertedPrice', '$Source', '$Reason', '$Currency', '', '$Date', '$AddedBy', '$DonorPoints', '$TotalRank')"); - - - // Clear their user cache keys because the users_info values has been modified - G::$Cache->delete_value("user_info_$UserID"); - G::$Cache->delete_value("user_info_heavy_$UserID"); - G::$Cache->delete_value("donor_info_$UserID"); - - } - G::$DB->set_query_id($QueryID); - } - - private static function calculate_special_rank($UserID) { - $UserID = (int)$UserID; - $QueryID = G::$DB->get_query_id(); - // Are they are special? - G::$DB->query(" - SELECT TotalRank, SpecialRank - FROM users_donor_ranks - WHERE UserID = '$UserID'"); - if (G::$DB->has_results()) { - // Adjust their special rank depending on the total rank. - list($TotalRank, $SpecialRank) = G::$DB->next_record(); - if ($TotalRank < 10) { - $SpecialRank = 0; - } - if ($SpecialRank < 1 && $TotalRank >= 10) { - Misc::send_pm($UserID, 0, "You've Reached Special Donor Rank #1! You've Earned: One User Pick. Details Inside.", self::get_special_rank_one_pm()); - $SpecialRank = 1; - } - if ($SpecialRank < 2 && $TotalRank >= 20) { - Misc::send_pm($UserID, 0, "You've Reached Special Donor Rank #2! You've Earned: The Double-Avatar. Details Inside.", self::get_special_rank_two_pm()); - $SpecialRank = 2; - } - if ($SpecialRank < 3 && $TotalRank >= 50) { - Misc::send_pm($UserID, 0, "You've Reached Special Donor Rank #3! You've Earned: Diamond Rank. Details Inside.", self::get_special_rank_three_pm()); - $SpecialRank = 3; - } - // Make them special - G::$DB->query(" - UPDATE users_donor_ranks - SET SpecialRank = '$SpecialRank' - WHERE UserID = '$UserID'"); - G::$Cache->delete_value("donor_info_$UserID"); - } - G::$DB->set_query_id($QueryID); - } - - public static function schedule() { - self::$IsSchedule = true; - - DonationsBitcoin::find_new_donations(); - self::expire_ranks(); - self::get_new_conversion_rates(); - } - - public static function expire_ranks() { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT UserID, Rank - FROM users_donor_ranks - WHERE Rank > 1 - AND SpecialRank != 3 - AND RankExpirationTime < NOW() - INTERVAL 766 HOUR"); - // 2 hours less than 32 days to account for schedule run times - - if (G::$DB->record_count() > 0) { - $UserIDs = array(); - while (list($UserID, $Rank) = G::$DB->next_record()) { - G::$Cache->delete_value("donor_info_$UserID"); - G::$Cache->delete_value("donor_title_$UserID"); - G::$Cache->delete_value("donor_profile_rewards_$UserID"); - $UserIDs[] = $UserID; - } - $In = implode(',', $UserIDs); - G::$DB->query(" - UPDATE users_donor_ranks - SET Rank = Rank - IF(Rank = " . MAX_RANK . ", 2, 1), RankExpirationTime = NOW() - WHERE UserID IN ($In)"); - } - G::$DB->set_query_id($QueryID); - } - - private static function calculate_rank($Amount) { - return floor($Amount / 5); - } - - public static function update_rank($UserID, $Rank, $TotalRank, $Reason) { - $Rank = (int)$Rank; - $TotalRank = (int)$TotalRank; - - self::donate($UserID, array( - "Manipulation" => "Direct", - "Rank" => $Rank, - "TotalRank" => $TotalRank, - "Reason" => $Reason, - "Source" => "Modify Values", - "Currency" => "EUR")); - } - - public static function hide_stats($UserID) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - INSERT INTO users_donor_ranks - (UserID, Hidden) - VALUES - ('$UserID', '1') - ON DUPLICATE KEY UPDATE - Hidden = '1'"); - G::$DB->set_query_id($QueryID); - } - - public static function show_stats($UserID) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - INSERT INTO users_donor_ranks - (UserID, Hidden) - VALUES - ('$UserID', '0') - ON DUPLICATE KEY UPDATE - Hidden = '0'"); - G::$DB->set_query_id($QueryID); - } - - public static function is_visible($UserID) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT Hidden - FROM users_donor_ranks - WHERE Hidden = '0' - AND UserID = '$UserID'"); - $HasResults = G::$DB->has_results(); - G::$DB->set_query_id($QueryID); - return $HasResults; - } - - public static function has_donor_forum($UserID) { - return self::get_rank($UserID) >= DONOR_FORUM_RANK || self::get_special_rank($UserID) >= MAX_SPECIAL_RANK; - } - - /** - * Put all the common donor info in the same cache key to save some cache calls - */ - public static function get_donor_info($UserID) { - // Our cache class should prevent identical memcached requests - $DonorInfo = G::$Cache->get_value("donor_info_$UserID"); - if ($DonorInfo === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - Rank, - SpecialRank, - TotalRank, - DonationTime, - RankExpirationTime + INTERVAL 766 HOUR - FROM users_donor_ranks - WHERE UserID = '$UserID'"); - // 2 hours less than 32 days to account for schedule run times - if (G::$DB->has_results()) { - list($Rank, $SpecialRank, $TotalRank, $DonationTime, $ExpireTime) = G::$DB->next_record(MYSQLI_NUM, false); - if ($DonationTime === null) { - $DonationTime = 0; - } - if ($ExpireTime === null) { - $ExpireTime = 0; - } - } else { - $Rank = $SpecialRank = $TotalRank = $DonationTime = $ExpireTime = 0; - } - if (Permissions::is_mod($UserID)) { - $Rank = MAX_EXTRA_RANK; - $SpecialRank = MAX_SPECIAL_RANK; - } - G::$DB->query(" - SELECT - IconMouseOverText, - AvatarMouseOverText, - CustomIcon, - CustomIconLink, - SecondAvatar - FROM donor_rewards - WHERE UserID = '$UserID'"); - $Rewards = G::$DB->next_record(MYSQLI_ASSOC); - G::$DB->set_query_id($QueryID); - - $DonorInfo = array( - 'Rank' => (int)$Rank, - 'SRank' => (int)$SpecialRank, - 'TotRank' => (int)$TotalRank, - 'Time' => $DonationTime, - 'ExpireTime' => $ExpireTime, - 'Rewards' => $Rewards); - G::$Cache->cache_value("donor_info_$UserID", $DonorInfo, 0); - } - return $DonorInfo; - } - - public static function get_rank($UserID) { - return self::get_donor_info($UserID)['Rank']; - } - - public static function get_special_rank($UserID) { - return self::get_donor_info($UserID)['SRank']; - } - - public static function get_total_rank($UserID) { - return self::get_donor_info($UserID)['TotRank']; - } - - public static function get_donation_time($UserID) { - return self::get_donor_info($UserID)['Time']; - } - - public static function get_personal_collages($UserID) { - $DonorInfo = self::get_donor_info($UserID); - if ($DonorInfo['SRank'] == MAX_SPECIAL_RANK) { - $Collages = 5; - } else { - $Collages = min($DonorInfo['Rank'], 5); // One extra collage per donor rank up to 5 - } - return $Collages; - } - - public static function get_titles($UserID) { - $Results = G::$Cache->get_value("donor_title_$UserID"); - if ($Results === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT Prefix, Suffix, UseComma - FROM donor_forum_usernames - WHERE UserID = '$UserID'"); - $Results = G::$DB->next_record(); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value("donor_title_$UserID", $Results, 0); - } - return $Results; - } - - - - public static function get_enabled_rewards($UserID) { - $Rewards = array(); - $Rank = self::get_rank($UserID); - $SpecialRank = self::get_special_rank($UserID); - $HasAll = $SpecialRank == 3; - - $Rewards = array( - 'HasAvatarMouseOverText' => false, - 'HasCustomDonorIcon' => false, - 'HasDonorForum' => false, - 'HasDonorIconLink' => false, - 'HasDonorIconMouseOverText' => false, - 'HasProfileInfo1' => false, - 'HasProfileInfo2' => false, - 'HasProfileInfo3' => false, - 'HasProfileInfo4' => false, - 'HasSecondAvatar' => false); - -// if ($Rank >= 1 || $HasAll) { + private static $ForumDescriptions = array( + "I want only two houses, rather than seven... I feel like letting go of things", + "A billion here, a billion there, sooner or later it adds up to real money.", + "I've cut back, because I'm buying a house in the West Village.", + "Some girls are just born with glitter in their veins.", + "I get half a million just to show up at parties. My life is, like, really, really fun.", + "Some people change when they think they're a star or something", + "I'd rather not talk about money. It’s kind of gross.", + "I have not been to my house in Bermuda for two or three years, and the same goes for my house in Portofino. How long do I have to keep leading this life of sacrifice?", + "When I see someone who is making anywhere from $300,000 to $750,000 a year, that's middle class.", + "Money doesn't make you happy. I now have $50 million but I was just as happy when I had $48 million.", + "I'd rather smoke crack than eat cheese from a tin.", + "I am who I am. I can’t pretend to be somebody who makes $25,000 a year.", + "A girl never knows when she might need a couple of diamonds at ten 'o' clock in the morning.", + "I wouldn't run for president. I wouldn't want to move to a smaller house.", + "I have the stardom glow.", + "What's Walmart? Do they like, sell wall stuff?", + "Whenever I watch TV and see those poor starving kids all over the world, I can't help but cry. I mean I'd love to be skinny like that, but not with all those flies and death and stuff.", + "Too much money ain't enough money.", + "What's a soup kitchen?", + "I work very hard and I’m worth every cent!", + "To all my Barbies out there who date Benjamin Franklin, George Washington, Abraham Lincoln, you'll be better off in life. Get that money." + ); + + private static $IsSchedule = false; + + public static function regular_donate($UserID, $DonationAmount, $Source, $Reason, $Currency = "EUR") { + self::donate($UserID, array( + "Source" => $Source, + "Price" => $DonationAmount, + "Currency" => $Currency, + "Source" => $Source, + "Reason" => $Reason, + "SendPM" => true)); + } + + public static function donate($UserID, $Args) { + $UserID = (int)$UserID; + $QueryID = G::$DB->get_query_id(); + + G::$DB->query(" + SELECT 1 + FROM users_main + WHERE ID = '$UserID' + LIMIT 1"); + if (G::$DB->has_results()) { + G::$Cache->InternalCache = false; + foreach ($Args as &$Arg) { + $Arg = db_string($Arg); + } + extract($Args); + + // We don't always get a date passed in. + if (empty($Date)) { + $Date = sqltime(); + } + + // Get the ID of the staff member making the edit + $AddedBy = 0; + if (!self::$IsSchedule) { + $AddedBy = G::$LoggedUser['ID']; + } + + // Legacy donor, should remove at some point + G::$DB->query(" + UPDATE users_info + SET Donor = '1' + WHERE UserID = '$UserID'"); + // Give them the extra invite + $ExtraInvite = G::$DB->affected_rows(); + + // A staff member is directly manipulating donor points + if (isset($Manipulation) && $Manipulation === "Direct") { + $DonorPoints = $Rank; + $AdjustedRank = $Rank >= MAX_EXTRA_RANK ? MAX_EXTRA_RANK : $Rank; + G::$DB->query(" + INSERT INTO users_donor_ranks + (UserID, Rank, TotalRank, DonationTime, RankExpirationTime) + VALUES + ('$UserID', '$AdjustedRank', '$TotalRank', '$Date', NOW()) + ON DUPLICATE KEY UPDATE + Rank = '$AdjustedRank', + TotalRank = '$TotalRank', + DonationTime = '$Date', + RankExpirationTime = NOW()"); + } else { + // Donations from the store get donor points directly, no need to calculate them + if ($Source == "Store Parser") { + $ConvertedPrice = self::currency_exchange($Amount * $Price, $Currency); + } else { + $ConvertedPrice = self::currency_exchange($Price, $Currency); + $DonorPoints = self::calculate_rank($ConvertedPrice); + } + $IncreaseRank = $DonorPoints; + + // Rank is the same thing as DonorPoints + $CurrentRank = self::get_rank($UserID); + // A user's donor rank can never exceed MAX_EXTRA_RANK + // If the amount they donated causes it to overflow, chnage it to MAX_EXTRA_RANK + // The total rank isn't affected by this, so their original donor point value is added to it + if (($CurrentRank + $DonorPoints) >= MAX_EXTRA_RANK) { + $AdjustedRank = MAX_EXTRA_RANK; + } else { + $AdjustedRank = $CurrentRank + $DonorPoints; + } + G::$DB->query(" + INSERT INTO users_donor_ranks + (UserID, Rank, TotalRank, DonationTime, RankExpirationTime) + VALUES + ('$UserID', '$AdjustedRank', '$DonorPoints', '$Date', NOW()) + ON DUPLICATE KEY UPDATE + Rank = '$AdjustedRank', + TotalRank = TotalRank + '$DonorPoints', + DonationTime = '$Date', + RankExpirationTime = NOW()"); + } + // Donor cache key is outdated + G::$Cache->delete_value("donor_info_$UserID"); + + // Get their rank + $Rank = self::get_rank($UserID); + $TotalRank = self::get_total_rank($UserID); + + // Now that their rank and total rank has been set, we can calculate their special rank + self::calculate_special_rank($UserID); + + // Hand out invites + G::$DB->query(" + SELECT InvitesRecievedRank + FROM users_donor_ranks + WHERE UserID = '$UserID'"); + list($InvitesRecievedRank) = G::$DB->next_record(); + $AdjustedRank = $Rank >= MAX_RANK ? (MAX_RANK - 1) : $Rank; + $InviteRank = $AdjustedRank - $InvitesRecievedRank; + if ($InviteRank > 0) { + $Invites = $ExtraInvite ? ($InviteRank + 1) : $InviteRank; + G::$DB->query(" + UPDATE users_main + SET Invites = Invites + '$Invites' + WHERE ID = $UserID"); + G::$DB->query(" + UPDATE users_donor_ranks + SET InvitesRecievedRank = '$AdjustedRank' + WHERE UserID = '$UserID'"); + } + + // Send them a thank you PM + if ($SendPM) { + $Subject = "Your contribution has been received and credited. Thank you!"; + $Body = self::get_pm_body($Source, $Currency, $Price, $IncreaseRank, $Rank); + Misc::send_pm($UserID, 0, $Subject, $Body); + } + + // Lastly, add this donation to our history + G::$DB->query(" + INSERT INTO donations + (UserID, Amount, Source, Reason, Currency, Email, Time, AddedBy, Rank, TotalRank) + VALUES + ('$UserID', '$ConvertedPrice', '$Source', '$Reason', '$Currency', '', '$Date', '$AddedBy', '$DonorPoints', '$TotalRank')"); + + + // Clear their user cache keys because the users_info values has been modified + G::$Cache->delete_value("user_info_$UserID"); + G::$Cache->delete_value("user_info_heavy_$UserID"); + G::$Cache->delete_value("donor_info_$UserID"); + + } + G::$DB->set_query_id($QueryID); + } + + private static function calculate_special_rank($UserID) { + $UserID = (int)$UserID; + $QueryID = G::$DB->get_query_id(); + // Are they are special? + G::$DB->query(" + SELECT TotalRank, SpecialRank + FROM users_donor_ranks + WHERE UserID = '$UserID'"); + if (G::$DB->has_results()) { + // Adjust their special rank depending on the total rank. + list($TotalRank, $SpecialRank) = G::$DB->next_record(); + if ($TotalRank < 10) { + $SpecialRank = 0; + } + if ($SpecialRank < 1 && $TotalRank >= 10) { + Misc::send_pm($UserID, 0, "You've Reached Special Donor Rank #1! You've Earned: One User Pick. Details Inside.", self::get_special_rank_one_pm()); + $SpecialRank = 1; + } + if ($SpecialRank < 2 && $TotalRank >= 20) { + Misc::send_pm($UserID, 0, "You've Reached Special Donor Rank #2! You've Earned: The Double-Avatar. Details Inside.", self::get_special_rank_two_pm()); + $SpecialRank = 2; + } + if ($SpecialRank < 3 && $TotalRank >= 50) { + Misc::send_pm($UserID, 0, "You've Reached Special Donor Rank #3! You've Earned: Diamond Rank. Details Inside.", self::get_special_rank_three_pm()); + $SpecialRank = 3; + } + // Make them special + G::$DB->query(" + UPDATE users_donor_ranks + SET SpecialRank = '$SpecialRank' + WHERE UserID = '$UserID'"); + G::$Cache->delete_value("donor_info_$UserID"); + } + G::$DB->set_query_id($QueryID); + } + + public static function schedule() { + self::$IsSchedule = true; + + DonationsBitcoin::find_new_donations(); + self::expire_ranks(); + self::get_new_conversion_rates(); + } + + public static function expire_ranks() { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT UserID, Rank + FROM users_donor_ranks + WHERE Rank > 1 + AND SpecialRank != 3 + AND RankExpirationTime < NOW() - INTERVAL 766 HOUR"); + // 2 hours less than 32 days to account for schedule run times + + if (G::$DB->record_count() > 0) { + $UserIDs = []; + while (list($UserID, $Rank) = G::$DB->next_record()) { + G::$Cache->delete_value("donor_info_$UserID"); + G::$Cache->delete_value("donor_title_$UserID"); + G::$Cache->delete_value("donor_profile_rewards_$UserID"); + $UserIDs[] = $UserID; + } + $In = implode(',', $UserIDs); + G::$DB->query(" + UPDATE users_donor_ranks + SET Rank = Rank - IF(Rank = " . MAX_RANK . ", 2, 1), RankExpirationTime = NOW() + WHERE UserID IN ($In)"); + } + G::$DB->set_query_id($QueryID); + } + + private static function calculate_rank($Amount) { + return floor($Amount / 5); + } + + public static function update_rank($UserID, $Rank, $TotalRank, $Reason) { + $Rank = (int)$Rank; + $TotalRank = (int)$TotalRank; + + self::donate($UserID, array( + "Manipulation" => "Direct", + "Rank" => $Rank, + "TotalRank" => $TotalRank, + "Reason" => $Reason, + "Source" => "Modify Values", + "Currency" => "EUR")); + } + + public static function hide_stats($UserID) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + INSERT INTO users_donor_ranks + (UserID, Hidden) + VALUES + ('$UserID', '1') + ON DUPLICATE KEY UPDATE + Hidden = '1'"); + G::$DB->set_query_id($QueryID); + } + + public static function show_stats($UserID) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + INSERT INTO users_donor_ranks + (UserID, Hidden) + VALUES + ('$UserID', '0') + ON DUPLICATE KEY UPDATE + Hidden = '0'"); + G::$DB->set_query_id($QueryID); + } + + public static function is_visible($UserID) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT Hidden + FROM users_donor_ranks + WHERE Hidden = '0' + AND UserID = '$UserID'"); + $HasResults = G::$DB->has_results(); + G::$DB->set_query_id($QueryID); + return $HasResults; + } + + public static function has_donor_forum($UserID) { + return self::get_rank($UserID) >= DONOR_FORUM_RANK || self::get_special_rank($UserID) >= MAX_SPECIAL_RANK; + } + + /** + * Put all the common donor info in the same cache key to save some cache calls + */ + public static function get_donor_info($UserID) { + // Our cache class should prevent identical memcached requests + $DonorInfo = G::$Cache->get_value("donor_info_$UserID"); + if ($DonorInfo === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + Rank, + SpecialRank, + TotalRank, + DonationTime, + RankExpirationTime + INTERVAL 766 HOUR + FROM users_donor_ranks + WHERE UserID = '$UserID'"); + // 2 hours less than 32 days to account for schedule run times + if (G::$DB->has_results()) { + list($Rank, $SpecialRank, $TotalRank, $DonationTime, $ExpireTime) = G::$DB->next_record(MYSQLI_NUM, false); + if ($DonationTime === null) { + $DonationTime = 0; + } + if ($ExpireTime === null) { + $ExpireTime = 0; + } + } else { + $Rank = $SpecialRank = $TotalRank = $DonationTime = $ExpireTime = 0; + } + if (Permissions::is_mod($UserID)) { + $Rank = MAX_EXTRA_RANK; + $SpecialRank = MAX_SPECIAL_RANK; + } + G::$DB->query(" + SELECT + IconMouseOverText, + AvatarMouseOverText, + CustomIcon, + CustomIconLink, + SecondAvatar + FROM donor_rewards + WHERE UserID = '$UserID'"); + $Rewards = G::$DB->next_record(MYSQLI_ASSOC); + G::$DB->set_query_id($QueryID); + + $DonorInfo = array( + 'Rank' => (int)$Rank, + 'SRank' => (int)$SpecialRank, + 'TotRank' => (int)$TotalRank, + 'Time' => $DonationTime, + 'ExpireTime' => $ExpireTime, + 'Rewards' => $Rewards); + G::$Cache->cache_value("donor_info_$UserID", $DonorInfo, 0); + } + return $DonorInfo; + } + + public static function get_rank($UserID) { + return self::get_donor_info($UserID)['Rank']; + } + + public static function get_special_rank($UserID) { + return self::get_donor_info($UserID)['SRank']; + } + + public static function get_total_rank($UserID) { + return self::get_donor_info($UserID)['TotRank']; + } + + public static function get_donation_time($UserID) { + return self::get_donor_info($UserID)['Time']; + } + + public static function get_personal_collages($UserID) { + $DonorInfo = self::get_donor_info($UserID); + if ($DonorInfo['SRank'] == MAX_SPECIAL_RANK) { + $Collages = 5; + } else { + $Collages = min($DonorInfo['Rank'], 5); // One extra collage per donor rank up to 5 + } + return $Collages; + } + + public static function get_titles($UserID) { + $Results = G::$Cache->get_value("donor_title_$UserID"); + if ($Results === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT Prefix, Suffix, UseComma + FROM donor_forum_usernames + WHERE UserID = '$UserID'"); + $Results = G::$DB->next_record(); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value("donor_title_$UserID", $Results, 0); + } + return $Results; + } + + + + public static function get_enabled_rewards($UserID) { + $Rewards = []; + $Rank = self::get_rank($UserID); + $SpecialRank = self::get_special_rank($UserID); + $HasAll = $SpecialRank == 3; + + $Rewards = array( + 'HasAvatarMouseOverText' => false, + 'HasCustomDonorIcon' => false, + 'HasDonorForum' => false, + 'HasDonorIconLink' => false, + 'HasDonorIconMouseOverText' => false, + 'HasProfileInfo1' => false, + 'HasProfileInfo2' => false, + 'HasProfileInfo3' => false, + 'HasProfileInfo4' => false, + 'HasSecondAvatar' => false); + +// if ($Rank >= 1 || $HasAll) { // -// } - if ($Rank >= 2 || $HasAll) { - $Rewards["HasDonorIconMouseOverText"] = true; - $Rewards["HasProfileInfo1"] = true; - } - if ($Rank >= 3 || $HasAll) { - $Rewards["HasAvatarMouseOverText"] = true; - $Rewards["HasProfileInfo2"] = true; - } - if ($Rank >= 4 || $HasAll) { - $Rewards["HasDonorIconLink"] = true; - $Rewards["HasProfileInfo3"] = true; - } - if ($Rank >= MAX_RANK || $HasAll) { - $Rewards["HasCustomDonorIcon"] = true; - $Rewards["HasDonorForum"] = true; - $Rewards["HasProfileInfo4"] = true; - } - if ($SpecialRank >= 2) { - $Rewards["HasSecondAvatar"] = true; - } - return $Rewards; - } - - public static function get_rewards($UserID) { - return self::get_donor_info($UserID)['Rewards']; - } - - public static function get_profile_rewards($UserID) { - $Results = G::$Cache->get_value("donor_profile_rewards_$UserID"); - if ($Results === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - ProfileInfo1, - ProfileInfoTitle1, - ProfileInfo2, - ProfileInfoTitle2, - ProfileInfo3, - ProfileInfoTitle3, - ProfileInfo4, - ProfileInfoTitle4 - FROM donor_rewards - WHERE UserID = '$UserID'"); - $Results = G::$DB->next_record(); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value("donor_profile_rewards_$UserID", $Results, 0); - } - return $Results; - } - - private static function add_profile_info_reward($Counter, &$Insert, &$Values, &$Update) { - if (isset($_POST["profile_title_" . $Counter]) && isset($_POST["profile_info_" . $Counter])) { - $ProfileTitle = db_string($_POST["profile_title_" . $Counter]); - $ProfileInfo = db_string($_POST["profile_info_" . $Counter]); - $ProfileInfoTitleSQL = "ProfileInfoTitle" . $Counter; - $ProfileInfoSQL = "ProfileInfo" . $Counter; - $Insert[] = "$ProfileInfoTitleSQL"; - $Values[] = "'$ProfileInfoTitle'"; - $Update[] = "$ProfileInfoTitleSQL = '$ProfileTitle'"; - $Insert[] = "$ProfileInfoSQL"; - $Values[] = "'$ProfileInfo'"; - $Update[] = "$ProfileInfoSQL = '$ProfileInfo'"; - } - } - - - - public static function update_rewards($UserID) { - $Rank = self::get_rank($UserID); - $SpecialRank = self::get_special_rank($UserID); - $HasAll = $SpecialRank == 3; - $Counter = 0; - $Insert = array(); - $Values = array(); - $Update = array(); - - $Insert[] = "UserID"; - $Values[] = "'$UserID'"; - if ($Rank >= 1 || $HasAll) { - - } - if ($Rank >= 2 || $HasAll) { - if (isset($_POST['donor_icon_mouse_over_text'])) { - $IconMouseOverText = db_string($_POST['donor_icon_mouse_over_text']); - $Insert[] = "IconMouseOverText"; - $Values[] = "'$IconMouseOverText'"; - $Update[] = "IconMouseOverText = '$IconMouseOverText'"; - } - $Counter++; - } - if ($Rank >= 3 || $HasAll) { - if (isset($_POST['avatar_mouse_over_text'])) { - $AvatarMouseOverText = db_string($_POST['avatar_mouse_over_text']); - $Insert[] = "AvatarMouseOverText"; - $Values[] = "'$AvatarMouseOverText'"; - $Update[] = "AvatarMouseOverText = '$AvatarMouseOverText'"; - } - $Counter++; - } - if ($Rank >= 4 || $HasAll) { - if (isset($_POST['donor_icon_link'])) { - $CustomIconLink = db_string($_POST['donor_icon_link']); - if (!Misc::is_valid_url($CustomIconLink)) { - $CustomIconLink = ''; - } - $Insert[] = "CustomIconLink"; - $Values[] = "'$CustomIconLink'"; - $Update[] = "CustomIconLink = '$CustomIconLink'"; - } - $Counter++; - } - if ($Rank >= MAX_RANK || $HasAll) { - if (isset($_POST['donor_icon_custom_url'])) { - $CustomIcon = db_string($_POST['donor_icon_custom_url']); - if (!Misc::is_valid_url($CustomIcon)) { - $CustomIcon = ''; - } - $Insert[] = "CustomIcon"; - $Values[] = "'$CustomIcon'"; - $Update[] = "CustomIcon = '$CustomIcon'"; - } - self::update_titles($UserID, $_POST['donor_title_prefix'], $_POST['donor_title_suffix'], $_POST['donor_title_comma']); - $Counter++; - } - for ($i = 1; $i <= $Counter; $i++) { - self::add_profile_info_reward($i, $Insert, $Values, $Update); - } - if ($SpecialRank >= 2) { - if (isset($_POST['second_avatar'])) { - $SecondAvatar = db_string($_POST['second_avatar']); - if (!Misc::is_valid_url($SecondAvatar)) { - $SecondAvatar = ''; - } - $Insert[] = "SecondAvatar"; - $Values[] = "'$SecondAvatar'"; - $Update[] = "SecondAvatar = '$SecondAvatar'"; - } - } - $Insert = implode(', ', $Insert); - $Values = implode(', ', $Values); - $Update = implode(', ', $Update); - if ($Counter > 0) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - INSERT INTO donor_rewards - ($Insert) - VALUES - ($Values) - ON DUPLICATE KEY UPDATE - $Update"); - G::$DB->set_query_id($QueryID); - } - G::$Cache->delete_value("donor_profile_rewards_$UserID"); - G::$Cache->delete_value("donor_info_$UserID"); - - } - - public static function update_titles($UserID, $Prefix, $Suffix, $UseComma) { - $QueryID = G::$DB->get_query_id(); - $Prefix = trim(db_string($Prefix)); - $Suffix = trim(db_string($Suffix)); - $UseComma = empty($UseComma) ? true : false; - G::$DB->query(" - INSERT INTO donor_forum_usernames - (UserID, Prefix, Suffix, UseComma) - VALUES - ('$UserID', '$Prefix', '$Suffix', '$UseComma') - ON DUPLICATE KEY UPDATE - Prefix = '$Prefix', - Suffix = '$Suffix', - UseComma = '$UseComma'"); - G::$Cache->delete_value("donor_title_$UserID"); - G::$DB->set_query_id($QueryID); - } - - - public static function get_donation_history($UserID) { - $UserID = (int)$UserID; - if (empty($UserID)) { - error(404); - } - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT Amount, Email, Time, Currency, Reason, Source, AddedBy, Rank, TotalRank - FROM donations - WHERE UserID = '$UserID' - ORDER BY Time DESC"); - $DonationHistory = G::$DB->to_array(false, MYSQLI_ASSOC, false); - G::$DB->set_query_id($QueryID); - return $DonationHistory; - } - - public static function get_rank_expiration($UserID) { - $DonorInfo = self::get_donor_info($UserID); - if ($DonorInfo['SRank'] == MAX_SPECIAL_RANK || $DonorInfo['Rank'] == 1) { - $Return = 'Never'; - } elseif ($DonorInfo['ExpireTime']) { - $ExpireTime = strtotime($DonorInfo['ExpireTime']); - if ($ExpireTime - time() < 60) { - $Return = 'Soon'; - } else { - $Expiration = time_diff($ExpireTime); // 32 days - $Return = "in $Expiration"; - } - } else { - $Return = ''; - } - return $Return; - } - - public static function get_leaderboard_position($UserID) { - $UserID = (int)$UserID; - $QueryID = G::$DB->get_query_id(); - G::$DB->query("SET @RowNum := 0"); - G::$DB->query(" - SELECT Position - FROM ( - SELECT d.UserID, @RowNum := @RowNum + 1 AS Position - FROM users_donor_ranks AS d - ORDER BY TotalRank DESC - ) l - WHERE UserID = '$UserID'"); - if (G::$DB->has_results()) { - list($Position) = G::$DB->next_record(); - } else { - $Position = 0; - } - G::$DB->set_query_id($QueryID); - return $Position; - } - - public static function is_donor($UserID) { - return self::get_rank($UserID) > 0; - } - - public static function currency_exchange($Amount, $Currency) { - if (!self::is_valid_currency($Currency)) { - error("$Currency is not valid currency"); - } - switch ($Currency) { - case 'USD': - $Amount = self::usd_to_euro($Amount); - break; - case 'BTC': - $Amount = self::btc_to_euro($Amount); - break; - default: - break; - } - return round($Amount, 2); - } - - public static function is_valid_currency($Currency) { - return $Currency == 'EUR' || $Currency == 'BTC' || $Currency == 'USD'; - } - - public static function btc_to_euro($Amount) { - $Rate = G::$Cache->get_value('btc_rate'); - if (empty($Rate)) { - $Rate = self::get_stored_conversion_rate('BTC'); - G::$Cache->cache_value('btc_rate', $Rate, 86400); - } - return $Rate * $Amount; - } - - public static function usd_to_euro($Amount) { - $Rate = G::$Cache->get_value('usd_rate'); - if (empty($Rate)) { - $Rate = self::get_stored_conversion_rate('USD'); - G::$Cache->cache_value('usd_rate', $Rate, 86400); - } - return $Rate * $Amount; - } - - public static function get_stored_conversion_rate($Currency) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT Rate - FROM currency_conversion_rates - WHERE Currency = '$Currency'"); - list($Rate) = G::$DB->next_record(MYSQLI_NUM, false); - G::$DB->set_query_id($QueryID); - return $Rate; - } - - private static function set_stored_conversion_rate($Currency, $Rate) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - REPLACE INTO currency_conversion_rates - (Currency, Rate, Time) - VALUES - ('$Currency', $Rate, NOW())"); - if ($Currency == 'USD') { - $KeyName = 'usd_rate'; - } elseif ($Currency == 'BTC') { - $KeyName = 'btc_rate'; - } - G::$Cache->cache_value($KeyName, $Rate, 86400); - G::$DB->set_query_id($QueryID); - } - - private static function get_new_conversion_rates() { - if ($BTC = file_get_contents(BTC_API_URL)) { - $BTC = json_decode($BTC, true); - if (isset($BTC['24h_avg'])) { - if ($Rate = round($BTC['24h_avg'], 4)) { // We don't need good precision - self::set_stored_conversion_rate('BTC', $Rate); - } - } - } - if ($USD = file_get_contents(USD_API_URL)) { - // Valid JSON isn't returned so we make it valid. - $Replace = array( - 'lhs' => '"lhs"', - 'rhs' => '"rhs"', - 'error' => '"error"', - 'icc' => '"icc"' - ); - - $USD = str_replace(array_keys($Replace), array_values($Replace), $USD); - $USD = json_decode($USD, true); - if (isset($USD['rhs'])) { - // The response is in format "# Euroes", extracts the numbers. - $Rate = preg_split("/[\s,]+/", $USD['rhs']); - if ($Rate = round($Rate[0], 4)) { // We don't need good precision - self::set_stored_conversion_rate('USD', $Rate); - } - } - } - } - - public static function get_forum_description() { - return self::$ForumDescriptions[rand(0, count(self::$ForumDescriptions) - 1)]; - } - - private static function get_pm_body($Source, $Currency, $DonationAmount, $ReceivedRank, $CurrentRank) { - if ($Currency != 'BTC') { - $DonationAmount = number_format($DonationAmount, 2); - } - if ($CurrentRank >= MAX_RANK) { - $CurrentRank = MAX_RANK - 1; - } elseif ($CurrentRank == 5) { - $CurrentRank = 4; - } - return "Thank you for your generosity and support. It's users like you who make all of this possible. What follows is a brief description of your transaction: +// } + if ($Rank >= 2 || $HasAll) { + $Rewards["HasDonorIconMouseOverText"] = true; + $Rewards["HasProfileInfo1"] = true; + } + if ($Rank >= 3 || $HasAll) { + $Rewards["HasAvatarMouseOverText"] = true; + $Rewards["HasProfileInfo2"] = true; + } + if ($Rank >= 4 || $HasAll) { + $Rewards["HasDonorIconLink"] = true; + $Rewards["HasProfileInfo3"] = true; + } + if ($Rank >= MAX_RANK || $HasAll) { + $Rewards["HasCustomDonorIcon"] = true; + $Rewards["HasDonorForum"] = true; + $Rewards["HasProfileInfo4"] = true; + } + if ($SpecialRank >= 2) { + $Rewards["HasSecondAvatar"] = true; + } + return $Rewards; + } + + public static function get_rewards($UserID) { + return self::get_donor_info($UserID)['Rewards']; + } + + public static function get_profile_rewards($UserID) { + $Results = G::$Cache->get_value("donor_profile_rewards_$UserID"); + if ($Results === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + ProfileInfo1, + ProfileInfoTitle1, + ProfileInfo2, + ProfileInfoTitle2, + ProfileInfo3, + ProfileInfoTitle3, + ProfileInfo4, + ProfileInfoTitle4 + FROM donor_rewards + WHERE UserID = '$UserID'"); + $Results = G::$DB->next_record(); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value("donor_profile_rewards_$UserID", $Results, 0); + } + return $Results; + } + + private static function add_profile_info_reward($Counter, &$Insert, &$Values, &$Update) { + if (isset($_POST["profile_title_" . $Counter]) && isset($_POST["profile_info_" . $Counter])) { + $ProfileTitle = db_string($_POST["profile_title_" . $Counter]); + $ProfileInfo = db_string($_POST["profile_info_" . $Counter]); + $ProfileInfoTitleSQL = "ProfileInfoTitle" . $Counter; + $ProfileInfoSQL = "ProfileInfo" . $Counter; + $Insert[] = "$ProfileInfoTitleSQL"; + $Values[] = "'$ProfileInfoTitle'"; + $Update[] = "$ProfileInfoTitleSQL = '$ProfileTitle'"; + $Insert[] = "$ProfileInfoSQL"; + $Values[] = "'$ProfileInfo'"; + $Update[] = "$ProfileInfoSQL = '$ProfileInfo'"; + } + } + + + + public static function update_rewards($UserID) { + $Rank = self::get_rank($UserID); + $SpecialRank = self::get_special_rank($UserID); + $HasAll = $SpecialRank == 3; + $Counter = 0; + $Insert = []; + $Values = []; + $Update = []; + + $Insert[] = "UserID"; + $Values[] = "'$UserID'"; + if ($Rank >= 1 || $HasAll) { + + } + if ($Rank >= 2 || $HasAll) { + if (isset($_POST['donor_icon_mouse_over_text'])) { + $IconMouseOverText = db_string($_POST['donor_icon_mouse_over_text']); + $Insert[] = "IconMouseOverText"; + $Values[] = "'$IconMouseOverText'"; + $Update[] = "IconMouseOverText = '$IconMouseOverText'"; + } + $Counter++; + } + if ($Rank >= 3 || $HasAll) { + if (isset($_POST['avatar_mouse_over_text'])) { + $AvatarMouseOverText = db_string($_POST['avatar_mouse_over_text']); + $Insert[] = "AvatarMouseOverText"; + $Values[] = "'$AvatarMouseOverText'"; + $Update[] = "AvatarMouseOverText = '$AvatarMouseOverText'"; + } + $Counter++; + } + if ($Rank >= 4 || $HasAll) { + if (isset($_POST['donor_icon_link'])) { + $CustomIconLink = db_string($_POST['donor_icon_link']); + if (!Misc::is_valid_url($CustomIconLink)) { + $CustomIconLink = ''; + } + $Insert[] = "CustomIconLink"; + $Values[] = "'$CustomIconLink'"; + $Update[] = "CustomIconLink = '$CustomIconLink'"; + } + $Counter++; + } + if ($Rank >= MAX_RANK || $HasAll) { + if (isset($_POST['donor_icon_custom_url'])) { + $CustomIcon = db_string($_POST['donor_icon_custom_url']); + if (!Misc::is_valid_url($CustomIcon)) { + $CustomIcon = ''; + } + $Insert[] = "CustomIcon"; + $Values[] = "'$CustomIcon'"; + $Update[] = "CustomIcon = '$CustomIcon'"; + } + self::update_titles($UserID, $_POST['donor_title_prefix'], $_POST['donor_title_suffix'], $_POST['donor_title_comma']); + $Counter++; + } + for ($i = 1; $i <= $Counter; $i++) { + self::add_profile_info_reward($i, $Insert, $Values, $Update); + } + if ($SpecialRank >= 2) { + if (isset($_POST['second_avatar'])) { + $SecondAvatar = db_string($_POST['second_avatar']); + if (!Misc::is_valid_url($SecondAvatar)) { + $SecondAvatar = ''; + } + $Insert[] = "SecondAvatar"; + $Values[] = "'$SecondAvatar'"; + $Update[] = "SecondAvatar = '$SecondAvatar'"; + } + } + $Insert = implode(', ', $Insert); + $Values = implode(', ', $Values); + $Update = implode(', ', $Update); + if ($Counter > 0) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + INSERT INTO donor_rewards + ($Insert) + VALUES + ($Values) + ON DUPLICATE KEY UPDATE + $Update"); + G::$DB->set_query_id($QueryID); + } + G::$Cache->delete_value("donor_profile_rewards_$UserID"); + G::$Cache->delete_value("donor_info_$UserID"); + + } + + public static function update_titles($UserID, $Prefix, $Suffix, $UseComma) { + $QueryID = G::$DB->get_query_id(); + $Prefix = trim(db_string($Prefix)); + $Suffix = trim(db_string($Suffix)); + $UseComma = empty($UseComma) ? true : false; + G::$DB->query(" + INSERT INTO donor_forum_usernames + (UserID, Prefix, Suffix, UseComma) + VALUES + ('$UserID', '$Prefix', '$Suffix', '$UseComma') + ON DUPLICATE KEY UPDATE + Prefix = '$Prefix', + Suffix = '$Suffix', + UseComma = '$UseComma'"); + G::$Cache->delete_value("donor_title_$UserID"); + G::$DB->set_query_id($QueryID); + } + + + public static function get_donation_history($UserID) { + $UserID = (int)$UserID; + if (empty($UserID)) { + error(404); + } + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT Amount, Email, Time, Currency, Reason, Source, AddedBy, Rank, TotalRank + FROM donations + WHERE UserID = '$UserID' + ORDER BY Time DESC"); + $DonationHistory = G::$DB->to_array(false, MYSQLI_ASSOC, false); + G::$DB->set_query_id($QueryID); + return $DonationHistory; + } + + public static function get_rank_expiration($UserID) { + $DonorInfo = self::get_donor_info($UserID); + if ($DonorInfo['SRank'] == MAX_SPECIAL_RANK || $DonorInfo['Rank'] == 1) { + $Return = 'Never'; + } elseif ($DonorInfo['ExpireTime']) { + $ExpireTime = strtotime($DonorInfo['ExpireTime']); + if ($ExpireTime - time() < 60) { + $Return = 'Soon'; + } else { + $Expiration = time_diff($ExpireTime); // 32 days + $Return = "in $Expiration"; + } + } else { + $Return = ''; + } + return $Return; + } + + public static function get_leaderboard_position($UserID) { + $UserID = (int)$UserID; + $QueryID = G::$DB->get_query_id(); + G::$DB->query("SET @RowNum := 0"); + G::$DB->query(" + SELECT Position + FROM ( + SELECT d.UserID, @RowNum := @RowNum + 1 AS Position + FROM users_donor_ranks AS d + ORDER BY TotalRank DESC + ) l + WHERE UserID = '$UserID'"); + if (G::$DB->has_results()) { + list($Position) = G::$DB->next_record(); + } else { + $Position = 0; + } + G::$DB->set_query_id($QueryID); + return $Position; + } + + public static function is_donor($UserID) { + return self::get_rank($UserID) > 0; + } + + public static function currency_exchange($Amount, $Currency) { + if (!self::is_valid_currency($Currency)) { + error("$Currency is not valid currency"); + } + switch ($Currency) { + case 'USD': + $Amount = self::usd_to_euro($Amount); + break; + case 'BTC': + $Amount = self::btc_to_euro($Amount); + break; + default: + break; + } + return round($Amount, 2); + } + + public static function is_valid_currency($Currency) { + return $Currency == 'EUR' || $Currency == 'BTC' || $Currency == 'USD'; + } + + public static function btc_to_euro($Amount) { + $Rate = G::$Cache->get_value('btc_rate'); + if (empty($Rate)) { + $Rate = self::get_stored_conversion_rate('BTC'); + G::$Cache->cache_value('btc_rate', $Rate, 86400); + } + return $Rate * $Amount; + } + + public static function usd_to_euro($Amount) { + $Rate = G::$Cache->get_value('usd_rate'); + if (empty($Rate)) { + $Rate = self::get_stored_conversion_rate('USD'); + G::$Cache->cache_value('usd_rate', $Rate, 86400); + } + return $Rate * $Amount; + } + + public static function get_stored_conversion_rate($Currency) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT Rate + FROM currency_conversion_rates + WHERE Currency = '$Currency'"); + list($Rate) = G::$DB->next_record(MYSQLI_NUM, false); + G::$DB->set_query_id($QueryID); + return $Rate; + } + + private static function set_stored_conversion_rate($Currency, $Rate) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + REPLACE INTO currency_conversion_rates + (Currency, Rate, Time) + VALUES + ('$Currency', $Rate, NOW())"); + if ($Currency == 'USD') { + $KeyName = 'usd_rate'; + } elseif ($Currency == 'BTC') { + $KeyName = 'btc_rate'; + } + G::$Cache->cache_value($KeyName, $Rate, 86400); + G::$DB->set_query_id($QueryID); + } + + private static function get_new_conversion_rates() { + if ($BTC = file_get_contents(BTC_API_URL)) { + $BTC = json_decode($BTC, true); + if (isset($BTC['24h_avg'])) { + if ($Rate = round($BTC['24h_avg'], 4)) { // We don't need good precision + self::set_stored_conversion_rate('BTC', $Rate); + } + } + } + if ($USD = file_get_contents(USD_API_URL)) { + // Valid JSON isn't returned so we make it valid. + $Replace = array( + 'lhs' => '"lhs"', + 'rhs' => '"rhs"', + 'error' => '"error"', + 'icc' => '"icc"' + ); + + $USD = str_replace(array_keys($Replace), array_values($Replace), $USD); + $USD = json_decode($USD, true); + if (isset($USD['rhs'])) { + // The response is in format "# Euroes", extracts the numbers. + $Rate = preg_split("/[\s,]+/", $USD['rhs']); + if ($Rate = round($Rate[0], 4)) { // We don't need good precision + self::set_stored_conversion_rate('USD', $Rate); + } + } + } + } + + public static function get_forum_description() { + return self::$ForumDescriptions[rand(0, count(self::$ForumDescriptions) - 1)]; + } + + private static function get_pm_body($Source, $Currency, $DonationAmount, $ReceivedRank, $CurrentRank) { + if ($Currency != 'BTC') { + $DonationAmount = number_format($DonationAmount, 2); + } + if ($CurrentRank >= MAX_RANK) { + $CurrentRank = MAX_RANK - 1; + } elseif ($CurrentRank == 5) { + $CurrentRank = 4; + } + return "Thank you for your generosity and support. It's users like you who make all of this possible. What follows is a brief description of your transaction: [*][b]You Contributed:[/b] $DonationAmount $Currency [*][b]You Received:[/b] $ReceivedRank Donor Point".($ReceivedRank == 1 ? '' : 's')." [*][b]Your Donor Rank:[/b] Donor Rank # $CurrentRank @@ -778,10 +778,10 @@ private static function get_pm_body($Source, $Currency, $DonationAmount, $Receiv ".SITE_NAME.' Staff [align=center][If you have any questions or concerns, please [url='.site_url().'staffpm.php]send a Staff PM[/url].]'; - } + } - private static function get_special_rank_one_pm() { - return 'Congratulations on reaching [url='.site_url().'forums.php?action=viewthread&threadid=178640&postid=4839790#post4839790]Special Rank #1[/url]! You\'ve been awarded [b]one user pick[/b]! This user pick will be featured on the '.SITE_NAME.' front page during an upcoming event. After you submit your pick, there is no guarantee as to how long it will take before your pick is featured. Picks will be featured on a first-submitted, first-served basis. Please abide by the following guidelines when making your selection: + private static function get_special_rank_one_pm() { + return 'Congratulations on reaching [url='.site_url().'forums.php?action=viewthread&threadid=178640&postid=4839790#post4839790]Special Rank #1[/url]! You\'ve been awarded [b]one user pick[/b]! This user pick will be featured on the '.SITE_NAME.' front page during an upcoming event. After you submit your pick, there is no guarantee as to how long it will take before your pick is featured. Picks will be featured on a first-submitted, first-served basis. Please abide by the following guidelines when making your selection: [*]Pick something that hasn\'t been chosen. You can tell if a pick has been used previously by looking at the collages it\'s in. [*]Complete the enclosed form carefully and completely. @@ -807,5 +807,5 @@ private static function get_special_rank_three_pm() { Sincerely, '.SITE_NAME.' Staff'; - } + } } diff --git a/classes/donationsbitcoin.class.php b/classes/donationsbitcoin.class.php index 9a00d0259..98eb3e4c5 100644 --- a/classes/donationsbitcoin.class.php +++ b/classes/donationsbitcoin.class.php @@ -1,186 +1,186 @@ - Amount, ...) - */ - public static function get_received() { - if (defined('BITCOIN_RPC_URL')) { - $Donations = BitcoinRpc::listreceivedbyaddress(); - } - if (empty($Donations)) { - return array(); - } - $BTCUsers = array(); - foreach ($Donations as $Account) { - $BTCUsers[$Account->address] = $Account->amount; - } - return $BTCUsers; - } + /** + * Ask bitcoind for a list of all addresses that have received bitcoins + * + * @return array (BitcoinAddress => Amount, ...) + */ + public static function get_received() { + if (defined('BITCOIN_RPC_URL')) { + $Donations = BitcoinRpc::listreceivedbyaddress(); + } + if (empty($Donations)) { + return []; + } + $BTCUsers = []; + foreach ($Donations as $Account) { + $BTCUsers[$Account->address] = $Account->amount; + } + return $BTCUsers; + } - /** - * Ask bitcoind for the current account balance - * - * @return float balance - */ - public static function get_balance() { - if (defined('BITCOIN_RPC_URL')) { - return BitcoinRpc::getbalance(); - } - } + /** + * Ask bitcoind for the current account balance + * + * @return float balance + */ + public static function get_balance() { + if (defined('BITCOIN_RPC_URL')) { + return BitcoinRpc::getbalance(); + } + } - /** - * Get a user's existing bitcoin address or generate a new one - * - * @param int $UserID - * @param bool $GenAddress whether to create a new address if it doesn't exist - * @return false if no address exists and $GenAddress is false - * string bitcoin address otherwise - */ - public static function get_address($UserID, $GenAddress = false) { - $UserID = (int)$UserID; - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT BitcoinAddress - FROM users_info - WHERE UserID = '$UserID'"); - list($Addr) = G::$DB->next_record(); - G::$DB->set_query_id($QueryID); + /** + * Get a user's existing bitcoin address or generate a new one + * + * @param int $UserID + * @param bool $GenAddress whether to create a new address if it doesn't exist + * @return false if no address exists and $GenAddress is false + * string bitcoin address otherwise + */ + public static function get_address($UserID, $GenAddress = false) { + $UserID = (int)$UserID; + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT BitcoinAddress + FROM users_info + WHERE UserID = '$UserID'"); + list($Addr) = G::$DB->next_record(); + G::$DB->set_query_id($QueryID); - if (!empty($Addr)) { - return $Addr; - } elseif ($GenAddress) { - if (defined('BITCOIN_RPC_URL')) { - $NewAddr = BitcoinRpc::getnewaddress(); - } - if (empty($NewAddr)) { - error(0); - } - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - UPDATE users_info - SET BitcoinAddress = '".db_string($NewAddr)."' - WHERE UserID = '$UserID' - AND BitcoinAddress IS NULL"); - G::$DB->set_query_id($QueryID); - return $NewAddr; - } else { - return false; - } - } + if (!empty($Addr)) { + return $Addr; + } elseif ($GenAddress) { + if (defined('BITCOIN_RPC_URL')) { + $NewAddr = BitcoinRpc::getnewaddress(); + } + if (empty($NewAddr)) { + error(0); + } + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + UPDATE users_info + SET BitcoinAddress = '".db_string($NewAddr)."' + WHERE UserID = '$UserID' + AND BitcoinAddress IS NULL"); + G::$DB->set_query_id($QueryID); + return $NewAddr; + } else { + return false; + } + } - /** - * Ask bitcoind for the total amount of bitcoins received - * - * @return float amount - */ - public static function get_total_received() { - if (defined('BITCOIN_RPC_URL')) { - $Accounts = BitcoinRpc::listreceivedbyaccount(); - } - if (empty($Accounts)) { - return 0.0; - } - foreach ($Accounts as $Account) { - if ($Account->account == '') { - return $Account->amount; - } - } - return 0.0; - } + /** + * Ask bitcoind for the total amount of bitcoins received + * + * @return float amount + */ + public static function get_total_received() { + if (defined('BITCOIN_RPC_URL')) { + $Accounts = BitcoinRpc::listreceivedbyaccount(); + } + if (empty($Accounts)) { + return 0.0; + } + foreach ($Accounts as $Account) { + if ($Account->account == '') { + return $Account->amount; + } + } + return 0.0; + } - /** - * Translate bitcoin addresses to user IDs - * - * @param array $Addresses list of bitcoin addresses - * @return array (BitcoinAddress => UserID, ...) - */ - public static function get_userids($Addresses) { - if (!is_array($Addresses) || empty($Addresses)) { - return false; - } - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT BitcoinAddress, UserID - FROM users_info - WHERE BitcoinAddress IN ('" . implode("', '", $Addresses) . "')"); - if (G::$DB->has_results()) { - $UserIDs = G::$DB->to_pair(0, 1); - } else { - $UserIDs = array(); - } - G::$DB->set_query_id($QueryID); - return $UserIDs; - } + /** + * Translate bitcoin addresses to user IDs + * + * @param array $Addresses list of bitcoin addresses + * @return array (BitcoinAddress => UserID, ...) + */ + public static function get_userids($Addresses) { + if (!is_array($Addresses) || empty($Addresses)) { + return false; + } + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT BitcoinAddress, UserID + FROM users_info + WHERE BitcoinAddress IN ('" . implode("', '", $Addresses) . "')"); + if (G::$DB->has_results()) { + $UserIDs = G::$DB->to_pair(0, 1); + } else { + $UserIDs = []; + } + G::$DB->set_query_id($QueryID); + return $UserIDs; + } - /** - * Find and process new donations since the last time this function was called. - */ - public static function find_new_donations() { - global $Debug; - if (($OldAmount = G::$Cache->get_value('btc_total_received')) === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT IFNULL(SUM(Amount), 0) - FROM donations_bitcoin"); - list($OldAmount) = G::$DB->next_record(MYSQLI_NUM, false); - G::$DB->set_query_id($QueryID); - } - $NewAmount = self::get_total_received(); - if ($NewAmount < $OldAmount) { - // This shouldn't happen. Perhaps bitcoind was restarted recently - // or the block index was removed. Either way, try again later - send_irc('PRIVMSG ' . LAB_CHAN . " :Bad bitcoin donation data (is $NewAmount, was $OldAmount). If this persists, something is probably wrong"); - return false; - } - if ($NewAmount > $OldAmount) { - // I really wish we didn't have to do it like this - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT BitcoinAddress, SUM(Amount) - FROM donations_bitcoin - GROUP BY BitcoinAddress"); - $OldDonations = G::$DB->to_pair(0, 1, false); - G::$DB->set_query_id($QueryID); - $NewDonations = self::get_received(); - foreach ($NewDonations as $Address => &$Amount) { - if (isset($OldDonations[$Address])) { - if ($Amount == $OldDonations[$Address]) { // Direct comparison should be fine as everything comes from bitcoind - unset($NewDonations[$Address]); - continue; - } - $Debug->log_var(array('old' => $OldDonations[$Address], 'new' => $Amount), "New donations from $Address"); - // PHP doesn't do fixed-point math, and json_decode has already botched the precision - // so let's just round this off to satoshis and pray that we're on a 64 bit system - $Amount = round($Amount - $OldDonations[$Address], 8); - } - $NewDonations[$Address] = $Amount; - } - $Debug->log_var($NewDonations, '$NewDonations'); - foreach (self::get_userids(array_keys($NewDonations)) as $Address => $UserID) { - Donations::regular_donate($UserID, $NewDonations[$Address], 'Bitcoin Parser', '', 'BTC'); - self::store_donation($Address, $NewDonations[$Address]); - } - G::$Cache->cache_value('btc_total_received', $NewAmount, 0); - } - } + /** + * Find and process new donations since the last time this function was called. + */ + public static function find_new_donations() { + global $Debug; + if (($OldAmount = G::$Cache->get_value('btc_total_received')) === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT IFNULL(SUM(Amount), 0) + FROM donations_bitcoin"); + list($OldAmount) = G::$DB->next_record(MYSQLI_NUM, false); + G::$DB->set_query_id($QueryID); + } + $NewAmount = self::get_total_received(); + if ($NewAmount < $OldAmount) { + // This shouldn't happen. Perhaps bitcoind was restarted recently + // or the block index was removed. Either way, try again later + send_irc('PRIVMSG ' . LAB_CHAN . " :Bad bitcoin donation data (is $NewAmount, was $OldAmount). If this persists, something is probably wrong"); + return false; + } + if ($NewAmount > $OldAmount) { + // I really wish we didn't have to do it like this + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT BitcoinAddress, SUM(Amount) + FROM donations_bitcoin + GROUP BY BitcoinAddress"); + $OldDonations = G::$DB->to_pair(0, 1, false); + G::$DB->set_query_id($QueryID); + $NewDonations = self::get_received(); + foreach ($NewDonations as $Address => &$Amount) { + if (isset($OldDonations[$Address])) { + if ($Amount == $OldDonations[$Address]) { // Direct comparison should be fine as everything comes from bitcoind + unset($NewDonations[$Address]); + continue; + } + $Debug->log_var(array('old' => $OldDonations[$Address], 'new' => $Amount), "New donations from $Address"); + // PHP doesn't do fixed-point math, and json_decode has already botched the precision + // so let's just round this off to satoshis and pray that we're on a 64 bit system + $Amount = round($Amount - $OldDonations[$Address], 8); + } + $NewDonations[$Address] = $Amount; + } + $Debug->log_var($NewDonations, '$NewDonations'); + foreach (self::get_userids(array_keys($NewDonations)) as $Address => $UserID) { + Donations::regular_donate($UserID, $NewDonations[$Address], 'Bitcoin Parser', '', 'BTC'); + self::store_donation($Address, $NewDonations[$Address]); + } + G::$Cache->cache_value('btc_total_received', $NewAmount, 0); + } + } - /** - * Record a donation in the database - * - * @param string $Address bitcoin address - * @param double $Amount amount of bitcoins transferred - */ - public static function store_donation($Address, $Amount) { - if (!is_numeric($Amount) || $Amount <= 0) { - // Panic! - return false; - } - G::$DB->query(" - INSERT INTO donations_bitcoin - (BitcoinAddress, Amount) - VALUES - ('$Address', $Amount)"); - } -} \ No newline at end of file + /** + * Record a donation in the database + * + * @param string $Address bitcoin address + * @param double $Amount amount of bitcoins transferred + */ + public static function store_donation($Address, $Amount) { + if (!is_numeric($Amount) || $Amount <= 0) { + // Panic! + return false; + } + G::$DB->query(" + INSERT INTO donations_bitcoin + (BitcoinAddress, Amount) + VALUES + ('$Address', $Amount)"); + } +} diff --git a/classes/donationsview.class.php b/classes/donationsview.class.php index 3a6a0615c..7c9a841ab 100644 --- a/classes/donationsview.class.php +++ b/classes/donationsview.class.php @@ -1,216 +1,214 @@ - - - - - - - - - - - - - - - - -
    - Donor System (add points) -
    Value: - - -
    Reason:
    - -
    + + + + + + + + + + + + + + + +
    + Donor System (add points) +
    Value: + + +
    Reason:
    + +
    - - - - - - - - - - - - - - - - - - - -
    - Donor System (modify values) -
    Active points:
    Total points:
    Reason:
    - -
    - + + + Donor System (modify values) + + + + Active points: + + + + Total points: + + + + Reason: + + + + + + + + + -
    -
    Donor Statistics
    -
      - -
    • - Total donor points: -
    • - -
    • - Current donor rank: -
    • -
    • - Leaderboard position: -
    • -
    • - Last donated: -
    • -
    • - Rank expires: -
    • - -
    • - This user hasn't donated. -
    • - -
    -
    - +
    Donor Statistics
    +
      + +
    • + Total donor points: +
    • + +
    • + Current donor rank: +
    • +
    • + Leaderboard position: +
    • +
    • + Last donated: +
    • +
    • + Rank expires: +
    • + +
    • + This user hasn't donated. +
    • + +
    + + -
    -
    - - Hide -
    -
    - -
    -
    - +
    + + Hide +
    +
    + +
    + + -
    -
    - Donation History View -
    - - -
    -= MAX_RANK ? MAX_RANK : $Rank; - $Overflow = $Rank - $CurrentRank; - $Display = $CurrentRank; - if ($Display == 5 || $Display == 6) { - $Display--; - } - if ($ShowOverflow && $Overflow) { - $Display .= " (+$Overflow)"; - } - if ($Rank >= 6) { - $Display .= ' [Gold]'; - } elseif ($Rank >= 4) { - $Display .= ' [Silver]'; - } elseif ($Rank >= 3) { - $Display .= ' [Bronze]'; - } elseif ($Rank >= 2) { - $Display .= ' [Copper]'; - } elseif ($Rank >= 1) { - $Display .= ' [Red]'; - } - } - echo $Display; - } + + + + += MAX_RANK ? MAX_RANK : $Rank; + $Overflow = $Rank - $CurrentRank; + $Display = $CurrentRank; + if ($Display == 5 || $Display == 6) { + $Display--; + } + if ($ShowOverflow && $Overflow) { + $Display .= " (+$Overflow)"; + } + if ($Rank >= 6) { + $Display .= ' [Gold]'; + } elseif ($Rank >= 4) { + $Display .= ' [Silver]'; + } elseif ($Rank >= 3) { + $Display .= ' [Bronze]'; + } elseif ($Rank >= 2) { + $Display .= ' [Copper]'; + } elseif ($Rank >= 1) { + $Display .= ' [Red]'; + } + } + echo $Display; + } } diff --git a/classes/feed.class.php b/classes/feed.class.php index a3b8de56d..268c79f38 100644 --- a/classes/feed.class.php +++ b/classes/feed.class.php @@ -1,75 +1,75 @@ -\n","\n\t\n"; - echo ''."\n"; - echo ''."\n"; - } + function open_feed() { + header("Content-type: application/xml; charset=UTF-8"); + echo "\n","\n\t\n"; + echo ''."\n"; + echo ''."\n"; + } - function close_feed() { - echo "\t\n"; - } + function close_feed() { + echo "\t\n"; + } - function channel($Title, $Description, $Section = '') { - $Site = $this->UseSSL ? site_url() : site_url(false); - echo "\t\t$Title :: ". SITE_NAME. "\n"; - echo "\t\t$Site$Section\n"; - echo "\t\t$Description\n"; - echo "\t\ten-us\n"; - echo "\t\t". date('r'). "\n"; - echo "\t\thttp://blogs.law.harvard.edu/tech/rss\n"; - echo "\t\tGazelle Feed Class\n\n"; - } + function channel($Title, $Description, $Section = '') { + $Site = $this->UseSSL ? site_url() : site_url(false); + echo "\t\t$Title :: ". SITE_NAME. "\n"; + echo "\t\t$Site$Section\n"; + echo "\t\t$Description\n"; + echo "\t\ten-us\n"; + echo "\t\t". date('r'). "\n"; + echo "\t\thttp://blogs.law.harvard.edu/tech/rss\n"; + echo "\t\tGazelle Feed Class\n\n"; + } - function item($Title, $Description, $Page, $Creator, $Comments = '', $Category = '', $Date = '') { //Escape with CDATA, otherwise the feed breaks. - if ($Date == '') { - $Date = date('r'); - } else { - $Date = date('r', strtotime($Date)); - } - $Site = $this->UseSSL ? site_url() : site_url(false); - $Item = "\t\t\n"; - $Item .= "\t\t\t<![CDATA[$Title]]>\n"; - $Item .= "\t\t\t\n"; - $Item .= "\t\t\t$Date\n"; - $Item .= "\t\t\t$Site$Page\n"; - $Item .= "\t\t\t$Site$Page\n"; - if ($Comments != '') { - $Item .= "\t\t\t$Site$Comments\n"; - } - if ($Category != '') { - $Item .= "\t\t\t\n"; - } - $Item .= "\t\t\t$Creator\n\t\t\n"; - return $Item; - } + function item($Title, $Description, $Page, $Creator, $Comments = '', $Category = '', $Date = '') { //Escape with CDATA, otherwise the feed breaks. + if ($Date == '') { + $Date = date('r'); + } else { + $Date = date('r', strtotime($Date)); + } + $Site = $this->UseSSL ? site_url() : site_url(false); + $Item = "\t\t\n"; + $Item .= "\t\t\t<![CDATA[$Title]]>\n"; + $Item .= "\t\t\t\n"; + $Item .= "\t\t\t$Date\n"; + $Item .= "\t\t\t$Site$Page\n"; + $Item .= "\t\t\t$Site$Page\n"; + if ($Comments != '') { + $Item .= "\t\t\t$Site$Comments\n"; + } + if ($Category != '') { + $Item .= "\t\t\t\n"; + } + $Item .= "\t\t\t$Creator\n\t\t\n"; + return $Item; + } - function retrieve($CacheKey, $AuthKey, $PassKey) { - global $Cache; - $Entries = $Cache->get_value($CacheKey); - if (!$Entries) { - $Entries = array(); - } else { - foreach ($Entries as $Item) { - echo str_replace(array('[[PASSKEY]]', '[[AUTHKEY]]'), array(display_str($PassKey), display_str($AuthKey)), $Item); - } - } - } + function retrieve($CacheKey, $AuthKey, $PassKey) { + global $Cache; + $Entries = $Cache->get_value($CacheKey); + if (!$Entries) { + $Entries = []; + } else { + foreach ($Entries as $Item) { + echo str_replace(array('[[PASSKEY]]', '[[AUTHKEY]]'), array(display_str($PassKey), display_str($AuthKey)), $Item); + } + } + } - function populate($CacheKey, $Item) { - global $Cache; - $Entries = $Cache->get_value($CacheKey, true); - if (!$Entries) { - $Entries = array(); - } else { - if (count($Entries) >= 50) { - array_pop($Entries); - } - } - array_unshift($Entries, $Item); - $Cache->cache_value($CacheKey, $Entries, 0); //inf cache - } + function populate($CacheKey, $Item) { + global $Cache; + $Entries = $Cache->get_value($CacheKey, true); + if (!$Entries) { + $Entries = []; + } else { + if (count($Entries) >= 50) { + array_pop($Entries); + } + } + array_unshift($Entries, $Item); + $Cache->cache_value($CacheKey, $Entries, 0); //inf cache + } } diff --git a/classes/file_checker.class.php b/classes/file_checker.class.php index 4ce951e1f..cb8656a4a 100644 --- a/classes/file_checker.class.php +++ b/classes/file_checker.class.php @@ -1,87 +1,87 @@ \ * | " - * - * TODO: Add "/" to the blacklist. Adding "/" to the blacklist causes problems with nested dirs, apparently. - * - * Only the following characters need to be escaped (see the link below): - * \ - ^ ] - * - * http://www.php.net/manual/en/regexp.reference.character-classes.php - */ - $AllBlockedChars = ' : ? < > \ * | " '; - if (preg_match('/[\\:?<>*|"]/', $Name, $Matches)) { - character_error($Matches[0], $AllBlockedChars); - } + /* + * These characters are invalid in NTFS on Windows systems: + * : ? / < > \ * | " + * + * TODO: Add "/" to the blacklist. Adding "/" to the blacklist causes problems with nested dirs, apparently. + * + * Only the following characters need to be escaped (see the link below): + * \ - ^ ] + * + * http://www.php.net/manual/en/regexp.reference.character-classes.php + */ + $AllBlockedChars = ' : ? < > \ * | " '; + if (preg_match('/[\\:?<>*|"]/', $Name, $Matches)) { + character_error($Matches[0], $AllBlockedChars); + } } function check_extensions($Type, $Name) { global $MusicExtensions, $ComicsExtensions, $BadExtensions; $extension = get_file_extension($Name); - if ($Type == 'Music' || $Type == 'Audiobooks' || $Type == 'Comedy' || $Type == 'E-Books') { + if ($Type == 'Music' || $Type == 'Audiobooks' || $Type == 'Comedy' || $Type == 'E-Books') { if (!isset($MusicExtensions[$extension])) { - invalid_error($Name); - } - } elseif ($Type == 'Comics') { + invalid_error($Name); + } + } elseif ($Type == 'Comics') { if (!isset($ComicsExtensions[$extension])) { - invalid_error($Name); - } + invalid_error($Name); + } } else { if (isset($BadExtensions[$extension])) { forbidden_error($Name); } - } + } } function get_file_extension($FileName) { - return strtolower(substr(strrchr($FileName, '.'), 1)); + return strtolower(substr(strrchr($FileName, '.'), 1)); } function invalid_error($Name) { - global $Err; - $Err = 'The torrent contained one or more invalid files (' . display_str($Name) . ')'; + global $Err; + $Err = 'The torrent contained one or more invalid files (' . display_str($Name) . ')'; } function forbidden_error($Name) { - global $Err; - $Err = 'The torrent contained one or more forbidden files (' . display_str($Name) . ')'; + global $Err; + $Err = 'The torrent contained one or more forbidden files (' . display_str($Name) . ')'; } function character_error($Character, $AllBlockedChars) { - global $Err; - $Err = "One or more of the files or folders in the torrent has a name that contains the forbidden character '$Character'. Please rename the files as necessary and recreate the torrent.

    \nNote: The complete list of characters that are disallowed are shown below:
    \n\t\t$AllBlockedChars"; + global $Err; + $Err = "One or more of the files or folders in the torrent has a name that contains the forbidden character '$Character'. Please rename the files as necessary and recreate the torrent.

    \nNote: The complete list of characters that are disallowed are shown below:
    \n\t\t$AllBlockedChars"; } diff --git a/classes/fonts/README.TXT b/classes/fonts/README.TXT index b977770c8..b0743ce50 100644 --- a/classes/fonts/README.TXT +++ b/classes/fonts/README.TXT @@ -9,15 +9,15 @@ IMPORTANT - READ CAREFULLY: This Microsoft End-User License Agreement ("EULA") i SOFTWARE PRODUCT LICENSE The SOFTWARE PRODUCT is protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. The SOFTWARE PRODUCT is licensed, not sold. 1. GRANT OF LICENSE. This EULA grants you the following rights: -· Installation and Use. You may install and use an unlimited number of copies of the SOFTWARE PRODUCT. -· Reproduction and Distribution. You may reproduce and distribute an unlimited number of copies of the SOFTWARE PRODUCT; provided that each copy shall be a true and complete copy, including all copyright and trademark notices, and shall be accompanied by a copy of this EULA. Copies of the SOFTWARE PRODUCT may not be distributed for profit either on a standalone basis or included as part of your own product. -2. DESCRIPTION OF OTHER RIGHTS AND LIMITATIONS. -· Limitations on Reverse Engineering, Decompilation, and Disassembly. You may not reverse engineer, decompile, or disassemble the SOFTWARE PRODUCT, except and only to the extent that such activity is expressly permitted by applicable law notwithstanding this limitation. +· Installation and Use. You may install and use an unlimited number of copies of the SOFTWARE PRODUCT. +· Reproduction and Distribution. You may reproduce and distribute an unlimited number of copies of the SOFTWARE PRODUCT; provided that each copy shall be a true and complete copy, including all copyright and trademark notices, and shall be accompanied by a copy of this EULA. Copies of the SOFTWARE PRODUCT may not be distributed for profit either on a standalone basis or included as part of your own product. +2. DESCRIPTION OF OTHER RIGHTS AND LIMITATIONS. +· Limitations on Reverse Engineering, Decompilation, and Disassembly. You may not reverse engineer, decompile, or disassemble the SOFTWARE PRODUCT, except and only to the extent that such activity is expressly permitted by applicable law notwithstanding this limitation. · Restrictions on Alteration. You may not rename, edit or create any derivative works from the SOFTWARE PRODUCT, other than subsetting when embedding them in documents. -· Software Transfer. You may permanently transfer all of your rights under this EULA, provided the recipient agrees to the terms of this EULA. -· Termination. Without prejudice to any other rights, Microsoft may terminate this EULA if you fail to comply with the terms and conditions of this EULA. In such event, you must destroy all copies of the SOFTWARE PRODUCT and all of its component parts. +· Software Transfer. You may permanently transfer all of your rights under this EULA, provided the recipient agrees to the terms of this EULA. +· Termination. Without prejudice to any other rights, Microsoft may terminate this EULA if you fail to comply with the terms and conditions of this EULA. In such event, you must destroy all copies of the SOFTWARE PRODUCT and all of its component parts. 3. COPYRIGHT. All title and copyrights in and to the SOFTWARE PRODUCT (including but not limited to any images, text, and "applets" incorporated into the SOFTWARE PRODUCT), the accompanying printed materials, and any copies of the SOFTWARE PRODUCT are owned by Microsoft or its suppliers. The SOFTWARE PRODUCT is protected by copyright laws and international treaty provisions. Therefore, you must treat the SOFTWARE PRODUCT like any other copyrighted material. -4. U.S. GOVERNMENT RESTRICTED RIGHTS. The SOFTWARE PRODUCT and documentation are provided with RESTRICTED RIGHTS. Use, duplication, or disclosure by the Government is subject to restrictions as set forth in subparagraph (c)(1)(ii) of the Rights in Technical Data and Computer Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and (2) of the Commercial Computer Software-Restricted Rights at 48 CFR 52.227-19, as applicable. Manufacturer is Microsoft Corporation/One Microsoft Way/Redmond, WA 98052-6399. +4. U.S. GOVERNMENT RESTRICTED RIGHTS. The SOFTWARE PRODUCT and documentation are provided with RESTRICTED RIGHTS. Use, duplication, or disclosure by the Government is subject to restrictions as set forth in subparagraph (c)(1)(ii) of the Rights in Technical Data and Computer Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and (2) of the Commercial Computer Software-Restricted Rights at 48 CFR 52.227-19, as applicable. Manufacturer is Microsoft Corporation/One Microsoft Way/Redmond, WA 98052-6399. LIMITED WARRANTY NO WARRANTIES. Microsoft expressly disclaims any warranty for the SOFTWARE PRODUCT. The SOFTWARE PRODUCT and any related documentation is provided "as is" without warranty of any kind, either express or implied, including, without limitation, the implied warranties or merchantability, fitness for a particular purpose, or noninfringement. The entire risk arising out of use or performance of the SOFTWARE PRODUCT remains with you. NO LIABILITY FOR CONSEQUENTIAL DAMAGES. In no event shall Microsoft or its suppliers be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or any other pecuniary loss) arising out of the use of or inability to use this Microsoft product, even if Microsoft has been advised of the possibility of such damages. Because some states/jurisdictions do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitation may not apply to you. diff --git a/classes/format.class.php b/classes/format.class.php index d31e57c81..826c0c90b 100644 --- a/classes/format.class.php +++ b/classes/format.class.php @@ -1,568 +1,568 @@ - 'tl_notice', - 'snatched' => 'tl_snatched', - - 'freeleech' => 'tl_free', - 'neutral leech' => 'tl_free tl_neutral', - 'personal freeleech'=> 'tl_free tl_personal', - - 'reported' => 'tl_reported', - 'bad tags' => 'tl_reported tl_bad_tags', - 'bad folders' => 'tl_reported tl_bad_folders', - 'bad file names'=> 'tl_reported tl_bad_file_names', - 'missing lineage'=> 'tl_reported tl_missing_lineage', - 'cassette approved' => 'tl_approved tl_cassete', - 'lossy master approved' => 'tl_approved tl_lossy_master', - 'lossy web approved' => 'tl_approved tl_lossy_web' - ); - - /** - * Shorten a string - * - * @param string $Str string to cut - * @param int $Length cut at length - * @param bool $Hard force cut at length instead of at closest word - * @param bool $ShowDots Show dots at the end - * @return string formatted string - */ - public static function cut_string($Str, $Length, $Hard = false, $ShowDots = true) { - if (mb_strlen($Str, 'UTF-8') > $Length) { - if ($Hard == 0) { - // Not hard, cut at closest word - $CutDesc = mb_substr($Str, 0, $Length, 'UTF-8'); - $DescArr = explode(' ', $CutDesc); - if (count($DescArr) > 1) { - array_pop($DescArr); - $CutDesc = implode(' ', $DescArr); - } - if ($ShowDots) { - //TODO: should we replace the three dots with an ellipsis character? - $CutDesc .= '...'; - } - } else { - $CutDesc = mb_substr($Str, 0, $Length, 'UTF-8'); - if ($ShowDots) { - $CutDesc .= '...'; - } - } - return $CutDesc; - } else { - return $Str; - } - } - - - /** - * Gets the CSS class corresponding to a ratio - * - * @param $Ratio ratio to get the css class for - * @return string the CSS class corresponding to the ratio range - */ - public static function get_ratio_color($Ratio) { - if ($Ratio < 0.1) { return 'r00'; } - if ($Ratio < 0.2) { return 'r01'; } - if ($Ratio < 0.3) { return 'r02'; } - if ($Ratio < 0.4) { return 'r03'; } - if ($Ratio < 0.5) { return 'r04'; } - if ($Ratio < 0.6) { return 'r05'; } - if ($Ratio < 0.7) { return 'r06'; } - if ($Ratio < 0.8) { return 'r07'; } - if ($Ratio < 0.9) { return 'r08'; } - if ($Ratio < 1) { return 'r09'; } - if ($Ratio < 2) { return 'r10'; } - if ($Ratio < 5) { return 'r20'; } - return 'r50'; - } - - - /** - * Calculates and formats a ratio. - * - * @param int $Dividend AKA numerator - * @param int $Divisor - * @param boolean $Color if true, ratio will be coloured. - * @return string formatted ratio HTML - */ - public static function get_ratio_html($Dividend, $Divisor, $Color = true) { - $Ratio = self::get_ratio($Dividend, $Divisor); - - if ($Ratio === false) { - return '--'; - } - if ($Ratio === '∞') { - return '∞'; - } - if ($Color) { - $Ratio = sprintf('%s', - self::get_ratio_color($Ratio), - self::get_ratio($Dividend, $Divisor, 5), - $Ratio - ); - } - - return $Ratio; - } - - /** - * Returns ratio - * @param int $Dividend - * @param int $Divisor - * @param int $Decimal floor to n decimals (e.g. subtract .005 to floor to 2 decimals) - * @return boolean|string - */ - public static function get_ratio($Dividend, $Divisor, $Decimal = 2) { - if ($Divisor == 0 && $Dividend == 0) { - return false; - } - if ($Divisor == 0) { - return '∞'; - } - return number_format(max($Dividend / $Divisor - (0.5 / pow(10, $Decimal)), 0), $Decimal); - } - - /** - * Gets the query string of the current page, minus the parameters in $Exclude - * - * @param array $Exclude Query string parameters to leave out, or blank to include all parameters. - * @param bool $Escape Whether to return a string prepared for HTML output - * @param bool $Sort Whether to sort the parameters by key - * @return An optionally HTML sanatized query string - */ - public static function get_url($Exclude = false, $Escape = true, $Sort = false) { - if ($Exclude !== false) { - $Separator = $Escape ? '&' : '&'; - $QueryItems = NULL; - parse_str($_SERVER['QUERY_STRING'], $QueryItems); - foreach ($Exclude as $Key) { - unset($QueryItems[$Key]); - } - if ($Sort) { - ksort($QueryItems); - } - return http_build_query($QueryItems, '', $Separator); - } else { - return $Escape ? display_str($_SERVER['QUERY_STRING']) : $_SERVER['QUERY_STRING']; - } - } - - - /** - * Finds what page we're on and gives it to us, as well as the LIMIT clause for SQL - * Takes in $_GET['page'] as an additional input - * - * @param int $PerPage Results to show per page - * @param int $DefaultResult Optional, which result's page we want if no page is specified - * If this parameter is not specified, we will default to page 1 - * - * @return array(int, string) What page we are on, and what to use in the LIMIT section of a query - * e.g. "SELECT [...] LIMIT $Limit;" - */ - public static function page_limit($PerPage, $DefaultResult = 1) { - if (!isset($_GET['page'])) { - $Page = ceil($DefaultResult / $PerPage); - if ($Page == 0) { - $Page = 1; - } - $Limit = $PerPage; - } else { - if (!is_number($_GET['page'])) { - error(0); - } - $Page = $_GET['page']; - if ($Page <= 0) { - $Page = 1; - } - $Limit = $PerPage * $Page - $PerPage . ", $PerPage"; - } - return array($Page, $Limit); - } - - // A9 magic. Some other poor soul can write the phpdoc. - // For data stored in memcached catalogues (giant arrays), e.g. forum threads - public static function catalogue_limit($Page, $PerPage, $CatalogueSize = 500) { - $CatalogueID = floor(($PerPage * $Page - $PerPage) / $CatalogueSize); - $CatalogueLimit = ($CatalogueID * $CatalogueSize).", $CatalogueSize"; - return array($CatalogueID, $CatalogueLimit); - } - - public static function catalogue_select($Catalogue, $Page, $PerPage, $CatalogueSize = 500) { - return array_slice($Catalogue, (($PerPage * $Page - $PerPage) % $CatalogueSize), $PerPage, true); - } - - - /* Get pages - * Returns a page list, given certain information about the pages. - * - * @param int $StartPage: The current record the page you're on starts with. - * e.g. if you're on page 2 of a forum thread with 25 posts per page, $StartPage is 25. - * If you're on page 1, $StartPage is 0. - * FIXME: I don't think this is right and you want to pass in a number 1 - max page - * @param int $TotalRecords: The total number of records in the result set. - * e.g. if you're on a forum thread with 152 posts, $TotalRecords is 152. - * @param int $ItemsPerPage: Self-explanatory. The number of records shown on each page - * e.g. if there are 25 posts per forum page, $ItemsPerPage is 25. - * @param int $ShowPages: The number of page links that are shown. - * e.g. If there are 20 pages that exist, but $ShowPages is only 11, only 11 links will be shown. - * @param string $Anchor A URL fragment to attach to the links. - * e.g. '#comment12' - * @return A sanitized HTML page listing. - */ - public static function get_pages($StartPage, $TotalRecords, $ItemsPerPage, $ShowPages = 11, $Anchor = '') { - global $Document, $Method; - $Location = "$Document.php"; - $StartPage = ceil($StartPage); - $TotalPages = 0; - if ($TotalRecords > 0) { - $StartPage = min($StartPage, ceil($TotalRecords / $ItemsPerPage)); - - $ShowPages--; - $TotalPages = ceil($TotalRecords / $ItemsPerPage); - - if ($TotalPages > $ShowPages) { - $StartPosition = $StartPage - round($ShowPages / 2); - - if ($StartPosition <= 0) { - $StartPosition = 1; - } else { - if ($StartPosition >= ($TotalPages - $ShowPages)) { - $StartPosition = $TotalPages - $ShowPages; - } - } - - $StopPage = $ShowPages + $StartPosition; - - } else { - $StopPage = $TotalPages; - $StartPosition = 1; - } - - $StartPosition = max($StartPosition, 1); - - $QueryString = self::get_url(array('page', 'post')); - if ($QueryString != '') { - $QueryString = "&$QueryString"; - } - - $Pages = ''; - - if ($StartPage > 1) { - $Pages .= "<< First "; - $Pages .= "< Prev | '; - } - //End change - - for ($i = $StartPosition; $i <= $StopPage; $i++) { - if ($i != $StartPage) { - $Pages .= ""; - } - $Pages .= ''; - if ($i * $ItemsPerPage > $TotalRecords) { - $Pages .= ((($i - 1) * $ItemsPerPage) + 1)."-$TotalRecords"; - } else { - $Pages .= ((($i - 1) * $ItemsPerPage) + 1).'-'.($i * $ItemsPerPage); - } - - $Pages .= ''; - if ($i != $StartPage) { - $Pages .= ''; - } - if ($i < $StopPage) { - $Pages .= ' | '; - } - } - - if ($StartPage && $StartPage < $TotalPages) { - $Pages .= " | Next > '; - $Pages .= " Last >>"; - } - } - if ($TotalPages > 1) { - return $Pages; - } - } - - - /** - * Format a size in bytes as a human readable string in KiB/MiB/... - * Note: KiB, MiB, etc. are the IEC units, which are in base 2. - * KB, MB are the SI units, which are in base 10. - * - * @param int $Size - * @param int $Levels Number of decimal places. Defaults to 2, unless the size >= 1TB, in which case it defaults to 4. - * @return string formatted number. - */ - public static function get_size($Size, $Levels = 2) { - $Units = array(' B', ' KB', ' MB', ' GB', ' TB', ' PB', ' EB', ' ZB', ' YB'); - $Size = (double)$Size; - for ($Steps = 0; abs($Size) >= 1024; $Size /= 1024, $Steps++) { - } - if (func_num_args() == 1 && $Steps >= 4) { - $Levels++; - } - return number_format($Size, $Levels) . $Units[$Steps]; - } - - - /** - * Format a number as a multiple of its highest power of 1000 (e.g. 10035 -> '10.04k') - * - * @param int $Number - * @return string formatted number. - */ - public static function human_format($Number) { - $Steps = 0; - while ($Number >= 1000) { - $Steps++; - $Number = $Number / 1000; - } - switch ($Steps) { - case 0: return round($Number); break; - case 1: return round($Number, 2).'k'; break; - case 2: return round($Number, 2).'M'; break; - case 3: return round($Number, 2).'G'; break; - case 4: return round($Number, 2).'T'; break; - case 5: return round($Number, 2).'P'; break; - default: - return round($Number, 2).'E + '.$Steps * 3; - } - } - - - /** - * Given a formatted string of a size, get the number of bytes it represents. - * - * @param string $Size formatted size string, e.g. 123.45k - * @return Number of bytes it represents, e.g. (123.45 * 1024) - */ - public static function get_bytes($Size) { - list($Value, $Unit) = sscanf($Size, "%f%s"); - $Unit = ltrim($Unit); - if (empty($Unit)) { - return $Value ? round($Value) : 0; - } - switch (strtolower($Unit[0])) { - case 'k': return round($Value * 1024); - case 'm': return round($Value * 1048576); - case 'g': return round($Value * 1073741824); - case 't': return round($Value * 1099511627776); - default: return 0; - } - } - - - /** - * Reverse the effects of display_str - un-sanitize HTML. - * Use sparingly. - * - * @param string $Str the string to unsanitize - * @return unsanitized string - */ - // Use sparingly - public static function undisplay_str($Str) { - return mb_convert_encoding($Str, 'UTF-8', 'HTML-ENTITIES'); - } - - - /** - * Echo data sent in a GET form field, useful for text areas. - * - * @param string $Index the name of the form field - * @param boolean $Return if set to true, value is returned instead of echoed. - * @return Sanitized value of field index if $Return == true - */ - public static function form($Index, $Return = false) { - if (!empty($_GET[$Index])) { - if ($Return) { - return display_str($_GET[$Index]); - } else { - echo display_str($_GET[$Index]); - } - } - } - - - /** - * Convenience function to echo out selected="selected" and checked="checked" so you don't have to. - * - * @param string $Name the name of the option in the select (or field in $Array) - * @param mixed $Value the value that the option must be for the option to be marked as selected or checked - * @param string $Attribute The value returned/echoed is $Attribute="$Attribute" with a leading space - * @param array $Array The array the option is in, defaults to GET. - * @return void - */ - public static function selected($Name, $Value, $Attribute = 'selected', $Array = array()) { - if (empty($Array)) { - $Array = $_GET; - } - if (isset($Array[$Name]) && $Array[$Name] !== '') { - if ($Array[$Name] == $Value) { - echo " $Attribute=\"$Attribute\""; - } - } - } - - /** - * Return a CSS class name if certain conditions are met. Mainly useful to mark links as 'active' - * - * @param mixed $Target The variable to compare all values against - * @param mixed $Tests The condition values. Type and dimension determines test type - * Scalar: $Tests must be equal to $Target for a match - * Vector: All elements in $Tests must correspond to equal values in $Target - * 2-dimensional array: At least one array must be identical to $Target - * @param string $ClassName CSS class name to return - * @param bool $AddAttribute Whether to include the "class" attribute in the output - * @param string $UserIDKey Key in _REQUEST for a user ID parameter, which if given will be compared to G::$LoggedUser[ID] - * - * @return string class name on match, otherwise an empty string - */ - public static function add_class($Target, $Tests, $ClassName, $AddAttribute, $UserIDKey = false) { - if ($UserIDKey && isset($_REQUEST[$UserIDKey]) && G::$LoggedUser['ID'] != $_REQUEST[$UserIDKey]) { - return ''; - } - $Pass = true; - if (!is_array($Tests)) { - // Scalars are nice and easy - $Pass = $Tests === $Target; - } elseif (!is_array($Tests[0])) { - // Test all values in vectors - foreach ($Tests as $Type => $Part) { - if (!isset($Target[$Type]) || $Target[$Type] !== $Part) { - $Pass = false; - break; - } - } - } else { - // Loop to the end of the array or until we find a matching test - foreach ($Tests as $Test) { - $Pass = true; - // If $Pass remains true after this test, it's a match - foreach ($Test as $Type => $Part) { - if (!isset($Target[$Type]) || $Target[$Type] !== $Part) { - $Pass = false; - break; - } - } - if ($Pass) { - break; - } - } - } - if (!$Pass) { - return ''; - } - if ($AddAttribute) { - return " class=\"$ClassName\""; - } - return " $ClassName"; - } - - - /** - * Detect the encoding of a string and transform it to UTF-8. - * - * @param string $Str - * @return UTF-8 encoded version of $Str - */ - public static function make_utf8($Str) { - if ($Str != '') { - if (self::is_utf8($Str)) { - $Encoding = 'UTF-8'; - } - if (empty($Encoding)) { - $Encoding = mb_detect_encoding($Str, 'UTF-8, ISO-8859-1'); - } - if (empty($Encoding)) { - $Encoding = 'ISO-8859-1'; - } - if ($Encoding == 'UTF-8') { - return $Str; - } else { - return @mb_convert_encoding($Str, 'UTF-8', $Encoding); - } - } - } - - /** - * Magical function. - * - * @param string $Str function to detect encoding on. - * @return true if the string is in UTF-8. - */ - public static function is_utf8($Str) { - return preg_match('%^(?: - [\x09\x0A\x0D\x20-\x7E] // ASCII - | [\xC2-\xDF][\x80-\xBF] // non-overlong 2-byte - | \xE0[\xA0-\xBF][\x80-\xBF] // excluding overlongs - | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} // straight 3-byte - | \xED[\x80-\x9F][\x80-\xBF] // excluding surrogates - | \xF0[\x90-\xBF][\x80-\xBF]{2} // planes 1-3 - | [\xF1-\xF3][\x80-\xBF]{3} // planes 4-15 - | \xF4[\x80-\x8F][\x80-\xBF]{2} // plane 16 - )*$%xs', $Str - ); - } - - /** - * Modified accessor for the $TorrentLabels array - * - * Converts $Text to lowercase and strips non-word characters - * - * @param string $Text Search string - * @return string CSS class(es) - */ - public static function find_torrent_label_class($Text) { - $Index = mb_eregi_replace('(?:[^\w\d\s]+)', '', strtolower($Text)); - if (isset(self::$TorrentLabels[$Index])) { - return self::$TorrentLabels[$Index]; - } else { - return self::$TorrentLabels['default']; - } - } - - /** - * Creates a strong element that notes the torrent's state. - * E.g.: snatched/freeleech/neutral leech/reported - * - * The CSS class is inferred using find_torrent_label_class($Text) - * - * @param string $Text Display text - * @param string $Class Custom CSS class - * @return string element - */ - public static function torrent_label($Text, $Class = '') { - if (empty($Class)) { - $Class = self::find_torrent_label_class($Text); - } - return sprintf('%2$s', - display_str($Class), display_str($Text)); - } - - /** - * Formats a CSS class name from a Category ID - * @global array $Categories - * @param int|string $CategoryID This number will be subtracted by one - * @return string - */ - public static function css_category($CategoryID = 1) { - global $Categories; - return 'cats_' . strtolower(str_replace(array('-', ' '), '', - $Categories[$CategoryID - 1])); - } + /** + * Torrent Labels + * Map a common display string to a CSS class + * Indexes are lower case + * Note the "tl_" prefix for "torrent label" + * + * There are five basic types: + * * tl_free (leech status) + * * tl_snatched + * * tl_reported + * * tl_approved + * * tl_notice (default) + * + * @var array Strings + */ + private static $TorrentLabels = array( + 'default' => 'tl_notice', + 'snatched' => 'tl_snatched', + + 'freeleech' => 'tl_free', + 'neutral leech' => 'tl_free tl_neutral', + 'personal freeleech'=> 'tl_free tl_personal', + + 'reported' => 'tl_reported', + 'bad tags' => 'tl_reported tl_bad_tags', + 'bad folders' => 'tl_reported tl_bad_folders', + 'bad file names'=> 'tl_reported tl_bad_file_names', + 'missing lineage'=> 'tl_reported tl_missing_lineage', + 'cassette approved' => 'tl_approved tl_cassete', + 'lossy master approved' => 'tl_approved tl_lossy_master', + 'lossy web approved' => 'tl_approved tl_lossy_web' + ); + + /** + * Shorten a string + * + * @param string $Str string to cut + * @param int $Length cut at length + * @param bool $Hard force cut at length instead of at closest word + * @param bool $ShowDots Show dots at the end + * @return string formatted string + */ + public static function cut_string($Str, $Length, $Hard = false, $ShowDots = true) { + if (mb_strlen($Str, 'UTF-8') > $Length) { + if ($Hard == 0) { + // Not hard, cut at closest word + $CutDesc = mb_substr($Str, 0, $Length, 'UTF-8'); + $DescArr = explode(' ', $CutDesc); + if (count($DescArr) > 1) { + array_pop($DescArr); + $CutDesc = implode(' ', $DescArr); + } + if ($ShowDots) { + //TODO: should we replace the three dots with an ellipsis character? + $CutDesc .= '...'; + } + } else { + $CutDesc = mb_substr($Str, 0, $Length, 'UTF-8'); + if ($ShowDots) { + $CutDesc .= '...'; + } + } + return $CutDesc; + } else { + return $Str; + } + } + + + /** + * Gets the CSS class corresponding to a ratio + * + * @param $Ratio ratio to get the css class for + * @return string the CSS class corresponding to the ratio range + */ + public static function get_ratio_color($Ratio) { + if ($Ratio < 0.1) { return 'r00'; } + if ($Ratio < 0.2) { return 'r01'; } + if ($Ratio < 0.3) { return 'r02'; } + if ($Ratio < 0.4) { return 'r03'; } + if ($Ratio < 0.5) { return 'r04'; } + if ($Ratio < 0.6) { return 'r05'; } + if ($Ratio < 0.7) { return 'r06'; } + if ($Ratio < 0.8) { return 'r07'; } + if ($Ratio < 0.9) { return 'r08'; } + if ($Ratio < 1) { return 'r09'; } + if ($Ratio < 2) { return 'r10'; } + if ($Ratio < 5) { return 'r20'; } + return 'r50'; + } + + + /** + * Calculates and formats a ratio. + * + * @param int $Dividend AKA numerator + * @param int $Divisor + * @param boolean $Color if true, ratio will be coloured. + * @return string formatted ratio HTML + */ + public static function get_ratio_html($Dividend, $Divisor, $Color = true) { + $Ratio = self::get_ratio($Dividend, $Divisor); + + if ($Ratio === false) { + return '--'; + } + if ($Ratio === '∞') { + return '∞'; + } + if ($Color) { + $Ratio = sprintf('%s', + self::get_ratio_color($Ratio), + self::get_ratio($Dividend, $Divisor, 5), + $Ratio + ); + } + + return $Ratio; + } + + /** + * Returns ratio + * @param int $Dividend + * @param int $Divisor + * @param int $Decimal floor to n decimals (e.g. subtract .005 to floor to 2 decimals) + * @return boolean|string + */ + public static function get_ratio($Dividend, $Divisor, $Decimal = 2) { + if ($Divisor == 0 && $Dividend == 0) { + return false; + } + if ($Divisor == 0) { + return '∞'; + } + return number_format(max($Dividend / $Divisor - (0.5 / pow(10, $Decimal)), 0), $Decimal); + } + + /** + * Gets the query string of the current page, minus the parameters in $Exclude + * + * @param array $Exclude Query string parameters to leave out, or blank to include all parameters. + * @param bool $Escape Whether to return a string prepared for HTML output + * @param bool $Sort Whether to sort the parameters by key + * @return An optionally HTML sanatized query string + */ + public static function get_url($Exclude = false, $Escape = true, $Sort = false) { + if ($Exclude !== false) { + $Separator = $Escape ? '&' : '&'; + $QueryItems = NULL; + parse_str($_SERVER['QUERY_STRING'], $QueryItems); + foreach ($Exclude as $Key) { + unset($QueryItems[$Key]); + } + if ($Sort) { + ksort($QueryItems); + } + return http_build_query($QueryItems, '', $Separator); + } else { + return $Escape ? display_str($_SERVER['QUERY_STRING']) : $_SERVER['QUERY_STRING']; + } + } + + + /** + * Finds what page we're on and gives it to us, as well as the LIMIT clause for SQL + * Takes in $_GET['page'] as an additional input + * + * @param int $PerPage Results to show per page + * @param int $DefaultResult Optional, which result's page we want if no page is specified + * If this parameter is not specified, we will default to page 1 + * + * @return array(int, string) What page we are on, and what to use in the LIMIT section of a query + * e.g. "SELECT [...] LIMIT $Limit;" + */ + public static function page_limit($PerPage, $DefaultResult = 1) { + if (!isset($_GET['page'])) { + $Page = ceil($DefaultResult / $PerPage); + if ($Page == 0) { + $Page = 1; + } + $Limit = $PerPage; + } else { + if (!is_number($_GET['page'])) { + error(0); + } + $Page = $_GET['page']; + if ($Page <= 0) { + $Page = 1; + } + $Limit = $PerPage * $Page - $PerPage . ", $PerPage"; + } + return array($Page, $Limit); + } + + // A9 magic. Some other poor soul can write the phpdoc. + // For data stored in memcached catalogues (giant arrays), e.g. forum threads + public static function catalogue_limit($Page, $PerPage, $CatalogueSize = 500) { + $CatalogueID = floor(($PerPage * $Page - $PerPage) / $CatalogueSize); + $CatalogueLimit = ($CatalogueID * $CatalogueSize).", $CatalogueSize"; + return array($CatalogueID, $CatalogueLimit); + } + + public static function catalogue_select($Catalogue, $Page, $PerPage, $CatalogueSize = 500) { + return array_slice($Catalogue, (($PerPage * $Page - $PerPage) % $CatalogueSize), $PerPage, true); + } + + + /* Get pages + * Returns a page list, given certain information about the pages. + * + * @param int $StartPage: The current record the page you're on starts with. + * e.g. if you're on page 2 of a forum thread with 25 posts per page, $StartPage is 25. + * If you're on page 1, $StartPage is 0. + * FIXME: I don't think this is right and you want to pass in a number 1 - max page + * @param int $TotalRecords: The total number of records in the result set. + * e.g. if you're on a forum thread with 152 posts, $TotalRecords is 152. + * @param int $ItemsPerPage: Self-explanatory. The number of records shown on each page + * e.g. if there are 25 posts per forum page, $ItemsPerPage is 25. + * @param int $ShowPages: The number of page links that are shown. + * e.g. If there are 20 pages that exist, but $ShowPages is only 11, only 11 links will be shown. + * @param string $Anchor A URL fragment to attach to the links. + * e.g. '#comment12' + * @return A sanitized HTML page listing. + */ + public static function get_pages($StartPage, $TotalRecords, $ItemsPerPage, $ShowPages = 11, $Anchor = '') { + global $Document, $Method; + $Location = "$Document.php"; + $StartPage = ceil($StartPage); + $TotalPages = 0; + if ($TotalRecords > 0) { + $StartPage = min($StartPage, ceil($TotalRecords / $ItemsPerPage)); + + $ShowPages--; + $TotalPages = ceil($TotalRecords / $ItemsPerPage); + + if ($TotalPages > $ShowPages) { + $StartPosition = $StartPage - round($ShowPages / 2); + + if ($StartPosition <= 0) { + $StartPosition = 1; + } else { + if ($StartPosition >= ($TotalPages - $ShowPages)) { + $StartPosition = $TotalPages - $ShowPages; + } + } + + $StopPage = $ShowPages + $StartPosition; + + } else { + $StopPage = $TotalPages; + $StartPosition = 1; + } + + $StartPosition = max($StartPosition, 1); + + $QueryString = self::get_url(array('page', 'post')); + if ($QueryString != '') { + $QueryString = "&$QueryString"; + } + + $Pages = ''; + + if ($StartPage > 1) { + $Pages .= "<< First "; + $Pages .= "< Prev | '; + } + //End change + + for ($i = $StartPosition; $i <= $StopPage; $i++) { + if ($i != $StartPage) { + $Pages .= ""; + } + $Pages .= ''; + if ($i * $ItemsPerPage > $TotalRecords) { + $Pages .= ((($i - 1) * $ItemsPerPage) + 1)."-$TotalRecords"; + } else { + $Pages .= ((($i - 1) * $ItemsPerPage) + 1).'-'.($i * $ItemsPerPage); + } + + $Pages .= ''; + if ($i != $StartPage) { + $Pages .= ''; + } + if ($i < $StopPage) { + $Pages .= ' | '; + } + } + + if ($StartPage && $StartPage < $TotalPages) { + $Pages .= " | Next > '; + $Pages .= " Last >>"; + } + } + if ($TotalPages > 1) { + return $Pages; + } + } + + + /** + * Format a size in bytes as a human readable string in KiB/MiB/... + * Note: KiB, MiB, etc. are the IEC units, which are in base 2. + * KB, MB are the SI units, which are in base 10. + * + * @param int $Size + * @param int $Levels Number of decimal places. Defaults to 2, unless the size >= 1TB, in which case it defaults to 4. + * @return string formatted number. + */ + public static function get_size($Size, $Levels = 2) { + $Units = array(' B', ' KB', ' MB', ' GB', ' TB', ' PB', ' EB', ' ZB', ' YB'); + $Size = (double)$Size; + for ($Steps = 0; abs($Size) >= 1024; $Size /= 1024, $Steps++) { + } + if (func_num_args() == 1 && $Steps >= 4) { + $Levels++; + } + return number_format($Size, $Levels) . $Units[$Steps]; + } + + + /** + * Format a number as a multiple of its highest power of 1000 (e.g. 10035 -> '10.04k') + * + * @param int $Number + * @return string formatted number. + */ + public static function human_format($Number) { + $Steps = 0; + while ($Number >= 1000) { + $Steps++; + $Number = $Number / 1000; + } + switch ($Steps) { + case 0: return round($Number); break; + case 1: return round($Number, 2).'k'; break; + case 2: return round($Number, 2).'M'; break; + case 3: return round($Number, 2).'G'; break; + case 4: return round($Number, 2).'T'; break; + case 5: return round($Number, 2).'P'; break; + default: + return round($Number, 2).'E + '.$Steps * 3; + } + } + + + /** + * Given a formatted string of a size, get the number of bytes it represents. + * + * @param string $Size formatted size string, e.g. 123.45k + * @return Number of bytes it represents, e.g. (123.45 * 1024) + */ + public static function get_bytes($Size) { + list($Value, $Unit) = sscanf($Size, "%f%s"); + $Unit = ltrim($Unit); + if (empty($Unit)) { + return $Value ? round($Value) : 0; + } + switch (strtolower($Unit[0])) { + case 'k': return round($Value * 1024); + case 'm': return round($Value * 1048576); + case 'g': return round($Value * 1073741824); + case 't': return round($Value * 1099511627776); + default: return 0; + } + } + + + /** + * Reverse the effects of display_str - un-sanitize HTML. + * Use sparingly. + * + * @param string $Str the string to unsanitize + * @return unsanitized string + */ + // Use sparingly + public static function undisplay_str($Str) { + return mb_convert_encoding($Str, 'UTF-8', 'HTML-ENTITIES'); + } + + + /** + * Echo data sent in a GET form field, useful for text areas. + * + * @param string $Index the name of the form field + * @param boolean $Return if set to true, value is returned instead of echoed. + * @return Sanitized value of field index if $Return == true + */ + public static function form($Index, $Return = false) { + if (!empty($_GET[$Index])) { + if ($Return) { + return display_str($_GET[$Index]); + } else { + echo display_str($_GET[$Index]); + } + } + } + + + /** + * Convenience function to echo out selected="selected" and checked="checked" so you don't have to. + * + * @param string $Name the name of the option in the select (or field in $Array) + * @param mixed $Value the value that the option must be for the option to be marked as selected or checked + * @param string $Attribute The value returned/echoed is $Attribute="$Attribute" with a leading space + * @param array $Array The array the option is in, defaults to GET. + * @return void + */ + public static function selected($Name, $Value, $Attribute = 'selected', $Array = []) { + if (empty($Array)) { + $Array = $_GET; + } + if (isset($Array[$Name]) && $Array[$Name] !== '') { + if ($Array[$Name] == $Value) { + echo " $Attribute=\"$Attribute\""; + } + } + } + + /** + * Return a CSS class name if certain conditions are met. Mainly useful to mark links as 'active' + * + * @param mixed $Target The variable to compare all values against + * @param mixed $Tests The condition values. Type and dimension determines test type + * Scalar: $Tests must be equal to $Target for a match + * Vector: All elements in $Tests must correspond to equal values in $Target + * 2-dimensional array: At least one array must be identical to $Target + * @param string $ClassName CSS class name to return + * @param bool $AddAttribute Whether to include the "class" attribute in the output + * @param string $UserIDKey Key in _REQUEST for a user ID parameter, which if given will be compared to G::$LoggedUser[ID] + * + * @return string class name on match, otherwise an empty string + */ + public static function add_class($Target, $Tests, $ClassName, $AddAttribute, $UserIDKey = false) { + if ($UserIDKey && isset($_REQUEST[$UserIDKey]) && G::$LoggedUser['ID'] != $_REQUEST[$UserIDKey]) { + return ''; + } + $Pass = true; + if (!is_array($Tests)) { + // Scalars are nice and easy + $Pass = $Tests === $Target; + } elseif (!is_array($Tests[0])) { + // Test all values in vectors + foreach ($Tests as $Type => $Part) { + if (!isset($Target[$Type]) || $Target[$Type] !== $Part) { + $Pass = false; + break; + } + } + } else { + // Loop to the end of the array or until we find a matching test + foreach ($Tests as $Test) { + $Pass = true; + // If $Pass remains true after this test, it's a match + foreach ($Test as $Type => $Part) { + if (!isset($Target[$Type]) || $Target[$Type] !== $Part) { + $Pass = false; + break; + } + } + if ($Pass) { + break; + } + } + } + if (!$Pass) { + return ''; + } + if ($AddAttribute) { + return " class=\"$ClassName\""; + } + return " $ClassName"; + } + + + /** + * Detect the encoding of a string and transform it to UTF-8. + * + * @param string $Str + * @return UTF-8 encoded version of $Str + */ + public static function make_utf8($Str) { + if ($Str != '') { + if (self::is_utf8($Str)) { + $Encoding = 'UTF-8'; + } + if (empty($Encoding)) { + $Encoding = mb_detect_encoding($Str, 'UTF-8, ISO-8859-1'); + } + if (empty($Encoding)) { + $Encoding = 'ISO-8859-1'; + } + if ($Encoding == 'UTF-8') { + return $Str; + } else { + return @mb_convert_encoding($Str, 'UTF-8', $Encoding); + } + } + } + + /** + * Magical function. + * + * @param string $Str function to detect encoding on. + * @return true if the string is in UTF-8. + */ + public static function is_utf8($Str) { + return preg_match('%^(?: + [\x09\x0A\x0D\x20-\x7E] // ASCII + | [\xC2-\xDF][\x80-\xBF] // non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] // excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} // straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] // excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} // planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} // planes 4-15 + | \xF4[\x80-\x8F][\x80-\xBF]{2} // plane 16 + )*$%xs', $Str + ); + } + + /** + * Modified accessor for the $TorrentLabels array + * + * Converts $Text to lowercase and strips non-word characters + * + * @param string $Text Search string + * @return string CSS class(es) + */ + public static function find_torrent_label_class($Text) { + $Index = mb_eregi_replace('(?:[^\w\d\s]+)', '', strtolower($Text)); + if (isset(self::$TorrentLabels[$Index])) { + return self::$TorrentLabels[$Index]; + } else { + return self::$TorrentLabels['default']; + } + } + + /** + * Creates a strong element that notes the torrent's state. + * E.g.: snatched/freeleech/neutral leech/reported + * + * The CSS class is inferred using find_torrent_label_class($Text) + * + * @param string $Text Display text + * @param string $Class Custom CSS class + * @return string element + */ + public static function torrent_label($Text, $Class = '') { + if (empty($Class)) { + $Class = self::find_torrent_label_class($Text); + } + return sprintf('%2$s', + display_str($Class), display_str($Text)); + } + + /** + * Formats a CSS class name from a Category ID + * @global array $Categories + * @param int|string $CategoryID This number will be subtracted by one + * @return string + */ + public static function css_category($CategoryID = 1) { + global $Categories; + return 'cats_' . strtolower(str_replace(array('-', ' '), '', + $Categories[$CategoryID - 1])); + } } diff --git a/classes/forums.class.php b/classes/forums.class.php index 7b36ebe7c..427070aa6 100644 --- a/classes/forums.class.php +++ b/classes/forums.class.php @@ -1,319 +1,357 @@ -get_value('thread_' . $ThreadID . '_info')) || !isset($ThreadInfo['Ranking'])) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - t.Title, - t.ForumID, - t.IsLocked, - t.IsSticky, - COUNT(fp.id) AS Posts, - t.LastPostAuthorID, - ISNULL(p.TopicID) AS NoPoll, - t.StickyPostID, - t.AuthorID as OP, - t.Ranking, - MAX(fp.AddedTime) as LastPostTime - FROM forums_topics AS t - JOIN forums_posts AS fp ON fp.TopicID = t.ID - LEFT JOIN forums_polls AS p ON p.TopicID = t.ID - WHERE t.ID = '$ThreadID' - GROUP BY fp.TopicID"); - if (!G::$DB->has_results()) { - G::$DB->set_query_id($QueryID); - return null; - } - $ThreadInfo = G::$DB->next_record(MYSQLI_ASSOC, false); - if ($ThreadInfo['StickyPostID']) { - $ThreadInfo['Posts']--; - G::$DB->query( - "SELECT - p.ID, - p.AuthorID, - p.AddedTime, - p.Body, - p.EditedUserID, - p.EditedTime, - ed.Username - FROM forums_posts AS p - LEFT JOIN users_main AS ed ON ed.ID = p.EditedUserID - WHERE p.TopicID = '$ThreadID' - AND p.ID = '" . $ThreadInfo['StickyPostID'] . "'"); - list ($ThreadInfo['StickyPost']) = G::$DB->to_array(false, MYSQLI_ASSOC); - } - G::$DB->set_query_id($QueryID); - if (!$SelectiveCache || !$ThreadInfo['IsLocked'] || $ThreadInfo['IsSticky']) { - G::$Cache->cache_value('thread_' . $ThreadID . '_info', $ThreadInfo, 0); - } - } - if ($Return) { - return $ThreadInfo; - } - } + /** + * Get information on a thread. + * + * @param int $ThreadID + * the thread ID. + * @param boolean $Return + * indicates whether thread info should be returned. + * @param Boolean $SelectiveCache + * cache thread info/ + * @return array holding thread information. + */ + public static function get_thread_info($ThreadID, $Return = true, $SelectiveCache = false) { + if ((!$ThreadInfo = G::$Cache->get_value('thread_' . $ThreadID . '_info')) || !isset($ThreadInfo['Ranking'])) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + t.Title, + t.ForumID, + t.IsLocked, + t.IsSticky, + COUNT(fp.id) AS Posts, + t.LastPostAuthorID, + ISNULL(p.TopicID) AS NoPoll, + t.StickyPostID, + t.AuthorID as OP, + t.Ranking, + MAX(fp.AddedTime) as LastPostTime + FROM forums_topics AS t + JOIN forums_posts AS fp ON fp.TopicID = t.ID + LEFT JOIN forums_polls AS p ON p.TopicID = t.ID + WHERE t.ID = '$ThreadID' + GROUP BY fp.TopicID"); + if (!G::$DB->has_results()) { + G::$DB->set_query_id($QueryID); + return null; + } + $ThreadInfo = G::$DB->next_record(MYSQLI_ASSOC, false); + if ($ThreadInfo['StickyPostID']) { + $ThreadInfo['Posts']--; + G::$DB->query( + "SELECT + p.ID, + p.AuthorID, + p.AddedTime, + p.Body, + p.EditedUserID, + p.EditedTime, + ed.Username + FROM forums_posts AS p + LEFT JOIN users_main AS ed ON ed.ID = p.EditedUserID + WHERE p.TopicID = '$ThreadID' + AND p.ID = '" . $ThreadInfo['StickyPostID'] . "'"); + list ($ThreadInfo['StickyPost']) = G::$DB->to_array(false, MYSQLI_ASSOC); + } + G::$DB->set_query_id($QueryID); + if (!$SelectiveCache || !$ThreadInfo['IsLocked'] || $ThreadInfo['IsSticky']) { + G::$Cache->cache_value('thread_' . $ThreadID . '_info', $ThreadInfo, 0); + } + } + if ($Return) { + return $ThreadInfo; + } + } - /** - * Checks whether user has permissions on a forum. - * - * @param int $ForumID - * the forum ID. - * @param string $Perm - * the permissision to check, defaults to 'Read' - * @return boolean true if user has permission - */ - public static function check_forumperm($ForumID, $Perm = 'Read') { - $Forums = self::get_forums(); - if (isset(G::$LoggedUser['CustomForums'][$ForumID]) && G::$LoggedUser['CustomForums'][$ForumID] == 1) { - return true; - } - if ($ForumID == DONOR_FORUM && Donations::has_donor_forum(G::$LoggedUser['ID'])) { - return true; - } - if ($Forums[$ForumID]['MinClass' . $Perm] > G::$LoggedUser['Class'] && (!isset(G::$LoggedUser['CustomForums'][$ForumID]) || G::$LoggedUser['CustomForums'][$ForumID] == 0)) { - return false; - } - if (isset(G::$LoggedUser['CustomForums'][$ForumID]) && G::$LoggedUser['CustomForums'][$ForumID] == 0) { - return false; - } - return true; - } + /** + * Checks whether user has permissions on a forum. + * + * @param int $ForumID + * the forum ID. + * @param string $Perm + * the permissision to check, defaults to 'Read' + * @return boolean true if user has permission + */ + public static function check_forumperm($ForumID, $Perm = 'Read') { + $Forums = self::get_forums(); + if (isset(G::$LoggedUser['CustomForums'][$ForumID]) && G::$LoggedUser['CustomForums'][$ForumID] == 1) { + return true; + } + if ($ForumID == DONOR_FORUM && Donations::has_donor_forum(G::$LoggedUser['ID'])) { + return true; + } + if ($Forums[$ForumID]['MinClass' . $Perm] > G::$LoggedUser['Class'] && (!isset(G::$LoggedUser['CustomForums'][$ForumID]) || G::$LoggedUser['CustomForums'][$ForumID] == 0)) { + return false; + } + if (isset(G::$LoggedUser['CustomForums'][$ForumID]) && G::$LoggedUser['CustomForums'][$ForumID] == 0) { + return false; + } + return true; + } - /** - * Gets basic info on a forum. - * - * @param int $ForumID - * the forum ID. - */ - public static function get_forum_info($ForumID) { - $Forum = G::$Cache->get_value("ForumInfo_$ForumID"); - if (!$Forum) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - Name, - MinClassRead, - MinClassWrite, - MinClassCreate, - COUNT(forums_topics.ID) AS Topics - FROM forums - LEFT JOIN forums_topics ON forums_topics.ForumID = forums.ID - WHERE forums.ID = '$ForumID' - GROUP BY ForumID"); - if (!G::$DB->has_results()) { - return false; - } - // Makes an array, with $Forum['Name'], etc. - $Forum = G::$DB->next_record(MYSQLI_ASSOC); + /** + * Gets basic info on a forum. + * + * @param int $ForumID + * the forum ID. + */ + public static function get_forum_info($ForumID) { + $Forum = G::$Cache->get_value("ForumInfo_$ForumID"); + if (!$Forum) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + Name, + MinClassRead, + MinClassWrite, + MinClassCreate, + COUNT(forums_topics.ID) AS Topics + FROM forums + LEFT JOIN forums_topics ON forums_topics.ForumID = forums.ID + WHERE forums.ID = '$ForumID' + GROUP BY ForumID"); + if (!G::$DB->has_results()) { + return false; + } + // Makes an array, with $Forum['Name'], etc. + $Forum = G::$DB->next_record(MYSQLI_ASSOC); - G::$DB->set_query_id($QueryID); + G::$DB->set_query_id($QueryID); - G::$Cache->cache_value("ForumInfo_$ForumID", $Forum, 86400); - } - return $Forum; - } + G::$Cache->cache_value("ForumInfo_$ForumID", $Forum, 86400); + } + return $Forum; + } - /** - * Get the forum categories - * @return array ForumCategoryID => Name - */ - public static function get_forum_categories() { - $ForumCats = G::$Cache->get_value('forums_categories'); - if ($ForumCats === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT ID, Name - FROM forums_categories - ORDER BY Sort, Name"); - $ForumCats = array(); - while (list ($ID, $Name) = G::$DB->next_record()) { - $ForumCats[$ID] = $Name; - } - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value('forums_categories', $ForumCats, 0); - } - return $ForumCats; - } + /** + * Get the forum categories + * @return array ForumCategoryID => Name + */ + public static function get_forum_categories() { + $ForumCats = G::$Cache->get_value('forums_categories'); + if ($ForumCats === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT ID, Name + FROM forums_categories + ORDER BY Sort, Name"); + $ForumCats = []; + while (list ($ID, $Name) = G::$DB->next_record()) { + $ForumCats[$ID] = $Name; + } + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value('forums_categories', $ForumCats, 0); + } + return $ForumCats; + } - /** - * Get the forums - * @return array ForumID => (various information about the forum) - */ - public static function get_forums() { - if (!$Forums = G::$Cache->get_value('forums_list')) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - f.ID, - f.CategoryID, - f.Name, - f.Description, - f.MinClassRead AS MinClassRead, - f.MinClassWrite AS MinClassWrite, - f.MinClassCreate AS MinClassCreate, - f.NumTopics, - f.NumPosts, - f.LastPostID, - f.LastPostAuthorID, - f.LastPostTopicID, - f.LastPostTime, - 0 AS SpecificRules, - t.Title, - t.IsLocked AS Locked, - t.IsSticky AS Sticky - FROM forums AS f - JOIN forums_categories AS fc ON fc.ID = f.CategoryID - LEFT JOIN forums_topics AS t ON t.ID = f.LastPostTopicID - GROUP BY f.ID - ORDER BY fc.Sort, fc.Name, f.CategoryID, f.Sort, f.Name"); - $Forums = G::$DB->to_array('ID', MYSQLI_ASSOC, false); + /** + * Get the forums + * @return array ForumID => (various information about the forum) + */ + public static function get_forums() { + if (!$Forums = G::$Cache->get_value('forums_list')) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + f.ID, + f.CategoryID, + f.Name, + f.Description, + f.MinClassRead AS MinClassRead, + f.MinClassWrite AS MinClassWrite, + f.MinClassCreate AS MinClassCreate, + f.NumTopics, + f.NumPosts, + f.LastPostID, + f.LastPostAuthorID, + f.LastPostTopicID, + f.LastPostTime, + 0 AS SpecificRules, + t.Title, + t.IsLocked AS Locked, + t.IsSticky AS Sticky + FROM forums AS f + JOIN forums_categories AS fc ON fc.ID = f.CategoryID + LEFT JOIN forums_topics AS t ON t.ID = f.LastPostTopicID + GROUP BY f.ID + ORDER BY fc.Sort, fc.Name, f.CategoryID, f.Sort, f.Name"); + $Forums = G::$DB->to_array('ID', MYSQLI_ASSOC, false); - G::$DB->query(" - SELECT ForumID, ThreadID - FROM forums_specific_rules"); - $SpecificRules = array(); - while (list($ForumID, $ThreadID) = G::$DB->next_record(MYSQLI_NUM, false)) { - $SpecificRules[$ForumID][] = $ThreadID; - } - G::$DB->set_query_id($QueryID); - foreach ($Forums as $ForumID => &$Forum) { - if (isset($SpecificRules[$ForumID])) { - $Forum['SpecificRules'] = $SpecificRules[$ForumID]; - } else { - $Forum['SpecificRules'] = array(); - } - } - G::$Cache->cache_value('forums_list', $Forums, 0); - } - return $Forums; - } + G::$DB->query(" + SELECT ForumID, ThreadID + FROM forums_specific_rules"); + $SpecificRules = []; + while (list($ForumID, $ThreadID) = G::$DB->next_record(MYSQLI_NUM, false)) { + $SpecificRules[$ForumID][] = $ThreadID; + } + G::$DB->set_query_id($QueryID); + foreach ($Forums as $ForumID => &$Forum) { + if (isset($SpecificRules[$ForumID])) { + $Forum['SpecificRules'] = $SpecificRules[$ForumID]; + } else { + $Forum['SpecificRules'] = []; + } + } + G::$Cache->cache_value('forums_list', $Forums, 0); + } + return $Forums; + } - /** - * Get all forums that the current user has special access to ("Extra forums" in the profile) - * @return array Array of ForumIDs - */ - public static function get_permitted_forums() { - return isset(G::$LoggedUser['CustomForums']) ? array_keys(G::$LoggedUser['CustomForums'], 1) : array(); - } + /** + * Get all forums that the current user has special access to ("Extra forums" in the profile) + * @return array Array of ForumIDs + */ + public static function get_permitted_forums() { + return isset(G::$LoggedUser['CustomForums']) ? array_keys(G::$LoggedUser['CustomForums'], 1) : []; + } - /** - * Get all forums that the current user does not have access to ("Restricted forums" in the profile) - * @return array Array of ForumIDs - */ - public static function get_restricted_forums() { - return isset(G::$LoggedUser['CustomForums']) ? array_keys(G::$LoggedUser['CustomForums'], 0) : array(); - } + /** + * Get all forums that the current user does not have access to ("Restricted forums" in the profile) + * @return array Array of ForumIDs + */ + public static function get_restricted_forums() { + return isset(G::$LoggedUser['CustomForums']) ? array_keys(G::$LoggedUser['CustomForums'], 0) : []; + } - /** - * Get the last read posts for the current user - * @param array $Forums Array of forums as returned by self::get_forums() - * @return array TopicID => array(TopicID, PostID, Page) where PostID is the ID of the last read post and Page is the page on which that post is - */ - public static function get_last_read($Forums) { - if (isset(G::$LoggedUser['PostsPerPage'])) { - $PerPage = G::$LoggedUser['PostsPerPage']; - } else { - $PerPage = POSTS_PER_PAGE; - } - $TopicIDs = array(); - foreach ($Forums as $Forum) { - if (!empty($Forum['LastPostTopicID'])) { - $TopicIDs[] = $Forum['LastPostTopicID']; - } - } - if (!empty($TopicIDs)) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - l.TopicID, - l.PostID, - CEIL( - ( - SELECT - COUNT(p.ID) - FROM forums_posts AS p - WHERE p.TopicID = l.TopicID - AND p.ID <= l.PostID - ) / $PerPage - ) AS Page - FROM forums_last_read_topics AS l - WHERE l.TopicID IN(" . implode(',', $TopicIDs) . ") AND - l.UserID = '" . G::$LoggedUser['ID'] . "'"); - $LastRead = G::$DB->to_array('TopicID', MYSQLI_ASSOC); - G::$DB->set_query_id($QueryID); - } else { - $LastRead = array(); - } - return $LastRead; - } + /** + * Get the last read posts for the current user + * @param array $Forums Array of forums as returned by self::get_forums() + * @return array TopicID => array(TopicID, PostID, Page) where PostID is the ID of the last read post and Page is the page on which that post is + */ + public static function get_last_read($Forums) { + if (isset(G::$LoggedUser['PostsPerPage'])) { + $PerPage = G::$LoggedUser['PostsPerPage']; + } else { + $PerPage = POSTS_PER_PAGE; + } + $TopicIDs = []; + foreach ($Forums as $Forum) { + if (!empty($Forum['LastPostTopicID'])) { + $TopicIDs[] = $Forum['LastPostTopicID']; + } + } + if (!empty($TopicIDs)) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + l.TopicID, + l.PostID, + CEIL( + ( + SELECT + COUNT(p.ID) + FROM forums_posts AS p + WHERE p.TopicID = l.TopicID + AND p.ID <= l.PostID + ) / $PerPage + ) AS Page + FROM forums_last_read_topics AS l + WHERE l.TopicID IN(" . implode(',', $TopicIDs) . ") AND + l.UserID = '" . G::$LoggedUser['ID'] . "'"); + $LastRead = G::$DB->to_array('TopicID', MYSQLI_ASSOC); + G::$DB->set_query_id($QueryID); + } else { + $LastRead = []; + } + return $LastRead; + } - /** - * Add a note to a topic. - * @param int $TopicID - * @param string $Note - * @param int|null $UserID - * @return boolean - */ - public static function add_topic_note($TopicID, $Note, $UserID = null) { - if ($UserID === null) { - $UserID = G::$LoggedUser['ID']; - } - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - INSERT INTO forums_topic_notes - (TopicID, AuthorID, AddedTime, Body) - VALUES - ($TopicID, $UserID, '" . sqltime() . "', '" . db_string($Note) . "')"); - G::$DB->set_query_id($QueryID); - return (bool)G::$DB->affected_rows(); - } + /** + * Add a note to a topic. + * @param int $TopicID + * @param string $Note + * @param int|null $UserID + * @return boolean + */ + public static function add_topic_note($TopicID, $Note, $UserID = null) { + if ($UserID === null) { + $UserID = G::$LoggedUser['ID']; + } + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + INSERT INTO forums_topic_notes + (TopicID, AuthorID, AddedTime, Body) + VALUES + ($TopicID, $UserID, '" . sqltime() . "', '" . db_string($Note) . "')"); + G::$DB->set_query_id($QueryID); + return (bool)G::$DB->affected_rows(); + } - /** - * Determine if a thread is unread - * @param bool $Locked - * @param bool $Sticky - * @param int $LastPostID - * @param array $LastRead An array as returned by self::get_last_read - * @param int $LastTopicID TopicID of the thread where the most recent post was made - * @param string $LastTime Datetime of the last post - * @return bool - */ - public static function is_unread($Locked, $Sticky, $LastPostID, $LastRead, $LastTopicID, $LastTime) { - return (!$Locked || $Sticky) && $LastPostID != 0 && ((empty($LastRead[$LastTopicID]) || $LastRead[$LastTopicID]['PostID'] < $LastPostID) && strtotime($LastTime) > G::$LoggedUser['CatchupTime']); - } + /** + * Determine if a thread is unread + * @param bool $Locked + * @param bool $Sticky + * @param int $LastPostID + * @param array $LastRead An array as returned by self::get_last_read + * @param int $LastTopicID TopicID of the thread where the most recent post was made + * @param string $LastTime Datetime of the last post + * @return bool + */ + public static function is_unread($Locked, $Sticky, $LastPostID, $LastRead, $LastTopicID, $LastTime) { + return (!$Locked || $Sticky) && $LastPostID != 0 && ((empty($LastRead[$LastTopicID]) || $LastRead[$LastTopicID]['PostID'] < $LastPostID) && strtotime($LastTime) > G::$LoggedUser['CatchupTime']); + } - /** - * Create the part of WHERE in the sql queries used to filter forums for a - * specific user (MinClassRead, restricted and permitted forums). - * @return string - */ - public static function user_forums_sql() { - // I couldn't come up with a good name, please rename this if you can. -- Y - $RestrictedForums = self::get_restricted_forums(); - $PermittedForums = self::get_permitted_forums(); - if (Donations::has_donor_forum(G::$LoggedUser['ID']) && !in_array(DONOR_FORUM, $PermittedForums)) { - $PermittedForums[] = DONOR_FORUM; - } - $SQL = "((f.MinClassRead <= '" . G::$LoggedUser['Class'] . "'"; - if (count($RestrictedForums)) { - $SQL .= " AND f.ID NOT IN ('" . implode("', '", $RestrictedForums) . "')"; - } - $SQL .= ')'; - if (count($PermittedForums)) { - $SQL .= " OR f.ID IN ('" . implode("', '", $PermittedForums) . "')"; - } - $SQL .= ')'; - return $SQL; - } + /** + * Create the part of WHERE in the sql queries used to filter forums for a + * specific user (MinClassRead, restricted and permitted forums). + * @return string + */ + public static function user_forums_sql() { + // I couldn't come up with a good name, please rename this if you can. -- Y + $RestrictedForums = self::get_restricted_forums(); + $PermittedForums = self::get_permitted_forums(); + if (Donations::has_donor_forum(G::$LoggedUser['ID']) && !in_array(DONOR_FORUM, $PermittedForums)) { + $PermittedForums[] = DONOR_FORUM; + } + $SQL = "((f.MinClassRead <= '" . G::$LoggedUser['Class'] . "'"; + if (count($RestrictedForums)) { + $SQL .= " AND f.ID NOT IN ('" . implode("', '", $RestrictedForums) . "')"; + } + $SQL .= ')'; + if (count($PermittedForums)) { + $SQL .= " OR f.ID IN ('" . implode("', '", $PermittedForums) . "')"; + } + $SQL .= ')'; + return $SQL; + } + + public static function get_transitions() { + $items = G::$Cache->get_value('forum_transitions'); + if (!$items) { + $queryId = G::$DB->get_query_id(); + G::$DB->prepared_query(' + SELECT forums_transitions_id AS id, source, destination, label, permission_levels + FROM forums_transitions'); + $items = G::$DB->to_array('id', MYSQLI_ASSOC); + G::$Cache->cache_value('forum_transitions', $items); + G::$DB->set_query_id($queryId); + } + + if (check_perms('site_moderate_forums')) { + return $items; + } + + return array_filter($items, function ($item) { + $perms = array_fill_keys(explode(',', $item['permission_levels']), 1); + if (count(array_intersect_key($perms, Users::user_info(G::$LoggedUser['ID'])['ExtraClasses'])) > 0) { + return true; + } + return false; + }); + } + + public static function get_thread_transitions($forum) { + $transitions = self::get_transitions(); + + $filtered = []; + foreach ($transitions as $transition) { + if ($transition['source'] == $forum) { + $filtered[] = $transition; + } + } + + return $filtered; + } } diff --git a/classes/g.class.php b/classes/g.class.php index 210c6b3ef..9ec418d31 100644 --- a/classes/g.class.php +++ b/classes/g.class.php @@ -1,18 +1,26 @@ -_getBase32LookupTable(); - - // Valid secret lengths are 80 to 640 bits - if ($secretLength < 16 || $secretLength > 128) { - throw new Exception('Bad secret length'); - } - $secret = ''; - $rnd = false; - if (function_exists('random_bytes')) { - $rnd = random_bytes($secretLength); - } elseif (function_exists('mcrypt_create_iv')) { - $rnd = mcrypt_create_iv($secretLength, MCRYPT_DEV_URANDOM); - } elseif (function_exists('openssl_random_pseudo_bytes')) { - $rnd = openssl_random_pseudo_bytes($secretLength, $cryptoStrong); - if (!$cryptoStrong) { - $rnd = false; - } - } - if ($rnd !== false) { - for ($i = 0; $i < $secretLength; ++$i) { - $secret .= $validChars[ord($rnd[$i]) & 31]; - } - } else { - throw new Exception('No source of secure random'); - } - - return $secret; - } - - /** - * Calculate the code, with given secret and point in time. - * - * @param string $secret - * @param int|null $timeSlice - * - * @return string - */ - public function getCode($secret, $timeSlice = null) - { - if ($timeSlice === null) { - $timeSlice = floor(time() / 30); - } - - $secretkey = $this->_base32Decode($secret); - - // Pack time into binary string - $time = chr(0) . chr(0) . chr(0) . chr(0) . pack('N*', $timeSlice); - // Hash it with users secret key - $hm = hash_hmac('SHA1', $time, $secretkey, true); - // Use last nipple of result as index/offset - $offset = ord(substr($hm, -1)) & 0x0F; - // grab 4 bytes of the result - $hashpart = substr($hm, $offset, 4); - - // Unpak binary value - $value = unpack('N', $hashpart); - $value = $value[1]; - // Only 32 bits - $value = $value & 0x7FFFFFFF; - - $modulo = pow(10, $this->_codeLength); - - return str_pad($value % $modulo, $this->_codeLength, '0', STR_PAD_LEFT); - } - - /** - * Get QR-Code URL for image, from google charts. - * - * @param string $name - * @param string $secret - * @param string $title - * @param array $params - * - * @return string - */ - public function getQRCodeGoogleUrl($name, $secret, $title = null, $params = array()) - { - $width = !empty($params['width']) && (int)$params['width'] > 0 ? (int)$params['width'] : 200; - $height = !empty($params['height']) && (int)$params['height'] > 0 ? (int)$params['height'] : 200; - $level = !empty($params['level']) && array_search($params['level'], array('L', 'M', 'Q', 'H')) !== false ? $params['level'] : 'M'; - - $urlencoded = urlencode('otpauth://totp/' . $name . '?secret=' . $secret . ''); - if (isset($title)) { - $urlencoded .= urlencode('&issuer=' . urlencode($title)); - } - - return 'https://chart.googleapis.com/chart?chs=' . $width . 'x' . $height . '&chld=' . $level . '|0&cht=qr&chl=' . $urlencoded . ''; - } - - /** - * Check if the code is correct. This will accept codes starting from $discrepancy*30sec ago to $discrepancy*30sec from now. - * - * @param string $secret - * @param string $code - * @param int $discrepancy This is the allowed time drift in 30 second units (8 means 4 minutes before or after) - * @param int|null $currentTimeSlice time slice if we want use other that time() - * - * @return bool - */ - public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null) - { - if ($currentTimeSlice === null) { - $currentTimeSlice = floor(time() / 30); - } - - if (strlen($code) != 6) { - return false; - } - - for ($i = -$discrepancy; $i <= $discrepancy; ++$i) { - $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i); - if ($this->timingSafeEquals($calculatedCode, $code)) { - return true; - } - } - - return false; - } - - /** - * Set the code length, should be >=6. - * - * @param int $length - * - * @return PHPGangsta_GoogleAuthenticator - */ - public function setCodeLength($length) - { - $this->_codeLength = $length; - - return $this; - } - - /** - * Helper class to decode base32. - * - * @param $secret - * - * @return bool|string - */ - protected function _base32Decode($secret) - { - if (empty($secret)) { - return ''; - } - - $base32chars = $this->_getBase32LookupTable(); - $base32charsFlipped = array_flip($base32chars); - - $paddingCharCount = substr_count($secret, $base32chars[32]); - $allowedValues = array(6, 4, 3, 1, 0); - if (!in_array($paddingCharCount, $allowedValues)) { - return false; - } - for ($i = 0; $i < 4; ++$i) { - if ($paddingCharCount == $allowedValues[$i] && - substr($secret, -($allowedValues[$i])) != str_repeat($base32chars[32], $allowedValues[$i]) - ) { - return false; - } - } - $secret = str_replace('=', '', $secret); - $secret = str_split($secret); - $binaryString = ''; - for ($i = 0; $i < count($secret); $i = $i + 8) { - $x = ''; - if (!in_array($secret[$i], $base32chars)) { - return false; - } - for ($j = 0; $j < 8; ++$j) { - $x .= str_pad(base_convert(@$base32charsFlipped[@$secret[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT); - } - $eightBits = str_split($x, 8); - for ($z = 0; $z < count($eightBits); ++$z) { - $binaryString .= (($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48) ? $y : ''; - } - } - - return $binaryString; - } - - /** - * Get array with all 32 characters for decoding from/encoding to base32. - * - * @return array - */ - protected function _getBase32LookupTable() - { - return array( - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7 - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15 - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23 - 'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31 - '=', // padding char - ); - } - - /** - * A timing safe equals comparison - * more info here: http://blog.ircmaxell.com/2014/11/its-all-about-time.html. - * - * @param string $safeString The internal (safe) value to be checked - * @param string $userString The user submitted (unsafe) value - * - * @return bool True if the two strings are identical - */ - private function timingSafeEquals($safeString, $userString) - { - if (function_exists('hash_equals')) { - return hash_equals($safeString, $userString); - } - $safeLen = strlen($safeString); - $userLen = strlen($userString); - - if ($userLen != $safeLen) { - return false; - } - - $result = 0; - - for ($i = 0; $i < $userLen; ++$i) { - $result |= (ord($safeString[$i]) ^ ord($userString[$i])); - } - - // They are only identical strings if $result is exactly 0... - return $result === 0; - } + protected $_codeLength = 6; + + /** + * Create new secret. + * 16 characters, randomly chosen from the allowed base32 characters. + * + * @param int $secretLength + * + * @return string + */ + public function createSecret($secretLength = 16) + { + $validChars = $this->_getBase32LookupTable(); + + // Valid secret lengths are 80 to 640 bits + if ($secretLength < 16 || $secretLength > 128) { + throw new Exception('Bad secret length'); + } + $secret = ''; + $rnd = false; + if (function_exists('random_bytes')) { + $rnd = random_bytes($secretLength); + } elseif (function_exists('mcrypt_create_iv')) { + $rnd = mcrypt_create_iv($secretLength, MCRYPT_DEV_URANDOM); + } elseif (function_exists('openssl_random_pseudo_bytes')) { + $rnd = openssl_random_pseudo_bytes($secretLength, $cryptoStrong); + if (!$cryptoStrong) { + $rnd = false; + } + } + if ($rnd !== false) { + for ($i = 0; $i < $secretLength; ++$i) { + $secret .= $validChars[ord($rnd[$i]) & 31]; + } + } else { + throw new Exception('No source of secure random'); + } + + return $secret; + } + + /** + * Calculate the code, with given secret and point in time. + * + * @param string $secret + * @param int|null $timeSlice + * + * @return string + */ + public function getCode($secret, $timeSlice = null) + { + if ($timeSlice === null) { + $timeSlice = floor(time() / 30); + } + + $secretkey = $this->_base32Decode($secret); + + // Pack time into binary string + $time = chr(0) . chr(0) . chr(0) . chr(0) . pack('N*', $timeSlice); + // Hash it with users secret key + $hm = hash_hmac('SHA1', $time, $secretkey, true); + // Use last nipple of result as index/offset + $offset = ord(substr($hm, -1)) & 0x0F; + // grab 4 bytes of the result + $hashpart = substr($hm, $offset, 4); + + // Unpak binary value + $value = unpack('N', $hashpart); + $value = $value[1]; + // Only 32 bits + $value = $value & 0x7FFFFFFF; + + $modulo = pow(10, $this->_codeLength); + + return str_pad($value % $modulo, $this->_codeLength, '0', STR_PAD_LEFT); + } + + /** + * Get QR-Code URL for image, from google charts. + * + * @param string $name + * @param string $secret + * @param string $title + * @param array $params + * + * @return string + */ + public function getQRCodeGoogleUrl($name, $secret, $title = null, $params = []) + { + $width = !empty($params['width']) && (int)$params['width'] > 0 ? (int)$params['width'] : 200; + $height = !empty($params['height']) && (int)$params['height'] > 0 ? (int)$params['height'] : 200; + $level = !empty($params['level']) && array_search($params['level'], array('L', 'M', 'Q', 'H')) !== false ? $params['level'] : 'M'; + + $urlencoded = urlencode('otpauth://totp/' . $name . '?secret=' . $secret . ''); + if (isset($title)) { + $urlencoded .= urlencode('&issuer=' . urlencode($title)); + } + + return 'https://chart.googleapis.com/chart?chs=' . $width . 'x' . $height . '&chld=' . $level . '|0&cht=qr&chl=' . $urlencoded . ''; + } + + /** + * Check if the code is correct. This will accept codes starting from $discrepancy*30sec ago to $discrepancy*30sec from now. + * + * @param string $secret + * @param string $code + * @param int $discrepancy This is the allowed time drift in 30 second units (8 means 4 minutes before or after) + * @param int|null $currentTimeSlice time slice if we want use other that time() + * + * @return bool + */ + public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null) + { + if ($currentTimeSlice === null) { + $currentTimeSlice = floor(time() / 30); + } + + if (strlen($code) != 6) { + return false; + } + + for ($i = -$discrepancy; $i <= $discrepancy; ++$i) { + $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i); + if ($this->timingSafeEquals($calculatedCode, $code)) { + return true; + } + } + + return false; + } + + /** + * Set the code length, should be >=6. + * + * @param int $length + * + * @return PHPGangsta_GoogleAuthenticator + */ + public function setCodeLength($length) + { + $this->_codeLength = $length; + + return $this; + } + + /** + * Helper class to decode base32. + * + * @param $secret + * + * @return bool|string + */ + protected function _base32Decode($secret) + { + if (empty($secret)) { + return ''; + } + + $base32chars = $this->_getBase32LookupTable(); + $base32charsFlipped = array_flip($base32chars); + + $paddingCharCount = substr_count($secret, $base32chars[32]); + $allowedValues = array(6, 4, 3, 1, 0); + if (!in_array($paddingCharCount, $allowedValues)) { + return false; + } + for ($i = 0; $i < 4; ++$i) { + if ($paddingCharCount == $allowedValues[$i] && + substr($secret, -($allowedValues[$i])) != str_repeat($base32chars[32], $allowedValues[$i]) + ) { + return false; + } + } + $secret = str_replace('=', '', $secret); + $secret = str_split($secret); + $binaryString = ''; + for ($i = 0; $i < count($secret); $i = $i + 8) { + $x = ''; + if (!in_array($secret[$i], $base32chars)) { + return false; + } + for ($j = 0; $j < 8; ++$j) { + $x .= str_pad(base_convert(@$base32charsFlipped[@$secret[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT); + } + $eightBits = str_split($x, 8); + for ($z = 0; $z < count($eightBits); ++$z) { + $binaryString .= (($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48) ? $y : ''; + } + } + + return $binaryString; + } + + /** + * Get array with all 32 characters for decoding from/encoding to base32. + * + * @return array + */ + protected function _getBase32LookupTable() + { + return array( + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7 + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15 + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23 + 'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31 + '=', // padding char + ); + } + + /** + * A timing safe equals comparison + * more info here: http://blog.ircmaxell.com/2014/11/its-all-about-time.html. + * + * @param string $safeString The internal (safe) value to be checked + * @param string $userString The user submitted (unsafe) value + * + * @return bool True if the two strings are identical + */ + private function timingSafeEquals($safeString, $userString) + { + if (function_exists('hash_equals')) { + return hash_equals($safeString, $userString); + } + $safeLen = strlen($safeString); + $userLen = strlen($userString); + + if ($userLen != $safeLen) { + return false; + } + + $result = 0; + + for ($i = 0; $i < $userLen; ++$i) { + $result |= (ord($safeString[$i]) ^ ord($userString[$i])); + } + + // They are only identical strings if $result is exactly 0... + return $result === 0; + } } diff --git a/classes/image.class.php b/classes/image.class.php index 874fe45c4..53280cbf4 100644 --- a/classes/image.class.php +++ b/classes/image.class.php @@ -1,57 +1,57 @@ -Image = imagecreate($Width, $Height); - $this->Font = SERVER_ROOT.'/classes/fonts/VERDANA.TTF'; - if (function_exists('imageantialias')) { - imageantialias($this->Image, true); - } - } - - function color($Red, $Green, $Blue, $Alpha = 0) { - return imagecolorallocatealpha($this->Image, $Red, $Green, $Blue, $Alpha); - } - - function line($x1, $y1, $x2, $y2, $Color, $Thickness = 1) { - if ($Thickness == 1) { - return imageline($this->Image, $x1, $y1, $x2, $y2, $Color); - } - $t = $Thickness / 2 - 0.5; - if ($x1 == $x2 || $y1 == $y2) { - return imagefilledrectangle($this->Image, round(min($x1, $x2) - $t), round(min($y1, $y2) - $t), round(max($x1, $x2) + $t), round(max($y1, $y2) + $t), $color); - } - $k = ($y2 - $y1) / ($x2 - $x1); //y = kx + q - $a = $t / sqrt(1 + pow($k, 2)); - $Points = array( - round($x1 - (1 + $k) * $a), round($y1 + (1 - $k) * $a), - round($x1 - (1 - $k) * $a), round($y1 - (1 + $k) * $a), - round($x2 + (1 + $k) * $a), round($y2 - (1 - $k) * $a), - round($x2 + (1 - $k) * $a), round($y2 + (1 + $k) * $a), - ); - imagefilledpolygon($this->Image, $Points, 4, $Color); - return imagepolygon($this->Image, $Points, 4, $Color); - } - - function ellipse($x, $y, $Width, $Height, $Color) { - return imageEllipse($this->Image, $x, $y, $Width, $Height, $Color); - } - - function text($x, $y, $Color, $Text) { - return imagettftext ($this->Image, $this->FontSize, $this->TextAngle, $x, $y, $Color, $this->Font, $Text); - } - - function make_png($FileName = null) { - return imagepng($this->Image, $FileName); - } + var $Image = false; + var $FontSize = 10; + var $Font = ''; + var $TextAngle = 0; + + function create($Width, $Height) { + $this->Image = imagecreate($Width, $Height); + $this->Font = SERVER_ROOT.'/classes/fonts/VERDANA.TTF'; + if (function_exists('imageantialias')) { + imageantialias($this->Image, true); + } + } + + function color($Red, $Green, $Blue, $Alpha = 0) { + return imagecolorallocatealpha($this->Image, $Red, $Green, $Blue, $Alpha); + } + + function line($x1, $y1, $x2, $y2, $Color, $Thickness = 1) { + if ($Thickness == 1) { + return imageline($this->Image, $x1, $y1, $x2, $y2, $Color); + } + $t = $Thickness / 2 - 0.5; + if ($x1 == $x2 || $y1 == $y2) { + return imagefilledrectangle($this->Image, round(min($x1, $x2) - $t), round(min($y1, $y2) - $t), round(max($x1, $x2) + $t), round(max($y1, $y2) + $t), $color); + } + $k = ($y2 - $y1) / ($x2 - $x1); //y = kx + q + $a = $t / sqrt(1 + pow($k, 2)); + $Points = array( + round($x1 - (1 + $k) * $a), round($y1 + (1 - $k) * $a), + round($x1 - (1 - $k) * $a), round($y1 - (1 + $k) * $a), + round($x2 + (1 + $k) * $a), round($y2 - (1 - $k) * $a), + round($x2 + (1 - $k) * $a), round($y2 + (1 + $k) * $a), + ); + imagefilledpolygon($this->Image, $Points, 4, $Color); + return imagepolygon($this->Image, $Points, 4, $Color); + } + + function ellipse($x, $y, $Width, $Height, $Color) { + return imageEllipse($this->Image, $x, $y, $Width, $Height, $Color); + } + + function text($x, $y, $Color, $Text) { + return imagettftext ($this->Image, $this->FontSize, $this->TextAngle, $x, $y, $Color, $this->Font, $Text); + } + + function make_png($FileName = null) { + return imagepng($this->Image, $FileName); + } } diff --git a/classes/imagetools.class.php b/classes/imagetools.class.php index 0b4e82086..b96c10037 100644 --- a/classes/imagetools.class.php +++ b/classes/imagetools.class.php @@ -5,243 +5,243 @@ * Thumbnail aide, mostly */ class ImageTools { - /** - * Store processed links to avoid repetition - * @var array 'URL' => 'Parsed URL' - */ - private static $Storage = array(); - - /** - * We use true as an extra property to make the domain an array key - * @var array $Hosts Array of image hosts - */ - private static $Hosts = array( - 'whatimg.com' => true, - 'imgur.com' => true - ); - - /** - * Blacklisted sites - * @var array $Blacklist Array of blacklisted hosts - */ - private static $Blacklist = array( - 'tinypic.com' - ); - - /** - * Array of image hosts that provide thumbnailing - * @var array $Thumbs - */ - private static $Thumbs = array( - 'i.imgur.com' => true, - 'whatimg.com' => true - ); - - /** - * Array of extensions - * @var array $Extensions - */ - private static $Extensions = array( - 'jpg' => true, - 'jpeg' => true, - 'png' => true, - 'gif' => true - ); - - /** - * Array of user IDs whose avatars have been checked for size - * @var array $CheckedAvatars - */ - private static $CheckedAvatars = array(); - private static $CheckedAvatars2 = array(); - - /** - * Array of user IDs whose donor icons have been checked for size - * @var array $CheckedDonorIcons - */ - private static $CheckedDonorIcons = array(); - - /** - * Checks from our list of valid hosts - * @param string $Host Domain/host to check - * @return boolean - */ - public static function valid_host($Host) { - return !empty(self::$Hosts[$Host]) && self::$Hosts[$Host] === true; - } - - /** - * Checks if a link's host is (not) good, otherwise displays an error. - * @param string $Url Link to an image - * @return boolean - */ - public static function blacklisted($Url, $ShowError = true) { - foreach (self::$Blacklist as &$Value) { - $Blacklisted = stripos($Url, $Value); - if ($Blacklisted !== false) { - $ParsedUrl = parse_url($Url); - if ($ShowError) { - error($ParsedUrl['host'] . ' is not an allowed image host. Please use a different host.'); - } - return true; - } - } - return false; - } - - /** - * Checks to see if a link has a thumbnail - * @param string $Url Link to an image - * @return string|false Matched host or false - */ - private static function thumbnailable($Url) { - $ParsedUrl = parse_url($Url); - return !empty(self::$Thumbs[$ParsedUrl['host']]); - } - - /** - * Checks an extension - * @param string $Ext Extension to check - * @return boolean - */ - private static function valid_extension($Ext) { -// return @self::$Extensions[$Ext] === true; - return !empty(self::$Extensions[$Ext]) && (self::$Extensions[$Ext] === true); - } - - /** - * Stores a link with a (thumbnail) link - * @param type $Link - * @param type $Processed - */ - private static function store($Link, $Processed) { - self::$Storage[$Link] = $Processed; - } - - /** - * Retrieves an entry from our storage - * @param type $Link - * @return boolean|string Returns false if no match - */ - private static function get_stored($Link) { - if (isset(self::$Storage[$Link])) { - return self::$Storage[$Link]; - } - return false; - } - - /** - * Checks if URL points to a whatimg thumbnail. - */ - private static function has_whatimg_thumb($Url) { - return (strpos($Url, '_thumb') !== false); - } - - /** - * Cleans up imgur URL if it already has a modifier attached to the end of it. - */ - private static function clean_imgur_url($Url) { - $Extension = pathinfo($Url, PATHINFO_EXTENSION); - $Full = preg_replace('/\.[^.]*$/', '', $Url); - $Base = substr($Full, 0, strrpos($Full, '/')); - $Path = substr($Full, strrpos($Full, '/') + 1); - if (strlen($Path) == 6) { - $Last = $Path[strlen($Path) - 1]; - if ($Last == 'm' || $Last == 'l' || $Last == 's' || $Last == 'h' || $Last == 'b') { - $Path = substr($Path, 0, -1); - } - } - return "$Base/$Path.$Extension"; - } - - /** - * Replaces the extension. - */ - private static function replace_extension($String, $Extension) { - return preg_replace('/\.[^.]*$/', $Extension, $String); - } - - /** - * Create image proxy URL - * @param string $Url image URL - * @param bool/string $CheckSize - accepts one of false, "avatar", "avatar2", or "donoricon" - * @param bool/string/number $UserID - user ID for avatars and donor icons - * @return image proxy URL - */ - public static function proxy_url($Url, $CheckSize, $UserID, &$ExtraInfo) { - global $SSL; - - if ($UserID) { - $ExtraInfo = "&userid=$UserID"; - if ($CheckSize === 'avatar' && !isset(self::$CheckedAvatars[$UserID])) { - $ExtraInfo .= "&type=$CheckSize"; - self::$CheckedAvatars[$UserID] = true; - } elseif ($CheckSize === 'avatar2' && !isset(self::$CheckedAvatars2[$UserID])) { - $ExtraInfo .= "&type=$CheckSize"; - self::$CheckedAvatars2[$UserID] = true; - } elseif ($CheckSize === 'donoricon' && !isset(self::$CheckedDonorIcons[$UserID])) { - $ExtraInfo .= "&type=$CheckSize"; - self::$CheckedDonorIcons[$UserID] = true; - } - } - - return ($SSL ? 'https' : 'http') . '://' . SITE_URL . "/image.php?c=1&i=" . urlencode($Url); - } - - /** - * Determine the image URL. This takes care of the image proxy and thumbnailing. - * @param string $Url - * @param bool $Thumb - * @param bool/string $CheckSize - accepts one of false, "avatar", "avatar2", or "donoricon" - * @param bool/string/number $UserID - user ID for avatars and donor icons - * @return string - */ - public static function process($Url, $Thumb = false, $CheckSize = false, $UserID = false) { - if (empty($Url)) { - return ''; - } - - if ($Found = self::get_stored($Url . ($Thumb ? '_thumb' : ''))) { - return $Found; - } - - $ProcessedUrl = $Url; - if ($Thumb) { - $Extension = pathinfo($Url, PATHINFO_EXTENSION); - if (self::thumbnailable($Url) && self::valid_extension($Extension)) { - if (strpos($Url, 'whatimg') !== false && !self::has_whatimg_thumb($Url)) { - $ProcessedUrl = self::replace_extension($Url, '_thumb.' . $Extension); - } elseif (strpos($Url, 'imgur') !== false) { - $ProcessedUrl = self::replace_extension(self::clean_imgur_url($Url), 'm.' . $Extension); - } - } - } - - $ExtraInfo = ''; - if (check_perms('site_proxy_images')) { - $ProcessedUrl = self::proxy_url($ProcessedUrl, $CheckSize, $UserID, $ExtraInfo); - } - self::store($Url . ($Thumb ? '_thumb' : ''), $ProcessedUrl); - return $ProcessedUrl . $ExtraInfo; - } - - /** - * Cover art thumbnail in browse, on artist pages etc. - * @global array $CategoryIcons - * @param string $Url - * @param int $CategoryID - */ - public static function cover_thumb($Url, $CategoryID) { - global $CategoryIcons; - if ($Url) { - $Src = self::process($Url, true); - $Lightbox = self::process($Url); - } else { - $Src = STATIC_SERVER . 'common/noartwork/' . $CategoryIcons[$CategoryID - 1]; - $Lightbox = $Src; - } + /** + * Store processed links to avoid repetition + * @var array 'URL' => 'Parsed URL' + */ + private static $Storage = []; + + /** + * We use true as an extra property to make the domain an array key + * @var array $Hosts Array of image hosts + */ + private static $Hosts = array( + 'whatimg.com' => true, + 'imgur.com' => true + ); + + /** + * Blacklisted sites + * @var array $Blacklist Array of blacklisted hosts + */ + private static $Blacklist = array( + 'tinypic.com' + ); + + /** + * Array of image hosts that provide thumbnailing + * @var array $Thumbs + */ + private static $Thumbs = array( + 'i.imgur.com' => true, + 'whatimg.com' => true + ); + + /** + * Array of extensions + * @var array $Extensions + */ + private static $Extensions = array( + 'jpg' => true, + 'jpeg' => true, + 'png' => true, + 'gif' => true + ); + + /** + * Array of user IDs whose avatars have been checked for size + * @var array $CheckedAvatars + */ + private static $CheckedAvatars = []; + private static $CheckedAvatars2 = []; + + /** + * Array of user IDs whose donor icons have been checked for size + * @var array $CheckedDonorIcons + */ + private static $CheckedDonorIcons = []; + + /** + * Checks from our list of valid hosts + * @param string $Host Domain/host to check + * @return boolean + */ + public static function valid_host($Host) { + return !empty(self::$Hosts[$Host]) && self::$Hosts[$Host] === true; + } + + /** + * Checks if a link's host is (not) good, otherwise displays an error. + * @param string $Url Link to an image + * @return boolean + */ + public static function blacklisted($Url, $ShowError = true) { + foreach (self::$Blacklist as &$Value) { + $Blacklisted = stripos($Url, $Value); + if ($Blacklisted !== false) { + $ParsedUrl = parse_url($Url); + if ($ShowError) { + error($ParsedUrl['host'] . ' is not an allowed image host. Please use a different host.'); + } + return true; + } + } + return false; + } + + /** + * Checks to see if a link has a thumbnail + * @param string $Url Link to an image + * @return string|false Matched host or false + */ + private static function thumbnailable($Url) { + $ParsedUrl = parse_url($Url); + return !empty(self::$Thumbs[$ParsedUrl['host']]); + } + + /** + * Checks an extension + * @param string $Ext Extension to check + * @return boolean + */ + private static function valid_extension($Ext) { +// return @self::$Extensions[$Ext] === true; + return !empty(self::$Extensions[$Ext]) && (self::$Extensions[$Ext] === true); + } + + /** + * Stores a link with a (thumbnail) link + * @param type $Link + * @param type $Processed + */ + private static function store($Link, $Processed) { + self::$Storage[$Link] = $Processed; + } + + /** + * Retrieves an entry from our storage + * @param type $Link + * @return boolean|string Returns false if no match + */ + private static function get_stored($Link) { + if (isset(self::$Storage[$Link])) { + return self::$Storage[$Link]; + } + return false; + } + + /** + * Checks if URL points to a whatimg thumbnail. + */ + private static function has_whatimg_thumb($Url) { + return (strpos($Url, '_thumb') !== false); + } + + /** + * Cleans up imgur URL if it already has a modifier attached to the end of it. + */ + private static function clean_imgur_url($Url) { + $Extension = pathinfo($Url, PATHINFO_EXTENSION); + $Full = preg_replace('/\.[^.]*$/', '', $Url); + $Base = substr($Full, 0, strrpos($Full, '/')); + $Path = substr($Full, strrpos($Full, '/') + 1); + if (strlen($Path) == 6) { + $Last = $Path[strlen($Path) - 1]; + if ($Last == 'm' || $Last == 'l' || $Last == 's' || $Last == 'h' || $Last == 'b') { + $Path = substr($Path, 0, -1); + } + } + return "$Base/$Path.$Extension"; + } + + /** + * Replaces the extension. + */ + private static function replace_extension($String, $Extension) { + return preg_replace('/\.[^.]*$/', $Extension, $String); + } + + /** + * Create image proxy URL + * @param string $Url image URL + * @param bool/string $CheckSize - accepts one of false, "avatar", "avatar2", or "donoricon" + * @param bool/string/number $UserID - user ID for avatars and donor icons + * @return image proxy URL + */ + public static function proxy_url($Url, $CheckSize, $UserID, &$ExtraInfo) { + global $SSL; + + if ($UserID) { + $ExtraInfo = "&userid=$UserID"; + if ($CheckSize === 'avatar' && !isset(self::$CheckedAvatars[$UserID])) { + $ExtraInfo .= "&type=$CheckSize"; + self::$CheckedAvatars[$UserID] = true; + } elseif ($CheckSize === 'avatar2' && !isset(self::$CheckedAvatars2[$UserID])) { + $ExtraInfo .= "&type=$CheckSize"; + self::$CheckedAvatars2[$UserID] = true; + } elseif ($CheckSize === 'donoricon' && !isset(self::$CheckedDonorIcons[$UserID])) { + $ExtraInfo .= "&type=$CheckSize"; + self::$CheckedDonorIcons[$UserID] = true; + } + } + + return ($SSL ? 'https' : 'http') . '://' . SITE_URL . "/image.php?c=1&i=" . urlencode($Url); + } + + /** + * Determine the image URL. This takes care of the image proxy and thumbnailing. + * @param string $Url + * @param bool $Thumb + * @param bool/string $CheckSize - accepts one of false, "avatar", "avatar2", or "donoricon" + * @param bool/string/number $UserID - user ID for avatars and donor icons + * @return string + */ + public static function process($Url, $Thumb = false, $CheckSize = false, $UserID = false) { + if (empty($Url)) { + return ''; + } + + if ($Found = self::get_stored($Url . ($Thumb ? '_thumb' : ''))) { + return $Found; + } + + $ProcessedUrl = $Url; + if ($Thumb) { + $Extension = pathinfo($Url, PATHINFO_EXTENSION); + if (self::thumbnailable($Url) && self::valid_extension($Extension)) { + if (strpos($Url, 'whatimg') !== false && !self::has_whatimg_thumb($Url)) { + $ProcessedUrl = self::replace_extension($Url, '_thumb.' . $Extension); + } elseif (strpos($Url, 'imgur') !== false) { + $ProcessedUrl = self::replace_extension(self::clean_imgur_url($Url), 'm.' . $Extension); + } + } + } + + $ExtraInfo = ''; + if (check_perms('site_proxy_images')) { + $ProcessedUrl = self::proxy_url($ProcessedUrl, $CheckSize, $UserID, $ExtraInfo); + } + self::store($Url . ($Thumb ? '_thumb' : ''), $ProcessedUrl); + return $ProcessedUrl . $ExtraInfo; + } + + /** + * Cover art thumbnail in browse, on artist pages etc. + * @global array $CategoryIcons + * @param string $Url + * @param int $CategoryID + */ + public static function cover_thumb($Url, $CategoryID) { + global $CategoryIcons; + if ($Url) { + $Src = self::process($Url, true); + $Lightbox = self::process($Url); + } else { + $Src = STATIC_SERVER . 'common/noartwork/' . $CategoryIcons[$CategoryID - 1]; + $Lightbox = $Src; + } ?> - Cover + Cover diff --git a/classes/invite_tree.class.php b/classes/invite_tree.class.php index d0e55711d..8c9eb19d9 100644 --- a/classes/invite_tree.class.php +++ b/classes/invite_tree.class.php @@ -1,4 +1,4 @@ -UserID = $UserID; - if (isset($Options['visible']) && $Options['visible'] === false) { - $this->Visible = false; - } - } + // Set things up + function __construct($UserID, $Options = []) { + $this->UserID = $UserID; + if (isset($Options['visible']) && $Options['visible'] === false) { + $this->Visible = false; + } + } - function make_tree() { - $QueryID = G::$DB->get_query_id(); + function make_tree() { + $QueryID = G::$DB->get_query_id(); - $UserID = $this->UserID; + $UserID = $this->UserID; ?> -
    -query(" - SELECT TreePosition, TreeID, TreeLevel - FROM invite_tree - WHERE UserID = $UserID"); - if (!G::$DB->has_results()) { - return; - } - list($TreePosition, $TreeID, $TreeLevel) = G::$DB->next_record(MYSQLI_NUM, false); - - G::$DB->query(" - SELECT TreePosition - FROM invite_tree - WHERE TreeID = $TreeID - AND TreeLevel = $TreeLevel - AND TreePosition > $TreePosition - ORDER BY TreePosition ASC - LIMIT 1"); - if (G::$DB->has_results()) { - list($MaxPosition) = G::$DB->next_record(MYSQLI_NUM, false); - } else { - $MaxPosition = false; - } - $TreeQuery = G::$DB->query(" - SELECT - it.UserID, - Enabled, - PermissionID, - Donor, - Uploaded, - Downloaded, - Paranoia, - TreePosition, - TreeLevel - FROM invite_tree AS it - JOIN users_main AS um ON um.ID = it.UserID - JOIN users_info AS ui ON ui.UserID = it.UserID - WHERE TreeID = $TreeID - AND TreePosition > $TreePosition". - ($MaxPosition ? " AND TreePosition < $MaxPosition" : '')." - AND TreeLevel > $TreeLevel - ORDER BY TreePosition"); - - $PreviousTreeLevel = $TreeLevel; - - // Stats for the summary - $MaxTreeLevel = $TreeLevel; // The deepest level (this changes) - $OriginalTreeLevel = $TreeLevel; // The level of the user we're viewing - $BaseTreeLevel = $TreeLevel + 1; // The level of users invited by our user - $Count = 0; - $Branches = 0; - $DisabledCount = 0; - $DonorCount = 0; - $ParanoidCount = 0; - $TotalUpload = 0; - $TotalDownload = 0; - $TopLevelUpload = 0; - $TopLevelDownload = 0; - - $ClassSummary = array(); - global $Classes; - foreach ($Classes as $ClassID => $Val) { - $ClassSummary[$ClassID] = 0; - } - - // We store this in an output buffer, so we can show the summary at the top without having to loop through twice - ob_start(); - while (list($ID, $Enabled, $Class, $Donor, $Uploaded, $Downloaded, $Paranoia, $TreePosition, $TreeLevel) = G::$DB->next_record(MYSQLI_NUM, false)) { - - // Do stats - $Count++; - - if ($TreeLevel > $MaxTreeLevel) { - $MaxTreeLevel = $TreeLevel; - } - - if ($TreeLevel == $BaseTreeLevel) { - $Branches++; - $TopLevelUpload += $Uploaded; - $TopLevelDownload += $Downloaded; - } - - $ClassSummary[$Class]++; - if ($Enabled == 2) { - $DisabledCount++; - } - if ($Donor) { - $DonorCount++; - } - - // Manage tree depth - if ($TreeLevel > $PreviousTreeLevel) { - for ($i = 0; $i < $TreeLevel - $PreviousTreeLevel; $i++) { - echo "\n\n
      \n\t
    • \n"; - } - } elseif ($TreeLevel < $PreviousTreeLevel) { - for ($i = 0; $i < $PreviousTreeLevel - $TreeLevel; $i++) { - echo "\t
    • \n
    \n"; - } - echo "\t\n\t
  • \n"; - } else { - echo "\t
  • \n\t
  • \n"; - } - $UserClass = $Classes[$Class]['Level']; +
    +query(" + SELECT TreePosition, TreeID, TreeLevel + FROM invite_tree + WHERE UserID = $UserID"); + if (!G::$DB->has_results()) { + return; + } + list($TreePosition, $TreeID, $TreeLevel) = G::$DB->next_record(MYSQLI_NUM, false); + + G::$DB->query(" + SELECT TreePosition + FROM invite_tree + WHERE TreeID = $TreeID + AND TreeLevel = $TreeLevel + AND TreePosition > $TreePosition + ORDER BY TreePosition ASC + LIMIT 1"); + if (G::$DB->has_results()) { + list($MaxPosition) = G::$DB->next_record(MYSQLI_NUM, false); + } else { + $MaxPosition = false; + } + $TreeQuery = G::$DB->query(" + SELECT + it.UserID, + um.Enabled, + um.PermissionID, + ui.Donor, + uls.Uploaded, + uls.Downloaded, + um.Paranoia, + it.TreePosition, + it.TreeLevel + FROM invite_tree AS it + INNER JOIN users_main AS um ON (um.ID = it.UserID) + INNER JOIN users_leech_stats AS uls ON (uls.UserID = it.UserID) + INNER JOIN users_info AS ui ON (ui.UserID = it.UserID) + WHERE TreeID = $TreeID + AND TreePosition > $TreePosition". + ($MaxPosition ? " AND TreePosition < $MaxPosition" : '')." + AND TreeLevel > $TreeLevel + ORDER BY TreePosition"); + + $PreviousTreeLevel = $TreeLevel; + + // Stats for the summary + $MaxTreeLevel = $TreeLevel; // The deepest level (this changes) + $OriginalTreeLevel = $TreeLevel; // The level of the user we're viewing + $BaseTreeLevel = $TreeLevel + 1; // The level of users invited by our user + $Count = 0; + $Branches = 0; + $DisabledCount = 0; + $DonorCount = 0; + $ParanoidCount = 0; + $TotalUpload = 0; + $TotalDownload = 0; + $TopLevelUpload = 0; + $TopLevelDownload = 0; + + $ClassSummary = []; + global $Classes; + foreach ($Classes as $ClassID => $Val) { + $ClassSummary[$ClassID] = 0; + } + + // We store this in an output buffer, so we can show the summary at the top without having to loop through twice + ob_start(); + while (list($ID, $Enabled, $Class, $Donor, $Uploaded, $Downloaded, $Paranoia, $TreePosition, $TreeLevel) = G::$DB->next_record(MYSQLI_NUM, false)) { + + // Do stats + $Count++; + + if ($TreeLevel > $MaxTreeLevel) { + $MaxTreeLevel = $TreeLevel; + } + + if ($TreeLevel == $BaseTreeLevel) { + $Branches++; + $TopLevelUpload += $Uploaded; + $TopLevelDownload += $Downloaded; + } + + $ClassSummary[$Class]++; + if ($Enabled == 2) { + $DisabledCount++; + } + if ($Donor) { + $DonorCount++; + } + + // Manage tree depth + if ($TreeLevel > $PreviousTreeLevel) { + for ($i = 0; $i < $TreeLevel - $PreviousTreeLevel; $i++) { + echo "\n\n
      \n\t
    • \n"; + } + } elseif ($TreeLevel < $PreviousTreeLevel) { + for ($i = 0; $i < $PreviousTreeLevel - $TreeLevel; $i++) { + echo "\t
    • \n
    \n"; + } + echo "\t
  • \n\t
  • \n"; + } else { + echo "\t
  • \n\t
  • \n"; + } + $UserClass = $Classes[$Class]['Level']; ?> - - + -  Uploaded: -  Downloaded: -  Ratio: - +  Downloaded: +  Ratio: + -  Hidden - -set_query_id($TreeQuery); - } +set_query_id($TreeQuery); + } - $Tree = ob_get_clean(); - for ($i = 0; $i < $PreviousTreeLevel - $OriginalTreeLevel; $i++) { - $Tree .= "\t
  • \n\n"; - } + $Tree = ob_get_clean(); + for ($i = 0; $i < $PreviousTreeLevel - $OriginalTreeLevel; $i++) { + $Tree .= "\t\n\n"; + } - if ($Count) { + if ($Count) { ?> -

    - This tree has entries, branches, and a depth of . - It has - $ClassCount) { - if ($ClassCount == 0) { - continue; - } - $LastClass = Users::make_class_string($ClassID); - if ($ClassCount > 1) { - if ($LastClass == 'Torrent Celebrity') { - $LastClass = 'Torrent Celebrities'; - } else { - $LastClass.='s'; - } - } - $LastClass = "$ClassCount $LastClass (" . number_format(($ClassCount / $Count) * 100) . '%)'; - - $ClassStrings[] = $LastClass; - } - if (count($ClassStrings) > 1) { - array_pop($ClassStrings); - echo implode(', ', $ClassStrings); - echo ' and '.$LastClass; - } else { - echo $LastClass; - } - echo '. '; - echo $DisabledCount; - echo ($DisabledCount == 1) ? ' user is' : ' users are'; - echo ' disabled ('; - if ($DisabledCount == 0) { - echo '0%)'; - } else { - echo number_format(($DisabledCount / $Count) * 100) . '%)'; - } - echo ', and '; - echo $DonorCount; - echo ($DonorCount == 1) ? ' user has' : ' users have'; - echo ' donated ('; - if ($DonorCount == 0) { - echo '0%)'; - } else { - echo number_format(($DonorCount / $Count) * 100) . '%)'; - } - echo '.

    '; - - echo '

    '; - echo 'The total amount uploaded by the entire tree was '.Format::get_size($TotalUpload); - echo '; the total amount downloaded was '.Format::get_size($TotalDownload); - echo '; and the total ratio is '.Format::get_ratio_html($TotalUpload, $TotalDownload).'. '; - echo '

    '; - - echo '

    '; - echo 'The total amount uploaded by direct invitees (the top level) was '.Format::get_size($TopLevelUpload); - echo '; the total amount downloaded was '.Format::get_size($TopLevelDownload); - echo '; and the total ratio is '.Format::get_ratio_html($TopLevelUpload, $TopLevelDownload).'. '; - - echo "These numbers include the stats of paranoid users and will be factored into the invitation giving script.\n\t\t

    \n"; - - if ($ParanoidCount) { - echo '

    '; - echo $ParanoidCount; - echo ($ParanoidCount == 1) ? ' user (' : ' users ('; - echo number_format(($ParanoidCount / $Count) * 100); - echo '%) '; - echo ($ParanoidCount == 1) ? ' is' : ' are'; - echo ' too paranoid to have their stats shown here, and '; - echo ($ParanoidCount == 1) ? ' was' : ' were'; - echo ' not factored into the stats for the total tree.'; - echo '

    '; - } - } +

    + This tree has entries, branches, and a depth of . + It has + $ClassCount) { + if ($ClassCount == 0) { + continue; + } + $LastClass = Users::make_class_string($ClassID); + if ($ClassCount > 1) { + if ($LastClass == 'Torrent Celebrity') { + $LastClass = 'Torrent Celebrities'; + } else { + $LastClass.='s'; + } + } + $LastClass = "$ClassCount $LastClass (" . number_format(($ClassCount / $Count) * 100) . '%)'; + + $ClassStrings[] = $LastClass; + } + if (count($ClassStrings) > 1) { + array_pop($ClassStrings); + echo implode(', ', $ClassStrings); + echo ' and '.$LastClass; + } else { + echo $LastClass; + } + echo '. '; + echo $DisabledCount; + echo ($DisabledCount == 1) ? ' user is' : ' users are'; + echo ' disabled ('; + if ($DisabledCount == 0) { + echo '0%)'; + } else { + echo number_format(($DisabledCount / $Count) * 100) . '%)'; + } + echo ', and '; + echo $DonorCount; + echo ($DonorCount == 1) ? ' user has' : ' users have'; + echo ' donated ('; + if ($DonorCount == 0) { + echo '0%)'; + } else { + echo number_format(($DonorCount / $Count) * 100) . '%)'; + } + echo '.

    '; + + echo '

    '; + echo 'The total amount uploaded by the entire tree was '.Format::get_size($TotalUpload); + echo '; the total amount downloaded was '.Format::get_size($TotalDownload); + echo '; and the total ratio is '.Format::get_ratio_html($TotalUpload, $TotalDownload).'. '; + echo '

    '; + + echo '

    '; + echo 'The total amount uploaded by direct invitees (the top level) was '.Format::get_size($TopLevelUpload); + echo '; the total amount downloaded was '.Format::get_size($TopLevelDownload); + echo '; and the total ratio is '.Format::get_ratio_html($TopLevelUpload, $TopLevelDownload).'. '; + + echo "These numbers include the stats of paranoid users and will be factored into the invitation giving script.\n\t\t

    \n"; + + if ($ParanoidCount) { + echo '

    '; + echo $ParanoidCount; + echo ($ParanoidCount == 1) ? ' user (' : ' users ('; + echo number_format(($ParanoidCount / $Count) * 100); + echo '%) '; + echo ($ParanoidCount == 1) ? ' is' : ' are'; + echo ' too paranoid to have their stats shown here, and '; + echo ($ParanoidCount == 1) ? ' was' : ' were'; + echo ' not factored into the stats for the total tree.'; + echo '

    '; + } + } ?> -
    - -
    -set_query_id($QueryID); - } +
    + + +set_query_id($QueryID); + } } ?> diff --git a/classes/irc.class.php b/classes/irc.class.php index a36f6a962..629169726 100644 --- a/classes/irc.class.php +++ b/classes/irc.class.php @@ -1,183 +1,183 @@ -send_to($Bot->get_channel(), 'The database is currently unavailable; try again later.'); - } + function halt($Msg) { + global $Bot; + $Bot->send_to($Bot->get_channel(), 'The database is currently unavailable; try again later.'); + } } abstract class IRC_BOT { - abstract protected function connect_events(); - abstract protected function channel_events(); - abstract protected function query_events(); - abstract protected function irc_events(); - abstract protected function listener_events(); - - protected $Debug = false; - protected $Socket = false; - protected $Data = false; - protected $Whois = false; - protected $Identified = array(); - protected $Channels = array(); - protected $Messages = array(); - protected $LastChan = false; - protected $ListenSocket = false; - protected $Listened = false; - protected $Connecting = false; - protected $State = 1; // Drone is live - public $Restart = 0; // Die by default - - public function __construct() { - if (isset($_SERVER['HOME']) && is_dir($_SERVER['HOME']) && getcwd() != $_SERVER['HOME']) { - chdir($_SERVER['HOME']); - } - ob_end_clean(); - restore_error_handler(); //Avoid PHP error logging - set_time_limit(0); - } - - public function connect() { - $this->connect_irc(); - $this->connect_listener(); - $this->post_connect(); - } - - private function connect_irc($Reconnect = false) { - $this->Connecting = true; - //Open a socket to the IRC server - if (defined('BOT_PORT_SSL')) { - $IrcAddress = 'tls://' . BOT_SERVER . ':' . BOT_PORT_SSL; - } else { - $IrcAddress = 'tcp://' . BOT_SERVER . ':' . BOT_PORT; - } - while (!$this->Socket = stream_socket_client($IrcAddress, $ErrNr, $ErrStr)) { - sleep(15); - } - stream_set_blocking($this->Socket, 0); - $this->Connecting = false; - if ($Reconnect) { - $this->post_connect(); - } - } - - private function connect_listener() { - //create a socket to listen on - $ListenAddress = 'tcp://' . SOCKET_LISTEN_ADDRESS . ':' . SOCKET_LISTEN_PORT; - if (!$this->ListenSocket = stream_socket_server($ListenAddress, $ErrNr, $ErrStr)) { - die("Cannot create listen socket: $ErrStr"); - } - stream_set_blocking($this->ListenSocket, false); - } - - private function post_connect() { - fwrite($this->Socket, "NICK ".BOT_NICK."Init\n"); - fwrite($this->Socket, "USER ".BOT_NICK." * * :IRC Bot\n"); - $this->listen(); - } - - public function disconnect() { - fclose($this->ListenSocket); - $this->State = 0; //Drones dead - } - - public function get_channel() { - preg_match('/.+ PRIVMSG ([^:]+) :.+/', $this->Data, $Channel); - if (preg_match('/#.+/', $Channel[1])) { - return $Channel[1]; - } else { - return false; - } - } - - public function get_nick() { - preg_match('/:([^!:]+)!.+@[^\s]+ PRIVMSG [^:]+ :.+/', $this->Data, $Nick); - return $Nick[1]; - } - - protected function get_message() { - preg_match('/:.+ PRIVMSG [^:]+ :(.+)/', $this->Data, $Msg); - return trim($Msg[1]); - } - - protected function get_irc_host() { - preg_match('/:[^!:]+!.+@([^\s]+) PRIVMSG [^:]+ :.+/', $this->Data, $Host); - return trim($Host[1]); - } - - protected function get_word($Select = 1) { - preg_match('/:.+ PRIVMSG [^:]+ :(.+)/', $this->Data, $Word); - $Word = split(' ', $Word[1]); - return trim($Word[$Select]); - } - - protected function get_action() { - preg_match('/:.+ PRIVMSG [^:]+ :!(\S+)/', $this->Data, $Action); - return strtoupper($Action[1]); - } - - protected function send_raw($Text) { - if (!feof($this->Socket)) { - fwrite($this->Socket, "$Text\n"); - } elseif (!$this->Connecting) { - $this->Connecting = true; - sleep(120); - $this->connect_irc(true); - } - } - - public function send_to($Channel, $Text) { - // split the message up into <= 460 character strings and send each individually - // this is used to prevent messages from getting truncated - $Text = wordwrap($Text, 460, "\n", true); - $TextArray = explode("\n", $Text); - foreach ($TextArray as $Text) { - $this->send_raw("PRIVMSG $Channel :$Text"); - } - } - - protected function whois($Nick) { - $this->Whois = $Nick; - $this->send_raw("WHOIS $Nick"); - } - - /* - This function uses blacklisted_ip, which is no longer in RC2. - You can probably find it in old RC1 code kicking aronud if you need it. - protected function ip_check($IP, $Gline = false, $Channel = BOT_REPORT_CHAN) { - if (blacklisted_ip($IP)) { - $this->send_to($Channel, 'TOR IP Detected: '.$IP); - if ($Gline) { - $this->send_raw('GLINE *@'.$IP.' 90d :DNSBL Proxy'); - } - } - if (Tools::site_ban_ip($IP)) { - $this->send_to($Channel, 'Site IP Ban Detected: '.$IP); - if ($Gline) { - $this->send_raw('GLINE *@'.$IP.' 90d :IP Ban'); - } - } - }*/ - - protected function listen() { - G::$Cache->InternalCache = false; - stream_set_timeout($this->Socket, 10000000000); - while ($this->State == 1) { - $NullSock = null; - $Sockets = array($this->Socket, $this->ListenSocket); - if (stream_select($Sockets, $NullSock, $NullSock, null) === false) { - die(); - } - foreach ($Sockets as $Socket) { - if ($Socket === $this->Socket) { - $this->irc_events(); - } else { - $this->Listened = stream_socket_accept($Socket); - $this->listener_events(); - } - } - G::$DB->LinkID = false; - G::$DB->Queries = array(); - } - } + abstract protected function connect_events(); + abstract protected function channel_events(); + abstract protected function query_events(); + abstract protected function irc_events(); + abstract protected function listener_events(); + + protected $Debug = false; + protected $Socket = false; + protected $Data = false; + protected $Whois = false; + protected $Identified = []; + protected $Channels = []; + protected $Messages = []; + protected $LastChan = false; + protected $ListenSocket = false; + protected $Listened = false; + protected $Connecting = false; + protected $State = 1; // Drone is live + public $Restart = 0; // Die by default + + public function __construct() { + if (isset($_SERVER['HOME']) && is_dir($_SERVER['HOME']) && getcwd() != $_SERVER['HOME']) { + chdir($_SERVER['HOME']); + } + ob_end_clean(); + restore_error_handler(); //Avoid PHP error logging + set_time_limit(0); + } + + public function connect() { + $this->connect_irc(); + $this->connect_listener(); + $this->post_connect(); + } + + private function connect_irc($Reconnect = false) { + $this->Connecting = true; + //Open a socket to the IRC server + if (defined('BOT_PORT_SSL')) { + $IrcAddress = 'tls://' . BOT_SERVER . ':' . BOT_PORT_SSL; + } else { + $IrcAddress = 'tcp://' . BOT_SERVER . ':' . BOT_PORT; + } + while (!$this->Socket = stream_socket_client($IrcAddress, $ErrNr, $ErrStr)) { + sleep(15); + } + stream_set_blocking($this->Socket, 0); + $this->Connecting = false; + if ($Reconnect) { + $this->post_connect(); + } + } + + private function connect_listener() { + //create a socket to listen on + $ListenAddress = 'tcp://' . SOCKET_LISTEN_ADDRESS . ':' . SOCKET_LISTEN_PORT; + if (!$this->ListenSocket = stream_socket_server($ListenAddress, $ErrNr, $ErrStr)) { + die("Cannot create listen socket: $ErrStr"); + } + stream_set_blocking($this->ListenSocket, false); + } + + private function post_connect() { + fwrite($this->Socket, "NICK ".BOT_NICK."Init\n"); + fwrite($this->Socket, "USER ".BOT_NICK." * * :IRC Bot\n"); + $this->listen(); + } + + public function disconnect() { + fclose($this->ListenSocket); + $this->State = 0; //Drones dead + } + + public function get_channel() { + preg_match('/.+ PRIVMSG ([^:]+) :.+/', $this->Data, $Channel); + if (preg_match('/#.+/', $Channel[1])) { + return $Channel[1]; + } else { + return false; + } + } + + public function get_nick() { + preg_match('/:([^!:]+)!.+@[^\s]+ PRIVMSG [^:]+ :.+/', $this->Data, $Nick); + return $Nick[1]; + } + + protected function get_message() { + preg_match('/:.+ PRIVMSG [^:]+ :(.+)/', $this->Data, $Msg); + return trim($Msg[1]); + } + + protected function get_irc_host() { + preg_match('/:[^!:]+!.+@([^\s]+) PRIVMSG [^:]+ :.+/', $this->Data, $Host); + return trim($Host[1]); + } + + protected function get_word($Select = 1) { + preg_match('/:.+ PRIVMSG [^:]+ :(.+)/', $this->Data, $Word); + $Word = split(' ', $Word[1]); + return trim($Word[$Select]); + } + + protected function get_action() { + preg_match('/:.+ PRIVMSG [^:]+ :!(\S+)/', $this->Data, $Action); + return strtoupper($Action[1]); + } + + protected function send_raw($Text) { + if (!feof($this->Socket)) { + fwrite($this->Socket, "$Text\n"); + } elseif (!$this->Connecting) { + $this->Connecting = true; + sleep(120); + $this->connect_irc(true); + } + } + + public function send_to($Channel, $Text) { + // split the message up into <= 460 character strings and send each individually + // this is used to prevent messages from getting truncated + $Text = wordwrap($Text, 460, "\n", true); + $TextArray = explode("\n", $Text); + foreach ($TextArray as $Text) { + $this->send_raw("PRIVMSG $Channel :$Text"); + } + } + + protected function whois($Nick) { + $this->Whois = $Nick; + $this->send_raw("WHOIS $Nick"); + } + + /* + This function uses blacklisted_ip, which is no longer in RC2. + You can probably find it in old RC1 code kicking aronud if you need it. + protected function ip_check($IP, $Gline = false, $Channel = BOT_REPORT_CHAN) { + if (blacklisted_ip($IP)) { + $this->send_to($Channel, 'TOR IP Detected: '.$IP); + if ($Gline) { + $this->send_raw('GLINE *@'.$IP.' 90d :DNSBL Proxy'); + } + } + if (Tools::site_ban_ip($IP)) { + $this->send_to($Channel, 'Site IP Ban Detected: '.$IP); + if ($Gline) { + $this->send_raw('GLINE *@'.$IP.' 90d :IP Ban'); + } + } + }*/ + + protected function listen() { + G::$Cache->InternalCache = false; + stream_set_timeout($this->Socket, 10000000000); + while ($this->State == 1) { + $NullSock = null; + $Sockets = array($this->Socket, $this->ListenSocket); + if (stream_select($Sockets, $NullSock, $NullSock, null) === false) { + die(); + } + foreach ($Sockets as $Socket) { + if ($Socket === $this->Socket) { + $this->irc_events(); + } else { + $this->Listened = stream_socket_accept($Socket); + $this->listener_events(); + } + } + G::$DB->LinkID = false; + G::$DB->Queries = []; + } + } } ?> diff --git a/classes/irc.class.php.save b/classes/irc.class.php.save index b040c0f70..712ff691c 100644 --- a/classes/irc.class.php.save +++ b/classes/irc.class.php.save @@ -1,184 +1,184 @@ send_to($ + function halt($Msg) { + global $Bot; + $Bot->send_to($ Bot->get_channel(), 'The database is currently unavailable; try again later.'); - } + } } abstract class IRC_BOT { - abstract protected function connect_events(); - abstract protected function channel_events(); - abstract protected function query_events(); - abstract protected function irc_events(); - abstract protected function listener_events(); - - protected $Debug = false; - protected $Socket = false; - protected $Data = false; - protected $Whois = false; - protected $Identified = array(); - protected $Channels = array(); - protected $Messages = array(); - protected $LastChan = false; - protected $ListenSocket = false; - protected $Listened = false; - protected $Connecting = false; - protected $State = 1; // Drone is live - public $Restart = 0; // Die by default - - public function __construct() { - if (isset($_SERVER['HOME']) && is_dir($_SERVER['HOME']) && getcwd() != $_SERVER['HOME']) { - chdir($_SERVER['HOME']); - } - ob_end_clean(); - restore_error_handler(); //Avoid PHP error logging - set_time_limit(0); - } - - public function connect() { - $this->connect_irc(); - $this->connect_listener(); - $this->post_connect(); - } - - private function connect_irc($Reconnect = false) { - $this->Connecting = true; - //Open a socket to the IRC server - if (defined('BOT_PORT_SSL')) { - $IrcAddress = 'tls://' . BOT_SERVER . ':' . BOT_PORT_SSL; - } else { - $IrcAddress = 'tcp://' . BOT_SERVER . ':' . BOT_PORT; - } - while (!$this->Socket = stream_socket_client($IrcAddress, $ErrNr, $ErrStr)) { - sleep(15); - } - stream_set_blocking($this->Socket, 0); - $this->Connecting = false; - if ($Reconnect) { - $this->post_connect(); - } - } - - private function connect_listener() { - //create a socket to listen on - $ListenAddress = 'tcp://' . SOCKET_LISTEN_ADDRESS . ':' . SOCKET_LISTEN_PORT; - if (!$this->ListenSocket = stream_socket_server($ListenAddress, $ErrNr, $ErrStr)) { - die("Cannot create listen socket: $ErrStr"); - } - stream_set_blocking($this->ListenSocket, false); - } - - private function post_connect() { - fwrite($this->Socket, "NICK ".BOT_NICK."Init\n"); - fwrite($this->Socket, "USER ".BOT_NICK." * * :IRC Bot\n"); - $this->listen(); - } - - public function disconnect() { - fclose($this->ListenSocket); - $this->State = 0; //Drones dead - } - - public function get_channel() { - preg_match('/.+ PRIVMSG ([^:]+) :.+/', $this->Data, $Channel); - if (preg_match('/#.+/', $Channel[1])) { - return $Channel[1]; - } else { - return false; - } - } - - public function get_nick() { - preg_match('/:([^!:]+)!.+@[^\s]+ PRIVMSG [^:]+ :.+/', $this->Data, $Nick); - return $Nick[1]; - } - - protected function get_message() { - preg_match('/:.+ PRIVMSG [^:]+ :(.+)/', $this->Data, $Msg); - return trim($Msg[1]); - } - - protected function get_irc_host() { - preg_match('/:[^!:]+!.+@([^\s]+) PRIVMSG [^:]+ :.+/', $this->Data, $Host); - return trim($Host[1]); - } - - protected function get_word($Select = 1) { - preg_match('/:.+ PRIVMSG [^:]+ :(.+)/', $this->Data, $Word); - $Word = split(' ', $Word[1]); - return trim($Word[$Select]); - } - - protected function get_action() { - preg_match('/:.+ PRIVMSG [^:]+ :!(\S+)/', $this->Data, $Action); - return strtoupper($Action[1]); - } - - protected function send_raw($Text) { - if (!feof($this->Socket)) { - fwrite($this->Socket, "$Text\n"); - } elseif (!$this->Connecting) { - $this->Connecting = true; - sleep(120); - $this->connect_irc(true); - } - } - - public function send_to($Channel, $Text) { - // split the message up into <= 460 character strings and send each individually - // this is used to prevent messages from getting truncated - $Text = wordwrap($Text, 460, "\n", true); - $TextArray = explode("\n", $Text); - foreach ($TextArray as $Text) { - $this->send_raw("PRIVMSG $Channel :$Text"); - } - } - - protected function whois($Nick) { - $this->Whois = $Nick; - $this->send_raw("WHOIS $Nick"); - } - - /* - This function uses blacklisted_ip, which is no longer in RC2. - You can probably find it in old RC1 code kicking aronud if you need it. - protected function ip_check($IP, $Gline = false, $Channel = BOT_REPORT_CHAN) { - if (blacklisted_ip($IP)) { - $this->send_to($Channel, 'TOR IP Detected: '.$IP); - if ($Gline) { - $this->send_raw('GLINE *@'.$IP.' 90d :DNSBL Proxy'); - } - } - if (Tools::site_ban_ip($IP)) { - $this->send_to($Channel, 'Site IP Ban Detected: '.$IP); - if ($Gline) { - $this->send_raw('GLINE *@'.$IP.' 90d :IP Ban'); - } - } - }*/ - - protected function listen() { - G::$Cache->InternalCache = false; - stream_set_timeout($this->Socket, 10000000000); - while ($this->State == 1) { - $NullSock = null; - $Sockets = array($this->Socket, $this->ListenSocket); - if (stream_select($Sockets, $NullSock, $NullSock, null) === false) { - die(); - } - foreach ($Sockets as $Socket) { - if ($Socket === $this->Socket) { - $this->irc_events(); - } else { - $this->Listened = stream_socket_accept($Socket); - $this->listener_events(); - } - } - G::$DB->LinkID = false; - G::$DB->Queries = array(); - } - } + abstract protected function connect_events(); + abstract protected function channel_events(); + abstract protected function query_events(); + abstract protected function irc_events(); + abstract protected function listener_events(); + + protected $Debug = false; + protected $Socket = false; + protected $Data = false; + protected $Whois = false; + protected $Identified = []; + protected $Channels = []; + protected $Messages = []; + protected $LastChan = false; + protected $ListenSocket = false; + protected $Listened = false; + protected $Connecting = false; + protected $State = 1; // Drone is live + public $Restart = 0; // Die by default + + public function __construct() { + if (isset($_SERVER['HOME']) && is_dir($_SERVER['HOME']) && getcwd() != $_SERVER['HOME']) { + chdir($_SERVER['HOME']); + } + ob_end_clean(); + restore_error_handler(); //Avoid PHP error logging + set_time_limit(0); + } + + public function connect() { + $this->connect_irc(); + $this->connect_listener(); + $this->post_connect(); + } + + private function connect_irc($Reconnect = false) { + $this->Connecting = true; + //Open a socket to the IRC server + if (defined('BOT_PORT_SSL')) { + $IrcAddress = 'tls://' . BOT_SERVER . ':' . BOT_PORT_SSL; + } else { + $IrcAddress = 'tcp://' . BOT_SERVER . ':' . BOT_PORT; + } + while (!$this->Socket = stream_socket_client($IrcAddress, $ErrNr, $ErrStr)) { + sleep(15); + } + stream_set_blocking($this->Socket, 0); + $this->Connecting = false; + if ($Reconnect) { + $this->post_connect(); + } + } + + private function connect_listener() { + //create a socket to listen on + $ListenAddress = 'tcp://' . SOCKET_LISTEN_ADDRESS . ':' . SOCKET_LISTEN_PORT; + if (!$this->ListenSocket = stream_socket_server($ListenAddress, $ErrNr, $ErrStr)) { + die("Cannot create listen socket: $ErrStr"); + } + stream_set_blocking($this->ListenSocket, false); + } + + private function post_connect() { + fwrite($this->Socket, "NICK ".BOT_NICK."Init\n"); + fwrite($this->Socket, "USER ".BOT_NICK." * * :IRC Bot\n"); + $this->listen(); + } + + public function disconnect() { + fclose($this->ListenSocket); + $this->State = 0; //Drones dead + } + + public function get_channel() { + preg_match('/.+ PRIVMSG ([^:]+) :.+/', $this->Data, $Channel); + if (preg_match('/#.+/', $Channel[1])) { + return $Channel[1]; + } else { + return false; + } + } + + public function get_nick() { + preg_match('/:([^!:]+)!.+@[^\s]+ PRIVMSG [^:]+ :.+/', $this->Data, $Nick); + return $Nick[1]; + } + + protected function get_message() { + preg_match('/:.+ PRIVMSG [^:]+ :(.+)/', $this->Data, $Msg); + return trim($Msg[1]); + } + + protected function get_irc_host() { + preg_match('/:[^!:]+!.+@([^\s]+) PRIVMSG [^:]+ :.+/', $this->Data, $Host); + return trim($Host[1]); + } + + protected function get_word($Select = 1) { + preg_match('/:.+ PRIVMSG [^:]+ :(.+)/', $this->Data, $Word); + $Word = split(' ', $Word[1]); + return trim($Word[$Select]); + } + + protected function get_action() { + preg_match('/:.+ PRIVMSG [^:]+ :!(\S+)/', $this->Data, $Action); + return strtoupper($Action[1]); + } + + protected function send_raw($Text) { + if (!feof($this->Socket)) { + fwrite($this->Socket, "$Text\n"); + } elseif (!$this->Connecting) { + $this->Connecting = true; + sleep(120); + $this->connect_irc(true); + } + } + + public function send_to($Channel, $Text) { + // split the message up into <= 460 character strings and send each individually + // this is used to prevent messages from getting truncated + $Text = wordwrap($Text, 460, "\n", true); + $TextArray = explode("\n", $Text); + foreach ($TextArray as $Text) { + $this->send_raw("PRIVMSG $Channel :$Text"); + } + } + + protected function whois($Nick) { + $this->Whois = $Nick; + $this->send_raw("WHOIS $Nick"); + } + + /* + This function uses blacklisted_ip, which is no longer in RC2. + You can probably find it in old RC1 code kicking aronud if you need it. + protected function ip_check($IP, $Gline = false, $Channel = BOT_REPORT_CHAN) { + if (blacklisted_ip($IP)) { + $this->send_to($Channel, 'TOR IP Detected: '.$IP); + if ($Gline) { + $this->send_raw('GLINE *@'.$IP.' 90d :DNSBL Proxy'); + } + } + if (Tools::site_ban_ip($IP)) { + $this->send_to($Channel, 'Site IP Ban Detected: '.$IP); + if ($Gline) { + $this->send_raw('GLINE *@'.$IP.' 90d :IP Ban'); + } + } + }*/ + + protected function listen() { + G::$Cache->InternalCache = false; + stream_set_timeout($this->Socket, 10000000000); + while ($this->State == 1) { + $NullSock = null; + $Sockets = array($this->Socket, $this->ListenSocket); + if (stream_select($Sockets, $NullSock, $NullSock, null) === false) { + die(); + } + foreach ($Sockets as $Socket) { + if ($Socket === $this->Socket) { + $this->irc_events(); + } else { + $this->Listened = stream_socket_accept($Socket); + $this->listener_events(); + } + } + G::$DB->LinkID = false; + G::$DB->Queries = []; + } + } } ?> diff --git a/classes/lastfm.class.php b/classes/lastfm.class.php index 459a10c1a..68958d679 100644 --- a/classes/lastfm.class.php +++ b/classes/lastfm.class.php @@ -1,202 +1,202 @@ -get_value("lastfm_username_$UserID"); - if ($Username === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT Username - FROM lastfm_users - WHERE ID = $UserID"); - list($Username) = G::$DB->next_record(); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value("lastfm_username_$UserID", $Username, 0); - } - return $Username; - } + public static function get_lastfm_username($UserID) { + $Username = G::$Cache->get_value("lastfm_username_$UserID"); + if ($Username === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT Username + FROM lastfm_users + WHERE ID = $UserID"); + list($Username) = G::$DB->next_record(); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value("lastfm_username_$UserID", $Username, 0); + } + return $Username; + } - public static function get_artist_events($ArtistID, $Artist, $Limit = 15) { - $ArtistEvents = G::$Cache->get_value("artist_events_$ArtistID"); - if (empty($ArtistEvents)) { - $ArtistEvents = self::lastfm_request("artist.getEvents", array("artist" => $Artist, "limit" => $Limit)); - G::$Cache->cache_value("artist_events_$ArtistID", $ArtistEvents, 432000); - } - return $ArtistEvents; - } + public static function get_artist_events($ArtistID, $Artist, $Limit = 15) { + $ArtistEvents = G::$Cache->get_value("artist_events_$ArtistID"); + if (empty($ArtistEvents)) { + $ArtistEvents = self::lastfm_request("artist.getEvents", array("artist" => $Artist, "limit" => $Limit)); + G::$Cache->cache_value("artist_events_$ArtistID", $ArtistEvents, 432000); + } + return $ArtistEvents; + } - public static function get_user_info($Username) { - $Response = G::$Cache->get_value("lastfm_user_info_$Username"); - if (empty($Response)) { - $Response = self::lastfm_request("user.getInfo", array("user" => $Username)); - G::$Cache->cache_value("lastfm_user_info_$Username", $Response, 86400); - } - return $Response; - } + public static function get_user_info($Username) { + $Response = G::$Cache->get_value("lastfm_user_info_$Username"); + if (empty($Response)) { + $Response = self::lastfm_request("user.getInfo", array("user" => $Username)); + G::$Cache->cache_value("lastfm_user_info_$Username", $Response, 86400); + } + return $Response; + } - public static function compare_user_with($Username1, $Limit = 15) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT username - FROM lastfm_users - WHERE ID = '" . G::$LoggedUser['ID'] . "'"); - if (G::$DB->has_results()) { - list($Username2) = G::$DB->next_record(); - //Make sure the usernames are in the correct order to avoid dupe cache keys. - if (strcasecmp($Username1, $Username2)) { - $Temp = $Username1; - $Username1 = $Username2; - $Username2 = $Temp; - } - $Response = G::$Cache->get_value("lastfm_compare_$Username1" . "_$Username2"); - if (empty($Response)) { - $Response = self::lastfm_request("tasteometer.compare", array("type1" => "user", "type2" => "user", "value1" => $Username1, "value2" => $Username2, "limit" => $Limit)); - $Response = json_encode($Response); - G::$Cache->cache_value("lastfm_compare_$Username1" . "_$Username2", $Response, 86400); - } - } else { - $Response = null; - } - G::$DB->set_query_id($QueryID); - return $Response; - } + public static function compare_user_with($Username1, $Limit = 15) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT username + FROM lastfm_users + WHERE ID = '" . G::$LoggedUser['ID'] . "'"); + if (G::$DB->has_results()) { + list($Username2) = G::$DB->next_record(); + //Make sure the usernames are in the correct order to avoid dupe cache keys. + if (strcasecmp($Username1, $Username2)) { + $Temp = $Username1; + $Username1 = $Username2; + $Username2 = $Temp; + } + $Response = G::$Cache->get_value("lastfm_compare_$Username1" . "_$Username2"); + if (empty($Response)) { + $Response = self::lastfm_request("tasteometer.compare", array("type1" => "user", "type2" => "user", "value1" => $Username1, "value2" => $Username2, "limit" => $Limit)); + $Response = json_encode($Response); + G::$Cache->cache_value("lastfm_compare_$Username1" . "_$Username2", $Response, 86400); + } + } else { + $Response = null; + } + G::$DB->set_query_id($QueryID); + return $Response; + } - public static function get_last_played_track($Username) { - $Response = G::$Cache->get_value("lastfm_last_played_track_$Username"); - if (empty($Response)) { - $Response = self::lastfm_request("user.getRecentTracks", array("user" => $Username, "limit" => 1)); - // Take the single last played track out of the response. - $Response = $Response['recenttracks']['track']; - $Response = json_encode($Response); - G::$Cache->cache_value("lastfm_last_played_track_$Username", $Response, 7200); - } - return $Response; - } + public static function get_last_played_track($Username) { + $Response = G::$Cache->get_value("lastfm_last_played_track_$Username"); + if (empty($Response)) { + $Response = self::lastfm_request("user.getRecentTracks", array("user" => $Username, "limit" => 1)); + // Take the single last played track out of the response. + $Response = $Response['recenttracks']['track']; + $Response = json_encode($Response); + G::$Cache->cache_value("lastfm_last_played_track_$Username", $Response, 7200); + } + return $Response; + } - public static function get_top_artists($Username, $Limit = 15) { - $Response = G::$Cache->get_value("lastfm_top_artists_$Username"); - if (empty($Response)) { - sleep(1); - $Response = self::lastfm_request("user.getTopArtists", array("user" => $Username, "limit" => $Limit)); - $Response = json_encode($Response); - G::$Cache->cache_value("lastfm_top_artists_$Username", $Response, 86400); - } - return $Response; - } + public static function get_top_artists($Username, $Limit = 15) { + $Response = G::$Cache->get_value("lastfm_top_artists_$Username"); + if (empty($Response)) { + sleep(1); + $Response = self::lastfm_request("user.getTopArtists", array("user" => $Username, "limit" => $Limit)); + $Response = json_encode($Response); + G::$Cache->cache_value("lastfm_top_artists_$Username", $Response, 86400); + } + return $Response; + } - public static function get_top_albums($Username, $Limit = 15) { - $Response = G::$Cache->get_value("lastfm_top_albums_$Username"); - if (empty($Response)) { - sleep(2); - $Response = self::lastfm_request("user.getTopAlbums", array("user" => $Username, "limit" => $Limit)); - $Response = json_encode($Response); - G::$Cache->cache_value("lastfm_top_albums_$Username", $Response, 86400); - } - return $Response; - } + public static function get_top_albums($Username, $Limit = 15) { + $Response = G::$Cache->get_value("lastfm_top_albums_$Username"); + if (empty($Response)) { + sleep(2); + $Response = self::lastfm_request("user.getTopAlbums", array("user" => $Username, "limit" => $Limit)); + $Response = json_encode($Response); + G::$Cache->cache_value("lastfm_top_albums_$Username", $Response, 86400); + } + return $Response; + } - public static function get_top_tracks($Username, $Limit = 15) { - $Response = G::$Cache->get_value("lastfm_top_tracks_$Username"); - if (empty($Response)) { - sleep(3); - $Response = self::lastfm_request("user.getTopTracks", array("user" => $Username, "limit" => $Limit)); - $Response = json_encode($Response); - G::$Cache->cache_value("lastfm_top_tracks_$Username", $Response, 86400); - } - return $Response; - } + public static function get_top_tracks($Username, $Limit = 15) { + $Response = G::$Cache->get_value("lastfm_top_tracks_$Username"); + if (empty($Response)) { + sleep(3); + $Response = self::lastfm_request("user.getTopTracks", array("user" => $Username, "limit" => $Limit)); + $Response = json_encode($Response); + G::$Cache->cache_value("lastfm_top_tracks_$Username", $Response, 86400); + } + return $Response; + } - public static function get_user_artist_chart($Username, $From = '', $To = '') { - $Response = G::$Cache->get_value("lastfm_artist_chart_$Username"); - if (empty($Response)) { - $Response = self::lastfm_request("user.getWeeklyArtistChart", array("user" => $Username)); - $Response = json_encode($Response); - G::$Cache->cache_value("lastfm_artist_chart_$Username", $Response, 86400); - } - return $Response; - } + public static function get_user_artist_chart($Username, $From = '', $To = '') { + $Response = G::$Cache->get_value("lastfm_artist_chart_$Username"); + if (empty($Response)) { + $Response = self::lastfm_request("user.getWeeklyArtistChart", array("user" => $Username)); + $Response = json_encode($Response); + G::$Cache->cache_value("lastfm_artist_chart_$Username", $Response, 86400); + } + return $Response; + } - public static function get_weekly_artists($Limit = 100) { - $Response = G::$Cache->get_value("lastfm_top_artists_$Limit"); - if (empty($Response)) { - $Response = self::lastfm_request("chart.getTopArtists", array("limit" => $Limit)); - $Response = json_encode($Response); - G::$Cache->cache_value("lastfm_top_artists_$Limit", $Response, 86400); - } - return $Response; - } + public static function get_weekly_artists($Limit = 100) { + $Response = G::$Cache->get_value("lastfm_top_artists_$Limit"); + if (empty($Response)) { + $Response = self::lastfm_request("chart.getTopArtists", array("limit" => $Limit)); + $Response = json_encode($Response); + G::$Cache->cache_value("lastfm_top_artists_$Limit", $Response, 86400); + } + return $Response; + } - public static function get_hyped_artists($Limit = 100) { - $Response = G::$Cache->get_value("lastfm_hyped_artists_$Limit"); - if (empty($Response)) { - $Response = self::lastfm_request("chart.getHypedArtists", array("limit" => $Limit)); - $Response = json_encode($Response); - G::$Cache->cache_value("lastfm_hyped_artists_$Limit", $Response, 86400); - } - return $Response; - } + public static function get_hyped_artists($Limit = 100) { + $Response = G::$Cache->get_value("lastfm_hyped_artists_$Limit"); + if (empty($Response)) { + $Response = self::lastfm_request("chart.getHypedArtists", array("limit" => $Limit)); + $Response = json_encode($Response); + G::$Cache->cache_value("lastfm_hyped_artists_$Limit", $Response, 86400); + } + return $Response; + } - public static function clear_cache($Username, $UserID) { - $Response = G::$Cache->get_value("lastfm_clear_cache_$UserID"); - if (empty($Response)) { - // Prevent clearing the cache on the same uid page for the next 10 minutes. - G::$Cache->cache_value("lastfm_clear_cache_$UserID", 1, 600); - G::$Cache->delete_value("lastfm_user_info_$Username"); - G::$Cache->delete_value("lastfm_last_played_track_$Username"); - G::$Cache->delete_value("lastfm_top_artists_$Username"); - G::$Cache->delete_value("lastfm_top_albums_$Username"); - G::$Cache->delete_value("lastfm_top_tracks_$Username"); - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT username - FROM lastfm_users - WHERE ID = " . G::$LoggedUser['ID']); - if (G::$DB->has_results()) { - list($Username2) = G::$DB->next_record(); - //Make sure the usernames are in the correct order to avoid dupe cache keys. - if (strcasecmp($Username, $Username2)) { - $Temp = $Username; - $Username = $Username2; - $Username2 = $Temp; - } - G::$Cache->delete_value("lastfm_compare_{$Username}_$Username2"); - } - G::$DB->set_query_id($QueryID); - } - } + public static function clear_cache($Username, $UserID) { + $Response = G::$Cache->get_value("lastfm_clear_cache_$UserID"); + if (empty($Response)) { + // Prevent clearing the cache on the same uid page for the next 10 minutes. + G::$Cache->cache_value("lastfm_clear_cache_$UserID", 1, 600); + G::$Cache->delete_value("lastfm_user_info_$Username"); + G::$Cache->delete_value("lastfm_last_played_track_$Username"); + G::$Cache->delete_value("lastfm_top_artists_$Username"); + G::$Cache->delete_value("lastfm_top_albums_$Username"); + G::$Cache->delete_value("lastfm_top_tracks_$Username"); + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT username + FROM lastfm_users + WHERE ID = " . G::$LoggedUser['ID']); + if (G::$DB->has_results()) { + list($Username2) = G::$DB->next_record(); + //Make sure the usernames are in the correct order to avoid dupe cache keys. + if (strcasecmp($Username, $Username2)) { + $Temp = $Username; + $Username = $Username2; + $Username2 = $Temp; + } + G::$Cache->delete_value("lastfm_compare_{$Username}_$Username2"); + } + G::$DB->set_query_id($QueryID); + } + } - private static function lastfm_request($Method, $Args) { - if (!defined('LASTFM_API_KEY')) { - return false; - } - $RecentFailsKey = 'lastfm_api_fails'; - $RecentFails = (int)G::$Cache->get_value($RecentFailsKey); - if ($RecentFails > 5) { - // Take a break if last.fm's API is down/nonfunctional - return false; - } - $Url = LASTFM_API_URL . $Method; - if (is_array($Args)) { - foreach ($Args as $Key => $Value) { - $Url .= "&$Key=" . urlencode($Value); - } - $Url .= "&format=json&api_key=" . LASTFM_API_KEY; + private static function lastfm_request($Method, $Args) { + if (!defined('LASTFM_API_KEY')) { + return false; + } + $RecentFailsKey = 'lastfm_api_fails'; + $RecentFails = (int)G::$Cache->get_value($RecentFailsKey); + if ($RecentFails > 5) { + // Take a break if last.fm's API is down/nonfunctional + return false; + } + $Url = LASTFM_API_URL . $Method; + if (is_array($Args)) { + foreach ($Args as $Key => $Value) { + $Url .= "&$Key=" . urlencode($Value); + } + $Url .= "&format=json&api_key=" . LASTFM_API_KEY; - $Curl = curl_init(); - curl_setopt($Curl, CURLOPT_HEADER, 0); - curl_setopt($Curl, CURLOPT_TIMEOUT, 3); - curl_setopt($Curl, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($Curl, CURLOPT_URL, $Url); - $Return = curl_exec($Curl); - $Errno = curl_errno($Curl); - curl_close($Curl); - if ($Errno) { - G::$Cache->cache_value($RecentFailsKey, $RecentFails + 1, 1800); - return false; - } - return json_decode($Return, true); - } - } + $Curl = curl_init(); + curl_setopt($Curl, CURLOPT_HEADER, 0); + curl_setopt($Curl, CURLOPT_TIMEOUT, 3); + curl_setopt($Curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($Curl, CURLOPT_URL, $Url); + $Return = curl_exec($Curl); + $Errno = curl_errno($Curl); + curl_close($Curl); + if ($Errno) { + G::$Cache->cache_value($RecentFailsKey, $RecentFails + 1, 1800); + return false; + } + return json_decode($Return, true); + } + } } diff --git a/classes/lastfmview.class.php b/classes/lastfmview.class.php index 9531c1e14..35627d645 100644 --- a/classes/lastfmview.class.php +++ b/classes/lastfmview.class.php @@ -1,38 +1,38 @@ - -
    -
    Last.fm
    -
      -
    • - Username: -
    • -
      > -
      -
    • - Show more info -get_value("lastfm_clear_cache_$UserID"); - if (empty($Response)) { +
      +
      Last.fm
      +
        +
      • + Username: +
      • +
        > +
        +
      • + Show more info +get_value("lastfm_clear_cache_$UserID"); + if (empty($Response)) { ?> - - Reload stats - - -
      • -
      -
      - + Reload stats + + +
    • +
    +
    +set_table($Table); - } + public function __construct($Table = 'bookmarks_torrents') { + $this->set_table($Table); + } - /** - * Runs a SQL query and clears the Cache key - * - * G::$Cache->delete_value didn't always work, but setting the key to null, did. (?) - * - * @param string $sql - */ - protected function query_and_clear_cache($sql) { - $QueryID = G::$DB->get_query_id(); - if (is_string($sql) && G::$DB->query($sql)) { - G::$Cache->delete_value('bookmarks_group_ids_' . G::$LoggedUser['ID']); - } - G::$DB->set_query_id($QueryID); - } + /** + * Runs a SQL query and clears the Cache key + * + * G::$Cache->delete_value didn't always work, but setting the key to null, did. (?) + * + * @param string $sql + */ + protected function query_and_clear_cache($sql) { + $QueryID = G::$DB->get_query_id(); + if (is_string($sql) && G::$DB->query($sql)) { + G::$Cache->delete_value('bookmarks_group_ids_' . G::$LoggedUser['ID']); + } + G::$DB->set_query_id($QueryID); + } - /** - * Uses (checkboxes) $_POST['remove'] to delete entries. - * - * Uses an IN() to match multiple items in one query. - */ - public function mass_remove() { - $SQL = array(); - foreach ($_POST['remove'] as $GroupID => $K) { - if (is_number($GroupID)) { - $SQL[] = sprintf('%d', $GroupID); - } - } + /** + * Uses (checkboxes) $_POST['remove'] to delete entries. + * + * Uses an IN() to match multiple items in one query. + */ + public function mass_remove() { + $SQL = []; + foreach ($_POST['remove'] as $GroupID => $K) { + if (is_number($GroupID)) { + $SQL[] = sprintf('%d', $GroupID); + } + } - if (!empty($SQL)) { - $SQL = sprintf(' - DELETE FROM %s - WHERE UserID = %d - AND GroupID IN (%s)', - $this->Table, - G::$LoggedUser['ID'], - implode(', ', $SQL) - ); - $this->query_and_clear_cache($SQL); - } - } + if (!empty($SQL)) { + $SQL = sprintf(' + DELETE FROM %s + WHERE UserID = %d + AND GroupID IN (%s)', + $this->Table, + G::$LoggedUser['ID'], + implode(', ', $SQL) + ); + $this->query_and_clear_cache($SQL); + } + } - /** - * Uses $_POST['sort'] values to update the DB. - */ - public function mass_update() { - $SQL = array(); - foreach ($_POST['sort'] as $GroupID => $Sort) { - if (is_number($Sort) && is_number($GroupID)) { - $SQL[] = sprintf('(%d, %d, %d)', $GroupID, $Sort, G::$LoggedUser['ID']); - } - } + /** + * Uses $_POST['sort'] values to update the DB. + */ + public function mass_update() { + $SQL = []; + foreach ($_POST['sort'] as $GroupID => $Sort) { + if (is_number($Sort) && is_number($GroupID)) { + $SQL[] = sprintf('(%d, %d, %d)', $GroupID, $Sort, G::$LoggedUser['ID']); + } + } - if (!empty($SQL)) { - $SQL = sprintf(' - INSERT INTO %s - (GroupID, Sort, UserID) - VALUES - %s - ON DUPLICATE KEY UPDATE - Sort = VALUES (Sort)', - $this->Table, - implode(', ', $SQL)); - $this->query_and_clear_cache($SQL); - } - } + if (!empty($SQL)) { + $SQL = sprintf(' + INSERT INTO %s + (GroupID, Sort, UserID) + VALUES + %s + ON DUPLICATE KEY UPDATE + Sort = VALUES (Sort)', + $this->Table, + implode(', ', $SQL)); + $this->query_and_clear_cache($SQL); + } + } } diff --git a/classes/mass_user_torrents_editor.class.php b/classes/mass_user_torrents_editor.class.php index 58c9e185c..5b77e7b07 100644 --- a/classes/mass_user_torrents_editor.class.php +++ b/classes/mass_user_torrents_editor.class.php @@ -14,48 +14,48 @@ * It could also be used for other types like collages. */ abstract class MASS_USER_TORRENTS_EDITOR { - /** - * The affected DB table - * @var string $Table - */ - protected $Table; + /** + * The affected DB table + * @var string $Table + */ + protected $Table; - /** - * Set the Table - * @param string $Table - */ - final public function set_table($Table) { - $this->Table = db_string($Table); - } + /** + * Set the Table + * @param string $Table + */ + final public function set_table($Table) { + $this->Table = db_string($Table); + } - /** - * Get the Table - * @return string $Table - */ - final public function get_table() { - return $this->Table; - } + /** + * Get the Table + * @return string $Table + */ + final public function get_table() { + return $this->Table; + } - /** - * The extending class must provide a method to send a query and clear the cache - */ - abstract protected function query_and_clear_cache($sql); + /** + * The extending class must provide a method to send a query and clear the cache + */ + abstract protected function query_and_clear_cache($sql); - /** - * A method to insert many rows into a single table - * Not required in subsequent classes - */ - public function mass_add() {} + /** + * A method to insert many rows into a single table + * Not required in subsequent classes + */ + public function mass_add() {} - /** - * A method to remove many rows from a table - * The extending class must have a mass_remove method - */ - abstract public function mass_remove(); + /** + * A method to remove many rows from a table + * The extending class must have a mass_remove method + */ + abstract public function mass_remove(); - /** - * A method to update many rows in a table - * The extending class must have a mass_update method - */ - abstract public function mass_update(); -} \ No newline at end of file + /** + * A method to update many rows in a table + * The extending class must have a mass_update method + */ + abstract public function mass_update(); +} diff --git a/classes/mass_user_torrents_table_view.class.php b/classes/mass_user_torrents_table_view.class.php index 08ee3168f..f0451acd8 100644 --- a/classes/mass_user_torrents_table_view.class.php +++ b/classes/mass_user_torrents_table_view.class.php @@ -11,262 +11,262 @@ * It can be used for Bookmarks, Collages, or anywhere where torrents are managed. */ class MASS_USER_TORRENTS_TABLE_VIEW { - /** - * Used to set text the page heading (h2 tag) - * @var string $Heading - */ - private $Heading = 'Manage Torrents'; - - /** - * Sets the value of the input name="type" - * Later to be used as $_POST['type'] in a form processor - * @var string $EditType - */ - private $EditType; - - /** - * Flag for empty $TorrentList - * @var bool $HasTorrentList - */ - private $HasTorrents; - - /** - * Internal reference to the TorrentList - * @var array $TorrentList - */ - private $TorrentList; - - /** - * Ref. to $CollageDataList - * @var array $CollageDataList - */ - private $CollageDataList; - - /** - * Counter for number of groups - * @var in $NumGroups - */ - private $NumGroups = 0; - - /** - * When creating a new instance of this class, TorrentList and - * CollageDataList must be passed. Additionally, a heading can be added. - * - * @param array $TorrentList - * @param array $CollageDataList - * @param string $EditType - * @param string $Heading - */ - public function __construct (array &$TorrentList, array &$CollageDataList, $EditType, $Heading = null) { - $this->set_heading($Heading); - $this->set_edit_type($EditType); - - $this->TorrentList = $TorrentList; - $this->CollageDataList = $CollageDataList; - - $this->HasTorrents = !empty($TorrentList); - if (!$this->HasTorrents) { - $this->no_torrents(); - } - } - - private function no_torrents () { + /** + * Used to set text the page heading (h2 tag) + * @var string $Heading + */ + private $Heading = 'Manage Torrents'; + + /** + * Sets the value of the input name="type" + * Later to be used as $_POST['type'] in a form processor + * @var string $EditType + */ + private $EditType; + + /** + * Flag for empty $TorrentList + * @var bool $HasTorrentList + */ + private $HasTorrents; + + /** + * Internal reference to the TorrentList + * @var array $TorrentList + */ + private $TorrentList; + + /** + * Ref. to $CollageDataList + * @var array $CollageDataList + */ + private $CollageDataList; + + /** + * Counter for number of groups + * @var in $NumGroups + */ + private $NumGroups = 0; + + /** + * When creating a new instance of this class, TorrentList and + * CollageDataList must be passed. Additionally, a heading can be added. + * + * @param array $TorrentList + * @param array $CollageDataList + * @param string $EditType + * @param string $Heading + */ + public function __construct (array &$TorrentList, array &$CollageDataList, $EditType, $Heading = null) { + $this->set_heading($Heading); + $this->set_edit_type($EditType); + + $this->TorrentList = $TorrentList; + $this->CollageDataList = $CollageDataList; + + $this->HasTorrents = !empty($TorrentList); + if (!$this->HasTorrents) { + $this->no_torrents(); + } + } + + private function no_torrents () { ?> -
    -
    -

    No torrents found.

    -
    -
    -

    Add some torrents and come back later.

    -
    -
    +
    +
    +

    No torrents found.

    +
    +
    +

    Add some torrents and come back later.

    +
    +
    header(); - $this->body(); - $this->footer(); - } - - /** - * Renders a comptele page/table header: div#thin, h2, scripts, notes, - * form, table, etc. - */ - public function header () { - if ($this->HasTorrents) { + } + + /** + * Renders a complete page and table + */ + public function render_all () { + $this->header(); + $this->body(); + $this->footer(); + } + + /** + * Renders a comptele page/table header: div#thin, h2, scripts, notes, + * form, table, etc. + */ + public function header () { + if ($this->HasTorrents) { ?>
    -
    -

    Heading)?>

    -
    - - - - - - -
    Sorting
    -
      -
    • Click on the headings to organize columns automatically.
    • -
    • Sort multiple columns simultaneously by holding down the shift key and clicking other column headers.
    • -
    • Click and drag any row to change its order.
    • -
    • Double-click on a row to check it.
    • -
    -
    - -
    - -buttons(); ?> - - - - - - - - - - - - - - +
    +

    Heading)?>

    +
    + +
    Order#YearArtistTorrentBookmarkedRemove
    + + + + +
    Sorting
    +
      +
    • Click on the headings to organize columns automatically.
    • +
    • Sort multiple columns simultaneously by holding down the shift key and clicking other column headers.
    • +
    • Click and drag any row to change its order.
    • +
    • Double-click on a row to check it.
    • +
    +
    + + + +buttons(); ?> + + + + + + + + + + + + + + HasTorrents) { + } + } + + /** + * Closes header code + */ + public function footer () { + if ($this->HasTorrents) { ?> - -
    Order#YearArtistTorrentBookmarkedRemove
    + + -buttons(); ?> +buttons(); ?> -
    - - - -
    -
    +
    + + + +
    +
    HasTorrents) - foreach ($this->TorrentList as $GroupID => $Group) { - $Artists = array(); - extract($Group); - extract($this->CollageDataList[$GroupID]); - - $this->NumGroups++; - - if (!is_array($ExtendedArtists)) { - $ExtendedArtists = array(); - } - if (!is_array($Artists)) { - $Artists = array(); - } - $DisplayName = self::display_name($ExtendedArtists, $Artists, $VanityHouse); - $TorrentLink = ''.$Name.''; - $Year = $Year > 0 ? $Year : ''; - $DateAdded = date($Time); - - $this->row($Sort, $GroupID, $Year, $DisplayName, $TorrentLink, $DateAdded); - } - } - - /** - * Outputs a single row - * - * @param string|int $Sort - * @param string|int $GroupID - * @param string|int $GroupYear - * @param string $DisplayName - * @param string $TorrentLink - */ - public function row ($Sort, $GroupID, $GroupYear, $DisplayName, $TorrentLink, $DateAdded) { - $CSS = $this->NumGroups % 2 === 0 ? 'rowa' : 'rowb'; + } + } + + /** + * Formats data for use in row + * + */ + public function body () { + if ($this->HasTorrents) + foreach ($this->TorrentList as $GroupID => $Group) { + $Artists = []; + extract($Group); + extract($this->CollageDataList[$GroupID]); + + $this->NumGroups++; + + if (!is_array($ExtendedArtists)) { + $ExtendedArtists = []; + } + if (!is_array($Artists)) { + $Artists = []; + } + $DisplayName = self::display_name($ExtendedArtists, $Artists, $VanityHouse); + $TorrentLink = ''.$Name.''; + $Year = $Year > 0 ? $Year : ''; + $DateAdded = date($Time); + + $this->row($Sort, $GroupID, $Year, $DisplayName, $TorrentLink, $DateAdded); + } + } + + /** + * Outputs a single row + * + * @param string|int $Sort + * @param string|int $GroupID + * @param string|int $GroupYear + * @param string $DisplayName + * @param string $TorrentLink + */ + public function row ($Sort, $GroupID, $GroupYear, $DisplayName, $TorrentLink, $DateAdded) { + $CSS = $this->NumGroups % 2 === 0 ? 'rowa' : 'rowb'; ?> - - - - - NumGroups?> - - - - - - + + + + + NumGroups?> + + + + + + 0) { - $DisplayName = Artists::display_artists(array('1'=>$Artists), true, false); - } - if ($VanityHouse) { - $DisplayName .= ' [VH]'; - } - return $DisplayName; - } - - /** - * Renders buttons used at the top and bottom of the table - */ - public function buttons () { + } + + /** + * Parses a simple display name + * + * @param array $ExtendedArtists + * @param array $Artists + * @param string $VanityHouse + * @return string $DisplayName + */ + public static function display_name (array &$ExtendedArtists, array &$Artists, $VanityHouse) { + $DisplayName = ''; + if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) + || !empty($ExtendedArtists[5]) || !empty($ExtendedArtists[6])) { + unset($ExtendedArtists[2], $ExtendedArtists[3]); + $DisplayName = Artists::display_artists($ExtendedArtists, true, false); + } elseif (count($Artists) > 0) { + $DisplayName = Artists::display_artists(array('1'=>$Artists), true, false); + } + if ($VanityHouse) { + $DisplayName .= ' [VH]'; + } + return $DisplayName; + } + + /** + * Renders buttons used at the top and bottom of the table + */ + public function buttons () { ?> -
    - - -
    +
    + + +
    EditType = $EditType; - } - - /** - * Set's the current page's heading - * @param string $Heading - */ - public function set_heading ($Heading) { - $this->Heading = $Heading; - } + } + + + /** + * @param string $EditType + */ + public function set_edit_type ($EditType) { + $this->EditType = $EditType; + } + + /** + * Set's the current page's heading + * @param string $Heading + */ + public function set_heading ($Heading) { + $this->Heading = $Heading; + } } diff --git a/classes/misc.class.php b/classes/misc.class.php index a5252b077..5a9ec41c6 100644 --- a/classes/misc.class.php +++ b/classes/misc.class.php @@ -1,516 +1,516 @@ -'."\r\n"; - $Headers .= 'Reply-To: '.$From.'@'.MAIL_HOST."\r\n"; - $Headers .= 'Message-Id: <'.Users::make_secret().'@'.MAIL_HOST.">\r\n"; - $Headers .= 'X-Priority: 3'."\r\n"; - mail($To, $Subject, $Body, $Headers, "-f $From@".MAIL_HOST); - } - - - /** - * Sanitize a string to be allowed as a filename. - * - * @param string $EscapeStr the string to escape - * @return the string with all banned characters removed. - */ - public static function file_string($EscapeStr) { - return str_replace(array('"', '*', '/', ':', '<', '>', '?', '\\', '|'), '', $EscapeStr); - } - - - /** - * Sends a PM from $FromId to $ToId. - * - * @param string $ToID ID of user to send PM to. If $ToID is an array and $ConvID is empty, a message will be sent to multiple users. - * @param string $FromID ID of user to send PM from, 0 to send from system - * @param string $Subject - * @param string $Body - * @param int $ConvID The conversation the message goes in. Leave blank to start a new conversation. - * @return - */ - public static function send_pm($ToID, $FromID, $Subject, $Body, $ConvID = '') { - global $Time; - $UnescapedSubject = $Subject; - $UnescapedBody = $Body; - $Subject = db_string($Subject); - $Body = db_string($Body); - if ($ToID == 0 || $ToID == $FromID) { - // Don't allow users to send messages to the system or themselves - return; - } - - $QueryID = G::$DB->get_query_id(); - - if ($ConvID == '') { - // Create a new conversation. - G::$DB->query(" - INSERT INTO pm_conversations (Subject) - VALUES ('$Subject')"); - $ConvID = G::$DB->inserted_id(); - G::$DB->query(" - INSERT INTO pm_conversations_users - (UserID, ConvID, InInbox, InSentbox, SentDate, ReceivedDate, UnRead) - VALUES - ('$ToID', '$ConvID', '1','0','".sqltime()."', '".sqltime()."', '1')"); - if ($FromID != 0) { - G::$DB->query(" - INSERT INTO pm_conversations_users - (UserID, ConvID, InInbox, InSentbox, SentDate, ReceivedDate, UnRead) - VALUES - ('$FromID', '$ConvID', '0','1','".sqltime()."', '".sqltime()."', '0')"); - } - $ToID = array($ToID); - } else { - // Update the pre-existing conversations. - G::$DB->query(" - UPDATE pm_conversations_users - SET - InInbox = '1', - UnRead = '1', - ReceivedDate = '".sqltime()."' - WHERE UserID IN (".implode(',', $ToID).") - AND ConvID = '$ConvID'"); - - G::$DB->query(" - UPDATE pm_conversations_users - SET - InSentbox = '1', - SentDate = '".sqltime()."' - WHERE UserID = '$FromID' - AND ConvID = '$ConvID'"); - } - - // Now that we have a $ConvID for sure, send the message. - G::$DB->query(" - INSERT INTO pm_messages - (SenderID, ConvID, SentDate, Body) - VALUES - ('$FromID', '$ConvID', '".sqltime()."', '$Body')"); - - // Update the cached new message count. - foreach ($ToID as $ID) { - G::$DB->query(" - SELECT COUNT(ConvID) - FROM pm_conversations_users - WHERE UnRead = '1' - AND UserID = '$ID' - AND InInbox = '1'"); - list($UnRead) = G::$DB->next_record(); - G::$Cache->cache_value("inbox_new_$ID", $UnRead); - } - - G::$DB->query(" - SELECT Username - FROM users_main - WHERE ID = '$FromID'"); - list($SenderName) = G::$DB->next_record(); - foreach ($ToID as $ID) { - G::$DB->query(" - SELECT COUNT(ConvID) - FROM pm_conversations_users - WHERE UnRead = '1' - AND UserID = '$ID' - AND InInbox = '1'"); - list($UnRead) = G::$DB->next_record(); - G::$Cache->cache_value("inbox_new_$ID", $UnRead); - - NotificationsManager::send_push($ID, "Message from $SenderName, Subject: $UnescapedSubject", $UnescapedBody, site_url() . 'inbox.php', NotificationsManager::INBOX); - } - - G::$DB->set_query_id($QueryID); - - return $ConvID; - } - - /** - * Create thread function. - * - * @param int $ForumID - * @param int $AuthorID ID of the user creating the post. - * @param string $Title - * @param string $PostBody - * @return -1 on error, -2 on user not existing, thread id on success. - */ - public static function create_thread($ForumID, $AuthorID, $Title, $PostBody) { - global $Time; - if (!$ForumID || !$AuthorID || !is_number($AuthorID) || !$Title || !$PostBody) { - return -1; - } - - $QueryID = G::$DB->get_query_id(); - - G::$DB->prepared_query(' - SELECT Username - FROM users_main - WHERE ID = ?', $AuthorID); - if (!G::$DB->has_results()) { - G::$DB->set_query_id($QueryID); - return -2; - } - list($AuthorName) = G::$DB->next_record(); - - $ThreadInfo = array(); - $ThreadInfo['IsLocked'] = 0; - $ThreadInfo['IsSticky'] = 0; - - G::$DB->prepared_query(' - INSERT INTO forums_topics - (Title, AuthorID, ForumID, LastPostID, LastPostTime, LastPostAuthorID, CreatedTime) - VALUES - (?, ?, ?, ?, ?, ?, ?)', - $Title, $AuthorID, $ForumID, -1, sqltime(), $AuthorID, sqltime()); - $TopicID = G::$DB->inserted_id(); - $Posts = 1; - - G::$DB->prepared_query(' - INSERT INTO forums_posts - (TopicID, AuthorID, AddedTime, Body) - VALUES - (?, ?, ?, ?)', $TopicID, $AuthorID, sqltime(), $PostBody); - $PostID = G::$DB->inserted_id(); - - G::$DB->prepared_query(' - UPDATE forums - SET - NumPosts = NumPosts + 1, - NumTopics = NumTopics + 1, - LastPostID = ?, - LastPostAuthorID = ?, - LastPostTopicID = ?, - LastPostTime = ? - WHERE ID = ?', $PostID, $AuthorID, $TopicID, sqltime(), $ForumID); - - G::$DB->prepared_query(' - UPDATE forums_topics - SET - NumPosts = NumPosts + 1, - LastPostID = ?, - LastPostAuthorID = ?, - LastPostTime = ? - WHERE ID = ?', $PostID, $AuthorID, sqltime(), $TopicID); - - // Bump this topic to head of the cache - list($Forum,,, $Stickies) = G::$Cache->get_value("forums_$ForumID"); - if (!empty($Forum)) { - if (count($Forum) == TOPICS_PER_PAGE && $Stickies < TOPICS_PER_PAGE) { - array_pop($Forum); - } - G::$DB->prepared_query(' - SELECT IsLocked, IsSticky, NumPosts - FROM forums_topics - WHERE ID = ?', $TopicID); - list($IsLocked, $IsSticky, $NumPosts) = G::$DB->next_record(); - $Part1 = array_slice($Forum, 0, $Stickies, true); //Stickys - $Part2 = array( - $TopicID => array( - 'ID' => $TopicID, - 'Title' => $Title, - 'AuthorID' => $AuthorID, - 'IsLocked' => $IsLocked, - 'IsSticky' => $IsSticky, - 'NumPosts' => $NumPosts, - 'LastPostID' => $PostID, - 'LastPostTime' => sqltime(), - 'LastPostAuthorID' => $AuthorID, - ) - ); //Bumped thread - $Part3 = array_slice($Forum, $Stickies, TOPICS_PER_PAGE, true); //Rest of page - if ($Stickies > 0) { - $Part1 = array_slice($Forum, 0, $Stickies, true); //Stickies - $Part3 = array_slice($Forum, $Stickies, TOPICS_PER_PAGE - $Stickies - 1, true); //Rest of page - } else { - $Part1 = array(); - $Part3 = $Forum; - } - if (is_null($Part1)) { - $Part1 = array(); - } - if (is_null($Part3)) { - $Part3 = array(); - } - $Forum = $Part1 + $Part2 + $Part3; - G::$Cache->cache_value("forums_$ForumID", array($Forum, '', 0, $Stickies), 0); - } - - //Update the forum root - G::$Cache->begin_transaction('forums_list'); - $UpdateArray = array( - 'NumPosts' => '+1', - 'NumTopics' => '+1', - 'LastPostID' => $PostID, - 'LastPostAuthorID' => $AuthorID, - 'LastPostTopicID' => $TopicID, - 'LastPostTime' => sqltime(), - 'Title' => $Title, - 'IsLocked' => $ThreadInfo['IsLocked'], - 'IsSticky' => $ThreadInfo['IsSticky'] - ); - - $UpdateArray['NumTopics'] = '+1'; - - G::$Cache->update_row($ForumID, $UpdateArray); - G::$Cache->commit_transaction(0); - - $CatalogueID = floor((POSTS_PER_PAGE * ceil($Posts / POSTS_PER_PAGE) - POSTS_PER_PAGE) / THREAD_CATALOGUE); - G::$Cache->begin_transaction('thread_'.$TopicID.'_catalogue_'.$CatalogueID); - $Post = array( - 'ID' => $PostID, - 'AuthorID' => G::$LoggedUser['ID'], - 'AddedTime' => sqltime(), - 'Body' => $PostBody, - 'EditedUserID' => 0, - 'EditedTime' => '0000-00-00 00:00:00', - 'Username' => '' - ); - G::$Cache->insert('', $Post); - G::$Cache->commit_transaction(0); - - G::$Cache->begin_transaction('thread_'.$TopicID.'_info'); - G::$Cache->update_row(false, array('Posts' => '+1', 'LastPostAuthorID' => $AuthorID)); - G::$Cache->commit_transaction(0); - - G::$DB->set_query_id($QueryID); - - return $TopicID; - } - - /** - * Variant of in_array() with trailing wildcard support - * - * @param string $Needle, array $Haystack - * @return boolean true if (substring of) $Needle exists in $Haystack - */ - public static function in_array_partial($Needle, $Haystack) { - static $Searches = array(); - if (array_key_exists($Needle, $Searches)) { - return $Searches[$Needle]; - } - foreach ($Haystack as $String) { - if (substr($String, -1) == '*') { - if (!strncmp($Needle, $String, strlen($String) - 1)) { - $Searches[$Needle] = true; - return true; - } - } elseif (!strcmp($Needle, $String)) { - $Searches[$Needle] = true; - return true; - } - } - $Searches[$Needle] = false; - return false; - } - - /** - * Used to check if keys in $_POST and $_GET are all set, and throws an error if not. - * This reduces 'if' statement redundancy for a lot of variables - * - * @param array $Request Either $_POST or $_GET, or whatever other array you want to check. - * @param array $Keys The keys to ensure are set. - * @param boolean $AllowEmpty If set to true, a key that is in the request but blank will not throw an error. - * @param int $Error The error code to throw if one of the keys isn't in the array. - */ - public static function assert_isset_request($Request, $Keys = null, $AllowEmpty = false, $Error = 0) { - if (isset($Keys)) { - foreach ($Keys as $K) { - if (!isset($Request[$K]) || ($AllowEmpty == false && $Request[$K] == '')) { - error($Error); - break; - } - } - } else { - foreach ($Request as $R) { - if (!isset($R) || ($AllowEmpty == false && $R == '')) { - error($Error); - break; - } - } - } - } - - - /** - * Given an array of tags, return an array of their IDs. - * - * @param array $TagNames - * @return array IDs - */ - public static function get_tags($TagNames) { - $TagIDs = array(); - foreach ($TagNames as $Index => $TagName) { - $Tag = G::$Cache->get_value("tag_id_$TagName"); - if (is_array($Tag)) { - unset($TagNames[$Index]); - $TagIDs[$Tag['ID']] = $Tag['Name']; - } - } - if (count($TagNames) > 0) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT ID, Name - FROM tags - WHERE Name IN ('".implode("', '", $TagNames)."')"); - $SQLTagIDs = G::$DB->to_array(); - G::$DB->set_query_id($QueryID); - foreach ($SQLTagIDs as $Tag) { - $TagIDs[$Tag['ID']] = $Tag['Name']; - G::$Cache->cache_value('tag_id_'.$Tag['Name'], $Tag, 0); - } - } - - return($TagIDs); - } - - - /** - * Gets the alias of the tag; if there is no alias, silently returns the original tag. - * - * @param string $BadTag the tag we want to alias - * @return string The aliased tag. - */ - public static function get_alias_tag($BadTag) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT AliasTag - FROM tag_aliases - WHERE BadTag = '$BadTag' - LIMIT 1"); - if (G::$DB->has_results()) { - list($AliasTag) = G::$DB->next_record(); - } else { - $AliasTag = $BadTag; - } - G::$DB->set_query_id($QueryID); - return $AliasTag; - } - - - /* - * Write a message to the system log. - * - * @param string $Message the message to write. - */ - public static function write_log($Message) { - global $Time; - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - INSERT INTO log (Message, Time) - VALUES ('" . db_string($Message) . "', '" . sqltime() . "')"); - G::$DB->set_query_id($QueryID); - } - - - /** - * Get a tag ready for database input and display. - * - * @param string $Str - * @return string sanitized version of $Str - */ - public static function sanitize_tag($Str) { - $Str = strtolower($Str); - $Str = preg_replace('/[^a-z0-9.]/', '', $Str); - $Str = preg_replace('/(^[.,]*)|([.,]*$)/', '', $Str); - $Str = htmlspecialchars($Str); - $Str = db_string(trim($Str)); - return $Str; - } - - /** - * HTML escape an entire array for output. - * @param array $Array, what we want to escape - * @param boolean|array $Escape - * if true, all keys escaped - * if false, no escaping. - * If array, it's a list of array keys not to escape. - * @param boolean $Reverse reverses $Escape such that then it's an array of keys to escape - * @return array mutated version of $Array with values escaped. - */ - public static function display_array($Array, $Escape = array(), $Reverse = false) { - foreach ($Array as $Key => $Val) { - if ((!is_array($Escape) && $Escape == true) || (!$Reverse && !in_array($Key, $Escape)) || ($Reverse && in_array($Key, $Escape))) { - $Array[$Key] = display_str($Val); - } - } - return $Array; - } - - /** - * Searches for a key/value pair in an array. - * - * @return array of results - */ - public static function search_array($Array, $Key, $Value) { - $Results = array(); - if (is_array($Array)) - { - if (isset($Array[$Key]) && $Array[$Key] == $Value) { - $Results[] = $Array; - } - - foreach ($Array as $subarray) { - $Results = array_merge($Results, self::search_array($subarray, $Key, $Value)); - } - } - return $Results; - } - - /** - * Search for $Needle in the string $Haystack which is a list of values separated by $Separator. - * @param string $Haystack - * @param string $Needle - * @param string $Separator - * @param boolean $Strict - * @return boolean - */ - public static function search_joined_string($Haystack, $Needle, $Separator = '|', $Strict = true) { - return (array_search($Needle, explode($Separator, $Haystack), $Strict) !== false); - } - - /** - * Check for a ":" in the beginning of a torrent meta data string - * to see if it's stored in the old base64-encoded format - * - * @param string $Torrent the torrent data - * @return true if the torrent is stored in binary format - */ - public static function is_new_torrent(&$Data) { - return strpos(substr($Data, 0, 10), ':') !== false; - } - - public static function display_recommend($ID, $Type, $Hide = true) { - if ($Hide) { - $Hide = ' style="display: none;"'; - } - ?> -
    class="center"> -
    - Recommend to: - - - -
    -

    -
    -'."\r\n"; + $Headers .= 'Reply-To: '.$From.'@'.MAIL_HOST."\r\n"; + $Headers .= 'Message-Id: <'.Users::make_secret().'@'.MAIL_HOST.">\r\n"; + $Headers .= 'X-Priority: 3'."\r\n"; + mail($To, $Subject, $Body, $Headers, "-f $From@".MAIL_HOST); + } + + + /** + * Sanitize a string to be allowed as a filename. + * + * @param string $EscapeStr the string to escape + * @return the string with all banned characters removed. + */ + public static function file_string($EscapeStr) { + return str_replace(array('"', '*', '/', ':', '<', '>', '?', '\\', '|'), '', $EscapeStr); + } + + + /** + * Sends a PM from $FromId to $ToId. + * + * @param string $ToID ID of user to send PM to. If $ToID is an array and $ConvID is empty, a message will be sent to multiple users. + * @param string $FromID ID of user to send PM from, 0 to send from system + * @param string $Subject + * @param string $Body + * @param int $ConvID The conversation the message goes in. Leave blank to start a new conversation. + * @return + */ + public static function send_pm($ToID, $FromID, $Subject, $Body, $ConvID = '') { + global $Time; + $UnescapedSubject = $Subject; + $UnescapedBody = $Body; + $Subject = db_string($Subject); + $Body = db_string($Body); + if ($ToID == 0 || $ToID == $FromID) { + // Don't allow users to send messages to the system or themselves + return; + } + + $QueryID = G::$DB->get_query_id(); + + if ($ConvID == '') { + // Create a new conversation. + G::$DB->query(" + INSERT INTO pm_conversations (Subject) + VALUES ('$Subject')"); + $ConvID = G::$DB->inserted_id(); + G::$DB->query(" + INSERT INTO pm_conversations_users + (UserID, ConvID, InInbox, InSentbox, SentDate, ReceivedDate, UnRead) + VALUES + ('$ToID', '$ConvID', '1','0','".sqltime()."', '".sqltime()."', '1')"); + if ($FromID != 0) { + G::$DB->query(" + INSERT INTO pm_conversations_users + (UserID, ConvID, InInbox, InSentbox, SentDate, ReceivedDate, UnRead) + VALUES + ('$FromID', '$ConvID', '0','1','".sqltime()."', '".sqltime()."', '0')"); + } + $ToID = array($ToID); + } else { + // Update the pre-existing conversations. + G::$DB->query(" + UPDATE pm_conversations_users + SET + InInbox = '1', + UnRead = '1', + ReceivedDate = '".sqltime()."' + WHERE UserID IN (".implode(',', $ToID).") + AND ConvID = '$ConvID'"); + + G::$DB->query(" + UPDATE pm_conversations_users + SET + InSentbox = '1', + SentDate = '".sqltime()."' + WHERE UserID = '$FromID' + AND ConvID = '$ConvID'"); + } + + // Now that we have a $ConvID for sure, send the message. + G::$DB->query(" + INSERT INTO pm_messages + (SenderID, ConvID, SentDate, Body) + VALUES + ('$FromID', '$ConvID', '".sqltime()."', '$Body')"); + + // Update the cached new message count. + foreach ($ToID as $ID) { + G::$DB->query(" + SELECT COUNT(ConvID) + FROM pm_conversations_users + WHERE UnRead = '1' + AND UserID = '$ID' + AND InInbox = '1'"); + list($UnRead) = G::$DB->next_record(); + G::$Cache->cache_value("inbox_new_$ID", $UnRead); + } + + G::$DB->query(" + SELECT Username + FROM users_main + WHERE ID = '$FromID'"); + list($SenderName) = G::$DB->next_record(); + foreach ($ToID as $ID) { + G::$DB->query(" + SELECT COUNT(ConvID) + FROM pm_conversations_users + WHERE UnRead = '1' + AND UserID = '$ID' + AND InInbox = '1'"); + list($UnRead) = G::$DB->next_record(); + G::$Cache->cache_value("inbox_new_$ID", $UnRead); + + NotificationsManager::send_push($ID, "Message from $SenderName, Subject: $UnescapedSubject", $UnescapedBody, site_url() . 'inbox.php', NotificationsManager::INBOX); + } + + G::$DB->set_query_id($QueryID); + + return $ConvID; + } + + /** + * Create thread function. + * + * @param int $ForumID + * @param int $AuthorID ID of the user creating the post. + * @param string $Title + * @param string $PostBody + * @return -1 on error, -2 on user not existing, thread id on success. + */ + public static function create_thread($ForumID, $AuthorID, $Title, $PostBody) { + global $Time; + if (!$ForumID || !$AuthorID || !is_number($AuthorID) || !$Title || !$PostBody) { + return -1; + } + + $QueryID = G::$DB->get_query_id(); + + G::$DB->prepared_query(' + SELECT Username + FROM users_main + WHERE ID = ?', $AuthorID); + if (!G::$DB->has_results()) { + G::$DB->set_query_id($QueryID); + return -2; + } + list($AuthorName) = G::$DB->next_record(); + + $ThreadInfo = []; + $ThreadInfo['IsLocked'] = 0; + $ThreadInfo['IsSticky'] = 0; + + G::$DB->prepared_query(' + INSERT INTO forums_topics + (Title, AuthorID, ForumID, LastPostID, LastPostTime, LastPostAuthorID, CreatedTime) + VALUES + (?, ?, ?, ?, ?, ?, ?)', + $Title, $AuthorID, $ForumID, -1, sqltime(), $AuthorID, sqltime()); + $TopicID = G::$DB->inserted_id(); + $Posts = 1; + + G::$DB->prepared_query(' + INSERT INTO forums_posts + (TopicID, AuthorID, AddedTime, Body) + VALUES + (?, ?, ?, ?)', $TopicID, $AuthorID, sqltime(), $PostBody); + $PostID = G::$DB->inserted_id(); + + G::$DB->prepared_query(' + UPDATE forums + SET + NumPosts = NumPosts + 1, + NumTopics = NumTopics + 1, + LastPostID = ?, + LastPostAuthorID = ?, + LastPostTopicID = ?, + LastPostTime = ? + WHERE ID = ?', $PostID, $AuthorID, $TopicID, sqltime(), $ForumID); + + G::$DB->prepared_query(' + UPDATE forums_topics + SET + NumPosts = NumPosts + 1, + LastPostID = ?, + LastPostAuthorID = ?, + LastPostTime = ? + WHERE ID = ?', $PostID, $AuthorID, sqltime(), $TopicID); + + // Bump this topic to head of the cache + list($Forum,,, $Stickies) = G::$Cache->get_value("forums_$ForumID"); + if (!empty($Forum)) { + if (count($Forum) == TOPICS_PER_PAGE && $Stickies < TOPICS_PER_PAGE) { + array_pop($Forum); + } + G::$DB->prepared_query(' + SELECT IsLocked, IsSticky, NumPosts + FROM forums_topics + WHERE ID = ?', $TopicID); + list($IsLocked, $IsSticky, $NumPosts) = G::$DB->next_record(); + $Part1 = array_slice($Forum, 0, $Stickies, true); //Stickys + $Part2 = array( + $TopicID => array( + 'ID' => $TopicID, + 'Title' => $Title, + 'AuthorID' => $AuthorID, + 'IsLocked' => $IsLocked, + 'IsSticky' => $IsSticky, + 'NumPosts' => $NumPosts, + 'LastPostID' => $PostID, + 'LastPostTime' => sqltime(), + 'LastPostAuthorID' => $AuthorID, + ) + ); //Bumped thread + $Part3 = array_slice($Forum, $Stickies, TOPICS_PER_PAGE, true); //Rest of page + if ($Stickies > 0) { + $Part1 = array_slice($Forum, 0, $Stickies, true); //Stickies + $Part3 = array_slice($Forum, $Stickies, TOPICS_PER_PAGE - $Stickies - 1, true); //Rest of page + } else { + $Part1 = []; + $Part3 = $Forum; + } + if (is_null($Part1)) { + $Part1 = []; + } + if (is_null($Part3)) { + $Part3 = []; + } + $Forum = $Part1 + $Part2 + $Part3; + G::$Cache->cache_value("forums_$ForumID", array($Forum, '', 0, $Stickies), 0); + } + + //Update the forum root + G::$Cache->begin_transaction('forums_list'); + $UpdateArray = array( + 'NumPosts' => '+1', + 'NumTopics' => '+1', + 'LastPostID' => $PostID, + 'LastPostAuthorID' => $AuthorID, + 'LastPostTopicID' => $TopicID, + 'LastPostTime' => sqltime(), + 'Title' => $Title, + 'IsLocked' => $ThreadInfo['IsLocked'], + 'IsSticky' => $ThreadInfo['IsSticky'] + ); + + $UpdateArray['NumTopics'] = '+1'; + + G::$Cache->update_row($ForumID, $UpdateArray); + G::$Cache->commit_transaction(0); + + $CatalogueID = floor((POSTS_PER_PAGE * ceil($Posts / POSTS_PER_PAGE) - POSTS_PER_PAGE) / THREAD_CATALOGUE); + G::$Cache->begin_transaction('thread_'.$TopicID.'_catalogue_'.$CatalogueID); + $Post = array( + 'ID' => $PostID, + 'AuthorID' => G::$LoggedUser['ID'], + 'AddedTime' => sqltime(), + 'Body' => $PostBody, + 'EditedUserID' => 0, + 'EditedTime' => '0000-00-00 00:00:00', + 'Username' => '' + ); + G::$Cache->insert('', $Post); + G::$Cache->commit_transaction(0); + + G::$Cache->begin_transaction('thread_'.$TopicID.'_info'); + G::$Cache->update_row(false, array('Posts' => '+1', 'LastPostAuthorID' => $AuthorID)); + G::$Cache->commit_transaction(0); + + G::$DB->set_query_id($QueryID); + + return $TopicID; + } + + /** + * Variant of in_array with trailing wildcard support + * + * @param string $Needle, array $Haystack + * @return boolean true if (substring of) $Needle exists in $Haystack + */ + public static function in_array_partial($Needle, $Haystack) { + static $Searches = []; + if (array_key_exists($Needle, $Searches)) { + return $Searches[$Needle]; + } + foreach ($Haystack as $String) { + if (substr($String, -1) == '*') { + if (!strncmp($Needle, $String, strlen($String) - 1)) { + $Searches[$Needle] = true; + return true; + } + } elseif (!strcmp($Needle, $String)) { + $Searches[$Needle] = true; + return true; + } + } + $Searches[$Needle] = false; + return false; + } + + /** + * Used to check if keys in $_POST and $_GET are all set, and throws an error if not. + * This reduces 'if' statement redundancy for a lot of variables + * + * @param array $Request Either $_POST or $_GET, or whatever other array you want to check. + * @param array $Keys The keys to ensure are set. + * @param boolean $AllowEmpty If set to true, a key that is in the request but blank will not throw an error. + * @param int $Error The error code to throw if one of the keys isn't in the array. + */ + public static function assert_isset_request($Request, $Keys = null, $AllowEmpty = false, $Error = 0) { + if (isset($Keys)) { + foreach ($Keys as $K) { + if (!isset($Request[$K]) || ($AllowEmpty == false && $Request[$K] == '')) { + error($Error); + break; + } + } + } else { + foreach ($Request as $R) { + if (!isset($R) || ($AllowEmpty == false && $R == '')) { + error($Error); + break; + } + } + } + } + + + /** + * Given an array of tags, return an array of their IDs. + * + * @param array $TagNames + * @return array IDs + */ + public static function get_tags($TagNames) { + $TagIDs = []; + foreach ($TagNames as $Index => $TagName) { + $Tag = G::$Cache->get_value("tag_id_$TagName"); + if (is_array($Tag)) { + unset($TagNames[$Index]); + $TagIDs[$Tag['ID']] = $Tag['Name']; + } + } + if (count($TagNames) > 0) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT ID, Name + FROM tags + WHERE Name IN ('".implode("', '", $TagNames)."')"); + $SQLTagIDs = G::$DB->to_array; + G::$DB->set_query_id($QueryID); + foreach ($SQLTagIDs as $Tag) { + $TagIDs[$Tag['ID']] = $Tag['Name']; + G::$Cache->cache_value('tag_id_'.$Tag['Name'], $Tag, 0); + } + } + + return($TagIDs); + } + + + /** + * Gets the alias of the tag; if there is no alias, silently returns the original tag. + * + * @param string $BadTag the tag we want to alias + * @return string The aliased tag. + */ + public static function get_alias_tag($BadTag) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT AliasTag + FROM tag_aliases + WHERE BadTag = '$BadTag' + LIMIT 1"); + if (G::$DB->has_results()) { + list($AliasTag) = G::$DB->next_record(); + } else { + $AliasTag = $BadTag; + } + G::$DB->set_query_id($QueryID); + return $AliasTag; + } + + + /* + * Write a message to the system log. + * + * @param string $Message the message to write. + */ + public static function write_log($Message) { + global $Time; + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + INSERT INTO log (Message, Time) + VALUES ('" . db_string($Message) . "', '" . sqltime() . "')"); + G::$DB->set_query_id($QueryID); + } + + + /** + * Get a tag ready for database input and display. + * + * @param string $Str + * @return string sanitized version of $Str + */ + public static function sanitize_tag($Str) { + $Str = strtolower($Str); + $Str = preg_replace('/[^a-z0-9.]/', '', $Str); + $Str = preg_replace('/(^[.,]*)|([.,]*$)/', '', $Str); + $Str = htmlspecialchars($Str); + $Str = db_string(trim($Str)); + return $Str; + } + + /** + * HTML escape an entire array for output. + * @param array $Array, what we want to escape + * @param boolean|array $Escape + * if true, all keys escaped + * if false, no escaping. + * If array, it's a list of array keys not to escape. + * @param boolean $Reverse reverses $Escape such that then it's an array of keys to escape + * @return array mutated version of $Array with values escaped. + */ + public static function display_array($Array, $Escape = [], $Reverse = false) { + foreach ($Array as $Key => $Val) { + if ((!is_array($Escape) && $Escape == true) || (!$Reverse && !in_array($Key, $Escape)) || ($Reverse && in_array($Key, $Escape))) { + $Array[$Key] = display_str($Val); + } + } + return $Array; + } + + /** + * Searches for a key/value pair in an array. + * + * @return array of results + */ + public static function search_array($Array, $Key, $Value) { + $Results = []; + if (is_array($Array)) + { + if (isset($Array[$Key]) && $Array[$Key] == $Value) { + $Results[] = $Array; + } + + foreach ($Array as $subarray) { + $Results = array_merge($Results, self::search_array($subarray, $Key, $Value)); + } + } + return $Results; + } + + /** + * Search for $Needle in the string $Haystack which is a list of values separated by $Separator. + * @param string $Haystack + * @param string $Needle + * @param string $Separator + * @param boolean $Strict + * @return boolean + */ + public static function search_joined_string($Haystack, $Needle, $Separator = '|', $Strict = true) { + return (array_search($Needle, explode($Separator, $Haystack), $Strict) !== false); + } + + /** + * Check for a ":" in the beginning of a torrent meta data string + * to see if it's stored in the old base64-encoded format + * + * @param string $Torrent the torrent data + * @return true if the torrent is stored in binary format + */ + public static function is_new_torrent(&$Data) { + return strpos(substr($Data, 0, 10), ':') !== false; + } + + public static function display_recommend($ID, $Type, $Hide = true) { + if ($Hide) { + $Hide = ' style="display: none;"'; + } + ?> +
    class="center"> +
    + Recommend to: + + + +
    +

    +
    + diff --git a/classes/mysql.class.php b/classes/mysql.class.php index 5bb1a6d20..aee9395b2 100644 --- a/classes/mysql.class.php +++ b/classes/mysql.class.php @@ -1,4 +1,4 @@ -query(" - SELECT * - FROM table..."); + SELECT * + FROM table..."); - Is functionally equivalent to using mysqli_query("SELECT * FROM table...") - Stores the result set in $this->QueryID - Returns the result set, so you can save it for later (see set_query_id()) + Is functionally equivalent to using mysqli_query("SELECT * FROM table...") + Stores the result set in $this->QueryID + Returns the result set, so you can save it for later (see set_query_id()) ----- * Getting data from a query $array = $DB->next_record(); - Is functionally equivalent to using mysqli_fetch_array($ResultSet) - You do not need to specify a result set - it uses $this-QueryID + Is functionally equivalent to using mysqli_fetch_array($ResultSet) + You do not need to specify a result set - it uses $this-QueryID ----- * Escaping a string db_string($str); - Is a wrapper for $DB->escape_str(), which is a wrapper for - mysqli_real_escape_string(). The db_string() function exists so that you - don't have to keep calling $DB->escape_str(). + Is a wrapper for $DB->escape_str(), which is a wrapper for + mysqli_real_escape_string(). The db_string() function exists so that you + don't have to keep calling $DB->escape_str(). - USE THIS FUNCTION EVERY TIME YOU USE AN UNVALIDATED USER-SUPPLIED VALUE IN - A DATABASE QUERY! + USE THIS FUNCTION EVERY TIME YOU USE AN UNVALIDATED USER-SUPPLIED VALUE IN + A DATABASE QUERY! //--------- Advanced usage --------------------------------------------------------- @@ -57,555 +57,555 @@ * This is how you loop over the result set: while (list($All, $Columns, $That, $You, $Select) = $DB->next_record()) { - echo "Do stuff with $All of the ".$Columns.$That.$You.$Select; + echo "Do stuff with $All of the ".$Columns.$That.$You.$Select; } ----- * There are also a couple more mysqli functions that have been wrapped. They are: record_count() - Wrapper to mysqli_num_rows() + Wrapper to mysqli_num_rows() affected_rows() - Wrapper to mysqli_affected_rows() + Wrapper to mysqli_affected_rows() inserted_id() - Wrapper to mysqli_insert_id() + Wrapper to mysqli_insert_id() close - Wrapper to mysqli_close() + Wrapper to mysqli_close() ----- * And, of course, a few handy custom functions. to_array($Key = false) - Transforms an entire result set into an array (useful in situations where you - can't order the rows properly in the query). + Transforms an entire result set into an array (useful in situations where you + can't order the rows properly in the query). - If $Key is set, the function uses $Key as the index (good for looking up a - field). Otherwise, it uses an iterator. + If $Key is set, the function uses $Key as the index (good for looking up a + field). Otherwise, it uses an iterator. - For an example of this function in action, check out forum.php. + For an example of this function in action, check out forum.php. collect($Key) - Loops over the result set, creating an array from one of the fields ($Key). - For an example, see forum.php. + Loops over the result set, creating an array from one of the fields ($Key). + For an example, see forum.php. set_query_id($ResultSet) - This class can only hold one result set at a time. Using set_query_id allows - you to set the result set that the class is using to the result set in - $ResultSet. This result set should have been obtained earlier by using - $DB->query(). + This class can only hold one result set at a time. Using set_query_id allows + you to set the result set that the class is using to the result set in + $ResultSet. This result set should have been obtained earlier by using + $DB->query(). - Example: + Example: - $FoodRS = $DB->query(" - SELECT * - FROM food"); - $DB->query(" - SELECT * - FROM drink"); - $Drinks = $DB->next_record(); - $DB->set_query_id($FoodRS); - $Food = $DB->next_record(); + $FoodRS = $DB->query(" + SELECT * + FROM food"); + $DB->query(" + SELECT * + FROM drink"); + $Drinks = $DB->next_record(); + $DB->set_query_id($FoodRS); + $Food = $DB->next_record(); - Of course, this example is contrived, but you get the point. + Of course, this example is contrived, but you get the point. ------------------------------------------------------------------------------------- *///--------------------------------------------------------------------------------- if (!extension_loaded('mysqli')) { - die('Mysqli Extension not loaded.'); + die('Mysqli Extension not loaded.'); } function enum_boolean($bool) { - return $bool == true ? '1' : '0'; + return $bool == true ? '1' : '0'; } //Handles escaping function db_string($String, $DisableWildcards = false) { - global $DB; - //Escape - $String = $DB->escape_str($String); - //Remove user input wildcards - if ($DisableWildcards) { - $String = str_replace(array('%','_'), array('\%','\_'), $String); - } - return $String; + global $DB; + //Escape + $String = $DB->escape_str($String); + //Remove user input wildcards + if ($DisableWildcards) { + $String = str_replace(array('%','_'), array('\%','\_'), $String); + } + return $String; } -function db_array($Array, $DontEscape = array(), $Quote = false) { - foreach ($Array as $Key => $Val) { - if (!in_array($Key, $DontEscape)) { - if ($Quote) { - $Array[$Key] = '\''.db_string(trim($Val)).'\''; - } else { - $Array[$Key] = db_string(trim($Val)); - } - } - } - return $Array; +function db_array($Array, $DontEscape = [], $Quote = false) { + foreach ($Array as $Key => $Val) { + if (!in_array($Key, $DontEscape)) { + if ($Quote) { + $Array[$Key] = '\''.db_string(trim($Val)).'\''; + } else { + $Array[$Key] = db_string(trim($Val)); + } + } + } + return $Array; } //TODO: revisit access levels once Drone is replaced by ZeRobot class DB_MYSQL { - /** @var mysqli|bool */ - public $LinkID = false; - /** @var mysqli_result|bool */ - protected $QueryID = false; - protected $Record = array(); - protected $Row; - protected $Errno = 0; - protected $Error = ''; - - protected $PreparedQuery = null; - protected $Statement = null; - - public $Queries = array(); - public $Time = 0.0; - - protected $Database = ''; - protected $Server = ''; - protected $User = ''; - protected $Pass = ''; - protected $Port = 0; - protected $Socket = ''; - - function __construct($Database = SQLDB, $User = SQLLOGIN, $Pass = SQLPASS, $Server = SQLHOST, $Port = SQLPORT, $Socket = SQLSOCK) { - $this->Database = $Database; - $this->Server = $Server; - $this->User = $User; - $this->Pass = $Pass; - $this->Port = $Port; - $this->Socket = $Socket; - } - - function halt($Msg) { - global $Debug, $argv; - $DBError = 'MySQL: '.strval($Msg).' SQL error: '.strval($this->Errno).' ('.strval($this->Error).')'; - if ($this->Errno == 1194) { - send_irc('PRIVMSG '.ADMIN_CHAN.' :'.$this->Error); - } - /*if ($this->Errno == 1194) { - preg_match("Table '(\S+)' is marked as crashed and should be repaired", $this->Error, $Matches); - } */ - $Debug->analysis('!dev DB Error', $DBError, 3600 * 24); - if (DEBUG_MODE || check_perms('site_debug') || isset($argv[1])) { - echo '
    '.display_str($DBError).'
    '; - if (DEBUG_MODE || check_perms('site_debug')) { - print_r($this->Queries); - } - die(); - } else { - error('-1'); - } - } - - function connect() { - if (!$this->LinkID) { - $this->LinkID = mysqli_connect($this->Server, $this->User, $this->Pass, $this->Database, $this->Port, $this->Socket); // defined in config.php - if (!$this->LinkID) { - $this->Errno = mysqli_connect_errno(); - $this->Error = mysqli_connect_error(); - $this->halt('Connection failed (host:'.$this->Server.':'.$this->Port.')'); - } - } - } - - private function setup_query() { - /* - * If there was a previous query, we store the warnings. We cannot do - * this immediately after mysqli_query because mysqli_insert_id will - * break otherwise due to mysqli_get_warnings sending a SHOW WARNINGS; - * query. When sending a query, however, we're sure that we won't call - * mysqli_insert_id (or any similar function, for that matter) later on, - * so we can safely get the warnings without breaking things. - * Note that this means that we have to call $this->warnings manually - * for the last query! - */ - if ($this->QueryID) { - $this->warnings(); - } - - $this->connect(); - } - - /** - * Runs a raw query assuming pre-sanitized input. However, attempting to self sanitize (such - * as via db_string) is still not as safe for using prepared statements so for queries - * involving user input, you really should not use this function (instead opting for - * prepare_query) {@See DB_MYSQL::prepare_query} - * - * When running a batch of queries using the same statement - * with a variety of inputs, it's more performant to reuse the statement - * with {@see DB_MYSQL::prepare} and {@see DB_MYSQL::execute} - * - * @return mysqli_result|bool Returns a mysqli_result object - * for successful SELECT queries, - * or TRUE for other successful DML queries - * or FALSE on failure. - * - * @param $Query - * @param int $AutoHandle - * @return mysqli_result|bool - */ - function query($Query, $AutoHandle=1) { - $this->setup_query(); - $LinkID = &$this->LinkID; - - $Closure = function() use ($LinkID, $Query) { - return mysqli_query($this->LinkID, $Query); - }; - - return $this->attempt_query($Query, $Closure, $AutoHandle); - } - - /** - * Prepares an SQL statement for execution with data. - * - * Normally, you'll most likely just want to be using - * DB_MYSQL::prepared_query to call both DB_MYSQL::prepare - * and DB_MYSQL::execute for one-off queries, you can use - * this separately in the case where you plan to be running - * this query repeatedly while just changing the bound - * parameters (such as if doing a bulk update or the like). - * - * @return mysqli_stmt|bool Returns a statement object - * or FALSE if an error occurred. - */ - function prepare($Query) { - $this->setup_query(); - $this->PreparedQuery = $Query; - $this->Statement = $this->LinkID->prepare($Query); - if ($this->Statement === false) { - $this->halt('Invalid Query: ' . mysqli_error($this->LinkID)); - } - return $this->Statement; - } - - /** - * Bind variables to our last prepared query and execute it. - * - * Variables that are passed into the function will have their - * type automatically set for how to bind it to the query (either - * integer (i), double (d), or string (s)). - * - * @param array $Parameters,... variables for the query - * @return mysqli_result|bool Returns a mysqli_result object - * for successful SELECT queries, - * or TRUE for other successful DML queries - * or FALSE on failure. - */ - function execute(...$Parameters) { - /** @var mysqli_stmt $Statement */ - $Statement = &$this->Statement; - - if (count($Parameters) > 0) { - $Binders = ""; - foreach ($Parameters as $Parameter) { - if (is_integer($Parameter)) { - $Binders .= "i"; - } - elseif (is_double($Parameter)) { - $Binders .= "d"; - } - else { - $Binders .= "s"; - } - } - $Statement->bind_param($Binders, ...$Parameters); - } - - $Closure = function() use ($Statement) { - $Statement->execute(); - return $Statement->get_result(); - }; - - $Query = "Prepared Statement: {$this->PreparedQuery}\n"; - foreach ($Parameters as $key => $value) { - $Query .= "$key => $value\n"; - } - - - return $this->attempt_query($Query, $Closure); - } - - /** - * Prepare and execute a prepared query returning the result set. - * - * Utility function that wraps DB_MYSQL::prepare and DB_MYSQL::execute - * as most times, the query is going to be one-off and this will save - * on keystrokes. If you do plan to be executing a prepared query - * multiple times with different bound parameters, you'll want to call - * the two functions separately instead of this function. - * - * @param $Query - * @param array ...$Parameters - * @return bool|mysqli_result - */ - function prepared_query($Query, ...$Parameters) { - $this->prepare($Query); - return $this->execute(...$Parameters); - } - - function prepared_query_array($Query, array $args) { - $this->prepare($Query); - $param = []; - $bind = ''; - $n = count($args); - for ($i = 0; $i < $n; ++$i) { - if (is_integer($args[$i])) { - $bind .= 'i'; - } - elseif (is_double($args[$i])) { - $bind .= 'd'; - } - else { - $bind .= 's'; - } - $param[] = &$args[$i]; - } - $refbind = &$bind; - array_unshift($param, $refbind); - $stmt = &$this->Statement; - call_user_func_array([$this->Statement, "bind_param"], $param); - - return $this->attempt_query( - $Query, - function() use ($stmt) { - $stmt->execute(); - return $stmt->get_result(); - } - ); - } - - private function attempt_query($Query, Callable $Closure, $AutoHandle=1) { - global $Debug; - $QueryStartTime = microtime(true); - // In the event of a MySQL deadlock, we sleep allowing MySQL time to unlock, then attempt again for a maximum of 5 tries - for ($i = 1; $i < 6; $i++) { - $this->QueryID = $Closure(); - if (!in_array(mysqli_errno($this->LinkID), array(1213, 1205))) { - break; - } - $Debug->analysis('Non-Fatal Deadlock:', $Query, 3600 * 24); - trigger_error("Database deadlock, attempt $i"); - - sleep($i * rand(2, 5)); // Wait longer as attempts increase - } - $QueryEndTime = microtime(true); - // Kills admin pages, and prevents Debug->analysis when the whole set exceeds 1 MB - if (($Len = strlen($Query))>16384) { - $Query = substr($Query, 0, 16384).'... '.($Len-16384).' bytes trimmed'; - } - $this->Queries[] = array($Query, ($QueryEndTime - $QueryStartTime) * 1000, null); - $this->Time += ($QueryEndTime - $QueryStartTime) * 1000; - - // Update/Insert/etc statements for prepared queries don't return a QueryID, - // but mysqli_errno is also going to be 0 for no error - $this->Errno = mysqli_errno($this->LinkID); - if (!$this->QueryID && $this->Errno !== 0) { - $this->Error = mysqli_error($this->LinkID); - - if ($AutoHandle) { - $this->halt("Invalid Query: $Query"); - } else { - return $this->Errno; - } - } - - $this->Row = 0; - if ($AutoHandle) { - return $this->QueryID; - } - } - - function query_unb($Query) { - $this->connect(); - mysqli_real_query($this->LinkID, $Query); - } - - function inserted_id() { - if ($this->LinkID) { - return mysqli_insert_id($this->LinkID); - } - } - - function next_record($Type = MYSQLI_BOTH, $Escape = true, $Reverse = false) { - // $Escape can be true, false, or an array of keys to not escape - // If $Reverse is true, then $Escape is an array of keys to escape - if ($this->LinkID) { - $this->Record = mysqli_fetch_array($this->QueryID, $Type); - $this->Row++; - if (!is_array($this->Record)) { - $this->QueryID = false; - } elseif ($Escape !== false) { - $this->Record = Misc::display_array($this->Record, $Escape, $Reverse); - } - return $this->Record; - } - return null; - } - - /** - * Fetches next record from the result set of the previously executed query. - * - * Utility around next_record where we just return the array as MYSQLI_BOTH - * and require the user to explicitly define which columns to define (as opposed - * to all columns always being escaped, which is a bad sort of lazy). Things that - * need to be escaped are strings that users input (with any characters) and - * are not displayed inside a textarea or input field. - * - * @param mixed $Escape Boolean true/false for escaping entire/none of query - * or can be an array of array keys for what columns to escape - * @return array next result set if exists - */ - function fetch_record(...$Escape) { - if (count($Escape) === 1 && $Escape[0] === true) { - $Escape = true; - } - elseif (count($Escape) === 0) { - $Escape = false; - } - return $this->next_record(MYSQLI_BOTH, $Escape, true); - } - - function close() { - if ($this->LinkID) { - if (!mysqli_close($this->LinkID)) { - $this->halt('Cannot close connection or connection did not open.'); - } - $this->LinkID = false; - } - } - - /* - * returns an integer with the number of rows found - * returns a string if the number of rows found exceeds MAXINT - */ - function record_count() { - if ($this->QueryID) { - return mysqli_num_rows($this->QueryID); - } - } - - /* - * returns true if the query exists and there were records found - * returns false if the query does not exist or if there were 0 records returned - */ - function has_results() { - return ($this->QueryID && $this->record_count() !== 0); - } - - function affected_rows() { - if ($this->LinkID) { - return $this->LinkID->affected_rows; - } - } - - function info() { - return mysqli_get_host_info($this->LinkID); - } - - // You should use db_string() instead. - function escape_str($Str) { - $this->connect(); - if (is_array($Str)) { - trigger_error('Attempted to escape array.'); - return ''; - } - return mysqli_real_escape_string($this->LinkID, $Str); - } - - // Creates an array from a result set - // If $Key is set, use the $Key column in the result set as the array key - // Otherwise, use an integer - function to_array($Key = false, $Type = MYSQLI_BOTH, $Escape = true) { - $Return = array(); - while ($Row = mysqli_fetch_array($this->QueryID, $Type)) { - if ($Escape !== false) { - $Row = Misc::display_array($Row, $Escape); - } - if ($Key !== false) { - $Return[$Row[$Key]] = $Row; - } else { - $Return[] = $Row; - } - } - mysqli_data_seek($this->QueryID, 0); - return $Return; - } - - // Loops through the result set, collecting the $ValField column into an array with $KeyField as keys - function to_pair($KeyField, $ValField, $Escape = true) { - $Return = array(); - while ($Row = mysqli_fetch_array($this->QueryID)) { - if ($Escape) { - $Key = display_str($Row[$KeyField]); - $Val = display_str($Row[$ValField]); - } else { - $Key = $Row[$KeyField]; - $Val = $Row[$ValField]; - } - $Return[$Key] = $Val; - } - mysqli_data_seek($this->QueryID, 0); - return $Return; - } - - // Loops through the result set, collecting the $Key column into an array - function collect($Key, $Escape = true) { - $Return = array(); - while ($Row = mysqli_fetch_array($this->QueryID)) { - $Return[] = $Escape ? display_str($Row[$Key]) : $Row[$Key]; - } - mysqli_data_seek($this->QueryID, 0); - return $Return; - } - - function set_query_id(&$ResultSet) { - $this->QueryID = $ResultSet; - $this->Row = 0; - } - - function get_query_id() { - return $this->QueryID; - } - - function beginning() { - mysqli_data_seek($this->QueryID, 0); - $this->Row = 0; - } - - /** - * This function determines whether the last query caused warning messages - * and stores them in $this->Queries. - */ - function warnings() { - $Warnings = array(); - if ($this->LinkID !== false && mysqli_warning_count($this->LinkID)) { - $e = mysqli_get_warnings($this->LinkID); - do { - if ($e->errno == 1592) { - // 1592: Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. - continue; - } - $Warnings[] = 'Code ' . $e->errno . ': ' . display_str($e->message); - } while ($e->next()); - } - $this->Queries[count($this->Queries) - 1][2] = $Warnings; - } - - function begin_transaction() { - mysqli_begin_transaction($this->LinkID); - } - - function commit() { - mysqli_commit($this->LinkID); - } - - function rollback() { - mysqli_rollback($this->LinkID); - } + /** @var mysqli|bool */ + public $LinkID = false; + /** @var mysqli_result|bool */ + protected $QueryID = false; + protected $Record = []; + protected $Row; + protected $Errno = 0; + protected $Error = ''; + + protected $PreparedQuery = null; + protected $Statement = null; + + public $Queries = []; + public $Time = 0.0; + + protected $Database = ''; + protected $Server = ''; + protected $User = ''; + protected $Pass = ''; + protected $Port = 0; + protected $Socket = ''; + + function __construct($Database = SQLDB, $User = SQLLOGIN, $Pass = SQLPASS, $Server = SQLHOST, $Port = SQLPORT, $Socket = SQLSOCK) { + $this->Database = $Database; + $this->Server = $Server; + $this->User = $User; + $this->Pass = $Pass; + $this->Port = $Port; + $this->Socket = $Socket; + } + + function halt($Msg) { + global $Debug, $argv; + $DBError = 'MySQL: '.strval($Msg).' SQL error: '.strval($this->Errno).' ('.strval($this->Error).')'; + if ($this->Errno == 1194) { + send_irc('PRIVMSG '.ADMIN_CHAN.' :'.$this->Error); + } + /*if ($this->Errno == 1194) { + preg_match("Table '(\S+)' is marked as crashed and should be repaired", $this->Error, $Matches); + } */ + $Debug->analysis('!dev DB Error', $DBError, 3600 * 24); + if (DEBUG_MODE || check_perms('site_debug') || isset($argv[1])) { + echo '
    '.display_str($DBError).'
    '; + if (DEBUG_MODE || check_perms('site_debug')) { + print_r($this->Queries); + } + die(); + } else { + error('-1'); + } + } + + function connect() { + if (!$this->LinkID) { + $this->LinkID = mysqli_connect($this->Server, $this->User, $this->Pass, $this->Database, $this->Port, $this->Socket); // defined in config.php + if (!$this->LinkID) { + $this->Errno = mysqli_connect_errno(); + $this->Error = mysqli_connect_error(); + $this->halt('Connection failed (host:'.$this->Server.':'.$this->Port.')'); + } + } + } + + private function setup_query() { + /* + * If there was a previous query, we store the warnings. We cannot do + * this immediately after mysqli_query because mysqli_insert_id will + * break otherwise due to mysqli_get_warnings sending a SHOW WARNINGS; + * query. When sending a query, however, we're sure that we won't call + * mysqli_insert_id (or any similar function, for that matter) later on, + * so we can safely get the warnings without breaking things. + * Note that this means that we have to call $this->warnings manually + * for the last query! + */ + if ($this->QueryID) { + $this->warnings(); + } + + $this->connect(); + } + + /** + * Runs a raw query assuming pre-sanitized input. However, attempting to self sanitize (such + * as via db_string) is still not as safe for using prepared statements so for queries + * involving user input, you really should not use this function (instead opting for + * prepared_query) {@See DB_MYSQL::prepared_query} + * + * When running a batch of queries using the same statement + * with a variety of inputs, it's more performant to reuse the statement + * with {@see DB_MYSQL::prepare} and {@see DB_MYSQL::execute} + * + * @return mysqli_result|bool Returns a mysqli_result object + * for successful SELECT queries, + * or TRUE for other successful DML queries + * or FALSE on failure. + * + * @param $Query + * @param int $AutoHandle + * @return mysqli_result|bool + */ + function query($Query, $AutoHandle=1) { + $this->setup_query(); + $LinkID = &$this->LinkID; + + $Closure = function() use ($LinkID, $Query) { + return mysqli_query($this->LinkID, $Query); + }; + + return $this->attempt_query($Query, $Closure, $AutoHandle); + } + + /** + * Prepares an SQL statement for execution with data. + * + * Normally, you'll most likely just want to be using + * DB_MYSQL::prepared_query to call both DB_MYSQL::prepare + * and DB_MYSQL::execute for one-off queries, you can use + * this separately in the case where you plan to be running + * this query repeatedly while just changing the bound + * parameters (such as if doing a bulk update or the like). + * + * @return mysqli_stmt|bool Returns a statement object + * or FALSE if an error occurred. + */ + function prepare($Query) { + $this->setup_query(); + $this->PreparedQuery = $Query; + $this->Statement = $this->LinkID->prepare($Query); + if ($this->Statement === false) { + $this->halt("Invalid Query: [$Query] " . mysqli_error($this->LinkID)); + } + return $this->Statement; + } + + /** + * Bind variables to our last prepared query and execute it. + * + * Variables that are passed into the function will have their + * type automatically set for how to bind it to the query (either + * integer (i), double (d), or string (s)). + * + * @param array $Parameters,... variables for the query + * @return mysqli_result|bool Returns a mysqli_result object + * for successful SELECT queries, + * or TRUE for other successful DML queries + * or FALSE on failure. + */ + function execute(...$Parameters) { + /** @var mysqli_stmt $Statement */ + $Statement = &$this->Statement; + + if (count($Parameters) > 0) { + $Binders = ""; + foreach ($Parameters as $Parameter) { + if (is_integer($Parameter)) { + $Binders .= "i"; + } + elseif (is_double($Parameter)) { + $Binders .= "d"; + } + else { + $Binders .= "s"; + } + } + $Statement->bind_param($Binders, ...$Parameters); + } + + $Closure = function() use ($Statement) { + $Statement->execute(); + return $Statement->get_result(); + }; + + $Query = "$this->PreparedQuery\n"; + foreach ($Parameters as $key => $value) { + $Query .= "$key => $value\n"; + } + + + return $this->attempt_query($Query, $Closure); + } + + /** + * Prepare and execute a prepared query returning the result set. + * + * Utility function that wraps DB_MYSQL::prepare and DB_MYSQL::execute + * as most times, the query is going to be one-off and this will save + * on keystrokes. If you do plan to be executing a prepared query + * multiple times with different bound parameters, you'll want to call + * the two functions separately instead of this function. + * + * @param $Query + * @param array ...$Parameters + * @return bool|mysqli_result + */ + function prepared_query($Query, ...$Parameters) { + $this->prepare($Query); + return $this->execute(...$Parameters); + } + + function prepared_query_array($Query, array $args) { + $this->prepare($Query); + $param = []; + $bind = ''; + $n = count($args); + for ($i = 0; $i < $n; ++$i) { + if (is_integer($args[$i])) { + $bind .= 'i'; + } + elseif (is_double($args[$i])) { + $bind .= 'd'; + } + else { + $bind .= 's'; + } + $param[] = &$args[$i]; + } + $refbind = &$bind; + array_unshift($param, $refbind); + $stmt = &$this->Statement; + call_user_func_array([$this->Statement, "bind_param"], $param); + + return $this->attempt_query( + $Query, + function() use ($stmt) { + $stmt->execute(); + return $stmt->get_result(); + } + ); + } + + private function attempt_query($Query, Callable $Closure, $AutoHandle=1) { + global $Debug; + $QueryStartTime = microtime(true); + // In the event of a MySQL deadlock, we sleep allowing MySQL time to unlock, then attempt again for a maximum of 5 tries + for ($i = 1; $i < 6; $i++) { + $this->QueryID = $Closure(); + if (!in_array(mysqli_errno($this->LinkID), array(1213, 1205))) { + break; + } + $Debug->analysis('Non-Fatal Deadlock:', $Query, 3600 * 24); + trigger_error("Database deadlock, attempt $i"); + + sleep($i * rand(2, 5)); // Wait longer as attempts increase + } + $QueryEndTime = microtime(true); + // Kills admin pages, and prevents Debug->analysis when the whole set exceeds 1 MB + if (($Len = strlen($Query))>16384) { + $Query = substr($Query, 0, 16384).'... '.($Len-16384).' bytes trimmed'; + } + $this->Queries[] = array($Query, ($QueryEndTime - $QueryStartTime) * 1000, null); + $this->Time += ($QueryEndTime - $QueryStartTime) * 1000; + + // Update/Insert/etc statements for prepared queries don't return a QueryID, + // but mysqli_errno is also going to be 0 for no error + $this->Errno = mysqli_errno($this->LinkID); + if (!$this->QueryID && $this->Errno !== 0) { + $this->Error = mysqli_error($this->LinkID); + + if ($AutoHandle) { + $this->halt("Invalid Query: $Query"); + } else { + return $this->Errno; + } + } + + $this->Row = 0; + if ($AutoHandle) { + return $this->QueryID; + } + } + + function query_unb($Query) { + $this->connect(); + mysqli_real_query($this->LinkID, $Query); + } + + function inserted_id() { + if ($this->LinkID) { + return mysqli_insert_id($this->LinkID); + } + } + + function next_record($Type = MYSQLI_BOTH, $Escape = true, $Reverse = false) { + // $Escape can be true, false, or an array of keys to not escape + // If $Reverse is true, then $Escape is an array of keys to escape + if ($this->LinkID) { + $this->Record = mysqli_fetch_array($this->QueryID, $Type); + $this->Row++; + if (!is_array($this->Record)) { + $this->QueryID = false; + } elseif ($Escape !== false) { + $this->Record = Misc::display_array($this->Record, $Escape, $Reverse); + } + return $this->Record; + } + return null; + } + + /** + * Fetches next record from the result set of the previously executed query. + * + * Utility around next_record where we just return the array as MYSQLI_BOTH + * and require the user to explicitly define which columns to define (as opposed + * to all columns always being escaped, which is a bad sort of lazy). Things that + * need to be escaped are strings that users input (with any characters) and + * are not displayed inside a textarea or input field. + * + * @param mixed $Escape Boolean true/false for escaping entire/none of query + * or can be an array of array keys for what columns to escape + * @return array next result set if exists + */ + function fetch_record(...$Escape) { + if (count($Escape) === 1 && $Escape[0] === true) { + $Escape = true; + } + elseif (count($Escape) === 0) { + $Escape = false; + } + return $this->next_record(MYSQLI_BOTH, $Escape, true); + } + + function close() { + if ($this->LinkID) { + if (!mysqli_close($this->LinkID)) { + $this->halt('Cannot close connection or connection did not open.'); + } + $this->LinkID = false; + } + } + + /* + * returns an integer with the number of rows found + * returns a string if the number of rows found exceeds MAXINT + */ + function record_count() { + if ($this->QueryID) { + return mysqli_num_rows($this->QueryID); + } + } + + /* + * returns true if the query exists and there were records found + * returns false if the query does not exist or if there were 0 records returned + */ + function has_results() { + return ($this->QueryID && $this->record_count() !== 0); + } + + function affected_rows() { + if ($this->LinkID) { + return $this->LinkID->affected_rows; + } + } + + function info() { + return mysqli_get_host_info($this->LinkID); + } + + // You should use db_string() instead. + function escape_str($Str) { + $this->connect(); + if (is_array($Str)) { + trigger_error('Attempted to escape array.'); + return ''; + } + return mysqli_real_escape_string($this->LinkID, $Str); + } + + // Creates an array from a result set + // If $Key is set, use the $Key column in the result set as the array key + // Otherwise, use an integer + function to_array($Key = false, $Type = MYSQLI_BOTH, $Escape = true) { + $Return = []; + while ($Row = mysqli_fetch_array($this->QueryID, $Type)) { + if ($Escape !== false) { + $Row = Misc::display_array($Row, $Escape); + } + if ($Key !== false) { + $Return[$Row[$Key]] = $Row; + } else { + $Return[] = $Row; + } + } + mysqli_data_seek($this->QueryID, 0); + return $Return; + } + + // Loops through the result set, collecting the $ValField column into an array with $KeyField as keys + function to_pair($KeyField, $ValField, $Escape = true) { + $Return = []; + while ($Row = mysqli_fetch_array($this->QueryID)) { + if ($Escape) { + $Key = display_str($Row[$KeyField]); + $Val = display_str($Row[$ValField]); + } else { + $Key = $Row[$KeyField]; + $Val = $Row[$ValField]; + } + $Return[$Key] = $Val; + } + mysqli_data_seek($this->QueryID, 0); + return $Return; + } + + // Loops through the result set, collecting the $Key column into an array + function collect($Key, $Escape = true) { + $Return = []; + while ($Row = mysqli_fetch_array($this->QueryID)) { + $Return[] = $Escape ? display_str($Row[$Key]) : $Row[$Key]; + } + mysqli_data_seek($this->QueryID, 0); + return $Return; + } + + function set_query_id(&$ResultSet) { + $this->QueryID = $ResultSet; + $this->Row = 0; + } + + function get_query_id() { + return $this->QueryID; + } + + function beginning() { + mysqli_data_seek($this->QueryID, 0); + $this->Row = 0; + } + + /** + * This function determines whether the last query caused warning messages + * and stores them in $this->Queries. + */ + function warnings() { + $Warnings = []; + if ($this->LinkID !== false && mysqli_warning_count($this->LinkID)) { + $e = mysqli_get_warnings($this->LinkID); + do { + if ($e->errno == 1592) { + // 1592: Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. + continue; + } + $Warnings[] = 'Code ' . $e->errno . ': ' . display_str($e->message); + } while ($e->next()); + } + $this->Queries[count($this->Queries) - 1][2] = $Warnings; + } + + function begin_transaction() { + mysqli_begin_transaction($this->LinkID); + } + + function commit() { + mysqli_commit($this->LinkID); + } + + function rollback() { + mysqli_rollback($this->LinkID); + } } diff --git a/classes/notificationsmanager.class.php b/classes/notificationsmanager.class.php index afb0efdaa..2e1d4bdc6 100644 --- a/classes/notificationsmanager.class.php +++ b/classes/notificationsmanager.class.php @@ -1,800 +1,800 @@ - self::IMPORTANT, - 'critical' => self::CRITICAL, - 'warning' => self::WARNING, - 'info' => self::INFO); - - // Types. These names must correspond to column names in users_notifications_settings - const NEWS = 'News'; - const BLOG = 'Blog'; - const STAFFBLOG = 'StaffBlog'; - const STAFFPM = 'StaffPM'; - const INBOX = 'Inbox'; - const QUOTES = 'Quotes'; - const SUBSCRIPTIONS = 'Subscriptions'; - const TORRENTS = 'Torrents'; - const COLLAGES = 'Collages'; - const SITEALERTS = 'SiteAlerts'; - const FORUMALERTS = 'ForumAlerts'; - const REQUESTALERTS = 'RequestAlerts'; - const COLLAGEALERTS = 'CollageAlerts'; - const TORRENTALERTS = 'TorrentAlerts'; - const GLOBALNOTICE = 'Global'; - - public static $Types = array( - 'News', - 'Blog', - 'StaffPM', - 'Inbox', - 'Quotes', - 'Subscriptions', - 'Torrents', - 'Collages', - 'SiteAlerts', - 'ForumAlerts', - 'RequestAlerts', - 'CollageAlerts', - 'TorrentAlerts'); - - private $UserID; - private $Notifications; - private $Settings; - private $Skipped; - - function __construct($UserID, $Skip = array(), $Load = true, $AutoSkip = true) { - $this->UserID = $UserID; - $this->Notifications = array(); - $this->Settings = self::get_settings($UserID); - $this->Skipped = $Skip; - if ($AutoSkip) { - foreach ($this->Settings as $Key => $Value) { - // Skip disabled and traditional settings - if ($Value == self::OPT_DISABLED || $this->is_traditional($Key)) { - $this->Skipped[$Key] = true; - } - } - } - if ($Load) { - $this->load_global_notification(); - if (!isset($this->Skipped[self::NEWS])) { - $this->load_news(); - } - if (!isset($this->Skipped[self::BLOG])) { - $this->load_blog(); - } - // if (!isset($this->Skipped[self::STAFFBLOG])) { - // $this->load_staff_blog(); - // } - if (!isset($this->Skipped[self::STAFFPM])) { - $this->load_staff_pms(); - } - if (!isset($this->Skipped[self::INBOX])) { - $this->load_inbox(); - } - if (!isset($this->Skipped[self::TORRENTS])) { - $this->load_torrent_notifications(); - } - if (!isset($this->Skipped[self::COLLAGES])) { - $this->load_collage_subscriptions(); - } - if (!isset($this->Skipped[self::QUOTES])) { - $this->load_quote_notifications(); - } - if (!isset($this->Skipped[self::SUBSCRIPTIONS])) { - $this->load_subscriptions(); - } - // $this->load_one_reads(); // The code that sets these notices is commented out. - } - } - - public function get_notifications() { - return $this->Notifications; - } - - public function clear_notifications_array() { - unset($this->Notifications); - $this->Notifications = array(); - } - - private function create_notification($Type, $ID, $Message, $URL, $Importance) { - $this->Notifications[$Type] = array( - 'id' => (int)$ID, - 'message' => $Message, - 'url' => $URL, - 'importance' => $Importance); - } - - public static function notify_user($UserID, $Type, $Message, $URL, $Importance = self::INFO) { - self::notify_users(array($UserID), $Type, $Message, $URL, $Importance); - } - - public static function notify_users($UserIDs, $Type, $Message, $URL, $Importance = self::INFO) { - /** - if (!isset($Importance)) { - $Importance = self::INFO; - } - $Type = db_string($Type); - if (!empty($UserIDs)) { - $UserIDs = implode(',', $UserIDs); - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT UserID - FROM users_notifications_settings - WHERE $Type != 0 - AND UserID IN ($UserIDs)"); - $UserIDs = array(); - while (list($ID) = G::$DB->next_record()) { - $UserIDs[] = $ID; - } - G::$DB->set_query_id($QueryID); - foreach ($UserIDs as $UserID) { - $OneReads = G::$Cache->get_value("notifications_one_reads_$UserID"); - if (!$OneReads) { - $OneReads = array(); - } - array_unshift($OneReads, $this->create_notification($OneReads, "oneread_" . uniqid(), null, $Message, $URL, $Importance)); - $OneReads = array_filter($OneReads); - G::$Cache->cache_value("notifications_one_reads_$UserID", $OneReads, 0); - } - } - **/ - } - - public static function get_notification_enabled_users($Type, $UserID) { - $Type = db_string($Type); - $UserWhere = ''; - if (isset($UserID)) { - $UserID = (int)$UserID; - $UserWhere = " AND UserID = '$UserID'"; - } - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT UserID - FROM users_notifications_settings - WHERE $Type != 0 - $UserWhere"); - $IDs = array(); - while (list($ID) = G::$DB->next_record()) { - $IDs[] = $ID; - } - G::$DB->set_query_id($QueryID); - return $IDs; - } - - public function load_one_reads() { - $OneReads = G::$Cache->get_value('notifications_one_reads_' . G::$LoggedUser['ID']); - if (is_array($OneReads)) { - $this->Notifications = $this->Notifications + $OneReads; - } - } - - public static function clear_one_read($ID) { - $OneReads = G::$Cache->get_value('notifications_one_reads_' . G::$LoggedUser['ID']); - if ($OneReads) { - unset($OneReads[$ID]); - if (count($OneReads) > 0) { - G::$Cache->cache_value('notifications_one_reads_' . G::$LoggedUser['ID'], $OneReads, 0); - } else { - G::$Cache->delete_value('notifications_one_reads_' . G::$LoggedUser['ID']); - } - } - - } - - public function load_global_notification() { - $GlobalNotification = G::$Cache->get_value('global_notification'); - if ($GlobalNotification) { - $Read = G::$Cache->get_value('user_read_global_' . G::$LoggedUser['ID']); - if (!$Read) { - $this->create_notification(self::GLOBALNOTICE, 0, $GlobalNotification['Message'], $GlobalNotification['URL'], $GlobalNotification['Importance']); - } - } - } - - public static function get_global_notification() { - return G::$Cache->get_value('global_notification'); - } - - public static function set_global_notification($Message, $URL, $Importance, $Expiration) { - if (empty($Message) || empty($Expiration)) { - error('Error setting notification'); - } - G::$Cache->cache_value('global_notification', array("Message" => $Message, "URL" => $URL, "Importance" => $Importance, "Expiration" => $Expiration), $Expiration); - } - - public static function delete_global_notification() { - G::$Cache->delete_value('global_notification'); - } - - public static function clear_global_notification() { - $GlobalNotification = G::$Cache->get_value('global_notification'); - if ($GlobalNotification) { - // This is some trickery - // since we can't know which users have the read cache key set - // we set the expiration time of their cache key to that of the length of the notification - // this gaurantees that their cache key will expire after the notification expires - G::$Cache->cache_value('user_read_global_' . G::$LoggedUser['ID'], true, $GlobalNotification['Expiration']); - } - } - - public function load_news() { - $MyNews = G::$LoggedUser['LastReadNews']; - $CurrentNews = G::$Cache->get_value('news_latest_id'); - $Title = G::$Cache->get_value('news_latest_title'); - if ($CurrentNews === false || $Title === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(' - SELECT ID, Title - FROM news - ORDER BY Time DESC - LIMIT 1'); - if (G::$DB->has_results()) { - list($CurrentNews, $Title) = G::$DB->next_record(); - } else { - $CurrentNews = -1; - } - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value('news_latest_id', $CurrentNews, 0); - G::$Cache->cache_value('news_latest_title', $Title, 0); - } - if ($MyNews < $CurrentNews) { - $this->create_notification(self::NEWS, $CurrentNews, "Announcement: $Title", "index.php#news$CurrentNews", self::IMPORTANT); - } - } - - public function load_blog() { - $MyBlog = G::$LoggedUser['LastReadBlog']; - $CurrentBlog = G::$Cache->get_value('blog_latest_id'); - $Title = G::$Cache->get_value('blog_latest_title'); - if ($CurrentBlog === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(' - SELECT ID, Title - FROM blog - WHERE Important = 1 - ORDER BY Time DESC - LIMIT 1'); - if (G::$DB->has_results()) { - list($CurrentBlog, $Title) = G::$DB->next_record(); - } else { - $CurrentBlog = -1; - } - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value('blog_latest_id', $CurrentBlog, 0); - G::$Cache->cache_value('blog_latest_title', $Title, 0); - } - if ($MyBlog < $CurrentBlog) { - $this->create_notification(self::BLOG, $CurrentBlog, "Blog: $Title", "blog.php#blog$CurrentBlog", self::IMPORTANT); - } - } - - public function load_staff_blog() { - if (check_perms('users_mod')) { - global $SBlogReadTime, $LatestSBlogTime; - if (!$SBlogReadTime && ($SBlogReadTime = G::$Cache->get_value('staff_blog_read_' . G::$LoggedUser['ID'])) === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT Time - FROM staff_blog_visits - WHERE UserID = " . G::$LoggedUser['ID']); - if (list($SBlogReadTime) = G::$DB->next_record()) { - $SBlogReadTime = strtotime($SBlogReadTime); - } else { - $SBlogReadTime = 0; - } - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value('staff_blog_read_' . G::$LoggedUser['ID'], $SBlogReadTime, 1209600); - } - if (!$LatestSBlogTime && ($LatestSBlogTime = G::$Cache->get_value('staff_blog_latest_time')) === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(' - SELECT MAX(Time) - FROM staff_blog'); - if (list($LatestSBlogTime) = G::$DB->next_record()) { - $LatestSBlogTime = strtotime($LatestSBlogTime); - } else { - $LatestSBlogTime = 0; - } - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value('staff_blog_latest_time', $LatestSBlogTime, 1209600); - } - if ($SBlogReadTime < $LatestSBlogTime) { - $this->create_notification(self::STAFFBLOG, 0, 'New Staff Blog Post!', 'staffblog.php', self::IMPORTANT); - } - } - } - - public function load_staff_pms() { - $NewStaffPMs = G::$Cache->get_value('staff_pm_new_' . G::$LoggedUser['ID']); - if ($NewStaffPMs === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT COUNT(ID) - FROM staff_pm_conversations - WHERE UserID = '" . G::$LoggedUser['ID'] . "' - AND Unread = '1'"); - list($NewStaffPMs) = G::$DB->next_record(); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value('staff_pm_new_' . G::$LoggedUser['ID'], $NewStaffPMs, 0); - } - - if ($NewStaffPMs > 0) { - $Title = 'You have ' . ($NewStaffPMs == 1 ? 'a' : $NewStaffPMs) . ' new Staff PM' . ($NewStaffPMs > 1 ? 's' : ''); - $this->create_notification(self::STAFFPM, 0, $Title, 'staffpm.php', self::INFO); - } - } - - public function load_inbox() { - $NewMessages = G::$Cache->get_value('inbox_new_' . G::$LoggedUser['ID']); - if ($NewMessages === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT COUNT(UnRead) - FROM pm_conversations_users - WHERE UserID = '" . G::$LoggedUser['ID'] . "' - AND UnRead = '1' - AND InInbox = '1'"); - list($NewMessages) = G::$DB->next_record(); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value('inbox_new_' . G::$LoggedUser['ID'], $NewMessages, 0); - } - - if ($NewMessages > 0) { - $Title = 'You have ' . ($NewMessages == 1 ? 'a' : $NewMessages) . ' new message' . ($NewMessages > 1 ? 's' : ''); - $this->create_notification(self::INBOX, 0, $Title, Inbox::get_inbox_link(), self::INFO); - } - } - - public function load_torrent_notifications() { - if (check_perms('site_torrents_notify')) { - $NewNotifications = G::$Cache->get_value('notifications_new_' . G::$LoggedUser['ID']); - if ($NewNotifications === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT COUNT(UserID) - FROM users_notify_torrents - WHERE UserID = ' " . G::$LoggedUser['ID'] . "' - AND UnRead = '1'"); - list($NewNotifications) = G::$DB->next_record(); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value('notifications_new_' . G::$LoggedUser['ID'], $NewNotifications, 0); - } - } - if ($NewNotifications > 0) { - $Title = 'You have ' . ($NewNotifications == 1 ? 'a' : $NewNotifications) . ' new torrent notification' . ($NewNotifications > 1 ? 's' : ''); - $this->create_notification(self::TORRENTS, 0, $Title, 'torrents.php?action=notify', self::INFO); - } - } - - public function load_collage_subscriptions() { - if (check_perms('site_collages_subscribe')) { - $NewCollages = G::$Cache->get_value('collage_subs_user_new_' . G::$LoggedUser['ID']); - if ($NewCollages === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT COUNT(DISTINCT s.CollageID) - FROM users_collage_subs AS s - JOIN collages AS c ON s.CollageID = c.ID - JOIN collages_torrents AS ct ON ct.CollageID = c.ID - WHERE s.UserID = " . G::$LoggedUser['ID'] . " - AND ct.AddedOn > s.LastVisit - AND c.Deleted = '0'"); - list($NewCollages) = G::$DB->next_record(); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value('collage_subs_user_new_' . G::$LoggedUser['ID'], $NewCollages, 0); - } - if ($NewCollages > 0) { - $Title = 'You have ' . ($NewCollages == 1 ? 'a' : $NewCollages) . ' new collage update' . ($NewCollages > 1 ? 's' : ''); - $this->create_notification(self::COLLAGES, 0, $Title, 'userhistory.php?action=subscribed_collages', self::INFO); - } - } - } - - public function load_quote_notifications() { - if (isset(G::$LoggedUser['NotifyOnQuote']) && G::$LoggedUser['NotifyOnQuote']) { - $QuoteNotificationsCount = Subscriptions::has_new_quote_notifications(); - if ($QuoteNotificationsCount > 0) { - $Title = 'New quote' . ($QuoteNotificationsCount > 1 ? 's' : ''); - $this->create_notification(self::QUOTES, 0, $Title, 'userhistory.php?action=quote_notifications', self::INFO); - } - } - } - - public function load_subscriptions() { - $SubscriptionsCount = Subscriptions::has_new_subscriptions(); - if ($SubscriptionsCount > 0) { - $Title = 'New subscription' . ($SubscriptionsCount > 1 ? 's' : ''); - $this->create_notification(self::SUBSCRIPTIONS, 0, $Title, 'userhistory.php?action=subscriptions', self::INFO); - } - } - - public static function clear_news($News = null) { - $QueryID = G::$DB->get_query_id(); - if (!$News) { - if (!$News = G::$Cache->get_value('news')) { - G::$DB->query(' - SELECT - ID, - Title, - Body, - Time - FROM news - ORDER BY Time DESC - LIMIT 1'); - $News = G::$DB->to_array(false, MYSQLI_NUM, false); - G::$Cache->cache_value('news_latest_id', $News[0][0], 0); - } - } - - if (G::$LoggedUser['LastReadNews'] != $News[0][0]) { - G::$Cache->begin_transaction('user_info_heavy_' . G::$LoggedUser['ID']); - G::$Cache->update_row(false, array('LastReadNews' => $News[0][0])); - G::$Cache->commit_transaction(0); - G::$DB->query(" - UPDATE users_info - SET LastReadNews = '".$News[0][0]."' - WHERE UserID = " . G::$LoggedUser['ID']); - G::$LoggedUser['LastReadNews'] = $News[0][0]; - } - G::$DB->set_query_id($QueryID); - } - - public static function clear_blog($Blog = null) { - $QueryID = G::$DB->get_query_id(); - if (!$Blog) { - if (!$Blog = G::$Cache->get_value('blog')) { - G::$DB->query(" - SELECT - b.ID, - um.Username, - b.UserID, - b.Title, - b.Body, - b.Time, - b.ThreadID - FROM blog AS b - LEFT JOIN users_main AS um ON b.UserID = um.ID - ORDER BY Time DESC - LIMIT 1"); - $Blog = G::$DB->to_array(); - } - } - if (G::$LoggedUser['LastReadBlog'] < $Blog[0][0]) { - G::$Cache->begin_transaction('user_info_heavy_' . G::$LoggedUser['ID']); - G::$Cache->update_row(false, array('LastReadBlog' => $Blog[0][0])); - G::$Cache->commit_transaction(0); - G::$DB->query(" - UPDATE users_info - SET LastReadBlog = '". $Blog[0][0]."' - WHERE UserID = " . G::$LoggedUser['ID']); - G::$LoggedUser['LastReadBlog'] = $Blog[0][0]; - } - G::$DB->set_query_id($QueryID); - } - - public static function clear_staff_pms() { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT ID - FROM staff_pm_conversations - WHERE Unread = true - AND UserID = " . G::$LoggedUser['ID']); - $IDs = array(); - while (list($ID) = G::$DB->next_record()) { - $IDs[] = $ID; - } - $IDs = implode(',', $IDs); - if (!empty($IDs)) { - G::$DB->query(" - UPDATE staff_pm_conversations - SET Unread = false - WHERE ID IN ($IDs)"); - } - G::$Cache->delete_value('staff_pm_new_' . G::$LoggedUser['ID']); - G::$DB->set_query_id($QueryID); - } - - public static function clear_inbox() { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT ConvID - FROM pm_conversations_users - WHERE Unread = '1' - AND UserID = " . G::$LoggedUser['ID']); - $IDs = array(); - while (list($ID) = G::$DB->next_record()) { - $IDs[] = $ID; - } - $IDs = implode(',', $IDs); - if (!empty($IDs)) { - G::$DB->query(" - UPDATE pm_conversations_users - SET Unread = '0' - WHERE ConvID IN ($IDs) - AND UserID = " . G::$LoggedUser['ID']); - } - G::$Cache->delete_value('inbox_new_' . G::$LoggedUser['ID']); - G::$DB->set_query_id($QueryID); - } - - public static function clear_torrents() { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT TorrentID - FROM users_notify_torrents - WHERE UserID = ' " . G::$LoggedUser['ID'] . "' - AND UnRead = '1'"); - $IDs = array(); - while (list($ID) = G::$DB->next_record()) { - $IDs[] = $ID; - } - $IDs = implode(',', $IDs); - if (!empty($IDs)) { - G::$DB->query(" - UPDATE users_notify_torrents - SET Unread = '0' - WHERE TorrentID IN ($IDs) - AND UserID = " . G::$LoggedUser['ID']); - } - G::$Cache->delete_value('notifications_new_' . G::$LoggedUser['ID']); - G::$DB->set_query_id($QueryID); - } - - public static function clear_collages() { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - UPDATE users_collage_subs - SET LastVisit = NOW() - WHERE UserID = " . G::$LoggedUser['ID']); - G::$Cache->delete_value('collage_subs_user_new_' . G::$LoggedUser['ID']); - G::$DB->set_query_id($QueryID); - } - - public static function clear_quotes() { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - UPDATE users_notify_quoted - SET UnRead = '0' - WHERE UserID = " . G::$LoggedUser['ID']); - G::$Cache->delete_value('notify_quoted_' . G::$LoggedUser['ID']); - G::$DB->set_query_id($QueryID); - } - - public static function clear_subscriptions() { - $QueryID = G::$DB->get_query_id(); - if (($UserSubscriptions = G::$Cache->get_value('subscriptions_user_' . G::$LoggedUser['ID'])) === false) { - G::$DB->query(" - SELECT TopicID - FROM users_subscriptions - WHERE UserID = " . G::$LoggedUser['ID']); - if ($UserSubscriptions = G::$DB->collect(0)) { - G::$Cache->cache_value('subscriptions_user_' . G::$LoggedUser['ID'], $UserSubscriptions, 0); - } - } - if (!empty($UserSubscriptions)) { - G::$DB->query(" - INSERT INTO forums_last_read_topics (UserID, TopicID, PostID) - SELECT '" . G::$LoggedUser['ID'] . "', ID, LastPostID - FROM forums_topics - WHERE ID IN (".implode(',', $UserSubscriptions).') - ON DUPLICATE KEY UPDATE - PostID = LastPostID'); - } - G::$Cache->delete_value('subscriptions_user_new_' . G::$LoggedUser['ID']); - G::$DB->set_query_id($QueryID); - } + // Option types + const OPT_DISABLED = 0; + const OPT_POPUP = 1; + const OPT_TRADITIONAL = 2; + const OPT_PUSH = 3; + const OPT_POPUP_PUSH = 4; + const OPT_TRADITIONAL_PUSH = 5; + + // Importances + const IMPORTANT = 'information'; + const CRITICAL = 'error'; + const WARNING = 'warning'; + const INFO = 'confirmation'; + + public static $Importances = array( + 'important' => self::IMPORTANT, + 'critical' => self::CRITICAL, + 'warning' => self::WARNING, + 'info' => self::INFO); + + // Types. These names must correspond to column names in users_notifications_settings + const NEWS = 'News'; + const BLOG = 'Blog'; + const STAFFBLOG = 'StaffBlog'; + const STAFFPM = 'StaffPM'; + const INBOX = 'Inbox'; + const QUOTES = 'Quotes'; + const SUBSCRIPTIONS = 'Subscriptions'; + const TORRENTS = 'Torrents'; + const COLLAGES = 'Collages'; + const SITEALERTS = 'SiteAlerts'; + const FORUMALERTS = 'ForumAlerts'; + const REQUESTALERTS = 'RequestAlerts'; + const COLLAGEALERTS = 'CollageAlerts'; + const TORRENTALERTS = 'TorrentAlerts'; + const GLOBALNOTICE = 'Global'; + + public static $Types = array( + 'News', + 'Blog', + 'StaffPM', + 'Inbox', + 'Quotes', + 'Subscriptions', + 'Torrents', + 'Collages', + 'SiteAlerts', + 'ForumAlerts', + 'RequestAlerts', + 'CollageAlerts', + 'TorrentAlerts'); + + private $UserID; + private $Notifications; + private $Settings; + private $Skipped; + + function __construct($UserID, $Skip = array(), $Load = true, $AutoSkip = true) { + $this->UserID = $UserID; + $this->Notifications = array(); + $this->Settings = self::get_settings($UserID); + $this->Skipped = $Skip; + if ($AutoSkip) { + foreach ($this->Settings as $Key => $Value) { + // Skip disabled and traditional settings + if ($Value == self::OPT_DISABLED || $this->is_traditional($Key)) { + $this->Skipped[$Key] = true; + } + } + } + if ($Load) { + $this->load_global_notification(); + if (!isset($this->Skipped[self::NEWS])) { + $this->load_news(); + } + if (!isset($this->Skipped[self::BLOG])) { + $this->load_blog(); + } + // if (!isset($this->Skipped[self::STAFFBLOG])) { + // $this->load_staff_blog(); + // } + if (!isset($this->Skipped[self::STAFFPM])) { + $this->load_staff_pms(); + } + if (!isset($this->Skipped[self::INBOX])) { + $this->load_inbox(); + } + if (!isset($this->Skipped[self::TORRENTS])) { + $this->load_torrent_notifications(); + } + if (!isset($this->Skipped[self::COLLAGES])) { + $this->load_collage_subscriptions(); + } + if (!isset($this->Skipped[self::QUOTES])) { + $this->load_quote_notifications(); + } + if (!isset($this->Skipped[self::SUBSCRIPTIONS])) { + $this->load_subscriptions(); + } + // $this->load_one_reads(); // The code that sets these notices is commented out. + } + } + + public function get_notifications() { + return $this->Notifications; + } + + public function clear_notifications_array() { + unset($this->Notifications); + $this->Notifications = array(); + } + + private function create_notification($Type, $ID, $Message, $URL, $Importance) { + $this->Notifications[$Type] = array( + 'id' => (int)$ID, + 'message' => $Message, + 'url' => $URL, + 'importance' => $Importance); + } + + public static function notify_user($UserID, $Type, $Message, $URL, $Importance = self::INFO) { + self::notify_users(array($UserID), $Type, $Message, $URL, $Importance); + } + + public static function notify_users($UserIDs, $Type, $Message, $URL, $Importance = self::INFO) { + /** + if (!isset($Importance)) { + $Importance = self::INFO; + } + $Type = db_string($Type); + if (!empty($UserIDs)) { + $UserIDs = implode(',', $UserIDs); + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT UserID + FROM users_notifications_settings + WHERE $Type != 0 + AND UserID IN ($UserIDs)"); + $UserIDs = array(); + while (list($ID) = G::$DB->next_record()) { + $UserIDs[] = $ID; + } + G::$DB->set_query_id($QueryID); + foreach ($UserIDs as $UserID) { + $OneReads = G::$Cache->get_value("notifications_one_reads_$UserID"); + if (!$OneReads) { + $OneReads = array(); + } + array_unshift($OneReads, $this->create_notification($OneReads, "oneread_" . uniqid(), null, $Message, $URL, $Importance)); + $OneReads = array_filter($OneReads); + G::$Cache->cache_value("notifications_one_reads_$UserID", $OneReads, 0); + } + } + **/ + } + + public static function get_notification_enabled_users($Type, $UserID) { + $Type = db_string($Type); + $UserWhere = ''; + if (isset($UserID)) { + $UserID = (int)$UserID; + $UserWhere = " AND UserID = '$UserID'"; + } + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT UserID + FROM users_notifications_settings + WHERE $Type != 0 + $UserWhere"); + $IDs = array(); + while (list($ID) = G::$DB->next_record()) { + $IDs[] = $ID; + } + G::$DB->set_query_id($QueryID); + return $IDs; + } + + public function load_one_reads() { + $OneReads = G::$Cache->get_value('notifications_one_reads_' . G::$LoggedUser['ID']); + if (is_array($OneReads)) { + $this->Notifications = $this->Notifications + $OneReads; + } + } + + public static function clear_one_read($ID) { + $OneReads = G::$Cache->get_value('notifications_one_reads_' . G::$LoggedUser['ID']); + if ($OneReads) { + unset($OneReads[$ID]); + if (count($OneReads) > 0) { + G::$Cache->cache_value('notifications_one_reads_' . G::$LoggedUser['ID'], $OneReads, 0); + } else { + G::$Cache->delete_value('notifications_one_reads_' . G::$LoggedUser['ID']); + } + } + + } + + public function load_global_notification() { + $GlobalNotification = G::$Cache->get_value('global_notification'); + if ($GlobalNotification) { + $Read = G::$Cache->get_value('user_read_global_' . G::$LoggedUser['ID']); + if (!$Read) { + $this->create_notification(self::GLOBALNOTICE, 0, $GlobalNotification['Message'], $GlobalNotification['URL'], $GlobalNotification['Importance']); + } + } + } + + public static function get_global_notification() { + return G::$Cache->get_value('global_notification'); + } + + public static function set_global_notification($Message, $URL, $Importance, $Expiration) { + if (empty($Message) || empty($Expiration)) { + error('Error setting notification'); + } + G::$Cache->cache_value('global_notification', array("Message" => $Message, "URL" => $URL, "Importance" => $Importance, "Expiration" => $Expiration), $Expiration); + } + + public static function delete_global_notification() { + G::$Cache->delete_value('global_notification'); + } + + public static function clear_global_notification() { + $GlobalNotification = G::$Cache->get_value('global_notification'); + if ($GlobalNotification) { + // This is some trickery + // since we can't know which users have the read cache key set + // we set the expiration time of their cache key to that of the length of the notification + // this gaurantees that their cache key will expire after the notification expires + G::$Cache->cache_value('user_read_global_' . G::$LoggedUser['ID'], true, $GlobalNotification['Expiration']); + } + } + + public function load_news() { + $MyNews = G::$LoggedUser['LastReadNews']; + $CurrentNews = G::$Cache->get_value('news_latest_id'); + $Title = G::$Cache->get_value('news_latest_title'); + if ($CurrentNews === false || $Title === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(' + SELECT ID, Title + FROM news + ORDER BY Time DESC + LIMIT 1'); + if (G::$DB->has_results()) { + list($CurrentNews, $Title) = G::$DB->next_record(); + } else { + $CurrentNews = -1; + } + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value('news_latest_id', $CurrentNews, 0); + G::$Cache->cache_value('news_latest_title', $Title, 0); + } + if ($MyNews < $CurrentNews) { + $this->create_notification(self::NEWS, $CurrentNews, "Announcement: $Title", "index.php#news$CurrentNews", self::IMPORTANT); + } + } + + public function load_blog() { + $MyBlog = G::$LoggedUser['LastReadBlog']; + $CurrentBlog = G::$Cache->get_value('blog_latest_id'); + $Title = G::$Cache->get_value('blog_latest_title'); + if ($CurrentBlog === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(' + SELECT ID, Title + FROM blog + WHERE Important = 1 + ORDER BY Time DESC + LIMIT 1'); + if (G::$DB->has_results()) { + list($CurrentBlog, $Title) = G::$DB->next_record(); + } else { + $CurrentBlog = -1; + } + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value('blog_latest_id', $CurrentBlog, 0); + G::$Cache->cache_value('blog_latest_title', $Title, 0); + } + if ($MyBlog < $CurrentBlog) { + $this->create_notification(self::BLOG, $CurrentBlog, "Blog: $Title", "blog.php#blog$CurrentBlog", self::IMPORTANT); + } + } + + public function load_staff_blog() { + if (check_perms('users_mod')) { + global $SBlogReadTime, $LatestSBlogTime; + if (!$SBlogReadTime && ($SBlogReadTime = G::$Cache->get_value('staff_blog_read_' . G::$LoggedUser['ID'])) === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT Time + FROM staff_blog_visits + WHERE UserID = " . G::$LoggedUser['ID']); + if (list($SBlogReadTime) = G::$DB->next_record()) { + $SBlogReadTime = strtotime($SBlogReadTime); + } else { + $SBlogReadTime = 0; + } + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value('staff_blog_read_' . G::$LoggedUser['ID'], $SBlogReadTime, 1209600); + } + if (!$LatestSBlogTime && ($LatestSBlogTime = G::$Cache->get_value('staff_blog_latest_time')) === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(' + SELECT MAX(Time) + FROM staff_blog'); + if (list($LatestSBlogTime) = G::$DB->next_record()) { + $LatestSBlogTime = strtotime($LatestSBlogTime); + } else { + $LatestSBlogTime = 0; + } + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value('staff_blog_latest_time', $LatestSBlogTime, 1209600); + } + if ($SBlogReadTime < $LatestSBlogTime) { + $this->create_notification(self::STAFFBLOG, 0, 'New Staff Blog Post!', 'staffblog.php', self::IMPORTANT); + } + } + } + + public function load_staff_pms() { + $NewStaffPMs = G::$Cache->get_value('staff_pm_new_' . G::$LoggedUser['ID']); + if ($NewStaffPMs === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT COUNT(ID) + FROM staff_pm_conversations + WHERE UserID = '" . G::$LoggedUser['ID'] . "' + AND Unread = '1'"); + list($NewStaffPMs) = G::$DB->next_record(); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value('staff_pm_new_' . G::$LoggedUser['ID'], $NewStaffPMs, 0); + } + + if ($NewStaffPMs > 0) { + $Title = 'You have ' . ($NewStaffPMs == 1 ? 'a' : $NewStaffPMs) . ' new Staff PM' . ($NewStaffPMs > 1 ? 's' : ''); + $this->create_notification(self::STAFFPM, 0, $Title, 'staffpm.php', self::INFO); + } + } + + public function load_inbox() { + $NewMessages = G::$Cache->get_value('inbox_new_' . G::$LoggedUser['ID']); + if ($NewMessages === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT COUNT(UnRead) + FROM pm_conversations_users + WHERE UserID = '" . G::$LoggedUser['ID'] . "' + AND UnRead = '1' + AND InInbox = '1'"); + list($NewMessages) = G::$DB->next_record(); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value('inbox_new_' . G::$LoggedUser['ID'], $NewMessages, 0); + } + + if ($NewMessages > 0) { + $Title = 'You have ' . ($NewMessages == 1 ? 'a' : $NewMessages) . ' new message' . ($NewMessages > 1 ? 's' : ''); + $this->create_notification(self::INBOX, 0, $Title, Inbox::get_inbox_link(), self::INFO); + } + } + + public function load_torrent_notifications() { + if (check_perms('site_torrents_notify')) { + $NewNotifications = G::$Cache->get_value('notifications_new_' . G::$LoggedUser['ID']); + if ($NewNotifications === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT COUNT(UserID) + FROM users_notify_torrents + WHERE UserID = ' " . G::$LoggedUser['ID'] . "' + AND UnRead = '1'"); + list($NewNotifications) = G::$DB->next_record(); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value('notifications_new_' . G::$LoggedUser['ID'], $NewNotifications, 0); + } + } + if ($NewNotifications > 0) { + $Title = 'You have ' . ($NewNotifications == 1 ? 'a' : $NewNotifications) . ' new torrent notification' . ($NewNotifications > 1 ? 's' : ''); + $this->create_notification(self::TORRENTS, 0, $Title, 'torrents.php?action=notify', self::INFO); + } + } + + public function load_collage_subscriptions() { + if (check_perms('site_collages_subscribe')) { + $NewCollages = G::$Cache->get_value('collage_subs_user_new_' . G::$LoggedUser['ID']); + if ($NewCollages === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT COUNT(DISTINCT s.CollageID) + FROM users_collage_subs AS s + JOIN collages AS c ON s.CollageID = c.ID + JOIN collages_torrents AS ct ON ct.CollageID = c.ID + WHERE s.UserID = " . G::$LoggedUser['ID'] . " + AND ct.AddedOn > s.LastVisit + AND c.Deleted = '0'"); + list($NewCollages) = G::$DB->next_record(); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value('collage_subs_user_new_' . G::$LoggedUser['ID'], $NewCollages, 0); + } + if ($NewCollages > 0) { + $Title = 'You have ' . ($NewCollages == 1 ? 'a' : $NewCollages) . ' new collage update' . ($NewCollages > 1 ? 's' : ''); + $this->create_notification(self::COLLAGES, 0, $Title, 'userhistory.php?action=subscribed_collages', self::INFO); + } + } + } + + public function load_quote_notifications() { + if (isset(G::$LoggedUser['NotifyOnQuote']) && G::$LoggedUser['NotifyOnQuote']) { + $QuoteNotificationsCount = Subscriptions::has_new_quote_notifications(); + if ($QuoteNotificationsCount > 0) { + $Title = 'New quote' . ($QuoteNotificationsCount > 1 ? 's' : ''); + $this->create_notification(self::QUOTES, 0, $Title, 'userhistory.php?action=quote_notifications', self::INFO); + } + } + } + + public function load_subscriptions() { + $SubscriptionsCount = Subscriptions::has_new_subscriptions(); + if ($SubscriptionsCount > 0) { + $Title = 'New subscription' . ($SubscriptionsCount > 1 ? 's' : ''); + $this->create_notification(self::SUBSCRIPTIONS, 0, $Title, 'userhistory.php?action=subscriptions', self::INFO); + } + } + + public static function clear_news($News = null) { + $QueryID = G::$DB->get_query_id(); + if (!$News) { + if (!$News = G::$Cache->get_value('news')) { + G::$DB->query(' + SELECT + ID, + Title, + Body, + Time + FROM news + ORDER BY Time DESC + LIMIT 1'); + $News = G::$DB->to_array(false, MYSQLI_NUM, false); + G::$Cache->cache_value('news_latest_id', $News[0][0], 0); + } + } + + if (G::$LoggedUser['LastReadNews'] != $News[0][0]) { + G::$Cache->begin_transaction('user_info_heavy_' . G::$LoggedUser['ID']); + G::$Cache->update_row(false, array('LastReadNews' => $News[0][0])); + G::$Cache->commit_transaction(0); + G::$DB->query(" + UPDATE users_info + SET LastReadNews = '".$News[0][0]."' + WHERE UserID = " . G::$LoggedUser['ID']); + G::$LoggedUser['LastReadNews'] = $News[0][0]; + } + G::$DB->set_query_id($QueryID); + } + + public static function clear_blog($Blog = null) { + $QueryID = G::$DB->get_query_id(); + if (!$Blog) { + if (!$Blog = G::$Cache->get_value('blog')) { + G::$DB->query(" + SELECT + b.ID, + um.Username, + b.UserID, + b.Title, + b.Body, + b.Time, + b.ThreadID + FROM blog AS b + LEFT JOIN users_main AS um ON b.UserID = um.ID + ORDER BY Time DESC + LIMIT 1"); + $Blog = G::$DB->to_array(); + } + } + if (G::$LoggedUser['LastReadBlog'] < $Blog[0][0]) { + G::$Cache->begin_transaction('user_info_heavy_' . G::$LoggedUser['ID']); + G::$Cache->update_row(false, array('LastReadBlog' => $Blog[0][0])); + G::$Cache->commit_transaction(0); + G::$DB->query(" + UPDATE users_info + SET LastReadBlog = '". $Blog[0][0]."' + WHERE UserID = " . G::$LoggedUser['ID']); + G::$LoggedUser['LastReadBlog'] = $Blog[0][0]; + } + G::$DB->set_query_id($QueryID); + } + + public static function clear_staff_pms() { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT ID + FROM staff_pm_conversations + WHERE Unread = true + AND UserID = " . G::$LoggedUser['ID']); + $IDs = array(); + while (list($ID) = G::$DB->next_record()) { + $IDs[] = $ID; + } + $IDs = implode(',', $IDs); + if (!empty($IDs)) { + G::$DB->query(" + UPDATE staff_pm_conversations + SET Unread = false + WHERE ID IN ($IDs)"); + } + G::$Cache->delete_value('staff_pm_new_' . G::$LoggedUser['ID']); + G::$DB->set_query_id($QueryID); + } + + public static function clear_inbox() { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT ConvID + FROM pm_conversations_users + WHERE Unread = '1' + AND UserID = " . G::$LoggedUser['ID']); + $IDs = array(); + while (list($ID) = G::$DB->next_record()) { + $IDs[] = $ID; + } + $IDs = implode(',', $IDs); + if (!empty($IDs)) { + G::$DB->query(" + UPDATE pm_conversations_users + SET Unread = '0' + WHERE ConvID IN ($IDs) + AND UserID = " . G::$LoggedUser['ID']); + } + G::$Cache->delete_value('inbox_new_' . G::$LoggedUser['ID']); + G::$DB->set_query_id($QueryID); + } + + public static function clear_torrents() { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT TorrentID + FROM users_notify_torrents + WHERE UserID = ' " . G::$LoggedUser['ID'] . "' + AND UnRead = '1'"); + $IDs = array(); + while (list($ID) = G::$DB->next_record()) { + $IDs[] = $ID; + } + $IDs = implode(',', $IDs); + if (!empty($IDs)) { + G::$DB->query(" + UPDATE users_notify_torrents + SET Unread = '0' + WHERE TorrentID IN ($IDs) + AND UserID = " . G::$LoggedUser['ID']); + } + G::$Cache->delete_value('notifications_new_' . G::$LoggedUser['ID']); + G::$DB->set_query_id($QueryID); + } + + public static function clear_collages() { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + UPDATE users_collage_subs + SET LastVisit = NOW() + WHERE UserID = " . G::$LoggedUser['ID']); + G::$Cache->delete_value('collage_subs_user_new_' . G::$LoggedUser['ID']); + G::$DB->set_query_id($QueryID); + } + + public static function clear_quotes() { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + UPDATE users_notify_quoted + SET UnRead = '0' + WHERE UserID = " . G::$LoggedUser['ID']); + G::$Cache->delete_value('notify_quoted_' . G::$LoggedUser['ID']); + G::$DB->set_query_id($QueryID); + } + + public static function clear_subscriptions() { + $QueryID = G::$DB->get_query_id(); + if (($UserSubscriptions = G::$Cache->get_value('subscriptions_user_' . G::$LoggedUser['ID'])) === false) { + G::$DB->query(" + SELECT TopicID + FROM users_subscriptions + WHERE UserID = " . G::$LoggedUser['ID']); + if ($UserSubscriptions = G::$DB->collect(0)) { + G::$Cache->cache_value('subscriptions_user_' . G::$LoggedUser['ID'], $UserSubscriptions, 0); + } + } + if (!empty($UserSubscriptions)) { + G::$DB->query(" + INSERT INTO forums_last_read_topics (UserID, TopicID, PostID) + SELECT '" . G::$LoggedUser['ID'] . "', ID, LastPostID + FROM forums_topics + WHERE ID IN (".implode(',', $UserSubscriptions).') + ON DUPLICATE KEY UPDATE + PostID = LastPostID'); + } + G::$Cache->delete_value('subscriptions_user_new_' . G::$LoggedUser['ID']); + G::$DB->set_query_id($QueryID); + } /* - // TODO: Figure out what these functions are supposed to do and fix them - public static function send_notification($UserID, $ID, $Type, $Message, $URL, $Importance = 'alert', $AutoExpire = false) { - $Notifications = G::$Cache->get_value("user_cache_notifications_$UserID"); - if (empty($Notifications)) { - $Notifications = array(); - } - array_unshift($Notifications, $this->create_notification($Type, $ID, $Message, $URL, $Importance, $AutoExpire)); - G::$Cache->cache_value("user_cache_notifications_$UserID", $Notifications, 0); - } - - public static function clear_notification($UserID, $Index) { - $Notifications = G::$Cache->get_value("user_cache_notifications_$UserID"); - if (count($Notifications)) { - unset($Notifications[$Index]); - $Notifications = array_values($Notifications); - G::$Cache->cache_value("user_cache_notifications_$UserID", $Notifications, 0); - } - } + // TODO: Figure out what these functions are supposed to do and fix them + public static function send_notification($UserID, $ID, $Type, $Message, $URL, $Importance = 'alert', $AutoExpire = false) { + $Notifications = G::$Cache->get_value("user_cache_notifications_$UserID"); + if (empty($Notifications)) { + $Notifications = array(); + } + array_unshift($Notifications, $this->create_notification($Type, $ID, $Message, $URL, $Importance, $AutoExpire)); + G::$Cache->cache_value("user_cache_notifications_$UserID", $Notifications, 0); + } + + public static function clear_notification($UserID, $Index) { + $Notifications = G::$Cache->get_value("user_cache_notifications_$UserID"); + if (count($Notifications)) { + unset($Notifications[$Index]); + $Notifications = array_values($Notifications); + G::$Cache->cache_value("user_cache_notifications_$UserID", $Notifications, 0); + } + } */ - public static function get_settings($UserID) { - $Results = G::$Cache->get_value("users_notifications_settings_$UserID"); - if (!$Results) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT * - FROM users_notifications_settings AS n - LEFT JOIN users_push_notifications AS p - ON p.UserID = n.UserID - WHERE n.UserID = '$UserID'"); - $Results = G::$DB->next_record(MYSQLI_ASSOC, false); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value("users_notifications_settings_$UserID", $Results, 0); - } - return $Results; - } - - public static function save_settings($UserID, $Settings=null) { - if (!is_array($Settings)) { - // A little cheat technique, gets all keys in the $_POST array starting with 'notifications_' - $Settings = array_intersect_key($_POST, array_flip(preg_grep('/^notifications_/', array_keys($_POST)))); - } - $Update = array(); - foreach (self::$Types as $Type) { - $Popup = array_key_exists("notifications_{$Type}_popup", $Settings); - $Traditional = array_key_exists("notifications_{$Type}_traditional", $Settings); - $Push = array_key_exists("notifications_{$Type}_push", $Settings); - $Result = self::OPT_DISABLED; - if ($Popup) { - $Result = $Push ? self::OPT_POPUP_PUSH : self::OPT_POPUP; - } elseif ($Traditional) { - $Result = $Push ? self::OPT_TRADITIONAL_PUSH : self::OPT_TRADITIONAL; - } elseif ($Push) { - $Result = self::OPT_PUSH; - } - $Update[] = "$Type = $Result"; - } - $Update = implode(',', $Update); - - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - UPDATE users_notifications_settings - SET $Update - WHERE UserID = '$UserID'"); - - $PushService = (int) $_POST['pushservice']; - $PushOptionsArray = array("PushKey" => $_POST['pushkey']); - if ($PushService === 6) { //pushbullet - $PushOptionsArray['PushDevice'] = $_POST['pushdevice']; - } - $PushOptions = db_string(serialize($PushOptionsArray)); - - if ($PushService != 0) { - G::$DB->query(" - INSERT INTO users_push_notifications - (UserID, PushService, PushOptions) - VALUES - ('$UserID', '$PushService', '$PushOptions') - ON DUPLICATE KEY UPDATE - PushService = '$PushService', - PushOptions = '$PushOptions'"); - } else { - G::$DB->query("UPDATE users_push_notifications SET PushService = 0 WHERE UserID = '$UserID'"); - } - - G::$DB->set_query_id($QueryID); - G::$Cache->delete_value("users_notifications_settings_$UserID"); - } - - public function is_traditional($Type) { - return $this->Settings[$Type] == self::OPT_TRADITIONAL || $this->Settings[$Type] == self::OPT_TRADITIONAL_PUSH; - } - - public function is_skipped($Type) { - return isset($this->Skipped[$Type]); - } - - public function use_noty() { - return in_array(self::OPT_POPUP, $this->Settings) || in_array(self::OPT_POPUP_PUSH, $this->Settings); - } - - /** - * Send a push notification to a user - * - * @param array $UserIDs integer or array of integers of UserIDs to push - * @param string $Title the title to be displayed in the push - * @param string $Body the body of the push - * @param string $URL url for the push notification to contain - * @param string $Type what sort of push is it? PM, Rippy, News, etc - */ - public static function send_push($UserIDs, $Title, $Body, $URL = '', $Type = self::GLOBALNOTICE) { - if (!is_array($UserIDs)) { - $UserIDs = array($UserIDs); - } - foreach($UserIDs as $UserID) { - $UserID = (int) $UserID; - $QueryID = G::$DB->get_query_id(); - $SQL = " - SELECT - p.PushService, p.PushOptions - FROM users_notifications_settings AS n - JOIN users_push_notifications AS p ON n.UserID = p.UserID - WHERE n.UserID = '$UserID' - AND p.PushService != 0"; - if ($Type != self::GLOBALNOTICE) { - $SQL .= " AND n.$Type IN (" . self::OPT_PUSH . "," . self::OPT_POPUP_PUSH . "," . self::OPT_TRADITIONAL_PUSH . ")"; - } - G::$DB->query($SQL); - - if (G::$DB->has_results()) { - list($PushService, $PushOptions) = G::$DB->next_record(MYSQLI_NUM, false); - $PushOptions = unserialize($PushOptions); - switch ($PushService) { - case '1': - $Service = "NMA"; - break; - case '2': - $Service = "Prowl"; - break; - // Case 3 is missing because notifo is dead. - case '4': - $Service = "Toasty"; - break; - case '5': - $Service = "Pushover"; - break; - case '6': - $Service = "PushBullet"; - break; - default: - break; - } - if (!empty($Service) && !empty($PushOptions['PushKey'])) { - $Options = array("service" => strtolower($Service), - "user" => array("key" => $PushOptions['PushKey']), - "message" => array("title" => $Title, "body" => $Body, "url" => $URL)); - - if ($Service === 'PushBullet') { - $Options["user"]["device"] = $PushOptions['PushDevice']; - - } - - $JSON = json_encode($Options); - G::$DB->query(" - INSERT INTO push_notifications_usage - (PushService, TimesUsed) - VALUES - ('$Service', 1) - ON DUPLICATE KEY UPDATE - TimesUsed = TimesUsed + 1"); - - $PushServerSocket = fsockopen("127.0.0.1", 6789); - fwrite($PushServerSocket, $JSON); - fclose($PushServerSocket); - } - } - G::$DB->set_query_id($QueryID); - } - } - - /** - * Gets users who have push notifications enabled - * - */ - public static function get_push_enabled_users() { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT UserID - FROM users_push_notifications - WHERE PushService != 0 - AND UserID != '" . G::$LoggedUser['ID']. "'"); - $PushUsers = G::$DB->collect("UserID"); - G::$DB->set_query_id($QueryID); - return $PushUsers; - } + public static function get_settings($UserID) { + $Results = G::$Cache->get_value("users_notifications_settings_$UserID"); + if (!$Results) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT * + FROM users_notifications_settings AS n + LEFT JOIN users_push_notifications AS p + ON p.UserID = n.UserID + WHERE n.UserID = '$UserID'"); + $Results = G::$DB->next_record(MYSQLI_ASSOC, false); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value("users_notifications_settings_$UserID", $Results, 0); + } + return $Results; + } + + public static function save_settings($UserID, $Settings=null) { + if (!is_array($Settings)) { + // A little cheat technique, gets all keys in the $_POST array starting with 'notifications_' + $Settings = array_intersect_key($_POST, array_flip(preg_grep('/^notifications_/', array_keys($_POST)))); + } + $Update = array(); + foreach (self::$Types as $Type) { + $Popup = array_key_exists("notifications_{$Type}_popup", $Settings); + $Traditional = array_key_exists("notifications_{$Type}_traditional", $Settings); + $Push = array_key_exists("notifications_{$Type}_push", $Settings); + $Result = self::OPT_DISABLED; + if ($Popup) { + $Result = $Push ? self::OPT_POPUP_PUSH : self::OPT_POPUP; + } elseif ($Traditional) { + $Result = $Push ? self::OPT_TRADITIONAL_PUSH : self::OPT_TRADITIONAL; + } elseif ($Push) { + $Result = self::OPT_PUSH; + } + $Update[] = "$Type = $Result"; + } + $Update = implode(',', $Update); + + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + UPDATE users_notifications_settings + SET $Update + WHERE UserID = '$UserID'"); + + $PushService = (int) $_POST['pushservice']; + $PushOptionsArray = array("PushKey" => $_POST['pushkey']); + if ($PushService === 6) { //pushbullet + $PushOptionsArray['PushDevice'] = $_POST['pushdevice']; + } + $PushOptions = db_string(serialize($PushOptionsArray)); + + if ($PushService != 0) { + G::$DB->query(" + INSERT INTO users_push_notifications + (UserID, PushService, PushOptions) + VALUES + ('$UserID', '$PushService', '$PushOptions') + ON DUPLICATE KEY UPDATE + PushService = '$PushService', + PushOptions = '$PushOptions'"); + } else { + G::$DB->query("UPDATE users_push_notifications SET PushService = 0 WHERE UserID = '$UserID'"); + } + + G::$DB->set_query_id($QueryID); + G::$Cache->delete_value("users_notifications_settings_$UserID"); + } + + public function is_traditional($Type) { + return $this->Settings[$Type] == self::OPT_TRADITIONAL || $this->Settings[$Type] == self::OPT_TRADITIONAL_PUSH; + } + + public function is_skipped($Type) { + return isset($this->Skipped[$Type]); + } + + public function use_noty() { + return in_array(self::OPT_POPUP, $this->Settings) || in_array(self::OPT_POPUP_PUSH, $this->Settings); + } + + /** + * Send a push notification to a user + * + * @param array $UserIDs integer or array of integers of UserIDs to push + * @param string $Title the title to be displayed in the push + * @param string $Body the body of the push + * @param string $URL url for the push notification to contain + * @param string $Type what sort of push is it? PM, Rippy, News, etc + */ + public static function send_push($UserIDs, $Title, $Body, $URL = '', $Type = self::GLOBALNOTICE) { + if (!is_array($UserIDs)) { + $UserIDs = array($UserIDs); + } + foreach($UserIDs as $UserID) { + $UserID = (int) $UserID; + $QueryID = G::$DB->get_query_id(); + $SQL = " + SELECT + p.PushService, p.PushOptions + FROM users_notifications_settings AS n + JOIN users_push_notifications AS p ON n.UserID = p.UserID + WHERE n.UserID = '$UserID' + AND p.PushService != 0"; + if ($Type != self::GLOBALNOTICE) { + $SQL .= " AND n.$Type IN (" . self::OPT_PUSH . "," . self::OPT_POPUP_PUSH . "," . self::OPT_TRADITIONAL_PUSH . ")"; + } + G::$DB->query($SQL); + + if (G::$DB->has_results()) { + list($PushService, $PushOptions) = G::$DB->next_record(MYSQLI_NUM, false); + $PushOptions = unserialize($PushOptions); + switch ($PushService) { + case '1': + $Service = "NMA"; + break; + case '2': + $Service = "Prowl"; + break; + // Case 3 is missing because notifo is dead. + case '4': + $Service = "Toasty"; + break; + case '5': + $Service = "Pushover"; + break; + case '6': + $Service = "PushBullet"; + break; + default: + break; + } + if (!empty($Service) && !empty($PushOptions['PushKey'])) { + $Options = array("service" => strtolower($Service), + "user" => array("key" => $PushOptions['PushKey']), + "message" => array("title" => $Title, "body" => $Body, "url" => $URL)); + + if ($Service === 'PushBullet') { + $Options["user"]["device"] = $PushOptions['PushDevice']; + + } + + $JSON = json_encode($Options); + G::$DB->query(" + INSERT INTO push_notifications_usage + (PushService, TimesUsed) + VALUES + ('$Service', 1) + ON DUPLICATE KEY UPDATE + TimesUsed = TimesUsed + 1"); + + $PushServerSocket = fsockopen("127.0.0.1", 6789); + fwrite($PushServerSocket, $JSON); + fclose($PushServerSocket); + } + } + G::$DB->set_query_id($QueryID); + } + } + + /** + * Gets users who have push notifications enabled + * + */ + public static function get_push_enabled_users() { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT UserID + FROM users_push_notifications + WHERE PushService != 0 + AND UserID != '" . G::$LoggedUser['ID']. "'"); + $PushUsers = G::$DB->collect("UserID"); + G::$DB->set_query_id($QueryID); + return $PushUsers; + } } diff --git a/classes/notificationsmanagerview.class.php b/classes/notificationsmanagerview.class.php index 9317af438..c6e5bce88 100644 --- a/classes/notificationsmanagerview.class.php +++ b/classes/notificationsmanagerview.class.php @@ -1,156 +1,158 @@ - - -?v=" type="text/javascript"> + - - Push notifications - - - - - - + + Push notifications + + + + + + - - - News announcements - - - - - - - - Blog announcements - - - - - - - - Inbox messages - - - - - - - - Staff messages - - - - - - - - Thread subscriptions - - - - - - - - Quote notifications - - - - - - - - - Torrent notifications - - - - - - + + + News announcements + + + + + + + + Blog announcements + + + + + + + + Inbox messages + + + + + + + + Staff messages + + + + + + + + Thread subscriptions + + + + + + + + Quote notifications + + + + + + + + + Torrent notifications + + + + + + - - - Collage subscriptions - - - - - - + + Collage subscriptions + + + + + + - - - - - - + /> + Pop-up + + + + + +$Contents[message]"; - } + public static function format_traditional($Contents) { + return "$Contents[message]"; + } } diff --git a/classes/paranoia.class.php b/classes/paranoia.class.php index ed575583e..51370a2f2 100644 --- a/classes/paranoia.class.php +++ b/classes/paranoia.class.php @@ -1,4 +1,4 @@ -= $OverrideClass; - return ($PermissionName === null || - (isset(G::$LoggedUser['Permissions'][$PermissionName]) && G::$LoggedUser['Permissions'][$PermissionName])) - && (G::$LoggedUser['Class'] >= $MinClass - || G::$LoggedUser['EffectiveClass'] >= $MinClass - || $Override); - } + $Override = G::$LoggedUser['EffectiveClass'] >= $OverrideClass; + return ($PermissionName === null || + (isset(G::$LoggedUser['Permissions'][$PermissionName]) && G::$LoggedUser['Permissions'][$PermissionName])) + && (G::$LoggedUser['Class'] >= $MinClass + || G::$LoggedUser['EffectiveClass'] >= $MinClass + || $Override); + } - /** - * Gets the permissions associated with a certain permissionid - * - * @param int $PermissionID the kind of permissions to fetch - * @return array permissions - */ - public static function get_permissions($PermissionID) { - $Permission = G::$Cache->get_value("perm_$PermissionID"); - if (empty($Permission)) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT Level AS Class, `Values` AS Permissions, Secondary, PermittedForums - FROM permissions - WHERE ID = '$PermissionID'"); - $Permission = G::$DB->next_record(MYSQLI_ASSOC, array('Permissions')); - G::$DB->set_query_id($QueryID); - $Permission['Permissions'] = unserialize($Permission['Permissions']); - G::$Cache->cache_value("perm_$PermissionID", $Permission, 2592000); - } - return $Permission; - } + /** + * Gets the permissions associated with a certain permissionid + * + * @param int $PermissionID the kind of permissions to fetch + * @return array permissions + */ + public static function get_permissions($PermissionID) { + $Permission = G::$Cache->get_value("perm_$PermissionID"); + if (empty($Permission)) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT Level AS Class, `Values` AS Permissions, Secondary, PermittedForums + FROM permissions + WHERE ID = '$PermissionID'"); + $Permission = G::$DB->next_record(MYSQLI_ASSOC, array('Permissions')); + G::$DB->set_query_id($QueryID); + $Permission['Permissions'] = unserialize($Permission['Permissions']); + G::$Cache->cache_value("perm_$PermissionID", $Permission, 2592000); + } + return $Permission; + } - /** - * Get a user's permissions. - * - * @param $UserID - * @param array|false $CustomPermissions - * Pass in the user's custom permissions if you already have them. - * Leave false if you don't have their permissions. The function will fetch them. - * @return array Mapping of PermissionName=>bool/int - */ - public static function get_permissions_for_user($UserID, $CustomPermissions = false) { - $UserInfo = Users::user_info($UserID); + /** + * Get a user's permissions. + * + * @param $UserID + * @param array|false $CustomPermissions + * Pass in the user's custom permissions if you already have them. + * Leave false if you don't have their permissions. The function will fetch them. + * @return array Mapping of PermissionName=>bool/int + */ + public static function get_permissions_for_user($UserID, $CustomPermissions = false) { + $UserInfo = Users::user_info($UserID); - // Fetch custom permissions if they weren't passed in. - if ($CustomPermissions === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(' - SELECT CustomPermissions - FROM users_main - WHERE ID = ' . (int)$UserID); - list($CustomPermissions) = G::$DB->next_record(MYSQLI_NUM, false); - G::$DB->set_query_id($QueryID); - } + // Fetch custom permissions if they weren't passed in. + if ($CustomPermissions === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(' + SELECT CustomPermissions + FROM users_main + WHERE ID = ' . (int)$UserID); + list($CustomPermissions) = G::$DB->next_record(MYSQLI_NUM, false); + G::$DB->set_query_id($QueryID); + } - if (!empty($CustomPermissions) && !is_array($CustomPermissions)) { - $CustomPermissions = unserialize($CustomPermissions); - } + if (!empty($CustomPermissions) && !is_array($CustomPermissions)) { + $CustomPermissions = unserialize($CustomPermissions); + } - $Permissions = self::get_permissions($UserInfo['PermissionID']); + $Permissions = self::get_permissions($UserInfo['PermissionID']); - // Manage 'special' inherited permissions - $BonusPerms = array(); - $BonusCollages = 0; - foreach ($UserInfo['ExtraClasses'] as $PermID => $Value) { - $ClassPerms = self::get_permissions($PermID); - $BonusCollages += $ClassPerms['Permissions']['MaxCollages']; - unset($ClassPerms['Permissions']['MaxCollages']); - $BonusPerms = array_merge($BonusPerms, $ClassPerms['Permissions']); - } + // Manage 'special' inherited permissions + $BonusPerms = []; + $BonusCollages = 0; + foreach ($UserInfo['ExtraClasses'] as $PermID => $Value) { + $ClassPerms = self::get_permissions($PermID); + $BonusCollages += $ClassPerms['Permissions']['MaxCollages']; + unset($ClassPerms['Permissions']['MaxCollages']); + $BonusPerms = array_merge($BonusPerms, $ClassPerms['Permissions']); + } - if (empty($CustomPermissions)) { - $CustomPermissions = array(); - } + if (empty($CustomPermissions)) { + $CustomPermissions = []; + } - // This is legacy donor cruft - if ($UserInfo['Donor']) { - $DonorPerms = self::get_permissions(DONOR); - unset($DonorPerms['Permissions']['MaxCollages']); - } else { - $DonorPerms = array('Permissions' => array()); - } - $MaxCollages = $BonusCollages; - if (is_numeric($Permissions['Permissions']['MaxCollages'])) { - $MaxCollages += $Permissions['Permissions']['MaxCollages']; - } - if (isset($CustomPermissions['MaxCollages'])) { - $MaxCollages += $CustomPermissions['MaxCollages']; - unset($CustomPermissions['MaxCollages']); - } - $Permissions['Permissions']['MaxCollages'] = $MaxCollages; - // Combine the permissions - return array_merge( - $Permissions['Permissions'], - $BonusPerms, - $CustomPermissions, - $DonorPerms['Permissions']); - } + // This is legacy donor cruft + if ($UserInfo['Donor']) { + $DonorPerms = self::get_permissions(DONOR); + unset($DonorPerms['Permissions']['MaxCollages']); + } else { + $DonorPerms = array('Permissions' => []); + } + $MaxCollages = $BonusCollages; + if (is_numeric($Permissions['Permissions']['MaxCollages'])) { + $MaxCollages += $Permissions['Permissions']['MaxCollages']; + } + if (isset($CustomPermissions['MaxCollages'])) { + $MaxCollages += $CustomPermissions['MaxCollages']; + unset($CustomPermissions['MaxCollages']); + } + $Permissions['Permissions']['MaxCollages'] = $MaxCollages; + // Combine the permissions + return array_merge( + $Permissions['Permissions'], + $BonusPerms, + $CustomPermissions, + $DonorPerms['Permissions']); + } - public static function is_mod($UserID) { - return self::has_permission($UserID, 'users_mod'); - } + public static function is_mod($UserID) { + return self::has_permission($UserID, 'users_mod'); + } - public static function has_permission($UserID, $privilege) { - $Permissions = self::get_permissions_for_user($UserID); - return isset($Permissions[$privilege]) && $Permissions[$privilege]; - } + public static function has_permission($UserID, $privilege) { + $Permissions = self::get_permissions_for_user($UserID); + return isset($Permissions[$privilege]) && $Permissions[$privilege]; + } } diff --git a/classes/permissions_form.php b/classes/permissions_form.php index 8917e2781..b7a6b2a74 100644 --- a/classes/permissions_form.php +++ b/classes/permissions_form.php @@ -1,285 +1,295 @@ - 'Can leech (Does this work?).', - 'site_upload' => 'Upload torrent access.', - 'site_vote' => 'Request vote access.', - 'site_submit_requests' => 'Request create access.', - 'site_advanced_search' => 'Advanced search access.', - 'site_top10' => 'Top 10 access.', - 'site_advanced_top10' => 'Advanced Top 10 access.', - 'site_album_votes' => 'Voting for favorite torrents.', - 'site_torrents_notify' => 'Notifications access.', - 'site_collages_create' => 'Collage create access.', - 'site_collages_manage' => 'Collage manage access.', - 'site_collages_delete' => 'Collage delete access.', - 'site_collages_subscribe' => 'Collage subscription access.', - 'site_collages_personal' => 'Can have a personal collage.', - 'site_collages_renamepersonal' => 'Can rename own personal collages.', - 'site_make_bookmarks' => 'Bookmarks access.', - 'site_edit_wiki' => 'Wiki edit access.', - 'site_can_invite_always' => 'Can invite past user limit.', - 'site_send_unlimited_invites' => 'Unlimited invites.', - 'site_moderate_requests' => 'Request moderation access.', - 'site_delete_artist' => 'Can delete artists (must be able to delete torrents+requests).', - 'site_moderate_forums' => 'Forum moderation access.', - 'site_admin_forums' => 'Forum administrator access.', - 'site_view_flow' => 'Can view stats and data pools.', - 'site_view_full_log' => 'Can view old log entries.', - 'site_view_torrent_snatchlist' => 'Can view torrent snatch lists.', - 'site_recommend_own' => 'Can recommend own torrents.', - 'site_manage_recommendations' => 'Recommendations management access.', - 'site_delete_tag' => 'Can delete tags.', - 'site_disable_ip_history' => 'Disable IP history.', - 'zip_downloader' => 'Download multiple torrents at once.', - 'site_debug' => 'Developer access.', - 'site_database_specifics' => 'Can view database specifics', - 'site_proxy_images' => 'Image proxy & anti-canary.', - 'site_search_many' => 'Can go past low limit of search results.', - 'site_user_stats' => 'Can view own user stat graphs.', - 'users_edit_usernames' => 'Can edit usernames.', - 'users_edit_ratio' => 'Can edit anyone\'s upload/download amounts.', - 'users_edit_own_ratio' => 'Can edit own upload/download amounts.', - 'users_edit_titles' => 'Can edit titles.', - 'users_edit_avatars' => 'Can edit avatars.', - 'users_edit_invites' => 'Can edit invite numbers and cancel sent invites.', - 'users_edit_watch_hours' => 'Can edit contrib watch hours.', - 'users_edit_reset_keys' => 'Can reset passkey/authkey.', - 'users_edit_profiles' => 'Can edit anyone\'s profile.', - 'users_view_friends' => 'Can view anyone\'s friends.', - 'users_reset_own_keys' => 'Can reset own passkey/authkey.', - 'users_edit_password' => 'Can change passwords.', - 'users_promote_below' => 'Can promote users to below current level.', - 'users_promote_to' => 'Can promote users up to current level.', - 'users_give_donor' => 'Can give donor access.', - 'users_warn' => 'Can warn users.', - 'users_disable_users' => 'Can disable users.', - 'users_disable_posts' => 'Can disable users\' posting privileges.', - 'users_disable_any' => 'Can disable any users\' rights.', - 'users_delete_users' => 'Can delete users.', - 'users_view_invites' => 'Can view who user has invited.', - 'users_view_seedleech' => 'Can view what a user is seeding or leeching.', - 'users_view_uploaded' => 'Can view a user\'s uploads, regardless of privacy level.', - 'users_view_keys' => 'Can view passkeys.', - 'users_view_ips' => 'Can view IP addresses.', - 'users_view_email' => 'Can view email addresses.', - 'users_invite_notes' => 'Can add a staff note when inviting someone.', - 'users_override_paranoia' => 'Can override paranoia.', - 'users_logout' => 'Can log users out (old?).', - 'users_make_invisible' => 'Can make users invisible.', - 'users_mod' => 'Basic moderator tools.', - 'torrents_edit' => 'Can edit any torrent.', - 'torrents_delete' => 'Can delete torrents.', - 'torrents_delete_fast' => 'Can delete more than 3 torrents at a time.', - 'torrents_freeleech' => 'Can make torrents freeleech.', - 'torrents_search_fast' => 'Rapid search (for scripts).', - 'torrents_hide_dnu' => 'Hide the Do Not Upload list by default.', - 'torrents_fix_ghosts' => 'Can fix "ghost" groups on artist pages.', - 'admin_manage_news' => 'Can manage site news.', - 'admin_manage_blog' => 'Can manage the site blog.', - 'admin_manage_polls' => 'Can manage polls.', - 'admin_manage_forums' => 'Can manage forums (add/edit/delete).', - 'admin_manage_fls' => 'Can manage First Line Support (FLS) crew.', - 'admin_manage_user_fls' => 'Can manage user FL tokens.', - 'admin_manage_applicants' => 'Can manage job roles and user applications.', - 'admin_manage_payments' => 'Can manage payments.', - 'admin_manage_navigation' => 'Can manage navigation links.', - 'admin_bp_history' => 'Can view bonus points spent by other users.', - 'admin_reports' => 'Can access reports system.', - 'admin_advanced_user_search' => 'Can access advanced user search.', - 'admin_create_users' => 'Can create users through an administrative form.', - 'admin_donor_log' => 'Can view the donor log.', - 'admin_manage_ipbans' => 'Can manage IP bans.', - 'admin_dnu' => 'Can manage do not upload list.', - 'admin_clear_cache' => 'Can clear cached.', - 'admin_whitelist' => 'Can manage the list of allowed clients.', - 'admin_manage_permissions' => 'Can edit permission classes/user permissions.', - 'admin_schedule' => 'Can run the site schedule.', - 'admin_login_watch' => 'Can manage login watch.', - 'admin_manage_wiki' => 'Can manage wiki access.', - 'admin_update_geoip' => 'Can update geoIP data.', - 'admin_staffpm_stats' => 'Can view Staff PM stats.', - 'site_collages_recover' => 'Can recover \'deleted\' collages.', - 'torrents_add_artist' => 'Can add artists to any group.', - 'edit_unknowns' => 'Can edit unknown release information.', - 'forums_polls_create' => 'Can create polls in the forums.', - 'forums_polls_moderate' => 'Can feature and close polls.', - 'torrents_edit_vanityhouse' => 'Can mark groups as part of Vanity House.', - 'artist_edit_vanityhouse' => 'Can mark artists as part of Vanity House.', - 'site_tag_aliases_read' => 'Can view the list of tag aliases.' + 'site_leech' => 'Can leech (Does this work?).', + 'site_upload' => 'Upload torrent access.', + 'site_vote' => 'Request vote access.', + 'site_submit_requests' => 'Request create access.', + 'site_advanced_search' => 'Advanced search access.', + 'site_top10' => 'Top 10 access.', + 'site_advanced_top10' => 'Advanced Top 10 access.', + 'site_album_votes' => 'Voting for favorite torrents.', + 'site_torrents_notify' => 'Notifications access.', + 'site_collages_create' => 'Collage create access.', + 'site_collages_manage' => 'Collage manage access.', + 'site_collages_delete' => 'Collage delete access.', + 'site_collages_subscribe' => 'Collage subscription access.', + 'site_collages_personal' => 'Can have a personal collage.', + 'site_collages_renamepersonal' => 'Can rename own personal collages.', + 'site_make_bookmarks' => 'Bookmarks access.', + 'site_edit_wiki' => 'Wiki edit access.', + 'site_can_invite_always' => 'Can invite past user limit.', + 'site_send_unlimited_invites' => 'Unlimited invites.', + 'site_moderate_requests' => 'Request moderation access.', + 'site_delete_artist' => 'Can delete artists (must be able to delete torrents+requests).', + 'site_moderate_forums' => 'Forum moderation access.', + 'site_admin_forums' => 'Forum administrator access.', + 'site_view_flow' => 'Can view stats and data pools.', + 'site_view_full_log' => 'Can view old log entries.', + 'site_view_torrent_snatchlist' => 'Can view torrent snatch lists.', + 'site_recommend_own' => 'Can recommend own torrents.', + 'site_manage_recommendations' => 'Recommendations management access.', + 'site_delete_tag' => 'Can delete tags.', + 'site_disable_ip_history' => 'Disable IP history.', + 'zip_downloader' => 'Download multiple torrents at once.', + 'site_debug' => 'Developer access.', + 'site_database_specifics' => 'Can view database specifics', + 'site_proxy_images' => 'Image proxy & anti-canary.', + 'site_search_many' => 'Can go past low limit of search results.', + 'site_user_stats' => 'Can view own user stat graphs.', + 'site_unlimit_ajax' => 'Can bypass ajax api limits.', + 'users_edit_usernames' => 'Can edit usernames.', + 'users_edit_ratio' => 'Can edit anyone\'s upload/download amounts.', + 'users_edit_own_ratio' => 'Can edit own upload/download amounts.', + 'users_edit_titles' => 'Can edit titles.', + 'users_edit_avatars' => 'Can edit avatars.', + 'users_edit_invites' => 'Can edit invite numbers and cancel sent invites.', + 'users_edit_watch_hours' => 'Can edit contrib watch hours.', + 'users_edit_reset_keys' => 'Can reset passkey/authkey.', + 'users_edit_profiles' => 'Can edit anyone\'s profile.', + 'users_view_friends' => 'Can view anyone\'s friends.', + 'users_reset_own_keys' => 'Can reset own passkey/authkey.', + 'users_edit_password' => 'Can change passwords.', + 'users_promote_below' => 'Can promote users to below current level.', + 'users_promote_to' => 'Can promote users up to current level.', + 'users_give_donor' => 'Can give donor access.', + 'users_warn' => 'Can warn users.', + 'users_disable_users' => 'Can disable users.', + 'users_disable_posts' => 'Can disable users\' posting privileges.', + 'users_disable_any' => 'Can disable any users\' rights.', + 'users_delete_users' => 'Can delete users.', + 'users_view_invites' => 'Can view who user has invited.', + 'users_view_seedleech' => 'Can view what a user is seeding or leeching.', + 'users_view_uploaded' => 'Can view a user\'s uploads, regardless of privacy level.', + 'users_view_keys' => 'Can view passkeys.', + 'users_view_ips' => 'Can view IP addresses.', + 'users_view_email' => 'Can view email addresses.', + 'users_invite_notes' => 'Can add a staff note when inviting someone.', + 'users_override_paranoia' => 'Can override paranoia.', + 'users_logout' => 'Can log users out (old?).', + 'users_make_invisible' => 'Can make users invisible.', + 'users_mod' => 'Basic moderator tools.', + 'torrents_edit' => 'Can edit any torrent.', + 'torrents_delete' => 'Can delete torrents.', + 'torrents_delete_fast' => 'Can delete more than 3 torrents at a time.', + 'torrents_freeleech' => 'Can make torrents freeleech.', + 'torrents_search_fast' => 'Rapid search (for scripts).', + 'torrents_hide_dnu' => 'Hide the Do Not Upload list by default.', + 'torrents_fix_ghosts' => 'Can fix "ghost" groups on artist pages.', + 'admin_manage_news' => 'Can manage site news.', + 'admin_manage_blog' => 'Can manage the site blog.', + 'admin_manage_contest' => 'Can manage contests.', + 'admin_manage_polls' => 'Can manage polls.', + 'admin_manage_forums' => 'Can manage forums (add/edit/delete).', + 'admin_manage_fls' => 'Can manage First Line Support (FLS) crew.', + 'admin_manage_user_fls' => 'Can manage user FL tokens.', + 'admin_manage_applicants' => 'Can manage job roles and user applications.', + 'admin_manage_referrals' => 'Can manage referrals.', + 'admin_manage_payments' => 'Can manage payments.', + 'admin_manage_navigation' => 'Can manage navigation links.', + 'admin_view_referrals' => 'Can view referred users.', + 'admin_bp_history' => 'Can view bonus points spent by other users.', + 'admin_reports' => 'Can access reports system.', + 'admin_advanced_user_search' => 'Can access advanced user search.', + 'admin_create_users' => 'Can create users through an administrative form.', + 'admin_donor_log' => 'Can view the donor log.', + 'admin_manage_ipbans' => 'Can manage IP bans.', + 'admin_dnu' => 'Can manage do not upload list.', + 'admin_clear_cache' => 'Can clear cached.', + 'admin_whitelist' => 'Can manage the list of allowed clients.', + 'admin_manage_permissions' => 'Can edit permission classes/user permissions.', + 'admin_recovery' => 'Can manage account recovery.', + 'admin_schedule' => 'Can run the site schedule.', + 'admin_login_watch' => 'Can manage login watch.', + 'admin_manage_wiki' => 'Can manage wiki access.', + 'admin_update_geoip' => 'Can update geoIP data.', + 'admin_staffpm_stats' => 'Can view Staff PM stats.', + 'site_collages_recover' => 'Can recover \'deleted\' collages.', + 'torrents_add_artist' => 'Can add artists to any group.', + 'edit_unknowns' => 'Can edit unknown release information.', + 'forums_polls_create' => 'Can create polls in the forums.', + 'forums_polls_moderate' => 'Can feature and close polls.', + 'torrents_edit_vanityhouse' => 'Can mark groups as part of Vanity House.', + 'artist_edit_vanityhouse' => 'Can mark artists as part of Vanity House.', + 'site_tag_aliases_read' => 'Can view the list of tag aliases.' ); function permissions_form() { ?>
    -
    - - - - - - + +
    Site
    - + + + + + + - -
    Site
    + -
    - -
    - - - - - - +
    Users
    - +
    +
    +
    + + + + + + - -
    Users
    + - *Everything is only applicable to users with the same or lower class level -
    -
    -
    - - - - - - +
    Torrents
    - +
    +
    +
    + + + + + + - -
    Torrents
    + -
    -
    -
    - - - - - - +
    Administrative
    - +
    +
    +
    + + + + + + - -
    Administrative
    + -
    -
    -
    +
    +
    +
    -init(); - $this->listen(); - } - - private function init() { - $this->ListenSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); - socket_set_option($this->ListenSocket, SOL_SOCKET, SO_REUSEADDR, 1); - socket_bind($this->ListenSocket, PUSH_SOCKET_LISTEN_ADDRESS, PUSH_SOCKET_LISTEN_PORT); - socket_listen($this->ListenSocket); - socket_set_nonblock($this->ListenSocket); - echo "\nInitialized\n"; - } - - private function listen() { - echo "\nListening...\n"; - while ( ($this->State) == 1 ) { - if ($this->Listened = @socket_accept($this->ListenSocket)) { - $Data = socket_read($this->Listened, 512); - $this->parse_data($Data); - } - usleep(5000); - } - } - - private function parse_data($Data) { - $JSON = json_decode($Data, true); - $Service = strtolower($JSON['service']); - switch ($Service) { - case 'nma': - $this->push_nma($JSON['user']['key'], $JSON['message']['title'], $JSON['message']['body'], $JSON['message']['url']); - break; - case 'prowl': - $this->push_prowl($JSON['user']['key'], $JSON['message']['title'], $JSON['message']['body'], $JSON['message']['url']); - break; - case 'toasty': - $this->push_toasty($JSON['user']['key'], $JSON['message']['title'], $JSON['message']['body'], $JSON['message']['url']); - break; - case 'pushover': - $this->push_pushover($JSON['user']['key'], $JSON['message']['title'], $JSON['message']['body'], $JSON['message']['url']); - break; - case 'pushbullet': - $this->push_pushbullet( - $JSON['user']['key'], - $JSON['user']['device'], - $JSON['message']['title'], - $JSON['message']['body'], - $JSON['message']['url'] - ); - default: - break; - } - } - - private function push_prowl($Key, $Title, $Message, $URL) { - $API = "https://api.prowlapp.com/publicapi/add"; - $Fields = array( - 'apikey' => urlencode($Key), - 'application' => urlencode(SITE_NAME), - 'event' => urlencode($Title), - 'description' => urlencode($Message) - ); - if (!empty($URL)) { - $Fields['url'] = $URL; - } - $FieldsString = ""; - foreach ($Fields as $key => $value) { - $FieldsString .= $key . '=' . $value . '&'; - } - rtrim($FieldsString, '&'); - - $Curl = curl_init(); - curl_setopt($Curl, CURLOPT_URL, $API); - curl_setopt($Curl, CURLOPT_POST, count($Fields)); - curl_setopt($Curl, CURLOPT_POSTFIELDS, $FieldsString); - curl_exec($Curl); - curl_close($Curl); - echo "Push sent to Prowl"; - } - - private function push_toasty($Key, $Title, $Message, $URL) { - $API = "http://api.supertoasty.com/notify/" . urlencode($Key) . "?"; - if (!empty($URL)) { - $Message = $Message . " " . $URL; - } - $Fields = array( - 'title' => urlencode($Title), - 'text' => urlencode($Message), - 'sender' => urlencode(SITE_NAME) - ); - $FieldsString = ""; - foreach ($Fields as $key => $value) { - $FieldsString .= $key . '=' . $value . '&'; - } - rtrim($FieldsString, '&'); - - $Curl = curl_init(); - curl_setopt($Curl, CURLOPT_URL, $API); - curl_setopt($Curl, CURLOPT_POST, count($Fields)); - curl_setopt($Curl, CURLOPT_POSTFIELDS, $FieldsString); - curl_exec($Curl); - curl_close($Curl); - echo "Push sent to Toasty"; - } - - private function push_nma($Key, $Title, $Message, $URL) { - $NMA = new NMA_API(array( - 'apikey' => $Key - )); - if ($NMA->verify()) { - if ($NMA->notify(SITE_NAME, $Title, $Message, $URL)) { - echo "Push sent to NMA"; - } - } - } - - private function push_pushover($UserKey, $Title, $Message, $URL) { - curl_setopt_array($ch = curl_init(), array( - CURLOPT_URL => "https://api.pushover.net/1/messages.json", - CURLOPT_POSTFIELDS => array( - "token" => PUSHOVER_KEY, - "user" => $UserKey, - "title" => $Title, - "message" => $Message, - "url" => $URL - ) - )); - curl_exec($ch); - curl_close($ch); - echo "Push sent to Pushover"; - } - - /** - * Notify via pushbullet - * - * @param $UserKey User API key - * @param $DeviceID device to push to - * @param $Title Notification title - * @param $Message Notification message - * @param $URL For compatibility with other command. Just gets appended. - */ - private function push_pushbullet($UserKey, $DeviceID, - $Title, $Message, $URL) { - if (!empty($URL)) { - $Message .= ' ' . $URL; - } - - curl_setopt_array($Curl = curl_init(), array( - CURLOPT_URL => 'https://api.pushbullet.com/api/pushes', - CURLOPT_POSTFIELDS => array( - 'type' => 'note', - 'title' => $Title, - 'body' => $Message, - 'device_iden' => $DeviceID - ), - CURLOPT_USERPWD => $UserKey . ':', - CURLOPT_HTTPAUTH => CURLAUTH_BASIC, - CURLOPT_RETURNTRANSFER => True - )); - - $Result = curl_exec($Curl); - echo "Push sent to Pushbullet"; - curl_close($Curl); - - - - } + private $ListenSocket = false; + private $State = 1; + private $Listened = false; + + public function __construct() { + // restore_error_handler(); //Avoid PHP error logging + set_time_limit(0); + $this->init(); + $this->listen(); + } + + private function init() { + $this->ListenSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + socket_set_option($this->ListenSocket, SOL_SOCKET, SO_REUSEADDR, 1); + socket_bind($this->ListenSocket, PUSH_SOCKET_LISTEN_ADDRESS, PUSH_SOCKET_LISTEN_PORT); + socket_listen($this->ListenSocket); + socket_set_nonblock($this->ListenSocket); + echo "\nInitialized\n"; + } + + private function listen() { + echo "\nListening...\n"; + while ( ($this->State) == 1 ) { + if ($this->Listened = @socket_accept($this->ListenSocket)) { + $Data = socket_read($this->Listened, 512); + $this->parse_data($Data); + } + usleep(5000); + } + } + + private function parse_data($Data) { + $JSON = json_decode($Data, true); + $Service = strtolower($JSON['service']); + switch ($Service) { + case 'nma': + $this->push_nma($JSON['user']['key'], $JSON['message']['title'], $JSON['message']['body'], $JSON['message']['url']); + break; + case 'prowl': + $this->push_prowl($JSON['user']['key'], $JSON['message']['title'], $JSON['message']['body'], $JSON['message']['url']); + break; + case 'toasty': + $this->push_toasty($JSON['user']['key'], $JSON['message']['title'], $JSON['message']['body'], $JSON['message']['url']); + break; + case 'pushover': + $this->push_pushover($JSON['user']['key'], $JSON['message']['title'], $JSON['message']['body'], $JSON['message']['url']); + break; + case 'pushbullet': + $this->push_pushbullet( + $JSON['user']['key'], + $JSON['user']['device'], + $JSON['message']['title'], + $JSON['message']['body'], + $JSON['message']['url'] + ); + default: + break; + } + } + + private function push_prowl($Key, $Title, $Message, $URL) { + $API = "https://api.prowlapp.com/publicapi/add"; + $Fields = array( + 'apikey' => urlencode($Key), + 'application' => urlencode(SITE_NAME), + 'event' => urlencode($Title), + 'description' => urlencode($Message) + ); + if (!empty($URL)) { + $Fields['url'] = $URL; + } + $FieldsString = ""; + foreach ($Fields as $key => $value) { + $FieldsString .= $key . '=' . $value . '&'; + } + rtrim($FieldsString, '&'); + + $Curl = curl_init(); + curl_setopt($Curl, CURLOPT_URL, $API); + curl_setopt($Curl, CURLOPT_POST, count($Fields)); + curl_setopt($Curl, CURLOPT_POSTFIELDS, $FieldsString); + curl_exec($Curl); + curl_close($Curl); + echo "Push sent to Prowl"; + } + + private function push_toasty($Key, $Title, $Message, $URL) { + $API = "http://api.supertoasty.com/notify/" . urlencode($Key) . "?"; + if (!empty($URL)) { + $Message = $Message . " " . $URL; + } + $Fields = array( + 'title' => urlencode($Title), + 'text' => urlencode($Message), + 'sender' => urlencode(SITE_NAME) + ); + $FieldsString = ""; + foreach ($Fields as $key => $value) { + $FieldsString .= $key . '=' . $value . '&'; + } + rtrim($FieldsString, '&'); + + $Curl = curl_init(); + curl_setopt($Curl, CURLOPT_URL, $API); + curl_setopt($Curl, CURLOPT_POST, count($Fields)); + curl_setopt($Curl, CURLOPT_POSTFIELDS, $FieldsString); + curl_exec($Curl); + curl_close($Curl); + echo "Push sent to Toasty"; + } + + private function push_nma($Key, $Title, $Message, $URL) { + $NMA = new NMA_API(array( + 'apikey' => $Key + )); + if ($NMA->verify()) { + if ($NMA->notify(SITE_NAME, $Title, $Message, $URL)) { + echo "Push sent to NMA"; + } + } + } + + private function push_pushover($UserKey, $Title, $Message, $URL) { + curl_setopt_array($ch = curl_init(), array( + CURLOPT_URL => "https://api.pushover.net/1/messages.json", + CURLOPT_POSTFIELDS => array( + "token" => PUSHOVER_KEY, + "user" => $UserKey, + "title" => $Title, + "message" => $Message, + "url" => $URL + ) + )); + curl_exec($ch); + curl_close($ch); + echo "Push sent to Pushover"; + } + + /** + * Notify via pushbullet + * + * @param $UserKey User API key + * @param $DeviceID device to push to + * @param $Title Notification title + * @param $Message Notification message + * @param $URL For compatibility with other command. Just gets appended. + */ + private function push_pushbullet($UserKey, $DeviceID, + $Title, $Message, $URL) { + if (!empty($URL)) { + $Message .= ' ' . $URL; + } + + curl_setopt_array($Curl = curl_init(), array( + CURLOPT_URL => 'https://api.pushbullet.com/api/pushes', + CURLOPT_POSTFIELDS => array( + 'type' => 'note', + 'title' => $Title, + 'body' => $Message, + 'device_iden' => $DeviceID + ), + CURLOPT_USERPWD => $UserKey . ':', + CURLOPT_HTTPAUTH => CURLAUTH_BASIC, + CURLOPT_RETURNTRANSFER => True + )); + + $Result = curl_exec($Curl); + echo "Push sent to Pushbullet"; + curl_close($Curl); + + + + } } $PushServer = new PushServer(); diff --git a/classes/qr.class.php b/classes/qr.class.php index e27717210..59140ce2a 100644 --- a/classes/qr.class.php +++ b/classes/qr.class.php @@ -1,4 +1,4 @@ - 0, 'g' => 0, 'b' => 0, 'a' => 0]; - - /** @var array */ - protected $color_background = ['r' => 255, 'g' => 255, 'b' => 255, 'a' => 0]; - - /** @var string */ - protected $label = ''; - - /** @var int */ - protected $label_font_size = 16; - - /** @var string */ - protected $label_font_path = ''; - - /** @var int */ - protected $label_halign = self::LABEL_HALIGN_CENTER; - - /** @var int */ - protected $label_valign = self::LABEL_VALIGN_MIDDLE; - - /** @var resource */ - protected $image = null; - - /** @var int */ - protected $version; - - /** @var int */ - protected $error_correction = self::LEVEL_MEDIUM; - - /** @var array */ - protected $error_corrections_available = [ - self::LEVEL_LOW, - self::LEVEL_MEDIUM, - self::LEVEL_QUARTILE, - self::LEVEL_HIGH, - ]; - - /** @var int */ - protected $module_size; - - /** @var string */ - protected $image_type = self::IMAGE_TYPE_PNG; - - /** @var array */ - protected $image_types_available = [ - self::IMAGE_TYPE_GIF, - self::IMAGE_TYPE_PNG, - self::IMAGE_TYPE_JPEG, - self::IMAGE_TYPE_WBMP, - ]; - - /** @var string */ - protected $image_path; - - /** @var string */ - protected $path; - - /** @var int */ - protected $structure_append_n; - - /** @var int */ - protected $structure_append_m; - - /** @var int */ - protected $structure_append_parity; - - /** @var string */ - protected $structure_append_original_data; - - /** - * Class constructor. - * - * @param string $text - */ - public function __construct($text = '') - { - $this->setPath(__DIR__ . '/../qr/data'); - $this->setImagePath(__DIR__ . '/../qr/image'); - $this->setLabelFontPath(__DIR__ . '/../qr/font/opensans.ttf'); - $this->setText($text); - } - - /** - * Set structure append. - * - * @param int $n - * @param int $m - * @param int $parity Parity - * @param string $original_data Original data - * - * @return QrCode - */ - public function setStructureAppend($n, $m, $parity, $original_data) - { - $this->structure_append_n = $n; - $this->structure_append_m = $m; - $this->structure_append_parity = $parity; - $this->structure_append_original_data = $original_data; - - return $this; - } - - /** - * Set QR Code version. - * - * @param int $version QR Code version - * - * @return QrCode - */ - public function setVersion($version) - { - if ($version <= 40 && $version >= 0) { - $this->version = $version; - } - - return $this; - } - - /** - * Return QR Code version. - * - * @return int - */ - public function getVersion() - { - return $this->version; - } - - /** - * Set QR Code error correction level. - * - * @param mixed $error_correction Error Correction Level - * - * @return QrCode - */ - public function setErrorCorrection($error_correction) - { - if (!is_numeric($error_correction)) { - $level_constant = 'QrCode::LEVEL_' . strtoupper($error_correction); - $error_correction = constant($level_constant); - } - - if (in_array($error_correction, $this->error_corrections_available)) { - $this->error_correction = $error_correction; - } - - return $this; - } - - /** - * Return QR Code error correction level. - * - * @return int - */ - public function getErrorCorrection() - { - return $this->error_correction; - } - - /** - * Set QR Code module size. - * - * @param int $module_size Module size - * - * @return QrCode - */ - public function setModuleSize($module_size) - { - $this->module_size = $module_size; - - return $this; - } - - /** - * Return QR Code module size. - * - * @return int - */ - public function getModuleSize() - { - return $this->module_size; - } - - /** - * Set image type for rendering. - * - * @param string $image_type Image type - * @return QrCode - * @throws Exception - */ - public function setImageType($image_type) - { - if (!in_array($image_type, $this->image_types_available)) { - throw new Exception('QRCode: image type ' . $image_type . ' is invalid.'); - } - - $this->image_type = $image_type; - - return $this; - } - - /** - * Return image type for rendering. - * - * @return string - */ - public function getImageType() - { - return $this->image_type; - } - - /** - * Set image type for rendering via extension. - * - * @param string $extension Image extension - * - * @return QrCode - */ - public function setExtension($extension) - { - if ($extension == 'jpg') { - $this->setImageType('jpeg'); - } else { - $this->setImageType($extension); - } - - return $this; - } - - /** - * Set path to the images directory. - * - * @param string $image_path Image directory - * - * @return QrCode - */ - public function setImagePath($image_path) - { - $this->image_path = $image_path; - - return $this; - } - - /** - * Return path to the images directory. - * - * @return string - */ - public function getImagePath() - { - return $this->image_path; - } - - /** - * Set path to the data directory. - * - * @param string $path Data directory - * - * @return QrCode - */ - public function setPath($path) - { - $this->path = $path; - - return $this; - } - - /** - * Return path to the data directory. - * - * @return string - */ - public function getPath() - { - return $this->path; - } - - /** - * Set logo in QR Code. - * - * @param string $logo Logo Path - * @return QrCode - * @throws Exception - */ - public function setLogo($logo) - { - if (!file_exists($logo)) { - throw new Exception("$logo file does not exist"); - } - - $this->logo = $logo; - - return $this; - } - - /** - * Set logo size in QR Code(default 48). - * - * @param int $logo_size Logo Size - * - * @return QrCode - */ - public function setLogoSize($logo_size) - { - $this->logo_size = $logo_size; - - return $this; - } - - /** - * Set text to hide in QR Code. - * - * @param string $text Text to hide - * - * @return QrCode - */ - public function setText($text) - { - $this->text = $text; - - return $this; - } - - /** - * Return text that will be hid in QR Code. - * - * @return string - */ - public function getText() - { - return $this->text; - } - - /** - * Set QR Code size (width). - * - * @param int $size Width of the QR Code - * - * @return QrCode - */ - public function setSize($size) - { - $this->size = $size; - - return $this; - } - - /** - * Return QR Code size (width). - * - * @return int - */ - public function getSize() - { - return $this->size; - } - - /** - * Set padding around the QR Code. - * - * @param int $padding Padding around QR Code - * - * @return QrCode - */ - public function setPadding($padding) - { - $this->padding = $padding; - - return $this; - } - - /** - * Return padding around the QR Code. - * - * @return int - */ - public function getPadding() - { - return $this->padding; - } - - /** - * Set draw required four-module wide margin. - * - * @param bool $draw_quiet_zone State of required four-module wide margin drawing - * - * @return QrCode - */ - public function setDrawQuietZone($draw_quiet_zone) - { - $this->draw_quiet_zone = $draw_quiet_zone; - - return $this; - } - - /** - * Return draw required four-module wide margin. - * - * @return bool - */ - public function getDrawQuietZone() - { - return $this->draw_quiet_zone; - } - - /** - * Set draw border around QR Code. - * - * @param bool $draw_border State of border drawing - * - * @return QrCode - */ - public function setDrawBorder($draw_border) - { - $this->draw_border = $draw_border; - - return $this; - } - - /** - * Return draw border around QR Code. - * - * @return bool - */ - public function getDrawBorder() - { - return $this->draw_border; - } - - /** - * Set QR Code label (text). - * - * @param int|string $label Label to print under QR code - * - * @return QrCode - */ - public function setLabel($label) - { - $this->label = $label; - - return $this; - } - - /** - * Return QR Code label (text). - * - * @return string - */ - public function getLabel() - { - return $this->label; - } - - /** - * Set QR Code label font size. - * - * @param int $label_font_size Font size of the QR code label - * - * @return QrCode - */ - public function setLabelFontSize($label_font_size) - { - $this->label_font_size = $label_font_size; - - return $this; - } - - /** - * Return QR Code label font size. - * - * @return int - */ - public function getLabelFontSize() - { - return $this->label_font_size; - } - - /** - * Set QR Code label font path. - * - * @param int $label_font_path Path to the QR Code label's TTF font file - * - * @return QrCode - */ - public function setLabelFontPath($label_font_path) - { - $this->label_font_path = $label_font_path; - - return $this; - } - - /** - * Return path to the QR Code label's TTF font file. - * - * @return string - */ - public function getLabelFontPath() - { - return $this->label_font_path; - } - - /** - * Set label horizontal alignment. - * - * @param int $label_halign Label horizontal alignment - * - * @return QrCode - */ - public function setLabelHalign($label_halign) - { - $this->label_halign = $label_halign; - - return $this; - } - - /** - * Return label horizontal alignment. - * - * @return int - */ - public function getLabelHalign() - { - return $this->label_halign; - } - - /** - * Set label vertical alignment. - * - * @param int $label_valign Label vertical alignment - * - * @return QrCode - */ - public function setLabelValign($label_valign) - { - $this->label_valign = $label_valign; - - return $this; - } - - /** - * Return label vertical alignment. - * - * @return int - */ - public function getLabelValign() - { - return $this->label_valign; - } - - /** - * Set foreground color of the QR Code. - * - * @param array $color_foreground RGB color - * - * @return QrCode - */ - public function setForegroundColor($color_foreground) - { - if (!isset($color_foreground['a'])) { - $color_foreground['a'] = 0; - } - - $this->color_foreground = $color_foreground; - - return $this; - } - - /** - * Return foreground color of the QR Code. - * - * @return array - */ - public function getForegroundColor() - { - return $this->color_foreground; - } - - /** - * Set background color of the QR Code. - * - * @param array $color_background RGB color - * - * @return QrCode - */ - public function setBackgroundColor($color_background) - { - if (!isset($color_background['a'])) { - $color_background['a'] = 0; - } - - $this->color_background = $color_background; - - return $this; - } - - /** - * Return background color of the QR Code. - * - * @return array - */ - public function getBackgroundColor() - { - return $this->color_background; - } - - /** - * Return the image resource. - * - * @return resource - */ - public function getImage() - { - if (empty($this->image)) { - $this->create(); - } - - return $this->image; - } - - /** - * Return the data URI. - * - * @return string - */ - public function getDataUri() - { - if (empty($this->image)) { - $this->create(); - } - - ob_start(); - call_user_func('image' . $this->image_type, $this->image); - $contents = ob_get_clean(); - - return 'data:image/' . $this->image_type . ';base64,' . base64_encode($contents); - } - - /** - * Render the QR Code then save it to given file name. - * - * @param string $filename File name of the QR Code - * - * @return QrCode - */ - public function save($filename) - { - $this->render($filename); - - return $this; - } - - /** - * Render the QR Code then save it to given file name or - * output it to the browser when file name omitted. - * - * @param null|string $filename File name of the QR Code - * @param null|string $format Format of the file (png, jpeg, jpg, gif, wbmp) - * @return QrCode - * @throws Exception - */ - public function render($filename = null, $format = 'png') - { - $this->create(); - - if ($format == 'jpg') { - $format = 'jpeg'; - } - - if (!in_array($format, $this->image_types_available)) { - $format = $this->image_type; - } - - if (!function_exists('image' . $format)) { - throw new Exception('QRCode: function image' . $format . ' does not exists.'); - } - - if ($filename === null) { - $success = call_user_func('image' . $format, $this->image); - } else { - $success = call_user_func_array('image' . $format, [$this->image, $filename]); - } - - if ($success === false) { - throw new Exception('QRCode: function image' . $format . ' failed.'); - } - - return $this; - } - - /** - * Returns the content type corresponding to the image type. - * - * @return string - */ - public function getContentType() - { - $contentType = 'image/' . $this->image_type; - - return $contentType; - } - - /** - * Create QR Code and return its content. - * - * @param string|null $format Image type (gif, png, wbmp, jpeg) - * @return string - * @throws Exception - */ - public function get($format = null) - { - $this->create(); - - if ($format == 'jpg') { - $format = 'jpeg'; - } - - if (!in_array($format, $this->image_types_available)) { - $format = $this->image_type; - } - - if (!function_exists('image' . $format)) { - throw new Exception('QRCode: function image' . $format . ' does not exists.'); - } - - ob_start(); - $success = call_user_func('image' . $format, $this->image); - - if ($success === false) { - throw new Exception('QRCode: function image' . $format . ' failed.'); - } - - $content = ob_get_clean(); - - return $content; - } - - /** - * Create the image. - * - * @throws \OverflowException - */ - public function create() - { - $image_path = $this->image_path; - $path = $this->path; - - $version_ul = 40; - - $qrcode_data_string = $this->text;//Previously from $_GET["d"]; - - $qrcode_error_correct = $this->error_correction;//Previously from $_GET["e"]; - $qrcode_module_size = $this->module_size;//Previously from $_GET["s"]; - $qrcode_version = $this->version;//Previously from $_GET["v"]; - $qrcode_image_type = $this->image_type;//Previously from $_GET["t"]; - - $qrcode_structureappend_n = $this->structure_append_n;//Previously from $_GET["n"]; - $qrcode_structureappend_m = $this->structure_append_m;//Previously from $_GET["m"]; - $qrcode_structureappend_parity = $this->structure_append_parity;//Previously from $_GET["p"]; - $qrcode_structureappend_originaldata = $this->structure_append_original_data;//Previously from $_GET["o"]; - - if ($qrcode_module_size > 0) { - } else { - if ($qrcode_image_type == 'jpeg') { - $qrcode_module_size = 8; - } else { - $qrcode_module_size = 4; - } - } - $data_length = strlen($qrcode_data_string); - if ($data_length <= 0) { - throw new Exception('QRCode: data does not exist.'); - } - $data_counter = 0; - if ($qrcode_structureappend_n > 1 - && $qrcode_structureappend_n <= 16 - && $qrcode_structureappend_m > 0 - && $qrcode_structureappend_m <= 16 - ) { - $data_value[0] = 3; - $data_bits[0] = 4; - - $data_value[1] = $qrcode_structureappend_m - 1; - $data_bits[1] = 4; - - $data_value[2] = $qrcode_structureappend_n - 1; - $data_bits[2] = 4; - - $originaldata_length = strlen($qrcode_structureappend_originaldata); - if ($originaldata_length > 1) { - $qrcode_structureappend_parity = 0; - $i = 0; - while ($i < $originaldata_length) { - $qrcode_structureappend_parity = ($qrcode_structureappend_parity ^ ord(substr($qrcode_structureappend_originaldata, $i, 1))); - ++$i; - } - } - - $data_value[3] = $qrcode_structureappend_parity; - $data_bits[3] = 8; - - $data_counter = 4; - } - - $data_bits[$data_counter] = 4; - - /* --- determine encode mode */ - - if (preg_match('/[^0-9]/', $qrcode_data_string) != 0) { - if (preg_match("/[^0-9A-Z \$\*\%\+\.\/\:\-]/", $qrcode_data_string) != 0) { - /* --- 8bit byte mode */ - - $codeword_num_plus = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8]; - - $data_value[$data_counter] = 4; - ++$data_counter; - $data_value[$data_counter] = $data_length; - $data_bits[$data_counter] = 8; /* #version 1-9 */ - $codeword_num_counter_value = $data_counter; - - ++$data_counter; - $i = 0; - while ($i < $data_length) { - $data_value[$data_counter] = ord(substr($qrcode_data_string, $i, 1)); - $data_bits[$data_counter] = 8; - ++$data_counter; - ++$i; - } - } else { - /* ---- alphanumeric mode */ - - $codeword_num_plus = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]; - - $data_value[$data_counter] = 2; - ++$data_counter; - $data_value[$data_counter] = $data_length; - $data_bits[$data_counter] = 9; /* #version 1-9 */ - $codeword_num_counter_value = $data_counter; - - $alphanumeric_character_hash = ['0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, - '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, - 'F' => 15, 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23, - 'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31, - 'W' => 32, 'X' => 33, 'Y' => 34, 'Z' => 35, ' ' => 36, '$' => 37, '%' => 38, '*' => 39, - '+' => 40, '-' => 41, '.' => 42, '/' => 43, ':' => 44]; - - $i = 0; - ++$data_counter; - while ($i < $data_length) { - if (($i % 2) == 0) { - $data_value[$data_counter] = $alphanumeric_character_hash[substr($qrcode_data_string, $i, 1)]; - $data_bits[$data_counter] = 6; - } else { - $data_value[$data_counter] = $data_value[$data_counter] * 45 + $alphanumeric_character_hash[substr($qrcode_data_string, $i, 1)]; - $data_bits[$data_counter] = 11; - ++$data_counter; - } - ++$i; - } - } - } else { - /* ---- numeric mode */ - - $codeword_num_plus = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]; - - $data_value[$data_counter] = 1; - ++$data_counter; - $data_value[$data_counter] = $data_length; - $data_bits[$data_counter] = 10; /* #version 1-9 */ - $codeword_num_counter_value = $data_counter; - - $i = 0; - ++$data_counter; - while ($i < $data_length) { - if (($i % 3) == 0) { - $data_value[$data_counter] = substr($qrcode_data_string, $i, 1); - $data_bits[$data_counter] = 4; - } else { - $data_value[$data_counter] = $data_value[$data_counter] * 10 + substr($qrcode_data_string, $i, 1); - if (($i % 3) == 1) { - $data_bits[$data_counter] = 7; - } else { - $data_bits[$data_counter] = 10; - ++$data_counter; - } - } - ++$i; - } - } - if (array_key_exists($data_counter, $data_bits) && $data_bits[$data_counter] > 0) { - ++$data_counter; - } - $i = 0; - $total_data_bits = 0; - while ($i < $data_counter) { - $total_data_bits += $data_bits[$i]; - ++$i; - } - - $ecc_character_hash = [ - 'L' => '1', - 'l' => '1', - 'M' => '0', - 'm' => '0', - 'Q' => '3', - 'q' => '3', - 'H' => '2', - 'h' => '2', - ]; - - if (!is_numeric($qrcode_error_correct)) { - $ec = @$ecc_character_hash[$qrcode_error_correct]; - } else { - $ec = $qrcode_error_correct; - } - - if (!$ec) { - $ec = 0; - } - - $max_data_bits = 0; - - $max_data_bits_array = [ - 0, 128, 224, 352, 512, 688, 864, 992, 1232, 1456, 1728, - 2032, 2320, 2672, 2920, 3320, 3624, 4056, 4504, 5016, 5352, - 5712, 6256, 6880, 7312, 8000, 8496, 9024, 9544, 10136, 10984, - 11640, 12328, 13048, 13800, 14496, 15312, 15936, 16816, 17728, 18672, - - 152, 272, 440, 640, 864, 1088, 1248, 1552, 1856, 2192, - 2592, 2960, 3424, 3688, 4184, 4712, 5176, 5768, 6360, 6888, - 7456, 8048, 8752, 9392, 10208, 10960, 11744, 12248, 13048, 13880, - 14744, 15640, 16568, 17528, 18448, 19472, 20528, 21616, 22496, 23648, - - 72, 128, 208, 288, 368, 480, 528, 688, 800, 976, - 1120, 1264, 1440, 1576, 1784, 2024, 2264, 2504, 2728, 3080, - 3248, 3536, 3712, 4112, 4304, 4768, 5024, 5288, 5608, 5960, - 6344, 6760, 7208, 7688, 7888, 8432, 8768, 9136, 9776, 10208, - - 104, 176, 272, 384, 496, 608, 704, 880, 1056, 1232, - 1440, 1648, 1952, 2088, 2360, 2600, 2936, 3176, 3560, 3880, - 4096, 4544, 4912, 5312, 5744, 6032, 6464, 6968, 7288, 7880, - 8264, 8920, 9368, 9848, 10288, 10832, 11408, 12016, 12656, 13328 - ]; - if (!is_numeric($qrcode_version)) { - $qrcode_version = 0; - } - if (!$qrcode_version) { - /* #--- auto version select */ - $i = 1 + 40 * $ec; - $j = $i + 39; - $qrcode_version = 1; - while ($i <= $j) { - if (($max_data_bits_array[$i]) >= $total_data_bits + $codeword_num_plus[$qrcode_version]) { - $max_data_bits = $max_data_bits_array[$i]; - break; - } - ++$i; - ++$qrcode_version; - } - } else { - $max_data_bits = $max_data_bits_array[$qrcode_version + 40 * $ec]; - } - if ($qrcode_version > $version_ul) { - throw new Exception('QRCode : version too large'); - } - - $total_data_bits += $codeword_num_plus[$qrcode_version]; - $data_bits[$codeword_num_counter_value] += $codeword_num_plus[$qrcode_version]; - - $max_codewords_array = [0, 26, 44, 70, 100, 134, 172, 196, 242, - 292, 346, 404, 466, 532, 581, 655, 733, 815, 901, 991, 1085, 1156, - 1258, 1364, 1474, 1588, 1706, 1828, 1921, 2051, 2185, 2323, 2465, - 2611, 2761, 2876, 3034, 3196, 3362, 3532, 3706]; - - $max_codewords = $max_codewords_array[$qrcode_version]; - $max_modules_1side = 17 + ($qrcode_version << 2); - - $matrix_remain_bit = [0, 0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0]; - - /* ---- read version ECC data file */ - - $byte_num = $matrix_remain_bit[$qrcode_version] + ($max_codewords << 3); - $filename = $path . '/qrv' . $qrcode_version . '_' . $ec . '.dat'; - $fp1 = fopen($filename, 'rb'); - $matx = fread($fp1, $byte_num); - $maty = fread($fp1, $byte_num); - $masks = fread($fp1, $byte_num); - $fi_x = fread($fp1, 15); - $fi_y = fread($fp1, 15); - $rs_ecc_codewords = ord(fread($fp1, 1)); - $rso = fread($fp1, 128); - fclose($fp1); - - $matrix_x_array = unpack('C*', $matx); - $matrix_y_array = unpack('C*', $maty); - $mask_array = unpack('C*', $masks); - - $rs_block_order = unpack('C*', $rso); - - $format_information_x2 = unpack('C*', $fi_x); - $format_information_y2 = unpack('C*', $fi_y); - - $format_information_x1 = [0, 1, 2, 3, 4, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8]; - $format_information_y1 = [8, 8, 8, 8, 8, 8, 8, 8, 7, 5, 4, 3, 2, 1, 0]; - - $max_data_codewords = ($max_data_bits >> 3); - - $filename = $path . '/rsc' . $rs_ecc_codewords . '.dat'; - $fp0 = fopen($filename, 'rb'); - $i = 0; - $rs_cal_table_array = []; - while ($i < 256) { - $rs_cal_table_array[$i] = fread($fp0, $rs_ecc_codewords); - ++$i; - } - fclose($fp0); - - /* --- set terminator */ - - if ($total_data_bits <= $max_data_bits - 4) { - $data_value[$data_counter] = 0; - $data_bits[$data_counter] = 4; - } else { - if ($total_data_bits < $max_data_bits) { - $data_value[$data_counter] = 0; - $data_bits[$data_counter] = $max_data_bits - $total_data_bits; - } else { - if ($total_data_bits > $max_data_bits) { - throw new \OverflowException('QRCode: overflow error'); - } - } - } - - /* ----divide data by 8bit */ - - $i = 0; - $codewords_counter = 0; - $codewords[0] = 0; - $remaining_bits = 8; - - while ($i <= $data_counter) { - $buffer = @$data_value[$i]; - $buffer_bits = @$data_bits[$i]; - - $flag = 1; - while ($flag) { - if ($remaining_bits > $buffer_bits) { - $codewords[$codewords_counter] = ((@$codewords[$codewords_counter] << $buffer_bits) | $buffer); - $remaining_bits -= $buffer_bits; - $flag = 0; - } else { - $buffer_bits -= $remaining_bits; - $codewords[$codewords_counter] = (($codewords[$codewords_counter] << $remaining_bits) | ($buffer >> $buffer_bits)); - - if ($buffer_bits == 0) { - $flag = 0; - } else { - $buffer = ($buffer & ((1 << $buffer_bits) - 1)); - $flag = 1; - } - - ++$codewords_counter; - if ($codewords_counter < $max_data_codewords - 1) { - $codewords[$codewords_counter] = 0; - } - $remaining_bits = 8; - } - } - ++$i; - } - if ($remaining_bits != 8) { - $codewords[$codewords_counter] = $codewords[$codewords_counter] << $remaining_bits; - } else { - --$codewords_counter; - } - - /* ---- set padding character */ - - if ($codewords_counter < $max_data_codewords - 1) { - $flag = 1; - while ($codewords_counter < $max_data_codewords - 1) { - ++$codewords_counter; - if ($flag == 1) { - $codewords[$codewords_counter] = 236; - } else { - $codewords[$codewords_counter] = 17; - } - $flag = $flag * (-1); - } - } - - /* ---- RS-ECC prepare */ - - $i = 0; - $j = 0; - $rs_block_number = 0; - $rs_temp[0] = ''; - - while ($i < $max_data_codewords) { - $rs_temp[$rs_block_number] .= chr($codewords[$i]); - ++$j; - - if ($j >= $rs_block_order[$rs_block_number + 1] - $rs_ecc_codewords) { - $j = 0; - ++$rs_block_number; - $rs_temp[$rs_block_number] = ''; - } - ++$i; - } - - /* - # - # RS-ECC main - # - */ - - $rs_block_number = 0; - $rs_block_order_num = count($rs_block_order); - - while ($rs_block_number < $rs_block_order_num) { - $rs_codewords = $rs_block_order[$rs_block_number + 1]; - $rs_data_codewords = $rs_codewords - $rs_ecc_codewords; - - $rstemp = $rs_temp[$rs_block_number] . str_repeat(chr(0), $rs_ecc_codewords); - $padding_data = str_repeat(chr(0), $rs_data_codewords); - - $j = $rs_data_codewords; - while ($j > 0) { - $first = ord(substr($rstemp, 0, 1)); - - if ($first) { - $left_chr = substr($rstemp, 1); - $cal = $rs_cal_table_array[$first] . $padding_data; - $rstemp = $left_chr ^ $cal; - } else { - $rstemp = substr($rstemp, 1); - } - - --$j; - } - - $codewords = array_merge($codewords, unpack('C*', $rstemp)); - - ++$rs_block_number; - } - - /* ---- flash matrix */ - $matrix_content = []; - $i = 0; - while ($i < $max_modules_1side) { - $j = 0; - while ($j < $max_modules_1side) { - $matrix_content[$j][$i] = 0; - ++$j; - } - ++$i; - } - - /* --- attach data */ - - $i = 0; - while ($i < $max_codewords) { - $codeword_i = $codewords[$i]; - $j = 8; - while ($j >= 1) { - $codeword_bits_number = ($i << 3) + $j; - $matrix_content[$matrix_x_array[$codeword_bits_number]][$matrix_y_array[$codeword_bits_number]] = ((255 * ($codeword_i & 1)) ^ $mask_array[$codeword_bits_number]); - $codeword_i = $codeword_i >> 1; - --$j; - } - ++$i; - } - - $matrix_remain = $matrix_remain_bit[$qrcode_version]; - while ($matrix_remain) { - $remain_bit_temp = $matrix_remain + ($max_codewords << 3); - $matrix_content[$matrix_x_array[$remain_bit_temp]][$matrix_y_array[$remain_bit_temp]] = (255 ^ $mask_array[$remain_bit_temp]); - --$matrix_remain; - } - - #--- mask select - - $min_demerit_score = 0; - $hor_master = ''; - $ver_master = ''; - $k = 0; - while ($k < $max_modules_1side) { - $l = 0; - while ($l < $max_modules_1side) { - $hor_master = $hor_master . chr($matrix_content[$l][$k]); - $ver_master = $ver_master . chr($matrix_content[$k][$l]); - ++$l; - } - ++$k; - } - $i = 0; - $all_matrix = $max_modules_1side * $max_modules_1side; - $mask_number = 0; - while ($i < 8) { - $demerit_n1 = 0; - $ptn_temp = []; - $bit = 1 << $i; - $bit_r = (~$bit) & 255; - $bit_mask = str_repeat(chr($bit), $all_matrix); - $hor = $hor_master & $bit_mask; - $ver = $ver_master & $bit_mask; - - $ver_shift1 = $ver . str_repeat(chr(170), $max_modules_1side); - $ver_shift2 = str_repeat(chr(170), $max_modules_1side) . $ver; - $ver_shift1_0 = $ver . str_repeat(chr(0), $max_modules_1side); - $ver_shift2_0 = str_repeat(chr(0), $max_modules_1side) . $ver; - $ver_or = chunk_split(~($ver_shift1 | $ver_shift2), $max_modules_1side, chr(170)); - $ver_and = chunk_split(~($ver_shift1_0 & $ver_shift2_0), $max_modules_1side, chr(170)); - - $hor = chunk_split(~$hor, $max_modules_1side, chr(170)); - $ver = chunk_split(~$ver, $max_modules_1side, chr(170)); - $hor = $hor . chr(170) . $ver; - - $n1_search = '/' . str_repeat(chr(255), 5) . '+|' . str_repeat(chr($bit_r), 5) . '+/'; - $n3_search = chr($bit_r) . chr(255) . chr($bit_r) . chr($bit_r) . chr($bit_r) . chr(255) . chr($bit_r); - - $demerit_n3 = substr_count($hor, $n3_search) * 40; - $demerit_n4 = floor(abs(((100 * (substr_count($ver, chr($bit_r)) / ($byte_num))) - 50) / 5)) * 10; - - $n2_search1 = '/' . chr($bit_r) . chr($bit_r) . '+/'; - $n2_search2 = '/' . chr(255) . chr(255) . '+/'; - $demerit_n2 = 0; - preg_match_all($n2_search1, $ver_and, $ptn_temp); - foreach ($ptn_temp[0] as $str_temp) { - $demerit_n2 += (strlen($str_temp) - 1); - } - $ptn_temp = []; - preg_match_all($n2_search2, $ver_or, $ptn_temp); - foreach ($ptn_temp[0] as $str_temp) { - $demerit_n2 += (strlen($str_temp) - 1); - } - $demerit_n2 *= 3; - - $ptn_temp = []; - - preg_match_all($n1_search, $hor, $ptn_temp); - foreach ($ptn_temp[0] as $str_temp) { - $demerit_n1 += (strlen($str_temp) - 2); - } - - $demerit_score = $demerit_n1 + $demerit_n2 + $demerit_n3 + $demerit_n4; - - if ($demerit_score <= $min_demerit_score || $i == 0) { - $mask_number = $i; - $min_demerit_score = $demerit_score; - } - - ++$i; - } - - $mask_content = 1 << $mask_number; - - # --- format information - - $format_information_value = (($ec << 3) | $mask_number); - $format_information_array = ['101010000010010', '101000100100101', - '101111001111100', '101101101001011', '100010111111001', '100000011001110', - '100111110010111', '100101010100000', '111011111000100', '111001011110011', - '111110110101010', '111100010011101', '110011000101111', '110001100011000', - '110110001000001', '110100101110110', '001011010001001', '001001110111110', - '001110011100111', '001100111010000', '000011101100010', '000001001010101', - '000110100001100', '000100000111011', '011010101011111', '011000001101000', - '011111100110001', '011101000000110', '010010010110100', '010000110000011', - '010111011011010', '010101111101101']; - $i = 0; - while ($i < 15) { - $content = substr($format_information_array[$format_information_value], $i, 1); - - $matrix_content[$format_information_x1[$i]][$format_information_y1[$i]] = $content * 255; - $matrix_content[$format_information_x2[$i + 1]][$format_information_y2[$i + 1]] = $content * 255; - ++$i; - } - - $mib = $max_modules_1side + 8; - - if ($this->size == 0) { - $this->size = $mib * $qrcode_module_size; - if ($this->size > 1480) { - throw new Exception('QRCode: image size too large'); - } - } - - $image_width = $this->size + $this->padding * 2; - $image_height = $this->size + $this->padding * 2; - - if (!empty($this->label)) { - if (!function_exists('imagettfbbox')) { - throw new Exception('QRCode: missing function "imagettfbbox". Did you install the FreeType library?'); - } - $font_box = imagettfbbox($this->label_font_size, 0, $this->label_font_path, $this->label); - $label_width = (int)$font_box[2] - (int)$font_box[0]; - $label_height = (int)$font_box[0] - (int)$font_box[7]; - - if ($this->label_valign == self::LABEL_VALIGN_MIDDLE) { - $image_height += $label_height + $this->padding; - } else { - $image_height += $label_height; - } - } - - $output_image = imagecreate($image_width, $image_height); - imagecolorallocate($output_image, 255, 255, 255); - - $image_path = $image_path . '/qrv' . $qrcode_version . '.png'; - - $base_image = imagecreatefrompng($image_path); - $code_size = $this->size; - $module_size = function ($size = 1) use ($code_size, $base_image) { - return round($code_size / imagesx($base_image) * $size); - }; - - $col[1] = imagecolorallocate($base_image, 0, 0, 0); - $col[0] = imagecolorallocate($base_image, 255, 255, 255); - - $i = 4; - $mxe = 4 + $max_modules_1side; - $ii = 0; - while ($i < $mxe) { - $j = 4; - $jj = 0; - while ($j < $mxe) { - if ($matrix_content[$ii][$jj] & $mask_content) { - imagesetpixel($base_image, $i, $j, $col[1]); - } - ++$j; - ++$jj; - } - ++$i; - ++$ii; - } - - if ($this->draw_quiet_zone == true) { - imagecopyresampled($output_image, $base_image, $this->padding, $this->padding, 0, 0, $this->size, $this->size, $mib, $mib); - } else { - imagecopyresampled($output_image, $base_image, $this->padding, $this->padding, 4, 4, $this->size, $this->size, $mib - 8, $mib - 8); - } - - if ($this->draw_border == true) { - $border_width = $this->padding; - $border_height = $this->size + $this->padding - 1; - $border_color = imagecolorallocate($output_image, 0, 0, 0); - imagerectangle($output_image, $border_width, $border_width, $border_height, $border_height, $border_color); - } - - if (!empty($this->label)) { - // Label horizontal alignment - switch ($this->label_halign) { - case self::LABEL_HALIGN_LEFT: - $font_x = 0; - break; - - case self::LABEL_HALIGN_LEFT_BORDER: - $font_x = $this->padding; - break; - - case self::LABEL_HALIGN_LEFT_CODE: - if ($this->draw_quiet_zone == true) { - $font_x = $this->padding + $module_size(4); - } else { - $font_x = $this->padding; - } - break; - - case self::LABEL_HALIGN_RIGHT: - $font_x = $this->size + ($this->padding * 2) - $label_width; - break; - - case self::LABEL_HALIGN_RIGHT_BORDER: - $font_x = $this->size + $this->padding - $label_width; - break; - - case self::LABEL_HALIGN_RIGHT_CODE: - if ($this->draw_quiet_zone == true) { - $font_x = $this->size + $this->padding - $label_width - $module_size(4); - } else { - $font_x = $this->size + $this->padding - $label_width; - } - break; - - default: - $font_x = floor($image_width - $label_width) / 2; - } - - // Label vertical alignment - switch ($this->label_valign) { - case self::LABEL_VALIGN_TOP_NO_BORDER: - $font_y = $image_height - $this->padding - 1; - break; - - case self::LABEL_VALIGN_BOTTOM: - $font_y = $image_height; - break; - - default: - $font_y = $image_height - $this->padding; - } - - $label_bg_x1 = $font_x - $module_size(2); - $label_bg_y1 = $font_y - $label_height; - $label_bg_x2 = $font_x + $label_width + $module_size(2); - $label_bg_y2 = $font_y; - - $color = imagecolorallocate($output_image, 0, 0, 0); - $label_bg_color = imagecolorallocate($output_image, 255, 255, 255); - - imagefilledrectangle($output_image, $label_bg_x1, $label_bg_y1, $label_bg_x2, $label_bg_y2, $label_bg_color); - imagettftext($output_image, $this->label_font_size, 0, $font_x, $font_y, $color, $this->label_font_path, $this->label); - } - - $imagecolorset_function = new ReflectionFunction('imagecolorset'); - $allow_alpha = $imagecolorset_function->getNumberOfParameters() == 6; - - if ($this->color_background != null) { - $index = imagecolorclosest($output_image, 255, 255, 255); - if ($allow_alpha) { - imagecolorset($output_image, $index, $this->color_background['r'], $this->color_background['g'], $this->color_background['b'], $this->color_background['a']); - } else { - imagecolorset($output_image, $index, $this->color_background['r'], $this->color_background['g'], $this->color_background['b']); - } - } - - if ($this->color_foreground != null) { - $index = imagecolorclosest($output_image, 0, 0, 0); - if ($allow_alpha) { - imagecolorset($output_image, $index, $this->color_foreground['r'], $this->color_foreground['g'], $this->color_foreground['b'], $this->color_foreground['a']); - } else { - imagecolorset($output_image, $index, $this->color_foreground['r'], $this->color_foreground['g'], $this->color_foreground['b']); - } - } - - if (!empty($this->logo)) { - $output_image_org = $output_image; - $output_image = imagecreatetruecolor($image_width, $image_height); - imagecopy($output_image, $output_image_org, 0, 0, 0, 0, $image_width, $image_height); - - $logo_image = call_user_func('imagecreatefrom' . $this->image_type, $this->logo); - if (!$logo_image) { - throw new Exception('imagecreatefrom' . $this->image_type . ' ' . $this->logo . ' failed'); - } - $src_w = imagesx($logo_image); - $src_h = imagesy($logo_image); - - $dst_x = ($image_width - $this->logo_size) / 2; - $dst_y = ($this->size + $this->padding * 2 - $this->logo_size) / 2; - - $successful = imagecopyresampled($output_image, $logo_image, $dst_x, $dst_y, 0, 0, $this->logo_size, $this->logo_size, $src_w, $src_h); - if (!$successful) { - throw new Exception('add logo [image' . $this->format . '] failed.'); - } - imagedestroy($logo_image); - } - $this->image = $output_image; - } + /** @const int Error Correction Level Low (7%) */ + const LEVEL_LOW = 1; + + /** @const int Error Correction Level Medium (15%) */ + const LEVEL_MEDIUM = 0; + + /** @const int Error Correction Level Quartile (25%) */ + const LEVEL_QUARTILE = 3; + + /** @const int Error Correction Level High (30%) */ + const LEVEL_HIGH = 2; + + /** @const string Image type png */ + const IMAGE_TYPE_PNG = 'png'; + + /** @const string Image type gif */ + const IMAGE_TYPE_GIF = 'gif'; + + /** @const string Image type jpeg */ + const IMAGE_TYPE_JPEG = 'jpeg'; + + /** @const string Image type wbmp */ + const IMAGE_TYPE_WBMP = 'wbmp'; + + /** @const int Horizontal label alignment to the center of image */ + const LABEL_HALIGN_CENTER = 0; + + /** @const int Horizontal label alignment to the left side of image */ + const LABEL_HALIGN_LEFT = 1; + + /** @const int Horizontal label alignment to the left border of QR Code */ + const LABEL_HALIGN_LEFT_BORDER = 2; + + /** @const int Horizontal label alignment to the left side of QR Code */ + const LABEL_HALIGN_LEFT_CODE = 3; + + /** @const int Horizontal label alignment to the right side of image */ + const LABEL_HALIGN_RIGHT = 4; + + /** @const int Horizontal label alignment to the right border of QR Code */ + const LABEL_HALIGN_RIGHT_BORDER = 5; + + /** @const int Horizontal label alignment to the right side of QR Code */ + const LABEL_HALIGN_RIGHT_CODE = 6; + + /** @const int Vertical label alignment to the top */ + const LABEL_VALIGN_TOP = 1; + + /** @const int Vertical label alignment to the top and hide border */ + const LABEL_VALIGN_TOP_NO_BORDER = 2; + + /** @const int Vertical label alignment to the middle */ + const LABEL_VALIGN_MIDDLE = 3; + + /** @const int Vertical label alignment to the bottom */ + const LABEL_VALIGN_BOTTOM = 4; + + /** @var string */ + protected $logo = null; + + protected $logo_size = 48; + + /** @var string */ + protected $text = ''; + + /** @var int */ + protected $size = 0; + + /** @var int */ + protected $padding = 16; + + /** @var bool */ + protected $draw_quiet_zone = false; + + /** @var bool */ + protected $draw_border = false; + + /** @var array */ + protected $color_foreground = ['r' => 0, 'g' => 0, 'b' => 0, 'a' => 0]; + + /** @var array */ + protected $color_background = ['r' => 255, 'g' => 255, 'b' => 255, 'a' => 0]; + + /** @var string */ + protected $label = ''; + + /** @var int */ + protected $label_font_size = 16; + + /** @var string */ + protected $label_font_path = ''; + + /** @var int */ + protected $label_halign = self::LABEL_HALIGN_CENTER; + + /** @var int */ + protected $label_valign = self::LABEL_VALIGN_MIDDLE; + + /** @var resource */ + protected $image = null; + + /** @var int */ + protected $version; + + /** @var int */ + protected $error_correction = self::LEVEL_MEDIUM; + + /** @var array */ + protected $error_corrections_available = [ + self::LEVEL_LOW, + self::LEVEL_MEDIUM, + self::LEVEL_QUARTILE, + self::LEVEL_HIGH, + ]; + + /** @var int */ + protected $module_size; + + /** @var string */ + protected $image_type = self::IMAGE_TYPE_PNG; + + /** @var array */ + protected $image_types_available = [ + self::IMAGE_TYPE_GIF, + self::IMAGE_TYPE_PNG, + self::IMAGE_TYPE_JPEG, + self::IMAGE_TYPE_WBMP, + ]; + + /** @var string */ + protected $image_path; + + /** @var string */ + protected $path; + + /** @var int */ + protected $structure_append_n; + + /** @var int */ + protected $structure_append_m; + + /** @var int */ + protected $structure_append_parity; + + /** @var string */ + protected $structure_append_original_data; + + /** + * Class constructor. + * + * @param string $text + */ + public function __construct($text = '') + { + $this->setPath(__DIR__ . '/../qr/data'); + $this->setImagePath(__DIR__ . '/../qr/image'); + $this->setLabelFontPath(__DIR__ . '/../qr/font/opensans.ttf'); + $this->setText($text); + } + + /** + * Set structure append. + * + * @param int $n + * @param int $m + * @param int $parity Parity + * @param string $original_data Original data + * + * @return QrCode + */ + public function setStructureAppend($n, $m, $parity, $original_data) + { + $this->structure_append_n = $n; + $this->structure_append_m = $m; + $this->structure_append_parity = $parity; + $this->structure_append_original_data = $original_data; + + return $this; + } + + /** + * Set QR Code version. + * + * @param int $version QR Code version + * + * @return QrCode + */ + public function setVersion($version) + { + if ($version <= 40 && $version >= 0) { + $this->version = $version; + } + + return $this; + } + + /** + * Return QR Code version. + * + * @return int + */ + public function getVersion() + { + return $this->version; + } + + /** + * Set QR Code error correction level. + * + * @param mixed $error_correction Error Correction Level + * + * @return QrCode + */ + public function setErrorCorrection($error_correction) + { + if (!is_numeric($error_correction)) { + $level_constant = 'QrCode::LEVEL_' . strtoupper($error_correction); + $error_correction = constant($level_constant); + } + + if (in_array($error_correction, $this->error_corrections_available)) { + $this->error_correction = $error_correction; + } + + return $this; + } + + /** + * Return QR Code error correction level. + * + * @return int + */ + public function getErrorCorrection() + { + return $this->error_correction; + } + + /** + * Set QR Code module size. + * + * @param int $module_size Module size + * + * @return QrCode + */ + public function setModuleSize($module_size) + { + $this->module_size = $module_size; + + return $this; + } + + /** + * Return QR Code module size. + * + * @return int + */ + public function getModuleSize() + { + return $this->module_size; + } + + /** + * Set image type for rendering. + * + * @param string $image_type Image type + * @return QrCode + * @throws Exception + */ + public function setImageType($image_type) + { + if (!in_array($image_type, $this->image_types_available)) { + throw new Exception('QRCode: image type ' . $image_type . ' is invalid.'); + } + + $this->image_type = $image_type; + + return $this; + } + + /** + * Return image type for rendering. + * + * @return string + */ + public function getImageType() + { + return $this->image_type; + } + + /** + * Set image type for rendering via extension. + * + * @param string $extension Image extension + * + * @return QrCode + */ + public function setExtension($extension) + { + if ($extension == 'jpg') { + $this->setImageType('jpeg'); + } else { + $this->setImageType($extension); + } + + return $this; + } + + /** + * Set path to the images directory. + * + * @param string $image_path Image directory + * + * @return QrCode + */ + public function setImagePath($image_path) + { + $this->image_path = $image_path; + + return $this; + } + + /** + * Return path to the images directory. + * + * @return string + */ + public function getImagePath() + { + return $this->image_path; + } + + /** + * Set path to the data directory. + * + * @param string $path Data directory + * + * @return QrCode + */ + public function setPath($path) + { + $this->path = $path; + + return $this; + } + + /** + * Return path to the data directory. + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Set logo in QR Code. + * + * @param string $logo Logo Path + * @return QrCode + * @throws Exception + */ + public function setLogo($logo) + { + if (!file_exists($logo)) { + throw new Exception("$logo file does not exist"); + } + + $this->logo = $logo; + + return $this; + } + + /** + * Set logo size in QR Code(default 48). + * + * @param int $logo_size Logo Size + * + * @return QrCode + */ + public function setLogoSize($logo_size) + { + $this->logo_size = $logo_size; + + return $this; + } + + /** + * Set text to hide in QR Code. + * + * @param string $text Text to hide + * + * @return QrCode + */ + public function setText($text) + { + $this->text = $text; + + return $this; + } + + /** + * Return text that will be hid in QR Code. + * + * @return string + */ + public function getText() + { + return $this->text; + } + + /** + * Set QR Code size (width). + * + * @param int $size Width of the QR Code + * + * @return QrCode + */ + public function setSize($size) + { + $this->size = $size; + + return $this; + } + + /** + * Return QR Code size (width). + * + * @return int + */ + public function getSize() + { + return $this->size; + } + + /** + * Set padding around the QR Code. + * + * @param int $padding Padding around QR Code + * + * @return QrCode + */ + public function setPadding($padding) + { + $this->padding = $padding; + + return $this; + } + + /** + * Return padding around the QR Code. + * + * @return int + */ + public function getPadding() + { + return $this->padding; + } + + /** + * Set draw required four-module wide margin. + * + * @param bool $draw_quiet_zone State of required four-module wide margin drawing + * + * @return QrCode + */ + public function setDrawQuietZone($draw_quiet_zone) + { + $this->draw_quiet_zone = $draw_quiet_zone; + + return $this; + } + + /** + * Return draw required four-module wide margin. + * + * @return bool + */ + public function getDrawQuietZone() + { + return $this->draw_quiet_zone; + } + + /** + * Set draw border around QR Code. + * + * @param bool $draw_border State of border drawing + * + * @return QrCode + */ + public function setDrawBorder($draw_border) + { + $this->draw_border = $draw_border; + + return $this; + } + + /** + * Return draw border around QR Code. + * + * @return bool + */ + public function getDrawBorder() + { + return $this->draw_border; + } + + /** + * Set QR Code label (text). + * + * @param int|string $label Label to print under QR code + * + * @return QrCode + */ + public function setLabel($label) + { + $this->label = $label; + + return $this; + } + + /** + * Return QR Code label (text). + * + * @return string + */ + public function getLabel() + { + return $this->label; + } + + /** + * Set QR Code label font size. + * + * @param int $label_font_size Font size of the QR code label + * + * @return QrCode + */ + public function setLabelFontSize($label_font_size) + { + $this->label_font_size = $label_font_size; + + return $this; + } + + /** + * Return QR Code label font size. + * + * @return int + */ + public function getLabelFontSize() + { + return $this->label_font_size; + } + + /** + * Set QR Code label font path. + * + * @param int $label_font_path Path to the QR Code label's TTF font file + * + * @return QrCode + */ + public function setLabelFontPath($label_font_path) + { + $this->label_font_path = $label_font_path; + + return $this; + } + + /** + * Return path to the QR Code label's TTF font file. + * + * @return string + */ + public function getLabelFontPath() + { + return $this->label_font_path; + } + + /** + * Set label horizontal alignment. + * + * @param int $label_halign Label horizontal alignment + * + * @return QrCode + */ + public function setLabelHalign($label_halign) + { + $this->label_halign = $label_halign; + + return $this; + } + + /** + * Return label horizontal alignment. + * + * @return int + */ + public function getLabelHalign() + { + return $this->label_halign; + } + + /** + * Set label vertical alignment. + * + * @param int $label_valign Label vertical alignment + * + * @return QrCode + */ + public function setLabelValign($label_valign) + { + $this->label_valign = $label_valign; + + return $this; + } + + /** + * Return label vertical alignment. + * + * @return int + */ + public function getLabelValign() + { + return $this->label_valign; + } + + /** + * Set foreground color of the QR Code. + * + * @param array $color_foreground RGB color + * + * @return QrCode + */ + public function setForegroundColor($color_foreground) + { + if (!isset($color_foreground['a'])) { + $color_foreground['a'] = 0; + } + + $this->color_foreground = $color_foreground; + + return $this; + } + + /** + * Return foreground color of the QR Code. + * + * @return array + */ + public function getForegroundColor() + { + return $this->color_foreground; + } + + /** + * Set background color of the QR Code. + * + * @param array $color_background RGB color + * + * @return QrCode + */ + public function setBackgroundColor($color_background) + { + if (!isset($color_background['a'])) { + $color_background['a'] = 0; + } + + $this->color_background = $color_background; + + return $this; + } + + /** + * Return background color of the QR Code. + * + * @return array + */ + public function getBackgroundColor() + { + return $this->color_background; + } + + /** + * Return the image resource. + * + * @return resource + */ + public function getImage() + { + if (empty($this->image)) { + $this->create(); + } + + return $this->image; + } + + /** + * Return the data URI. + * + * @return string + */ + public function getDataUri() + { + if (empty($this->image)) { + $this->create(); + } + + ob_start(); + call_user_func('image' . $this->image_type, $this->image); + $contents = ob_get_clean(); + + return 'data:image/' . $this->image_type . ';base64,' . base64_encode($contents); + } + + /** + * Render the QR Code then save it to given file name. + * + * @param string $filename File name of the QR Code + * + * @return QrCode + */ + public function save($filename) + { + $this->render($filename); + + return $this; + } + + /** + * Render the QR Code then save it to given file name or + * output it to the browser when file name omitted. + * + * @param null|string $filename File name of the QR Code + * @param null|string $format Format of the file (png, jpeg, jpg, gif, wbmp) + * @return QrCode + * @throws Exception + */ + public function render($filename = null, $format = 'png') + { + $this->create(); + + if ($format == 'jpg') { + $format = 'jpeg'; + } + + if (!in_array($format, $this->image_types_available)) { + $format = $this->image_type; + } + + if (!function_exists('image' . $format)) { + throw new Exception('QRCode: function image' . $format . ' does not exists.'); + } + + if ($filename === null) { + $success = call_user_func('image' . $format, $this->image); + } else { + $success = call_user_func_array('image' . $format, [$this->image, $filename]); + } + + if ($success === false) { + throw new Exception('QRCode: function image' . $format . ' failed.'); + } + + return $this; + } + + /** + * Returns the content type corresponding to the image type. + * + * @return string + */ + public function getContentType() + { + $contentType = 'image/' . $this->image_type; + + return $contentType; + } + + /** + * Create QR Code and return its content. + * + * @param string|null $format Image type (gif, png, wbmp, jpeg) + * @return string + * @throws Exception + */ + public function get($format = null) + { + $this->create(); + + if ($format == 'jpg') { + $format = 'jpeg'; + } + + if (!in_array($format, $this->image_types_available)) { + $format = $this->image_type; + } + + if (!function_exists('image' . $format)) { + throw new Exception('QRCode: function image' . $format . ' does not exists.'); + } + + ob_start(); + $success = call_user_func('image' . $format, $this->image); + + if ($success === false) { + throw new Exception('QRCode: function image' . $format . ' failed.'); + } + + $content = ob_get_clean(); + + return $content; + } + + /** + * Create the image. + * + * @throws \OverflowException + */ + public function create() + { + $image_path = $this->image_path; + $path = $this->path; + + $version_ul = 40; + + $qrcode_data_string = $this->text;//Previously from $_GET["d"]; + + $qrcode_error_correct = $this->error_correction;//Previously from $_GET["e"]; + $qrcode_module_size = $this->module_size;//Previously from $_GET["s"]; + $qrcode_version = $this->version;//Previously from $_GET["v"]; + $qrcode_image_type = $this->image_type;//Previously from $_GET["t"]; + + $qrcode_structureappend_n = $this->structure_append_n;//Previously from $_GET["n"]; + $qrcode_structureappend_m = $this->structure_append_m;//Previously from $_GET["m"]; + $qrcode_structureappend_parity = $this->structure_append_parity;//Previously from $_GET["p"]; + $qrcode_structureappend_originaldata = $this->structure_append_original_data;//Previously from $_GET["o"]; + + if ($qrcode_module_size > 0) { + } else { + if ($qrcode_image_type == 'jpeg') { + $qrcode_module_size = 8; + } else { + $qrcode_module_size = 4; + } + } + $data_length = strlen($qrcode_data_string); + if ($data_length <= 0) { + throw new Exception('QRCode: data does not exist.'); + } + $data_counter = 0; + if ($qrcode_structureappend_n > 1 + && $qrcode_structureappend_n <= 16 + && $qrcode_structureappend_m > 0 + && $qrcode_structureappend_m <= 16 + ) { + $data_value[0] = 3; + $data_bits[0] = 4; + + $data_value[1] = $qrcode_structureappend_m - 1; + $data_bits[1] = 4; + + $data_value[2] = $qrcode_structureappend_n - 1; + $data_bits[2] = 4; + + $originaldata_length = strlen($qrcode_structureappend_originaldata); + if ($originaldata_length > 1) { + $qrcode_structureappend_parity = 0; + $i = 0; + while ($i < $originaldata_length) { + $qrcode_structureappend_parity = ($qrcode_structureappend_parity ^ ord(substr($qrcode_structureappend_originaldata, $i, 1))); + ++$i; + } + } + + $data_value[3] = $qrcode_structureappend_parity; + $data_bits[3] = 8; + + $data_counter = 4; + } + + $data_bits[$data_counter] = 4; + + /* --- determine encode mode */ + + if (preg_match('/[^0-9]/', $qrcode_data_string) != 0) { + if (preg_match("/[^0-9A-Z \$\*\%\+\.\/\:\-]/", $qrcode_data_string) != 0) { + /* --- 8bit byte mode */ + + $codeword_num_plus = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8]; + + $data_value[$data_counter] = 4; + ++$data_counter; + $data_value[$data_counter] = $data_length; + $data_bits[$data_counter] = 8; /* #version 1-9 */ + $codeword_num_counter_value = $data_counter; + + ++$data_counter; + $i = 0; + while ($i < $data_length) { + $data_value[$data_counter] = ord(substr($qrcode_data_string, $i, 1)); + $data_bits[$data_counter] = 8; + ++$data_counter; + ++$i; + } + } else { + /* ---- alphanumeric mode */ + + $codeword_num_plus = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]; + + $data_value[$data_counter] = 2; + ++$data_counter; + $data_value[$data_counter] = $data_length; + $data_bits[$data_counter] = 9; /* #version 1-9 */ + $codeword_num_counter_value = $data_counter; + + $alphanumeric_character_hash = ['0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, + '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, + 'F' => 15, 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23, + 'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31, + 'W' => 32, 'X' => 33, 'Y' => 34, 'Z' => 35, ' ' => 36, '$' => 37, '%' => 38, '*' => 39, + '+' => 40, '-' => 41, '.' => 42, '/' => 43, ':' => 44]; + + $i = 0; + ++$data_counter; + while ($i < $data_length) { + if (($i % 2) == 0) { + $data_value[$data_counter] = $alphanumeric_character_hash[substr($qrcode_data_string, $i, 1)]; + $data_bits[$data_counter] = 6; + } else { + $data_value[$data_counter] = $data_value[$data_counter] * 45 + $alphanumeric_character_hash[substr($qrcode_data_string, $i, 1)]; + $data_bits[$data_counter] = 11; + ++$data_counter; + } + ++$i; + } + } + } else { + /* ---- numeric mode */ + + $codeword_num_plus = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]; + + $data_value[$data_counter] = 1; + ++$data_counter; + $data_value[$data_counter] = $data_length; + $data_bits[$data_counter] = 10; /* #version 1-9 */ + $codeword_num_counter_value = $data_counter; + + $i = 0; + ++$data_counter; + while ($i < $data_length) { + if (($i % 3) == 0) { + $data_value[$data_counter] = substr($qrcode_data_string, $i, 1); + $data_bits[$data_counter] = 4; + } else { + $data_value[$data_counter] = $data_value[$data_counter] * 10 + substr($qrcode_data_string, $i, 1); + if (($i % 3) == 1) { + $data_bits[$data_counter] = 7; + } else { + $data_bits[$data_counter] = 10; + ++$data_counter; + } + } + ++$i; + } + } + if (array_key_exists($data_counter, $data_bits) && $data_bits[$data_counter] > 0) { + ++$data_counter; + } + $i = 0; + $total_data_bits = 0; + while ($i < $data_counter) { + $total_data_bits += $data_bits[$i]; + ++$i; + } + + $ecc_character_hash = [ + 'L' => '1', + 'l' => '1', + 'M' => '0', + 'm' => '0', + 'Q' => '3', + 'q' => '3', + 'H' => '2', + 'h' => '2', + ]; + + if (!is_numeric($qrcode_error_correct)) { + $ec = @$ecc_character_hash[$qrcode_error_correct]; + } else { + $ec = $qrcode_error_correct; + } + + if (!$ec) { + $ec = 0; + } + + $max_data_bits = 0; + + $max_data_bits_array = [ + 0, 128, 224, 352, 512, 688, 864, 992, 1232, 1456, 1728, + 2032, 2320, 2672, 2920, 3320, 3624, 4056, 4504, 5016, 5352, + 5712, 6256, 6880, 7312, 8000, 8496, 9024, 9544, 10136, 10984, + 11640, 12328, 13048, 13800, 14496, 15312, 15936, 16816, 17728, 18672, + + 152, 272, 440, 640, 864, 1088, 1248, 1552, 1856, 2192, + 2592, 2960, 3424, 3688, 4184, 4712, 5176, 5768, 6360, 6888, + 7456, 8048, 8752, 9392, 10208, 10960, 11744, 12248, 13048, 13880, + 14744, 15640, 16568, 17528, 18448, 19472, 20528, 21616, 22496, 23648, + + 72, 128, 208, 288, 368, 480, 528, 688, 800, 976, + 1120, 1264, 1440, 1576, 1784, 2024, 2264, 2504, 2728, 3080, + 3248, 3536, 3712, 4112, 4304, 4768, 5024, 5288, 5608, 5960, + 6344, 6760, 7208, 7688, 7888, 8432, 8768, 9136, 9776, 10208, + + 104, 176, 272, 384, 496, 608, 704, 880, 1056, 1232, + 1440, 1648, 1952, 2088, 2360, 2600, 2936, 3176, 3560, 3880, + 4096, 4544, 4912, 5312, 5744, 6032, 6464, 6968, 7288, 7880, + 8264, 8920, 9368, 9848, 10288, 10832, 11408, 12016, 12656, 13328 + ]; + if (!is_numeric($qrcode_version)) { + $qrcode_version = 0; + } + if (!$qrcode_version) { + /* #--- auto version select */ + $i = 1 + 40 * $ec; + $j = $i + 39; + $qrcode_version = 1; + while ($i <= $j) { + if (($max_data_bits_array[$i]) >= $total_data_bits + $codeword_num_plus[$qrcode_version]) { + $max_data_bits = $max_data_bits_array[$i]; + break; + } + ++$i; + ++$qrcode_version; + } + } else { + $max_data_bits = $max_data_bits_array[$qrcode_version + 40 * $ec]; + } + if ($qrcode_version > $version_ul) { + throw new Exception('QRCode : version too large'); + } + + $total_data_bits += $codeword_num_plus[$qrcode_version]; + $data_bits[$codeword_num_counter_value] += $codeword_num_plus[$qrcode_version]; + + $max_codewords_array = [0, 26, 44, 70, 100, 134, 172, 196, 242, + 292, 346, 404, 466, 532, 581, 655, 733, 815, 901, 991, 1085, 1156, + 1258, 1364, 1474, 1588, 1706, 1828, 1921, 2051, 2185, 2323, 2465, + 2611, 2761, 2876, 3034, 3196, 3362, 3532, 3706]; + + $max_codewords = $max_codewords_array[$qrcode_version]; + $max_modules_1side = 17 + ($qrcode_version << 2); + + $matrix_remain_bit = [0, 0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0]; + + /* ---- read version ECC data file */ + + $byte_num = $matrix_remain_bit[$qrcode_version] + ($max_codewords << 3); + $filename = $path . '/qrv' . $qrcode_version . '_' . $ec . '.dat'; + $fp1 = fopen($filename, 'rb'); + $matx = fread($fp1, $byte_num); + $maty = fread($fp1, $byte_num); + $masks = fread($fp1, $byte_num); + $fi_x = fread($fp1, 15); + $fi_y = fread($fp1, 15); + $rs_ecc_codewords = ord(fread($fp1, 1)); + $rso = fread($fp1, 128); + fclose($fp1); + + $matrix_x_array = unpack('C*', $matx); + $matrix_y_array = unpack('C*', $maty); + $mask_array = unpack('C*', $masks); + + $rs_block_order = unpack('C*', $rso); + + $format_information_x2 = unpack('C*', $fi_x); + $format_information_y2 = unpack('C*', $fi_y); + + $format_information_x1 = [0, 1, 2, 3, 4, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8]; + $format_information_y1 = [8, 8, 8, 8, 8, 8, 8, 8, 7, 5, 4, 3, 2, 1, 0]; + + $max_data_codewords = ($max_data_bits >> 3); + + $filename = $path . '/rsc' . $rs_ecc_codewords . '.dat'; + $fp0 = fopen($filename, 'rb'); + $i = 0; + $rs_cal_table_array = []; + while ($i < 256) { + $rs_cal_table_array[$i] = fread($fp0, $rs_ecc_codewords); + ++$i; + } + fclose($fp0); + + /* --- set terminator */ + + if ($total_data_bits <= $max_data_bits - 4) { + $data_value[$data_counter] = 0; + $data_bits[$data_counter] = 4; + } else { + if ($total_data_bits < $max_data_bits) { + $data_value[$data_counter] = 0; + $data_bits[$data_counter] = $max_data_bits - $total_data_bits; + } else { + if ($total_data_bits > $max_data_bits) { + throw new \OverflowException('QRCode: overflow error'); + } + } + } + + /* ----divide data by 8bit */ + + $i = 0; + $codewords_counter = 0; + $codewords[0] = 0; + $remaining_bits = 8; + + while ($i <= $data_counter) { + $buffer = @$data_value[$i]; + $buffer_bits = @$data_bits[$i]; + + $flag = 1; + while ($flag) { + if ($remaining_bits > $buffer_bits) { + $codewords[$codewords_counter] = ((@$codewords[$codewords_counter] << $buffer_bits) | $buffer); + $remaining_bits -= $buffer_bits; + $flag = 0; + } else { + $buffer_bits -= $remaining_bits; + $codewords[$codewords_counter] = (($codewords[$codewords_counter] << $remaining_bits) | ($buffer >> $buffer_bits)); + + if ($buffer_bits == 0) { + $flag = 0; + } else { + $buffer = ($buffer & ((1 << $buffer_bits) - 1)); + $flag = 1; + } + + ++$codewords_counter; + if ($codewords_counter < $max_data_codewords - 1) { + $codewords[$codewords_counter] = 0; + } + $remaining_bits = 8; + } + } + ++$i; + } + if ($remaining_bits != 8) { + $codewords[$codewords_counter] = $codewords[$codewords_counter] << $remaining_bits; + } else { + --$codewords_counter; + } + + /* ---- set padding character */ + + if ($codewords_counter < $max_data_codewords - 1) { + $flag = 1; + while ($codewords_counter < $max_data_codewords - 1) { + ++$codewords_counter; + if ($flag == 1) { + $codewords[$codewords_counter] = 236; + } else { + $codewords[$codewords_counter] = 17; + } + $flag = $flag * (-1); + } + } + + /* ---- RS-ECC prepare */ + + $i = 0; + $j = 0; + $rs_block_number = 0; + $rs_temp[0] = ''; + + while ($i < $max_data_codewords) { + $rs_temp[$rs_block_number] .= chr($codewords[$i]); + ++$j; + + if ($j >= $rs_block_order[$rs_block_number + 1] - $rs_ecc_codewords) { + $j = 0; + ++$rs_block_number; + $rs_temp[$rs_block_number] = ''; + } + ++$i; + } + + /* + # + # RS-ECC main + # + */ + + $rs_block_number = 0; + $rs_block_order_num = count($rs_block_order); + + while ($rs_block_number < $rs_block_order_num) { + $rs_codewords = $rs_block_order[$rs_block_number + 1]; + $rs_data_codewords = $rs_codewords - $rs_ecc_codewords; + + $rstemp = $rs_temp[$rs_block_number] . str_repeat(chr(0), $rs_ecc_codewords); + $padding_data = str_repeat(chr(0), $rs_data_codewords); + + $j = $rs_data_codewords; + while ($j > 0) { + $first = ord(substr($rstemp, 0, 1)); + + if ($first) { + $left_chr = substr($rstemp, 1); + $cal = $rs_cal_table_array[$first] . $padding_data; + $rstemp = $left_chr ^ $cal; + } else { + $rstemp = substr($rstemp, 1); + } + + --$j; + } + + $codewords = array_merge($codewords, unpack('C*', $rstemp)); + + ++$rs_block_number; + } + + /* ---- flash matrix */ + $matrix_content = []; + $i = 0; + while ($i < $max_modules_1side) { + $j = 0; + while ($j < $max_modules_1side) { + $matrix_content[$j][$i] = 0; + ++$j; + } + ++$i; + } + + /* --- attach data */ + + $i = 0; + while ($i < $max_codewords) { + $codeword_i = $codewords[$i]; + $j = 8; + while ($j >= 1) { + $codeword_bits_number = ($i << 3) + $j; + $matrix_content[$matrix_x_array[$codeword_bits_number]][$matrix_y_array[$codeword_bits_number]] = ((255 * ($codeword_i & 1)) ^ $mask_array[$codeword_bits_number]); + $codeword_i = $codeword_i >> 1; + --$j; + } + ++$i; + } + + $matrix_remain = $matrix_remain_bit[$qrcode_version]; + while ($matrix_remain) { + $remain_bit_temp = $matrix_remain + ($max_codewords << 3); + $matrix_content[$matrix_x_array[$remain_bit_temp]][$matrix_y_array[$remain_bit_temp]] = (255 ^ $mask_array[$remain_bit_temp]); + --$matrix_remain; + } + + #--- mask select + + $min_demerit_score = 0; + $hor_master = ''; + $ver_master = ''; + $k = 0; + while ($k < $max_modules_1side) { + $l = 0; + while ($l < $max_modules_1side) { + $hor_master = $hor_master . chr($matrix_content[$l][$k]); + $ver_master = $ver_master . chr($matrix_content[$k][$l]); + ++$l; + } + ++$k; + } + $i = 0; + $all_matrix = $max_modules_1side * $max_modules_1side; + $mask_number = 0; + while ($i < 8) { + $demerit_n1 = 0; + $ptn_temp = []; + $bit = 1 << $i; + $bit_r = (~$bit) & 255; + $bit_mask = str_repeat(chr($bit), $all_matrix); + $hor = $hor_master & $bit_mask; + $ver = $ver_master & $bit_mask; + + $ver_shift1 = $ver . str_repeat(chr(170), $max_modules_1side); + $ver_shift2 = str_repeat(chr(170), $max_modules_1side) . $ver; + $ver_shift1_0 = $ver . str_repeat(chr(0), $max_modules_1side); + $ver_shift2_0 = str_repeat(chr(0), $max_modules_1side) . $ver; + $ver_or = chunk_split(~($ver_shift1 | $ver_shift2), $max_modules_1side, chr(170)); + $ver_and = chunk_split(~($ver_shift1_0 & $ver_shift2_0), $max_modules_1side, chr(170)); + + $hor = chunk_split(~$hor, $max_modules_1side, chr(170)); + $ver = chunk_split(~$ver, $max_modules_1side, chr(170)); + $hor = $hor . chr(170) . $ver; + + $n1_search = '/' . str_repeat(chr(255), 5) . '+|' . str_repeat(chr($bit_r), 5) . '+/'; + $n3_search = chr($bit_r) . chr(255) . chr($bit_r) . chr($bit_r) . chr($bit_r) . chr(255) . chr($bit_r); + + $demerit_n3 = substr_count($hor, $n3_search) * 40; + $demerit_n4 = floor(abs(((100 * (substr_count($ver, chr($bit_r)) / ($byte_num))) - 50) / 5)) * 10; + + $n2_search1 = '/' . chr($bit_r) . chr($bit_r) . '+/'; + $n2_search2 = '/' . chr(255) . chr(255) . '+/'; + $demerit_n2 = 0; + preg_match_all($n2_search1, $ver_and, $ptn_temp); + foreach ($ptn_temp[0] as $str_temp) { + $demerit_n2 += (strlen($str_temp) - 1); + } + $ptn_temp = []; + preg_match_all($n2_search2, $ver_or, $ptn_temp); + foreach ($ptn_temp[0] as $str_temp) { + $demerit_n2 += (strlen($str_temp) - 1); + } + $demerit_n2 *= 3; + + $ptn_temp = []; + + preg_match_all($n1_search, $hor, $ptn_temp); + foreach ($ptn_temp[0] as $str_temp) { + $demerit_n1 += (strlen($str_temp) - 2); + } + + $demerit_score = $demerit_n1 + $demerit_n2 + $demerit_n3 + $demerit_n4; + + if ($demerit_score <= $min_demerit_score || $i == 0) { + $mask_number = $i; + $min_demerit_score = $demerit_score; + } + + ++$i; + } + + $mask_content = 1 << $mask_number; + + # --- format information + + $format_information_value = (($ec << 3) | $mask_number); + $format_information_array = ['101010000010010', '101000100100101', + '101111001111100', '101101101001011', '100010111111001', '100000011001110', + '100111110010111', '100101010100000', '111011111000100', '111001011110011', + '111110110101010', '111100010011101', '110011000101111', '110001100011000', + '110110001000001', '110100101110110', '001011010001001', '001001110111110', + '001110011100111', '001100111010000', '000011101100010', '000001001010101', + '000110100001100', '000100000111011', '011010101011111', '011000001101000', + '011111100110001', '011101000000110', '010010010110100', '010000110000011', + '010111011011010', '010101111101101']; + $i = 0; + while ($i < 15) { + $content = substr($format_information_array[$format_information_value], $i, 1); + + $matrix_content[$format_information_x1[$i]][$format_information_y1[$i]] = $content * 255; + $matrix_content[$format_information_x2[$i + 1]][$format_information_y2[$i + 1]] = $content * 255; + ++$i; + } + + $mib = $max_modules_1side + 8; + + if ($this->size == 0) { + $this->size = $mib * $qrcode_module_size; + if ($this->size > 1480) { + throw new Exception('QRCode: image size too large'); + } + } + + $image_width = $this->size + $this->padding * 2; + $image_height = $this->size + $this->padding * 2; + + if (!empty($this->label)) { + if (!function_exists('imagettfbbox')) { + throw new Exception('QRCode: missing function "imagettfbbox". Did you install the FreeType library?'); + } + $font_box = imagettfbbox($this->label_font_size, 0, $this->label_font_path, $this->label); + $label_width = (int)$font_box[2] - (int)$font_box[0]; + $label_height = (int)$font_box[0] - (int)$font_box[7]; + + if ($this->label_valign == self::LABEL_VALIGN_MIDDLE) { + $image_height += $label_height + $this->padding; + } else { + $image_height += $label_height; + } + } + + $output_image = imagecreate($image_width, $image_height); + imagecolorallocate($output_image, 255, 255, 255); + + $image_path = $image_path . '/qrv' . $qrcode_version . '.png'; + + $base_image = imagecreatefrompng($image_path); + $code_size = $this->size; + $module_size = function ($size = 1) use ($code_size, $base_image) { + return round($code_size / imagesx($base_image) * $size); + }; + + $col[1] = imagecolorallocate($base_image, 0, 0, 0); + $col[0] = imagecolorallocate($base_image, 255, 255, 255); + + $i = 4; + $mxe = 4 + $max_modules_1side; + $ii = 0; + while ($i < $mxe) { + $j = 4; + $jj = 0; + while ($j < $mxe) { + if ($matrix_content[$ii][$jj] & $mask_content) { + imagesetpixel($base_image, $i, $j, $col[1]); + } + ++$j; + ++$jj; + } + ++$i; + ++$ii; + } + + if ($this->draw_quiet_zone == true) { + imagecopyresampled($output_image, $base_image, $this->padding, $this->padding, 0, 0, $this->size, $this->size, $mib, $mib); + } else { + imagecopyresampled($output_image, $base_image, $this->padding, $this->padding, 4, 4, $this->size, $this->size, $mib - 8, $mib - 8); + } + + if ($this->draw_border == true) { + $border_width = $this->padding; + $border_height = $this->size + $this->padding - 1; + $border_color = imagecolorallocate($output_image, 0, 0, 0); + imagerectangle($output_image, $border_width, $border_width, $border_height, $border_height, $border_color); + } + + if (!empty($this->label)) { + // Label horizontal alignment + switch ($this->label_halign) { + case self::LABEL_HALIGN_LEFT: + $font_x = 0; + break; + + case self::LABEL_HALIGN_LEFT_BORDER: + $font_x = $this->padding; + break; + + case self::LABEL_HALIGN_LEFT_CODE: + if ($this->draw_quiet_zone == true) { + $font_x = $this->padding + $module_size(4); + } else { + $font_x = $this->padding; + } + break; + + case self::LABEL_HALIGN_RIGHT: + $font_x = $this->size + ($this->padding * 2) - $label_width; + break; + + case self::LABEL_HALIGN_RIGHT_BORDER: + $font_x = $this->size + $this->padding - $label_width; + break; + + case self::LABEL_HALIGN_RIGHT_CODE: + if ($this->draw_quiet_zone == true) { + $font_x = $this->size + $this->padding - $label_width - $module_size(4); + } else { + $font_x = $this->size + $this->padding - $label_width; + } + break; + + default: + $font_x = floor($image_width - $label_width) / 2; + } + + // Label vertical alignment + switch ($this->label_valign) { + case self::LABEL_VALIGN_TOP_NO_BORDER: + $font_y = $image_height - $this->padding - 1; + break; + + case self::LABEL_VALIGN_BOTTOM: + $font_y = $image_height; + break; + + default: + $font_y = $image_height - $this->padding; + } + + $label_bg_x1 = $font_x - $module_size(2); + $label_bg_y1 = $font_y - $label_height; + $label_bg_x2 = $font_x + $label_width + $module_size(2); + $label_bg_y2 = $font_y; + + $color = imagecolorallocate($output_image, 0, 0, 0); + $label_bg_color = imagecolorallocate($output_image, 255, 255, 255); + + imagefilledrectangle($output_image, $label_bg_x1, $label_bg_y1, $label_bg_x2, $label_bg_y2, $label_bg_color); + imagettftext($output_image, $this->label_font_size, 0, $font_x, $font_y, $color, $this->label_font_path, $this->label); + } + + $imagecolorset_function = new ReflectionFunction('imagecolorset'); + $allow_alpha = $imagecolorset_function->getNumberOfParameters() == 6; + + if ($this->color_background != null) { + $index = imagecolorclosest($output_image, 255, 255, 255); + if ($allow_alpha) { + imagecolorset($output_image, $index, $this->color_background['r'], $this->color_background['g'], $this->color_background['b'], $this->color_background['a']); + } else { + imagecolorset($output_image, $index, $this->color_background['r'], $this->color_background['g'], $this->color_background['b']); + } + } + + if ($this->color_foreground != null) { + $index = imagecolorclosest($output_image, 0, 0, 0); + if ($allow_alpha) { + imagecolorset($output_image, $index, $this->color_foreground['r'], $this->color_foreground['g'], $this->color_foreground['b'], $this->color_foreground['a']); + } else { + imagecolorset($output_image, $index, $this->color_foreground['r'], $this->color_foreground['g'], $this->color_foreground['b']); + } + } + + if (!empty($this->logo)) { + $output_image_org = $output_image; + $output_image = imagecreatetruecolor($image_width, $image_height); + imagecopy($output_image, $output_image_org, 0, 0, 0, 0, $image_width, $image_height); + + $logo_image = call_user_func('imagecreatefrom' . $this->image_type, $this->logo); + if (!$logo_image) { + throw new Exception('imagecreatefrom' . $this->image_type . ' ' . $this->logo . ' failed'); + } + $src_w = imagesx($logo_image); + $src_h = imagesy($logo_image); + + $dst_x = ($image_width - $this->logo_size) / 2; + $dst_y = ($this->size + $this->padding * 2 - $this->logo_size) / 2; + + $successful = imagecopyresampled($output_image, $logo_image, $dst_x, $dst_y, 0, 0, $this->logo_size, $this->logo_size, $src_w, $src_h); + if (!$successful) { + throw new Exception('add logo [image' . $this->format . '] failed.'); + } + imagedestroy($logo_image); + } + $this->image = $output_image; + } } diff --git a/classes/regex.php b/classes/regex.php index 0fff050c4..6f987cf9b 100644 --- a/classes/regex.php +++ b/classes/regex.php @@ -1,4 +1,4 @@ -get_query_id(); - - G::$DB->prepared_query(" - SELECT REPLACE(t.Name, '.', '_') - FROM tags AS t - INNER JOIN requests_tags AS rt ON t.ID = rt.TagID - WHERE rt.RequestID = ?", $RequestID); - $TagList = G::$DB->collect(0, false); - - G::$DB->prepared_query(' - REPLACE INTO sphinx_requests_delta ( - ID, UserID, TimeAdded, LastVote, CategoryID, Title, - Year, ReleaseType, CatalogueNumber, RecordLabel, BitrateList, - FormatList, MediaList, LogCue, FillerID, TorrentID, - TimeFilled, Visible, Votes, Bounty, TagList) - SELECT - ID, r.UserID, UNIX_TIMESTAMP(TimeAdded) AS TimeAdded, - UNIX_TIMESTAMP(LastVote) AS LastVote, CategoryID, Title, - Year, ReleaseType, CatalogueNumber, RecordLabel, BitrateList, - FormatList, MediaList, LogCue, FillerID, TorrentID, - UNIX_TIMESTAMP(TimeFilled) AS TimeFilled, Visible, - COUNT(rv.UserID) AS Votes, SUM(rv.Bounty) >> 10 AS Bounty, - ? - FROM requests AS r - LEFT JOIN requests_votes AS rv ON rv.RequestID = r.ID - WHERE r.ID = ? - GROUP BY r.ID', - implode(' ', $TagList), $RequestID); - G::$DB->prepared_query(" - UPDATE sphinx_requests_delta - SET ArtistList = ( - SELECT GROUP_CONCAT(aa.Name SEPARATOR ' ') - FROM requests_artists AS ra - JOIN artists_alias AS aa ON aa.AliasID = ra.AliasID - WHERE ra.RequestID = $RequestID - GROUP BY NULL - ) - WHERE ID = ?", $RequestID); - G::$DB->set_query_id($QueryID); - - G::$Cache->delete_value("request_$RequestID"); - } - - - - /** - * Function to get data from an array of $RequestIDs. Order of keys doesn't matter (let's keep it that way). - * - * @param array $RequestIDs - * @param boolean $Return if set to false, data won't be returned (ie. if we just want to prime the cache.) - * @return The array of requests. - * Format: array(RequestID => Associative array) - * To see what's exactly inside each associate array, peek inside the function. It won't bite. - */ - // - //In places where the output from this is merged with sphinx filters, it will be in a different order. - public static function get_requests($RequestIDs, $Return = true) { - $Found = $NotFound = array_fill_keys($RequestIDs, false); - // Try to fetch the requests from the cache first. - foreach ($RequestIDs as $i => $RequestID) { - if (!is_number($RequestID)) { - unset($RequestIDs[$i], $Found[$GroupID], $NotFound[$GroupID]); - continue; - } - $Data = G::$Cache->get_value("request_$RequestID"); - if (!empty($Data)) { - unset($NotFound[$RequestID]); - $Found[$RequestID] = $Data; - } - } - // Make sure there's something in $RequestIDs, otherwise the SQL will break - if (count($RequestIDs) === 0) { - return array(); - } - $IDs = implode(',', array_keys($NotFound)); - - /* - Don't change without ensuring you change everything else that uses get_requests() - */ - - if (count($NotFound) > 0) { - $QueryID = G::$DB->get_query_id(); - - G::$DB->query(" - SELECT - ID, - UserID, - TimeAdded, - LastVote, - CategoryID, - Title, - Year, - Image, - Description, - CatalogueNumber, - RecordLabel, - ReleaseType, - BitrateList, - FormatList, - MediaList, - LogCue, - Checksum, - FillerID, - TorrentID, - TimeFilled, - GroupID, - OCLC - FROM requests - WHERE ID IN ($IDs) - ORDER BY ID"); - $Requests = G::$DB->to_array(false, MYSQLI_ASSOC, true); - $Tags = self::get_tags(G::$DB->collect('ID', false)); - foreach ($Requests as $Request) { - unset($NotFound[$Request['ID']]); - $Request['Tags'] = isset($Tags[$Request['ID']]) ? $Tags[$Request['ID']] : []; - $Found[$Request['ID']] = $Request; - G::$Cache->cache_value('request_'.$Request['ID'], $Request, 0); - } - G::$DB->set_query_id($QueryID); - - // Orphan requests. There shouldn't ever be any - if (count($NotFound) > 0) { - foreach (array_keys($NotFound) as $GroupID) { - unset($Found[$GroupID]); - } - } - } - - if ($Return) { // If we're interested in the data, and not just caching it - return $Found; - } - } - - /** - * Return a single request. Wrapper for get_requests - * - * @param int $RequestID - * @return request array or false if request doesn't exist. See get_requests for a description of the format - */ - public static function get_request($RequestID) { - $Request = self::get_requests([$RequestID]); - if (isset($Request[$RequestID])) { - return $Request[$RequestID]; - } - return false; - } - - public static function get_artists($RequestID) { - $Artists = G::$Cache->get_value("request_artists_$RequestID"); - if (is_array($Artists)) { - $Results = $Artists; - } else { - $Results = []; - $QueryID = G::$DB->get_query_id(); - G::$DB->prepared_query(' - SELECT - ra.ArtistID, - aa.Name, - ra.Importance - FROM requests_artists AS ra - INNER JOIN artists_alias AS aa ON ra.AliasID = aa.AliasID - WHERE ra.RequestID = ? - ORDER BY ra.Importance ASC, aa.Name ASC', - $RequestID); - $ArtistRaw = G::$DB->to_array(); - G::$DB->set_query_id($QueryID); - foreach ($ArtistRaw as $ArtistRow) { - list($ArtistID, $ArtistName, $ArtistImportance) = $ArtistRow; - $Results[$ArtistImportance][] = array('id' => $ArtistID, 'name' => $ArtistName); - } - G::$Cache->cache_value("request_artists_$RequestID", $Results); - } - return $Results; - } - - public static function get_tags($RequestIDs) { - if (empty($RequestIDs)) { - return []; - } - if (is_array($RequestIDs)) { - $RequestIDs = implode(',', $RequestIDs); - } - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - rt.RequestID, - rt.TagID, - t.Name - FROM requests_tags AS rt - JOIN tags AS t ON rt.TagID = t.ID - WHERE rt.RequestID IN ($RequestIDs) - ORDER BY rt.TagID ASC"); - $Tags = G::$DB->to_array(false, MYSQLI_NUM, false); - G::$DB->set_query_id($QueryID); - $Results = array(); - foreach ($Tags as $TagsRow) { - list($RequestID, $TagID, $TagName) = $TagsRow; - $Results[$RequestID][$TagID] = $TagName; - } - return $Results; - } - - public static function get_votes_array($RequestID) { - $RequestVotes = G::$Cache->get_value("request_votes_$RequestID"); - if (!is_array($RequestVotes)) { - $QueryID = G::$DB->get_query_id(); - G::$DB->prepared_query(' - SELECT - rv.UserID, - rv.Bounty, - u.Username - FROM requests_votes AS rv - LEFT JOIN users_main AS u ON (u.ID = rv.UserID) - WHERE rv.RequestID = ? - ORDER BY rv.Bounty DESC', - $RequestID); - if (!G::$DB->has_results()) { - return [ - 'TotalBounty' => 0, - 'Voters' => [] - ]; - } - $Votes = G::$DB->to_array(); - - $RequestVotes = []; - $RequestVotes['TotalBounty'] = array_sum(G::$DB->collect('Bounty')); - - foreach ($Votes as $Vote) { - list($UserID, $Bounty, $Username) = $Vote; - $VoteArray = []; - $VotesArray[] = ['UserID' => $UserID, 'Username' => $Username, 'Bounty' => $Bounty]; - } - - $RequestVotes['Voters'] = $VotesArray; - G::$Cache->cache_value("request_votes_$RequestID", $RequestVotes); - G::$DB->set_query_id($QueryID); - } - return $RequestVotes; - } + /** + * Update the sphinx requests delta table for a request. + * + * @param $RequestID + */ + public static function update_sphinx_requests($RequestID) { + $QueryID = G::$DB->get_query_id(); + + G::$DB->prepared_query(" + SELECT REPLACE(t.Name, '.', '_') + FROM tags AS t + INNER JOIN requests_tags AS rt ON t.ID = rt.TagID + WHERE rt.RequestID = ?", $RequestID); + $TagList = G::$DB->collect(0, false); + + G::$DB->prepared_query(' + REPLACE INTO sphinx_requests_delta ( + ID, UserID, TimeAdded, LastVote, CategoryID, Title, + Year, ReleaseType, CatalogueNumber, RecordLabel, BitrateList, + FormatList, MediaList, LogCue, FillerID, TorrentID, + TimeFilled, Visible, Votes, Bounty, TagList) + SELECT + ID, r.UserID, UNIX_TIMESTAMP(TimeAdded) AS TimeAdded, + UNIX_TIMESTAMP(LastVote) AS LastVote, CategoryID, Title, + Year, ReleaseType, CatalogueNumber, RecordLabel, BitrateList, + FormatList, MediaList, LogCue, FillerID, TorrentID, + UNIX_TIMESTAMP(TimeFilled) AS TimeFilled, Visible, + COUNT(rv.UserID) AS Votes, SUM(rv.Bounty) >> 10 AS Bounty, + ? + FROM requests AS r + LEFT JOIN requests_votes AS rv ON rv.RequestID = r.ID + WHERE r.ID = ? + GROUP BY r.ID', + implode(' ', $TagList), $RequestID); + G::$DB->prepared_query(" + UPDATE sphinx_requests_delta + SET ArtistList = ( + SELECT GROUP_CONCAT(aa.Name SEPARATOR ' ') + FROM requests_artists AS ra + JOIN artists_alias AS aa ON aa.AliasID = ra.AliasID + WHERE ra.RequestID = $RequestID + GROUP BY NULL + ) + WHERE ID = ?", $RequestID); + G::$DB->set_query_id($QueryID); + + G::$Cache->delete_value("request_$RequestID"); + } + + + + /** + * Function to get data from an array of $RequestIDs. Order of keys doesn't matter (let's keep it that way). + * + * @param array $RequestIDs + * @param boolean $Return if set to false, data won't be returned (ie. if we just want to prime the cache.) + * @return The array of requests. + * Format: array(RequestID => Associative array) + * To see what's exactly inside each associate array, peek inside the function. It won't bite. + */ + // + //In places where the output from this is merged with sphinx filters, it will be in a different order. + public static function get_requests($RequestIDs, $Return = true) { + $Found = $NotFound = array_fill_keys($RequestIDs, false); + // Try to fetch the requests from the cache first. + foreach ($RequestIDs as $i => $RequestID) { + if (!is_number($RequestID)) { + unset($RequestIDs[$i], $Found[$GroupID], $NotFound[$GroupID]); + continue; + } + $Data = G::$Cache->get_value("request_$RequestID"); + if (!empty($Data)) { + unset($NotFound[$RequestID]); + $Found[$RequestID] = $Data; + } + } + // Make sure there's something in $RequestIDs, otherwise the SQL will break + if (count($RequestIDs) === 0) { + return []; + } + $IDs = implode(',', array_keys($NotFound)); + + /* + Don't change without ensuring you change everything else that uses get_requests() + */ + + if (count($NotFound) > 0) { + $QueryID = G::$DB->get_query_id(); + + G::$DB->query(" + SELECT + ID, + UserID, + TimeAdded, + LastVote, + CategoryID, + Title, + Year, + Image, + Description, + CatalogueNumber, + RecordLabel, + ReleaseType, + BitrateList, + FormatList, + MediaList, + LogCue, + Checksum, + FillerID, + TorrentID, + TimeFilled, + GroupID, + OCLC + FROM requests + WHERE ID IN ($IDs) + ORDER BY ID"); + $Requests = G::$DB->to_array(false, MYSQLI_ASSOC, true); + $Tags = self::get_tags(G::$DB->collect('ID', false)); + foreach ($Requests as $Request) { + unset($NotFound[$Request['ID']]); + $Request['Tags'] = isset($Tags[$Request['ID']]) ? $Tags[$Request['ID']] : []; + $Found[$Request['ID']] = $Request; + G::$Cache->cache_value('request_'.$Request['ID'], $Request, 0); + } + G::$DB->set_query_id($QueryID); + + // Orphan requests. There shouldn't ever be any + if (count($NotFound) > 0) { + foreach (array_keys($NotFound) as $GroupID) { + unset($Found[$GroupID]); + } + } + } + + if ($Return) { // If we're interested in the data, and not just caching it + return $Found; + } + } + + /** + * Return a single request. Wrapper for get_requests + * + * @param int $RequestID + * @return request array or false if request doesn't exist. See get_requests for a description of the format + */ + public static function get_request($RequestID) { + $Request = self::get_requests([$RequestID]); + if (isset($Request[$RequestID])) { + return $Request[$RequestID]; + } + return false; + } + + public static function get_artists($RequestID) { + $Artists = G::$Cache->get_value("request_artists_$RequestID"); + if (is_array($Artists)) { + $Results = $Artists; + } else { + $Results = []; + $QueryID = G::$DB->get_query_id(); + G::$DB->prepared_query(' + SELECT + ra.ArtistID, + aa.Name, + ra.Importance + FROM requests_artists AS ra + INNER JOIN artists_alias AS aa ON ra.AliasID = aa.AliasID + WHERE ra.RequestID = ? + ORDER BY ra.Importance ASC, aa.Name ASC', + $RequestID); + $ArtistRaw = G::$DB->to_array(); + G::$DB->set_query_id($QueryID); + foreach ($ArtistRaw as $ArtistRow) { + list($ArtistID, $ArtistName, $ArtistImportance) = $ArtistRow; + $Results[$ArtistImportance][] = array('id' => $ArtistID, 'name' => $ArtistName); + } + G::$Cache->cache_value("request_artists_$RequestID", $Results); + } + return $Results; + } + + public static function get_tags($RequestIDs) { + if (empty($RequestIDs)) { + return []; + } + if (is_array($RequestIDs)) { + $RequestIDs = implode(',', $RequestIDs); + } + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + rt.RequestID, + rt.TagID, + t.Name + FROM requests_tags AS rt + JOIN tags AS t ON rt.TagID = t.ID + WHERE rt.RequestID IN ($RequestIDs) + ORDER BY rt.TagID ASC"); + $Tags = G::$DB->to_array(false, MYSQLI_NUM, false); + G::$DB->set_query_id($QueryID); + $Results = []; + foreach ($Tags as $TagsRow) { + list($RequestID, $TagID, $TagName) = $TagsRow; + $Results[$RequestID][$TagID] = $TagName; + } + return $Results; + } + + public static function get_votes_array($RequestID) { + $RequestVotes = G::$Cache->get_value("request_votes_$RequestID"); + if (!is_array($RequestVotes)) { + $QueryID = G::$DB->get_query_id(); + G::$DB->prepared_query(' + SELECT + rv.UserID, + rv.Bounty, + u.Username + FROM requests_votes AS rv + LEFT JOIN users_main AS u ON (u.ID = rv.UserID) + WHERE rv.RequestID = ? + ORDER BY rv.Bounty DESC', + $RequestID); + if (!G::$DB->has_results()) { + return [ + 'TotalBounty' => 0, + 'Voters' => [] + ]; + } + $Votes = G::$DB->to_array(); + + $RequestVotes = []; + $RequestVotes['TotalBounty'] = array_sum(G::$DB->collect('Bounty')); + + foreach ($Votes as $Vote) { + list($UserID, $Bounty, $Username) = $Vote; + $VoteArray = []; + $VotesArray[] = ['UserID' => $UserID, 'Username' => $Username, 'Bounty' => $Bounty]; + } + + $RequestVotes['Voters'] = $VotesArray; + G::$Cache->cache_value("request_votes_$RequestID", $RequestVotes); + G::$DB->set_query_id($QueryID); + } + return $RequestVotes; + } } diff --git a/classes/revisionhistory.class.php b/classes/revisionhistory.class.php index e76d5847d..487575990 100644 --- a/classes/revisionhistory.class.php +++ b/classes/revisionhistory.class.php @@ -1,29 +1,29 @@ -get_query_id(); - G::$DB->query(" - SELECT - RevisionID, - Summary, - Time, - UserID - FROM $Table - WHERE PageID = $PageID - ORDER BY RevisionID DESC"); - $Ret = G::$DB->to_array(); - G::$DB->set_query_id($QueryID); - return $Ret; - } + /** + * Read the revision history of an artist or torrent page + * @param string $Page artists or torrents + * @param in $PageID + * @return array + */ + public static function get_revision_history($Page, $PageID) { + if ($Page == 'artists') { + $Table = 'wiki_artists'; + } else { + $Table = 'wiki_torrents'; + } + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + RevisionID, + Summary, + Time, + UserID + FROM $Table + WHERE PageID = $PageID + ORDER BY RevisionID DESC"); + $Ret = G::$DB->to_array(); + G::$DB->set_query_id($QueryID); + return $Ret; + } } diff --git a/classes/revisionhistoryview.class.php b/classes/revisionhistoryview.class.php index 062ded06e..59e38fe23 100644 --- a/classes/revisionhistoryview.class.php +++ b/classes/revisionhistoryview.class.php @@ -1,41 +1,41 @@ - - - - - - - - - + + + + + + + - - - - - - - -
    RevisionDateUserSummary
    RevisionDateUserSummary
    - #$RevisionID" ?> - - - - - - -
    -"> + + #$RevisionID" ?> + + + + + + + + + + + + + +Staff PM'; - $irc = 'IRC'; - $vpns_article = 'Proxy/VPN Tips'; - $ips_article = 'Multiple IPs'; - $autofl_article = 'Freeleech Autosnatching Policy'; - $bugs_article = 'Responsible Disclosure Policy'; - $golden_rules = array( - [ 'n' => "1.1", - 'short' => "One account per person, per lifetime.", - 'long' => "Users are allowed one account per lifetime. If your account is disabled, contact staff in ${disabled_channel} on ${irc}. Never make another account, you will be disabled without question." ], - [ 'n' => "1.2", - 'short' => "Do not trade, sell, give away, or offer accounts.", - 'long' => "If you no longer wish to use your account, send a ${staffpm} and request that your account be disabled." ], - [ 'n' => "1.3", - 'short' => "Do not share accounts.", - 'long' => "Accounts are for personal use only. Granting access to your account in any way (e.g., shared login details, external programs) is prohibited. Invite friends or direct them to the interview channel (#recruitment)." ], - [ 'n' => "1.4", - 'short' => "Do not let your account become inactive.", - 'long' => "You agree to log into the site regularly in order to keep your account in good standing. Failure to do so will result in your account being disabled. See Account Inactivity for more information." ], - [ 'n' => "2.1", - 'short' => "Do not invite bad users.", - 'long' => "You are responsible for your invitees. You will not be punished if your invitees fail to maintain required share ratios, but invitees who break golden rules will place your invite privileges and account at risk." ], - [ 'n' => "2.2", - 'short' => "Do not trade, sell, publicly give away, or publicly offer invites.", - 'long' => "Only invite people you know and trust. Do not offer invites via other trackers, forums, social media, or other public locations. Responding to public invite requests is prohibited. Exception: Staff-designated recruiters may offer invites in approved locations." ], - [ 'n' => "2.3", - 'short' => "Do not request invites or accounts.", - 'long' => "Requesting invites to—or accounts on—${site_name} or other trackers is prohibited. Invites may be offered, but not requested, in the Invites forum (restricted to the Elite class and above). You may request invites by messaging users only when they have offered them in the Invites Forum. Unsolicited invite requests, even by private message, are prohibited." ], - [ 'n' => "3.1", - 'short' => "Do not engage in ratio manipulation.", - 'long' => "Transferring buffer—or increasing your buffer—through unintended uses of the BitTorrent protocol or site features (e.g., request abuse) constitutes ratio manipulation. When in doubt, send a ${staffpm} asking for more information." ], - [ 'n' => "3.2", - 'short' => "Do not report incorrect data to the tracker (i.e., cheating).", - 'long' => "Reporting incorrect data to the tracker constitutes cheating, whether it is accomplished through the use of a modified \"cheat client\" or through manipulation of an approved client." ], - [ 'n' => "3.3", - 'short' => "Do not use unapproved clients.", - 'long' => "Your client must be listed on the Client Whitelist. You must not use clients that have been modified in any way. Developers interested in testing unstable clients must receive staff approval prior to testing." ], - [ 'n' => "3.4", - 'short' => "Do not modify ${site_name} .torrent files.", - 'long' => "Embedding non-${site_name} announce URLs in ${site_name} .torrents is prohibited. Doing so causes false data to be reported and will be interpreted as cheating. This applies to standalone .torrent files and .torrent files that have been loaded into a client." ], - [ 'n' => "3.5", - 'short' => "Do not share .torrent files or your passkey.", - 'long' => "Embedded in each ${site_name} .torrent file is an announce URL containing your personal passkey. Passkeys enable users to report stats to the tracker." ], - [ 'n' => "4.1", - 'short' => "Do not blackmail, threaten, or expose fellow users or staff.", - 'long' => "Exposing or threatening to expose private information about users for any reason is prohibited. Private information includes, but is not limited to, personally identifying information (e.g., names, records, biographical details, photos). Information that has not been openly volunteered by a user should not be discussed or shared without permission. This includes private information collected via investigations into openly volunteered information (e.g., Google search results)." ], - [ 'n' => "4.2", - 'short' => "Do not scam or defraud.", - 'long' => "Scams (e.g., phishing) of any kind are prohibited." ], - [ 'n' => "4.3", - 'short' => "Do not disrespect staff decisions.", - 'long' => "Disagreements must be discussed privately with the deciding moderator. If the moderator has retired or is unavailable, you may send a ${staffpm}. Do not contact multiple moderators hoping to find one amenable to your cause; however, you may contact a site administrator if you require a second opinion. Options for contacting staff include private message, Staff PM, and #help on ${irc}." ], - [ 'n' => "4.4", - 'short' => "Do not impersonate staff.", - 'long' => "Impersonating staff or official service accounts (e.g., Hermes) on-site, off-site, or on IRC is prohibited. Deceptively misrepresenting staff decisions is also prohibited." ], - [ 'n' => "4.5", - 'short' => "Do not backseat moderate.", - 'long' => "\"Backseat moderation\" occurs when users police other users. Confronting, provoking, or chastising users suspected of violating rules—or users suspected of submitting reports—is prohibited. Submit a report if you see a rule violation." ], - [ 'n' => "4.6", - 'short' => "Do not request special events.", - 'long' => "Special events (e.g., freeleech, neutral leech, picks) are launched at the discretion of the staff. They do not adhere to a fixed schedule, and may not be requested by users." ], - [ 'n' => "4.7", - 'short' => "Do not harvest user-identifying information.", - 'long' => "It is prohibited to use ${site_name}'s services to harvest user-identifying information of any kind (e.g., IP addresses, personal links) through the use of scripts, exploits, or other techniques." ], - [ 'n' => "4.8", - 'short' => "Do not use ${site_name}'s services (including the tracker, website, and IRC network) for commercial gain.", - 'long' => "Commercializing services provided by or code maintained by ${site_name} (e.g., Gazelle, Ocelot) is prohibited. Commercializing content provided by ${site_name} users via the aforementioned services (e.g., user torrent data) is prohibited. Referral schemes, financial solicitations, and money offers are also prohibited." ], - [ 'n' => "5.1", - 'short' => "Do not browse ${site_name} using any free proxy or VPN service.", - 'long' => "You may browse the site through a VPN/proxy only if you have paid for this service. This includes (for example) self-hosted VPNs or proxies, services like NordVPN and VPNs or proxies that come with a seedbox. When in doubt, please send a ${staffpm}. The use of Tor for browsing the site is not allowed." ], - [ 'n' => "5.2", - 'short' => "Do not abuse automated site access.", - 'long' => "All automated site access must be done through the API. API use is limited to 5 requests within any 10-second window. Scripts and other automated processes must not scrape the site's HTML pages. When in doubt, seek advice from staff." ], - [ 'n' => "5.3", - 'short' => "Do not autosnatch freeleech torrents.", - 'long' => "The automatic snatching of freeleech torrents using any method involving little or no user-input (e.g., API-based scripts, log or site scraping, etc.) is prohibited. See ${site_name}'s ${autofl_article} article for more information." ], - [ 'n' => "6.1", - 'short' => "Do not seek or exploit live bugs for any reason.", - 'long' => "Seeking or exploiting bugs in the live site (as opposed to a local development environment) is prohibited. If you discover a critical bug or security vulnerability, immediately report it in accordance with ${site_name}'s ${bugs_article}. Non-critical bugs can be reported in the Bugs Forum." ], - [ 'n' => "6.2", - 'short' => "Do not publish exploits.", - 'long' => "The publication, organization, dissemination, sharing, technical discussion, or technical facilitation of exploits is prohibited at staff discretion. Exploits are defined as unanticipated or unaccepted uses of internal, external, non-profit, or for-profit services. Exploits are subject to reclassification at any time." ], - [ 'n' => "7.0", - 'short' => "Be respectful to all staff members.", - 'long' => "Staff on ${site_name} are volunteers who dedicate their time in order to keep the site running, without receiving any compensation. Being disrespectful to them is prohibited, and might result in a warning or worse." ], - [ 'n' => "7.1", - 'short' => "Staff have the final word on rule interpretations.", - 'long' => "All rules on ${site_name} may be subject to different interpretations. Since the staff wrote these rules, their interpretation is final. If you need clarification on a rule, or if you think a rule should be restated, please send a ${staffpm}." ] - ); - echo "
      \n"; - foreach($golden_rules as $gr) { - $r_link = "gr${gr['n']}"; - echo "
    • " . - "${gr['n']}." . - '
      ' . - '
      ' . - $gr['short'] . - '
      ' . - '
      ' . - $gr['long'] . - '
      ' . - '
      ' . - "
    • \n"; - } - echo "
    \n"; - } + /** + * Displays the site's "Golden Rules". + * + */ + public static function display_golden_rules() { + $site_name = SITE_NAME; + $disabled_channel = BOT_DISABLED_CHAN; + $staffpm = 'Staff PM'; + $irc = 'IRC'; + $vpns_article = 'Proxy/VPN Tips'; + $ips_article = 'Multiple IPs'; + $autofl_article = 'Freeleech Autosnatching Policy'; + $bugs_article = 'Responsible Disclosure Policy'; + $golden_rules = array( + [ 'n' => "1.1", + 'short' => "One account per person, per lifetime.", + 'long' => "Users are allowed one account per lifetime. If your account is disabled, contact staff in ${disabled_channel} on ${irc}. Never make another account, you will be disabled without question." ], + [ 'n' => "1.2", + 'short' => "Do not trade, sell, give away, or offer accounts.", + 'long' => "If you no longer wish to use your account, send a ${staffpm} and request that your account be disabled." ], + [ 'n' => "1.3", + 'short' => "Do not share accounts.", + 'long' => "Accounts are for personal use only. Granting access to your account in any way (e.g., shared login details, external programs) is prohibited. Invite friends or direct them to the interview channel (#recruitment)." ], + [ 'n' => "1.4", + 'short' => "Do not let your account become inactive.", + 'long' => "You agree to log into the site regularly in order to keep your account in good standing. Failure to do so will result in your account being disabled. See Account Inactivity for more information." ], + [ 'n' => "2.1", + 'short' => "Do not invite bad users.", + 'long' => "You are responsible for your invitees. You will not be punished if your invitees fail to maintain required share ratios, but invitees who break golden rules will place your invite privileges and account at risk." ], + [ 'n' => "2.2", + 'short' => "Do not trade, sell, publicly give away, or publicly offer invites.", + 'long' => "Only invite people you know and trust. Do not offer invites via other trackers, forums, social media, or other public locations. Responding to public invite requests is prohibited. Exception: Staff-designated recruiters may offer invites in approved locations." ], + [ 'n' => "2.3", + 'short' => "Do not request invites or accounts.", + 'long' => "Requesting invites to—or accounts on—${site_name} or other trackers is prohibited. Invites may be offered, but not requested, in the Invites forum (restricted to the Elite class and above). You may request invites by messaging users only when they have offered them in the Invites Forum. Unsolicited invite requests, even by private message, are prohibited." ], + [ 'n' => "3.1", + 'short' => "Do not engage in ratio manipulation.", + 'long' => "Transferring buffer—or increasing your buffer—through unintended uses of the BitTorrent protocol or site features (e.g., request abuse) constitutes ratio manipulation. When in doubt, send a ${staffpm} asking for more information." ], + [ 'n' => "3.2", + 'short' => "Do not report incorrect data to the tracker (i.e., cheating).", + 'long' => "Reporting incorrect data to the tracker constitutes cheating, whether it is accomplished through the use of a modified \"cheat client\" or through manipulation of an approved client." ], + [ 'n' => "3.3", + 'short' => "Do not use unapproved clients.", + 'long' => "Your client must be listed on the Client Whitelist. You must not use clients that have been modified in any way. Developers interested in testing unstable clients must receive staff approval prior to testing." ], + [ 'n' => "3.4", + 'short' => "Do not modify ${site_name} .torrent files.", + 'long' => "Embedding non-${site_name} announce URLs in ${site_name} .torrents is prohibited. Doing so causes false data to be reported and will be interpreted as cheating. This applies to standalone .torrent files and .torrent files that have been loaded into a client." ], + [ 'n' => "3.5", + 'short' => "Do not share .torrent files or your passkey.", + 'long' => "Embedded in each ${site_name} .torrent file is an announce URL containing your personal passkey. Passkeys enable users to report stats to the tracker." ], + [ 'n' => "4.1", + 'short' => "Do not blackmail, threaten, or expose fellow users or staff.", + 'long' => "Exposing or threatening to expose private information about users for any reason is prohibited. Private information includes, but is not limited to, personally identifying information (e.g., names, records, biographical details, photos). Information that has not been openly volunteered by a user should not be discussed or shared without permission. This includes private information collected via investigations into openly volunteered information (e.g., Google search results)." ], + [ 'n' => "4.2", + 'short' => "Do not scam or defraud.", + 'long' => "Scams (e.g., phishing) of any kind are prohibited." ], + [ 'n' => "4.3", + 'short' => "Do not disrespect staff decisions.", + 'long' => "Disagreements must be discussed privately with the deciding moderator. If the moderator has retired or is unavailable, you may send a ${staffpm}. Do not contact multiple moderators hoping to find one amenable to your cause; however, you may contact a site administrator if you require a second opinion. Options for contacting staff include private message, Staff PM, and #help on ${irc}." ], + [ 'n' => "4.4", + 'short' => "Do not impersonate staff.", + 'long' => "Impersonating staff or official service accounts (e.g., Hermes) on-site, off-site, or on IRC is prohibited. Deceptively misrepresenting staff decisions is also prohibited." ], + [ 'n' => "4.5", + 'short' => "Do not backseat moderate.", + 'long' => "\"Backseat moderation\" occurs when users police other users. Confronting, provoking, or chastising users suspected of violating rules—or users suspected of submitting reports—is prohibited. Submit a report if you see a rule violation." ], + [ 'n' => "4.6", + 'short' => "Do not request special events.", + 'long' => "Special events (e.g., freeleech, neutral leech, picks) are launched at the discretion of the staff. They do not adhere to a fixed schedule, and may not be requested by users." ], + [ 'n' => "4.7", + 'short' => "Do not harvest user-identifying information.", + 'long' => "It is prohibited to use ${site_name}'s services to harvest user-identifying information of any kind (e.g., IP addresses, personal links) through the use of scripts, exploits, or other techniques." ], + [ 'n' => "4.8", + 'short' => "Do not use ${site_name}'s services (including the tracker, website, and IRC network) for commercial gain.", + 'long' => "Commercializing services provided by or code maintained by ${site_name} (e.g., Gazelle, Ocelot) is prohibited. Commercializing content provided by ${site_name} users via the aforementioned services (e.g., user torrent data) is prohibited. Referral schemes, financial solicitations, and money offers are also prohibited." ], + [ 'n' => "5.1", + 'short' => "Do not browse ${site_name} using any free proxy or VPN service.", + 'long' => "You may browse the site through a VPN/proxy only if you have paid for this service. This includes (for example) self-hosted VPNs or proxies, services like NordVPN and VPNs or proxies that come with a seedbox. When in doubt, please send a ${staffpm}. The use of Tor for browsing the site is not allowed." ], + [ 'n' => "5.2", + 'short' => "Do not abuse automated site access.", + 'long' => "All automated site access must be done through the API. API use is limited to 5 requests within any 10-second window. Scripts and other automated processes must not scrape the site's HTML pages. When in doubt, seek advice from staff." ], + [ 'n' => "5.3", + 'short' => "Do not autosnatch freeleech torrents.", + 'long' => "The automatic snatching of freeleech torrents using any method involving little or no user-input (e.g., API-based scripts, log or site scraping, etc.) is prohibited. See ${site_name}'s ${autofl_article} article for more information." ], + [ 'n' => "6.1", + 'short' => "Do not seek or exploit live bugs for any reason.", + 'long' => "Seeking or exploiting bugs in the live site (as opposed to a local development environment) is prohibited. If you discover a critical bug or security vulnerability, immediately report it in accordance with ${site_name}'s ${bugs_article}. Non-critical bugs can be reported in the Bugs Forum." ], + [ 'n' => "6.2", + 'short' => "Do not publish exploits.", + 'long' => "The publication, organization, dissemination, sharing, technical discussion, or technical facilitation of exploits is prohibited at staff discretion. Exploits are defined as unanticipated or unaccepted uses of internal, external, non-profit, or for-profit services. Exploits are subject to reclassification at any time." ], + [ 'n' => "7.0", + 'short' => "Be respectful to all staff members.", + 'long' => "Staff on ${site_name} are volunteers who dedicate their time in order to keep the site running, without receiving any compensation. Being disrespectful to them is prohibited, and might result in a warning or worse." ], + [ 'n' => "7.1", + 'short' => "Staff have the final word on rule interpretations.", + 'long' => "All rules on ${site_name} may be subject to different interpretations. Since the staff wrote these rules, their interpretation is final. If you need clarification on a rule, or if you think a rule should be restated, please send a ${staffpm}." ] + ); + echo "
      \n"; + foreach($golden_rules as $gr) { + $r_link = "gr${gr['n']}"; + echo "
    • " . + "${gr['n']}." . + '
      ' . + '
      ' . + $gr['short'] . + '
      ' . + '
      ' . + $gr['long'] . + '
      ' . + '
      ' . + "
    • \n"; + } + echo "
    \n"; + } - /** - * Displays the site's rules for tags. - * - * @param boolean $OnUpload - whether it's being displayed on a torrent upload form - */ - public static function display_site_tag_rules($OnUpload = false) { - ?> -
      -
    • Tags should be comma-separated, and you should use a period (".") to separate words inside a tag — e.g. "hip.hop".
    • + /** + * Displays the site's rules for tags. + * + * @param boolean $OnUpload - whether it's being displayed on a torrent upload form + */ + public static function display_site_tag_rules($OnUpload = false) { + ?> +
        +
      • Tags should be comma-separated, and you should use a period (".") to separate words inside a tag — e.g. "hip.hop".
      • -
      • There is a list of official tags the torrent upload page')?>. Please use these tags instead of "unofficial" tags (e.g. use the official "drum.and.bass" tag, instead of an unofficial "dnb" tag). Please note that the "2000s" tag refers to music produced between 2000 and 2009.
      • +
      • There is a list of official tags the torrent upload page')?>. Please use these tags instead of "unofficial" tags (e.g. use the official "drum.and.bass" tag, instead of an unofficial "dnb" tag). Please note that the "2000s" tag refers to music produced between 2000 and 2009.
      • -
      • Avoid abbreviations if at all possible. So instead of tagging an album as "alt", tag it as "alternative". Make sure that you use correct spelling.
      • +
      • Avoid abbreviations if at all possible. So instead of tagging an album as "alt", tag it as "alternative". Make sure that you use correct spelling.
      • -
      • Avoid using multiple synonymous tags. Using both "prog.rock" and "progressive.rock" is redundant and annoying — just use the official "progressive.rock" tag.
      • +
      • Avoid using multiple synonymous tags. Using both "prog.rock" and "progressive.rock" is redundant and annoying — just use the official "progressive.rock" tag.
      • -
      • Do not add "useless" tags, such as "seen.live", "awesome", "rap" (is encompassed by "hip.hop"), etc. If an album is live, you can tag it as "live".
      • +
      • Do not add "useless" tags, such as "seen.live", "awesome", "rap" (is encompassed by "hip.hop"), etc. If an album is live, you can tag it as "live".
      • -
      • Only tag information on the album itself — not the individual release. Tags such as "v0", "eac", "vinyl", "from.what", etc. are strictly forbidden. Remember that these tags will be used for other versions of the same album.
      • +
      • Only tag information on the album itself — not the individual release. Tags such as "v0", "eac", "vinyl", "from.what", etc. are strictly forbidden. Remember that these tags will be used for other versions of the same album.
      • -
      • You should be able to build up a list of tags using only the official tags the torrent upload page')?>. If you are in any doubt about whether or not a tag is acceptable, do not add it.
      • -
      -You should be able to build up a list of tags using only the official tags the torrent upload page')?>. If you are in any doubt about whether or not a tag is acceptable, do not add it. +
    + -
      -
    1. - Many forums (Serious Discussions, Chat, etc.) have their own set of rules. Make sure you read and take note of these rules before you attempt to post in one of these forums. -
    2. -
    3. - Don't use all capital letters, excessive !!! (exclamation marks) or ??? (question marks). It seems like you're shouting! -
    4. -
    5. - No lame referral schemes. This includes any website or any other similar scheme in which the poster gets personal gain from users clicking a link. -
    6. -
    7. - No asking for money for any reason whatsoever. Although we care about your friend who lost everything, or a dying relative who wants to enjoy their last few moments alive by being given lots of money, is not the place for it. -
    8. -
    9. - Do not inappropriately advertise your uploads. In special cases, it is acceptable to mention new uploads in an approved thread (e.g. Post your first upload here so people snatch it), but be sure to carefully read the thread's rules before posting. It is also acceptable to discuss releases you have uploaded when conversing about the music itself. Blatant attempts to advertise your uploads outside of the appropriate forums or threads may result in a warning or the loss of forum privileges. -
    10. -
    11. - No posting music requests in forums. There's a request link at the top of the page; please use that instead. -
    12. -
    13. - No flaming; be pleasant and polite. Don't use offensive language, and don't be confrontational for the sake of confrontation. -
    14. -
    15. - Don't point out or attack other members' share ratios. A higher ratio does not make you better than someone else. -
    16. -
    17. - Try not to ask stupid questions. A stupid question is one that you could have found the answer to yourself with a little research, or one that you're asking in the wrong place. If you do the basic research suggested (i.e., read the rules/wiki) or search the forums and don't find the answer to your question, then go ahead and ask. Staff and First Line Support (FLS) are not here to hand-feed you the answers you could have found on your own with a little bit of effort. -
    18. -
    19. - Be sure you read all the sticky threads in a forum before you post. -
    20. -
    21. - Use descriptive and specific subject lines. This helps others decide whether your particular words of wisdom relate to a topic they care about. -
    22. -
    23. - Try not to post comments that don't add anything to the discussion. When you're just cruising through a thread in a leisurely manner, it's not too annoying to read through a lot of "hear, hear"'s and "I agree"'s. But if you're actually trying to find information, it's a pain in the neck. So save those one-word responses for threads that have degenerated to the point where none but true aficionados are following them any more. -

      - Or short: NO spamming -

      -
    24. -
    25. - Refrain from quoting excessively. When quoting someone, use only the portion of the quote that is absolutely necessary. This includes quoting pictures! -
    26. -
    27. - No posting of requests for serials or cracks. No links to warez or crack sites in the forums. -
    28. -
    29. - No political or religious discussions. These types of discussions lead to arguments and flaming users, something that will not be tolerated. The only exception to this rule is the Serious Discussions forum, which exists solely for the purpose of intellectual discussion and civilized debate. -
    30. -
    31. - Don't waste other people's bandwidth by posting images of a large file size. -
    32. -
    33. - Be patient with newcomers. Once you have become an expert, it is easy to forget that you started out as a newbie too. Try to help them out if you can. -
    34. -
    35. - No requesting invites to any sites anywhere on the site or IRC. Invites may be offered in the Invitations forum (which is restricted to Elite and above), and nowhere else. -
    36. -
    37. - There are some language threads (e.g. French, Greek) in the Chat forum. Apart from these, no language other than English is permitted in the forums. If we can't understand it, we can't moderate it. Some FLS members speak other languages. You may send them a PM if you have a specific question and you do not feel comfortable in expressing yourself in English. -
    38. -
    39. - Be cautious when posting mature content on the forums. All mature imagery must abide by the rules found here. Gratuitously sexual or violent content which falls outside of the allowable categories will result in a warning or worse. -
    40. -
    41. - Mature content in posts must be properly tagged. The correct format is as follows: [mature=description] ...content... [/mature], where "description" is a mandatory description of the post contents. Misleading or inadequate descriptions will be penalized. -
    42. -
    43. - Threads created for the exclusive purpose of posting mature imagery will be trashed. Mature content (including graphic album art) should be contextually relevant to the thread and/or forum you're posting in. If you are in doubt as to whether a post is appropriate, send a Staff PM to the Forum Moderators and wait for a reply before proceeding. -
    44. -
    - +
      +
    1. + Many forums (Serious Discussions, Chat, etc.) have their own set of rules. Make sure you read and take note of these rules before you attempt to post in one of these forums. +
    2. +
    3. + Don't use all capital letters, excessive !!! (exclamation marks) or ??? (question marks). It seems like you're shouting! +
    4. +
    5. + No lame referral schemes. This includes any website or any other similar scheme in which the poster gets personal gain from users clicking a link. +
    6. +
    7. + No asking for money for any reason whatsoever. Although we care about your friend who lost everything, or a dying relative who wants to enjoy their last few moments alive by being given lots of money, is not the place for it. +
    8. +
    9. + Do not inappropriately advertise your uploads. In special cases, it is acceptable to mention new uploads in an approved thread (e.g. Post your first upload here so people snatch it), but be sure to carefully read the thread's rules before posting. It is also acceptable to discuss releases you have uploaded when conversing about the music itself. Blatant attempts to advertise your uploads outside of the appropriate forums or threads may result in a warning or the loss of forum privileges. +
    10. +
    11. + No posting music requests in forums. There's a request link at the top of the page; please use that instead. +
    12. +
    13. + No flaming; be pleasant and polite. Don't use offensive language, and don't be confrontational for the sake of confrontation. +
    14. +
    15. + Don't point out or attack other members' share ratios. A higher ratio does not make you better than someone else. +
    16. +
    17. + Try not to ask stupid questions. A stupid question is one that you could have found the answer to yourself with a little research, or one that you're asking in the wrong place. If you do the basic research suggested (i.e., read the rules/wiki) or search the forums and don't find the answer to your question, then go ahead and ask. Staff and First Line Support (FLS) are not here to hand-feed you the answers you could have found on your own with a little bit of effort. +
    18. +
    19. + Be sure you read all the sticky threads in a forum before you post. +
    20. +
    21. + Use descriptive and specific subject lines. This helps others decide whether your particular words of wisdom relate to a topic they care about. +
    22. +
    23. + Try not to post comments that don't add anything to the discussion. When you're just cruising through a thread in a leisurely manner, it's not too annoying to read through a lot of "hear, hear"'s and "I agree"'s. But if you're actually trying to find information, it's a pain in the neck. So save those one-word responses for threads that have degenerated to the point where none but true aficionados are following them any more. +

      + Or short: NO spamming +

      +
    24. +
    25. + Refrain from quoting excessively. When quoting someone, use only the portion of the quote that is absolutely necessary. This includes quoting pictures! +
    26. +
    27. + No posting of requests for serials or cracks. No links to warez or crack sites in the forums. +
    28. +
    29. + No political or religious discussions. These types of discussions lead to arguments and flaming users, something that will not be tolerated. The only exception to this rule is the Serious Discussions forum, which exists solely for the purpose of intellectual discussion and civilized debate. +
    30. +
    31. + Don't waste other people's bandwidth by posting images of a large file size. +
    32. +
    33. + Be patient with newcomers. Once you have become an expert, it is easy to forget that you started out as a newbie too. Try to help them out if you can. +
    34. +
    35. + No requesting invites to any sites anywhere on the site or IRC. Invites may be offered in the Invitations forum (which is restricted to Elite and above), and nowhere else. +
    36. +
    37. + There are some language threads (e.g. French, Greek) in the Chat forum. Apart from these, no language other than English is permitted in the forums. If we can't understand it, we can't moderate it. Some FLS members speak other languages. You may send them a PM if you have a specific question and you do not feel comfortable in expressing yourself in English. +
    38. +
    39. + Be cautious when posting mature content on the forums. All mature imagery must abide by the rules found here. Gratuitously sexual or violent content which falls outside of the allowable categories will result in a warning or worse. +
    40. +
    41. + Mature content in posts must be properly tagged. The correct format is as follows: [mature=description] ...content... [/mature], where "description" is a mandatory description of the post contents. Misleading or inadequate descriptions will be penalized. +
    42. +
    43. + Threads created for the exclusive purpose of posting mature imagery will be trashed. Mature content (including graphic album art) should be contextually relevant to the thread and/or forum you're posting in. If you are in doubt as to whether a post is appropriate, send a Staff PM to the Forum Moderators and wait for a reply before proceeding. +
    44. +
    + -
      -
    1. Staff have the final decision. If a staff member says stop and you continue, expect at least to be banned from the IRC network.
    2. -
    3. Be respectful to IRC Operators and Administrators. These people are site staff who volunteer their time for little compensation. They are there for the benefit of all and to aid in conflict resolution; do not waste their time.
    4. -
    5. Do not link shock sites or anything NSFW (not safe for work) without a warning. If in doubt, ask a staff member in about it.
    6. -
    7. Excessive swearing will get you kicked; keep swearing to a minimum.
    8. -
    9. Do not leave Caps Lock enabled all the time. It gets annoying, and you will likely get yourself kicked.
    10. -
    11. No arguing. You can't win an argument over the Internet, so you are just wasting your time trying.
    12. -
    13. No prejudice, especially related to race, religion, politics, sexual preference, ethnic background, etc. It is highly suggested to avoid these subjects entirely.
    14. -
    15. Flooding is irritating and will warrant you a kick. This includes, but is not limited to, automatic "now playing" scripts, pasting large amounts of text, and multiple consecutive lines with no relevance to the conversation at hand.
    16. -
    17. Impersonation of other members — particularly staff members — will not go unpunished. If you are uncertain of a user's identity, check their vhost.
    18. -
    19. Spamming is strictly forbidden. This includes, but is not limited to, personal sites, online auctions, and torrent uploads.
    20. -
    21. Obsessive annoyance — both to other users and staff — will not be tolerated.
    22. -
    23. Do not PM, DCC, or Query anyone you don't know or have never talked to without asking first; this applies specifically to staff.
    24. -
    25. No language other than English is permitted in the IRC channels. If we cannot understand it, we cannot moderate it.
    26. -
    27. The offering, selling, trading, and giving away of invites to this or any other site on our IRC network is strictly forbidden.
    28. + /** + * Displays the site's rules for conversing on its IRC network + * + */ + public static function display_irc_chat_rules() { + ?> +
        +
      1. Staff have the final decision. If a staff member says stop and you continue, expect at least to be banned from the IRC network.
      2. +
      3. Be respectful to IRC Operators and Administrators. These people are site staff who volunteer their time for little compensation. They are there for the benefit of all and to aid in conflict resolution; do not waste their time.
      4. +
      5. Do not link shock sites or anything NSFW (not safe for work) without a warning. If in doubt, ask a staff member in about it.
      6. +
      7. Excessive swearing will get you kicked; keep swearing to a minimum.
      8. +
      9. Do not leave Caps Lock enabled all the time. It gets annoying, and you will likely get yourself kicked.
      10. +
      11. No arguing. You can't win an argument over the Internet, so you are just wasting your time trying.
      12. +
      13. No prejudice, especially related to race, religion, politics, sexual preference, ethnic background, etc. It is highly suggested to avoid these subjects entirely.
      14. +
      15. Flooding is irritating and will warrant you a kick. This includes, but is not limited to, automatic "now playing" scripts, pasting large amounts of text, and multiple consecutive lines with no relevance to the conversation at hand.
      16. +
      17. Impersonation of other members — particularly staff members — will not go unpunished. If you are uncertain of a user's identity, check their vhost.
      18. +
      19. Spamming is strictly forbidden. This includes, but is not limited to, personal sites, online auctions, and torrent uploads.
      20. +
      21. Obsessive annoyance — both to other users and staff — will not be tolerated.
      22. +
      23. Do not PM, DCC, or Query anyone you don't know or have never talked to without asking first; this applies specifically to staff.
      24. +
      25. No language other than English is permitted in the IRC channels. If we cannot understand it, we cannot moderate it.
      26. +
      27. The offering, selling, trading, and giving away of invites to this or any other site on our IRC network is strictly forbidden.
      28. The creation of non-official channels on the IRC network is prohibited. Please send a Staff PM if you want to add a channel to our network. -
      29. Read the channel topic before asking questions.
      30. -
      -Read the channel topic before asking questions. +
    + __DIR__.'/../cache/twig'] +); //Begin browser identification if (session_status() === PHP_SESSION_NONE) { - session_start(); + session_start(); } if (!isset($_SESSION['WhichBrowser'])) { - $Debug->set_flag('start parsing user agent'); - $Result = new WhichBrowser\Parser($_SERVER['HTTP_USER_AGENT']); - $_SESSION['WhichBrowser'] = [ - 'Browser' => $Result->browser->getName(), - 'BrowserVersion' => explode('.',$Result->browser->getVersion())[0], - 'OperatingSystem' => $Result->os->getName(), - 'OperatingSystemVersion' => $Result->os->getVersion() - ]; - $Debug->set_flag('end parsing user agent'); + $Debug->set_flag('start parsing user agent'); + $Result = new WhichBrowser\Parser($_SERVER['HTTP_USER_AGENT']); + $_SESSION['WhichBrowser'] = [ + 'Browser' => $Result->browser->getName(), + 'BrowserVersion' => explode('.',$Result->browser->getVersion())[0], + 'OperatingSystem' => $Result->os->getName(), + 'OperatingSystemVersion' => $Result->os->getVersion() + ]; + $Debug->set_flag('end parsing user agent'); } $Browser = $_SESSION['WhichBrowser']['Browser']; @@ -126,187 +132,187 @@ // Permissions if (isset($_COOKIE['session'])) { - $LoginCookie = Crypto::decrypt($_COOKIE['session'], ENCKEY); + $LoginCookie = Crypto::decrypt($_COOKIE['session'], ENCKEY); } if (isset($LoginCookie)) { - list($SessionID, $LoggedUser['ID']) = explode('|~|', Crypto::decrypt($LoginCookie, ENCKEY)); - $LoggedUser['ID'] = (int)$LoggedUser['ID']; - - $UserID = $LoggedUser['ID']; //TODO: UserID should not be LoggedUser - - if (!$LoggedUser['ID'] || !$SessionID) { - logout(); - } - - $UserSessions = $Cache->get_value("users_sessions_$UserID"); - if (!is_array($UserSessions)) { - $DB->query(" - SELECT - SessionID, - Browser, - OperatingSystem, - IP, - LastUpdate - FROM users_sessions - WHERE UserID = '$UserID' - AND Active = 1 - ORDER BY LastUpdate DESC"); - $UserSessions = $DB->to_array('SessionID',MYSQLI_ASSOC); - $Cache->cache_value("users_sessions_$UserID", $UserSessions, 0); - } - - if (!array_key_exists($SessionID, $UserSessions)) { - logout(); - } - - // Check if user is enabled - $Enabled = $Cache->get_value('enabled_'.$LoggedUser['ID']); - if ($Enabled === false) { - $DB->query(" - SELECT Enabled - FROM users_main - WHERE ID = '{$LoggedUser['ID']}'"); - list($Enabled) = $DB->next_record(); - $Cache->cache_value('enabled_'.$LoggedUser['ID'], $Enabled, 0); - } - if ($Enabled == 2) { - - logout(); - } - - // Up/Down stats - $UserStats = Users::user_stats($LoggedUser['ID']); - - // Get info such as username - $LightInfo = Users::user_info($LoggedUser['ID']); - $HeavyInfo = Users::user_heavy_info($LoggedUser['ID']); - - // Create LoggedUser array - $LoggedUser = array_merge($HeavyInfo, $LightInfo, $UserStats); - G::$LoggedUser =& $LoggedUser; - - $LoggedUser['RSS_Auth'] = md5($LoggedUser['ID'] . RSS_HASH . $LoggedUser['torrent_pass']); - - // $LoggedUser['RatioWatch'] as a bool to disable things for users on Ratio Watch - $LoggedUser['RatioWatch'] = ( - $LoggedUser['RatioWatchEnds'] != '0000-00-00 00:00:00' - && time() < strtotime($LoggedUser['RatioWatchEnds']) - && ($LoggedUser['BytesDownloaded'] * $LoggedUser['RequiredRatio']) > $LoggedUser['BytesUploaded'] - ); - - // Load in the permissions - $LoggedUser['Permissions'] = Permissions::get_permissions_for_user($LoggedUser['ID'], $LoggedUser['CustomPermissions']); - $LoggedUser['Permissions']['MaxCollages'] += Donations::get_personal_collages($LoggedUser['ID']); - - // Change necessary triggers in external components - $Cache->CanClear = check_perms('admin_clear_cache'); - - // Because we <3 our staff - if (check_perms('site_disable_ip_history')) { - $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; - } - - // Update LastUpdate every 10 minutes - if (strtotime($UserSessions[$SessionID]['LastUpdate']) + 600 < time()) { - $DB->query(" - UPDATE users_main - SET LastAccess = '".sqltime()."' - WHERE ID = '{$LoggedUser['ID']}'"); - $DB->query(" - UPDATE users_sessions - SET - IP = '".$_SERVER['REMOTE_ADDR']."', - Browser = '$Browser', - BrowserVersion = '{$BrowserVersion}', - OperatingSystem = '$OperatingSystem', - OperatingSystemVersion = '{$OperatingSystemVersion}', - LastUpdate = '".sqltime()."' - WHERE UserID = '{$LoggedUser['ID']}' - AND SessionID = '".db_string($SessionID)."'"); - $Cache->begin_transaction("users_sessions_$UserID"); - $Cache->delete_row($SessionID); - $Cache->insert_front($SessionID,array( - 'SessionID' => $SessionID, - 'Browser' => $Browser, - 'BrowserVersion' => $BrowserVersion, - 'OperatingSystem' => $OperatingSystem, - 'OperatingSystemVersion' => $OperatingSystemVersion, - 'IP' => $_SERVER['REMOTE_ADDR'], - 'LastUpdate' => sqltime() - )); - $Cache->commit_transaction(0); - } - - // Notifications - if (isset($LoggedUser['Permissions']['site_torrents_notify'])) { - $LoggedUser['Notify'] = $Cache->get_value('notify_filters_'.$LoggedUser['ID']); - if (!is_array($LoggedUser['Notify'])) { - $DB->query(" - SELECT ID, Label - FROM users_notify_filters - WHERE UserID = '{$LoggedUser['ID']}'"); - $LoggedUser['Notify'] = $DB->to_array('ID'); - $Cache->cache_value('notify_filters_'.$LoggedUser['ID'], $LoggedUser['Notify'], 2592000); - } - } - - // We've never had to disable the wiki privs of anyone. - if ($LoggedUser['DisableWiki']) { - unset($LoggedUser['Permissions']['site_edit_wiki']); - } - - // IP changed - - if ($LoggedUser['IP'] != $_SERVER['REMOTE_ADDR'] && !check_perms('site_disable_ip_history')) { - - if (Tools::site_ban_ip($_SERVER['REMOTE_ADDR'])) { - error('Your IP address has been banned.'); - } - - $CurIP = db_string($LoggedUser['IP']); - $NewIP = db_string($_SERVER['REMOTE_ADDR']); - $DB->query(" - UPDATE users_history_ips - SET EndTime = '".sqltime()."' - WHERE EndTime IS NULL - AND UserID = '{$LoggedUser['ID']}' - AND IP = '$CurIP'"); - $DB->query(" - INSERT IGNORE INTO users_history_ips - (UserID, IP, StartTime) - VALUES - ('{$LoggedUser['ID']}', '$NewIP', '".sqltime()."')"); - - $ipcc = Tools::geoip($NewIP); - $DB->query(" - UPDATE users_main - SET IP = '$NewIP', ipcc = '$ipcc' - WHERE ID = '{$LoggedUser['ID']}'"); - $Cache->begin_transaction('user_info_heavy_'.$LoggedUser['ID']); - $Cache->update_row(false, array('IP' => $_SERVER['REMOTE_ADDR'])); - $Cache->commit_transaction(0); - } - - - // Get stylesheets - $Stylesheets = $Cache->get_value('stylesheets'); - if (!is_array($Stylesheets)) { - $DB->query(' - SELECT - ID, - LOWER(REPLACE(Name, " ", "_")) AS Name, - Name AS ProperName - FROM stylesheets ORDER BY ID DESC'); - $Stylesheets = $DB->to_array('ID', MYSQLI_BOTH); - $Cache->cache_value('stylesheets', $Stylesheets, 0); - } - - //A9 TODO: Clean up this messy solution - $LoggedUser['StyleName'] = $Stylesheets[$LoggedUser['StyleID']]['Name']; - - if (empty($LoggedUser['Username'])) { - logout(); // Ghost - } + list($SessionID, $LoggedUser['ID']) = explode('|~|', Crypto::decrypt($LoginCookie, ENCKEY)); + $LoggedUser['ID'] = (int)$LoggedUser['ID']; + + $UserID = $LoggedUser['ID']; //TODO: UserID should not be LoggedUser + + if (!$LoggedUser['ID'] || !$SessionID) { + logout(); + } + + $UserSessions = $Cache->get_value("users_sessions_$UserID"); + if (!is_array($UserSessions)) { + $DB->query(" + SELECT + SessionID, + Browser, + OperatingSystem, + IP, + LastUpdate + FROM users_sessions + WHERE UserID = '$UserID' + AND Active = 1 + ORDER BY LastUpdate DESC"); + $UserSessions = $DB->to_array('SessionID',MYSQLI_ASSOC); + $Cache->cache_value("users_sessions_$UserID", $UserSessions, 0); + } + + if (!array_key_exists($SessionID, $UserSessions)) { + logout(); + } + + // Check if user is enabled + $Enabled = $Cache->get_value('enabled_'.$LoggedUser['ID']); + if ($Enabled === false) { + $DB->query(" + SELECT Enabled + FROM users_main + WHERE ID = '{$LoggedUser['ID']}'"); + list($Enabled) = $DB->next_record(); + $Cache->cache_value('enabled_'.$LoggedUser['ID'], $Enabled, 0); + } + if ($Enabled == 2) { + + logout(); + } + + // Up/Down stats + $UserStats = Users::user_stats($LoggedUser['ID']); + + // Get info such as username + $LightInfo = Users::user_info($LoggedUser['ID']); + $HeavyInfo = Users::user_heavy_info($LoggedUser['ID']); + + // Create LoggedUser array + $LoggedUser = array_merge($HeavyInfo, $LightInfo, $UserStats); + G::$LoggedUser =& $LoggedUser; + + $LoggedUser['RSS_Auth'] = md5($LoggedUser['ID'] . RSS_HASH . $LoggedUser['torrent_pass']); + + // $LoggedUser['RatioWatch'] as a bool to disable things for users on Ratio Watch + $LoggedUser['RatioWatch'] = ( + $LoggedUser['RatioWatchEnds'] != '0000-00-00 00:00:00' + && time() < strtotime($LoggedUser['RatioWatchEnds']) + && ($LoggedUser['BytesDownloaded'] * $LoggedUser['RequiredRatio']) > $LoggedUser['BytesUploaded'] + ); + + // Load in the permissions + $LoggedUser['Permissions'] = Permissions::get_permissions_for_user($LoggedUser['ID'], $LoggedUser['CustomPermissions']); + $LoggedUser['Permissions']['MaxCollages'] += Donations::get_personal_collages($LoggedUser['ID']); + + // Change necessary triggers in external components + $Cache->CanClear = check_perms('admin_clear_cache'); + + // Because we <3 our staff + if (check_perms('site_disable_ip_history')) { + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + } + + // Update LastUpdate every 10 minutes + if (strtotime($UserSessions[$SessionID]['LastUpdate']) + 600 < time()) { + $DB->query(" + UPDATE users_main + SET LastAccess = '".sqltime()."' + WHERE ID = '{$LoggedUser['ID']}'"); + $DB->query(" + UPDATE users_sessions + SET + IP = '".$_SERVER['REMOTE_ADDR']."', + Browser = '$Browser', + BrowserVersion = '{$BrowserVersion}', + OperatingSystem = '$OperatingSystem', + OperatingSystemVersion = '{$OperatingSystemVersion}', + LastUpdate = '".sqltime()."' + WHERE UserID = '{$LoggedUser['ID']}' + AND SessionID = '".db_string($SessionID)."'"); + $Cache->begin_transaction("users_sessions_$UserID"); + $Cache->delete_row($SessionID); + $Cache->insert_front($SessionID,array( + 'SessionID' => $SessionID, + 'Browser' => $Browser, + 'BrowserVersion' => $BrowserVersion, + 'OperatingSystem' => $OperatingSystem, + 'OperatingSystemVersion' => $OperatingSystemVersion, + 'IP' => $_SERVER['REMOTE_ADDR'], + 'LastUpdate' => sqltime() + )); + $Cache->commit_transaction(0); + } + + // Notifications + if (isset($LoggedUser['Permissions']['site_torrents_notify'])) { + $LoggedUser['Notify'] = $Cache->get_value('notify_filters_'.$LoggedUser['ID']); + if (!is_array($LoggedUser['Notify'])) { + $DB->query(" + SELECT ID, Label + FROM users_notify_filters + WHERE UserID = '{$LoggedUser['ID']}'"); + $LoggedUser['Notify'] = $DB->to_array('ID'); + $Cache->cache_value('notify_filters_'.$LoggedUser['ID'], $LoggedUser['Notify'], 2592000); + } + } + + // We've never had to disable the wiki privs of anyone. + if ($LoggedUser['DisableWiki']) { + unset($LoggedUser['Permissions']['site_edit_wiki']); + } + + // IP changed + + if ($LoggedUser['IP'] != $_SERVER['REMOTE_ADDR'] && !check_perms('site_disable_ip_history')) { + + if (Tools::site_ban_ip($_SERVER['REMOTE_ADDR'])) { + error('Your IP address has been banned.'); + } + + $CurIP = db_string($LoggedUser['IP']); + $NewIP = db_string($_SERVER['REMOTE_ADDR']); + $DB->query(" + UPDATE users_history_ips + SET EndTime = '".sqltime()."' + WHERE EndTime IS NULL + AND UserID = '{$LoggedUser['ID']}' + AND IP = '$CurIP'"); + $DB->query(" + INSERT IGNORE INTO users_history_ips + (UserID, IP, StartTime) + VALUES + ('{$LoggedUser['ID']}', '$NewIP', '".sqltime()."')"); + + $ipcc = Tools::geoip($NewIP); + $DB->query(" + UPDATE users_main + SET IP = '$NewIP', ipcc = '$ipcc' + WHERE ID = '{$LoggedUser['ID']}'"); + $Cache->begin_transaction('user_info_heavy_'.$LoggedUser['ID']); + $Cache->update_row(false, array('IP' => $_SERVER['REMOTE_ADDR'])); + $Cache->commit_transaction(0); + } + + + // Get stylesheets + $Stylesheets = $Cache->get_value('stylesheets'); + if (!is_array($Stylesheets)) { + $DB->query(' + SELECT + ID, + LOWER(REPLACE(Name, " ", "_")) AS Name, + Name AS ProperName + FROM stylesheets ORDER BY ID DESC'); + $Stylesheets = $DB->to_array('ID', MYSQLI_BOTH); + $Cache->cache_value('stylesheets', $Stylesheets, 0); + } + + //A9 TODO: Clean up this messy solution + $LoggedUser['StyleName'] = $Stylesheets[$LoggedUser['StyleID']]['Name']; + + if (empty($LoggedUser['Username'])) { + logout(); // Ghost + } } $Debug->set_flag('end user handling'); @@ -317,50 +323,50 @@ * Log out the current session */ function logout() { - global $SessionID; - setcookie('session', '', time() - 60 * 60 * 24 * 365, '/', '', false); - setcookie('keeplogged', '', time() - 60 * 60 * 24 * 365, '/', '', false); - setcookie('session', '', time() - 60 * 60 * 24 * 365, '/', '', false); - if ($SessionID) { - - G::$DB->query(" - DELETE FROM users_sessions - WHERE UserID = '" . G::$LoggedUser['ID'] . "' - AND SessionID = '".db_string($SessionID)."'"); - - G::$Cache->begin_transaction('users_sessions_' . G::$LoggedUser['ID']); - G::$Cache->delete_row($SessionID); - G::$Cache->commit_transaction(0); - } - G::$Cache->delete_value('user_info_' . G::$LoggedUser['ID']); - G::$Cache->delete_value('user_stats_' . G::$LoggedUser['ID']); - G::$Cache->delete_value('user_info_heavy_' . G::$LoggedUser['ID']); - - header('Location: login.php'); - - die(); + global $SessionID; + setcookie('session', '', time() - 60 * 60 * 24 * 365, '/', '', false); + setcookie('keeplogged', '', time() - 60 * 60 * 24 * 365, '/', '', false); + setcookie('session', '', time() - 60 * 60 * 24 * 365, '/', '', false); + if ($SessionID) { + + G::$DB->query(" + DELETE FROM users_sessions + WHERE UserID = '" . G::$LoggedUser['ID'] . "' + AND SessionID = '".db_string($SessionID)."'"); + + G::$Cache->begin_transaction('users_sessions_' . G::$LoggedUser['ID']); + G::$Cache->delete_row($SessionID); + G::$Cache->commit_transaction(0); + } + G::$Cache->delete_value('user_info_' . G::$LoggedUser['ID']); + G::$Cache->delete_value('user_stats_' . G::$LoggedUser['ID']); + G::$Cache->delete_value('user_info_heavy_' . G::$LoggedUser['ID']); + + header('Location: login.php'); + + die(); } /** * Logout all sessions */ function logout_all_sessions() { - $UserID = G::$LoggedUser['ID']; + $UserID = G::$LoggedUser['ID']; - G::$DB->query(" - DELETE FROM users_sessions - WHERE UserID = '$UserID'"); + G::$DB->query(" + DELETE FROM users_sessions + WHERE UserID = '$UserID'"); - G::$Cache->delete_value('users_sessions_' . $UserID); - logout(); + G::$Cache->delete_value('users_sessions_' . $UserID); + logout(); } function enforce_login() { - global $SessionID; - if (!$SessionID || !G::$LoggedUser) { - setcookie('redirect', $_SERVER['REQUEST_URI'], time() + 60 * 30, '/', '', false); - logout(); - } + global $SessionID; + if (!$SessionID || !G::$LoggedUser) { + setcookie('redirect', $_SERVER['REQUEST_URI'], time() + 60 * 30, '/', '', false); + logout(); + } } /** @@ -371,23 +377,23 @@ function enforce_login() { * @return bool authorisation status. Prints an error message to LAB_CHAN on IRC on failure. */ function authorize($Ajax = false) { - if (empty($_REQUEST['auth']) || $_REQUEST['auth'] != G::$LoggedUser['AuthKey']) { - send_irc("PRIVMSG ".LAB_CHAN." :".G::$LoggedUser['Username']." just failed authorize on ".$_SERVER['REQUEST_URI'].(!empty($_SERVER['HTTP_REFERER']) ? " coming from ".$_SERVER['HTTP_REFERER'] : "")); - error('Invalid authorization key. Go back, refresh, and try again.', $Ajax); - return false; - } - return true; + if (empty($_REQUEST['auth']) || $_REQUEST['auth'] != G::$LoggedUser['AuthKey']) { + send_irc("PRIVMSG ".LAB_CHAN." :".G::$LoggedUser['Username']." just failed authorize on ".$_SERVER['REQUEST_URI'].(!empty($_SERVER['HTTP_REFERER']) ? " coming from ".$_SERVER['HTTP_REFERER'] : "")); + error('Invalid authorization key. Go back, refresh, and try again.', $Ajax); + return false; + } + return true; } function authorizeIfPost($Ajax = false) { - if ($_SERVER['REQUEST_METHOD'] === 'POST') { - if (empty($_POST['auth']) || $_POST['auth'] != G::$LoggedUser['AuthKey']) { - send_irc("PRIVMSG " . LAB_CHAN . " :" . G::$LoggedUser['Username'] . " just failed authorize on " . $_SERVER['REQUEST_URI'] . (!empty($_SERVER['HTTP_REFERER']) ? " coming from " . $_SERVER['HTTP_REFERER'] : "")); - error('Invalid authorization key. Go back, refresh, and try again.', $Ajax); - return false; - } - } - return true; + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (empty($_POST['auth']) || $_POST['auth'] != G::$LoggedUser['AuthKey']) { + send_irc("PRIVMSG " . LAB_CHAN . " :" . G::$LoggedUser['Username'] . " just failed authorize on " . $_SERVER['REQUEST_URI'] . (!empty($_SERVER['HTTP_REFERER']) ? " coming from " . $_SERVER['HTTP_REFERER'] : "")); + error('Invalid authorization key. Go back, refresh, and try again.', $Ajax); + return false; + } + } + return true; } $Debug->set_flag('ending function definitions'); @@ -395,18 +401,18 @@ function authorizeIfPost($Ajax = false) { //Include /sections/*/index.php $Document = basename(parse_url($_SERVER['SCRIPT_NAME'], PHP_URL_PATH), '.php'); if (!preg_match('/^[a-z0-9]+$/i', $Document)) { - error(404); + error(404); } $StripPostKeys = array_fill_keys(array('password', 'cur_pass', 'new_pass_1', 'new_pass_2', 'verifypassword', 'confirm_password', 'ChangePassword', 'Password'), true); $Cache->cache_value('php_' . getmypid(), - [ - 'start' => sqltime(), - 'document' => $Document, - 'query' => $_SERVER['QUERY_STRING'], - 'get' => $_GET, - 'post' => array_diff_key($_POST, $StripPostKeys) - ], 600 + [ + 'start' => sqltime(), + 'document' => $Document, + 'query' => $_SERVER['QUERY_STRING'], + 'get' => $_GET, + 'post' => array_diff_key($_POST, $StripPostKeys) + ], 600 ); // Locked account constant @@ -416,29 +422,29 @@ function authorizeIfPost($Ajax = false) { G::$Router = new \Gazelle\Router(G::$LoggedUser['AuthKey']); if (isset(G::$LoggedUser['LockedAccount']) && !in_array($Document, $AllowedPages)) { - require(SERVER_ROOT . '/sections/locked/index.php'); + require(SERVER_ROOT . '/sections/locked/index.php'); } else { - if (!file_exists(SERVER_ROOT . '/sections/' . $Document . '/index.php')) { - error(404); - } - else { - require(SERVER_ROOT . '/sections/' . $Document . '/index.php'); - } + if (!file_exists(SERVER_ROOT . '/sections/' . $Document . '/index.php')) { + error(404); + } + else { + require(SERVER_ROOT . '/sections/' . $Document . '/index.php'); + } } if (G::$Router->hasRoutes()) { - $action = $_REQUEST['action'] ?? ''; - try { - /** @noinspection PhpIncludeInspection */ - require_once(G::$Router->getRoute($action)); - } - catch (\Gazelle\Exception\RouterException $exception) { - error(404); - } - catch (\Gazelle\Exception\InvalidAccessException $exception) { - error(403); - } + $action = $_REQUEST['action'] ?? ''; + try { + /** @noinspection PhpIncludeInspection */ + require_once(G::$Router->getRoute($action)); + } + catch (\Gazelle\Exception\RouterException $exception) { + error(404); + } + catch (\Gazelle\Exception\InvalidAccessException $exception) { + error(403); + } } $Debug->set_flag('completed module execution'); @@ -449,8 +455,8 @@ function authorizeIfPost($Ajax = false) { * Define the following constant in files that handle file downloads. */ if (!defined('SKIP_NO_CACHE_HEADERS')) { - header('Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0'); - header('Pragma: no-cache'); + header('Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0'); + header('Pragma: no-cache'); } ob_end_flush(); diff --git a/classes/sitehistory.class.php b/classes/sitehistory.class.php index e55b39c4f..06856a641 100644 --- a/classes/sitehistory.class.php +++ b/classes/sitehistory.class.php @@ -1,269 +1,269 @@ - "Code", "Event", "Milestone", "Policy", "Release", "Staff Change"); - private static $SubCategories = array(1 => "Announcement", "Blog Post", "Change Log", "Forum Post", "Wiki", "Other", "External Source"); - private static $Tags = array( - "api", - "celebration", - "class.primary", - "class.secondary", - "collage", - "community", - "conclusion", - "contest", - "design", - "donate", - "editing", - "editorial", - "feature", - "featured.article", - "featured.album", - "featured.product", - "finances", - "format", - "forum", - "freeleech", - "freeleech.tokens", - "gazelle", - "hierarchy", - "inbox", - "infrastructure", - "interview", - "irc", - "log", - "neutral.leech", - "notifications", - "ocelot", - "paranoia", - "picks.guest", - "picks.staff", - "promotion", - "ratio", - "record", - "report", - "request", - "requirement", - "retirement", - "rippy", - "search", - "settings", - "start", - "stats", - "store", - "stylesheet", - "tagging", - "transcode", - "toolbox", - "top.10", - "torrent", - "torrent.group", - "upload", - "vanity.house", - "voting", - "whitelist", - "wiki"); + private static $Categories = array(1 => "Code", "Event", "Milestone", "Policy", "Release", "Staff Change"); + private static $SubCategories = array(1 => "Announcement", "Blog Post", "Change Log", "Forum Post", "Wiki", "Other", "External Source"); + private static $Tags = array( + "api", + "celebration", + "class.primary", + "class.secondary", + "collage", + "community", + "conclusion", + "contest", + "design", + "donate", + "editing", + "editorial", + "feature", + "featured.article", + "featured.album", + "featured.product", + "finances", + "format", + "forum", + "freeleech", + "freeleech.tokens", + "gazelle", + "hierarchy", + "inbox", + "infrastructure", + "interview", + "irc", + "log", + "neutral.leech", + "notifications", + "ocelot", + "paranoia", + "picks.guest", + "picks.staff", + "promotion", + "ratio", + "record", + "report", + "request", + "requirement", + "retirement", + "rippy", + "search", + "settings", + "start", + "stats", + "store", + "stylesheet", + "tagging", + "transcode", + "toolbox", + "top.10", + "torrent", + "torrent.group", + "upload", + "vanity.house", + "voting", + "whitelist", + "wiki"); - public static function get_months() { - $Results = G::$Cache->get_value("site_history_months"); - if (!$Results) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT DISTINCT - YEAR(DATE) AS Year, MONTH(Date) AS Month, MONTHNAME(Date) AS MonthName - FROM site_history - ORDER BY Date DESC"); - $Results = G::$DB->to_array(); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value("site_history_months", $Results, 0); - } - return $Results; - } + public static function get_months() { + $Results = G::$Cache->get_value("site_history_months"); + if (!$Results) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT DISTINCT + YEAR(DATE) AS Year, MONTH(Date) AS Month, MONTHNAME(Date) AS MonthName + FROM site_history + ORDER BY Date DESC"); + $Results = G::$DB->to_array(); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value("site_history_months", $Results, 0); + } + return $Results; + } - public static function get_event($ID) { - if (!empty($ID)) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - ID, Title, Url, Category, SubCategory, Tags, Body, AddedBy, Date - FROM site_history - WHERE ID = '$ID' - ORDER BY Date DESC"); - $Event = G::$DB->next_record(); - G::$DB->set_query_id($QueryID); - return $Event; - } - } + public static function get_event($ID) { + if (!empty($ID)) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + ID, Title, Url, Category, SubCategory, Tags, Body, AddedBy, Date + FROM site_history + WHERE ID = '$ID' + ORDER BY Date DESC"); + $Event = G::$DB->next_record(); + G::$DB->set_query_id($QueryID); + return $Event; + } + } - public static function get_latest_events($Limit) { - self::get_events(null, null, null, null, null, null, $Limit); - } + public static function get_latest_events($Limit) { + self::get_events(null, null, null, null, null, null, $Limit); + } - public static function get_events($Month, $Year, $Title, $Category, $SubCategory, $Tags, $Limit) { - $Month = (int)$Month; - $Year = (int)$Year; - $Title = db_string($Title); - $Category = (int)$Category; - $SubCategory = (int)$SubCategory; - $Tags = db_string($Tags); - $Limit = (int)$Limit; - $Where = array(); - if (!empty($Month)) { - $Where[] = " MONTH(Date) = '$Month' "; - } - if (!empty($Year)) { - $Where[] = " YEAR(Date) = '$Year' "; - } - if (!empty($Title)) { - $Where[] = " Title LIKE '%$Title%' "; - } - if (!empty($Category)) { - $Where[] = " Category = '$Category '"; - } - if (!empty($SubCategory)) { - $Where[] = " SubCategory = '$SubCategory '"; - } - if (!empty($Tags)) { - $Tags = explode(',', $Tags); - $Or = '('; - foreach ($Tags as $Tag) { - $Tag = trim($Tag); - $Or .= " Tags LIKE '%$Tag%' OR "; - } - if (strlen($Or) > 1) { - $Or = rtrim($Or, 'OR '); - $Or .= ')'; - $Where[] = $Or; - } - } - if (!empty($Limit)) { - $Limit = " LIMIT $Limit"; - } else { - $Limit = ''; - } - if (count($Where) > 0) { - $Query = ' WHERE ' . implode('AND', $Where); - } else { - $Query = ''; - } + public static function get_events($Month, $Year, $Title, $Category, $SubCategory, $Tags, $Limit) { + $Month = (int)$Month; + $Year = (int)$Year; + $Title = db_string($Title); + $Category = (int)$Category; + $SubCategory = (int)$SubCategory; + $Tags = db_string($Tags); + $Limit = (int)$Limit; + $Where = []; + if (!empty($Month)) { + $Where[] = " MONTH(Date) = '$Month' "; + } + if (!empty($Year)) { + $Where[] = " YEAR(Date) = '$Year' "; + } + if (!empty($Title)) { + $Where[] = " Title LIKE '%$Title%' "; + } + if (!empty($Category)) { + $Where[] = " Category = '$Category '"; + } + if (!empty($SubCategory)) { + $Where[] = " SubCategory = '$SubCategory '"; + } + if (!empty($Tags)) { + $Tags = explode(',', $Tags); + $Or = '('; + foreach ($Tags as $Tag) { + $Tag = trim($Tag); + $Or .= " Tags LIKE '%$Tag%' OR "; + } + if (strlen($Or) > 1) { + $Or = rtrim($Or, 'OR '); + $Or .= ')'; + $Where[] = $Or; + } + } + if (!empty($Limit)) { + $Limit = " LIMIT $Limit"; + } else { + $Limit = ''; + } + if (count($Where) > 0) { + $Query = ' WHERE ' . implode('AND', $Where); + } else { + $Query = ''; + } - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - ID, Title, Url, Category, SubCategory, Tags, Body, AddedBy, Date - FROM site_history - $Query - ORDER BY Date DESC - $Limit"); - $Events = G::$DB->to_array(); - G::$DB->set_query_id($QueryID); - return $Events; - } + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + ID, Title, Url, Category, SubCategory, Tags, Body, AddedBy, Date + FROM site_history + $Query + ORDER BY Date DESC + $Limit"); + $Events = G::$DB->to_array(); + G::$DB->set_query_id($QueryID); + return $Events; + } - public static function add_event($Date, $Title, $Link, $Category, $SubCategory, $Tags, $Body, $UserID) { - if (empty($Date)) { - $Date = sqltime(); - } else { - list($Y, $M, $D) = explode('-', $Date); - if (!checkdate($M, $D, $Y)) { - error("Error"); - } - } - $Title = db_string($Title); - $Link = db_string($Link); - $Category = (int)$Category; - $SubCategory = (int)$SubCategory; - $Tags = db_string(strtolower((preg_replace('/\s+/', '', $Tags)))); - $ExplodedTags = explode(',', $Tags); - foreach ($ExplodedTags as $Tag) { - if (!in_array($Tag, self::get_tags())) { - error("Invalid tag"); - } - } - $Body = db_string($Body); - $UserID = (int)$UserID; + public static function add_event($Date, $Title, $Link, $Category, $SubCategory, $Tags, $Body, $UserID) { + if (empty($Date)) { + $Date = sqltime(); + } else { + list($Y, $M, $D) = explode('-', $Date); + if (!checkdate($M, $D, $Y)) { + error("Error"); + } + } + $Title = db_string($Title); + $Link = db_string($Link); + $Category = (int)$Category; + $SubCategory = (int)$SubCategory; + $Tags = db_string(strtolower((preg_replace('/\s+/', '', $Tags)))); + $ExplodedTags = explode(',', $Tags); + foreach ($ExplodedTags as $Tag) { + if (!in_array($Tag, self::get_tags())) { + error("Invalid tag"); + } + } + $Body = db_string($Body); + $UserID = (int)$UserID; - if (empty($Title) || empty($Category) || empty($SubCategory)) { - error("Error"); - } + if (empty($Title) || empty($Category) || empty($SubCategory)) { + error("Error"); + } - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - INSERT INTO site_history - (Title, Url, Category, SubCategory, Tags, Body, AddedBy, Date) - VALUES - ('$Title', '$Link', '$Category', '$SubCategory', '$Tags', '$Body', '$UserID', '$Date')"); - G::$DB->set_query_id($QueryID); - G::$Cache->delete_value("site_history_months"); - } + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + INSERT INTO site_history + (Title, Url, Category, SubCategory, Tags, Body, AddedBy, Date) + VALUES + ('$Title', '$Link', '$Category', '$SubCategory', '$Tags', '$Body', '$UserID', '$Date')"); + G::$DB->set_query_id($QueryID); + G::$Cache->delete_value("site_history_months"); + } - public static function update_event($ID, $Date, $Title, $Link, $Category, $SubCategory, $Tags, $Body, $UserID) { - if (empty($Date)) { - $Date = sqltime(); - } else { - $Date = db_string($Date); - list($Y, $M, $D) = explode('-', $Date); - if (!checkdate($M, $D, $Y)) { - error("Error"); - } - } - $ID = (int)$ID; - $Title = db_string($Title); - $Link = db_string($Link); - $Category = (int)$Category; - $SubCategory = (int)$SubCategory; - $Tags = db_string(strtolower((preg_replace('/\s+/', '', $Tags)))); - $ExplodedTags = explode(",", $Tags); - foreach ($ExplodedTags as $Tag) { - if (!in_array($Tag, self::get_tags())) { - error("Invalid tag"); - } - } - $Body = db_string($Body); - $UserID = (int)$UserID; + public static function update_event($ID, $Date, $Title, $Link, $Category, $SubCategory, $Tags, $Body, $UserID) { + if (empty($Date)) { + $Date = sqltime(); + } else { + $Date = db_string($Date); + list($Y, $M, $D) = explode('-', $Date); + if (!checkdate($M, $D, $Y)) { + error("Error"); + } + } + $ID = (int)$ID; + $Title = db_string($Title); + $Link = db_string($Link); + $Category = (int)$Category; + $SubCategory = (int)$SubCategory; + $Tags = db_string(strtolower((preg_replace('/\s+/', '', $Tags)))); + $ExplodedTags = explode(",", $Tags); + foreach ($ExplodedTags as $Tag) { + if (!in_array($Tag, self::get_tags())) { + error("Invalid tag"); + } + } + $Body = db_string($Body); + $UserID = (int)$UserID; - if (empty($ID) || empty($Title) || empty($Category) || empty($SubCategory)) { - error("Error"); - } + if (empty($ID) || empty($Title) || empty($Category) || empty($SubCategory)) { + error("Error"); + } - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - UPDATE site_history - SET - Title = '$Title', - Url = '$Link', - Category = '$Category', - SubCategory = '$SubCategory', - Tags = '$Tags', - Body = '$Body', - AddedBy = '$UserID', - Date = '$Date' - WHERE ID = '$ID'"); - G::$DB->set_query_id($QueryID); - G::$Cache->delete_value("site_history_months"); - } + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + UPDATE site_history + SET + Title = '$Title', + Url = '$Link', + Category = '$Category', + SubCategory = '$SubCategory', + Tags = '$Tags', + Body = '$Body', + AddedBy = '$UserID', + Date = '$Date' + WHERE ID = '$ID'"); + G::$DB->set_query_id($QueryID); + G::$Cache->delete_value("site_history_months"); + } - public static function delete_event($ID) { - if (!is_numeric($ID)) { - error(404); - } - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - DELETE FROM site_history - WHERE ID = '$ID'"); - G::$DB->set_query_id($QueryID); - G::$Cache->delete_value("site_history_months"); - } + public static function delete_event($ID) { + if (!is_numeric($ID)) { + error(404); + } + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + DELETE FROM site_history + WHERE ID = '$ID'"); + G::$DB->set_query_id($QueryID); + G::$Cache->delete_value("site_history_months"); + } - public static function get_categories() { - return self::$Categories; - } + public static function get_categories() { + return self::$Categories; + } - public static function get_sub_categories() { - return self::$SubCategories; - } + public static function get_sub_categories() { + return self::$SubCategories; + } - public static function get_tags() { - return self::$Tags; - } + public static function get_tags() { + return self::$Tags; + } } diff --git a/classes/sitehistoryview.class.php b/classes/sitehistoryview.class.php index 38eafeab9..65311c0e9 100644 --- a/classes/sitehistoryview.class.php +++ b/classes/sitehistoryview.class.php @@ -1,226 +1,225 @@ - - - + Create new event + + -
    -
    -
    - - Edit - +
    +
    +
    + + Edit + - - - - - + + - + + - - - - - -
    -
    - -
    -
    - -
    - -
    - -
    - + + + + +
    +
    + +
    +
    + +
    + +
    + +
    +$Tag" . ", "; - } - echo rtrim($FormattedTags, ', '); - } + private static function render_tags($Tags) { + $Tags = explode(',', $Tags); + natcasesort($Tags); + $FormattedTags = ''; + foreach ($Tags as $Tag) { + $FormattedTags .= "$Tag" . ", "; + } + echo rtrim($FormattedTags, ', '); + } - public static function render_months($Months) { ?> -
    -
    Calendar
    -
    -$Year"; - } + public static function render_months($Months) { ?> +
    +
    Calendar
    +
    +$Year"; + } ?> - - -
    -
    -&year="> + +
    +
    + -
    -
    Search
    -
    -
    - - -

    - -

    - + +

    + +

    + -

    - +

    + -

    - -
    -
    -
    - value=""> + + +

    + + + + + -
    - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + +
    Title: - -
    Link: - -
    Date: - /> -
    Category: - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - -
    Title: + +
    Link: + +
    Date: + /> +
    Category: + -
    Subcategory: - -
    Tags: - - -
    Body: - -
    - - - - - - value=""> + + +
    Subcategory: + +
    Tags: + + +
    Body: + +
    + + + + + + -
    - -
      - -
    • - - - - - - -
    • - -
    -
    - +
    + +
      + +
    • + + + + + + +
    • + +
    +
    +get_value('site_option_' . $Name); if ($Value === false) { - G::$DB->query("SELECT Value FROM site_options WHERE Name = '" . db_string($Name) . "'"); - - if (G::$DB->has_results()) { - list($Value) = G::$DB->next_record(); - G::$Cache->cache_value('site_option_' . $Name, $Value); + G::$DB->prepared_query('SELECT Value FROM site_options WHERE Name = ?', $Name); + list($Value) = G::$DB->next_record(); + if (isset($Value)) { + G::$Cache->cache_value('site_option_' . $Name, $Value, 3600); } } - - return ($Value === false ? $DefaultValue : $Value); + return (is_null($Value) ? $DefaultValue : $Value); } -} \ No newline at end of file +} diff --git a/classes/sphinxql.class.php b/classes/sphinxql.class.php index 783b3eed4..d80ff4b5b 100644 --- a/classes/sphinxql.class.php +++ b/classes/sphinxql.class.php @@ -1,149 +1,149 @@ -Server = $Server; - $this->Port = $Port; - $this->Socket = $Socket; - $this->Ident = self::get_ident($Server, $Port, $Socket); - } + /** + * Initialize Sphinxql object + * + * @param string $Server server address or hostname + * @param int $Port listening port + * @param string $Socket Unix socket address, overrides $Server:$Port + */ + public function __construct($Server, $Port, $Socket) { + $this->Server = $Server; + $this->Port = $Port; + $this->Socket = $Socket; + $this->Ident = self::get_ident($Server, $Port, $Socket); + } - /** - * Create server ident based on connection information - * - * @param string $Server server address or hostname - * @param int $Port listening port - * @param string $Socket Unix socket address, overrides $Server:$Port - * @return string identification string - */ - private static function get_ident($Server, $Port, $Socket) { - if ($Socket) { - return $Socket; - } else { - return "$Server:$Port"; - } - } + /** + * Create server ident based on connection information + * + * @param string $Server server address or hostname + * @param int $Port listening port + * @param string $Socket Unix socket address, overrides $Server:$Port + * @return string identification string + */ + private static function get_ident($Server, $Port, $Socket) { + if ($Socket) { + return $Socket; + } else { + return "$Server:$Port"; + } + } - /** - * Create Sphinxql object or return existing one - * - * @param string $Server server address or hostname - * @param int $Port listening port - * @param string $Socket Unix socket address, overrides $Server:$Port - * @return Sphinxql object - */ - public static function init_connection($Server, $Port, $Socket) { - $Ident = self::get_ident($Server, $Port, $Socket); - if (!isset(self::$Connections[$Ident])) { - self::$Connections[$Ident] = new Sphinxql($Server, $Port, $Socket); - } - return self::$Connections[$Ident]; - } + /** + * Create Sphinxql object or return existing one + * + * @param string $Server server address or hostname + * @param int $Port listening port + * @param string $Socket Unix socket address, overrides $Server:$Port + * @return Sphinxql object + */ + public static function init_connection($Server, $Port, $Socket) { + $Ident = self::get_ident($Server, $Port, $Socket); + if (!isset(self::$Connections[$Ident])) { + self::$Connections[$Ident] = new Sphinxql($Server, $Port, $Socket); + } + return self::$Connections[$Ident]; + } - /** - * Connect the Sphinxql object to the Sphinx server - */ - public function sph_connect() { - if ($this->Connected || $this->connect_errno) { - return; - } - global $Debug; - $Debug->set_flag("Connecting to Sphinx server $this->Ident"); - for ($Attempt = 0; $Attempt < 3; $Attempt++) { - parent::__construct($this->Server, '', '', '', $this->Port, $this->Socket); - if (!$this->connect_errno) { - $this->Connected = true; - break; - } - sleep(1); - } - if ($this->connect_errno) { - $Errno = $this->connect_errno; - $Error = $this->connect_error; - $this->error("Connection failed. (".strval($Errno).": ".strval($Error).")"); - $Debug->set_flag("Could not connect to Sphinx server $this->Ident. (".strval($Errno).": ".strval($Error).")"); - } else { - $Debug->set_flag("Connected to Sphinx server $this->Ident"); - } - } + /** + * Connect the Sphinxql object to the Sphinx server + */ + public function sph_connect() { + if ($this->Connected || $this->connect_errno) { + return; + } + global $Debug; + $Debug->set_flag("Connecting to Sphinx server $this->Ident"); + for ($Attempt = 0; $Attempt < 3; $Attempt++) { + parent::__construct($this->Server, '', '', '', $this->Port, $this->Socket); + if (!$this->connect_errno) { + $this->Connected = true; + break; + } + sleep(1); + } + if ($this->connect_errno) { + $Errno = $this->connect_errno; + $Error = $this->connect_error; + $this->error("Connection failed. (".strval($Errno).": ".strval($Error).")"); + $Debug->set_flag("Could not connect to Sphinx server $this->Ident. (".strval($Errno).": ".strval($Error).")"); + } else { + $Debug->set_flag("Connected to Sphinx server $this->Ident"); + } + } - /** - * Print a message to privileged users and optionally halt page processing - * - * @param string $Msg message to display - * @param bool $Halt halt page processing. Default is to continue processing the page - * @return Sphinxql object - */ - public function error($Msg, $Halt = false) { - global $Debug; - $ErrorMsg = 'SphinxQL ('.$this->Ident.'): '.strval($Msg); - $Debug->analysis('SphinxQL Error', $ErrorMsg, 3600*24); - if ($Halt === true && (DEBUG_MODE || check_perms('site_debug'))) { - echo '
    '.display_str($ErrorMsg).'
    '; - die(); - } elseif ($Halt === true) { - error('-1'); - } - } + /** + * Print a message to privileged users and optionally halt page processing + * + * @param string $Msg message to display + * @param bool $Halt halt page processing. Default is to continue processing the page + * @return Sphinxql object + */ + public function error($Msg, $Halt = false) { + global $Debug; + $ErrorMsg = 'SphinxQL ('.$this->Ident.'): '.strval($Msg); + $Debug->analysis('SphinxQL Error', $ErrorMsg, 3600*24); + if ($Halt === true && (DEBUG_MODE || check_perms('site_debug'))) { + echo '
    '.display_str($ErrorMsg).'
    '; + die(); + } elseif ($Halt === true) { + error('-1'); + } + } - /** - * Escape special characters before sending them to the Sphinx server. - * Two escapes needed because the first one is eaten up by the mysql driver. - * Lowercase ASCII characters because some Sphinx operators are all caps words. - * - * @param string $String string to escape - * @return string escaped string - */ - public static function sph_escape_string($String) { - return strtr(strtolower($String), array( - '('=>'\\\\(', - ')'=>'\\\\)', - '|'=>'\\\\|', - '-'=>'\\\\-', - '@'=>'\\\\@', - '~'=>'\\\\~', - '&'=>'\\\\&', - '\''=>'\\\'', - '<'=>'\\\\<', - '!'=>'\\\\!', - '"'=>'\\\\"', - '/'=>'\\\\/', - '*'=>'\\\\*', - '$'=>'\\\\$', - '^'=>'\\\\^', - '\\'=>'\\\\\\\\') - ); - } + /** + * Escape special characters before sending them to the Sphinx server. + * Two escapes needed because the first one is eaten up by the mysql driver. + * Lowercase ASCII characters because some Sphinx operators are all caps words. + * + * @param string $String string to escape + * @return string escaped string + */ + public static function sph_escape_string($String) { + return strtr(strtolower($String), array( + '('=>'\\\\(', + ')'=>'\\\\)', + '|'=>'\\\\|', + '-'=>'\\\\-', + '@'=>'\\\\@', + '~'=>'\\\\~', + '&'=>'\\\\&', + '\''=>'\\\'', + '<'=>'\\\\<', + '!'=>'\\\\!', + '"'=>'\\\\"', + '/'=>'\\\\/', + '*'=>'\\\\*', + '$'=>'\\\\$', + '^'=>'\\\\^', + '\\'=>'\\\\\\\\') + ); + } - /** - * Register sent queries globally for later retrieval by debug functions - * - * @param string $QueryString query text - * @param param $QueryProcessTime time building and processing the query - */ - public static function register_query($QueryString, $QueryProcessTime) { - self::$Queries[] = array($QueryString, $QueryProcessTime); - self::$Time += $QueryProcessTime; - } + /** + * Register sent queries globally for later retrieval by debug functions + * + * @param string $QueryString query text + * @param param $QueryProcessTime time building and processing the query + */ + public static function register_query($QueryString, $QueryProcessTime) { + self::$Queries[] = array($QueryString, $QueryProcessTime); + self::$Time += $QueryProcessTime; + } } diff --git a/classes/sphinxqlquery.class.php b/classes/sphinxqlquery.class.php index b03f8e911..979fc451c 100644 --- a/classes/sphinxqlquery.class.php +++ b/classes/sphinxqlquery.class.php @@ -1,388 +1,388 @@ -Sphinxql = Sphinxql::init_connection($Server, $Port, $Socket); - $this->reset(); - } + /** + * Initialize Sphinxql object + * + * @param string $Server server address or hostname + * @param int $Port listening port + * @param string $Socket Unix socket address, overrides $Server:$Port + */ + public function __construct($Server = SPHINXQL_HOST, $Port = SPHINXQL_PORT, $Socket = SPHINXQL_SOCK) { + $this->Sphinxql = Sphinxql::init_connection($Server, $Port, $Socket); + $this->reset(); + } - /** - * Specify what data the Sphinx query is supposed to return - * - * @param string $Fields Attributes and expressions - * @return current SphinxqlQuery object - */ - public function select($Fields) { - $this->Select = $Fields; - return $this; - } + /** + * Specify what data the Sphinx query is supposed to return + * + * @param string $Fields Attributes and expressions + * @return current SphinxqlQuery object + */ + public function select($Fields) { + $this->Select = $Fields; + return $this; + } - /** - * Specify the indexes to use in the search - * - * @param string $Indexes comma separated list of indexes - * @return current SphinxqlQuery object - */ - public function from($Indexes) { - $this->Indexes = $Indexes; - return $this; - } + /** + * Specify the indexes to use in the search + * + * @param string $Indexes comma separated list of indexes + * @return current SphinxqlQuery object + */ + public function from($Indexes) { + $this->Indexes = $Indexes; + return $this; + } - /** - * Add attribute filter. Calling multiple filter functions results in boolean AND between each condition. - * - * @param string $Attribute attribute which the filter will apply to - * @param mixed $Values scalar or array of numerical values. Array uses boolean OR in query condition - * @param bool $Exclude whether to exclude or include matching documents. Default mode is to include matches - * @return current SphinxqlQuery object - */ - public function where($Attribute, $Values, $Exclude = false) { - if (empty($Attribute) || !isset($Values)) { - $this->error("Attribute name and filter value are required."); - return $this; - } - $Filters = array(); - if (is_array($Values)) { - foreach ($Values as $Value) { - if (!is_number($Value)) { - $this->error("Filters only support numeric values."); - return $this; - } - } - if ($Exclude) { - $Filters[] = "$Attribute NOT IN (".implode(",", $Values).")"; - } else { - $Filters[] = "$Attribute IN (".implode(",", $Values).")"; - } - } else { - if (!is_number($Values)) { - $this->error("Filters only support numeric values."); - return $this; - } - if ($Exclude) { - $Filters[] = "$Attribute != $Values"; - } else { - $Filters[] = "$Attribute = $Values"; - } - } - $this->Filters[] = implode(" AND ", $Filters); - return $this; - } + /** + * Add attribute filter. Calling multiple filter functions results in boolean AND between each condition. + * + * @param string $Attribute attribute which the filter will apply to + * @param mixed $Values scalar or array of numerical values. Array uses boolean OR in query condition + * @param bool $Exclude whether to exclude or include matching documents. Default mode is to include matches + * @return current SphinxqlQuery object + */ + public function where($Attribute, $Values, $Exclude = false) { + if (empty($Attribute) || !isset($Values)) { + $this->error("Attribute name and filter value are required."); + return $this; + } + $Filters = []; + if (is_array($Values)) { + foreach ($Values as $Value) { + if (!is_number($Value)) { + $this->error("Filters only support numeric values."); + return $this; + } + } + if ($Exclude) { + $Filters[] = "$Attribute NOT IN (".implode(",", $Values).")"; + } else { + $Filters[] = "$Attribute IN (".implode(",", $Values).")"; + } + } else { + if (!is_number($Values)) { + $this->error("Filters only support numeric values."); + return $this; + } + if ($Exclude) { + $Filters[] = "$Attribute != $Values"; + } else { + $Filters[] = "$Attribute = $Values"; + } + } + $this->Filters[] = implode(" AND ", $Filters); + return $this; + } - /** - * Add attribute less-than filter. Calling multiple filter functions results in boolean AND between each condition. - * - * @param string $Attribute attribute which the filter will apply to - * @param array $Value upper limit for matches - * @param bool $Inclusive whether to use <= or < - * @return current SphinxqlQuery object - */ - public function where_lt($Attribute, $Value, $Inclusive = false) { - if (empty($Attribute) || !isset($Value) || !is_number($Value)) { - $this->error("Attribute name is required and only numeric filters are supported."); - return $this; - } - $this->Filters[] = $Inclusive ? "$Attribute <= $Value" : "$Attribute < $Value"; - return $this; - } + /** + * Add attribute less-than filter. Calling multiple filter functions results in boolean AND between each condition. + * + * @param string $Attribute attribute which the filter will apply to + * @param array $Value upper limit for matches + * @param bool $Inclusive whether to use <= or < + * @return current SphinxqlQuery object + */ + public function where_lt($Attribute, $Value, $Inclusive = false) { + if (empty($Attribute) || !isset($Value) || !is_number($Value)) { + $this->error("Attribute name is required and only numeric filters are supported."); + return $this; + } + $this->Filters[] = $Inclusive ? "$Attribute <= $Value" : "$Attribute < $Value"; + return $this; + } - /** - * Add attribute greater-than filter. Calling multiple filter functions results in boolean AND between each condition. - * - * @param string $Attribute attribute which the filter will apply to - * @param array $Value lower limit for matches - * @param bool $Inclusive whether to use >= or > - * @return current SphinxqlQuery object - */ - public function where_gt($Attribute, $Value, $Inclusive = false) { - if (empty($Attribute) || !isset($Value) || !is_number($Value)) { - $this->error("Attribute name is required and only numeric filters are supported."); - return $this; - } - $this->Filters[] = $Inclusive ? "$Attribute >= $Value" : "$Attribute > $Value"; - return $this; - } + /** + * Add attribute greater-than filter. Calling multiple filter functions results in boolean AND between each condition. + * + * @param string $Attribute attribute which the filter will apply to + * @param array $Value lower limit for matches + * @param bool $Inclusive whether to use >= or > + * @return current SphinxqlQuery object + */ + public function where_gt($Attribute, $Value, $Inclusive = false) { + if (empty($Attribute) || !isset($Value) || !is_number($Value)) { + $this->error("Attribute name is required and only numeric filters are supported."); + return $this; + } + $this->Filters[] = $Inclusive ? "$Attribute >= $Value" : "$Attribute > $Value"; + return $this; + } - /** - * Add attribute range filter. Calling multiple filter functions results in boolean AND between each condition. - * - * @param string $Attribute attribute which the filter will apply to - * @param array $Values pair of numerical values that defines the filter range - * @return current SphinxqlQuery object - */ - public function where_between($Attribute, $Values) { - if (empty($Attribute) || empty($Values) || count($Values) != 2 || !is_number($Values[0]) || !is_number($Values[1])) { - $this->error("Filter range requires array of two numerical boundaries as values."); - return $this; - } - $this->Filters[] = "$Attribute BETWEEN $Values[0] AND $Values[1]"; - return $this; - } + /** + * Add attribute range filter. Calling multiple filter functions results in boolean AND between each condition. + * + * @param string $Attribute attribute which the filter will apply to + * @param array $Values pair of numerical values that defines the filter range + * @return current SphinxqlQuery object + */ + public function where_between($Attribute, $Values) { + if (empty($Attribute) || empty($Values) || count($Values) != 2 || !is_number($Values[0]) || !is_number($Values[1])) { + $this->error("Filter range requires array of two numerical boundaries as values."); + return $this; + } + $this->Filters[] = "$Attribute BETWEEN $Values[0] AND $Values[1]"; + return $this; + } - /** - * Add fulltext query expression. Calling multiple filter functions results in boolean AND between each condition. - * Query expression is escaped automatically - * - * @param string $Expr query expression - * @param string $Field field to match $Expr against. Default is *, which means all available fields - * @return current SphinxqlQuery object - */ - public function where_match($Expr, $Field = '*', $Escape = true) { - if (empty($Expr)) { - return $this; - } - if ($Field !== false) { - $Field = "@$Field "; - } - if ($Escape === true) { - $this->Expressions[] = "$Field".Sphinxql::sph_escape_string($Expr); - } else { - $this->Expressions[] = $Field.$Expr; - } - return $this; - } + /** + * Add fulltext query expression. Calling multiple filter functions results in boolean AND between each condition. + * Query expression is escaped automatically + * + * @param string $Expr query expression + * @param string $Field field to match $Expr against. Default is *, which means all available fields + * @return current SphinxqlQuery object + */ + public function where_match($Expr, $Field = '*', $Escape = true) { + if (empty($Expr)) { + return $this; + } + if ($Field !== false) { + $Field = "@$Field "; + } + if ($Escape === true) { + $this->Expressions[] = "$Field".Sphinxql::sph_escape_string($Expr); + } else { + $this->Expressions[] = $Field.$Expr; + } + return $this; + } - /** - * Specify the order of the matches. Calling this function multiple times sets secondary priorities - * - * @param string $Attribute attribute to use for sorting. - * Passing an empty attribute value will clear the current sort settings - * @param string $Mode sort method to apply to the selected attribute - * @return current SphinxqlQuery object - */ - public function order_by($Attribute = false, $Mode = false) { - if (empty($Attribute)) { - $this->SortBy = array(); - } else { - $this->SortBy[] = "$Attribute $Mode"; - } - return $this; - } + /** + * Specify the order of the matches. Calling this function multiple times sets secondary priorities + * + * @param string $Attribute attribute to use for sorting. + * Passing an empty attribute value will clear the current sort settings + * @param string $Mode sort method to apply to the selected attribute + * @return current SphinxqlQuery object + */ + public function order_by($Attribute = false, $Mode = false) { + if (empty($Attribute)) { + $this->SortBy = []; + } else { + $this->SortBy[] = "$Attribute $Mode"; + } + return $this; + } - /** - * Specify how the results are grouped - * - * @param string $Attribute group matches with the same $Attribute value. - * Passing an empty attribute value will clear the current group settings - * @return current SphinxqlQuery object - */ - public function group_by($Attribute = false) { - if (empty($Attribute)) { - $this->GroupBy = ''; - } else { - $this->GroupBy = $Attribute; - } - return $this; - } + /** + * Specify how the results are grouped + * + * @param string $Attribute group matches with the same $Attribute value. + * Passing an empty attribute value will clear the current group settings + * @return current SphinxqlQuery object + */ + public function group_by($Attribute = false) { + if (empty($Attribute)) { + $this->GroupBy = ''; + } else { + $this->GroupBy = $Attribute; + } + return $this; + } - /** - * Specify the order of the results within groups - * - * @param string $Attribute attribute to use for sorting. - * Passing an empty attribute will clear the current group sort settings - * @param string $Mode sort method to apply to the selected attribute - * @return current SphinxqlQuery object - */ - public function order_group_by($Attribute = false, $Mode = false) { - if (empty($Attribute)) { - $this->SortGroupBy = ''; - } else { - $this->SortGroupBy = "$Attribute $Mode"; - } - return $this; - } + /** + * Specify the order of the results within groups + * + * @param string $Attribute attribute to use for sorting. + * Passing an empty attribute will clear the current group sort settings + * @param string $Mode sort method to apply to the selected attribute + * @return current SphinxqlQuery object + */ + public function order_group_by($Attribute = false, $Mode = false) { + if (empty($Attribute)) { + $this->SortGroupBy = ''; + } else { + $this->SortGroupBy = "$Attribute $Mode"; + } + return $this; + } - /** - * Specify the offset and amount of matches to return - * - * @param int $Offset number of matches to discard - * @param int $Limit number of matches to return - * @param int $MaxMatches number of results to store in the Sphinx server's memory. Must be >= ($Offset+$Limit) - * @return current SphinxqlQuery object - */ - public function limit($Offset, $Limit, $MaxMatches = SPHINX_MAX_MATCHES) { - $this->Limits = "$Offset, $Limit"; - $this->set('max_matches', $MaxMatches); - return $this; - } + /** + * Specify the offset and amount of matches to return + * + * @param int $Offset number of matches to discard + * @param int $Limit number of matches to return + * @param int $MaxMatches number of results to store in the Sphinx server's memory. Must be >= ($Offset+$Limit) + * @return current SphinxqlQuery object + */ + public function limit($Offset, $Limit, $MaxMatches = SPHINX_MAX_MATCHES) { + $this->Limits = "$Offset, $Limit"; + $this->set('max_matches', $MaxMatches); + return $this; + } - /** - * Tweak the settings to use for the query. Sanity checking shouldn't be needed as Sphinx already does it - * - * @param string $Name setting name - * @param mixed $Value value - * @return current SphinxqlQuery object - */ - public function set($Name, $Value) { - $this->Options[$Name] = $Value; - return $this; - } + /** + * Tweak the settings to use for the query. Sanity checking shouldn't be needed as Sphinx already does it + * + * @param string $Name setting name + * @param mixed $Value value + * @return current SphinxqlQuery object + */ + public function set($Name, $Value) { + $this->Options[$Name] = $Value; + return $this; + } - /** - * Combine the query options into a valid Sphinx query segment - * - * @return string of options - */ - private function build_options() { - $Options = array(); - foreach ($this->Options as $Option => $Value) { - $Options[] = "$Option = $Value"; - } - return implode(', ', $Options); - } + /** + * Combine the query options into a valid Sphinx query segment + * + * @return string of options + */ + private function build_options() { + $Options = []; + foreach ($this->Options as $Option => $Value) { + $Options[] = "$Option = $Value"; + } + return implode(', ', $Options); + } - /** - * Combine the query conditions into a valid Sphinx query segment - */ - private function build_query() { - if (!$this->Indexes) { - $this->error('Index name is required.'); - return false; - } - $this->QueryString = "SELECT $this->Select\nFROM $this->Indexes"; - if (!empty($this->Expressions)) { - $this->Filters['expr'] = "MATCH('".implode(' ', $this->Expressions)."')"; - } - if (!empty($this->Filters)) { - $this->QueryString .= "\nWHERE ".implode("\n\tAND ", $this->Filters); - unset($this->Filters['expr']); - } - if (!empty($this->GroupBy)) { - $this->QueryString .= "\nGROUP BY $this->GroupBy"; - } - if (!empty($this->SortGroupBy)) { - $this->QueryString .= "\nWITHIN GROUP ORDER BY $this->SortGroupBy"; - } - if (!empty($this->SortBy)) { - $this->QueryString .= "\nORDER BY ".implode(", ", $this->SortBy); - } - if (!empty($this->Limits)) { - $this->QueryString .= "\nLIMIT $this->Limits"; - } - if (!empty($this->Options)) { - $Options = $this->build_options(); - $this->QueryString .= "\nOPTION $Options"; - } - } + /** + * Combine the query conditions into a valid Sphinx query segment + */ + private function build_query() { + if (!$this->Indexes) { + $this->error('Index name is required.'); + return false; + } + $this->QueryString = "SELECT $this->Select\nFROM $this->Indexes"; + if (!empty($this->Expressions)) { + $this->Filters['expr'] = "MATCH('".implode(' ', $this->Expressions)."')"; + } + if (!empty($this->Filters)) { + $this->QueryString .= "\nWHERE ".implode("\n\tAND ", $this->Filters); + unset($this->Filters['expr']); + } + if (!empty($this->GroupBy)) { + $this->QueryString .= "\nGROUP BY $this->GroupBy"; + } + if (!empty($this->SortGroupBy)) { + $this->QueryString .= "\nWITHIN GROUP ORDER BY $this->SortGroupBy"; + } + if (!empty($this->SortBy)) { + $this->QueryString .= "\nORDER BY ".implode(", ", $this->SortBy); + } + if (!empty($this->Limits)) { + $this->QueryString .= "\nLIMIT $this->Limits"; + } + if (!empty($this->Options)) { + $Options = $this->build_options(); + $this->QueryString .= "\nOPTION $Options"; + } + } - /** - * Construct and send the query. Register the query in the global Sphinxql object - * - * @param bool GetMeta whether to fetch meta data for the executed query. Default is yes - * @return SphinxqlResult object - */ - public function query($GetMeta = true) { - $QueryStartTime = microtime(true); - $this->build_query(); - if (count($this->Errors) > 0) { - $ErrorMsg = implode("\n", $this->Errors); - $this->Sphinxql->error("Query builder found errors:\n$ErrorMsg"); - return new SphinxqlResult(null, null, 1, $ErrorMsg); - } - $QueryString = $this->QueryString; - $Result = $this->send_query($GetMeta); - $QueryProcessTime = (microtime(true) - $QueryStartTime)*1000; - Sphinxql::register_query($QueryString, $QueryProcessTime); - return $Result; - } + /** + * Construct and send the query. Register the query in the global Sphinxql object + * + * @param bool GetMeta whether to fetch meta data for the executed query. Default is yes + * @return SphinxqlResult object + */ + public function query($GetMeta = true) { + $QueryStartTime = microtime(true); + $this->build_query(); + if (count($this->Errors) > 0) { + $ErrorMsg = implode("\n", $this->Errors); + $this->Sphinxql->error("Query builder found errors:\n$ErrorMsg"); + return new SphinxqlResult(null, null, 1, $ErrorMsg); + } + $QueryString = $this->QueryString; + $Result = $this->send_query($GetMeta); + $QueryProcessTime = (microtime(true) - $QueryStartTime)*1000; + Sphinxql::register_query($QueryString, $QueryProcessTime); + return $Result; + } - /** - * Run a manually constructed query - * - * @param string Query query expression - * @param bool GetMeta whether to fetch meta data for the executed query. Default is yes - * @return SphinxqlResult object - */ - public function raw_query($Query, $GetMeta = true) { - $this->QueryString = $Query; - return $this->send_query($GetMeta); - } + /** + * Run a manually constructed query + * + * @param string Query query expression + * @param bool GetMeta whether to fetch meta data for the executed query. Default is yes + * @return SphinxqlResult object + */ + public function raw_query($Query, $GetMeta = true) { + $this->QueryString = $Query; + return $this->send_query($GetMeta); + } - /** - * Run a pre-processed query. Only used internally - * - * @param bool GetMeta whether to fetch meta data for the executed query - * @return SphinxqlResult object - */ - private function send_query($GetMeta) { - if (!$this->QueryString) { - return false; - } - $this->Sphinxql->sph_connect(); - $Result = $this->Sphinxql->query($this->QueryString); - if ($Result === false) { - $Errno = $this->Sphinxql->errno; - $Error = $this->Sphinxql->error; - $this->Sphinxql->error("Query returned error $Errno ($Error).\n$this->QueryString"); - $Meta = null; - } else { - $Errno = 0; - $Error = ''; - $Meta = $GetMeta ? $this->get_meta() : null; - } - return new SphinxqlResult($Result, $Meta, $Errno, $Error); - } + /** + * Run a pre-processed query. Only used internally + * + * @param bool GetMeta whether to fetch meta data for the executed query + * @return SphinxqlResult object + */ + private function send_query($GetMeta) { + if (!$this->QueryString) { + return false; + } + $this->Sphinxql->sph_connect(); + $Result = $this->Sphinxql->query($this->QueryString); + if ($Result === false) { + $Errno = $this->Sphinxql->errno; + $Error = $this->Sphinxql->error; + $this->Sphinxql->error("Query returned error $Errno ($Error).\n$this->QueryString"); + $Meta = null; + } else { + $Errno = 0; + $Error = ''; + $Meta = $GetMeta ? $this->get_meta() : null; + } + return new SphinxqlResult($Result, $Meta, $Errno, $Error); + } - /** - * Reset all query options and conditions - */ - public function reset() { - $this->Errors = array(); - $this->Expressions = array(); - $this->Filters = array(); - $this->GroupBy = ''; - $this->Indexes = ''; - $this->Limits = array(); - $this->Options = array('ranker' => 'none'); - $this->QueryString = ''; - $this->Select = '*'; - $this->SortBy = array(); - $this->SortGroupBy = ''; - } + /** + * Reset all query options and conditions + */ + public function reset() { + $this->Errors = []; + $this->Expressions = []; + $this->Filters = []; + $this->GroupBy = ''; + $this->Indexes = ''; + $this->Limits = []; + $this->Options = array('ranker' => 'none'); + $this->QueryString = ''; + $this->Select = '*'; + $this->SortBy = []; + $this->SortGroupBy = ''; + } - /** - * Fetch and store meta data for the last executed query - * - * @return meta data - */ - private function get_meta() { - return $this->raw_query("SHOW META", false)->to_pair(0, 1); - } + /** + * Fetch and store meta data for the last executed query + * + * @return meta data + */ + private function get_meta() { + return $this->raw_query("SHOW META", false)->to_pair(0, 1); + } - /** - * Copy attribute filters from another SphinxqlQuery object - * - * @param SphinxqlQuery $SphQLSource object to copy the filters from - * @return current SphinxqlQuery object - */ - public function copy_attributes_from($SphQLSource) { - $this->Filters = $SphQLSource->Filters; - } + /** + * Copy attribute filters from another SphinxqlQuery object + * + * @param SphinxqlQuery $SphQLSource object to copy the filters from + * @return current SphinxqlQuery object + */ + public function copy_attributes_from($SphQLSource) { + $this->Filters = $SphQLSource->Filters; + } - /** - * Store error messages - */ - private function error($Msg) { - $this->Errors[] = $Msg; - } + /** + * Store error messages + */ + private function error($Msg) { + $this->Errors[] = $Msg; + } } diff --git a/classes/sphinxqlresult.class.php b/classes/sphinxqlresult.class.php index 9fd431855..d50c48fc8 100644 --- a/classes/sphinxqlresult.class.php +++ b/classes/sphinxqlresult.class.php @@ -1,143 +1,143 @@ -Result = $Result; - $this->Meta = $Meta; - $this->Errno = $Errno; - $this->Error = $Error; - } + /** + * Create Sphinxql result object + * + * @param mysqli_result $Result query results + * @param array $Meta meta data for the query + * @param int $Errno error code returned by the query upon failure + * @param string $Error error message returned by the query upon failure + */ + public function __construct($Result, $Meta, $Errno, $Error) { + $this->Result = $Result; + $this->Meta = $Meta; + $this->Errno = $Errno; + $this->Error = $Error; + } - /** - * Redirect to the Mysqli result object if a nonexistent method is called - * - * @param string $Name method name - * @param array $Arguments arguments used in the function call - * @return whatever the parent function returns - */ - public function __call($Name, $Arguments) { - return call_user_func_array(array($this->Result, $Name), $Arguments); - } + /** + * Redirect to the Mysqli result object if a nonexistent method is called + * + * @param string $Name method name + * @param array $Arguments arguments used in the function call + * @return whatever the parent function returns + */ + public function __call($Name, $Arguments) { + return call_user_func_array(array($this->Result, $Name), $Arguments); + } - /** - * Did the query find anything? - * - * @return bool results were found - */ - public function has_results() { - return $this->get_meta('total') > 0; - } + /** + * Did the query find anything? + * + * @return bool results were found + */ + public function has_results() { + return $this->get_meta('total') > 0; + } - /** - * Collect and return the specified key of all results as a list - * - * @param string $Key key containing the desired data - * @return array with the $Key value of all results - */ - public function collect($Key) { - $Return = array(); - while ($Row = $this->fetch_array()) { - $Return[] = $Row[$Key]; - } - $this->data_seek(0); - return $Return; - } + /** + * Collect and return the specified key of all results as a list + * + * @param string $Key key containing the desired data + * @return array with the $Key value of all results + */ + public function collect($Key) { + $Return = []; + while ($Row = $this->fetch_array()) { + $Return[] = $Row[$Key]; + } + $this->data_seek(0); + return $Return; + } - /** - * Collect and return all available data for the matches optionally indexed by a specified key - * - * @param string $Key key to use as indexing value - * @param string $ResultType method to use when fetching data from the mysqli_result object. Default is MYSQLI_ASSOC - * @return array with all available data for the matches - */ - public function to_array($Key, $ResultType = MYSQLI_ASSOC) { - $Return = array(); - while ($Row = $this->fetch_array($ResultType)) { - if ($Key !== false) { - $Return[$Row[$Key]] = $Row; - } else { - $Return[] = $Row; - } - } - $this->data_seek(0); - return $Return; - } + /** + * Collect and return all available data for the matches optionally indexed by a specified key + * + * @param string $Key key to use as indexing value + * @param string $ResultType method to use when fetching data from the mysqli_result object. Default is MYSQLI_ASSOC + * @return array with all available data for the matches + */ + public function to_array($Key, $ResultType = MYSQLI_ASSOC) { + $Return = []; + while ($Row = $this->fetch_array($ResultType)) { + if ($Key !== false) { + $Return[$Row[$Key]] = $Row; + } else { + $Return[] = $Row; + } + } + $this->data_seek(0); + return $Return; + } - /** - * Collect pairs of keys for all matches - * - * @param string $Key1 key to use as indexing value - * @param string $Key2 key to use as value - * @return array with $Key1 => $Key2 pairs for matches - */ - public function to_pair($Key1, $Key2) { - $Return = array(); - while ($Row = $this->fetch_array()) { - $Return[$Row[$Key1]] = $Row[$Key2]; - } - $this->data_seek(0); - return $Return; - } + /** + * Collect pairs of keys for all matches + * + * @param string $Key1 key to use as indexing value + * @param string $Key2 key to use as value + * @return array with $Key1 => $Key2 pairs for matches + */ + public function to_pair($Key1, $Key2) { + $Return = []; + while ($Row = $this->fetch_array()) { + $Return[$Row[$Key1]] = $Row[$Key2]; + } + $this->data_seek(0); + return $Return; + } - /** - * Return specified portions of the current Sphinxql result object's meta data - * - * @param mixed $Keys scalar or array with keys to return. Default is false, which returns all meta data - * @return array with meta data - */ - public function get_meta($Keys = false) { - if ($Keys !== false) { - if (is_array($Keys)) { - $Return = array(); - foreach ($Keys as $Key) { - if (!isset($this->Meta[$Key])) { - continue; - } - $Return[$Key] = $this->Meta[$Key]; - } - return $Return; - } else { - return isset($this->Meta[$Keys]) ? $this->Meta[$Keys] : false; - } - } else { - return $this->Meta; - } - } + /** + * Return specified portions of the current Sphinxql result object's meta data + * + * @param mixed $Keys scalar or array with keys to return. Default is false, which returns all meta data + * @return array with meta data + */ + public function get_meta($Keys = false) { + if ($Keys !== false) { + if (is_array($Keys)) { + $Return = []; + foreach ($Keys as $Key) { + if (!isset($this->Meta[$Key])) { + continue; + } + $Return[$Key] = $this->Meta[$Key]; + } + return $Return; + } else { + return isset($this->Meta[$Keys]) ? $this->Meta[$Keys] : false; + } + } else { + return $this->Meta; + } + } - /** - * Return specified portions of the current Mysqli result object's information - * - * @param mixed $Keys scalar or array with keys to return. Default is false, which returns all available information - * @return array with result information - */ - public function get_result_info($Keys = false) { - if ($Keys !== false) { - if (is_array($Keys)) { - $Return = array(); - foreach ($Keys as $Key) { - if (!isset($this->Result->$Key)) { - continue; - } - $Return[$Key] = $this->Result->$Key; - } - return $Return; - } else { - return isset($this->Result->$Keys) ? $this->Result->$Keys : false; - } - } else { - return $this->Result; - } - } + /** + * Return specified portions of the current Mysqli result object's information + * + * @param mixed $Keys scalar or array with keys to return. Default is false, which returns all available information + * @return array with result information + */ + public function get_result_info($Keys = false) { + if ($Keys !== false) { + if (is_array($Keys)) { + $Return = []; + foreach ($Keys as $Key) { + if (!isset($this->Result->$Key)) { + continue; + } + $Return[$Key] = $this->Result->$Key; + } + return $Return; + } else { + return isset($this->Result->$Keys) ? $this->Result->$Keys : false; + } + } else { + return $this->Result; + } + } } diff --git a/classes/subscriptions.class.php b/classes/subscriptions.class.php index 21daf3ede..a4024f6bb 100644 --- a/classes/subscriptions.class.php +++ b/classes/subscriptions.class.php @@ -1,402 +1,402 @@ -get_query_id(); - /* - * Explanation of the parameters PageID and Page: Page contains where - * this quote comes from and can be forums, artist, collages, requests - * or torrents. The PageID contains the additional value that is - * necessary for the users_notify_quoted table. The PageIDs for the - * different Page are: forums: TopicID artist: ArtistID collages: - * CollageID requests: RequestID torrents: GroupID - */ - $Matches = array(); - preg_match_all('/\[quote(?:=(.*)(?:\|.*)?)?]|\[\/quote]/iU', $Body, $Matches, PREG_SET_ORDER); + /** + * Parse a post/comment body for quotes and notify all quoted users that have quote notifications enabled. + * @param string $Body + * @param int $PostID + * @param string $Page + * @param int $PageID + */ + public static function quote_notify($Body, $PostID, $Page, $PageID) { + $QueryID = G::$DB->get_query_id(); + /* + * Explanation of the parameters PageID and Page: Page contains where + * this quote comes from and can be forums, artist, collages, requests + * or torrents. The PageID contains the additional value that is + * necessary for the users_notify_quoted table. The PageIDs for the + * different Page are: forums: TopicID artist: ArtistID collages: + * CollageID requests: RequestID torrents: GroupID + */ + $Matches = []; + preg_match_all('/\[quote(?:=(.*)(?:\|.*)?)?]|\[\/quote]/iU', $Body, $Matches, PREG_SET_ORDER); - if (count($Matches)) { - $Usernames = array(); - $Level = 0; - foreach ($Matches as $M) { - if ($M[0] != '[/quote]') { - if ($Level == 0 && isset($M[1]) && strlen($M[1]) > 0 && preg_match(USERNAME_REGEX, $M[1])) { - $Usernames[] = preg_replace('/(^[.,]*)|([.,]*$)/', '', $M[1]); // wut? - } - ++$Level; - } else { - --$Level; - } - } - } - // remove any dupes in the array (the fast way) - $Usernames = array_flip(array_flip($Usernames)); + if (count($Matches)) { + $Usernames = []; + $Level = 0; + foreach ($Matches as $M) { + if ($M[0] != '[/quote]') { + if ($Level == 0 && isset($M[1]) && strlen($M[1]) > 0 && preg_match(USERNAME_REGEX, $M[1])) { + $Usernames[] = preg_replace('/(^[.,]*)|([.,]*$)/', '', $M[1]); // wut? + } + ++$Level; + } else { + --$Level; + } + } + } + // remove any dupes in the array (the fast way) + $Usernames = array_flip(array_flip($Usernames)); - G::$DB->query(" - SELECT m.ID - FROM users_main AS m - LEFT JOIN users_info AS i ON i.UserID = m.ID - WHERE m.Username IN ('" . implode("', '", $Usernames) . "') - AND i.NotifyOnQuote = '1' - AND i.UserID != " . G::$LoggedUser['ID']); + G::$DB->query(" + SELECT m.ID + FROM users_main AS m + LEFT JOIN users_info AS i ON i.UserID = m.ID + WHERE m.Username IN ('" . implode("', '", $Usernames) . "') + AND i.NotifyOnQuote = '1' + AND i.UserID != " . G::$LoggedUser['ID']); - $Results = G::$DB->to_array(); - foreach ($Results as $Result) { - $UserID = db_string($Result['ID']); - $QuoterID = db_string(G::$LoggedUser['ID']); - $Page = db_string($Page); - $PageID = db_string($PageID); - $PostID = db_string($PostID); + $Results = G::$DB->to_array(); + foreach ($Results as $Result) { + $UserID = db_string($Result['ID']); + $QuoterID = db_string(G::$LoggedUser['ID']); + $Page = db_string($Page); + $PageID = db_string($PageID); + $PostID = db_string($PostID); - G::$DB->query(" - INSERT IGNORE INTO users_notify_quoted - (UserID, QuoterID, Page, PageID, PostID, Date) - VALUES - ('$UserID', '$QuoterID', '$Page', '$PageID', '$PostID', '" . sqltime() . "')"); - G::$Cache->delete_value("notify_quoted_$UserID"); - if ($Page == 'forums') { - $URL = site_url() . "forums.php?action=viewthread&postid=$PostID"; - } else { - $URL = site_url() . "comments.php?action=jump&postid=$PostID"; - } - NotificationsManager::send_push($UserID, 'New Quote!', 'Quoted by ' . G::$LoggedUser['Username'] . " $URL", $URL, NotificationsManager::QUOTES); - } - G::$DB->set_query_id($QueryID); - } + G::$DB->query(" + INSERT IGNORE INTO users_notify_quoted + (UserID, QuoterID, Page, PageID, PostID, Date) + VALUES + ('$UserID', '$QuoterID', '$Page', '$PageID', '$PostID', '" . sqltime() . "')"); + G::$Cache->delete_value("notify_quoted_$UserID"); + if ($Page == 'forums') { + $URL = site_url() . "forums.php?action=viewthread&postid=$PostID"; + } else { + $URL = site_url() . "comments.php?action=jump&postid=$PostID"; + } + NotificationsManager::send_push($UserID, 'New Quote!', 'Quoted by ' . G::$LoggedUser['Username'] . " $URL", $URL, NotificationsManager::QUOTES); + } + G::$DB->set_query_id($QueryID); + } - /** - * (Un)subscribe from a forum thread. - * If UserID == 0, G::$LoggedUser[ID] is used - * @param int $TopicID - * @param int $UserID - */ - public static function subscribe($TopicID, $UserID = 0) { - if ($UserID == 0) { - $UserID = G::$LoggedUser['ID']; - } - $QueryID = G::$DB->get_query_id(); - $UserSubscriptions = self::get_subscriptions(); - $Key = self::has_subscribed($TopicID); - if ($Key !== false) { - G::$DB->query(' - DELETE FROM users_subscriptions - WHERE UserID = ' . db_string($UserID) . ' - AND TopicID = ' . db_string($TopicID)); - unset($UserSubscriptions[$Key]); - } else { - G::$DB->query(" - INSERT IGNORE INTO users_subscriptions (UserID, TopicID) - VALUES ($UserID, " . db_string($TopicID) . ")"); - array_push($UserSubscriptions, $TopicID); - } - G::$Cache->replace_value("subscriptions_user_$UserID", $UserSubscriptions, 0); - G::$Cache->delete_value("subscriptions_user_new_$UserID"); - G::$DB->set_query_id($QueryID); - } + /** + * (Un)subscribe from a forum thread. + * If UserID == 0, G::$LoggedUser[ID] is used + * @param int $TopicID + * @param int $UserID + */ + public static function subscribe($TopicID, $UserID = 0) { + if ($UserID == 0) { + $UserID = G::$LoggedUser['ID']; + } + $QueryID = G::$DB->get_query_id(); + $UserSubscriptions = self::get_subscriptions(); + $Key = self::has_subscribed($TopicID); + if ($Key !== false) { + G::$DB->query(' + DELETE FROM users_subscriptions + WHERE UserID = ' . db_string($UserID) . ' + AND TopicID = ' . db_string($TopicID)); + unset($UserSubscriptions[$Key]); + } else { + G::$DB->query(" + INSERT IGNORE INTO users_subscriptions (UserID, TopicID) + VALUES ($UserID, " . db_string($TopicID) . ")"); + array_push($UserSubscriptions, $TopicID); + } + G::$Cache->replace_value("subscriptions_user_$UserID", $UserSubscriptions, 0); + G::$Cache->delete_value("subscriptions_user_new_$UserID"); + G::$DB->set_query_id($QueryID); + } - /** - * (Un)subscribe from comments. - * If UserID == 0, G::$LoggedUser[ID] is used - * @param string $Page 'artist', 'collages', 'requests' or 'torrents' - * @param int $PageID ArtistID, CollageID, RequestID or GroupID - * @param int $UserID - */ - public static function subscribe_comments($Page, $PageID, $UserID = 0) { - if ($UserID == 0) { - $UserID = G::$LoggedUser['ID']; - } - $QueryID = G::$DB->get_query_id(); - $UserCommentSubscriptions = self::get_comment_subscriptions(); - $Key = self::has_subscribed_comments($Page, $PageID); - if ($Key !== false) { - G::$DB->query(" - DELETE FROM users_subscriptions_comments - WHERE UserID = " . db_string($UserID) . " - AND Page = '" . db_string($Page) . "' - AND PageID = " . db_string($PageID)); - unset($UserCommentSubscriptions[$Key]); - } else { - G::$DB->query(" - INSERT IGNORE INTO users_subscriptions_comments - (UserID, Page, PageID) - VALUES - ($UserID, '" . db_string($Page) . "', " . db_string($PageID) . ")"); - array_push($UserCommentSubscriptions, array($Page, $PageID)); - } - G::$Cache->replace_value("subscriptions_comments_user_$UserID", $UserCommentSubscriptions, 0); - G::$Cache->delete_value("subscriptions_comments_user_new_$UserID"); - G::$DB->set_query_id($QueryID); - } + /** + * (Un)subscribe from comments. + * If UserID == 0, G::$LoggedUser[ID] is used + * @param string $Page 'artist', 'collages', 'requests' or 'torrents' + * @param int $PageID ArtistID, CollageID, RequestID or GroupID + * @param int $UserID + */ + public static function subscribe_comments($Page, $PageID, $UserID = 0) { + if ($UserID == 0) { + $UserID = G::$LoggedUser['ID']; + } + $QueryID = G::$DB->get_query_id(); + $UserCommentSubscriptions = self::get_comment_subscriptions(); + $Key = self::has_subscribed_comments($Page, $PageID); + if ($Key !== false) { + G::$DB->query(" + DELETE FROM users_subscriptions_comments + WHERE UserID = " . db_string($UserID) . " + AND Page = '" . db_string($Page) . "' + AND PageID = " . db_string($PageID)); + unset($UserCommentSubscriptions[$Key]); + } else { + G::$DB->query(" + INSERT IGNORE INTO users_subscriptions_comments + (UserID, Page, PageID) + VALUES + ($UserID, '" . db_string($Page) . "', " . db_string($PageID) . ")"); + array_push($UserCommentSubscriptions, array($Page, $PageID)); + } + G::$Cache->replace_value("subscriptions_comments_user_$UserID", $UserCommentSubscriptions, 0); + G::$Cache->delete_value("subscriptions_comments_user_new_$UserID"); + G::$DB->set_query_id($QueryID); + } - /** - * Read $UserID's subscriptions. If the cache key isn't set, it gets filled. - * If UserID == 0, G::$LoggedUser[ID] is used - * @param int $UserID - * @return array Array of TopicIDs - */ - public static function get_subscriptions($UserID = 0) { - if ($UserID == 0) { - $UserID = G::$LoggedUser['ID']; - } - $QueryID = G::$DB->get_query_id(); - $UserSubscriptions = G::$Cache->get_value("subscriptions_user_$UserID"); - if ($UserSubscriptions === false) { - G::$DB->query(' - SELECT TopicID - FROM users_subscriptions - WHERE UserID = ' . db_string($UserID)); - $UserSubscriptions = G::$DB->collect(0); - G::$Cache->cache_value("subscriptions_user_$UserID", $UserSubscriptions, 0); - } - G::$DB->set_query_id($QueryID); - return $UserSubscriptions; - } + /** + * Read $UserID's subscriptions. If the cache key isn't set, it gets filled. + * If UserID == 0, G::$LoggedUser[ID] is used + * @param int $UserID + * @return array Array of TopicIDs + */ + public static function get_subscriptions($UserID = 0) { + if ($UserID == 0) { + $UserID = G::$LoggedUser['ID']; + } + $QueryID = G::$DB->get_query_id(); + $UserSubscriptions = G::$Cache->get_value("subscriptions_user_$UserID"); + if ($UserSubscriptions === false) { + G::$DB->query(' + SELECT TopicID + FROM users_subscriptions + WHERE UserID = ' . db_string($UserID)); + $UserSubscriptions = G::$DB->collect(0); + G::$Cache->cache_value("subscriptions_user_$UserID", $UserSubscriptions, 0); + } + G::$DB->set_query_id($QueryID); + return $UserSubscriptions; + } - /** - * Same as self::get_subscriptions, but for comment subscriptions - * @param int $UserID - * @return array Array of ($Page, $PageID) - */ - public static function get_comment_subscriptions($UserID = 0) { - if ($UserID == 0) { - $UserID = G::$LoggedUser['ID']; - } - $QueryID = G::$DB->get_query_id(); - $UserCommentSubscriptions = G::$Cache->get_value("subscriptions_comments_user_$UserID"); - if ($UserCommentSubscriptions === false) { - G::$DB->query(' - SELECT Page, PageID - FROM users_subscriptions_comments - WHERE UserID = ' . db_string($UserID)); - $UserCommentSubscriptions = G::$DB->to_array(false, MYSQLI_NUM); - G::$Cache->cache_value("subscriptions_comments_user_$UserID", $UserCommentSubscriptions, 0); - } - G::$DB->set_query_id($QueryID); - return $UserCommentSubscriptions; - } + /** + * Same as self::get_subscriptions, but for comment subscriptions + * @param int $UserID + * @return array Array of ($Page, $PageID) + */ + public static function get_comment_subscriptions($UserID = 0) { + if ($UserID == 0) { + $UserID = G::$LoggedUser['ID']; + } + $QueryID = G::$DB->get_query_id(); + $UserCommentSubscriptions = G::$Cache->get_value("subscriptions_comments_user_$UserID"); + if ($UserCommentSubscriptions === false) { + G::$DB->query(' + SELECT Page, PageID + FROM users_subscriptions_comments + WHERE UserID = ' . db_string($UserID)); + $UserCommentSubscriptions = G::$DB->to_array(false, MYSQLI_NUM); + G::$Cache->cache_value("subscriptions_comments_user_$UserID", $UserCommentSubscriptions, 0); + } + G::$DB->set_query_id($QueryID); + return $UserCommentSubscriptions; + } - /** - * Returns whether or not the current user has new subscriptions. This handles both forum and comment subscriptions. - * @return int Number of unread subscribed threads/comments - */ - public static function has_new_subscriptions() { - $QueryID = G::$DB->get_query_id(); + /** + * Returns whether or not the current user has new subscriptions. This handles both forum and comment subscriptions. + * @return int Number of unread subscribed threads/comments + */ + public static function has_new_subscriptions() { + $QueryID = G::$DB->get_query_id(); - $NewSubscriptions = G::$Cache->get_value('subscriptions_user_new_' . G::$LoggedUser['ID']); - if ($NewSubscriptions === false) { - // forum subscriptions - G::$DB->query(" - SELECT COUNT(1) - FROM users_subscriptions AS s - LEFT JOIN forums_last_read_topics AS l ON l.UserID = s.UserID AND l.TopicID = s.TopicID - JOIN forums_topics AS t ON t.ID = s.TopicID - JOIN forums AS f ON f.ID = t.ForumID - WHERE " . Forums::user_forums_sql() . " - AND IF(t.IsLocked = '1' AND t.IsSticky = '0'" . ", t.LastPostID, IF(l.PostID IS NULL, 0, l.PostID)) < t.LastPostID - AND s.UserID = " . G::$LoggedUser['ID']); - list($NewForumSubscriptions) = G::$DB->next_record(); + $NewSubscriptions = G::$Cache->get_value('subscriptions_user_new_' . G::$LoggedUser['ID']); + if ($NewSubscriptions === false) { + // forum subscriptions + G::$DB->query(" + SELECT COUNT(1) + FROM users_subscriptions AS s + LEFT JOIN forums_last_read_topics AS l ON l.UserID = s.UserID AND l.TopicID = s.TopicID + JOIN forums_topics AS t ON t.ID = s.TopicID + JOIN forums AS f ON f.ID = t.ForumID + WHERE " . Forums::user_forums_sql() . " + AND IF(t.IsLocked = '1' AND t.IsSticky = '0'" . ", t.LastPostID, IF(l.PostID IS NULL, 0, l.PostID)) < t.LastPostID + AND s.UserID = " . G::$LoggedUser['ID']); + list($NewForumSubscriptions) = G::$DB->next_record(); - // comment subscriptions - G::$DB->query(" - SELECT COUNT(1) - FROM users_subscriptions_comments AS s - LEFT JOIN users_comments_last_read AS lr ON lr.UserID = s.UserID AND lr.Page = s.Page AND lr.PageID = s.PageID - LEFT JOIN comments AS c ON c.ID = (SELECT MAX(ID) FROM comments WHERE Page = s.Page AND PageID = s.PageID) - LEFT JOIN collages AS co ON s.Page = 'collages' AND co.ID = s.PageID - WHERE s.UserID = " . G::$LoggedUser['ID'] . " - AND (s.Page != 'collages' OR co.Deleted = '0') - AND IF(lr.PostID IS NULL, 0, lr.PostID) < c.ID"); - list($NewCommentSubscriptions) = G::$DB->next_record(); + // comment subscriptions + G::$DB->query(" + SELECT COUNT(1) + FROM users_subscriptions_comments AS s + LEFT JOIN users_comments_last_read AS lr ON lr.UserID = s.UserID AND lr.Page = s.Page AND lr.PageID = s.PageID + LEFT JOIN comments AS c ON c.ID = (SELECT MAX(ID) FROM comments WHERE Page = s.Page AND PageID = s.PageID) + LEFT JOIN collages AS co ON s.Page = 'collages' AND co.ID = s.PageID + WHERE s.UserID = " . G::$LoggedUser['ID'] . " + AND (s.Page != 'collages' OR co.Deleted = '0') + AND IF(lr.PostID IS NULL, 0, lr.PostID) < c.ID"); + list($NewCommentSubscriptions) = G::$DB->next_record(); - $NewSubscriptions = $NewForumSubscriptions + $NewCommentSubscriptions; - G::$Cache->cache_value('subscriptions_user_new_' . G::$LoggedUser['ID'], $NewSubscriptions, 0); - } - G::$DB->set_query_id($QueryID); - return (int)$NewSubscriptions; - } + $NewSubscriptions = $NewForumSubscriptions + $NewCommentSubscriptions; + G::$Cache->cache_value('subscriptions_user_new_' . G::$LoggedUser['ID'], $NewSubscriptions, 0); + } + G::$DB->set_query_id($QueryID); + return (int)$NewSubscriptions; + } - /** - * Returns whether or not the current user has new quote notifications. - * @return int Number of unread quote notifications - */ - public static function has_new_quote_notifications() { - $QuoteNotificationsCount = G::$Cache->get_value('notify_quoted_' . G::$LoggedUser['ID']); - if ($QuoteNotificationsCount === false) { - $sql = " - SELECT COUNT(1) - FROM users_notify_quoted AS q - LEFT JOIN forums_topics AS t ON t.ID = q.PageID - LEFT JOIN forums AS f ON f.ID = t.ForumID - LEFT JOIN collages AS c ON q.Page = 'collages' AND c.ID = q.PageID - WHERE q.UserID = " . G::$LoggedUser['ID'] . " - AND q.UnRead - AND (q.Page != 'forums' OR " . Forums::user_forums_sql() . ") - AND (q.Page != 'collages' OR c.Deleted = '0')"; - $QueryID = G::$DB->get_query_id(); - G::$DB->query($sql); - list($QuoteNotificationsCount) = G::$DB->next_record(); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value('notify_quoted_' . G::$LoggedUser['ID'], $QuoteNotificationsCount, 0); - } - return (int)$QuoteNotificationsCount; - } + /** + * Returns whether or not the current user has new quote notifications. + * @return int Number of unread quote notifications + */ + public static function has_new_quote_notifications() { + $QuoteNotificationsCount = G::$Cache->get_value('notify_quoted_' . G::$LoggedUser['ID']); + if ($QuoteNotificationsCount === false) { + $sql = " + SELECT COUNT(1) + FROM users_notify_quoted AS q + LEFT JOIN forums_topics AS t ON t.ID = q.PageID + LEFT JOIN forums AS f ON f.ID = t.ForumID + LEFT JOIN collages AS c ON q.Page = 'collages' AND c.ID = q.PageID + WHERE q.UserID = " . G::$LoggedUser['ID'] . " + AND q.UnRead + AND (q.Page != 'forums' OR " . Forums::user_forums_sql() . ") + AND (q.Page != 'collages' OR c.Deleted = '0')"; + $QueryID = G::$DB->get_query_id(); + G::$DB->query($sql); + list($QuoteNotificationsCount) = G::$DB->next_record(); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value('notify_quoted_' . G::$LoggedUser['ID'], $QuoteNotificationsCount, 0); + } + return (int)$QuoteNotificationsCount; + } - /** - * Returns the key which holds this $TopicID in the subscription array. - * Use type-aware comparison operators with this! (ie. if (self::has_subscribed($TopicID) !== false) { ... }) - * @param int $TopicID - * @return bool|int - */ - public static function has_subscribed($TopicID) { - $UserSubscriptions = self::get_subscriptions(); - return array_search($TopicID, $UserSubscriptions); - } + /** + * Returns the key which holds this $TopicID in the subscription array. + * Use type-aware comparison operators with this! (ie. if (self::has_subscribed($TopicID) !== false) { ... }) + * @param int $TopicID + * @return bool|int + */ + public static function has_subscribed($TopicID) { + $UserSubscriptions = self::get_subscriptions(); + return array_search($TopicID, $UserSubscriptions); + } - /** - * Same as has_subscribed, but for comment subscriptions. - * @param string $Page 'artist', 'collages', 'requests' or 'torrents' - * @param int $PageID - * @return bool|int - */ - public static function has_subscribed_comments($Page, $PageID) { - $UserCommentSubscriptions = self::get_comment_subscriptions(); - return array_search(array($Page, $PageID), $UserCommentSubscriptions); - } + /** + * Same as has_subscribed, but for comment subscriptions. + * @param string $Page 'artist', 'collages', 'requests' or 'torrents' + * @param int $PageID + * @return bool|int + */ + public static function has_subscribed_comments($Page, $PageID) { + $UserCommentSubscriptions = self::get_comment_subscriptions(); + return array_search(array($Page, $PageID), $UserCommentSubscriptions); + } - /** - * Clear the subscription cache for all subscribers of a forum thread or artist/collage/request/torrent comments. - * @param string $Page 'forums', 'artist', 'collages', 'requests' or 'torrents' - * @param type $PageID TopicID, ArtistID, CollageID, RequestID or GroupID, respectively - */ - public static function flush_subscriptions($Page, $PageID) { - $QueryID = G::$DB->get_query_id(); - if ($Page == 'forums') { - G::$DB->query(" - SELECT UserID - FROM users_subscriptions - WHERE TopicID = '$PageID'"); - } else { - G::$DB->query(" - SELECT UserID - FROM users_subscriptions_comments - WHERE Page = '$Page' - AND PageID = '$PageID'"); - } - $Subscribers = G::$DB->collect('UserID'); - foreach ($Subscribers as $Subscriber) { - G::$Cache->delete_value("subscriptions_user_new_$Subscriber"); - } - G::$DB->set_query_id($QueryID); - } + /** + * Clear the subscription cache for all subscribers of a forum thread or artist/collage/request/torrent comments. + * @param string $Page 'forums', 'artist', 'collages', 'requests' or 'torrents' + * @param type $PageID TopicID, ArtistID, CollageID, RequestID or GroupID, respectively + */ + public static function flush_subscriptions($Page, $PageID) { + $QueryID = G::$DB->get_query_id(); + if ($Page == 'forums') { + G::$DB->query(" + SELECT UserID + FROM users_subscriptions + WHERE TopicID = '$PageID'"); + } else { + G::$DB->query(" + SELECT UserID + FROM users_subscriptions_comments + WHERE Page = '$Page' + AND PageID = '$PageID'"); + } + $Subscribers = G::$DB->collect('UserID'); + foreach ($Subscribers as $Subscriber) { + G::$Cache->delete_value("subscriptions_user_new_$Subscriber"); + } + G::$DB->set_query_id($QueryID); + } - /** - * Move all $Page subscriptions from $OldPageID to $NewPageID (for example when merging torrent groups). - * Passing $NewPageID = null will delete the subscriptions. - * @param string $Page 'forums', 'artist', 'collages', 'requests' or 'torrents' - * @param int $OldPageID TopicID, ArtistID, CollageID, RequestID or GroupID, respectively - * @param int|null $NewPageID As $OldPageID, or null to delete the subscriptions - */ - public static function move_subscriptions($Page, $OldPageID, $NewPageID) { - self::flush_subscriptions($Page, $OldPageID); - $QueryID = G::$DB->get_query_id(); - if ($Page == 'forums') { - if ($NewPageID !== null) { - G::$DB->query(" - UPDATE IGNORE users_subscriptions - SET TopicID = '$NewPageID' - WHERE TopicID = '$OldPageID'"); - // explanation see below - G::$DB->query(" - UPDATE IGNORE forums_last_read_topics - SET TopicID = $NewPageID - WHERE TopicID = $OldPageID"); - G::$DB->query(" - SELECT UserID, MIN(PostID) - FROM forums_last_read_topics - WHERE TopicID IN ($OldPageID, $NewPageID) - GROUP BY UserID - HAVING COUNT(1) = 2"); - $Results = G::$DB->to_array(false, MYSQLI_NUM); - foreach ($Results as $Result) { - G::$DB->query(" - UPDATE forums_last_read_topics - SET PostID = $Result[1] - WHERE TopicID = $NewPageID - AND UserID = $Result[0]"); - } - } - G::$DB->query(" - DELETE FROM users_subscriptions - WHERE TopicID = '$OldPageID'"); - G::$DB->query(" - DELETE FROM forums_last_read_topics - WHERE TopicID = $OldPageID"); - } else { - if ($NewPageID !== null) { - G::$DB->query(" - UPDATE IGNORE users_subscriptions_comments - SET PageID = '$NewPageID' - WHERE Page = '$Page' - AND PageID = '$OldPageID'"); - // last read handling - // 1) update all rows that have no key collisions (i.e. users that haven't previously read both pages or if there are only comments on one page) - G::$DB->query(" - UPDATE IGNORE users_comments_last_read - SET PageID = '$NewPageID' - WHERE Page = '$Page' - AND PageID = $OldPageID"); - // 2) get all last read records with key collisions (i.e. there are records for one user for both PageIDs) - G::$DB->query(" - SELECT UserID, MIN(PostID) - FROM users_comments_last_read - WHERE Page = '$Page' - AND PageID IN ($OldPageID, $NewPageID) - GROUP BY UserID - HAVING COUNT(1) = 2"); - $Results = G::$DB->to_array(false, MYSQLI_NUM); - // 3) update rows for those people found in 2) to the earlier post - foreach ($Results as $Result) { - G::$DB->query(" - UPDATE users_comments_last_read - SET PostID = $Result[1] - WHERE Page = '$Page' - AND PageID = $NewPageID - AND UserID = $Result[0]"); - } - } - G::$DB->query(" - DELETE FROM users_subscriptions_comments - WHERE Page = '$Page' - AND PageID = '$OldPageID'"); - G::$DB->query(" - DELETE FROM users_comments_last_read - WHERE Page = '$Page' - AND PageID = '$OldPageID'"); - } - G::$DB->set_query_id($QueryID); - } + /** + * Move all $Page subscriptions from $OldPageID to $NewPageID (for example when merging torrent groups). + * Passing $NewPageID = null will delete the subscriptions. + * @param string $Page 'forums', 'artist', 'collages', 'requests' or 'torrents' + * @param int $OldPageID TopicID, ArtistID, CollageID, RequestID or GroupID, respectively + * @param int|null $NewPageID As $OldPageID, or null to delete the subscriptions + */ + public static function move_subscriptions($Page, $OldPageID, $NewPageID) { + self::flush_subscriptions($Page, $OldPageID); + $QueryID = G::$DB->get_query_id(); + if ($Page == 'forums') { + if ($NewPageID !== null) { + G::$DB->query(" + UPDATE IGNORE users_subscriptions + SET TopicID = '$NewPageID' + WHERE TopicID = '$OldPageID'"); + // explanation see below + G::$DB->query(" + UPDATE IGNORE forums_last_read_topics + SET TopicID = $NewPageID + WHERE TopicID = $OldPageID"); + G::$DB->query(" + SELECT UserID, MIN(PostID) + FROM forums_last_read_topics + WHERE TopicID IN ($OldPageID, $NewPageID) + GROUP BY UserID + HAVING COUNT(1) = 2"); + $Results = G::$DB->to_array(false, MYSQLI_NUM); + foreach ($Results as $Result) { + G::$DB->query(" + UPDATE forums_last_read_topics + SET PostID = $Result[1] + WHERE TopicID = $NewPageID + AND UserID = $Result[0]"); + } + } + G::$DB->query(" + DELETE FROM users_subscriptions + WHERE TopicID = '$OldPageID'"); + G::$DB->query(" + DELETE FROM forums_last_read_topics + WHERE TopicID = $OldPageID"); + } else { + if ($NewPageID !== null) { + G::$DB->query(" + UPDATE IGNORE users_subscriptions_comments + SET PageID = '$NewPageID' + WHERE Page = '$Page' + AND PageID = '$OldPageID'"); + // last read handling + // 1) update all rows that have no key collisions (i.e. users that haven't previously read both pages or if there are only comments on one page) + G::$DB->query(" + UPDATE IGNORE users_comments_last_read + SET PageID = '$NewPageID' + WHERE Page = '$Page' + AND PageID = $OldPageID"); + // 2) get all last read records with key collisions (i.e. there are records for one user for both PageIDs) + G::$DB->query(" + SELECT UserID, MIN(PostID) + FROM users_comments_last_read + WHERE Page = '$Page' + AND PageID IN ($OldPageID, $NewPageID) + GROUP BY UserID + HAVING COUNT(1) = 2"); + $Results = G::$DB->to_array(false, MYSQLI_NUM); + // 3) update rows for those people found in 2) to the earlier post + foreach ($Results as $Result) { + G::$DB->query(" + UPDATE users_comments_last_read + SET PostID = $Result[1] + WHERE Page = '$Page' + AND PageID = $NewPageID + AND UserID = $Result[0]"); + } + } + G::$DB->query(" + DELETE FROM users_subscriptions_comments + WHERE Page = '$Page' + AND PageID = '$OldPageID'"); + G::$DB->query(" + DELETE FROM users_comments_last_read + WHERE Page = '$Page' + AND PageID = '$OldPageID'"); + } + G::$DB->set_query_id($QueryID); + } - /** - * Clear the quote notification cache for all subscribers of a forum thread or artist/collage/request/torrent comments. - * @param string $Page 'forums', 'artist', 'collages', 'requests' or 'torrents' - * @param int $PageID TopicID, ArtistID, CollageID, RequestID or GroupID, respectively - */ - public static function flush_quote_notifications($Page, $PageID) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT UserID - FROM users_notify_quoted - WHERE Page = '$Page' - AND PageID = $PageID"); - $Subscribers = G::$DB->collect('UserID'); - foreach ($Subscribers as $Subscriber) { - G::$Cache->delete_value("notify_quoted_$Subscriber"); - } - G::$DB->set_query_id($QueryID); - } + /** + * Clear the quote notification cache for all subscribers of a forum thread or artist/collage/request/torrent comments. + * @param string $Page 'forums', 'artist', 'collages', 'requests' or 'torrents' + * @param int $PageID TopicID, ArtistID, CollageID, RequestID or GroupID, respectively + */ + public static function flush_quote_notifications($Page, $PageID) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT UserID + FROM users_notify_quoted + WHERE Page = '$Page' + AND PageID = $PageID"); + $Subscribers = G::$DB->collect('UserID'); + foreach ($Subscribers as $Subscriber) { + G::$Cache->delete_value("notify_quoted_$Subscriber"); + } + G::$DB->set_query_id($QueryID); + } } diff --git a/classes/tags.class.php b/classes/tags.class.php index efa8b89b5..14d47c5c4 100644 --- a/classes/tags.class.php +++ b/classes/tags.class.php @@ -16,7 +16,7 @@ * Tags::format_top(); * ?> * e.g.: - * pop (2) + * pop (2) * rock (2) * hip.hop (1) * indie (1) @@ -25,255 +25,255 @@ * overall total amount of tags to provide a Top Tags list. Merging is optional. */ class Tags { - /** - * Collects all tags processed by the Tags Class - * @static - * @var array $All Class Tags - */ - private static $All = array(); + /** + * Collects all tags processed by the Tags Class + * @static + * @var array $All Class Tags + */ + private static $All = []; - /** - * All tags in the current instance - * @var array $Tags Instance Tags - */ - private $Tags = null; + /** + * All tags in the current instance + * @var array $Tags Instance Tags + */ + private $Tags = null; - /** - * @var array $TagLink Tag link list - */ - private $TagLink = array(); + /** + * @var array $TagLink Tag link list + */ + private $TagLink = []; - /** - * @var string $Primary The primary tag - */ - private $Primary = ''; + /** + * @var string $Primary The primary tag + */ + private $Primary = ''; - /** - * Filter tags array to remove empty spaces. - * - * @param string $TagList A string of tags separated by a space - * @param boolean $Merge Merge the tag list with the Class' tags - * E.g., compilations and soundtracks are skipped, so false - */ - public function __construct($TagList, $Merge = true) { - if ($TagList) { - $this->Tags = array_filter(explode(' ', str_replace('_', '.', $TagList))); + /** + * Filter tags array to remove empty spaces. + * + * @param string $TagList A string of tags separated by a space + * @param boolean $Merge Merge the tag list with the Class' tags + * E.g., compilations and soundtracks are skipped, so false + */ + public function __construct($TagList, $Merge = true) { + if ($TagList) { + $this->Tags = array_filter(explode(' ', str_replace('_', '.', $TagList))); - if ($Merge) { - self::$All = array_merge(self::$All, $this->Tags); - } + if ($Merge) { + self::$All = array_merge(self::$All, $this->Tags); + } - $this->Primary = $this->Tags[0]; - } else { - $this->Tags = array(); - } - } + $this->Primary = $this->Tags[0]; + } else { + $this->Tags = []; + } + } - /** - * @return string Primary Tag - */ - public function get_primary() { - return $this->Primary; - } + /** + * @return string Primary Tag + */ + public function get_primary() { + return $this->Primary; + } - /** - * Set the primary tag - * @param string $Primary - */ - public function set_primary($Primary) { - $this->Primary = (string)$Primary; - } + /** + * Set the primary tag + * @param string $Primary + */ + public function set_primary($Primary) { + $this->Primary = (string)$Primary; + } - /** - * Formats primary tag as a title - * @return string Title - */ - public function title() { - return ucwords(str_replace('.', ' ', $this->Primary)); - } + /** + * Formats primary tag as a title + * @return string Title + */ + public function title() { + return ucwords(str_replace('.', ' ', $this->Primary)); + } - /** - * Formats primary tag as a CSS class - * @return string CSS Class Name - */ - public function css_name() { - return 'tags_' . str_replace('.', '_', $this->Primary); - } + /** + * Formats primary tag as a CSS class + * @return string CSS Class Name + */ + public function css_name() { + return 'tags_' . str_replace('.', '_', $this->Primary); + } - /** - * @return array Tags - */ - public function get_tags() { - return $this->Tags; - } + /** + * @return array Tags + */ + public function get_tags() { + return $this->Tags; + } - /** - * @return array All tags - */ - public static function all() { - return self::$All; - } + /** + * @return array All tags + */ + public static function all() { + return self::$All; + } - /** - * Counts and sorts All tags - * @return array All tags sorted - */ - public static function sorted() { - $Sorted = array_count_values(self::$All); - arsort($Sorted); - return $Sorted; - } + /** + * Counts and sorts All tags + * @return array All tags sorted + */ + public static function sorted() { + $Sorted = array_count_values(self::$All); + arsort($Sorted); + return $Sorted; + } - /** - * Formats tags - * @param string $Link Link to a taglist page - * @param string $ArtistName Restrict tag search by this artist - * @return string List of tag links - */ - public function format($Link = 'torrents.php?taglist=', $ArtistName = '') { - if (!empty($ArtistName)) { - $ArtistName = "&artistname=" . urlencode($ArtistName) . "&action=advanced&searchsubmit=1"; - } - foreach ($this->Tags as $Tag) { - if (empty($this->TagLink[$Tag])) { - $this->TagLink[$Tag] = '' . $Tag . ''; - } - } - return implode(', ', $this->TagLink); - } + /** + * Formats tags + * @param string $Link Link to a taglist page + * @param string $ArtistName Restrict tag search by this artist + * @return string List of tag links + */ + public function format($Link = 'torrents.php?taglist=', $ArtistName = '') { + if (!empty($ArtistName)) { + $ArtistName = "&artistname=" . urlencode($ArtistName) . "&action=advanced&searchsubmit=1"; + } + foreach ($this->Tags as $Tag) { + if (empty($this->TagLink[$Tag])) { + $this->TagLink[$Tag] = '' . $Tag . ''; + } + } + return implode(', ', $this->TagLink); + } - /** - * Format a list of top tags - * @param int $Max Max number of items to get - * @param string $Link Page query where more items of this tag type can be found - * @param string $ArtistName Optional artist - */ - public static function format_top($Max = 5, $Link = 'torrents.php?taglist=', $ArtistName = '') { - if (empty(self::$All)) { ?> -
  • No torrent tags
  • - $Total) { ?> -
  • ()
  • - +
  • No torrent tags
  • + $Total) { ?> +
  • ()
  • +get_value('tag_aliases_search'); - if ($TagAliases === false) { - G::$DB->query(' - SELECT ID, BadTag, AliasTag - FROM tag_aliases - ORDER BY BadTag'); - $TagAliases = G::$DB->to_array(false, MYSQLI_ASSOC, false); - // Unify tag aliases to be in_this_format as tags not in.this.format - array_walk_recursive($TagAliases, create_function('&$val', '$val = preg_replace("/\./","_", $val);')); - // Clean up the array for smaller cache size - foreach ($TagAliases as &$TagAlias) { - foreach (array_keys($TagAlias) as $Key) { - if (is_numeric($Key)) { - unset($TagAlias[$Key]); - } - } - } - G::$Cache->cache_value('tag_aliases_search', $TagAliases, 3600 * 24 * 7); // cache for 7 days - } - return $TagAliases; - } + /** + * General purpose method to get all tag aliases from the DB + * @return array + */ + public static function get_aliases() { + $TagAliases = G::$Cache->get_value('tag_aliases_search'); + if ($TagAliases === false) { + G::$DB->query(' + SELECT ID, BadTag, AliasTag + FROM tag_aliases + ORDER BY BadTag'); + $TagAliases = G::$DB->to_array(false, MYSQLI_ASSOC, false); + // Unify tag aliases to be in_this_format as tags not in.this.format + array_walk_recursive($TagAliases, create_function('&$val', '$val = preg_replace("/\./","_", $val);')); + // Clean up the array for smaller cache size + foreach ($TagAliases as &$TagAlias) { + foreach (array_keys($TagAlias) as $Key) { + if (is_numeric($Key)) { + unset($TagAlias[$Key]); + } + } + } + G::$Cache->cache_value('tag_aliases_search', $TagAliases, 3600 * 24 * 7); // cache for 7 days + } + return $TagAliases; + } - /** - * Replace bad tags with tag aliases - * @param array $Tags Array with sub-arrays 'include' and 'exclude' - * @return array - */ - public static function remove_aliases($Tags) { - $TagAliases = self::get_aliases(); + /** + * Replace bad tags with tag aliases + * @param array $Tags Array with sub-arrays 'include' and 'exclude' + * @return array + */ + public static function remove_aliases($Tags) { + $TagAliases = self::get_aliases(); - if (isset($Tags['include'])) { - $End = count($Tags['include']); - for ($i = 0; $i < $End; $i++) { - foreach ($TagAliases as $TagAlias) { - if ($Tags['include'][$i] === $TagAlias['BadTag']) { - $Tags['include'][$i] = $TagAlias['AliasTag']; - break; - } - } - } - // Only keep unique entries after unifying tag standard - $Tags['include'] = array_unique($Tags['include']); - } + if (isset($Tags['include'])) { + $End = count($Tags['include']); + for ($i = 0; $i < $End; $i++) { + foreach ($TagAliases as $TagAlias) { + if ($Tags['include'][$i] === $TagAlias['BadTag']) { + $Tags['include'][$i] = $TagAlias['AliasTag']; + break; + } + } + } + // Only keep unique entries after unifying tag standard + $Tags['include'] = array_unique($Tags['include']); + } - if (isset($Tags['exclude'])) { - $End = count($Tags['exclude']); - for ($i = 0; $i < $End; $i++) { - foreach ($TagAliases as $TagAlias) { - if (substr($Tags['exclude'][$i], 1) === $TagAlias['BadTag']) { - $Tags['exclude'][$i] = '!'.$TagAlias['AliasTag']; - break; - } - } - } - // Only keep unique entries after unifying tag standard - $Tags['exclude'] = array_unique($Tags['exclude']); - } + if (isset($Tags['exclude'])) { + $End = count($Tags['exclude']); + for ($i = 0; $i < $End; $i++) { + foreach ($TagAliases as $TagAlias) { + if (substr($Tags['exclude'][$i], 1) === $TagAlias['BadTag']) { + $Tags['exclude'][$i] = '!'.$TagAlias['AliasTag']; + break; + } + } + } + // Only keep unique entries after unifying tag standard + $Tags['exclude'] = array_unique($Tags['exclude']); + } - return $Tags; - } + return $Tags; + } - /** - * Filters a list of include and exclude tags to be used in a Sphinx search - * @param array $Tags An array of tags with sub-arrays 'include' and 'exclude' - * @param boolean $EnableNegation Sphinx needs at least one positive search condition to support the NOT operator - * @param integer $TagType Search for Any or All of these tags. - * @return array Array keys predicate and input - * Predicate for a Sphinx 'taglist' query - * Input contains clean, aliased tags. Use it in a form instead of the user submitted string - */ - public static function tag_filter_sph($Tags, $EnableNegation, $TagType) { - $QueryParts = []; - $Tags = Tags::remove_aliases($Tags); - $TagList = str_replace('_', '.', implode(', ', array_merge($Tags['include'], $Tags['exclude']))); + /** + * Filters a list of include and exclude tags to be used in a Sphinx search + * @param array $Tags An array of tags with sub-arrays 'include' and 'exclude' + * @param boolean $EnableNegation Sphinx needs at least one positive search condition to support the NOT operator + * @param integer $TagType Search for Any or All of these tags. + * @return array Array keys predicate and input + * Predicate for a Sphinx 'taglist' query + * Input contains clean, aliased tags. Use it in a form instead of the user submitted string + */ + public static function tag_filter_sph($Tags, $EnableNegation, $TagType) { + $QueryParts = []; + $Tags = Tags::remove_aliases($Tags); + $TagList = str_replace('_', '.', implode(', ', array_merge($Tags['include'], $Tags['exclude']))); - if (!$EnableNegation && !empty($Tags['exclude'])) { - $Tags['include'] = array_merge($Tags['include'], $Tags['exclude']); - unset($Tags['exclude']); - } + if (!$EnableNegation && !empty($Tags['exclude'])) { + $Tags['include'] = array_merge($Tags['include'], $Tags['exclude']); + unset($Tags['exclude']); + } - foreach ($Tags['include'] as &$Tag) { - $Tag = Sphinxql::sph_escape_string($Tag); - } + foreach ($Tags['include'] as &$Tag) { + $Tag = Sphinxql::sph_escape_string($Tag); + } - if (!empty($Tags['exclude'])) { - foreach ($Tags['exclude'] as &$Tag) { - $Tag = '!' . Sphinxql::sph_escape_string(substr($Tag, 1)); - } - } + if (!empty($Tags['exclude'])) { + foreach ($Tags['exclude'] as &$Tag) { + $Tag = '!' . Sphinxql::sph_escape_string(substr($Tag, 1)); + } + } - // 'All' tags - if (!isset($TagType) || $TagType == 1) { - $SearchWords = array_merge($Tags['include'], $Tags['exclude']); - if (!empty($Tags)) { - $QueryParts[] = implode(' ', $SearchWords); - } - } - // 'Any' tags - else { - if (!empty($Tags['include'])) { - $QueryParts[] = '( ' . implode(' | ', $Tags['include']) . ' )'; - } - if (!empty($Tags['exclude'])) { - $QueryParts[] = implode(' ', $Tags['exclude']); - } - } + // 'All' tags + if (!isset($TagType) || $TagType == 1) { + $SearchWords = array_merge($Tags['include'], $Tags['exclude']); + if (!empty($Tags)) { + $QueryParts[] = implode(' ', $SearchWords); + } + } + // 'Any' tags + else { + if (!empty($Tags['include'])) { + $QueryParts[] = '( ' . implode(' | ', $Tags['include']) . ' )'; + } + if (!empty($Tags['exclude'])) { + $QueryParts[] = implode(' ', $Tags['exclude']); + } + } - return ['input' => $TagList, 'predicate' => implode(' ', $QueryParts)]; - } + return ['input' => $TagList, 'predicate' => implode(' ', $QueryParts)]; + } } diff --git a/classes/text.class.php b/classes/text.class.php index 68debc9fc..59698a8bf 100644 --- a/classes/text.class.php +++ b/classes/text.class.php @@ -1,1116 +1,1146 @@ - max number of attributes - * @var array $ValidTags - */ - private static $ValidTags = array('b'=>0, 'u'=>0, 'i'=>0, 's'=>0, '*'=>1, '#'=>1, '**'=>1, '##'=>1, '***'=>1, '###'=>1, 'artist'=>0, 'user'=>0, 'n'=>0, 'inlineurl'=>0, 'inlinesize'=>1, 'headline'=>1, 'align'=>1, 'color'=>1, 'colour'=>1, 'size'=>1, 'url'=>1, 'img'=>1, 'quote'=>1, 'pre'=>1, 'code'=>1, 'tex'=>0, 'hide'=>1, 'spoiler' => 1, 'plain'=>0, 'important'=>0, 'torrent'=>0, 'rule'=>0, 'mature'=>1, 'box'=>0 - ); - - /** - * Array of smilies; code => image file in STATIC_SERVER/common/smileys - * @var array $Smileys - */ - private static $Smileys = array( - ':angry:' => 'angry.gif', - ':-D' => 'biggrin.gif', - ':D' => 'biggrin.gif', - ':|' => 'blank.gif', - ':-|' => 'blank.gif', - ':blush:' => 'blush.gif', - ':cool:' => 'cool.gif', - ':'(' => 'crying.gif', - ':crying:' => 'crying.gif', - '>.>' => 'eyesright.gif', - ':frown:' => 'frown.gif', - '<3' => 'heart.gif', - ':unsure:' => 'hmm.gif', - //':\\' => 'hmm.gif', - ':whatlove:' => 'ilu.gif', - ':lol:' => 'laughing.gif', - ':loveflac:' => 'loveflac.gif', - ':flaclove:' => 'loveflac.gif', - ':ninja:' => 'ninja.gif', - ':no:' => 'no.gif', - ':nod:' => 'nod.gif', - ':ohno:' => 'ohnoes.gif', - ':ohnoes:' => 'ohnoes.gif', - ':omg:' => 'omg.gif', - ':o' => 'ohshit.gif', - ':O' => 'ohshit.gif', - ':paddle:' => 'paddle.gif', - ':(' => 'sad.gif', - ':-(' => 'sad.gif', - ':shifty:' => 'shifty.gif', - ':sick:' => 'sick.gif', - ':)' => 'smile.gif', - ':-)' => 'smile.gif', - ':sorry:' => 'sorry.gif', - ':thanks:' => 'thanks.gif', - ':P' => 'tongue.gif', - ':p' => 'tongue.gif', - ':-P' => 'tongue.gif', - ':-p' => 'tongue.gif', - ':wave:' => 'wave.gif', - ';-)' => 'wink.gif', - ':wink:' => 'wink.gif', - ':creepy:' => 'creepy.gif', - ':worried:' => 'worried.gif', - ':wtf:' => 'wtf.gif', - ':wub:' => 'wub.gif', - ); - - /** - * Processed version of the $Smileys array, see {@link smileys} - * @var array $ProcessedSmileys - */ - private static $ProcessedSmileys = array(); - - /** - * Whether or not to turn images into URLs (used inside [quote] tags). - * This is an integer reflecting the number of levels we're doing that - * transition, i.e. images will only be displayed as images if $NoImg <= 0. - * By setting this variable to a negative number you can delay the - * transition to a deeper level of quotes. - * @var int $NoImg - */ - private static $NoImg = 0; - - /** - * Internal counter for the level of recursion in to_html - * @var int $Levels - */ - private static $Levels = 0; - - /** - * The maximum amount of nesting allowed (exclusive) - * In reality n-1 nests are shown. - * @var int $MaximumNests - */ - private static $MaximumNests = 10; - - /** - * Used to detect and disable parsing (e.g. TOC) within quotes - * @var int $InQuotes - */ - private static $InQuotes = 0; - - /** - * Used to [hide] quote trains starting with the specified depth (inclusive) - * @var int $NestsBeforeHide - * - * This defaulted to 5 but was raised to 10 to effectively "disable" it until - * an optimal number of nested [quote] tags is chosen. The variable $MaximumNests - * effectively overrides this variable, if $MaximumNests is less than the value - * of $NestsBeforeHide. - */ - private static $NestsBeforeHide = 10; - - /** - * Array of headlines for Table Of Contents (TOC) - * @var array $HeadLines - */ - private static $Headlines; - - /** - * Counter for making headline URLs unique - * @var int $HeadLines - */ - private static $HeadlineID = 0; - - /** - * Depth - * @var array $HeadlineLevels - */ - private static $HeadlineLevels = array('1', '2', '3', '4'); - - /** - * TOC enabler - * @var bool $TOC - */ - public static $TOC = false; - - /** - * Output BBCode as XHTML - * @param string $Str BBCode text - * @param bool $OutputTOC Ouput TOC near (above) text - * @param int $Min See {@link parse_toc} - * @return string - */ - public static function full_format($Str, $OutputTOC = true, $Min = 3, $Rules = false) { - global $Debug; - $Debug->set_flag('BBCode start'); - $Str = display_str($Str); - self::$Headlines = array(); - - //Inline links - $URLPrefix = '(\[url\]|\[url\=|\[img\=|\[img\])'; - $Str = preg_replace('/'.$URLPrefix.'\s+/i', '$1', $Str); - $Str = preg_replace('/(?set_flag('BBCode end'); - return $HTML; - } - - public static function strip_bbcode($Str) { - $Str = display_str($Str); - - //Inline links - $Str = preg_replace('/(? $i) { - $Array[$ArrayPos] = substr($Str, $i, $TagPos - $i); - ++$ArrayPos; - $i = $TagPos; - } - - // 2) See if it's a [[wiki-link]] or an ordinary tag, and get the tag name - if (!empty($Tag[4][0])) { // Wiki-link - $WikiLink = true; - $TagName = substr($Tag[4][0], 2, -2); - $Attrib = ''; - } else { // 3) If it's not a wiki link: - $WikiLink = false; - $TagName = strtolower(substr($Tag[2][0], 1)); - - //3a) check it against the self::$ValidTags array to see if it's actually a tag and not [bullshit] - if (!isset(self::$ValidTags[$TagName])) { - $Array[$ArrayPos] = substr($Str, $i, ($TagPos - $i) + strlen($Tag[0][0])); - $i = $TagPos + strlen($Tag[0][0]); - ++$ArrayPos; - continue; - } - - $MaxAttribs = self::$ValidTags[$TagName]; - - // 3b) Get the attribute, if it exists [name=attribute] - if (!empty($Tag[3][0])) { - $Attrib = substr($Tag[3][0], 1); - } else { - $Attrib = ''; - } - } - - // 4) Move the pointer past the end of the tag - $i = $TagPos + strlen($Tag[0][0]); - - // 5) Find out where the tag closes (beginning of [/tag]) - - // Unfortunately, BBCode doesn't have nice standards like XHTML - // [*], [img=...], and http:// follow different formats - // Thus, we have to handle these before we handle the majority of tags - - //5a) Different for different types of tag. Some tags don't close, others are weird like [*] - if ($TagName == 'img' && !empty($Tag[3][0])) { //[img=...] - $Block = ''; // Nothing inside this tag - // Don't need to touch $i - } elseif ($TagName == 'inlineurl') { // We did a big replace early on to turn http:// into [inlineurl]http:// - - // Let's say the block can stop at a newline or a space - $CloseTag = strcspn($Str, " \n\r", $i); - if ($CloseTag === false) { // block finishes with URL - $CloseTag = $Len; - } - if (preg_match('/[!,.?:]+$/',substr($Str, $i, $CloseTag), $Match)) { - $CloseTag -= strlen($Match[0]); - } - $URL = substr($Str, $i, $CloseTag); - if (substr($URL, -1) == ')' && substr_count($URL, '(') < substr_count($URL, ')')) { - $CloseTag--; - $URL = substr($URL, 0, -1); - } - $Block = $URL; // Get the URL - - // strcspn returns the number of characters after the offset $i, not after the beginning of the string - // Therefore, we use += instead of the = everywhere else - $i += $CloseTag; // 5d) Move the pointer past the end of the [/close] tag. - } elseif ($WikiLink == true || $TagName == 'n') { - // Don't need to do anything - empty tag with no closing - } elseif ($TagName[0] === '*' || $TagName[0] === '#') { - // We're in a list. Find where it ends - $NewLine = $i; - do { // Look for \n[*] - $NewLine = strpos($Str, "\n", $NewLine + 1); - } while ($NewLine !== false && substr($Str, $NewLine + 1, 1 + strlen($TagName)) == "[$TagName"); - - $CloseTag = $NewLine; - if ($CloseTag === false) { // block finishes with list - $CloseTag = $Len; - } - $Block = substr($Str, $i, $CloseTag - $i); // Get the list - $i = $CloseTag; // 5d) Move the pointer past the end of the [/close] tag. - } else { - //5b) If it's a normal tag, it may have versions of itself nested inside - $CloseTag = $i - 1; - $InTagPos = $i - 1; - $NumInOpens = 0; - $NumInCloses = -1; - - $InOpenRegex = '/\[('.$TagName.')'; - if ($MaxAttribs > 0) { - $InOpenRegex .= "(=[^\n'\"\[\]]+)?"; - } - $InOpenRegex .= '\]/i'; - - - // Every time we find an internal open tag of the same type, search for the next close tag - // (as the first close tag won't do - it's been opened again) - do { - $CloseTag = strpos($StrLC, "[/$TagName]", $CloseTag + 1); - if ($CloseTag === false) { - $CloseTag = $Len; - break; - } else { - $NumInCloses++; // Majority of cases - } - - // Is there another open tag inside this one? - $OpenTag = preg_match($InOpenRegex, $Str, $InTag, PREG_OFFSET_CAPTURE, $InTagPos + 1); - if (!$OpenTag || $InTag[0][1] > $CloseTag) { - break; - } else { - $InTagPos = $InTag[0][1]; - $NumInOpens++; - } - - } while ($NumInOpens > $NumInCloses); - - - // Find the internal block inside the tag - $Block = substr($Str, $i, $CloseTag - $i); // 5c) Get the contents between [open] and [/close] and call it the block. - - $i = $CloseTag + strlen($TagName) + 3; // 5d) Move the pointer past the end of the [/close] tag. - - } - - // 6) Depending on what type of tag we're dealing with, create an array with the attribute and block. - switch ($TagName) { - case 'inlineurl': - $Array[$ArrayPos] = array('Type'=>'inlineurl', 'Attr'=>$Block, 'Val'=>''); - break; - case 'url': - $Array[$ArrayPos] = array('Type'=>'img', 'Attr'=>$Attrib, 'Val'=>$Block); - if (empty($Attrib)) { // [url]http://...[/url] - always set URL to attribute - $Array[$ArrayPos] = array('Type'=>'url', 'Attr'=>$Block, 'Val'=>''); - } else { - $Array[$ArrayPos] = array('Type'=>'url', 'Attr'=>$Attrib, 'Val'=>self::parse($Block)); - } - break; - case 'quote': - $Array[$ArrayPos] = array('Type'=>'quote', 'Attr'=>self::parse($Attrib), 'Val'=>self::parse($Block)); - break; - case 'box': - $Array[$ArrayPos] = ['Type'=>'box', 'Val'=>self::parse($Block)]; - break; - case 'img': - case 'image': - if (empty($Block)) { - $Block = $Attrib; - } - $Array[$ArrayPos] = array('Type'=>'img', 'Val'=>$Block); - break; - case 'aud': - case 'mp3': - case 'audio': - if (empty($Block)) { - $Block = $Attrib; - } - $Array[$ArrayPos] = array('Type'=>'aud', 'Val'=>$Block); - break; - case 'user': - $Array[$ArrayPos] = array('Type'=>'user', 'Val'=>$Block); - break; - case 'artist': - $Array[$ArrayPos] = array('Type'=>'artist', 'Val'=>$Block); - break; - case 'torrent': - $Array[$ArrayPos] = array('Type'=>'torrent', 'Val'=>$Block); - break; - case 'tex': - $Array[$ArrayPos] = array('Type'=>'tex', 'Val'=>$Block); - break; - case 'rule': - $Array[$ArrayPos] = array('Type'=>'rule', 'Val'=>$Block); - break; - case 'pre': - case 'code': - case 'plain': - $Block = strtr($Block, array('[inlineurl]' => '')); - - $Callback = function ($matches) { - $n = $matches[2]; - $text = ''; - if ($n < 5 && $n > 0) { - $e = str_repeat('=', $matches[2] + 1); - $text = $e . $matches[3] . $e; - } - return $text; - }; - $Block = preg_replace_callback('/\[(headline)\=(\d)\](.*?)\[\/\1\]/i', $Callback, $Block); - - $Block = preg_replace('/\[inlinesize\=3\](.*?)\[\/inlinesize\]/i', '====$1====', $Block); - $Block = preg_replace('/\[inlinesize\=5\](.*?)\[\/inlinesize\]/i', '===$1===', $Block); - $Block = preg_replace('/\[inlinesize\=7\](.*?)\[\/inlinesize\]/i', '==$1==', $Block); - - - $Array[$ArrayPos] = array('Type'=>$TagName, 'Val'=>$Block); - break; - case 'spoiler': - case 'hide': - $Array[$ArrayPos] = array('Type'=>'hide', 'Attr'=>$Attrib, 'Val'=>self::parse($Block)); - break; - case 'mature': - $Array[$ArrayPos] = array('Type'=>'mature', 'Attr'=>$Attrib, 'Val'=>self::parse($Block)); - break; - case '#': - case '*': - case '##': - case '**': - case '###': - case '***': - $CurrentId = 1; - $Array[$ArrayPos] = array('Type'=>'list'); - $Array[$ArrayPos]['Val'] = explode("[$TagName]", $Block); - $Array[$ArrayPos]['ListType'] = $TagName[0] === '*' ? 'ul' : 'ol'; - $Array[$ArrayPos]['Tag'] = $TagName; - $ChildPrefix = $ListPrefix === '' ? $ListId : $ListPrefix; - if ($Attrib !== '') { - $ChildPrefix = $Attrib; - } - foreach ($Array[$ArrayPos]['Val'] as $Key=>$Val) { - $Id = $ChildPrefix.'.'.($CurrentId++); - $Array[$ArrayPos]['Val'][$Key] = self::parse(trim($Val), $Id); - $Array[$ArrayPos]['Val'][$Key]['Id'] = $Id; - } - $ListId++; - break; - case 'n': - $ArrayPos--; - break; // n serves only to disrupt bbcode (backwards compatibility - use [pre]) - default: - if ($WikiLink == true) { - $Array[$ArrayPos] = array('Type'=>'wiki','Val'=>$TagName); - } else { - - // Basic tags, like [b] or [size=5] - - $Array[$ArrayPos] = array('Type'=>$TagName, 'Val'=>self::parse($Block)); - if (!empty($Attrib) && $MaxAttribs > 0) { - $Array[$ArrayPos]['Attr'] = strtolower($Attrib); - } - } - } - - $ArrayPos++; // 7) Increment array pointer, start again (past the end of the [/close] tag) - } - return $Array; - } - - /** - * Generates a navigation list for TOC - * @param int $Min Minimum number of headlines required for a TOC list - */ - public static function parse_toc ($Min = 3, $RulesTOC = false) { - if (count(self::$Headlines) > $Min) { - $tag = $RulesTOC ? 'ul' : 'ol'; - if ($RulesTOC) { - $list = "<$tag>"; - } else { - $list = "<$tag class=\"navigation_list\">"; - } - $i = 0; - $level = 0; - $off = 0; - - foreach (self::$Headlines as $t) { - $n = (int)$t[0]; - if ($i === 0 && $n > 1) { - $off = $n - $level; - } - self::headline_level($n, $level, $list, $i, $off, $tag); - $list .= sprintf('
  • %1$s', $t[1], $t[2]); - $level = $t[0]; - $off = 0; - $i++; - } - - $list .= str_repeat("
  • ", $level); - $list .= "\n\n"; - return $list; - } - } - - /** - * Generates the list items and proper depth - * - * First check if the item should be higher than the current level - * - Close the list and previous lists - * - * Then check if the item should go lower than the current level - * - If the list doesn't open on level one, use the Offset - * - Open appropriate sub lists - * - * Otherwise the item is on the same as level as the previous item - * - * @param int $ItemLevel Current item level - * @param int $Level Current list level - * @param str $List reference to an XHTML string - * @param int $i Iterator digit - * @param int $Offset If the list doesn't start at level 1 - */ - private static function headline_level (&$ItemLevel, &$Level, &$List, $i, &$Offset, $Tag) { - if ($ItemLevel < $Level) { - $diff = $Level - $ItemLevel; - $List .= '' . str_repeat("", $diff); - } elseif ($ItemLevel > $Level) { - $diff = $ItemLevel - $Level; - if ($Offset > 0) $List .= str_repeat("
  • <$Tag>", $Offset - 2); - - if ($ItemLevel > 1) { - $List .= $i === 0 ? '
  • ' : ''; - $List .= "\n<$Tag>\n"; - } - } else { - $List .= $i > 0 ? '
  • ' : '
  • '; - } - } - - private static function to_html ($Array, $Rules) { - global $SSL; - self::$Levels++; - /* - * Hax prevention - * That's the original comment on this. - * Most likely this was implemented to avoid anyone nesting enough - * elements to reach PHP's memory limit as nested elements are - * solved recursively. - * Original value of 10, it is now replaced in favor of - * $MaximumNests. - * If this line is ever executed then something is, infact - * being haxed as the if before the block type switch for different - * tags should always be limiting ahead of this line. - * (Larger than vs. smaller than.) - */ - if (self::$Levels > self::$MaximumNests) { - return $Array['Val']; // Hax prevention, breaks upon exceeding nests. - } - $Str = ''; - - if (array_key_exists('Id', $Array) && is_string($Array[0]) && count($Array) == 2) { - self::$Levels--; - return self::smileys($Array[0]); - } - - foreach ($Array as $Key=>$Block) { - if ($Key === 'Id') { - continue; - } - if (is_string($Block)) { - $Str .= self::smileys($Block); - self::$Levels--; - continue; - } - if (self::$Levels < self::$MaximumNests) { - switch ($Block['Type']) { - case 'b': - $Str .= ''.self::to_html($Block['Val'], $Rules).''; - break; - case 'u': - $Str .= ''.self::to_html($Block['Val'], $Rules).''; - break; - case 'i': - $Str .= ''.self::to_html($Block['Val'], $Rules).""; - break; - case 's': - $Str .= ''.self::to_html($Block['Val'], $Rules).''; - break; - case 'important': - $Str .= ''.self::to_html($Block['Val'], $Rules).''; - break; - case 'user': - $Str .= ''.$Block['Val'].''; - break; - case 'artist': - $Str .= ''.$Block['Val'].''; - break; - case 'rule': - $Rule = trim(strtolower($Block['Val'])); - if ($Rule[0] != 'r' && $Rule[0] != 'h') { - $Rule = 'r'.$Rule; - } - $Str .= ''.preg_replace('/[aA-zZ]/', '', $Block['Val']).''; - break; - case 'torrent': - $Pattern = '/('.NONSSL_SITE_URL.'\/torrents\.php.*[\?&]id=)?(\d+)($|&|\#).*/i'; - $Matches = array(); - if (preg_match($Pattern, $Block['Val'], $Matches)) { - if (isset($Matches[2])) { - $GroupID = $Matches[2]; - $Groups = Torrents::get_groups(array($GroupID), true, true, false); - if ($Groups[$GroupID]) { - $Group = $Groups[$GroupID]; - $Str .= Artists::display_artists($Group['ExtendedArtists']).''.$Group['Name'].''; - } else { - $Str .= '[torrent]'.str_replace('[inlineurl]', '', $Block['Val']).'[/torrent]'; - } - } - } else { - $Str .= '[torrent]'.str_replace('[inlineurl]', '', $Block['Val']).'[/torrent]'; - } - break; - case 'wiki': - $Str .= ''.$Block['Val'].''; - break; - case 'tex': - $Str .= ''.$Block['Val'].''; - break; - case 'plain': - $Str .= $Block['Val']; - break; - case 'pre': - $Str .= '
    '.$Block['Val'].'
    '; - break; - case 'code': - $Str .= ''.$Block['Val'].''; - break; - case 'list': - $Str .= "<$Block[ListType] class=\"postlist\">"; - foreach ($Block['Val'] as $Line) { - $Str .= ''.self::to_html($Line, $Rules).'
  • '; - } - $Str .= ''; - break; - case 'align': - $ValidAttribs = array('left', 'center', 'right'); - if (!in_array($Block['Attr'], $ValidAttribs)) { - $Str .= '[align='.$Block['Attr'].']'.self::to_html($Block['Val']).'[/align]'; - } else { - $Str .= '
    '.self::to_html($Block['Val'], $Rules).'
    '; - } - break; - case 'color': - case 'colour': - $ValidAttribs = array('aqua', 'black', 'blue', 'fuchsia', 'green', 'grey', 'lime', 'maroon', 'navy', 'olive', 'purple', 'red', 'silver', 'teal', 'white', 'yellow'); - if (!in_array($Block['Attr'], $ValidAttribs) && !preg_match('/^#[0-9a-f]{6}$/', $Block['Attr'])) { - $Str .= '[color='.$Block['Attr'].']'.self::to_html($Block['Val'], $Rules).'[/color]'; - } else { - $Str .= ''.self::to_html($Block['Val'], $Rules).''; - } - break; - case 'headline': - $text = self::to_html($Block['Val'], $Rules); - $raw = self::raw_text($Block['Val']); - if (!in_array($Block['Attr'], self::$HeadlineLevels)) { - $Str .= sprintf('%1$s%2$s%1$s', str_repeat('=', $Block['Attr'] + 1), $text); - } else { - $id = '_' . crc32($raw . self::$HeadlineID); - if (self::$InQuotes === 0) { - self::$Headlines[] = array($Block['Attr'], $raw, $id); - } - - $Str .= sprintf('%2$s', ($Block['Attr'] + 2), $text, $id); - self::$HeadlineID++; - } - break; - case 'inlinesize': - case 'size': - $ValidAttribs = array('1', '2', '3', '4', '5', '6', '7', '8', '9', '10'); - if (!in_array($Block['Attr'], $ValidAttribs)) { - $Str .= '[size='.$Block['Attr'].']'.self::to_html($Block['Val'], $Rules).'[/size]'; - } else { - $Str .= ''.self::to_html($Block['Val'], $Rules).''; - } - break; - case 'quote': - self::$NoImg++; // No images inside quote tags - self::$InQuotes++; - if (self::$InQuotes == self::$NestsBeforeHide) { //Put quotes that are nested beyond the specified limit in [hide] tags. - $Str .= 'Older quotes: Show'; - $Str .= '
    '; // Ensure new line after quote train hiding - } - self::$NoImg--; - self::$InQuotes--; - break; - case 'box': - $Str .= '
    '.self::to_html($Block['Val'], $Rules).'
    '; - break; - case 'hide': - $Str .= ''.(($Block['Attr']) ? $Block['Attr'] : 'Hidden text').': Show'; - $Str .= ''; - break; - case 'mature': - if (G::$LoggedUser['EnableMatureContent']) { - if (!empty($Block['Attr'])) { - $Str .= 'Mature content: ' . $Block['Attr'] . '
    Show'; - $Str .= ''; - } - else { - $Str .= 'Use of the [mature] tag requires a description. The correct format is as follows: [mature=description] ...content... [/mature], where "description" is a mandatory description of the post. Misleading descriptions will be penalized. For further information on our mature content policies, please refer to this wiki.'; - } - } - else { - $Str .= 'Mature content has been blocked. You can choose to view mature content by editing your settings.'; - } - break; - case 'img': - if (self::$NoImg > 0 && self::valid_url($Block['Val'])) { - $Str .= ''.$Block['Val'].' (image)'; - break; - } - if (!self::valid_url($Block['Val'], '\.(jpe?g|gif|png|bmp|tiff)')) { - $Str .= '[img]'.$Block['Val'].'[/img]'; - } else { - $LocalURL = self::local_url($Block['Val']); - if ($LocalURL) { - $Str .= ''.$Block['Val'].''; - } else { - $Str .= ''.$Block['Val'].''; - } - } - break; - - case 'aud': - if (self::$NoImg > 0 && self::valid_url($Block['Val'])) { - $Str .= ''.$Block['Val'].' (audio)'; - break; - } - if (!self::valid_url($Block['Val'], '\.(mp3|ogg|wav)')) { - $Str .= '[aud]'.$Block['Val'].'[/aud]'; - } else { - //TODO: Proxy this for staff? - $Str .= ''; - } - break; - - case 'url': - // Make sure the URL has a label - if (empty($Block['Val'])) { - $Block['Val'] = $Block['Attr']; - $NoName = true; // If there isn't a Val for this - } else { - $Block['Val'] = self::to_html($Block['Val'], $Rules); - $NoName = false; - } - - if (!self::valid_url($Block['Attr'])) { - $Str .= '[url='.$Block['Attr'].']'.$Block['Val'].'[/url]'; - } else { - $LocalURL = self::local_url($Block['Attr']); - if ($LocalURL) { - if ($NoName) { $Block['Val'] = substr($LocalURL,1); } - $Str .= ''.$Block['Val'].''; - } else { - $Str .= ''.$Block['Val'].''; - } - } - break; - - case 'inlineurl': - if (!self::valid_url($Block['Attr'], '', true)) { - $Array = self::parse($Block['Attr']); - $Block['Attr'] = $Array; - $Str .= self::to_html($Block['Attr'], $Rules); - } - - else { - $LocalURL = self::local_url($Block['Attr']); - if ($LocalURL) { - $Str .= ''.substr($LocalURL,1).''; - } else { - $Str .= ''.$Block['Attr'].''; - } - } - - break; - - } - } - } - self::$Levels--; - return $Str; - } - - private static function raw_text ($Array) { - $Str = ''; - foreach ($Array as $Block) { - if (is_string($Block)) { - $Str .= $Block; - continue; - } - switch ($Block['Type']) { - case 'headline': - break; - case 'b': - case 'u': - case 'i': - case 's': - case 'color': - case 'size': - case 'quote': - case 'align': - - $Str .= self::raw_text($Block['Val']); - break; - case 'tex': //since this will never strip cleanly, just remove it - break; - case 'artist': - case 'user': - case 'wiki': - case 'pre': - case 'code': - case 'aud': - case 'img': - $Str .= $Block['Val']; - break; - case 'list': - foreach ($Block['Val'] as $Line) { - $Str .= $Block['Tag'].self::raw_text($Line); - } - break; - - case 'url': - // Make sure the URL has a label - if (empty($Block['Val'])) { - $Block['Val'] = $Block['Attr']; - } else { - $Block['Val'] = self::raw_text($Block['Val']); - } - - $Str .= $Block['Val']; - break; - - case 'inlineurl': - if (!self::valid_url($Block['Attr'], '', true)) { - $Array = self::parse($Block['Attr']); - $Block['Attr'] = $Array; - $Str .= self::raw_text($Block['Attr']); - } - else { - $Str .= $Block['Attr']; - } - - break; - } - } - return $Str; - } - - private static function smileys($Str) { - if (!empty(G::$LoggedUser['DisableSmileys'])) { - return $Str; - } - if (count(self::$ProcessedSmileys) == 0 && count(self::$Smileys) > 0) { - foreach (self::$Smileys as $Key => $Val) { - self::$ProcessedSmileys[$Key] = ''; - } - reset(self::$ProcessedSmileys); - } - $Str = strtr($Str, self::$ProcessedSmileys); - return $Str; - } - - /** - * Given a String that is composed of HTML, attempt to convert it back - * into BBCode. Useful when we're trying to deal with the output from - * some other site's metadata - * - * @param String $Html - * @return String - */ - public static function parse_html($Html) { - $Document = new DOMDocument(); - $Document->loadHtml($Html); - $Elements = $Document->getElementsByTagName('span'); - // When removing elements, you have to iterate over the list backwards - for ($i = $Elements->length - 1; $i >= 0; $i--) { - $Element = $Elements->item($i); - if (strpos($Element->getAttribute('class'), 'size') !== false) { - $NewElement = $Document->createElement('size', $Element->nodeValue); - $NewElement->setAttribute('size', str_replace('size', '', $Element->getAttribute('class'))); - $Element->parentNode->replaceChild($NewElement, $Element); - } - elseif (strpos($Element->getAttribute('style'), 'font-style: italic') !== false) { - $NewElement = $Document->createElement('italic', $Element->nodeValue); - $Element->parentNode->replaceChild($NewElement, $Element); - } - elseif (strpos($Element->getAttribute('style'), 'text-decoration: underline') !== false) { - $NewElement = $Document->createElement('underline', $Element->nodeValue); - $Element->parentNode->replaceChild($NewElement, $Element); - } - elseif (strpos($Element->getAttribute('style'), 'color: ') !== false) { - $NewElement = $Document->createElement('color', $Element->nodeValue); - $NewElement->setAttribute('color', str_replace(array('color: ', ';'), '', $Element->getAttribute('style'))); - $Element->parentNode->replaceChild($NewElement, $Element); - } - } - - $Elements = $Document->getElementsByTagName('ul'); - for ($i = 0; $i < $Elements->length; $i++) { - $InnerElements = $Elements->item($i)->getElementsByTagName('li'); - for ($j = $InnerElements->length - 1; $j >= 0; $j--) { - $Element = $InnerElements->item($j); - $NewElement = $Document->createElement('bullet', $Element->nodeValue); - $Element->parentNode->replaceChild($NewElement, $Element); - } - } - - $Elements = $Document->getElementsByTagName('ol'); - for ($i = 0; $i < $Elements->length; $i++) { - $InnerElements = $Elements->item($i)->getElementsByTagName('li'); - for ($j = $InnerElements->length - 1; $j >= 0; $j--) { - $Element = $InnerElements->item($j); - $NewElement = $Document->createElement('number', $Element->nodeValue); - $Element->parentNode->replaceChild($NewElement, $Element); - } - } - - $Elements = $Document->getElementsByTagName('strong'); - for ($i = $Elements->length - 1; $i >= 0; $i--) { - $Element = $Elements->item($i); - if ($Element->hasAttribute('class') === 'important_text') { - $NewElement = $Document->createElement('important', $Element->nodeValue); - $Element->parentNode->replaceChild($NewElement, $Element); - } - } - - $Elements = $Document->getElementsByTagName('a'); - for ($i = $Elements->length - 1; $i >= 0; $i--) { - $Element = $Elements->item($i); - if ($Element->hasAttribute('href')) { - $Element->removeAttribute('rel'); - $Element->removeAttribute('target'); - if ($Element->getAttribute('href') === $Element->nodeValue) { - $Element->removeAttribute('href'); - } - elseif ($Element->getAttribute('href') === 'javascript:void(0);' - && $Element->getAttribute('onclick') === 'BBCode.spoiler(this);') { - $Spoilers = $Document->getElementsByTagName('blockquote'); - for ($j = $Spoilers->length - 1; $j >= 0; $j--) { - $Spoiler = $Spoilers->item($j); - if ($Spoiler->hasAttribute('class') && $Spoiler->getAttribute('class') === 'hidden spoiler') { - $NewElement = $Document->createElement('spoiler', $Spoiler->nodeValue); - $Element->parentNode->replaceChild($NewElement, $Element); - $Spoiler->parentNode->removeChild($Spoiler); - break; - } - } - } - elseif (substr($Element->getAttribute('href'), 0, 22) === 'artist.php?artistname=') { - $NewElement = $Document->createElement('artist', $Element->nodeValue); - $Element->parentNode->replaceChild($NewElement, $Element); - } - elseif (substr($Element->getAttribute('href'), 0, 30) === 'user.php?action=search&search=') { - $NewElement = $Document->createElement('user', $Element->nodeValue); - $Element->parentNode->replaceChild($NewElement, $Element); - } - } - } - - $Str = str_replace(array("\n", "\n", "", ""), "", $Document->saveHTML($Document->getElementsByTagName('body')->item(0))); - $Str = str_replace(array("\r\n", "\n"), "", $Str); - $Str = preg_replace("/\([a-zA-Z0-9 ]+)\<\/strong\>\: \/", "[spoiler=\\1]", $Str); - $Str = str_replace("", "[/spoiler]", $Str); - $Str = preg_replace("/\(.*)\<\/strong\>(.*)wrote\:(.*)\/","[quote=\\1]", $Str); - $Str = preg_replace("/\<(\/*)blockquote\>/", "[\\1quote]", $Str); - $Str = preg_replace("/\<(\/*)strong\>/", "[\\1b]", $Str); - $Str = preg_replace("/\<(\/*)italic\>/", "[\\1i]", $Str); - $Str = preg_replace("/\<(\/*)underline\>/", "[\\1u]", $Str); - $Str = preg_replace("/\<(\/*)important\>/", "[\\1important]", $Str); - $Str = preg_replace("/\/", "[color=\\1]", $Str); - $Str = str_replace("", "[/color]", $Str); - $Str = str_replace(array('', ''), array('[#]', '[*]'), $Str); - $Str = str_replace(array('', ''), '
    ', $Str); - $Str = str_replace(array('
      ', '
        ', '
    ', ''), '', $Str); - $Str = preg_replace("/\/", "[size=\\1]", $Str); - $Str = str_replace("", "[/size]", $Str); - //$Str = preg_replace("/\(.*)\<\/a\>/", "[rule]\\3[/rule]", $Str); - //$Str = preg_replace("/\(.*)\<\/a>/", "[[\\1]]", $Str); - $Str = preg_replace('#/torrents.php\?recordlabel="?(?:[^"]*)#', 'https://'.SITE_URL.'\\0', $Str); - $Str = preg_replace('#/torrents.php\?taglist="?(?:[^"]*)#', 'https://'.SITE_URL.'\\0', $Str); - $Str = preg_replace("/\<(\/*)artist\>/", "[\\1artist]", $Str); - $Str = preg_replace("/\((\/*)user\>/", "[\\1user]", $Str); - $Str = preg_replace("/\/", "[url=\\1]", $Str); - $Str = preg_replace("/\<(\/*)a\>/", "[\\1url]", $Str); - $Str = preg_replace("/\/", '[img]\\2[/img]', $Str); - $Str = str_replace('

    ', '', $Str); - $Str = str_replace('

    ', '
    ', $Str); - //return $Str; - return str_replace(array("
    ", "
    "), "\n", $Str); - } + /** + * Array of valid tags; tag => max number of attributes + * @var array $ValidTags + */ + private static $ValidTags = array('b'=>0, 'u'=>0, 'i'=>0, 's'=>0, '*'=>1, '#'=>1, '**'=>1, '##'=>1, '***'=>1, '###'=>1, 'artist'=>0, 'user'=>0, 'n'=>0, 'inlineurl'=>0, 'inlinesize'=>1, 'headline'=>1, 'align'=>1, 'color'=>1, 'colour'=>1, 'size'=>1, 'url'=>1, 'img'=>1, 'quote'=>1, 'pre'=>1, 'code'=>1, 'tex'=>0, 'hide'=>1, 'spoiler' => 1, 'plain'=>0, 'important'=>0, 'torrent'=>0, 'rule'=>0, 'mature'=>1, 'box'=>0 + ); + + /** + * Array of smilies; code => image file in STATIC_SERVER/common/smileys + * @var array $Smileys + */ + private static $Smileys = array( + ':angry:' => 'angry.gif', + ':-D' => 'biggrin.gif', + ':D' => 'biggrin.gif', + ':|' => 'blank.gif', + ':-|' => 'blank.gif', + ':blush:' => 'blush.gif', + ':cool:' => 'cool.gif', + ':'(' => 'crying.gif', + ':crying:' => 'crying.gif', + '>.>' => 'eyesright.gif', + ':frown:' => 'frown.gif', + '<3' => 'heart.gif', + ':unsure:' => 'hmm.gif', + //':\\' => 'hmm.gif', + ':whatlove:' => 'ilu.gif', + ':lol:' => 'laughing.gif', + ':loveflac:' => 'loveflac.gif', + ':flaclove:' => 'loveflac.gif', + ':ninja:' => 'ninja.gif', + ':no:' => 'no.gif', + ':nod:' => 'nod.gif', + ':ohno:' => 'ohnoes.gif', + ':ohnoes:' => 'ohnoes.gif', + ':omg:' => 'omg.gif', + ':o' => 'ohshit.gif', + ':O' => 'ohshit.gif', + ':paddle:' => 'paddle.gif', + ':(' => 'sad.gif', + ':-(' => 'sad.gif', + ':shifty:' => 'shifty.gif', + ':sick:' => 'sick.gif', + ':)' => 'smile.gif', + ':-)' => 'smile.gif', + ':sorry:' => 'sorry.gif', + ':thanks:' => 'thanks.gif', + ':P' => 'tongue.gif', + ':p' => 'tongue.gif', + ':-P' => 'tongue.gif', + ':-p' => 'tongue.gif', + ':wave:' => 'wave.gif', + ';-)' => 'wink.gif', + ':wink:' => 'wink.gif', + ':creepy:' => 'creepy.gif', + ':worried:' => 'worried.gif', + ':wtf:' => 'wtf.gif', + ':wub:' => 'wub.gif', + ); + + /** + * Processed version of the $Smileys array, see {@link smileys} + * @var array $ProcessedSmileys + */ + private static $ProcessedSmileys = []; + + /** + * Whether or not to turn images into URLs (used inside [quote] tags). + * This is an integer reflecting the number of levels we're doing that + * transition, i.e. images will only be displayed as images if $NoImg <= 0. + * By setting this variable to a negative number you can delay the + * transition to a deeper level of quotes. + * @var int $NoImg + */ + private static $NoImg = 0; + + /** + * Internal counter for the level of recursion in to_html + * @var int $Levels + */ + private static $Levels = 0; + + /** + * The maximum amount of nesting allowed (exclusive) + * In reality n-1 nests are shown. + * @var int $MaximumNests + */ + private static $MaximumNests = 10; + + /** + * Used to detect and disable parsing (e.g. TOC) within quotes + * @var int $InQuotes + */ + private static $InQuotes = 0; + + /** + * Used to [hide] quote trains starting with the specified depth (inclusive) + * @var int $NestsBeforeHide + * + * This defaulted to 5 but was raised to 10 to effectively "disable" it until + * an optimal number of nested [quote] tags is chosen. The variable $MaximumNests + * effectively overrides this variable, if $MaximumNests is less than the value + * of $NestsBeforeHide. + */ + private static $NestsBeforeHide = 10; + + /** + * Array of headlines for Table Of Contents (TOC) + * @var array $HeadLines + */ + private static $Headlines; + + /** + * Counter for making headline URLs unique + * @var int $HeadLines + */ + private static $HeadlineID = 0; + + /** + * Depth + * @var array $HeadlineLevels + */ + private static $HeadlineLevels = array('1', '2', '3', '4'); + + /** + * TOC enabler + * @var bool $TOC + */ + public static $TOC = false; + + /** + * Output BBCode as XHTML + * @param string $Str BBCode text + * @param bool $OutputTOC Ouput TOC near (above) text + * @param int $Min See {@link parse_toc} + * @return string + */ + public static function full_format($Str, $OutputTOC = true, $Min = 3, $Rules = false) { + global $Debug; + $Debug->set_flag('BBCode start'); + $Str = display_str($Str); + self::$Headlines = []; + + //Inline links + $URLPrefix = '(\[url\]|\[url\=|\[img\=|\[img\])'; + $Str = preg_replace('/'.$URLPrefix.'\s+/i', '$1', $Str); + $Str = preg_replace('/(?set_flag('BBCode end'); + return $HTML; + } + + public static function strip_bbcode($Str) { + $Str = display_str($Str); + + //Inline links + $Str = preg_replace('/(? $i) { + $Array[$ArrayPos] = substr($Str, $i, $TagPos - $i); + ++$ArrayPos; + $i = $TagPos; + } + + // 2) See if it's a [[wiki-link]] or an ordinary tag, and get the tag name + if (!empty($Tag[4][0])) { // Wiki-link + $WikiLink = true; + $TagName = substr($Tag[4][0], 2, -2); + $Attrib = ''; + } else { // 3) If it's not a wiki link: + $WikiLink = false; + $TagName = strtolower(substr($Tag[2][0], 1)); + + //3a) check it against the self::$ValidTags array to see if it's actually a tag and not [bullshit] + if (!isset(self::$ValidTags[$TagName])) { + $Array[$ArrayPos] = substr($Str, $i, ($TagPos - $i) + strlen($Tag[0][0])); + $i = $TagPos + strlen($Tag[0][0]); + ++$ArrayPos; + continue; + } + + $MaxAttribs = self::$ValidTags[$TagName]; + + // 3b) Get the attribute, if it exists [name=attribute] + if (!empty($Tag[3][0])) { + $Attrib = substr($Tag[3][0], 1); + } else { + $Attrib = ''; + } + } + + // 4) Move the pointer past the end of the tag + $i = $TagPos + strlen($Tag[0][0]); + + // 5) Find out where the tag closes (beginning of [/tag]) + + // Unfortunately, BBCode doesn't have nice standards like XHTML + // [*], [img=...], and http:// follow different formats + // Thus, we have to handle these before we handle the majority of tags + + //5a) Different for different types of tag. Some tags don't close, others are weird like [*] + if ($TagName == 'img' && !empty($Tag[3][0])) { //[img=...] + $Block = ''; // Nothing inside this tag + // Don't need to touch $i + } elseif ($TagName == 'inlineurl') { // We did a big replace early on to turn http:// into [inlineurl]http:// + + // Let's say the block can stop at a newline or a space + $CloseTag = strcspn($Str, " \n\r", $i); + if ($CloseTag === false) { // block finishes with URL + $CloseTag = $Len; + } + if (preg_match('/[!,.?:]+$/',substr($Str, $i, $CloseTag), $Match)) { + $CloseTag -= strlen($Match[0]); + } + $URL = substr($Str, $i, $CloseTag); + if (substr($URL, -1) == ')' && substr_count($URL, '(') < substr_count($URL, ')')) { + $CloseTag--; + $URL = substr($URL, 0, -1); + } + $Block = $URL; // Get the URL + + // strcspn returns the number of characters after the offset $i, not after the beginning of the string + // Therefore, we use += instead of the = everywhere else + $i += $CloseTag; // 5d) Move the pointer past the end of the [/close] tag. + } elseif ($WikiLink == true || $TagName == 'n') { + // Don't need to do anything - empty tag with no closing + } elseif ($TagName[0] === '*' || $TagName[0] === '#') { + // We're in a list. Find where it ends + $NewLine = $i; + do { // Look for \n[*] + $NewLine = strpos($Str, "\n", $NewLine + 1); + } while ($NewLine !== false && substr($Str, $NewLine + 1, 1 + strlen($TagName)) == "[$TagName"); + + $CloseTag = $NewLine; + if ($CloseTag === false) { // block finishes with list + $CloseTag = $Len; + } + $Block = substr($Str, $i, $CloseTag - $i); // Get the list + $i = $CloseTag; // 5d) Move the pointer past the end of the [/close] tag. + } else { + //5b) If it's a normal tag, it may have versions of itself nested inside + $CloseTag = $i - 1; + $InTagPos = $i - 1; + $NumInOpens = 0; + $NumInCloses = -1; + + $InOpenRegex = '/\[('.$TagName.')'; + if ($MaxAttribs > 0) { + $InOpenRegex .= "(=[^\n'\"\[\]]+)?"; + } + $InOpenRegex .= '\]/i'; + + + // Every time we find an internal open tag of the same type, search for the next close tag + // (as the first close tag won't do - it's been opened again) + do { + $CloseTag = strpos($StrLC, "[/$TagName]", $CloseTag + 1); + if ($CloseTag === false) { + $CloseTag = $Len; + break; + } else { + $NumInCloses++; // Majority of cases + } + + // Is there another open tag inside this one? + $OpenTag = preg_match($InOpenRegex, $Str, $InTag, PREG_OFFSET_CAPTURE, $InTagPos + 1); + if (!$OpenTag || $InTag[0][1] > $CloseTag) { + break; + } else { + $InTagPos = $InTag[0][1]; + $NumInOpens++; + } + + } while ($NumInOpens > $NumInCloses); + + + // Find the internal block inside the tag + $Block = substr($Str, $i, $CloseTag - $i); // 5c) Get the contents between [open] and [/close] and call it the block. + + $i = $CloseTag + strlen($TagName) + 3; // 5d) Move the pointer past the end of the [/close] tag. + + } + + // 6) Depending on what type of tag we're dealing with, create an array with the attribute and block. + switch ($TagName) { + case 'inlineurl': + $Array[$ArrayPos] = array('Type'=>'inlineurl', 'Attr'=>$Block, 'Val'=>''); + break; + case 'url': + $Array[$ArrayPos] = array('Type'=>'img', 'Attr'=>$Attrib, 'Val'=>$Block); + if (empty($Attrib)) { // [url]http://...[/url] - always set URL to attribute + $Array[$ArrayPos] = array('Type'=>'url', 'Attr'=>$Block, 'Val'=>''); + } else { + $Array[$ArrayPos] = array('Type'=>'url', 'Attr'=>$Attrib, 'Val'=>self::parse($Block)); + } + break; + case 'quote': + $Array[$ArrayPos] = array('Type'=>'quote', 'Attr'=>self::parse($Attrib), 'Val'=>self::parse($Block)); + break; + case 'box': + $Array[$ArrayPos] = ['Type'=>'box', 'Val'=>self::parse($Block)]; + break; + case 'img': + case 'image': + if (empty($Block)) { + $Block = $Attrib; + } + $Array[$ArrayPos] = array('Type'=>'img', 'Val'=>$Block); + break; + case 'aud': + case 'mp3': + case 'audio': + if (empty($Block)) { + $Block = $Attrib; + } + $Array[$ArrayPos] = array('Type'=>'aud', 'Val'=>$Block); + break; + case 'user': + $Array[$ArrayPos] = array('Type'=>'user', 'Val'=>$Block); + break; + case 'artist': + $Array[$ArrayPos] = array('Type'=>'artist', 'Val'=>$Block); + break; + case 'torrent': + $Array[$ArrayPos] = array('Type'=>'torrent', 'Val'=>$Block); + break; + case 'tex': + $Array[$ArrayPos] = array('Type'=>'tex', 'Val'=>$Block); + break; + case 'rule': + $Array[$ArrayPos] = array('Type'=>'rule', 'Val'=>$Block); + break; + case 'pre': + case 'code': + case 'plain': + $Block = strtr($Block, array('[inlineurl]' => '')); + + $Callback = function ($matches) { + $n = $matches[2]; + $text = ''; + if ($n < 5 && $n > 0) { + $e = str_repeat('=', $matches[2] + 1); + $text = $e . $matches[3] . $e; + } + return $text; + }; + $Block = preg_replace_callback('/\[(headline)\=(\d)\](.*?)\[\/\1\]/i', $Callback, $Block); + + $Block = preg_replace('/\[inlinesize\=3\](.*?)\[\/inlinesize\]/i', '====$1====', $Block); + $Block = preg_replace('/\[inlinesize\=5\](.*?)\[\/inlinesize\]/i', '===$1===', $Block); + $Block = preg_replace('/\[inlinesize\=7\](.*?)\[\/inlinesize\]/i', '==$1==', $Block); + + + $Array[$ArrayPos] = array('Type'=>$TagName, 'Val'=>$Block); + break; + case 'spoiler': + case 'hide': + $Array[$ArrayPos] = array('Type'=>'hide', 'Attr'=>$Attrib, 'Val'=>self::parse($Block)); + break; + case 'mature': + $Array[$ArrayPos] = array('Type'=>'mature', 'Attr'=>$Attrib, 'Val'=>self::parse($Block)); + break; + case '#': + case '*': + case '##': + case '**': + case '###': + case '***': + $CurrentId = 1; + $Array[$ArrayPos] = array('Type'=>'list'); + $Array[$ArrayPos]['Val'] = explode("[$TagName]", $Block); + $Array[$ArrayPos]['ListType'] = $TagName[0] === '*' ? 'ul' : 'ol'; + $Array[$ArrayPos]['Tag'] = $TagName; + $ChildPrefix = $ListPrefix === '' ? $ListId : $ListPrefix; + if ($Attrib !== '') { + $ChildPrefix = $Attrib; + } + foreach ($Array[$ArrayPos]['Val'] as $Key=>$Val) { + $Id = $ChildPrefix.'.'.($CurrentId++); + $Array[$ArrayPos]['Val'][$Key] = self::parse(trim($Val), $Id); + $Array[$ArrayPos]['Val'][$Key]['Id'] = $Id; + } + $ListId++; + break; + case 'n': + $ArrayPos--; + break; // n serves only to disrupt bbcode (backwards compatibility - use [pre]) + default: + if ($WikiLink == true) { + $Array[$ArrayPos] = array('Type'=>'wiki','Val'=>$TagName); + } else { + + // Basic tags, like [b] or [size=5] + + $Array[$ArrayPos] = array('Type'=>$TagName, 'Val'=>self::parse($Block)); + if (!empty($Attrib) && $MaxAttribs > 0) { + $Array[$ArrayPos]['Attr'] = strtolower($Attrib); + } + } + } + + $ArrayPos++; // 7) Increment array pointer, start again (past the end of the [/close] tag) + } + return $Array; + } + + /** + * Generates a navigation list for TOC + * @param int $Min Minimum number of headlines required for a TOC list + */ + public static function parse_toc ($Min = 3, $RulesTOC = false) { + if (count(self::$Headlines) > $Min) { + $tag = $RulesTOC ? 'ul' : 'ol'; + if ($RulesTOC) { + $list = "<$tag>"; + } else { + $list = "<$tag class=\"navigation_list\">"; + } + $i = 0; + $level = 0; + $off = 0; + + foreach (self::$Headlines as $t) { + $n = (int)$t[0]; + if ($i === 0 && $n > 1) { + $off = $n - $level; + } + self::headline_level($n, $level, $list, $i, $off, $tag); + $list .= sprintf('
  • %1$s', $t[1], $t[2]); + $level = $t[0]; + $off = 0; + $i++; + } + + $list .= str_repeat("
  • ", $level); + $list .= "\n\n"; + return $list; + } + } + + /** + * Generates the list items and proper depth + * + * First check if the item should be higher than the current level + * - Close the list and previous lists + * + * Then check if the item should go lower than the current level + * - If the list doesn't open on level one, use the Offset + * - Open appropriate sub lists + * + * Otherwise the item is on the same as level as the previous item + * + * @param int $ItemLevel Current item level + * @param int $Level Current list level + * @param str $List reference to an XHTML string + * @param int $i Iterator digit + * @param int $Offset If the list doesn't start at level 1 + */ + private static function headline_level (&$ItemLevel, &$Level, &$List, $i, &$Offset, $Tag) { + if ($ItemLevel < $Level) { + $diff = $Level - $ItemLevel; + $List .= '' . str_repeat("", $diff); + } elseif ($ItemLevel > $Level) { + $diff = $ItemLevel - $Level; + if ($Offset > 0) $List .= str_repeat("
  • <$Tag>", $Offset - 2); + + if ($ItemLevel > 1) { + $List .= $i === 0 ? '
  • ' : ''; + $List .= "\n<$Tag>\n"; + } + } else { + $List .= $i > 0 ? '
  • ' : '
  • '; + } + } + + private static function to_html ($Array, $Rules) { + global $SSL; + self::$Levels++; + /* + * Hax prevention + * That's the original comment on this. + * Most likely this was implemented to avoid anyone nesting enough + * elements to reach PHP's memory limit as nested elements are + * solved recursively. + * Original value of 10, it is now replaced in favor of + * $MaximumNests. + * If this line is ever executed then something is, infact + * being haxed as the if before the block type switch for different + * tags should always be limiting ahead of this line. + * (Larger than vs. smaller than.) + */ + if (self::$Levels > self::$MaximumNests) { + return $Array['Val']; // Hax prevention, breaks upon exceeding nests. + } + $Str = ''; + + if (array_key_exists('Id', $Array) && is_string($Array[0]) && count($Array) == 2) { + self::$Levels--; + return self::smileys($Array[0]); + } + + foreach ($Array as $Key=>$Block) { + if ($Key === 'Id') { + continue; + } + if (is_string($Block)) { + $Str .= self::smileys($Block); + self::$Levels--; + continue; + } + if (self::$Levels < self::$MaximumNests) { + switch ($Block['Type']) { + case 'b': + $Str .= ''.self::to_html($Block['Val'], $Rules).''; + break; + case 'u': + $Str .= ''.self::to_html($Block['Val'], $Rules).''; + break; + case 'i': + $Str .= ''.self::to_html($Block['Val'], $Rules).""; + break; + case 's': + $Str .= ''.self::to_html($Block['Val'], $Rules).''; + break; + case 'important': + $Str .= ''.self::to_html($Block['Val'], $Rules).''; + break; + case 'user': + $Str .= ''.$Block['Val'].''; + break; + case 'artist': + $Str .= ''.$Block['Val'].''; + break; + case 'rule': + $Rule = trim(strtolower($Block['Val'])); + if ($Rule[0] != 'r' && $Rule[0] != 'h') { + $Rule = 'r'.$Rule; + } + $Str .= ''.preg_replace('/[aA-zZ]/', '', $Block['Val']).''; + break; + case 'torrent': + $Pattern = '/('.NONSSL_SITE_URL.'\/torrents\.php.*[\?&]id=)?(\d+)($|&|\#).*/i'; + $Matches = []; + if (preg_match($Pattern, $Block['Val'], $Matches)) { + if (isset($Matches[2])) { + $GroupID = $Matches[2]; + $Groups = Torrents::get_groups(array($GroupID), true, true, false); + if ($Groups[$GroupID]) { + $Group = $Groups[$GroupID]; + $Str .= Artists::display_artists($Group['ExtendedArtists']).''.$Group['Name'].''; + } else { + $Str .= '[torrent]'.str_replace('[inlineurl]', '', $Block['Val']).'[/torrent]'; + } + } + } else { + $Str .= '[torrent]'.str_replace('[inlineurl]', '', $Block['Val']).'[/torrent]'; + } + break; + case 'wiki': + $Str .= ''.$Block['Val'].''; + break; + case 'tex': + $Str .= ''.$Block['Val'].''; + break; + case 'plain': + $Str .= $Block['Val']; + break; + case 'pre': + $Str .= '
    '.$Block['Val'].'
    '; + break; + case 'code': + $Str .= ''.$Block['Val'].''; + break; + case 'list': + $Str .= "<$Block[ListType] class=\"postlist\">"; + foreach ($Block['Val'] as $Line) { + $Str .= ''.self::to_html($Line, $Rules).'
  • '; + } + $Str .= ''; + break; + case 'align': + $ValidAttribs = array('left', 'center', 'right'); + if (!in_array($Block['Attr'], $ValidAttribs)) { + $Str .= '[align='.$Block['Attr'].']'.self::to_html($Block['Val']).'[/align]'; + } else { + $Str .= '
    '.self::to_html($Block['Val'], $Rules).'
    '; + } + break; + case 'color': + case 'colour': + $ValidAttribs = array('aqua', 'black', 'blue', 'fuchsia', 'green', 'grey', 'lime', 'maroon', 'navy', 'olive', 'purple', 'red', 'silver', 'teal', 'white', 'yellow'); + if (!in_array($Block['Attr'], $ValidAttribs) && !preg_match('/^#[0-9a-f]{6}$/', $Block['Attr'])) { + $Str .= '[color='.$Block['Attr'].']'.self::to_html($Block['Val'], $Rules).'[/color]'; + } else { + $Str .= ''.self::to_html($Block['Val'], $Rules).''; + } + break; + case 'headline': + $text = self::to_html($Block['Val'], $Rules); + $raw = self::raw_text($Block['Val']); + if (!in_array($Block['Attr'], self::$HeadlineLevels)) { + $Str .= sprintf('%1$s%2$s%1$s', str_repeat('=', $Block['Attr'] + 1), $text); + } else { + $id = '_' . crc32($raw . self::$HeadlineID); + if (self::$InQuotes === 0) { + self::$Headlines[] = array($Block['Attr'], $raw, $id); + } + + $Str .= sprintf('%2$s', ($Block['Attr'] + 2), $text, $id); + self::$HeadlineID++; + } + break; + case 'inlinesize': + case 'size': + $ValidAttribs = array('1', '2', '3', '4', '5', '6', '7', '8', '9', '10'); + if (!in_array($Block['Attr'], $ValidAttribs)) { + $Str .= '[size='.$Block['Attr'].']'.self::to_html($Block['Val'], $Rules).'[/size]'; + } else { + $Str .= ''.self::to_html($Block['Val'], $Rules).''; + } + break; + case 'quote': + self::$NoImg++; // No images inside quote tags + self::$InQuotes++; + if (self::$InQuotes == self::$NestsBeforeHide) { //Put quotes that are nested beyond the specified limit in [hide] tags. + $Str .= 'Older quotes: Show'; + $Str .= '
    '; // Ensure new line after quote train hiding + } + self::$NoImg--; + self::$InQuotes--; + break; + case 'box': + $Str .= '
    '.self::to_html($Block['Val'], $Rules).'
    '; + break; + case 'hide': + $Str .= ''.(($Block['Attr']) ? $Block['Attr'] : 'Hidden text').': Show'; + $Str .= ''; + break; + case 'mature': + if (G::$LoggedUser['EnableMatureContent']) { + if (!empty($Block['Attr'])) { + $Str .= 'Mature content: ' . $Block['Attr'] . '
    Show'; + $Str .= ''; + } + else { + $Str .= 'Use of the [mature] tag requires a description. The correct format is as follows: [mature=description] ...content... [/mature], where "description" is a mandatory description of the post. Misleading descriptions will be penalized. For further information on our mature content policies, please refer to this wiki.'; + } + } + else { + $Str .= 'Mature content has been blocked. You can choose to view mature content by editing your settings.'; + } + break; + case 'img': + if (self::$NoImg > 0 && self::valid_url($Block['Val'])) { + $Str .= ''.$Block['Val'].' (image)'; + break; + } + if (!self::valid_url($Block['Val'], '\.(jpe?g|gif|png|bmp|tiff)')) { + $Str .= '[img]'.$Block['Val'].'[/img]'; + } else { + $LocalURL = self::local_url($Block['Val']); + if ($LocalURL) { + $Str .= ''.$Block['Val'].''; + } else { + $Str .= ''.$Block['Val'].''; + } + } + break; + + case 'aud': + if (self::$NoImg > 0 && self::valid_url($Block['Val'])) { + $Str .= ''.$Block['Val'].' (audio)'; + break; + } + if (!self::valid_url($Block['Val'], '\.(mp3|ogg|wav)')) { + $Str .= '[aud]'.$Block['Val'].'[/aud]'; + } else { + //TODO: Proxy this for staff? + $Str .= ''; + } + break; + + case 'url': + // Make sure the URL has a label + if (empty($Block['Val'])) { + $Block['Val'] = $Block['Attr']; + $NoName = true; // If there isn't a Val for this + } else { + $Block['Val'] = self::to_html($Block['Val'], $Rules); + $NoName = false; + } + + if (!self::valid_url($Block['Attr'])) { + $Str .= '[url='.$Block['Attr'].']'.$Block['Val'].'[/url]'; + } else { + $LocalURL = self::local_url($Block['Attr']); + if ($LocalURL) { + if ($NoName) { $Block['Val'] = substr($LocalURL,1); } + $Str .= ''.$Block['Val'].''; + } else { + $Str .= ''.$Block['Val'].''; + } + } + break; + + case 'inlineurl': + if (!self::valid_url($Block['Attr'], '', true)) { + $Array = self::parse($Block['Attr']); + $Block['Attr'] = $Array; + $Str .= self::to_html($Block['Attr'], $Rules); + } + + else { + $LocalURL = self::local_url($Block['Attr']); + if ($LocalURL) { + $Str .= ''.substr($LocalURL,1).''; + } else { + $Str .= ''.$Block['Attr'].''; + } + } + + break; + + } + } + } + self::$Levels--; + return $Str; + } + + private static function raw_text ($Array) { + $Str = ''; + foreach ($Array as $Block) { + if (is_string($Block)) { + $Str .= $Block; + continue; + } + switch ($Block['Type']) { + case 'headline': + break; + case 'b': + case 'u': + case 'i': + case 's': + case 'color': + case 'size': + case 'quote': + case 'align': + + $Str .= self::raw_text($Block['Val']); + break; + case 'tex': //since this will never strip cleanly, just remove it + break; + case 'artist': + case 'user': + case 'wiki': + case 'pre': + case 'code': + case 'aud': + case 'img': + $Str .= $Block['Val']; + break; + case 'list': + foreach ($Block['Val'] as $Line) { + $Str .= $Block['Tag'].self::raw_text($Line); + } + break; + + case 'url': + // Make sure the URL has a label + if (empty($Block['Val'])) { + $Block['Val'] = $Block['Attr']; + } else { + $Block['Val'] = self::raw_text($Block['Val']); + } + + $Str .= $Block['Val']; + break; + + case 'inlineurl': + if (!self::valid_url($Block['Attr'], '', true)) { + $Array = self::parse($Block['Attr']); + $Block['Attr'] = $Array; + $Str .= self::raw_text($Block['Attr']); + } + else { + $Str .= $Block['Attr']; + } + + break; + } + } + return $Str; + } + + private static function smileys($Str) { + if (!empty(G::$LoggedUser['DisableSmileys'])) { + return $Str; + } + if (count(self::$ProcessedSmileys) == 0 && count(self::$Smileys) > 0) { + foreach (self::$Smileys as $Key => $Val) { + self::$ProcessedSmileys[$Key] = ''; + } + reset(self::$ProcessedSmileys); + } + $Str = strtr($Str, self::$ProcessedSmileys); + return $Str; + } + + /** + * Given a String that is composed of HTML, attempt to convert it back + * into BBCode. Useful when we're trying to deal with the output from + * some other site's metadata. This also should reverse the HTML encoding + * that display_str does so we do not have to call reverse_display_str on the + * result. + * + * @param String $Html + * @return String + */ + public static function parse_html($Html) { + $Document = new DOMDocument(); + $Document->loadHtml(stripslashes($Html)); + + // For any manipulation that we do on the DOM tree, always go in reverse order or + // else you end up with broken array pointers and missed elements + $CopyNode = function ($OriginalElement, $NewElement) { + for ($i = count($OriginalElement->childNodes) - 1; $i >= 0; $i--) { + if (count($NewElement->childNodes) > 0) { + $NewElement->insertBefore($OriginalElement->childNodes[$i], $NewElement->childNodes[0]); + } + else { + $NewElement->appendChild($OriginalElement->childNodes[$i]); + } + } + }; + + $Elements = $Document->getElementsByTagName('div'); + for ($i = $Elements->length - 1; $i >= 0; $i--) { + $Element = $Elements->item($i); + if (strpos($Element->getAttribute('style'), 'text-align') !== false) { + $NewElement = $Document->createElement('align'); + $CopyNode($Element, $NewElement); + $NewElement->setAttribute('align', str_replace('text-align: ', '', $Element->getAttribute('style'))); + $Element->parentNode->replaceChild($NewElement, $Element); + } + } + + $Elements = $Document->getElementsByTagName('span'); + for ($i = $Elements->length - 1; $i >= 0; $i--) { + $Element = $Elements->item($i); + if (strpos($Element->getAttribute('class'), 'size') !== false) { + $NewElement = $Document->createElement('size'); + $CopyNode($Element, $NewElement); + $NewElement->setAttribute('size', str_replace('size', '', $Element->getAttribute('class'))); + $Element->parentNode->replaceChild($NewElement, $Element); + } + elseif (strpos($Element->getAttribute('style'), 'font-style: italic') !== false) { + $NewElement = $Document->createElement('italic'); + $CopyNode($Element, $NewElement); + $Element->parentNode->replaceChild($NewElement, $Element); + } + elseif (strpos($Element->getAttribute('style'), 'text-decoration: underline') !== false) { + $NewElement = $Document->createElement('underline'); + $CopyNode($Element, $NewElement); + $Element->parentNode->replaceChild($NewElement, $Element); + } + elseif (strpos($Element->getAttribute('style'), 'color: ') !== false) { + $NewElement = $Document->createElement('color'); + $CopyNode($Element, $NewElement); + $NewElement->setAttribute('color', str_replace(array('color: ', ';'), '', $Element->getAttribute('style'))); + $Element->parentNode->replaceChild($NewElement, $Element); + } + } + + $Elements = $Document->getElementsByTagName('ul'); + for ($i = 0; $i < $Elements->length; $i++) { + $InnerElements = $Elements->item($i)->getElementsByTagName('li'); + for ($j = $InnerElements->length - 1; $j >= 0; $j--) { + $Element = $InnerElements->item($j); + $NewElement = $Document->createElement('bullet'); + $CopyNode($Element, $NewElement); + $Element->parentNode->replaceChild($NewElement, $Element); + } + } + + $Elements = $Document->getElementsByTagName('ol'); + for ($i = 0; $i < $Elements->length; $i++) { + $InnerElements = $Elements->item($i)->getElementsByTagName('li'); + for ($j = $InnerElements->length - 1; $j >= 0; $j--) { + $Element = $InnerElements->item($j); + $NewElement = $Document->createElement('number'); + $CopyNode($Element, $NewElement); + $Element->parentNode->replaceChild($NewElement, $Element); + } + } + + $Elements = $Document->getElementsByTagName('strong'); + for ($i = $Elements->length - 1; $i >= 0; $i--) { + $Element = $Elements->item($i); + if ($Element->hasAttribute('class') === 'important_text') { + $NewElement = $Document->createElement('important'); + $CopyNode($Element, $NewElement); + $Element->parentNode->replaceChild($NewElement, $Element); + } + } + + $Elements = $Document->getElementsByTagName('a'); + for ($i = $Elements->length - 1; $i >= 0; $i--) { + $Element = $Elements->item($i); + if ($Element->hasAttribute('href')) { + $Element->removeAttribute('rel'); + $Element->removeAttribute('target'); + if ($Element->getAttribute('href') === $Element->nodeValue) { + $Element->removeAttribute('href'); + } + elseif ($Element->getAttribute('href') === 'javascript:void(0);' + && $Element->getAttribute('onclick') === 'BBCode.spoiler(this);') { + $Spoilers = $Document->getElementsByTagName('blockquote'); + for ($j = $Spoilers->length - 1; $j >= 0; $j--) { + $Spoiler = $Spoilers->item($j); + if ($Spoiler->hasAttribute('class') && $Spoiler->getAttribute('class') === 'hidden spoiler') { + $NewElement = $Document->createElement('spoiler'); + $CopyNode($Spoiler, $NewElement); + $Element->parentNode->replaceChild($NewElement, $Element); + $Spoiler->parentNode->removeChild($Spoiler); + break; + } + } + } + elseif (substr($Element->getAttribute('href'), 0, 22) === 'artist.php?artistname=') { + $NewElement = $Document->createElement('artist'); + $CopyNode($Element, $NewElement); + $Element->parentNode->replaceChild($NewElement, $Element); + } + elseif (substr($Element->getAttribute('href'), 0, 30) === 'user.php?action=search&search=') { + $NewElement = $Document->createElement('user'); + $CopyNode($Element, $NewElement); + $Element->parentNode->replaceChild($NewElement, $Element); + } + } + } + + $Str = str_replace(array("\n", "\n", "", ""), "", $Document->saveHTML($Document->getElementsByTagName('body')->item(0))); + $Str = str_replace(array("\r\n", "\n"), "", $Str); + $Str = preg_replace("/\([a-zA-Z0-9 ]+)\<\/strong\>\: \/", "[spoiler=\\1]", $Str); + $Str = str_replace("", "[/spoiler]", $Str); + $Str = preg_replace("/\(.*)\<\/strong\>(.*)wrote\:(.*)\/","[quote=\\1]", $Str); + $Str = preg_replace("/\<(\/*)blockquote\>/", "[\\1quote]", $Str); + $Str = preg_replace("/\<(\/*)strong\>/", "[\\1b]", $Str); + $Str = preg_replace("/\<(\/*)italic\>/", "[\\1i]", $Str); + $Str = preg_replace("/\<(\/*)underline\>/", "[\\1u]", $Str); + $Str = preg_replace("/\<(\/*)important\>/", "[\\1important]", $Str); + $Str = preg_replace("/\<(\/*)code\>/", "[\\1code]", $Str); + $Str = preg_replace("/\<(\/*)pre\>/", "[\\1pre]", $Str); + $Str = preg_replace("/\/", "[color=\\1]", $Str); + $Str = str_replace("", "[/color]", $Str); + $Str = str_replace(array('', ''), array('[#]', '[*]'), $Str); + $Str = str_replace(array('', ''), '
    ', $Str); + $Str = str_replace(array('
      ', '
        ', '
    ', ''), '', $Str); + $Str = preg_replace("/\/", "[align=\\1]", $Str); + $Str = str_replace("", "[/align]", $Str); + $Str = preg_replace("/\/", "[size=\\1]", $Str); + $Str = str_replace("", "[/size]", $Str); + //$Str = preg_replace("/\(.*)\<\/a\>/", "[rule]\\3[/rule]", $Str); + //$Str = preg_replace("/\(.*)\<\/a>/", "[[\\1]]", $Str); + $Str = preg_replace('#/torrents.php\?recordlabel="?(?:[^"]*)#', 'https://'.SITE_URL.'\\0', $Str); + $Str = preg_replace('#/torrents.php\?taglist="?(?:[^"]*)#', 'https://'.SITE_URL.'\\0', $Str); + $Str = preg_replace("/\<(\/*)artist\>/", "[\\1artist]", $Str); + $Str = preg_replace("/\((\/*)user\>/", "[\\1user]", $Str); + $Str = preg_replace("/\/", "[url=\\1]", $Str); + $Str = preg_replace("/\<(\/*)a\>/", "[\\1url]", $Str); + $Str = preg_replace("/\/", '[img]\\2[/img]', $Str); + $Str = str_replace('

    ', '', $Str); + $Str = str_replace('

    ', '
    ', $Str); + //return $Str; + return str_replace(array("
    ", "
    "), "\n", $Str); + } } + /* +define("SITE_URL", 'https://orpheus.network'); -// Uncomment this part to test the class via command line: -function display_str($Str) { - return $Str; -} -function check_perms($Perm) { - return true; -} -$Str = "hello -[pre]http://anonym.to/?http://whatshirts.portmerch.com/ -====hi==== -===hi=== -==hi==[/pre] -====hi==== -hi"; -echo Text::full_format($Str); -echo "\n" +$Str = "
    Album Information
    \r\nInterstellar Rift Original Soundtrack<\/strong><\/span><\/span><\/div>
    \r\n
    \r\n
    • Catalog Number:<\/strong>\tN\/A<\/li>
    • Release Date:<\/strong>\tMay 29, 2017<\/li>
    • Publish Format:<\/strong>\tCommercial<\/li>
    • Release Price:<\/strong>\t13.00 AUD<\/li>
    • Media Format:<\/strong>\tDigital<\/li>
    • Classification:<\/strong>\tOriginal Soundtrack<\/li>
    • Published by:<\/strong>\tBoss Battle Records (distributed by Bandcamp)<\/li>
    • Composed by:<\/strong>\tCurtis Schweitzer<\/li>
    • Arranged by:<\/strong><\/li><\/ul>
      \n\r
      \n
      Tracklist
      \r\n01\tInterstellar Rift\t\t2:38
      \r\n02\tShipmaking\t\t\t4:16
      \r\n03\tGalaxy Dawn\t\t\t2:55
      \r\n04\tInterloper\t\t\t2:32
      \r\n05\tShipbreaking\t\t\t2:06
      \r\n06\tHorizon Wake\t\t\t4:24
      \r\n07\tSky Harvest\t\t\t3:50
      \r\n08\tTwilight Discord\t\t2:42
      \r\n09\tLanguid Constellations\t\t2:45
      \r\n10\tHurles Co.\t\t\t2:43
      \r\n11\tGalactic Trade Federation\t2:46
      \r\n12\tSentinel Security Systems\t4:08
      \r\n13\tDrifters\t\t\t3:18<\/pre><\/div>
      \r\n
      \r\n
      Notes
      \r\nComposed by Curtis Schweitzer
      \r\nMastered by Aaron Edwards
      \r\nSoundtrack curated by The Otherworld Agency<\/pre><\/div>"; +//echo $Str; +echo Text::parse_html($Str); +echo "\n"; */ diff --git a/classes/textarea_preview.class.php b/classes/textarea_preview.class.php index 8ab50e3d8..826f34a04 100644 --- a/classes/textarea_preview.class.php +++ b/classes/textarea_preview.class.php @@ -4,71 +4,71 @@ * generate the required JavaScript that enables the previews to work. */ class TEXTAREA_PREVIEW_SUPER { - /** - * @static - * @var int $Textareas Total number of textareas created - */ - protected static $Textareas = 0; - - /** - * @static - * @var array $_ID Array of textarea IDs - */ - protected static $_ID = array(); - - /** - * @static - * @var bool For use in JavaScript method - */ - private static $Executed = false; - - /** - * This method should only run once with $all as true and should be placed - * in the header or footer. - * - * If $all is true, it includes TextareaPreview and jQuery - * - * jQuery is required for this to work, include it in the headers. - * - * @static - * @param bool $all Output all required scripts, otherwise just do iterator() - * @example
      - * @return void - */ - public static function JavaScript ($all = true) { - if (self::$Textareas === 0) { - return; - } - if (self::$Executed === false && $all) { - View::parse('generic/textarea/script.phtml'); - } - - self::$Executed = true; - self::iterator(); - } - - /** - * This iterator generates JavaScript to initialize each JavaScript - * TextareaPreview object. - * - * It will generate a numeric or custom ID related to the textarea. - * @static - * @return void - */ - private static function iterator() { - $script = array(); - for ($i = 0; $i < self::$Textareas; $i++) { - if (isset(self::$_ID[$i]) && is_string(self::$_ID[$i])) { - $a = sprintf('%d, "%s"', $i, self::$_ID[$i]); - } else { - $a = $i; - } - $script[] = sprintf('[%s]', $a); - } - if (!empty($script)) { - View::parse('generic/textarea/script_factory.phtml', array('script' => join(', ', $script))); - } - } + /** + * @static + * @var int $Textareas Total number of textareas created + */ + protected static $Textareas = 0; + + /** + * @static + * @var array $_ID Array of textarea IDs + */ + protected static $_ID = []; + + /** + * @static + * @var bool For use in JavaScript method + */ + private static $Executed = false; + + /** + * This method should only run once with $all as true and should be placed + * in the header or footer. + * + * If $all is true, it includes TextareaPreview and jQuery + * + * jQuery is required for this to work, include it in the headers. + * + * @static + * @param bool $all Output all required scripts, otherwise just do iterator() + * @example
      + * @return void + */ + public static function JavaScript ($all = true) { + if (self::$Textareas === 0) { + return; + } + if (self::$Executed === false && $all) { + View::parse('generic/textarea/script.phtml'); + } + + self::$Executed = true; + self::iterator(); + } + + /** + * This iterator generates JavaScript to initialize each JavaScript + * TextareaPreview object. + * + * It will generate a numeric or custom ID related to the textarea. + * @static + * @return void + */ + private static function iterator() { + $script = []; + for ($i = 0; $i < self::$Textareas; $i++) { + if (isset(self::$_ID[$i]) && is_string(self::$_ID[$i])) { + $a = sprintf('%d, "%s"', $i, self::$_ID[$i]); + } else { + $a = $i; + } + $script[] = sprintf('[%s]', $a); + } + if (!empty($script)) { + View::parse('generic/textarea/script_factory.phtml', array('script' => join(', ', $script))); + } + } } /** @@ -87,7 +87,7 @@ private static function iterator() { * // no buttons or wrap preview divs. * // Buttons and preview divs are generated manually * $text = new TEXTAREA_PREVIEW('body_text', 'body_text', 'default text', - * 50, 20, false, false, array('disabled="disabled"', 'class="text"')); + * 50, 20, false, false, array('disabled="disabled"', 'class="text"')); * * $text->buttons(); // output buttons * @@ -100,123 +100,123 @@ private static function iterator() { * * // some template *
      - * - * - * - * - *
      - *
      - *
      + * + * + * + * + *
      + *
      + *
      *
      *
      */ class TEXTAREA_PREVIEW extends TEXTAREA_PREVIEW_SUPER { - /** - * @var int Unique ID - */ - private $id; - - /** - * Flag for preview output - * @var bool $preview - */ - private $preview = false; - - /** - * String table - * @var string Buffer - */ - private $buffer = null; - - /** - * This method creates a textarea - * - * @param string $Name name attribute - * @param string $ID id attribute - * @param string $Value default text attribute - * @param string $Cols cols attribute - * @param string $Rows rows attribute - * @param bool $Preview add the preview divs near the textarea - * @param bool $Buttons add the edit/preview buttons near the textarea - * @param bool $Buffer doesn't output the textarea, use getBuffer() - * @param array $ExtraAttributes array of attribute="value" - * @param bool $Required is this a required textarea - * - * If false for $Preview, $Buttons, or $Buffer, use the appropriate - * methods to add the those elements manually. Alternatively, use getID - * to create your own. - * - * It's important to have the right IDs as they make the JS function properly. - */ - public function __construct($Name, $ID = '', $Value = '', $Cols = 50, $Rows = 10, - $Preview = true, $Buttons = true, $Buffer = false, - array $ExtraAttributes = array(), $Required = false - ) { - $this->id = parent::$Textareas; - parent::$Textareas += 1; - array_push(parent::$_ID, $ID); - - if (empty($ID)) { - $ID = 'quickpost_' . $this->id; - } - - if (!empty($ExtraAttributes)) { - $Attributes = ' ' . implode(' ', $ExtraAttributes); - } else { - $Attributes = ''; - } - - if ($Preview === true) { - $this->preview(); - } - - $this->buffer = View::parse('generic/textarea/textarea.phtml', array( - 'ID' => $ID, - 'NID' => $this->id, - 'Name' => &$Name, - 'Value' => &$Value, - 'Cols' => &$Cols, - 'Rows' => &$Rows, - 'Attributes' => &$Attributes, - 'Required' => ($Required === true) ? 'required' : '' - ), $Buffer); - - if ($Buttons === true) { - $this->buttons(); - } - } - - /** - * Outputs the divs required for previewing the AJAX content - * Will only output once - */ - public function preview() { - if (!$this->preview) { - View::parse('generic/textarea/preview.phtml', array('ID' => $this->id)); - } - $this->preview = true; - } - - /** - * Outputs the preview and edit buttons - * Can be called many times to place buttons in different areas - */ - public function buttons() { - View::parse('generic/textarea/buttons.phtml', array('ID' => $this->id)); - } - - /** - * Returns the textarea's numeric ID. - */ - public function getID() { - return $this->id; - } - - /** - * Returns textarea string when buffer is enabled in the constructor - * @return string - */ - public function getBuffer() { - return $this->buffer; - } + /** + * @var int Unique ID + */ + private $id; + + /** + * Flag for preview output + * @var bool $preview + */ + private $preview = false; + + /** + * String table + * @var string Buffer + */ + private $buffer = null; + + /** + * This method creates a textarea + * + * @param string $Name name attribute + * @param string $ID id attribute + * @param string $Value default text attribute + * @param string $Cols cols attribute + * @param string $Rows rows attribute + * @param bool $Preview add the preview divs near the textarea + * @param bool $Buttons add the edit/preview buttons near the textarea + * @param bool $Buffer doesn't output the textarea, use getBuffer() + * @param array $ExtraAttributes array of attribute="value" + * @param bool $Required is this a required textarea + * + * If false for $Preview, $Buttons, or $Buffer, use the appropriate + * methods to add the those elements manually. Alternatively, use getID + * to create your own. + * + * It's important to have the right IDs as they make the JS function properly. + */ + public function __construct($Name, $ID = '', $Value = '', $Cols = 50, $Rows = 10, + $Preview = true, $Buttons = true, $Buffer = false, + array $ExtraAttributes = [], $Required = false + ) { + $this->id = parent::$Textareas; + parent::$Textareas += 1; + array_push(parent::$_ID, $ID); + + if (empty($ID)) { + $ID = 'quickpost_' . $this->id; + } + + if (!empty($ExtraAttributes)) { + $Attributes = ' ' . implode(' ', $ExtraAttributes); + } else { + $Attributes = ''; + } + + if ($Preview === true) { + $this->preview(); + } + + $this->buffer = View::parse('generic/textarea/textarea.phtml', array( + 'ID' => $ID, + 'NID' => $this->id, + 'Name' => &$Name, + 'Value' => &$Value, + 'Cols' => &$Cols, + 'Rows' => &$Rows, + 'Attributes' => &$Attributes, + 'Required' => ($Required === true) ? 'required' : '' + ), $Buffer); + + if ($Buttons === true) { + $this->buttons(); + } + } + + /** + * Outputs the divs required for previewing the AJAX content + * Will only output once + */ + public function preview() { + if (!$this->preview) { + View::parse('generic/textarea/preview.phtml', array('ID' => $this->id)); + } + $this->preview = true; + } + + /** + * Outputs the preview and edit buttons + * Can be called many times to place buttons in different areas + */ + public function buttons() { + View::parse('generic/textarea/buttons.phtml', array('ID' => $this->id)); + } + + /** + * Returns the textarea's numeric ID. + */ + public function getID() { + return $this->id; + } + + /** + * Returns textarea string when buffer is enabled in the constructor + * @return string + */ + public function getBuffer() { + return $this->buffer; + } } diff --git a/classes/thread.class.php b/classes/thread.class.php index a2fc2075b..e1454f933 100644 --- a/classes/thread.class.php +++ b/classes/thread.class.php @@ -6,150 +6,150 @@ */ class Thread { - private $id; // the ID of the row in the thread table - private $type; // the type of thread - private $created; // date created - private $story; // the array of notes in the conversation + private $id; // the ID of the row in the thread table + private $type; // the type of thread + private $created; // date created + private $story; // the array of notes in the conversation - /** - * persistent Thread constructor - * @param string $type Thread Type (corresponds to db thread_type.Name) - */ - public function __construct($type = null) { - if (!isset($type)) { - return; - } - $this->type = $type; - G::$DB->prepared_query(" - INSERT INTO thread (ThreadTypeID) VALUES ( - (SELECT ID FROM thread_type where Name = ?) - ) - ", $type); - $this->id = G::$DB->inserted_id(); - $this->story = []; - } + /** + * persistent Thread constructor + * @param string $type Thread Type (corresponds to db thread_type.Name) + */ + public function __construct($type = null) { + if (!isset($type)) { + return; + } + $this->type = $type; + G::$DB->prepared_query(" + INSERT INTO thread (ThreadTypeID) VALUES ( + (SELECT ID FROM thread_type where Name = ?) + ) + ", $type); + $this->id = G::$DB->inserted_id(); + $this->story = []; + } - /** - * @return int The id of a Thread - */ - public function id() { - return $this->id; - } + /** + * @return int The id of a Thread + */ + public function id() { + return $this->id; + } - /** - * Get the array of notes of the thread. A note is a hash with the following items: - * id - the id in thread_note table - * user_id - the id of the author in the users_main table - * user_name - the name of the member (normally we don't need anything else from there). - * visibility - this note is 'public' or just visibible to 'staff' - * created - when was this note created - * body - the note text itself - * @return array The list of notes in a thread ordered by most recent first. - */ - public function get_story() { - return $this->story; - } + /** + * Get the array of notes of the thread. A note is a hash with the following items: + * id - the id in thread_note table + * user_id - the id of the author in the users_main table + * user_name - the name of the member (normally we don't need anything else from there). + * visibility - this note is 'public' or just visibible to 'staff' + * created - when was this note created + * body - the note text itself + * @return array The list of notes in a thread ordered by most recent first. + */ + public function get_story() { + return $this->story; + } - /** - * Persist a note to the db. - * @param int $user_id The note author - * @param string $body The note text - * @param int $visibility 'public' or 'staff' - */ - public function save_note($user_id, $body, $visibility) { - G::$DB->prepared_query( - 'INSERT INTO thread_note (ThreadID, UserID, Body, Visibility) VALUES (?, ?, ?, ?)', - $this->id, $user_id, $body, $visibility - ); - return $this->refresh(); - } + /** + * Persist a note to the db. + * @param int $user_id The note author + * @param string $body The note text + * @param int $visibility 'public' or 'staff' + */ + public function save_note($user_id, $body, $visibility) { + G::$DB->prepared_query( + 'INSERT INTO thread_note (ThreadID, UserID, Body, Visibility) VALUES (?, ?, ?, ?)', + $this->id, $user_id, $body, $visibility + ); + return $this->refresh(); + } - /** - * Persist a change to the note - * @param int $id The id to identify a note - * @param string $body The note text - * @param int $visibility 'public' or 'staff' - */ - public function update_note($id, $body, $visibility) { - G::$DB->prepared_query( - 'UPDATE thread_note SET Body = ?, Visibility = ? WHERE ID = ?', - $body, $visibility, $id - ); - return $this->refresh(); - } + /** + * Persist a change to the note + * @param int $id The id to identify a note + * @param string $body The note text + * @param int $visibility 'public' or 'staff' + */ + public function update_note($id, $body, $visibility) { + G::$DB->prepared_query( + 'UPDATE thread_note SET Body = ?, Visibility = ? WHERE ID = ?', + $body, $visibility, $id + ); + return $this->refresh(); + } - /** - * Delete a note. - * @param int $note_id The id to identify a note - */ - public function delete_note($note_id) { - G::$DB->prepared_query( - 'DELETE FROM thread_note WHERE ThreadID = ? AND ID = ?', - $this->id(), - $note_id - ); - return $this->refresh(); - } + /** + * Delete a note. + * @param int $note_id The id to identify a note + */ + public function delete_note($note_id) { + G::$DB->prepared_query( + 'DELETE FROM thread_note WHERE ThreadID = ? AND ID = ?', + $this->id(), + $note_id + ); + return $this->refresh(); + } - /** - * Refresh the story cache when a note is added, changed, deleted. - */ - private function refresh() { - $key = "thread_story_" . $this->id; - G::$DB->prepared_query(' - SELECT ID, UserID, Visibility, Created, Body - FROM thread_note - WHERE ThreadID = ? - ORDER BY created; - ', $this->id); - $this->story = []; - if (G::$DB->has_results()) { - $user_cache = []; - while (($row = G::$DB->next_record())) { - if (!in_array($row['UserID'], $user_cache)) { - $user = Users::user_info($row['UserID']); - $user_cache[$row['UserID']] = $user['Username']; - } - $this->story[] = [ - 'id' => $row['ID'], - 'user_id' => $row['UserID'], - 'user_name' => $user_cache[$row['UserID']], - 'visibility' => $row['Visibility'], - 'created' => $row['Created'], - 'body' => $row['Body'] - ]; - } - } - G::$Cache->cache_value($key, $this->story, 3600); - return $this; - } + /** + * Refresh the story cache when a note is added, changed, deleted. + */ + private function refresh() { + $key = "thread_story_" . $this->id; + G::$DB->prepared_query(' + SELECT ID, UserID, Visibility, Created, Body + FROM thread_note + WHERE ThreadID = ? + ORDER BY created; + ', $this->id); + $this->story = []; + if (G::$DB->has_results()) { + $user_cache = []; + while (($row = G::$DB->next_record())) { + if (!in_array($row['UserID'], $user_cache)) { + $user = Users::user_info($row['UserID']); + $user_cache[$row['UserID']] = $user['Username']; + } + $this->story[] = [ + 'id' => $row['ID'], + 'user_id' => $row['UserID'], + 'user_name' => $user_cache[$row['UserID']], + 'visibility' => $row['Visibility'], + 'created' => $row['Created'], + 'body' => $row['Body'] + ]; + } + } + G::$Cache->cache_value($key, $this->story, 3600); + return $this; + } - // FACTORY METHODS + // FACTORY METHODS - /** - * Instantiate a new instance of a Thread from an id - * @param $id int The id of a Thread - * @return a Thread object - */ - static public function factory($id) { - $thread = new self(); - $key = "thread_$id"; - $data = G::$Cache->get_value($key); - if ($data === false) { - G::$DB->prepared_query(" - SELECT tt.Name as ThreadType, t.Created - FROM thread t - INNER JOIN thread_type tt ON (tt.ID = t.ThreadTypeID) - WHERE t.ID = ? - ", $id); - if (G::$DB->has_results()) { - $data = G::$DB->next_record(); - G::$Cache->cache_value($key, $data, 86400); - } - } - $thread->id = $id; - $thread->type = $data['ThreadType']; - $thread->created = $data['Created']; - return $thread->refresh(); /* load the story */ - } + /** + * Instantiate a new instance of a Thread from an id + * @param $id int The id of a Thread + * @return a Thread object + */ + static public function factory($id) { + $thread = new self(); + $key = "thread_$id"; + $data = G::$Cache->get_value($key); + if ($data === false) { + G::$DB->prepared_query(" + SELECT tt.Name as ThreadType, t.Created + FROM thread t + INNER JOIN thread_type tt ON (tt.ID = t.ThreadTypeID) + WHERE t.ID = ? + ", $id); + if (G::$DB->has_results()) { + $data = G::$DB->next_record(); + G::$Cache->cache_value($key, $data, 86400); + } + } + $thread->id = $id; + $thread->type = $data['ThreadType']; + $thread->created = $data['Created']; + return $thread->refresh(); /* load the story */ + } } diff --git a/classes/time.class.php b/classes/time.class.php index 74d7e468e..871cce437 100644 --- a/classes/time.class.php +++ b/classes/time.class.php @@ -3,7 +3,7 @@ use Gazelle\Util\Time; function time_ago($TimeStamp) { - return Time::timeAgo($TimeStamp); + return Time::timeAgo($TimeStamp); } /* @@ -11,7 +11,7 @@ function time_ago($TimeStamp) { * difference in text (e.g. "16 hours and 28 minutes", "1 day, 18 hours"). */ function time_diff($TimeStamp, $Levels = 2, $Span = true, $Lowercase = false, $StartTime = false) { - return Time::timeDiff($TimeStamp, $Levels, $Span, $Lowercase, $StartTime); + return Time::timeDiff($TimeStamp, $Levels, $Span, $Lowercase, $StartTime); } /** @@ -24,35 +24,35 @@ function time_diff($TimeStamp, $Levels = 2, $Span = true, $Lowercase = false, $S * @return string */ function convert_hours($Hours,$Levels=2,$Span=true) { - return Time::convertHours($Hours, $Levels, $Span); + return Time::convertHours($Hours, $Levels, $Span); } /* SQL utility functions */ function time_plus($Offset) { - return Time::timePlus($Offset); + return Time::timePlus($Offset); } function time_minus($Offset, $Fuzzy = false) { - return Time::timeMinus($Offset, $Fuzzy); + return Time::timeMinus($Offset, $Fuzzy); } function sqltime($timestamp = false) { - return Time::sqlTime($timestamp); + return Time::sqlTime($timestamp); } function validDate($DateString) { - return Time::validDate($DateString); + return Time::validDate($DateString); } function is_valid_date($Date) { - return Time::isValidDate($Date); + return Time::isValidDate($Date); } function is_valid_time($Time) { - return Time::isValidTime($Time); + return Time::isValidTime($Time); } function is_valid_datetime($DateTime, $Format = 'Y-m-d H:i') { - return Time::isValidDateTime($DateTime, $Format); + return Time::isValidDateTime($DateTime, $Format); } diff --git a/classes/tools.class.php b/classes/tools.class.php index 8505ba763..4abaebdd5 100644 --- a/classes/tools.class.php +++ b/classes/tools.class.php @@ -1,324 +1,324 @@ -get_value('ip_bans_'.$A); - if (!is_array($IPBans)) { - $SQL = sprintf(" - SELECT ID, FromIP, ToIP - FROM ip_bans - WHERE FromIP BETWEEN %d << 24 AND (%d << 24) - 1", $A, $A + 1); - $QueryID = G::$DB->get_query_id(); - G::$DB->query($SQL); - $IPBans = G::$DB->to_array(0, MYSQLI_NUM); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value('ip_bans_'.$A, $IPBans, 0); - } - foreach ($IPBans as $Index => $IPBan) { - list ($ID, $FromIP, $ToIP) = $IPBan; - if ($IPNum >= $FromIP && $IPNum <= $ToIP) { - return true; - } - } + /** + * Returns true if given IP is banned. + * + * @param string $IP + */ + public static function site_ban_ip($IP) { + global $Debug; + $A = substr($IP, 0, strcspn($IP, '.')); + $IPNum = Tools::ip_to_unsigned($IP); + $IPBans = G::$Cache->get_value('ip_bans_'.$A); + if (!is_array($IPBans)) { + $SQL = sprintf(" + SELECT ID, FromIP, ToIP + FROM ip_bans + WHERE FromIP BETWEEN %d << 24 AND (%d << 24) - 1", $A, $A + 1); + $QueryID = G::$DB->get_query_id(); + G::$DB->query($SQL); + $IPBans = G::$DB->to_array(0, MYSQLI_NUM); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value('ip_bans_'.$A, $IPBans, 0); + } + foreach ($IPBans as $Index => $IPBan) { + list ($ID, $FromIP, $ToIP) = $IPBan; + if ($IPNum >= $FromIP && $IPNum <= $ToIP) { + return true; + } + } - return false; - } + return false; + } - /** - * Returns the unsigned form of an IP address. - * - * @param string $IP The IP address x.x.x.x - * @return string the long it represents. - */ - public static function ip_to_unsigned($IP) { - return sprintf('%u', ip2long($IP)); - } + /** + * Returns the unsigned form of an IP address. + * + * @param string $IP The IP address x.x.x.x + * @return string the long it represents. + */ + public static function ip_to_unsigned($IP) { + return sprintf('%u', ip2long($IP)); + } - /** - * Geolocate an IP address using the database - * - * @param $IP the ip to fetch the country for - * @return the country of origin - */ - public static function geoip($IP) { - static $IPs = array(); - if (isset($IPs[$IP])) { - return $IPs[$IP]; - } - if (is_number($IP)) { - $Long = $IP; - } else { - $Long = Tools::ip_to_unsigned($IP); - } - if (!$Long || $Long == 2130706433) { // No need to check cc for 127.0.0.1 - return false; - } - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT EndIP, Code - FROM geoip_country - WHERE $Long >= StartIP - ORDER BY StartIP DESC - LIMIT 1"); - if ((!list($EndIP, $Country) = G::$DB->next_record()) || $EndIP < $Long) { - $Country = '?'; - } - G::$DB->set_query_id($QueryID); - $IPs[$IP] = $Country; - return $Country; - } + /** + * Geolocate an IP address using the database + * + * @param $IP the ip to fetch the country for + * @return the country of origin + */ + public static function geoip($IP) { + static $IPs = []; + if (isset($IPs[$IP])) { + return $IPs[$IP]; + } + if (is_number($IP)) { + $Long = $IP; + } else { + $Long = Tools::ip_to_unsigned($IP); + } + if (!$Long || $Long == 2130706433) { // No need to check cc for 127.0.0.1 + return false; + } + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT EndIP, Code + FROM geoip_country + WHERE $Long >= StartIP + ORDER BY StartIP DESC + LIMIT 1"); + if ((!list($EndIP, $Country) = G::$DB->next_record()) || $EndIP < $Long) { + $Country = '?'; + } + G::$DB->set_query_id($QueryID); + $IPs[$IP] = $Country; + return $Country; + } - /** - * Gets the hostname for an IP address - * - * @param $IP the IP to get the hostname for - * @return hostname fetched - */ - public static function get_host_by_ip($IP) { - $testar = explode('.', $IP); - if (count($testar) != 4) { - return $IP; - } - for ($i = 0; $i < 4; ++$i) { - if (!is_numeric($testar[$i])) { - return $IP; - } - } + /** + * Gets the hostname for an IP address + * + * @param $IP the IP to get the hostname for + * @return hostname fetched + */ + public static function get_host_by_ip($IP) { + $testar = explode('.', $IP); + if (count($testar) != 4) { + return $IP; + } + for ($i = 0; $i < 4; ++$i) { + if (!is_numeric($testar[$i])) { + return $IP; + } + } - $host = `host -W 1 $IP`; - return ($host ? end(explode(' ', $host)) : $IP); - } + $host = `host -W 1 $IP`; + return ($host ? end(explode(' ', $host)) : $IP); + } - /** - * Gets an hostname using AJAX - * - * @param $IP the IP to fetch - * @return a span with JavaScript code - */ - public static function get_host_by_ajax($IP) { - static $IPs = array(); - $Class = strtr($IP, '.', '-'); - $HTML = 'Resolving host...'; - if (!isset($IPs[$IP])) { - $HTML .= ''; - } - $HTML .= ''; - $IPs[$IP] = 1; - return $HTML; - } + /** + * Gets an hostname using AJAX + * + * @param $IP the IP to fetch + * @return a span with JavaScript code + */ + public static function get_host_by_ajax($IP) { + static $IPs = []; + $Class = strtr($IP, '.', '-'); + $HTML = 'Resolving host...'; + if (!isset($IPs[$IP])) { + $HTML .= ''; + } + $HTML .= ''; + $IPs[$IP] = 1; + return $HTML; + } - /** - * Looks up the full host of an IP address, by system call. - * Used as the server-side counterpart to get_host_by_ajax. - * - * @param string $IP The IP address to look up. - * @return string the host. - */ - public static function lookup_ip($IP) { - //TODO: use the G::$Cache - $Output = explode(' ',shell_exec('host -W 1 '.escapeshellarg($IP))); - if (count($Output) == 1 && empty($Output[0])) { - //No output at all implies the command failed - return ''; - } + /** + * Looks up the full host of an IP address, by system call. + * Used as the server-side counterpart to get_host_by_ajax. + * + * @param string $IP The IP address to look up. + * @return string the host. + */ + public static function lookup_ip($IP) { + //TODO: use the G::$Cache + $Output = explode(' ',shell_exec('host -W 1 '.escapeshellarg($IP))); + if (count($Output) == 1 && empty($Output[0])) { + //No output at all implies the command failed + return ''; + } - if (count($Output) != 5) { - return false; - } else { - return trim($Output[4]); - } - } + if (count($Output) != 5) { + return false; + } else { + return trim($Output[4]); + } + } - /** - * Format an IP address with links to IP history. - * - * @param string IP - * @return string The HTML - */ - public static function display_ip($IP) { - $Line = display_str($IP).' ('.Tools::get_country_code_by_ajax($IP).') '; - $Line .= '
      S'; + /** + * Format an IP address with links to IP history. + * + * @param string IP + * @return string The HTML + */ + public static function display_ip($IP) { + $Line = display_str($IP).' ('.Tools::get_country_code_by_ajax($IP).') '; + $Line .= 'S'; - return $Line; - } + return $Line; + } - public static function get_country_code_by_ajax($IP) { - static $IPs = array(); - $Class = strtr($IP, '.', '-'); - $HTML = 'Resolving CC...'; - if (!isset($IPs[$IP])) { - $HTML .= ''; - } - $HTML .= ''; - $IPs[$IP] = 1; - return $HTML; - } + public static function get_country_code_by_ajax($IP) { + static $IPs = []; + $Class = strtr($IP, '.', '-'); + $HTML = 'Resolving CC...'; + if (!isset($IPs[$IP])) { + $HTML .= ''; + } + $HTML .= ''; + $IPs[$IP] = 1; + return $HTML; + } - /** - * Disable an array of users. - * - * @param array $UserIDs (You can also send it one ID as an int, because fuck types) - * @param BanReason 0 - Unknown, 1 - Manual, 2 - Ratio, 3 - Inactive, 4 - Unused. - */ - public static function disable_users($UserIDs, $AdminComment, $BanReason = 1) { - $QueryID = G::$DB->get_query_id(); - if (!is_array($UserIDs)) { - $UserIDs = array($UserIDs); - } - G::$DB->query(" - UPDATE users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - SET m.Enabled = '2', - m.can_leech = '0', - i.AdminComment = CONCAT('".sqltime()." - ".($AdminComment ? $AdminComment : 'Disabled by system')."\n\n', i.AdminComment), - i.BanDate = '".sqltime()."', - i.BanReason = '$BanReason', - i.RatioWatchDownload = ".($BanReason == 2 ? 'm.Downloaded' : "'0'")." - WHERE m.ID IN(".implode(',', $UserIDs).') '); - G::$Cache->decrement('stats_user_count', G::$DB->affected_rows()); - foreach ($UserIDs as $UserID) { - G::$Cache->delete_value("enabled_$UserID"); - G::$Cache->delete_value("user_info_$UserID"); - G::$Cache->delete_value("user_info_heavy_$UserID"); - G::$Cache->delete_value("user_stats_$UserID"); + /** + * Disable an array of users. + * + * @param array $UserIDs (You can also send it one ID as an int, because fuck types) + * @param BanReason 0 - Unknown, 1 - Manual, 2 - Ratio, 3 - Inactive, 4 - Unused. + */ + public static function disable_users($UserIDs, $AdminComment, $BanReason = 1) { + $QueryID = G::$DB->get_query_id(); + if (!is_array($UserIDs)) { + $UserIDs = array($UserIDs); + } + G::$DB->query(" + UPDATE users_info AS i + JOIN users_main AS m ON m.ID = i.UserID + SET m.Enabled = '2', + m.can_leech = '0', + i.AdminComment = CONCAT('".sqltime()." - ".($AdminComment ? $AdminComment : 'Disabled by system')."\n\n', i.AdminComment), + i.BanDate = '".sqltime()."', + i.BanReason = '$BanReason', + i.RatioWatchDownload = ".($BanReason == 2 ? 'm.Downloaded' : "'0'")." + WHERE m.ID IN(".implode(',', $UserIDs).') '); + G::$Cache->decrement('stats_user_count', G::$DB->affected_rows()); + foreach ($UserIDs as $UserID) { + G::$Cache->delete_value("enabled_$UserID"); + G::$Cache->delete_value("user_info_$UserID"); + G::$Cache->delete_value("user_info_heavy_$UserID"); + G::$Cache->delete_value("user_stats_$UserID"); - G::$DB->query(" - SELECT SessionID - FROM users_sessions - WHERE UserID = '$UserID' - AND Active = 1"); - while (list($SessionID) = G::$DB->next_record()) { - G::$Cache->delete_value("session_$UserID"."_$SessionID"); - } - G::$Cache->delete_value("users_sessions_$UserID"); + G::$DB->query(" + SELECT SessionID + FROM users_sessions + WHERE UserID = '$UserID' + AND Active = 1"); + while (list($SessionID) = G::$DB->next_record()) { + G::$Cache->delete_value("session_$UserID"."_$SessionID"); + } + G::$Cache->delete_value("users_sessions_$UserID"); - G::$DB->query(" - DELETE FROM users_sessions - WHERE UserID = '$UserID'"); + G::$DB->query(" + DELETE FROM users_sessions + WHERE UserID = '$UserID'"); - } + } - // Remove the users from the tracker. - G::$DB->query(' - SELECT torrent_pass - FROM users_main - WHERE ID in ('.implode(', ', $UserIDs).')'); - $PassKeys = G::$DB->collect('torrent_pass'); - $Concat = ''; - foreach ($PassKeys as $PassKey) { - if (strlen($Concat) > 3950) { // Ocelot's read buffer is 4 KiB and anything exceeding it is truncated - Tracker::update_tracker('remove_users', array('passkeys' => $Concat)); - $Concat = $PassKey; - } else { - $Concat .= $PassKey; - } - } - Tracker::update_tracker('remove_users', array('passkeys' => $Concat)); - G::$DB->set_query_id($QueryID); - } + // Remove the users from the tracker. + G::$DB->query(' + SELECT torrent_pass + FROM users_main + WHERE ID in ('.implode(', ', $UserIDs).')'); + $PassKeys = G::$DB->collect('torrent_pass'); + $Concat = ''; + foreach ($PassKeys as $PassKey) { + if (strlen($Concat) > 3950) { // Ocelot's read buffer is 4 KiB and anything exceeding it is truncated + Tracker::update_tracker('remove_users', array('passkeys' => $Concat)); + $Concat = $PassKey; + } else { + $Concat .= $PassKey; + } + } + Tracker::update_tracker('remove_users', array('passkeys' => $Concat)); + G::$DB->set_query_id($QueryID); + } - /** - * Warn a user. - * - * @param int $UserID - * @param int $Duration length of warning in seconds - * @param string $reason - */ - public static function warn_user($UserID, $Duration, $Reason) { - global $Time; + /** + * Warn a user. + * + * @param int $UserID + * @param int $Duration length of warning in seconds + * @param string $reason + */ + public static function warn_user($UserID, $Duration, $Reason) { + global $Time; - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT Warned - FROM users_info - WHERE UserID = $UserID - AND Warned != '0000-00-00 00:00:00'"); - if (G::$DB->has_results()) { - //User was already warned, appending new warning to old. - list($OldDate) = G::$DB->next_record(); - $NewExpDate = date('Y-m-d H:i:s', strtotime($OldDate) + $Duration); + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT Warned + FROM users_info + WHERE UserID = $UserID + AND Warned != '0000-00-00 00:00:00'"); + if (G::$DB->has_results()) { + //User was already warned, appending new warning to old. + list($OldDate) = G::$DB->next_record(); + $NewExpDate = date('Y-m-d H:i:s', strtotime($OldDate) + $Duration); - Misc::send_pm($UserID, 0, - 'You have received multiple warnings.', - "When you received your latest warning (set to expire on ".date('Y-m-d', (time() + $Duration)).'), you already had a different warning (set to expire on '.date('Y-m-d', strtotime($OldDate)).").\n\n Due to this collision, your warning status will now expire at $NewExpDate."); + Misc::send_pm($UserID, 0, + 'You have received multiple warnings.', + "When you received your latest warning (set to expire on ".date('Y-m-d', (time() + $Duration)).'), you already had a different warning (set to expire on '.date('Y-m-d', strtotime($OldDate)).").\n\n Due to this collision, your warning status will now expire at $NewExpDate."); - $AdminComment = date('Y-m-d')." - Warning (Clash) extended to expire at $NewExpDate by " . G::$LoggedUser['Username'] . "\nReason: $Reason\n\n"; + $AdminComment = date('Y-m-d')." - Warning (Clash) extended to expire at $NewExpDate by " . G::$LoggedUser['Username'] . "\nReason: $Reason\n\n"; - G::$DB->query(' - UPDATE users_info - SET - Warned = \''.db_string($NewExpDate).'\', - WarnedTimes = WarnedTimes + 1, - AdminComment = CONCAT(\''.db_string($AdminComment).'\', AdminComment) - WHERE UserID = \''.db_string($UserID).'\''); - } else { - //Not changing, user was not already warned - $WarnTime = time_plus($Duration); + G::$DB->query(' + UPDATE users_info + SET + Warned = \''.db_string($NewExpDate).'\', + WarnedTimes = WarnedTimes + 1, + AdminComment = CONCAT(\''.db_string($AdminComment).'\', AdminComment) + WHERE UserID = \''.db_string($UserID).'\''); + } else { + //Not changing, user was not already warned + $WarnTime = time_plus($Duration); - G::$Cache->begin_transaction("user_info_$UserID"); - G::$Cache->update_row(false, array('Warned' => $WarnTime)); - G::$Cache->commit_transaction(0); + G::$Cache->begin_transaction("user_info_$UserID"); + G::$Cache->update_row(false, array('Warned' => $WarnTime)); + G::$Cache->commit_transaction(0); - $AdminComment = date('Y-m-d')." - Warned until $WarnTime by " . G::$LoggedUser['Username'] . "\nReason: $Reason\n\n"; + $AdminComment = date('Y-m-d')." - Warned until $WarnTime by " . G::$LoggedUser['Username'] . "\nReason: $Reason\n\n"; - G::$DB->query(' - UPDATE users_info - SET - Warned = \''.db_string($WarnTime).'\', - WarnedTimes = WarnedTimes + 1, - AdminComment = CONCAT(\''.db_string($AdminComment).'\', AdminComment) - WHERE UserID = \''.db_string($UserID).'\''); - } - G::$DB->set_query_id($QueryID); - } + G::$DB->query(' + UPDATE users_info + SET + Warned = \''.db_string($WarnTime).'\', + WarnedTimes = WarnedTimes + 1, + AdminComment = CONCAT(\''.db_string($AdminComment).'\', AdminComment) + WHERE UserID = \''.db_string($UserID).'\''); + } + G::$DB->set_query_id($QueryID); + } - /** - * Update the notes of a user - * @param unknown $UserID ID of user - * @param unknown $AdminComment Comment to update with - */ - public static function update_user_notes($UserID, $AdminComment) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(' - UPDATE users_info - SET AdminComment = CONCAT(\''.db_string($AdminComment).'\', AdminComment) - WHERE UserID = \''.db_string($UserID).'\''); - G::$DB->set_query_id($QueryID); - } + /** + * Update the notes of a user + * @param unknown $UserID ID of user + * @param unknown $AdminComment Comment to update with + */ + public static function update_user_notes($UserID, $AdminComment) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(' + UPDATE users_info + SET AdminComment = CONCAT(\''.db_string($AdminComment).'\', AdminComment) + WHERE UserID = \''.db_string($UserID).'\''); + G::$DB->set_query_id($QueryID); + } - /** - * Check if an IP address is part of a given CIDR range. - * @param string $CheckIP the IP address to be looked up - * @param string $Subnet the CIDR subnet to be checked against - */ - public static function check_cidr_range($CheckIP, $Subnet) { - $IP = ip2long($CheckIP); - $CIDR = split('/', $Subnet); - $SubnetIP = ip2long($CIDR[0]); - $SubnetMaskBits = 32 - $CIDR[1]; + /** + * Check if an IP address is part of a given CIDR range. + * @param string $CheckIP the IP address to be looked up + * @param string $Subnet the CIDR subnet to be checked against + */ + public static function check_cidr_range($CheckIP, $Subnet) { + $IP = ip2long($CheckIP); + $CIDR = split('/', $Subnet); + $SubnetIP = ip2long($CIDR[0]); + $SubnetMaskBits = 32 - $CIDR[1]; - return (($IP>>$SubnetMaskBits) == ($SubnetIP>>$SubnetMaskBits)); - } + return (($IP>>$SubnetMaskBits) == ($SubnetIP>>$SubnetMaskBits)); + } } ?> diff --git a/classes/top10.class.php b/classes/top10.class.php index 7533761bb..145a2b224 100644 --- a/classes/top10.class.php +++ b/classes/top10.class.php @@ -1,4 +1,4 @@ - - - + + + + + + + +
      + -
      - - +
      + + -
      - + -
      - - -
      - + + +
      +$String
      "; - } else { - return $String; - } - } + private static function get_selected_link($String, $Selected) { + if ($Selected) { + return "$String"; + } else { + return $String; + } + } - public static function render_artist_tile($Artist, $Category) { - if (self::is_valid_artist($Artist)) { - switch ($Category) { - case 'weekly': - case 'hyped': - self::render_tile("artist.php?artistname=", $Artist['name'], $Artist['image'][3]['#text']); - break; - default: - break; - } - } - } + public static function render_artist_tile($Artist, $Category) { + if (self::is_valid_artist($Artist)) { + switch ($Category) { + case 'weekly': + case 'hyped': + self::render_tile("artist.php?artistname=", $Artist['name'], $Artist['image'][3]['#text']); + break; + default: + break; + } + } + } - private static function render_tile($Url, $Name, $Image) { - if (!empty($Image)) { - $Name = display_str($Name); + private static function render_tile($Url, $Name, $Image) { + if (!empty($Image)) { + $Name = display_str($Name); ?> -
    • - - <?=$Name?> - -
    • - + + <?=$Name?> + +
    • + -
    • - href=""> -
    • - + href=""> + +Str = $Val; - $this->dec(); - } else { - $this->Val = $Val; - } - } - - // Decode an element based on the type. The type is really just an indicator. - function decode($Type, $Key) { - if (is_number($Type)) { // Element is a string - // Get length of string - $StrLen = $Type; - while ($this->Str[$this->Pos + 1] != ':') { - $this->Pos++; - $StrLen.=$this->Str[$this->Pos]; - } - $this->Val[$Key] = substr($this->Str, $this->Pos + 2, $StrLen); - - $this->Pos += $StrLen; - $this->Pos += 2; - - } elseif ($Type == 'i') { // Element is an int - $this->Pos++; - - // Find end of integer (first occurance of 'e' after position) - $End = strpos($this->Str, 'e', $this->Pos); - - // Get the integer, and - IMPORTANT - cast it as an int, so we know later that it's an int and not a string - $this->Val[$Key] = (int)substr($this->Str, $this->Pos, $End-$this->Pos); - $this->Pos = $End + 1; - - } elseif ($Type == 'l') { // Element is a list - $this->Val[$Key] = new BENCODE_LIST(substr($this->Str, $this->Pos)); - $this->Pos += $this->Val[$Key]->Pos; - - } elseif ($Type == 'd') { // Element is a dictionary - $this->Val[$Key] = new BENCODE_DICT(substr($this->Str, $this->Pos)); - $this->Pos += $this->Val[$Key]->Pos; - // Sort by key to respect spec - if (!empty($this->Val[$Key]->Val)) { - ksort($this->Val[$Key]->Val); - } - - } else { - die('Invalid torrent file'); - } - } - - function encode($Val) { - if (is_int($Val)) { // Integer - return 'i'.$Val.'e'; - } elseif (is_string($Val)) { - return strlen($Val).':'.$Val; - } elseif (is_object($Val)) { - return $Val->enc(); - } else { - return 'fail'; - } - } + var $Val; // Decoded array + var $Pos = 1; // Pointer that indicates our position in the string + var $Str = ''; // Torrent string + + function __construct($Val, $IsParsed = false) { + if (!$IsParsed) { + $this->Str = $Val; + $this->dec(); + } else { + $this->Val = $Val; + } + } + + // Decode an element based on the type. The type is really just an indicator. + function decode($Type, $Key) { + if (is_number($Type)) { // Element is a string + // Get length of string + $StrLen = $Type; + while ($this->Str[$this->Pos + 1] != ':') { + $this->Pos++; + $StrLen.=$this->Str[$this->Pos]; + } + $this->Val[$Key] = substr($this->Str, $this->Pos + 2, $StrLen); + + $this->Pos += $StrLen; + $this->Pos += 2; + + } elseif ($Type == 'i') { // Element is an int + $this->Pos++; + + // Find end of integer (first occurance of 'e' after position) + $End = strpos($this->Str, 'e', $this->Pos); + + // Get the integer, and - IMPORTANT - cast it as an int, so we know later that it's an int and not a string + $this->Val[$Key] = (int)substr($this->Str, $this->Pos, $End-$this->Pos); + $this->Pos = $End + 1; + + } elseif ($Type == 'l') { // Element is a list + $this->Val[$Key] = new BENCODE_LIST(substr($this->Str, $this->Pos)); + $this->Pos += $this->Val[$Key]->Pos; + + } elseif ($Type == 'd') { // Element is a dictionary + $this->Val[$Key] = new BENCODE_DICT(substr($this->Str, $this->Pos)); + $this->Pos += $this->Val[$Key]->Pos; + // Sort by key to respect spec + if (!empty($this->Val[$Key]->Val)) { + ksort($this->Val[$Key]->Val); + } + + } else { + die('Invalid torrent file'); + } + } + + function encode($Val) { + if (is_int($Val)) { // Integer + return 'i'.$Val.'e'; + } elseif (is_string($Val)) { + return strlen($Val).':'.$Val; + } elseif (is_object($Val)) { + return $Val->enc(); + } else { + return 'fail'; + } + } } class BENCODE_LIST extends BENCODE2 { - function enc() { - if (empty($this->Val)) { - return 'le'; - } - $Str = 'l'; - reset($this->Val); - foreach ($this->Val as $Value) { - $Str.=$this->encode($Value); - } - return $Str.'e'; - } - - // Decode a list - function dec() { - $Key = 0; // Array index - $Length = strlen($this->Str); - while ($this->Pos < $Length) { - $Type = $this->Str[$this->Pos]; - // $Type now indicates what type of element we're dealing with - // It's either an integer (string), 'i' (an integer), 'l' (a list), 'd' (a dictionary), or 'e' (end of dictionary/list) - - if ($Type == 'e') { // End of list - $this->Pos += 1; - unset($this->Str); // Since we're finished parsing the string, we don't need to store it anymore. Benchmarked - this makes the parser run way faster. - return; - } - - // Decode the bencoded element. - // This function changes $this->Pos and $this->Val, so you don't have to. - $this->decode($Type, $Key); - ++$Key; - } - return true; - } + function enc() { + if (empty($this->Val)) { + return 'le'; + } + $Str = 'l'; + reset($this->Val); + foreach ($this->Val as $Value) { + $Str.=$this->encode($Value); + } + return $Str.'e'; + } + + // Decode a list + function dec() { + $Key = 0; // Array index + $Length = strlen($this->Str); + while ($this->Pos < $Length) { + $Type = $this->Str[$this->Pos]; + // $Type now indicates what type of element we're dealing with + // It's either an integer (string), 'i' (an integer), 'l' (a list), 'd' (a dictionary), or 'e' (end of dictionary/list) + + if ($Type == 'e') { // End of list + $this->Pos += 1; + unset($this->Str); // Since we're finished parsing the string, we don't need to store it anymore. Benchmarked - this makes the parser run way faster. + return; + } + + // Decode the bencoded element. + // This function changes $this->Pos and $this->Val, so you don't have to. + $this->decode($Type, $Key); + ++$Key; + } + return true; + } } class BENCODE_DICT extends BENCODE2 { - function enc() { - if (empty($this->Val)) { - return 'de'; - } - $Str = 'd'; - reset($this->Val); - foreach ($this->Val as $Key => $Value) { - $Str.=strlen($Key).':'.$Key.$this->encode($Value); - } - return $Str.'e'; - } - - // Decode a dictionary - function dec() { - $Length = strlen($this->Str); - while ($this->Pos<$Length) { - - if ($this->Str[$this->Pos] == 'e') { // End of dictionary - $this->Pos += 1; - unset($this->Str); // Since we're finished parsing the string, we don't need to store it anymore. Benchmarked - this makes the parser run way faster. - return; - } - - // Get the dictionary key - // Length of the key, in bytes - $KeyLen = $this->Str[$this->Pos]; - - // Allow for multi-digit lengths - while ($this->Str[$this->Pos + 1] != ':' && $this->Pos + 1 < $Length) { - $this->Pos++; - $KeyLen.=$this->Str[$this->Pos]; - } - // $this->Pos is now on the last letter of the key length - // Adding 2 brings it past that character and the ':' to the beginning of the string - $this->Pos += 2; - - // Get the name of the key - $Key = substr($this->Str, $this->Pos, $KeyLen); - - // Move the position past the key to the beginning of the element - $this->Pos += $KeyLen; - $Type = $this->Str[$this->Pos]; - // $Type now indicates what type of element we're dealing with - // It's either an integer (string), 'i' (an integer), 'l' (a list), 'd' (a dictionary), or 'e' (end of dictionary/list) - - // Decode the bencoded element. - // This function changes $this->Pos and $this->Val, so you don't have to. - $this->decode($Type, $Key); - - - } - return true; - } + function enc() { + if (empty($this->Val)) { + return 'de'; + } + $Str = 'd'; + reset($this->Val); + foreach ($this->Val as $Key => $Value) { + $Str.=strlen($Key).':'.$Key.$this->encode($Value); + } + return $Str.'e'; + } + + // Decode a dictionary + function dec() { + $Length = strlen($this->Str); + while ($this->Pos<$Length) { + + if ($this->Str[$this->Pos] == 'e') { // End of dictionary + $this->Pos += 1; + unset($this->Str); // Since we're finished parsing the string, we don't need to store it anymore. Benchmarked - this makes the parser run way faster. + return; + } + + // Get the dictionary key + // Length of the key, in bytes + $KeyLen = $this->Str[$this->Pos]; + + // Allow for multi-digit lengths + while ($this->Str[$this->Pos + 1] != ':' && $this->Pos + 1 < $Length) { + $this->Pos++; + $KeyLen.=$this->Str[$this->Pos]; + } + // $this->Pos is now on the last letter of the key length + // Adding 2 brings it past that character and the ':' to the beginning of the string + $this->Pos += 2; + + // Get the name of the key + $Key = substr($this->Str, $this->Pos, $KeyLen); + + // Move the position past the key to the beginning of the element + $this->Pos += $KeyLen; + $Type = $this->Str[$this->Pos]; + // $Type now indicates what type of element we're dealing with + // It's either an integer (string), 'i' (an integer), 'l' (a list), 'd' (a dictionary), or 'e' (end of dictionary/list) + + // Decode the bencoded element. + // This function changes $this->Pos and $this->Val, so you don't have to. + $this->decode($Type, $Key); + + + } + return true; + } } class TORRENT extends BENCODE_DICT { - function dump() { - // Convenience function used for testing and figuring out how we store the data - print_r($this->Val); - } - - function dump_data() { - // Function which serializes $this->Val for storage - return base64_encode(serialize($this->Val)); - } - - /* - To use this, please remove the announce-list unset in make_private and be sure to still set_announce_url for backwards compatibility - function set_multi_announce() { - $Trackers = func_get_args(); - $AnnounceList = new BENCODE_LIST(array(),true); - foreach ($Trackers as $Tracker) { - $SubList = new BENCODE_LIST(array($Tracker),true); - unset($SubList->Str); - $AnnounceList->Val[] = $SubList; - } - $this->Val['announce-list'] = $AnnounceList; - } - */ - - function set_announce_url($Announce) { - $this->Val['announce'] = $Announce; - ksort($this->Val); - } - - // Returns an array of: - // * the files in the torrent - // * the total size of files described therein - function file_list() { - $FileList = array(); - if (!isset($this->Val['info']->Val['files'])) { // Single file mode - $TotalSize = $this->Val['info']->Val['length']; - $FileList[] = array($TotalSize, $this->get_name()); - } else { // Multiple file mode - $FileNames = array(); - $FileSizes = array(); - $TotalSize = 0; - $Files = $this->Val['info']->Val['files']->Val; - if (isset($Files[0]->Val['path.utf-8'])) { - $PathKey = 'path.utf-8'; - } else { - $PathKey = 'path'; - } - foreach ($Files as $File) { - $FileSize = $File->Val['length']; - $TotalSize += $FileSize; - - $FileName = ltrim(implode('/', $File->Val[$PathKey]->Val), '/'); - $FileSizes[] = $FileSize; - $FileNames[] = $FileName; - } - natcasesort($FileNames); - foreach ($FileNames as $Index => $FileName) { - $FileList[] = array($FileSizes[$Index], $FileName); - } - } - return array($TotalSize, $FileList); - } - - function get_name() { - if (isset($this->Val['info']->Val['name.utf-8'])) { - return $this->Val['info']->Val['name.utf-8']; - } else { - return $this->Val['info']->Val['name']; - } - } - - function make_private() { - //----- The following properties do not affect the infohash: - - // anounce-list is an unofficial extension to the protocol - // that allows for multiple trackers per torrent - unset($this->Val['announce-list']); - - // Bitcomet & Azureus cache peers in here - unset($this->Val['nodes']); - - // Azureus stores the dht_backup_enable flag here - unset($this->Val['azureus_properties']); - - // Remove web-seeds - unset($this->Val['url-list']); - - // Remove libtorrent resume info - unset($this->Val['libtorrent_resume']); - - //----- End properties that do not affect the infohash - if ($this->Val['info']->Val['private']) { - return true; // Torrent is private - } else { - // Torrent is not private! - // add private tracker flag and sort info dictionary - $this->Val['info']->Val['private'] = 1; - ksort($this->Val['info']->Val); - return false; - } - } + function dump() { + // Convenience function used for testing and figuring out how we store the data + print_r($this->Val); + } + + function dump_data() { + // Function which serializes $this->Val for storage + return base64_encode(serialize($this->Val)); + } + + /* + To use this, please remove the announce-list unset in make_private and be sure to still set_announce_url for backwards compatibility + function set_multi_announce() { + $Trackers = func_get_args(); + $AnnounceList = new BENCODE_LIST([],true); + foreach ($Trackers as $Tracker) { + $SubList = new BENCODE_LIST(array($Tracker),true); + unset($SubList->Str); + $AnnounceList->Val[] = $SubList; + } + $this->Val['announce-list'] = $AnnounceList; + } + */ + + function set_announce_url($Announce) { + $this->Val['announce'] = $Announce; + ksort($this->Val); + } + + // Returns an array of: + // * the files in the torrent + // * the total size of files described therein + function file_list() { + $FileList = []; + if (!isset($this->Val['info']->Val['files'])) { // Single file mode + $TotalSize = $this->Val['info']->Val['length']; + $FileList[] = array($TotalSize, $this->get_name()); + } else { // Multiple file mode + $FileNames = []; + $FileSizes = []; + $TotalSize = 0; + $Files = $this->Val['info']->Val['files']->Val; + if (isset($Files[0]->Val['path.utf-8'])) { + $PathKey = 'path.utf-8'; + } else { + $PathKey = 'path'; + } + foreach ($Files as $File) { + $FileSize = $File->Val['length']; + $TotalSize += $FileSize; + + $FileName = ltrim(implode('/', $File->Val[$PathKey]->Val), '/'); + $FileSizes[] = $FileSize; + $FileNames[] = $FileName; + } + natcasesort($FileNames); + foreach ($FileNames as $Index => $FileName) { + $FileList[] = array($FileSizes[$Index], $FileName); + } + } + return array($TotalSize, $FileList); + } + + function get_name() { + if (isset($this->Val['info']->Val['name.utf-8'])) { + return $this->Val['info']->Val['name.utf-8']; + } else { + return $this->Val['info']->Val['name']; + } + } + + function make_private() { + //----- The following properties do not affect the infohash: + + // anounce-list is an unofficial extension to the protocol + // that allows for multiple trackers per torrent + unset($this->Val['announce-list']); + + // Bitcomet & Azureus cache peers in here + unset($this->Val['nodes']); + + // Azureus stores the dht_backup_enable flag here + unset($this->Val['azureus_properties']); + + // Remove web-seeds + unset($this->Val['url-list']); + + // Remove libtorrent resume info + unset($this->Val['libtorrent_resume']); + + //----- End properties that do not affect the infohash + if ($this->Val['info']->Val['private']) { + return true; // Torrent is private + } else { + // Torrent is not private! + // add private tracker flag and sort info dictionary + $this->Val['info']->Val['private'] = 1; + ksort($this->Val['info']->Val); + return false; + } + } } ?> diff --git a/classes/torrent_32bit.class.php b/classes/torrent_32bit.class.php index 2af39f472..4f4457fc0 100644 --- a/classes/torrent_32bit.class.php +++ b/classes/torrent_32bit.class.php @@ -1,4 +1,4 @@ -Str = $Val; - $this->dec(); - } else { - $this->Val = $Val; - } - } - - // Decode an element based on the type - function decode($Type, $Key) { - if (ctype_digit($Type)) { // Element is a string - // Get length of string - $StrLen = $Type; - while ($this->Str[$this->Pos + 1] != ':') { - $this->Pos++; - $StrLen.=$this->Str[$this->Pos]; - } - $this->Val[$Key] = substr($this->Str, $this->Pos + 2, $StrLen); - - $this->Pos += $StrLen; - $this->Pos += 2; - - } elseif ($Type == 'i') { // Element is an int - $this->Pos++; - - // Find end of integer (first occurance of 'e' after position) - $End = strpos($this->Str, 'e', $this->Pos); - - // Get the integer, and mark it as an int (on our version 64 bit box, we cast it to an int) - $this->Val[$Key] = '[*INT*]'.substr($this->Str, $this->Pos, $End-$this->Pos); - $this->Pos = $End + 1; - - } elseif ($Type == 'l') { // Element is a list - $this->Val[$Key] = new BENCODE_LIST(substr($this->Str, $this->Pos)); - $this->Pos += $this->Val[$Key]->Pos; - - } elseif ($Type == 'd') { // Element is a dictionary - $this->Val[$Key] = new BENCODE_DICT(substr($this->Str, $this->Pos)); - $this->Pos += $this->Val[$Key]->Pos; - // Sort by key to respect spec - ksort($this->Val[$Key]->Val); - - } else { - die('Invalid torrent file'); - } - } - - function encode($Val) { - if (is_string($Val)) { - if (substr($Val, 0, 7) == '[*INT*]') { - return 'i'.substr($Val,7).'e'; - } else { - return strlen($Val).':'.$Val; - } - } elseif (is_object($Val)) { - return $Val->enc(); - } else { - return 'fail'; - } - } + var $Val; // Decoded array + var $Pos = 1; // Pointer that indicates our position in the string + var $Str = ''; // Torrent string + + function __construct($Val, $IsParsed = false) { + if (!$IsParsed) { + $this->Str = $Val; + $this->dec(); + } else { + $this->Val = $Val; + } + } + + // Decode an element based on the type + function decode($Type, $Key) { + if (ctype_digit($Type)) { // Element is a string + // Get length of string + $StrLen = $Type; + while ($this->Str[$this->Pos + 1] != ':') { + $this->Pos++; + $StrLen.=$this->Str[$this->Pos]; + } + $this->Val[$Key] = substr($this->Str, $this->Pos + 2, $StrLen); + + $this->Pos += $StrLen; + $this->Pos += 2; + + } elseif ($Type == 'i') { // Element is an int + $this->Pos++; + + // Find end of integer (first occurance of 'e' after position) + $End = strpos($this->Str, 'e', $this->Pos); + + // Get the integer, and mark it as an int (on our version 64 bit box, we cast it to an int) + $this->Val[$Key] = '[*INT*]'.substr($this->Str, $this->Pos, $End-$this->Pos); + $this->Pos = $End + 1; + + } elseif ($Type == 'l') { // Element is a list + $this->Val[$Key] = new BENCODE_LIST(substr($this->Str, $this->Pos)); + $this->Pos += $this->Val[$Key]->Pos; + + } elseif ($Type == 'd') { // Element is a dictionary + $this->Val[$Key] = new BENCODE_DICT(substr($this->Str, $this->Pos)); + $this->Pos += $this->Val[$Key]->Pos; + // Sort by key to respect spec + ksort($this->Val[$Key]->Val); + + } else { + die('Invalid torrent file'); + } + } + + function encode($Val) { + if (is_string($Val)) { + if (substr($Val, 0, 7) == '[*INT*]') { + return 'i'.substr($Val,7).'e'; + } else { + return strlen($Val).':'.$Val; + } + } elseif (is_object($Val)) { + return $Val->enc(); + } else { + return 'fail'; + } + } } class BENCODE_LIST extends BENCODE2 { - function enc() { - $Str = 'l'; - reset($this->Val); - while (list($Key, $Value) = each($this->Val)) { - $Str.=$this->encode($Value); - } - return $Str.'e'; - } - - // Decode a list - function dec() { - $Key = 0; // Array index - $Length = strlen($this->Str); - while ($this->Pos<$Length) { - $Type = $this->Str[$this->Pos]; - // $Type now indicates what type of element we're dealing with - // It's either an integer (string), 'i' (an integer), 'l' (a list), 'd' (a dictionary), or 'e' (end of dictionary/list) - - if ($Type == 'e') { // End of list - $this->Pos += 1; - unset($this->Str); // Since we're finished parsing the string, we don't need to store it anymore. Benchmarked - this makes the parser run way faster. - return; - } - - // Decode the bencoded element. - // This function changes $this->Pos and $this->Val, so you don't have to. - $this->decode($Type, $Key); - ++ $Key; - } - return true; - } + function enc() { + $Str = 'l'; + reset($this->Val); + while (list($Key, $Value) = each($this->Val)) { + $Str.=$this->encode($Value); + } + return $Str.'e'; + } + + // Decode a list + function dec() { + $Key = 0; // Array index + $Length = strlen($this->Str); + while ($this->Pos<$Length) { + $Type = $this->Str[$this->Pos]; + // $Type now indicates what type of element we're dealing with + // It's either an integer (string), 'i' (an integer), 'l' (a list), 'd' (a dictionary), or 'e' (end of dictionary/list) + + if ($Type == 'e') { // End of list + $this->Pos += 1; + unset($this->Str); // Since we're finished parsing the string, we don't need to store it anymore. Benchmarked - this makes the parser run way faster. + return; + } + + // Decode the bencoded element. + // This function changes $this->Pos and $this->Val, so you don't have to. + $this->decode($Type, $Key); + ++ $Key; + } + return true; + } } class BENCODE_DICT extends BENCODE2 { - function enc() { - $Str = 'd'; - reset($this->Val); - while (list($Key, $Value) = each($this->Val)) { - $Str.=strlen($Key).':'.$Key.$this->encode($Value); - } - return $Str.'e'; - } - - // Decode a dictionary - function dec() { - $Length = strlen($this->Str); - while ($this->Pos < $Length) { - - if ($this->Str[$this->Pos] == 'e') { // End of dictionary - $this->Pos += 1; - unset($this->Str); // Since we're finished parsing the string, we don't need to store it anymore. Benchmarked - this makes the parser run way faster. - return; - } - - // Get the dictionary key - // Length of the key, in bytes - $KeyLen = $this->Str[$this->Pos]; - - // Allow for multi-digit lengths - while ($this->Str[$this->Pos + 1] != ':' && $this->Pos + 1 < $Length) { - $this->Pos++; - $KeyLen.=$this->Str[$this->Pos]; - } - // $this->Pos is now on the last letter of the key length - // Adding 2 brings it past that character and the ':' to the beginning of the string - $this->Pos+=2; - - // Get the name of the key - $Key = substr($this->Str, $this->Pos, $KeyLen); - - // Move the position past the key to the beginning of the element - $this->Pos += $KeyLen; - $Type = $this->Str[$this->Pos]; - // $Type now indicates what type of element we're dealing with - // It's either an integer (string), 'i' (an integer), 'l' (a list), 'd' (a dictionary), or 'e' (end of dictionary/list) - - // Decode the bencoded element. - // This function changes $this->Pos and $this->Val, so you don't have to. - $this->decode($Type, $Key); - - - } - return true; - } + function enc() { + $Str = 'd'; + reset($this->Val); + while (list($Key, $Value) = each($this->Val)) { + $Str.=strlen($Key).':'.$Key.$this->encode($Value); + } + return $Str.'e'; + } + + // Decode a dictionary + function dec() { + $Length = strlen($this->Str); + while ($this->Pos < $Length) { + + if ($this->Str[$this->Pos] == 'e') { // End of dictionary + $this->Pos += 1; + unset($this->Str); // Since we're finished parsing the string, we don't need to store it anymore. Benchmarked - this makes the parser run way faster. + return; + } + + // Get the dictionary key + // Length of the key, in bytes + $KeyLen = $this->Str[$this->Pos]; + + // Allow for multi-digit lengths + while ($this->Str[$this->Pos + 1] != ':' && $this->Pos + 1 < $Length) { + $this->Pos++; + $KeyLen.=$this->Str[$this->Pos]; + } + // $this->Pos is now on the last letter of the key length + // Adding 2 brings it past that character and the ':' to the beginning of the string + $this->Pos+=2; + + // Get the name of the key + $Key = substr($this->Str, $this->Pos, $KeyLen); + + // Move the position past the key to the beginning of the element + $this->Pos += $KeyLen; + $Type = $this->Str[$this->Pos]; + // $Type now indicates what type of element we're dealing with + // It's either an integer (string), 'i' (an integer), 'l' (a list), 'd' (a dictionary), or 'e' (end of dictionary/list) + + // Decode the bencoded element. + // This function changes $this->Pos and $this->Val, so you don't have to. + $this->decode($Type, $Key); + + + } + return true; + } } class TORRENT extends BENCODE_DICT { - function dump() { - // Convenience function used for testing and figuring out how we store the data - print_r($this->Val); - } - - function dump_data() { - // Function which serializes $this->Val for storage - return base64_encode(serialize($this->Val)); - } - - function set_announce_url($Announce) { - $this->Val['announce'] = $Announce; - ksort($this->Val); - } - - // Returns an array of: - // * the files in the torrent - // * the total size of files described therein - function file_list() { - $FileList = array(); - if (!isset($this->Val['info']->Val['files'])) { // Single file mode - $TotalSize = substr($this->Val['info']->Val['length'],7); - $FileList[] = array($TotalSize, $this->get_name()); - } else { // Multiple file mode - $FileNames = array(); - $FileSizes = array(); - $TotalSize = 0; - $Files = $this->Val['info']->Val['files']->Val; - if (isset($Files[0]->Val['path.utf-8'])) { - $PathKey = 'path.utf-8'; - } else { - $PathKey = 'path'; - } - foreach ($Files as $File) { - $FileSize = substr($File->Val['length'], 7); - $TotalSize += $FileSize; - - $FileName = ltrim(implode('/', $File->Val[$PathKey]->Val), '/'); - $FileSizes[] = $FileSize; - $FileNames[] = $FileName; - } - natcasesort($FileNames); - foreach ($FileNames as $Index => $FileName) { - $FileList[] = array($FileSizes[$Index], $FileName); - } - } - return array($TotalSize, $FileList); - } - - function get_name() { - if (isset($this->Val['info']->Val['name.utf-8'])) { - return $this->Val['info']->Val['name.utf-8']; - } else { - return $this->Val['info']->Val['name']; - } - } - - function make_private() { - //----- The following properties do not affect the infohash: - - // anounce-list is an unofficial extension to the protocol - // that allows for multiple trackers per torrent - unset($this->Val['announce-list']); - - // Bitcomet & Azureus cache peers in here - unset($this->Val['nodes']); - - // Azureus stores the dht_backup_enable flag here - unset($this->Val['azureus_properties']); - - // Remove web-seeds - unset($this->Val['url-list']); - - // Remove libtorrent resume info - unset($this->Val['libtorrent_resume']); - - //----- End properties that do not affect the infohash - - if (!empty($this->Val['info']->Val['private']) && $this->Val['info']->Val['private'] == '[*INT*]1') { - return true; - } else { - // Torrent is not private! - // add private tracker flag and sort info dictionary - $this->Val['info']->Val['private'] = '[*INT*]1'; - ksort($this->Val['info']->Val); - return false; - } - } + function dump() { + // Convenience function used for testing and figuring out how we store the data + print_r($this->Val); + } + + function dump_data() { + // Function which serializes $this->Val for storage + return base64_encode(serialize($this->Val)); + } + + function set_announce_url($Announce) { + $this->Val['announce'] = $Announce; + ksort($this->Val); + } + + // Returns an array of: + // * the files in the torrent + // * the total size of files described therein + function file_list() { + $FileList = []; + if (!isset($this->Val['info']->Val['files'])) { // Single file mode + $TotalSize = substr($this->Val['info']->Val['length'],7); + $FileList[] = array($TotalSize, $this->get_name()); + } else { // Multiple file mode + $FileNames = []; + $FileSizes = []; + $TotalSize = 0; + $Files = $this->Val['info']->Val['files']->Val; + if (isset($Files[0]->Val['path.utf-8'])) { + $PathKey = 'path.utf-8'; + } else { + $PathKey = 'path'; + } + foreach ($Files as $File) { + $FileSize = substr($File->Val['length'], 7); + $TotalSize += $FileSize; + + $FileName = ltrim(implode('/', $File->Val[$PathKey]->Val), '/'); + $FileSizes[] = $FileSize; + $FileNames[] = $FileName; + } + natcasesort($FileNames); + foreach ($FileNames as $Index => $FileName) { + $FileList[] = array($FileSizes[$Index], $FileName); + } + } + return array($TotalSize, $FileList); + } + + function get_name() { + if (isset($this->Val['info']->Val['name.utf-8'])) { + return $this->Val['info']->Val['name.utf-8']; + } else { + return $this->Val['info']->Val['name']; + } + } + + function make_private() { + //----- The following properties do not affect the infohash: + + // anounce-list is an unofficial extension to the protocol + // that allows for multiple trackers per torrent + unset($this->Val['announce-list']); + + // Bitcomet & Azureus cache peers in here + unset($this->Val['nodes']); + + // Azureus stores the dht_backup_enable flag here + unset($this->Val['azureus_properties']); + + // Remove web-seeds + unset($this->Val['url-list']); + + // Remove libtorrent resume info + unset($this->Val['libtorrent_resume']); + + //----- End properties that do not affect the infohash + + if (!empty($this->Val['info']->Val['private']) && $this->Val['info']->Val['private'] == '[*INT*]1') { + return true; + } else { + // Torrent is not private! + // add private tracker flag and sort info dictionary + $this->Val['info']->Val['private'] = '[*INT*]1'; + ksort($this->Val['info']->Val); + return false; + } + } } ?> diff --git a/classes/torrent_form.class.php b/classes/torrent_form.class.php index 4cc8a046a..f610663df 100644 --- a/classes/torrent_form.class.php +++ b/classes/torrent_form.class.php @@ -4,769 +4,770 @@ ************ Torrent form class *************** upload.php and torrents.php **** ******************************************************************************** ** This class is used to create both the upload form, and the 'edit torrent' ** - ** form. It is broken down into several functions - head(), foot(), ** - ** music_form() [music], audiobook_form() [Audiobooks and comedy], and ** - ** simple_form() [everything else]. ** - ** ** + ** form. It is broken down into several functions - head(), foot(), ** + ** music_form() [music], audiobook_form() [Audiobooks and comedy], and ** + ** simple_form() [everything else]. ** + ** ** ** When it is called from the edit page, the forms are shortened quite a bit. ** - ** ** + ** ** ********************************************************************************/ use OrpheusNET\Logchecker\Logchecker; class TORRENT_FORM { - var $UploadForm = ''; - var $Categories = array(); - var $Formats = array(); - var $Bitrates = array(); - var $Media = array(); - var $NewTorrent = false; - var $Torrent = array(); - var $Error = false; - var $TorrentID = false; - var $Disabled = ''; - var $DisabledFlag = false; - - const TORRENT_INPUT_ACCEPT = ['application/x-bittorrent', '.torrent']; - const JSON_INPUT_ACCEPT = ['application/json', '.json']; - - function __construct($Torrent = false, $Error = false, $NewTorrent = true) { - - $this->NewTorrent = $NewTorrent; - $this->Torrent = $Torrent; - $this->Error = $Error; - - global $UploadForm, $Categories, $Formats, $Bitrates, $Media, $TorrentID; - - $this->UploadForm = $UploadForm; - $this->Categories = $Categories; - $this->Formats = $Formats; - $this->Bitrates = $Bitrates; - $this->Media = $Media; - $this->TorrentID = $TorrentID; - - if ($this->Torrent && $this->Torrent['GroupID']) { - $this->Disabled = ' disabled="disabled"'; - $this->DisabledFlag = true; - } - } - - function head() { - $AnnounceURL = (G::$LoggedUser['HttpsTracker']) ? ANNOUNCE_HTTPS_URL : ANNOUNCE_HTTP_URL; + var $UploadForm = ''; + var $Categories = []; + var $Formats = []; + var $Bitrates = []; + var $Media = []; + var $NewTorrent = false; + var $Torrent = []; + var $Error = false; + var $TorrentID = false; + var $Disabled = ''; + var $DisabledFlag = false; + + const TORRENT_INPUT_ACCEPT = ['application/x-bittorrent', '.torrent']; + const JSON_INPUT_ACCEPT = ['application/json', '.json']; + + function __construct($Torrent = false, $Error = false, $NewTorrent = true) { + + $this->NewTorrent = $NewTorrent; + $this->Torrent = $Torrent; + $this->Error = $Error; + + global $UploadForm, $Categories, $Formats, $Bitrates, $Media, $TorrentID; + + $this->UploadForm = $UploadForm; + $this->Categories = $Categories; + $this->Formats = $Formats; + $this->Bitrates = $Bitrates; + $this->Media = $Media; + $this->TorrentID = $TorrentID; + + if ($this->Torrent && $this->Torrent['GroupID']) { + $this->Disabled = ' disabled="disabled"'; + $this->DisabledFlag = true; + } + } + + function head() { + $AnnounceURL = (G::$LoggedUser['HttpsTracker']) ? ANNOUNCE_HTTPS_URL : ANNOUNCE_HTTP_URL; ?>
      -NewTorrent) { ?> -

      - Your personal announce URL is:
      - -

      -Error) { - echo "\t".'

      '.$this->Error."

      \n"; - } +NewTorrent) { ?> +

      + Your personal announce URL is:
      + +

      +Error) { + echo "\t".'

      '.$this->Error."

      \n"; + } ?> -
      -
      - - -NewTorrent) { ?> - - - -Torrent && $this->Torrent['GroupID']) { + +
      + + +NewTorrent) { ?> + + + +Torrent && $this->Torrent['GroupID']) { ?> - - -Torrent && $this->Torrent['RequestID']) { + + +Torrent && $this->Torrent['RequestID']) { ?> - -Torrent['RequestID'])?>" /> + -
      -NewTorrent) { ?> - - - - - - - - - - - - + +
      Torrent file: - -
      JSON file: - -
      Type: - + + + + + + + + + + + - -
      Torrent file: + +
      JSON file: + +
      Type: + -
      - -
      - +
      + +
      +Torrent; + function foot() { + $Torrent = $this->Torrent; ?> -
      - -NewTorrent) { - if (check_perms('torrents_freeleech')) { + +
      +NewTorrent) { + if (check_perms('torrents_freeleech')) { ?> - - - + + - -"> + + + + + - - - -
      Freeleech -
      Freeleech + - because - + because + -
      -

      Be sure that your torrent is approved by the rules. Not doing this will result in a warning or worse.

      -NewTorrent) { ?> -

      After uploading the torrent, you will have a one hour grace period during which no one other than you can fill requests with this torrent. Make use of this time wisely, and search the list of requests.

      - - NewTorrent) { echo ' value="Upload torrent"'; } else { echo ' value="Edit torrent"';} ?> /> -
      - + + +

      Be sure that your torrent is approved by the rules. Not doing this will result in a warning or worse.

      +NewTorrent) { ?> +

      After uploading the torrent, you will have a one hour grace period during which no one other than you can fill requests with this torrent. Make use of this time wisely, and search the list of requests.

      + + NewTorrent) { echo ' value="Upload torrent"'; } else { echo ' value="Edit torrent"';} ?> /> + + + +
      -get_query_id(); - $Torrent = $this->Torrent; - $IsRemaster = !empty($Torrent['Remastered']); - $UnknownRelease = !$this->NewTorrent && $IsRemaster && !$Torrent['RemasterYear']; - - if ($Torrent['GroupID']) { - G::$DB->query(' - SELECT - ID, - RemasterYear, - RemasterTitle, - RemasterRecordLabel, - RemasterCatalogueNumber - FROM torrents - WHERE GroupID = '.$Torrent['GroupID']." - AND Remastered = '1' - AND RemasterYear != 0 - ORDER BY RemasterYear DESC, - RemasterTitle DESC, - RemasterRecordLabel DESC, - RemasterCatalogueNumber DESC"); - - if (G::$DB->has_results()) { - $GroupRemasters = G::$DB->to_array(false, MYSQLI_BOTH, false); - } - } - - $HasLog = $Torrent['HasLog']; - $HasCue = $Torrent['HasCue']; - $BadTags = $Torrent['BadTags']; - $BadFolders = $Torrent['BadFolders']; - $BadFiles = $Torrent['BadFiles']; - $MissingLineage = $Torrent['MissingLineage']; - $CassetteApproved = $Torrent['CassetteApproved']; - $LossymasterApproved = $Torrent['LossymasterApproved']; - $LossywebApproved = $Torrent['LossywebApproved']; - global $ReleaseTypes; +get_query_id(); + $Torrent = $this->Torrent; + $IsRemaster = !empty($Torrent['Remastered']); + $UnknownRelease = !$this->NewTorrent && $IsRemaster && !$Torrent['RemasterYear']; + + if ($Torrent['GroupID']) { + G::$DB->query(' + SELECT + ID, + RemasterYear, + RemasterTitle, + RemasterRecordLabel, + RemasterCatalogueNumber + FROM torrents + WHERE GroupID = '.$Torrent['GroupID']." + AND Remastered = '1' + AND RemasterYear != 0 + ORDER BY RemasterYear DESC, + RemasterTitle DESC, + RemasterRecordLabel DESC, + RemasterCatalogueNumber DESC"); + + if (G::$DB->has_results()) { + $GroupRemasters = G::$DB->to_array(false, MYSQLI_BOTH, false); + } + } + + $HasLog = $Torrent['HasLog']; + $HasCue = $Torrent['HasCue']; + $BadTags = $Torrent['BadTags']; + $BadFolders = $Torrent['BadFolders']; + $BadFiles = $Torrent['BadFiles']; + $MissingLineage = $Torrent['MissingLineage']; + $CassetteApproved = $Torrent['CassetteApproved']; + $LossymasterApproved = $Torrent['LossymasterApproved']; + $LossywebApproved = $Torrent['LossywebApproved']; + global $ReleaseTypes; ?> - -NewTorrent) { ?> - - - + +
      Artist(s): - - $Artists) { - foreach ($Artists as $Artist) { + +NewTorrent) { ?> + + + - - - - - - - - - -
      - x -

      - -
      -
      -
      - + Disabled?> /> + + + + + + + + + + + + + + +
      + x +

      + +
      +
      +
      + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - -NewTorrent) { ?> - - - - - + + +NewTorrent) { ?> + + + + + - - - - - - - - + + + + + + + + - - - - + + + + - -NewTorrent) { ?> - - - - - - - - - - -NewTorrent) { + + + + +NewTorrent) { ?> + + + + + + + + + + +NewTorrent) { ?> - - - - - - - - + + + + + + + - -NewTorrent && check_perms('users_mod')) { ?> - - - + +NewTorrent && check_perms('users_mod')) { ?> + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -NewTorrent) { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +NewTorrent) { ?> - - - - - - - - - - - + + + + + + + + + + - - - - - + + + + + - -
      Artist(s): + + $Artists) { + foreach ($Artists as $Artist) { ?> - Disabled?> /> - -DisabledFlag) { + Disabled?> /> + +DisabledFlag) { ?> - + -+ + -
      - + - Disabled?> /> - - + - -
      Album title: - Disabled?> /> -

      Do not include the words remaster, re-issue, MFSL Gold, limited edition, bonus tracks, bonus disc or country-specific information in this field. That belongs in the edition information fields below; see this for further information. Also remember to use the correct capitalization for your upload. See the Capitalization Guidelines for more information.

      -
      MusicBrainz:
      Album title: + Disabled?> /> +

      Do not include the words remaster, re-issue, MFSL Gold, limited edition, bonus tracks, bonus disc or country-specific information in this field. That belongs in the edition information fields below; see this for further information. Also remember to use the correct capitalization for your upload. See the Capitalization Guidelines for more information.

      +
      MusicBrainz:
      - - - - - Disabled?> onblur="CheckYear();" /> This is the year of the original release. -
      Record label (optional):Disabled?> />
      Catalogue number (optional): - Disabled?> /> -
      - Please double-check the record label and catalogue number when using MusicBrainz. See this guide for more details. -
      - Release type: - -
      + + + + + Disabled?> onblur="CheckYear();" /> This is the year of the original release. +
      Record label (optional):Disabled?> />
      Catalogue number (optional): + Disabled?> /> +
      + Please double-check the record label and catalogue number when using MusicBrainz. See this guide for more details. +
      + Release type: + + Please take the time to fill this out properly. Need help? Try reading this wiki article or searching MusicBrainz. -

      Edit

      Edition information: - onclick="Remaster();NewTorrent) { ?> CheckYear();" /> - -

      Edit

      Edition information: + onclick="Remaster();NewTorrent) { ?> CheckYear();" /> + + -
      Scene: - /> - -
      Format: - +
      + + + + + + + + + + + + + + + + + + + + +
      Year (required): + /> +
      Title: + /> +

      Title of the release (e.g. "Deluxe Edition" or "Remastered").

      +
      Record label: + /> +

      This is for the record label of the release. It may differ from the original.

      +
      Catalogue number: /> +

      This is for the catalogue number of the release.

      +
      + +
      Scene: + /> + +
      Format: + - -
      Bitrate: - + +
      Bitrate: + - -
      Multi-format uploader:
      Multi-format uploader:
      Vanity House: - -
      Media: -
      Vanity House: + +
      Media: + -
      Log/cue: - />
      - />
      - +
      Log/cue: + />
      + />
      +NewTorrent && check_perms('users_mod')) { + if (!$this->NewTorrent && check_perms('users_mod')) { ?> -
      Upload Log: - Check your log files before uploading here. For multi-disc releases, click the "+" button to add multiple log files.
      - + -
      Bad tags: />
      Bad folder names: />
      Bad file names: />
      Missing lineage: />
      Cassette approved: />
      Lossy master approved: />
      Lossy web approved: />
      Upload Log: + Check your log files before uploading here. For multi-disc releases, click the "+" button to add multiple log files.
      + + +
      Bad tags: />
      Bad folder names: />
      Bad file names: />
      Missing lineage: />
      Cassette approved: />
      Lossy master approved: />
      Lossy web approved: />
      Tags: - - - - Disabled?> /> -
      - -
      Image (optional):Disabled?> />
      Album description: +
      Tags: + + + + Disabled?> /> +
      + +
      Image (optional):Disabled?> />
      Album description: Disabled)); ?> -

      Contains background information such as album history and maybe a review.

      -
      Release description (optional): +

      Contains background information such as album history and maybe a review.

      +
      Release description (optional): -

      Contains information like encoder settings or details of the ripping process. Do not paste the ripping log here.

      -
      -Contains information like encoder settings or details of the ripping process. Do not paste the ripping log here.

      +
      +set_query_id($QueryID); - }//function music_form + G::$DB->set_query_id($QueryID); + }//function music_form - function audiobook_form() { - $Torrent = $this->Torrent; + function audiobook_form() { + $Torrent = $this->Torrent; ?> - -NewTorrent) { ?> - - - - - - - - - - - - + +
      Author - Title: - -

      Should only include the author if applicable.

      -
      Year:
      Format: - +NewTorrent) { ?> + + + + + + + + + + + + - - - - + + + + - -NewTorrent) { ?> - - - - - - - - - - - + +NewTorrent) { ?> + + + + + + + + + + + - - - - - + + + + + - -
      Author - Title: + +

      Should only include the author if applicable.

      +
      Year:
      Format: + -
      Bitrate: - +
      Bitrate: + - -
      Tags: - /> -
      Image (optional):Disabled?> />
      Description: + + +
      Tags: + /> +
      Image (optional):Disabled?> />
      Description: -

      Contains information like the track listing, a review, a link to Discogs or MusicBrainz, etc.

      -
      Release description (optional): +

      Contains information like the track listing, a review, a link to Discogs or MusicBrainz, etc.

      +
      Release description (optional): -

      Contains information like encoder settings. For analog rips, this frequently contains lineage information.

      -
      -Torrent; -?> - -NewTorrent) { - if ($this->Categories[$CategoryID] == 'E-Books') { ?> - - - - - - - - - - - - - - - - - + +
      Author - Title:Title:
      Tags: />
      Image (optional):Disabled?> />
      Description: +

      Contains information like encoder settings. For analog rips, this frequently contains lineage information.

      +
      +Torrent; +?> + +NewTorrent) { + if ($this->Categories[$CategoryID] == 'E-Books') { ?> + + + + + + + + + + + + + + + + + - - -
      Author - Title:Title:
      Tags: />
      Image (optional):Disabled?> />
      Description: -
      - +
      + diff --git a/classes/torrents.class.php b/classes/torrents.class.php index e7c8ea038..6c1378fdb 100644 --- a/classes/torrents.class.php +++ b/classes/torrents.class.php @@ -1,1210 +1,1217 @@ - ( - * ID - * Name - * Year - * RecordLabel - * CatalogueNumber - * TagList - * ReleaseType - * VanityHouse - * WikiImage - * CategoryID - * Torrents => { - * ID => { - * GroupID, Media, Format, Encoding, RemasterYear, Remastered, - * RemasterTitle, RemasterRecordLabel, RemasterCatalogueNumber, Scene, - * HasLog, HasCue, LogScore, FileCount, FreeTorrent, Size, Leechers, - * Seeders, Snatched, Time, HasFile, PersonalFL, IsSnatched - * } - * } - * Artists => { - * { - * id, name, aliasid // Only main artists - * } - * } - * ExtendedArtists => { - * [1-6] => { // See documentation on Artists::get_artists - * id, name, aliasid - * } - * } - * Flags => { - * IsSnatched - * } - */ - public static function get_groups($GroupIDs, $Return = true, $GetArtists = true, $Torrents = true) { - $Found = $NotFound = array_fill_keys($GroupIDs, false); - $Key = $Torrents ? 'torrent_group_' : 'torrent_group_light_'; - - foreach ($GroupIDs as $i => $GroupID) { - if (!is_number($GroupID)) { - unset($GroupIDs[$i], $Found[$GroupID], $NotFound[$GroupID]); - continue; - } - $Data = G::$Cache->get_value($Key . $GroupID, true); - if (!empty($Data) && is_array($Data) && $Data['ver'] == CACHE::GROUP_VERSION) { - unset($NotFound[$GroupID]); - $Found[$GroupID] = $Data['d']; - } - } - // Make sure there's something in $GroupIDs, otherwise the SQL will break - if (count($GroupIDs) === 0) { - return array(); - } - - /* - Changing any of these attributes returned will cause very large, very dramatic site-wide chaos. - Do not change what is returned or the order thereof without updating: - torrents, artists, collages, bookmarks, better, the front page, - and anywhere else the get_groups function is used. - Update self::array_group(), too - */ - - if (count($NotFound) > 0) { - $IDs = implode(',', array_keys($NotFound)); - $NotFound = array(); - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - ID, Name, Year, RecordLabel, CatalogueNumber, TagList, ReleaseType, VanityHouse, WikiImage, CategoryID - FROM torrents_group - WHERE ID IN ($IDs)"); - - while ($Group = G::$DB->next_record(MYSQLI_ASSOC, true)) { - $NotFound[$Group['ID']] = $Group; - $NotFound[$Group['ID']]['Torrents'] = array(); - $NotFound[$Group['ID']]['Artists'] = array(); - } - G::$DB->set_query_id($QueryID); - - if ($Torrents) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - t.ID, - t.GroupID, - t.Media, - t.Format, - t.Encoding, - t.RemasterYear, - t.Remastered, - t.RemasterTitle, - t.RemasterRecordLabel, - t.RemasterCatalogueNumber, - t.Scene, - t.HasLog, - t.HasCue, - t.LogScore, - t.FileCount, - t.FreeTorrent, - t.Size, - t.Leechers, - t.Seeders, - t.Snatched, - t.Time, - t.ID AS HasFile, - t.HasLogDB, - t.LogChecksum, - tbt.TorrentID AS BadTags, - tbf.TorrentID AS BadFolders, - tfi.TorrentID AS BadFiles, - ml.TorrentID AS MissingLineage, - ca.TorrentID AS CassetteApproved, - lma.TorrentID AS LossymasterApproved, - lwa.TorrentID AS LossywebApproved - FROM torrents t - LEFT JOIN torrents_bad_tags AS tbt ON (tbt.TorrentID = t.ID) - LEFT JOIN torrents_bad_folders AS tbf ON (tbf.TorrentID = t.ID) - LEFT JOIN torrents_bad_files AS tfi ON (tfi.TorrentID = t.ID) - LEFT JOIN torrents_missing_lineage AS ml ON (ml.TorrentID = t.ID) - LEFT JOIN torrents_cassette_approved AS ca ON (ca.TorrentID = t.ID) - LEFT JOIN torrents_lossymaster_approved AS lma ON (lma.TorrentID = t.ID) - LEFT JOIN torrents_lossyweb_approved AS lwa ON (lwa.TorrentID = t.ID) - LEFT JOIN torrents_logs AS tl ON (tl.TorrentID = t.ID) - WHERE t.GroupID IN ($IDs) - ORDER BY t.GroupID, t.Remastered, (t.RemasterYear != 0) DESC, t.RemasterYear, t.RemasterTitle, - t.RemasterRecordLabel, t.RemasterCatalogueNumber, t.Media, t.Format, t.Encoding, t.ID"); - while ($Torrent = G::$DB->next_record(MYSQLI_ASSOC, true)) { - $NotFound[$Torrent['GroupID']]['Torrents'][$Torrent['ID']] = $Torrent; - } - G::$DB->set_query_id($QueryID); - } - - foreach ($NotFound as $GroupID => $GroupInfo) { - G::$Cache->cache_value($Key . $GroupID, array('ver' => CACHE::GROUP_VERSION, 'd' => $GroupInfo), 0); - } - - $Found = $NotFound + $Found; - } - - // Filter out orphans (elements that are == false) - $Found = array_filter($Found); - - if ($GetArtists) { - $Artists = Artists::get_artists($GroupIDs); - } else { - $Artists = array(); - } - - if ($Return) { // If we're interested in the data, and not just caching it - foreach ($Artists as $GroupID => $Data) { - if (!isset($Found[$GroupID])) { - continue; - } - if (array_key_exists(1, $Data) || array_key_exists(4, $Data) || array_key_exists(6, $Data)) { - $Found[$GroupID]['Artists'] = isset($Data[1]) ? $Data[1] : null; // Only use main artists (legacy) - // TODO: find a better solution than this crap / rewrite the artist system - for ($i = 1; $i <= 7; $i++) { - $Found[$GroupID]['ExtendedArtists'][$i] = isset($Data[$i]) ? $Data[$i] : null; - } - } - else { - $Found[$GroupID]['ExtendedArtists'] = false; - } - } - // Fetch all user specific torrent properties - if ($Torrents) { - foreach ($Found as &$Group) { - $Group['Flags'] = array('IsSnatched' => false); - if (!empty($Group['Torrents'])) { - foreach ($Group['Torrents'] as &$Torrent) { - self::torrent_properties($Torrent, $Group['Flags']); - } - } - } - } - return $Found; - } - } - - /** - * Returns a reconfigured array from a Torrent Group - * - * Use this with extract() instead of the volatile list($GroupID, ...) - * Then use the variables $GroupID, $GroupName, etc - * - * @example extract(Torrents::array_group($SomeGroup)); - * @param array $Group torrent group - * @return array Re-key'd array - */ - public static function array_group(array &$Group) { - return array( - 'GroupID' => $Group['ID'], - 'GroupName' => $Group['Name'], - 'GroupYear' => $Group['Year'], - 'GroupCategoryID' => $Group['CategoryID'], - 'GroupRecordLabel' => $Group['RecordLabel'], - 'GroupCatalogueNumber' => $Group['CatalogueNumber'], - 'GroupVanityHouse' => $Group['VanityHouse'], - 'GroupFlags' => isset($Group['Flags']) ? $Group['Flags'] : array('IsSnatched' => false), - 'TagList' => $Group['TagList'], - 'ReleaseType' => $Group['ReleaseType'], - 'WikiImage' => $Group['WikiImage'], - 'Torrents' => isset($Group['Torrents']) ? $Group['Torrents'] : array(), - 'Artists' => $Group['Artists'], - 'ExtendedArtists' => $Group['ExtendedArtists'] - ); - } - - /** - * Supplements a torrent array with information that only concerns certain users and therefore cannot be cached - * - * @param array $Torrent torrent array preferably in the form used by Torrents::get_groups() or get_group_info() - * @param int $TorrentID - */ - public static function torrent_properties(&$Torrent, &$Flags) { - $Torrent['PersonalFL'] = empty($Torrent['FreeTorrent']) && self::has_token($Torrent['ID']); - if ($Torrent['IsSnatched'] = self::has_snatched($Torrent['ID'])) { - $Flags['IsSnatched'] = true; - } - } - - - /* - * Write to the group log. - * - * @param int $GroupID - * @param int $TorrentID - * @param int $UserID - * @param string $Message - * @param boolean $Hidden Currently does fuck all. TODO: Fix that. - */ - public static function write_group_log($GroupID, $TorrentID, $UserID, $Message, $Hidden) { - global $Time; - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - INSERT INTO group_log - (GroupID, TorrentID, UserID, Info, Time, Hidden) - VALUES - ($GroupID, $TorrentID, $UserID, '".db_string($Message)."', '".sqltime()."', $Hidden)"); - G::$DB->set_query_id($QueryID); - } - - /** - * Delete a torrent. - * - * @param int $ID The ID of the torrent to delete. - * @param int $GroupID Set it if you have it handy. - * @param string $OcelotReason The deletion reason for ocelot to report to users. - */ - public static function delete_torrent($ID, $GroupID = 0, $OcelotReason = -1) { - $QueryID = G::$DB->get_query_id(); - - G::$DB->prepared_query(' - SELECT GroupID, UserID, info_hash, Format, Media, Encoding, HasLogDB, LogScore, LogChecksum - FROM torrents - WHERE ID = ? - ', $ID - ); - list($GroupID, $UserID, $InfoHash, $Format, $Media, $Encoding, $HasLogDB, $LogScore, $LogChecksum) = G::$DB->next_record(MYSQLI_BOTH, [2, 'info_hash']); - - $Bonus = new \Gazelle\Bonus(G::$DB, G::$Cache); - G::$DB->prepared_query(' - UPDATE users_main - SET BonusPoints = BonusPoints - ? - WHERE id = ? - ', $Bonus->getTorrentValue($Format, $Media, $Encoding, $HasLogDB, $LogScore, $LogChecksum), $UserID - ); - - $manager = new \Gazelle\DB(G::$DB, G::$Cache); - list($ok, $message) = $manager->soft_delete(SQLDB, 'torrents', [['ID', $ID]]); - if (!$ok) { - return $message; - } - Tracker::update_tracker('delete_torrent', array('info_hash' => rawurlencode($InfoHash), 'id' => $ID, 'reason' => $OcelotReason)); - G::$Cache->decrement('stats_torrent_count'); - - G::$DB->prepared_query(' - SELECT COUNT(*) - FROM torrents - WHERE GroupID = ?', $GroupID); - list($Count) = G::$DB->next_record(); - if ($Count > 0) { - Torrents::update_hash($GroupID); - } - - $manager->soft_delete(SQLDB, 'torrents_files', [['TorrentID', $ID]]); - $manager->soft_delete(SQLDB, 'torrents_bad_files', [['TorrentID', $ID]]); - $manager->soft_delete(SQLDB, 'torrents_bad_folders', [['TorrentID', $ID]]); - $manager->soft_delete(SQLDB, 'torrents_bad_tags', [['TorrentID', $ID]]); - $manager->soft_delete(SQLDB, 'torrents_cassette_approved', [['TorrentID', $ID]]); - $manager->soft_delete(SQLDB, 'torrents_lossymaster_approved', [['TorrentID', $ID]]); - $manager->soft_delete(SQLDB, 'torrents_lossyweb_approved', [['TorrentID', $ID]]); - $manager->soft_delete(SQLDB, 'torrents_missing_lineage', [['TorrentID', $ID]]); - - // Tells Sphinx that the group is removed - G::$DB->prepared_query(' - REPLACE INTO sphinx_delta (ID, Time) - VALUES (?, now())', $ID); - - G::$DB->prepared_query(" - UPDATE reportsv2 - SET - Status = 'Resolved', - LastChangeTime = now(), - ModComment = 'Report already dealt with (torrent deleted)' - WHERE Status != 'Resolved' - AND TorrentID = ?", $ID); - $Reports = G::$DB->affected_rows(); - if ($Reports) { - G::$Cache->decrement('num_torrent_reportsv2', $Reports); - } - - $deleted_keys = []; - // Torrent notifications - G::$DB->prepared_query(' - SELECT UserID - FROM users_notify_torrents - WHERE TorrentID = ?', $ID); - while (list($UserID) = G::$DB->next_record()) { - $deleted_keys[] = "notifications_new_$UserID"; - } - $manager->soft_delete(SQLDB, 'users_notify_torrents', [['TorrentID', $ID]]); - - $RecentUploads = G::$Cache->get_value("recent_uploads_$UserID"); - if (is_array($RecentUploads)) { - foreach ($RecentUploads as $Key => $Recent) { - if ($Recent['ID'] == $GroupID) { - $deleted_keys[] = "recent_uploads_$UserID"; - break; - } - } - } - - $deleted_keys[] = "torrent_download_$ID"; - $deleted_keys[] = "torrent_group_$GroupID"; - $deleted_keys[] = "torrents_details_$GroupID"; - G::$Cache->deleteMulti($deleted_keys); - - G::$DB->set_query_id($QueryID); - } - - public static function send_pm($TorrentID, $UploaderID, $Name, $Log, $TrumpID = 0, $PMUploader = false) { - global $DB; - - $Subject = 'Torrent deleted: ' . $Name; - - $MessageStart = 'A torrent '; - if ($TrumpID > 0) { - $MessageEnd = ' has been trumped. You can find the new torrent [url='.site_url().'torrents.php?torrentid='.$TrumpID.']here[/url].'; - } - else { - $MessageEnd = ' has been deleted.'; - } - $MessageEnd .= "\n\n[url=".site_url()."log.php?search=Torrent+{$TorrentID}]Log message[/url]: {$Log}."; - - // Uploader - if ($PMUploader) { - Misc::send_pm($UploaderID, 0, $Subject, $MessageStart.'you uploaded'.$MessageEnd); - } - $PMedUsers = [$UploaderID]; - - // Seeders - $Extra = implode(',', array_fill(0, count($PMedUsers), '?')); - $DB->prepared_query(" + const FILELIST_DELIM = 0xF7; // Hex for ÷ Must be the same as phrase_boundary in sphinx.conf! + const SNATCHED_UPDATE_INTERVAL = 3600; // How often we want to update users' snatch lists + const SNATCHED_UPDATE_AFTERDL = 300; // How long after a torrent download we want to update a user's snatch lists + + /** + * Function to get data and torrents for an array of GroupIDs. Order of keys doesn't matter + * + * @param array $GroupIDs + * @param boolean $Return if false, nothing is returned. For priming cache. + * @param boolean $GetArtists if true, each group will contain the result of + * Artists::get_artists($GroupID), in result[$GroupID]['ExtendedArtists'] + * @param boolean $Torrents if true, each group contains a list of torrents, in result[$GroupID]['Torrents'] + * + * @return array each row of the following format: + * GroupID => ( + * ID + * Name + * Year + * RecordLabel + * CatalogueNumber + * TagList + * ReleaseType + * VanityHouse + * WikiImage + * CategoryID + * Torrents => { + * ID => { + * GroupID, Media, Format, Encoding, RemasterYear, Remastered, + * RemasterTitle, RemasterRecordLabel, RemasterCatalogueNumber, Scene, + * HasLog, HasCue, LogScore, FileCount, FreeTorrent, Size, Leechers, + * Seeders, Snatched, Time, HasFile, PersonalFL, IsSnatched + * } + * } + * Artists => { + * { + * id, name, aliasid // Only main artists + * } + * } + * ExtendedArtists => { + * [1-6] => { // See documentation on Artists::get_artists + * id, name, aliasid + * } + * } + * Flags => { + * IsSnatched + * } + */ + public static function get_groups($GroupIDs, $Return = true, $GetArtists = true, $Torrents = true) { + $Found = $NotFound = array_fill_keys($GroupIDs, false); + $Key = $Torrents ? 'torrent_group_' : 'torrent_group_light_'; + + foreach ($GroupIDs as $i => $GroupID) { + if (!is_number($GroupID)) { + unset($GroupIDs[$i], $Found[$GroupID], $NotFound[$GroupID]); + continue; + } + $Data = G::$Cache->get_value($Key . $GroupID, true); + if (!empty($Data) && is_array($Data) && $Data['ver'] == CACHE::GROUP_VERSION) { + unset($NotFound[$GroupID]); + $Found[$GroupID] = $Data['d']; + } + } + // Make sure there's something in $GroupIDs, otherwise the SQL will break + if (count($GroupIDs) === 0) { + return []; + } + + /* + Changing any of these attributes returned will cause very large, very dramatic site-wide chaos. + Do not change what is returned or the order thereof without updating: + torrents, artists, collages, bookmarks, better, the front page, + and anywhere else the get_groups function is used. + Update self::array_group(), too + */ + + if (count($NotFound) > 0) { + $IDs = implode(',', array_keys($NotFound)); + $NotFound = []; + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + ID, Name, Year, RecordLabel, CatalogueNumber, TagList, ReleaseType, VanityHouse, WikiImage, CategoryID + FROM torrents_group + WHERE ID IN ($IDs)"); + + while ($Group = G::$DB->next_record(MYSQLI_ASSOC, true)) { + $NotFound[$Group['ID']] = $Group; + $NotFound[$Group['ID']]['Torrents'] = []; + $NotFound[$Group['ID']]['Artists'] = []; + } + G::$DB->set_query_id($QueryID); + + if ($Torrents) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + t.ID, + t.GroupID, + t.Media, + t.Format, + t.Encoding, + t.RemasterYear, + t.Remastered, + t.RemasterTitle, + t.RemasterRecordLabel, + t.RemasterCatalogueNumber, + t.Scene, + t.HasLog, + t.HasCue, + t.LogScore, + t.FileCount, + t.FreeTorrent, + t.Size, + tls.Leechers, + tls.Seeders, + tls.Snatched, + t.Time, + t.ID AS HasFile, /* wtf? always true */ + t.HasLogDB, + t.LogChecksum, + tbt.TorrentID AS BadTags, + tbf.TorrentID AS BadFolders, + tfi.TorrentID AS BadFiles, + ml.TorrentID AS MissingLineage, + ca.TorrentID AS CassetteApproved, + lma.TorrentID AS LossymasterApproved, + lwa.TorrentID AS LossywebApproved + FROM torrents t + INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID) + LEFT JOIN torrents_bad_tags AS tbt ON (tbt.TorrentID = t.ID) + LEFT JOIN torrents_bad_folders AS tbf ON (tbf.TorrentID = t.ID) + LEFT JOIN torrents_bad_files AS tfi ON (tfi.TorrentID = t.ID) + LEFT JOIN torrents_missing_lineage AS ml ON (ml.TorrentID = t.ID) + LEFT JOIN torrents_cassette_approved AS ca ON (ca.TorrentID = t.ID) + LEFT JOIN torrents_lossymaster_approved AS lma ON (lma.TorrentID = t.ID) + LEFT JOIN torrents_lossyweb_approved AS lwa ON (lwa.TorrentID = t.ID) + LEFT JOIN torrents_logs AS tl ON (tl.TorrentID = t.ID) + WHERE t.GroupID IN ($IDs) + ORDER BY t.GroupID, t.Remastered, (t.RemasterYear != 0) DESC, t.RemasterYear, t.RemasterTitle, + t.RemasterRecordLabel, t.RemasterCatalogueNumber, t.Media, t.Format, t.Encoding, t.ID"); + while ($Torrent = G::$DB->next_record(MYSQLI_ASSOC, true)) { + $NotFound[$Torrent['GroupID']]['Torrents'][$Torrent['ID']] = $Torrent; + } + G::$DB->set_query_id($QueryID); + } + + foreach ($NotFound as $GroupID => $GroupInfo) { + G::$Cache->cache_value($Key . $GroupID, array('ver' => CACHE::GROUP_VERSION, 'd' => $GroupInfo), 0); + } + + $Found = $NotFound + $Found; + } + + // Filter out orphans (elements that are == false) + $Found = array_filter($Found); + + if ($GetArtists) { + $Artists = Artists::get_artists($GroupIDs); + } else { + $Artists = []; + } + + if ($Return) { // If we're interested in the data, and not just caching it + foreach ($Artists as $GroupID => $Data) { + if (!isset($Found[$GroupID])) { + continue; + } + if (array_key_exists(1, $Data) || array_key_exists(4, $Data) || array_key_exists(6, $Data)) { + $Found[$GroupID]['Artists'] = isset($Data[1]) ? $Data[1] : null; // Only use main artists (legacy) + // TODO: find a better solution than this crap / rewrite the artist system + for ($i = 1; $i <= 7; $i++) { + $Found[$GroupID]['ExtendedArtists'][$i] = isset($Data[$i]) ? $Data[$i] : null; + } + } + else { + $Found[$GroupID]['ExtendedArtists'] = false; + } + } + // Fetch all user specific torrent properties + if ($Torrents) { + foreach ($Found as &$Group) { + $Group['Flags'] = array('IsSnatched' => false); + if (!empty($Group['Torrents'])) { + foreach ($Group['Torrents'] as &$Torrent) { + self::torrent_properties($Torrent, $Group['Flags']); + } + } + } + } + return $Found; + } + } + + /** + * Returns a reconfigured array from a Torrent Group + * + * Use this with extract() instead of the volatile list($GroupID, ...) + * Then use the variables $GroupID, $GroupName, etc + * + * @example extract(Torrents::array_group($SomeGroup)); + * @param array $Group torrent group + * @return array Re-key'd array + */ + public static function array_group(array &$Group) { + return array( + 'GroupID' => $Group['ID'], + 'GroupName' => $Group['Name'], + 'GroupYear' => $Group['Year'], + 'GroupCategoryID' => $Group['CategoryID'], + 'GroupRecordLabel' => $Group['RecordLabel'], + 'GroupCatalogueNumber' => $Group['CatalogueNumber'], + 'GroupVanityHouse' => $Group['VanityHouse'], + 'GroupFlags' => isset($Group['Flags']) ? $Group['Flags'] : array('IsSnatched' => false), + 'TagList' => $Group['TagList'], + 'ReleaseType' => $Group['ReleaseType'], + 'WikiImage' => $Group['WikiImage'], + 'Torrents' => isset($Group['Torrents']) ? $Group['Torrents'] : array(), + 'Artists' => $Group['Artists'], + 'ExtendedArtists' => $Group['ExtendedArtists'] + ); + } + + /** + * Supplements a torrent array with information that only concerns certain users and therefore cannot be cached + * + * @param array $Torrent torrent array preferably in the form used by Torrents::get_groups() or get_group_info() + * @param int $TorrentID + */ + public static function torrent_properties(&$Torrent, &$Flags) { + $Torrent['PersonalFL'] = empty($Torrent['FreeTorrent']) && self::has_token($Torrent['ID']); + if ($Torrent['IsSnatched'] = self::has_snatched($Torrent['ID'])) { + $Flags['IsSnatched'] = true; + } + } + + + /* + * Write to the group log. + * + * @param int $GroupID + * @param int $TorrentID + * @param int $UserID + * @param string $Message + * @param boolean $Hidden Currently does fuck all. TODO: Fix that. + */ + public static function write_group_log($GroupID, $TorrentID, $UserID, $Message, $Hidden) { + global $Time; + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + INSERT INTO group_log + (GroupID, TorrentID, UserID, Info, Time, Hidden) + VALUES + ($GroupID, $TorrentID, $UserID, '".db_string($Message)."', '".sqltime()."', $Hidden)"); + G::$DB->set_query_id($QueryID); + } + + /** + * Delete a torrent. + * + * @param int $ID The ID of the torrent to delete. + * @param int $GroupID Set it if you have it handy. + * @param string $OcelotReason The deletion reason for ocelot to report to users. + */ + public static function delete_torrent($ID, $GroupID = 0, $OcelotReason = -1) { + $QueryID = G::$DB->get_query_id(); + + G::$DB->prepared_query(' + SELECT GroupID, UserID, info_hash, Format, Media, Encoding, HasLogDB, LogScore, LogChecksum + FROM torrents + WHERE ID = ? + ', $ID + ); + list($GroupID, $UserID, $InfoHash, $Format, $Media, $Encoding, $HasLogDB, $LogScore, $LogChecksum) = G::$DB->next_record(MYSQLI_BOTH, [2, 'info_hash']); + + $Bonus = new \Gazelle\Bonus(G::$DB, G::$Cache); + G::$DB->prepared_query(' + UPDATE users_main + SET BonusPoints = BonusPoints - ? + WHERE id = ? + ', $ID <= MAX_PREV_TORRENT_ID ? 0 : $Bonus->getTorrentValue($Format, $Media, $Encoding, $HasLogDB, $LogScore, $LogChecksum), + $UserID + ); + + $manager = new \Gazelle\DB(G::$DB, G::$Cache); + list($ok, $message) = $manager->soft_delete(SQLDB, 'torrents_leech_stats', [['TorrentID', $ID]], false); + if (!$ok) { + return $message; + } + list($ok, $message) = $manager->soft_delete(SQLDB, 'torrents', [['ID', $ID]]); + if (!$ok) { + return $message; + } + Tracker::update_tracker('delete_torrent', array('info_hash' => rawurlencode($InfoHash), 'id' => $ID, 'reason' => $OcelotReason)); + G::$Cache->decrement('stats_torrent_count'); + + G::$DB->prepared_query(' + SELECT COUNT(*) + FROM torrents + WHERE GroupID = ?', $GroupID); + list($Count) = G::$DB->next_record(); + if ($Count > 0) { + Torrents::update_hash($GroupID); + } + + $manager->soft_delete(SQLDB, 'torrents_files', [['TorrentID', $ID]]); + $manager->soft_delete(SQLDB, 'torrents_bad_files', [['TorrentID', $ID]]); + $manager->soft_delete(SQLDB, 'torrents_bad_folders', [['TorrentID', $ID]]); + $manager->soft_delete(SQLDB, 'torrents_bad_tags', [['TorrentID', $ID]]); + $manager->soft_delete(SQLDB, 'torrents_cassette_approved', [['TorrentID', $ID]]); + $manager->soft_delete(SQLDB, 'torrents_lossymaster_approved', [['TorrentID', $ID]]); + $manager->soft_delete(SQLDB, 'torrents_lossyweb_approved', [['TorrentID', $ID]]); + $manager->soft_delete(SQLDB, 'torrents_missing_lineage', [['TorrentID', $ID]]); + + // Tells Sphinx that the group is removed + G::$DB->prepared_query(' + REPLACE INTO sphinx_delta (ID, Time) + VALUES (?, now())', $ID); + + G::$DB->prepared_query(" + UPDATE reportsv2 + SET + Status = 'Resolved', + LastChangeTime = now(), + ModComment = 'Report already dealt with (torrent deleted)' + WHERE Status != 'Resolved' + AND TorrentID = ?", $ID); + $Reports = G::$DB->affected_rows(); + if ($Reports) { + G::$Cache->decrement('num_torrent_reportsv2', $Reports); + } + + $deleted_keys = []; + // Torrent notifications + G::$DB->prepared_query(' + SELECT UserID + FROM users_notify_torrents + WHERE TorrentID = ?', $ID); + while (list($UserID) = G::$DB->next_record()) { + $deleted_keys[] = "notifications_new_$UserID"; + } + $manager->soft_delete(SQLDB, 'users_notify_torrents', [['TorrentID', $ID]]); + + $RecentUploads = G::$Cache->get_value("recent_uploads_$UserID"); + if (is_array($RecentUploads)) { + foreach ($RecentUploads as $Key => $Recent) { + if ($Recent['ID'] == $GroupID) { + $deleted_keys[] = "recent_uploads_$UserID"; + break; + } + } + } + + $deleted_keys[] = "torrent_download_$ID"; + $deleted_keys[] = "torrent_group_$GroupID"; + $deleted_keys[] = "torrents_details_$GroupID"; + G::$Cache->deleteMulti($deleted_keys); + + G::$DB->set_query_id($QueryID); + } + + public static function send_pm($TorrentID, $UploaderID, $Name, $Log, $TrumpID = 0, $PMUploader = false) { + global $DB; + + $Subject = 'Torrent deleted: ' . $Name; + + $MessageStart = 'A torrent '; + if ($TrumpID > 0) { + $MessageEnd = ' has been trumped. You can find the new torrent [url='.site_url().'torrents.php?torrentid='.$TrumpID.']here[/url].'; + } + else { + $MessageEnd = ' has been deleted.'; + } + $MessageEnd .= "\n\n[url=".site_url()."log.php?search=Torrent+{$TorrentID}]Log message[/url]: {$Log}."; + + // Uploader + if ($PMUploader) { + Misc::send_pm($UploaderID, 0, $Subject, $MessageStart.'you uploaded'.$MessageEnd); + } + $PMedUsers = [$UploaderID]; + + // Seeders + $Extra = implode(',', array_fill(0, count($PMedUsers), '?')); + $DB->prepared_query(" SELECT DISTINCT(xfu.uid) FROM - xbt_files_users AS xfu - JOIN users_info AS ui ON xfu.uid = ui.UserID + xbt_files_users AS xfu + JOIN users_info AS ui ON xfu.uid = ui.UserID WHERE xfu.fid = ? - AND ui.NotifyOnDeleteSeeding='1' - AND xfu.uid NOT IN ({$Extra})", $TorrentID, ...$PMedUsers); - $UserIDs = $DB->collect('uid'); - foreach ($UserIDs as $UserID) { - Misc::send_pm($UserID, 0, $Subject, $MessageStart."you're seeding".$MessageEnd); - } - $PMedUsers = array_merge($PMedUsers, $UserIDs); - - // Snatchers - $Extra = implode(',', array_fill(0, count($PMedUsers), '?')); - $DB->prepared_query(" + AND ui.NotifyOnDeleteSeeding='1' + AND xfu.uid NOT IN ({$Extra})", $TorrentID, ...$PMedUsers); + $UserIDs = $DB->collect('uid'); + foreach ($UserIDs as $UserID) { + Misc::send_pm($UserID, 0, $Subject, $MessageStart."you're seeding".$MessageEnd); + } + $PMedUsers = array_merge($PMedUsers, $UserIDs); + + // Snatchers + $Extra = implode(',', array_fill(0, count($PMedUsers), '?')); + $DB->prepared_query(" SELECT DISTINCT(xs.uid) FROM xbt_snatched AS xs JOIN users_info AS ui ON xs.uid = ui.UserID WHERE xs.fid=? AND ui.NotifyOnDeleteSnatched='1' AND xs.uid NOT IN ({$Extra})", $TorrentID, ...$PMedUsers); - $UserIDs = $DB->collect('uid'); - foreach ($UserIDs as $UserID) { - Misc::send_pm($UserID, 0, $Subject, $MessageStart."you've snatched".$MessageEnd); - } - $PMedUsers = array_merge($PMedUsers, $UserIDs); - - // Downloaders - $Extra = implode(',', array_fill(0, count($PMedUsers), '?')); - $DB->prepared_query(" + $UserIDs = $DB->collect('uid'); + foreach ($UserIDs as $UserID) { + Misc::send_pm($UserID, 0, $Subject, $MessageStart."you've snatched".$MessageEnd); + } + $PMedUsers = array_merge($PMedUsers, $UserIDs); + + // Downloaders + $Extra = implode(',', array_fill(0, count($PMedUsers), '?')); + $DB->prepared_query(" SELECT DISTINCT(ud.UserID) FROM users_downloads AS ud JOIN users_info AS ui ON ud.UserID = ui.UserID WHERE ud.TorrentID=? AND ui.NotifyOnDeleteDownloaded='1' AND ud.UserID NOT IN ({$Extra})", $TorrentID, ...$PMedUsers); - $UserIDs = $DB->collect('UserID'); - foreach ($UserIDs as $UserID) { - Misc::send_pm($UserID, 0, $Subject, $MessageStart."you've downloaded".$MessageEnd); - } - } - - - /** - * Delete a group, called after all of its torrents have been deleted. - * IMPORTANT: Never call this unless you're certain the group is no longer used by any torrents - * - * @param int $GroupID - */ - public static function delete_group($GroupID) { - $QueryID = G::$DB->get_query_id(); - - Misc::write_log("Group $GroupID automatically deleted (No torrents have this group)."); - - G::$DB->query(" - SELECT CategoryID - FROM torrents_group - WHERE ID = '$GroupID'"); - list($Category) = G::$DB->next_record(); - if ($Category == 1) { - G::$Cache->decrement('stats_album_count'); - } - G::$Cache->decrement('stats_group_count'); - - - - // Collages - G::$DB->query(" - SELECT CollageID - FROM collages_torrents - WHERE GroupID = '$GroupID'"); - if (G::$DB->has_results()) { - $CollageIDs = G::$DB->collect('CollageID'); - G::$DB->query(" - UPDATE collages - SET NumTorrents = NumTorrents - 1 - WHERE ID IN (".implode(', ', $CollageIDs).')'); - G::$DB->query(" - DELETE FROM collages_torrents - WHERE GroupID = '$GroupID'"); - - foreach ($CollageIDs as $CollageID) { - G::$Cache->delete_value("collage_$CollageID"); - } - G::$Cache->delete_value("torrent_collages_$GroupID"); - } - - // Artists - // Collect the artist IDs and then wipe the torrents_artist entry - G::$DB->query(" - SELECT ArtistID - FROM torrents_artists - WHERE GroupID = $GroupID"); - $Artists = G::$DB->collect('ArtistID'); - - G::$DB->query(" - DELETE FROM torrents_artists - WHERE GroupID = '$GroupID'"); - - foreach ($Artists as $ArtistID) { - if (empty($ArtistID)) { - continue; - } - // Get a count of how many groups or requests use the artist ID - G::$DB->query(" - SELECT COUNT(ag.ArtistID) - FROM artists_group AS ag - LEFT JOIN requests_artists AS ra ON ag.ArtistID = ra.ArtistID - WHERE ra.ArtistID IS NOT NULL - AND ag.ArtistID = '$ArtistID'"); - list($ReqCount) = G::$DB->next_record(); - G::$DB->query(" - SELECT COUNT(ag.ArtistID) - FROM artists_group AS ag - LEFT JOIN torrents_artists AS ta ON ag.ArtistID = ta.ArtistID - WHERE ta.ArtistID IS NOT NULL - AND ag.ArtistID = '$ArtistID'"); - list($GroupCount) = G::$DB->next_record(); - if (($ReqCount + $GroupCount) == 0) { - //The only group to use this artist - Artists::delete_artist($ArtistID); - } else { - //Not the only group, still need to clear cache - G::$Cache->delete_value("artist_groups_$ArtistID"); - } - } - - // Requests - G::$DB->query(" - SELECT ID - FROM requests - WHERE GroupID = '$GroupID'"); - $Requests = G::$DB->collect('ID'); - G::$DB->query(" - UPDATE requests - SET GroupID = NULL - WHERE GroupID = '$GroupID'"); - foreach ($Requests as $RequestID) { - G::$Cache->delete_value("request_$RequestID"); - } - - // comments - Comments::delete_page('torrents', $GroupID); - - G::$DB->query(" - DELETE FROM torrents_group - WHERE ID = '$GroupID'"); - G::$DB->query(" - DELETE FROM torrents_tags - WHERE GroupID = '$GroupID'"); - G::$DB->query(" - DELETE FROM torrents_tags_votes - WHERE GroupID = '$GroupID'"); - G::$DB->query(" - DELETE FROM bookmarks_torrents - WHERE GroupID = '$GroupID'"); - G::$DB->query(" - DELETE FROM wiki_torrents - WHERE PageID = '$GroupID'"); - - G::$Cache->delete_value("torrents_details_$GroupID"); - G::$Cache->delete_value("torrent_group_$GroupID"); - G::$Cache->delete_value("groups_artists_$GroupID"); - G::$DB->set_query_id($QueryID); - } - - - /** - * Update the cache and sphinx delta index to keep everything up-to-date. - * - * @param int $GroupID - */ - public static function update_hash($GroupID) { - $QueryID = G::$DB->get_query_id(); - - G::$DB->query(" - UPDATE torrents_group - SET TagList = ( - SELECT REPLACE(GROUP_CONCAT(tags.Name SEPARATOR ' '), '.', '_') - FROM torrents_tags AS t - INNER JOIN tags ON tags.ID = t.TagID - WHERE t.GroupID = '$GroupID' - GROUP BY t.GroupID - ) - WHERE ID = '$GroupID'"); - - // Fetch album vote score - G::$DB->query(" - SELECT Score - FROM torrents_votes - WHERE GroupID = $GroupID"); - if (G::$DB->has_results()) { - list($VoteScore) = G::$DB->next_record(); - } else { - $VoteScore = 0; - } - - // Fetch album artists - G::$DB->query(" - SELECT GROUP_CONCAT(aa.Name separator ' ') - FROM torrents_artists AS ta - JOIN artists_alias AS aa ON aa.AliasID = ta.AliasID - WHERE ta.GroupID = $GroupID - AND ta.Importance IN ('1', '4', '5', '6') - GROUP BY ta.GroupID"); - if (G::$DB->has_results()) { - list($ArtistName) = G::$DB->next_record(MYSQLI_NUM, false); - } else { - $ArtistName = ''; - } - - G::$DB->query(" - REPLACE INTO sphinx_delta - (ID, GroupID, GroupName, TagList, Year, CategoryID, Time, ReleaseType, RecordLabel, - CatalogueNumber, VanityHouse, Size, Snatched, Seeders, Leechers, LogScore, Scene, HasLog, - HasCue, FreeTorrent, Media, Format, Encoding, Description, RemasterYear, RemasterTitle, - RemasterRecordLabel, RemasterCatalogueNumber, FileList, VoteScore, ArtistName) - SELECT - t.ID, g.ID, Name, TagList, Year, CategoryID, UNIX_TIMESTAMP(t.Time), ReleaseType, - RecordLabel, CatalogueNumber, VanityHouse, Size, Snatched, Seeders, - Leechers, LogScore, CAST(Scene AS CHAR), CAST(HasLog AS CHAR), CAST(HasCue AS CHAR), - CAST(FreeTorrent AS CHAR), Media, Format, Encoding, Description, - RemasterYear, RemasterTitle, RemasterRecordLabel, RemasterCatalogueNumber, - REPLACE(REPLACE(FileList, '_', ' '), '/', ' ') AS FileList, $VoteScore, '".db_string($ArtistName)."' - FROM torrents AS t - JOIN torrents_group AS g ON g.ID = t.GroupID - WHERE g.ID = $GroupID"); - - G::$Cache->delete_value("torrents_details_$GroupID"); - G::$Cache->delete_value("torrent_group_$GroupID"); - G::$Cache->delete_value("torrent_group_light_$GroupID"); - - $ArtistInfo = Artists::get_artist($GroupID); - foreach ($ArtistInfo as $Importances => $Importance) { - foreach ($Importance as $Artist) { - G::$Cache->delete_value('artist_groups_'.$Artist['id']); //Needed for at least freeleech change, if not others. - } - } - - G::$Cache->delete_value("groups_artists_$GroupID"); - G::$DB->set_query_id($QueryID); - } - - /** - * Regenerate a torrent's file list from its meta data, - * update the database record and clear relevant cache keys - * - * @param int $TorrentID - */ - public static function regenerate_filelist($TorrentID) { - $QueryID = G::$DB->get_query_id(); - - G::$DB->query(" - SELECT tg.ID, - tf.File - FROM torrents_files AS tf - JOIN torrents AS t ON t.ID = tf.TorrentID - JOIN torrents_group AS tg ON tg.ID = t.GroupID - WHERE tf.TorrentID = $TorrentID"); - if (G::$DB->has_results()) { - list($GroupID, $Contents) = G::$DB->next_record(MYSQLI_NUM, false); - if (Misc::is_new_torrent($Contents)) { - $Tor = new BencodeTorrent($Contents); - $FilePath = (isset($Tor->Dec['info']['files']) ? Format::make_utf8($Tor->get_name()) : ''); - } else { - $Tor = new TORRENT(unserialize(base64_decode($Contents)), true); - $FilePath = (isset($Tor->Val['info']->Val['files']) ? Format::make_utf8($Tor->get_name()) : ''); - } - list($TotalSize, $FileList) = $Tor->file_list(); - foreach ($FileList as $File) { - $TmpFileList[] = self::filelist_format_file($File); - } - $FileString = implode("\n", $TmpFileList); - G::$DB->query(" - UPDATE torrents - SET Size = $TotalSize, FilePath = '".db_string($FilePath)."', FileList = '".db_string($FileString)."' - WHERE ID = $TorrentID"); - G::$Cache->delete_value("torrents_details_$GroupID"); - } - G::$DB->set_query_id($QueryID); - } - - /** - * Return UTF-8 encoded string to use as file delimiter in torrent file lists - */ - public static function filelist_delim() { - static $FilelistDelimUTF8; - if (isset($FilelistDelimUTF8)) { - return $FilelistDelimUTF8; - } - return $FilelistDelimUTF8 = utf8_encode(chr(self::FILELIST_DELIM)); - } - - /** - * Create a string that contains file info in a format that's easy to use for Sphinx - * - * @param array $File (File size, File name) - * @return string with the format .EXT sSIZEs NAME DELIMITER - */ - public static function filelist_format_file($File) { - list($Size, $Name) = $File; - $Name = Format::make_utf8(strtr($Name, "\n\r\t", ' ')); - $ExtPos = strrpos($Name, '.'); - // Should not be $ExtPos !== false. Extensionless files that start with a . should not get extensions - $Ext = ($ExtPos ? trim(substr($Name, $ExtPos + 1)) : ''); - return sprintf("%s s%ds %s %s", ".$Ext", $Size, $Name, self::filelist_delim()); - } - - /** - * Create a string that contains file info in the old format for the API - * - * @param string $File string with the format .EXT sSIZEs NAME DELIMITER - * @return string with the format NAME{{{SIZE}}} - */ - public static function filelist_old_format($File) { - $File = self::filelist_get_file($File); - return $File['name'] . '{{{' . $File['size'] . '}}}'; - } - - /** - * Translate a formatted file info string into a more useful array structure - * - * @param string $File string with the format .EXT sSIZEs NAME DELIMITER - * @return file info array with the keys 'ext', 'size' and 'name' - */ - public static function filelist_get_file($File) { - // Need this hack because filelists are always display_str()ed - $DelimLen = strlen(display_str(self::filelist_delim())) + 1; - list($FileExt, $Size, $Name) = explode(' ', $File, 3); - if ($Spaces = strspn($Name, ' ')) { - $Name = str_replace(' ', ' ', substr($Name, 0, $Spaces)) . substr($Name, $Spaces); - } - return array( - 'ext' => $FileExt, - 'size' => substr($Size, 1, -1), - 'name' => substr($Name, 0, -$DelimLen) - ); - } - - /** - * Format the information about a torrent. - * @param array $Data an array a subset of the following keys: - * Format, Encoding, HasLog, LogScore HasCue, Media, Scene, RemasterYear - * RemasterTitle, FreeTorrent, PersonalFL - * @param boolean $ShowMedia if false, Media key will be omitted - * @param boolean $ShowEdition if false, RemasterYear/RemasterTitle will be omitted - * @return string - */ - public static function torrent_info($Data, $ShowMedia = false, $ShowEdition = false, $ShowFlags = true, $GroupName = '') { - $Info = array(); - if (!empty($Data['Format'])) { - $Info[] = $Data['Format']; - } - if (!empty($Data['Encoding'])) { - $Info[] = $Data['Encoding']; - } - if (!empty($Data['HasLog'])) { - $Str = 'Log'; - if (!empty($Data['HasLogDB'])) { - $Str .= ' ('.$Data['LogScore'].'%)'; - } - $Info[] = $Str; - } - if (!empty($Data['HasCue'])) { - $Info[] = 'Cue'; - } - if ($ShowMedia && !empty($Data['Media'])) { - $Info[] = $Data['Media']; - } - if (!empty($Data['Scene'])) { - $Info[] = 'Scene'; - } - if (!count($Info) && $GroupName != '') { - $Info[] = $GroupName; - } - if ($ShowEdition) { - $EditionInfo = array(); - if (!empty($Data['RemasterYear'])) { - $EditionInfo[] = $Data['RemasterYear']; - } - if (!empty($Data['RemasterTitle'])) { - $EditionInfo[] = $Data['RemasterTitle']; - } - if (count($EditionInfo)) { - $Info[] = implode(' ', $EditionInfo); - } - } - if (!empty($Data['IsSnatched'])) { - $Info[] = Format::torrent_label('Snatched!'); - } - if (isset($Data['FreeTorrent'])) { - if ($Data['FreeTorrent'] == '1') { - $Info[] = Format::torrent_label('Freeleech!'); - } - if ($Data['FreeTorrent'] == '2') { - $Info[] = Format::torrent_label('Neutral Leech!'); - } - } - if (!empty($Data['PersonalFL'])) { - $Info[] = Format::torrent_label('Personal Freeleech!'); - } - if (!empty($Data['Reported'])) { - $Info[] = Format::torrent_label('Reported'); - } - - if ($ShowFlags) { - if ($Data['HasLog'] && $Data['HasLogDB'] && $Data['LogChecksum'] !== '1') { - $Info[] = Format::torrent_label('Bad/Missing Checksum'); - } - if (!empty($Data['BadTags'])) { - $Info[] = Format::torrent_label('Bad Tags'); - } - if (!empty($Data['BadFolders'])) { - $Info[] = Format::torrent_label('Bad Folders'); - } - if (!empty($Data['MissingLineage'])) { - $Info[] = Format::torrent_label('Missing Lineage'); - } - if (!empty($Data['CassetteApproved'])) { - $Info[] = Format::torrent_label('Cassette Approved'); - } - if (!empty($Data['LossymasterApproved'])) { - $Info[] = Format::torrent_label('Lossy Master Approved'); - } - if (!empty($Data['LossywebApproved'])) { - $Info[] = Format::torrent_label('Lossy WEB Approved'); - } - if (!empty($Data['BadFiles'])) { - $Info[] = Format::torrent_label('Bad File Names'); - } - } - - return implode(' / ', $Info); - } - - - /** - * Will freeleech / neutral leech / normalise a set of torrents - * - * @param array $TorrentIDs An array of torrent IDs to iterate over - * @param int $FreeNeutral 0 = normal, 1 = fl, 2 = nl - * @param int $FreeLeechType 0 = Unknown, 1 = Staff picks, 2 = Perma-FL (Toolbox, etc.), 3 = Vanity House - */ - public static function freeleech_torrents($TorrentIDs, $FreeNeutral = 1, $FreeLeechType = 0) { - if (!is_array($TorrentIDs)) { - $TorrentIDs = array($TorrentIDs); - } - - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - UPDATE torrents - SET FreeTorrent = '$FreeNeutral', FreeLeechType = '$FreeLeechType' - WHERE ID IN (".implode(', ', $TorrentIDs).')'); - - G::$DB->query(' - SELECT ID, GroupID, info_hash - FROM torrents - WHERE ID IN ('.implode(', ', $TorrentIDs).') - ORDER BY GroupID ASC'); - $Torrents = G::$DB->to_array(false, MYSQLI_NUM, false); - $GroupIDs = G::$DB->collect('GroupID'); - G::$DB->set_query_id($QueryID); - - foreach ($Torrents as $Torrent) { - list($TorrentID, $GroupID, $InfoHash) = $Torrent; - Tracker::update_tracker('update_torrent', array('info_hash' => rawurlencode($InfoHash), 'freetorrent' => $FreeNeutral)); - G::$Cache->delete_value("torrent_download_$TorrentID"); - Misc::write_log(G::$LoggedUser['Username']." marked torrent $TorrentID freeleech type $FreeLeechType!"); - Torrents::write_group_log($GroupID, $TorrentID, G::$LoggedUser['ID'], "marked as freeleech type $FreeLeechType!", 0); - } - - foreach ($GroupIDs as $GroupID) { - Torrents::update_hash($GroupID); - } - } - - - /** - * Convenience function to allow for passing groups to Torrents::freeleech_torrents() - * - * @param array $GroupIDs the groups in question - * @param int $FreeNeutral see Torrents::freeleech_torrents() - * @param int $FreeLeechType see Torrents::freeleech_torrents() - */ - public static function freeleech_groups($GroupIDs, $FreeNeutral = 1, $FreeLeechType = 0) { - $QueryID = G::$DB->get_query_id(); - - if (!is_array($GroupIDs)) { - $GroupIDs = array($GroupIDs); - } - - G::$DB->query(' - SELECT ID - FROM torrents - WHERE GroupID IN ('.implode(', ', $GroupIDs).')'); - if (G::$DB->has_results()) { - $TorrentIDs = G::$DB->collect('ID'); - Torrents::freeleech_torrents($TorrentIDs, $FreeNeutral, $FreeLeechType); - } - G::$DB->set_query_id($QueryID); - } - - - /** - * Check if the logged in user has an active freeleech token - * - * @param int $TorrentID - * @return true if an active token exists - */ - public static function has_token($TorrentID) { - if (empty(G::$LoggedUser)) { - return false; - } - - static $TokenTorrents; - $UserID = G::$LoggedUser['ID']; - if (!isset($TokenTorrents)) { - $TokenTorrents = G::$Cache->get_value("users_tokens_$UserID"); - if ($TokenTorrents === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT TorrentID - FROM users_freeleeches - WHERE UserID = $UserID - AND Expired = 0"); - $TokenTorrents = array_fill_keys(G::$DB->collect('TorrentID', false), true); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value("users_tokens_$UserID", $TokenTorrents); - } - } - return isset($TokenTorrents[$TorrentID]); - } - - - /** - * Check if the logged in user can use a freeleech token on this torrent - * - * @param int $Torrent - * @return boolen True if user is allowed to use a token - */ - public static function can_use_token($Torrent) { - if (empty(G::$LoggedUser)) { - return false; - } - return (G::$LoggedUser['FLTokens'] > 0 - && $Torrent['Size'] < 2147483648 - && !$Torrent['PersonalFL'] - && empty($Torrent['FreeTorrent']) - && G::$LoggedUser['CanLeech'] == '1'); - } - - /** - * Build snatchlists and check if a torrent has been snatched - * if a user has the 'ShowSnatched' option enabled - * @param int $TorrentID - * @return bool - */ - public static function has_snatched($TorrentID) { - if (empty(G::$LoggedUser) || empty(G::$LoggedUser['ShowSnatched'])) { - return false; - } - - $UserID = G::$LoggedUser['ID']; - $Buckets = 64; - $LastBucket = $Buckets - 1; - $BucketID = $TorrentID & $LastBucket; - static $SnatchedTorrents = array(), $UpdateTime = array(); - - if (empty($SnatchedTorrents)) { - $SnatchedTorrents = array_fill(0, $Buckets, false); - $UpdateTime = G::$Cache->get_value("users_snatched_{$UserID}_time"); - if ($UpdateTime === false) { - $UpdateTime = array( - 'last' => 0, - 'next' => 0); - } - } elseif (isset($SnatchedTorrents[$BucketID][$TorrentID])) { - return true; - } - - // Torrent was not found in the previously inspected snatch lists - $CurSnatchedTorrents =& $SnatchedTorrents[$BucketID]; - if ($CurSnatchedTorrents === false) { - $CurTime = time(); - // This bucket hasn't been checked before - $CurSnatchedTorrents = G::$Cache->get_value("users_snatched_{$UserID}_$BucketID", true); - if ($CurSnatchedTorrents === false || $CurTime > $UpdateTime['next']) { - $Updated = array(); - $QueryID = G::$DB->get_query_id(); - if ($CurSnatchedTorrents === false || $UpdateTime['last'] == 0) { - for ($i = 0; $i < $Buckets; $i++) { - $SnatchedTorrents[$i] = array(); - } - // Not found in cache. Since we don't have a suitable index, it's faster to update everything - G::$DB->query(" - SELECT fid - FROM xbt_snatched - WHERE uid = '$UserID'"); - while (list($ID) = G::$DB->next_record(MYSQLI_NUM, false)) { - $SnatchedTorrents[$ID & $LastBucket][(int)$ID] = true; - } - $Updated = array_fill(0, $Buckets, true); - } elseif (isset($CurSnatchedTorrents[$TorrentID])) { - // Old cache, but torrent is snatched, so no need to update - return true; - } else { - // Old cache, check if torrent has been snatched recently - G::$DB->query(" - SELECT fid - FROM xbt_snatched - WHERE uid = '$UserID' - AND tstamp >= $UpdateTime[last]"); - while (list($ID) = G::$DB->next_record(MYSQLI_NUM, false)) { - $CurBucketID = $ID & $LastBucket; - if ($SnatchedTorrents[$CurBucketID] === false) { - $SnatchedTorrents[$CurBucketID] = G::$Cache->get_value("users_snatched_{$UserID}_$CurBucketID", true); - if ($SnatchedTorrents[$CurBucketID] === false) { - $SnatchedTorrents[$CurBucketID] = array(); - } - } - $SnatchedTorrents[$CurBucketID][(int)$ID] = true; - $Updated[$CurBucketID] = true; - } - } - G::$DB->set_query_id($QueryID); - for ($i = 0; $i < $Buckets; $i++) { - if (isset($Updated[$i])) { - G::$Cache->cache_value("users_snatched_{$UserID}_$i", $SnatchedTorrents[$i], 0); - } - } - $UpdateTime['last'] = $CurTime; - $UpdateTime['next'] = $CurTime + self::SNATCHED_UPDATE_INTERVAL; - G::$Cache->cache_value("users_snatched_{$UserID}_time", $UpdateTime, 0); - } - } - return isset($CurSnatchedTorrents[$TorrentID]); - } - - /** - * Change the schedule for when the next update to a user's cached snatch list should be performed. - * By default, the change will only be made if the new update would happen sooner than the current - * @param int $Time Seconds until the next update - * @param bool $Force Whether to accept changes that would push back the update - */ - public static function set_snatch_update_time($UserID, $Time, $Force = false) { - if (!$UpdateTime = G::$Cache->get_value("users_snatched_{$UserID}_time")) { - return; - } - $NextTime = time() + $Time; - if ($Force || $NextTime < $UpdateTime['next']) { - // Skip if the change would delay the next update - $UpdateTime['next'] = $NextTime; - G::$Cache->cache_value("users_snatched_{$UserID}_time", $UpdateTime, 0); - } - } - - // Some constants for self::display_string's $Mode parameter - const DISPLAYSTRING_HTML = 1; // Whether or not to use HTML for the output (e.g. VH tooltip) - const DISPLAYSTRING_ARTISTS = 2; // Whether or not to display artists - const DISPLAYSTRING_YEAR = 4; // Whether or not to display the group's year - const DISPLAYSTRING_VH = 8; // Whether or not to display the VH flag - const DISPLAYSTRING_RELEASETYPE = 16; // Whether or not to display the release type - const DISPLAYSTRING_LINKED = 33; // Whether or not to link artists and the group - // The constant for linking is 32, but because linking only works with HTML, this constant is defined as 32|1 = 33, i.e. LINKED also includes HTML - // Keep this in mind when defining presets below! - - // Presets to facilitate the use of $Mode - const DISPLAYSTRING_DEFAULT = 63; // HTML|ARTISTS|YEAR|VH|RELEASETYPE|LINKED = 63 - const DISPLAYSTRING_SHORT = 6; // Very simple format, only artists and year, no linking (e.g. for forum thread titles) - - /** - * Return the display string for a given torrent group $GroupID. - * @param int $GroupID - * @return string - */ - public static function display_string($GroupID, $Mode = self::DISPLAYSTRING_DEFAULT) { - global $ReleaseTypes; // I hate this - - $GroupInfo = self::get_groups(array($GroupID), true, true, false)[$GroupID]; - $ExtendedArtists = $GroupInfo['ExtendedArtists']; - - if ($Mode & self::DISPLAYSTRING_ARTISTS) { - if (!empty($ExtendedArtists[1]) - || !empty($ExtendedArtists[4]) - || !empty($ExtendedArtists[5]) - || !empty($ExtendedArtists[6]) - ) { - unset($ExtendedArtists[2], $ExtendedArtists[3]); - $DisplayName = Artists::display_artists($ExtendedArtists, ($Mode & self::DISPLAYSTRING_LINKED)); - } else { - $DisplayName = ''; - } - } - - if ($Mode & self::DISPLAYSTRING_LINKED) { - $DisplayName .= "$GroupInfo[Name]"; - } else { - $DisplayName .= $GroupInfo['Name']; - } - - if (($Mode & self::DISPLAYSTRING_YEAR) && $GroupInfo['Year'] > 0) { - $DisplayName .= " [$GroupInfo[Year]]"; - } - - if (($Mode & self::DISPLAYSTRING_VH) && $GroupInfo['VanityHouse']) { - if ($Mode & self::DISPLAYSTRING_HTML) { - $DisplayName .= ' [VH]'; - } else { - $DisplayName .= ' [VH]'; - } - } - - if (($Mode & self::DISPLAYSTRING_RELEASETYPE) && $GroupInfo['ReleaseType'] > 0) { - $DisplayName .= ' ['.$ReleaseTypes[$GroupInfo['ReleaseType']].']'; - } - - return $DisplayName; - } - - public static function edition_string(array $Torrent, array $Group = array()) { - if ($Torrent['Remastered'] && $Torrent['RemasterYear'] != 0) { - $EditionName = $Torrent['RemasterYear']; - $AddExtra = ' - '; - if ($Torrent['RemasterRecordLabel']) { - $EditionName .= $AddExtra . display_str($Torrent['RemasterRecordLabel']); - $AddExtra = ' / '; - } - if ($Torrent['RemasterCatalogueNumber']) { - $EditionName .= $AddExtra . display_str($Torrent['RemasterCatalogueNumber']); - $AddExtra = ' / '; - } - if ($Torrent['RemasterTitle']) { - $EditionName .= $AddExtra . display_str($Torrent['RemasterTitle']); - $AddExtra = ' / '; - } - $EditionName .= $AddExtra . display_str($Torrent['Media']); - } else { - $AddExtra = ' / '; - if (!$Torrent['Remastered']) { - $EditionName = 'Original Release'; - if ($Group['RecordLabel']) { - $EditionName .= $AddExtra . $Group['RecordLabel']; - $AddExtra = ' / '; - } - if ($Group['CatalogueNumber']) { - $EditionName .= $AddExtra . $Group['CatalogueNumber']; - $AddExtra = ' / '; - } - } else { - $EditionName = 'Unknown Release(s)'; - } - $EditionName .= $AddExtra . display_str($Torrent['Media']); - } - return $EditionName; - } - - //Used to get reports info on a unison cache in both browsing pages and torrent pages. - public static function get_reports($TorrentID) { - $Reports = G::$Cache->get_value("reports_torrent_$TorrentID"); - if ($Reports === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - ID, - ReporterID, - Type, - UserComment, - ReportedTime - FROM reportsv2 - WHERE TorrentID = $TorrentID - AND Status != 'Resolved'"); - $Reports = G::$DB->to_array(false, MYSQLI_ASSOC, false); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value("reports_torrent_$TorrentID", $Reports, 0); - } - if (!check_perms('admin_reports')) { - $Return = array(); - foreach ($Reports as $Report) { - if ($Report['Type'] !== 'edited') { - $Return[] = $Report; - } - } - return $Return; - } - return $Reports; - } - - /** - * Update the logscore of a torrent. The score is the minimum score of any - * log files that are part of the torrent. - */ - public static function clear_log($TorrentID, $LogID) { - G::$DB->prepared_query(" - DELETE FROM torrents_logs WHERE TorrentID=? AND LogID=? - ", $TorrentID, $LogID - ); - return G::$DB->affected_rows(); - } - - public static function set_logscore($TorrentID, $GroupID) { - G::$DB->prepared_query(" - SELECT COUNT(*) FROM torrents_logs WHERE TorrentID=? - ", $TorrentID - ); - - list($count) = G::$DB->fetch_record(); - if (!$count) { - G::$DB->prepared_query(" - UPDATE torrents SET HasLogDB = 0, LogScore = 100, LogChecksum = 1 WHERE ID=? - ", $TorrentID - ); - } - else { - G::$DB->prepared_query(" - UPDATE torrents AS t - LEFT JOIN ( - SELECT - TorrentID, - MIN(CASE WHEN Adjusted = '1' THEN AdjustedScore ELSE Score END) AS Score, - MIN(CASE WHEN Adjusted = '1' THEN AdjustedChecksum ELSE Checksum END) AS Checksum - FROM torrents_logs - WHERE TorrentID = ? - GROUP BY TorrentID - ) AS tl ON t.ID = tl.TorrentID - SET - t.LogScore = tl.Score, - t.LogChecksum = tl.Checksum - WHERE t.ID = ? - ", $TorrentID, $TorrentID); - } - - G::$Cache->delete_value("torrent_group_{$GroupID}"); - G::$Cache->delete_value("torrents_details_{$GroupID}"); - } + $UserIDs = $DB->collect('UserID'); + foreach ($UserIDs as $UserID) { + Misc::send_pm($UserID, 0, $Subject, $MessageStart."you've downloaded".$MessageEnd); + } + } + + + /** + * Delete a group, called after all of its torrents have been deleted. + * IMPORTANT: Never call this unless you're certain the group is no longer used by any torrents + * + * @param int $GroupID + */ + public static function delete_group($GroupID) { + $QueryID = G::$DB->get_query_id(); + + Misc::write_log("Group $GroupID automatically deleted (No torrents have this group)."); + + G::$DB->query(" + SELECT CategoryID + FROM torrents_group + WHERE ID = '$GroupID'"); + list($Category) = G::$DB->next_record(); + if ($Category == 1) { + G::$Cache->decrement('stats_album_count'); + } + G::$Cache->decrement('stats_group_count'); + + + + // Collages + G::$DB->query(" + SELECT CollageID + FROM collages_torrents + WHERE GroupID = '$GroupID'"); + if (G::$DB->has_results()) { + $CollageIDs = G::$DB->collect('CollageID'); + G::$DB->query(" + UPDATE collages + SET NumTorrents = NumTorrents - 1 + WHERE ID IN (".implode(', ', $CollageIDs).')'); + G::$DB->query(" + DELETE FROM collages_torrents + WHERE GroupID = '$GroupID'"); + + foreach ($CollageIDs as $CollageID) { + G::$Cache->delete_value("collage_$CollageID"); + } + G::$Cache->delete_value("torrent_collages_$GroupID"); + } + + // Artists + // Collect the artist IDs and then wipe the torrents_artist entry + G::$DB->query(" + SELECT ArtistID + FROM torrents_artists + WHERE GroupID = $GroupID"); + $Artists = G::$DB->collect('ArtistID'); + + G::$DB->query(" + DELETE FROM torrents_artists + WHERE GroupID = '$GroupID'"); + + foreach ($Artists as $ArtistID) { + if (empty($ArtistID)) { + continue; + } + // Get a count of how many groups or requests use the artist ID + G::$DB->query(" + SELECT COUNT(ag.ArtistID) + FROM artists_group AS ag + LEFT JOIN requests_artists AS ra ON ag.ArtistID = ra.ArtistID + WHERE ra.ArtistID IS NOT NULL + AND ag.ArtistID = '$ArtistID'"); + list($ReqCount) = G::$DB->next_record(); + G::$DB->query(" + SELECT COUNT(ag.ArtistID) + FROM artists_group AS ag + LEFT JOIN torrents_artists AS ta ON ag.ArtistID = ta.ArtistID + WHERE ta.ArtistID IS NOT NULL + AND ag.ArtistID = '$ArtistID'"); + list($GroupCount) = G::$DB->next_record(); + if (($ReqCount + $GroupCount) == 0) { + //The only group to use this artist + Artists::delete_artist($ArtistID); + } else { + //Not the only group, still need to clear cache + G::$Cache->delete_value("artist_groups_$ArtistID"); + } + } + + // Requests + G::$DB->query(" + SELECT ID + FROM requests + WHERE GroupID = '$GroupID'"); + $Requests = G::$DB->collect('ID'); + G::$DB->query(" + UPDATE requests + SET GroupID = NULL + WHERE GroupID = '$GroupID'"); + foreach ($Requests as $RequestID) { + G::$Cache->delete_value("request_$RequestID"); + } + + // comments + Comments::delete_page('torrents', $GroupID); + + G::$DB->query(" + DELETE FROM torrents_group + WHERE ID = '$GroupID'"); + G::$DB->query(" + DELETE FROM torrents_tags + WHERE GroupID = '$GroupID'"); + G::$DB->query(" + DELETE FROM torrents_tags_votes + WHERE GroupID = '$GroupID'"); + G::$DB->query(" + DELETE FROM bookmarks_torrents + WHERE GroupID = '$GroupID'"); + G::$DB->query(" + DELETE FROM wiki_torrents + WHERE PageID = '$GroupID'"); + + G::$Cache->delete_value("torrents_details_$GroupID"); + G::$Cache->delete_value("torrent_group_$GroupID"); + G::$Cache->delete_value("groups_artists_$GroupID"); + G::$DB->set_query_id($QueryID); + } + + + /** + * Update the cache and sphinx delta index to keep everything up-to-date. + * + * @param int $GroupID + */ + public static function update_hash($GroupID) { + $QueryID = G::$DB->get_query_id(); + + G::$DB->query(" + UPDATE torrents_group + SET TagList = ( + SELECT REPLACE(GROUP_CONCAT(tags.Name SEPARATOR ' '), '.', '_') + FROM torrents_tags AS t + INNER JOIN tags ON tags.ID = t.TagID + WHERE t.GroupID = '$GroupID' + GROUP BY t.GroupID + ) + WHERE ID = '$GroupID'"); + + // Fetch album vote score + G::$DB->query(" + SELECT Score + FROM torrents_votes + WHERE GroupID = $GroupID"); + if (G::$DB->has_results()) { + list($VoteScore) = G::$DB->next_record(); + } else { + $VoteScore = 0; + } + + // Fetch album artists + G::$DB->query(" + SELECT GROUP_CONCAT(aa.Name separator ' ') + FROM torrents_artists AS ta + JOIN artists_alias AS aa ON aa.AliasID = ta.AliasID + WHERE ta.GroupID = $GroupID + AND ta.Importance IN ('1', '4', '5', '6') + GROUP BY ta.GroupID"); + if (G::$DB->has_results()) { + list($ArtistName) = G::$DB->next_record(MYSQLI_NUM, false); + } else { + $ArtistName = ''; + } + + G::$DB->query(" + REPLACE INTO sphinx_delta + (ID, GroupID, GroupName, TagList, Year, CategoryID, Time, ReleaseType, RecordLabel, + CatalogueNumber, VanityHouse, Size, Snatched, Seeders, Leechers, LogScore, Scene, HasLog, + HasCue, FreeTorrent, Media, Format, Encoding, Description, RemasterYear, RemasterTitle, + RemasterRecordLabel, RemasterCatalogueNumber, FileList, VoteScore, ArtistName) + SELECT + t.ID, g.ID, g.Name, g.TagList, g.Year, g.CategoryID, UNIX_TIMESTAMP(t.Time), g.ReleaseType, + g.RecordLabel, g.CatalogueNumber, g.VanityHouse, t.Size, tls.Snatched, tls.Seeders, + tls.Leechers, t.LogScore, CAST(t.Scene AS CHAR), CAST(t.HasLog AS CHAR), CAST(t.HasCue AS CHAR), + CAST(t.FreeTorrent AS CHAR), t.Media, t.Format, t.Encoding, t.Description, + t.RemasterYear, t.RemasterTitle, t.RemasterRecordLabel, t.RemasterCatalogueNumber, + REPLACE(REPLACE(t.FileList, '_', ' '), '/', ' ') AS FileList, $VoteScore, '".db_string($ArtistName)."' + FROM torrents AS t + INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID) + INNER JOIN torrents_group AS g ON (g.ID = t.GroupID) + WHERE g.ID = $GroupID"); + + G::$Cache->delete_value("torrents_details_$GroupID"); + G::$Cache->delete_value("torrent_group_$GroupID"); + G::$Cache->delete_value("torrent_group_light_$GroupID"); + + $ArtistInfo = Artists::get_artist($GroupID); + foreach ($ArtistInfo as $Importances => $Importance) { + foreach ($Importance as $Artist) { + G::$Cache->delete_value('artist_groups_'.$Artist['id']); //Needed for at least freeleech change, if not others. + } + } + + G::$Cache->delete_value("groups_artists_$GroupID"); + G::$DB->set_query_id($QueryID); + } + + /** + * Regenerate a torrent's file list from its meta data, + * update the database record and clear relevant cache keys + * + * @param int $TorrentID + */ + public static function regenerate_filelist($TorrentID) { + $QueryID = G::$DB->get_query_id(); + + G::$DB->query(" + SELECT tg.ID, + tf.File + FROM torrents_files AS tf + JOIN torrents AS t ON t.ID = tf.TorrentID + JOIN torrents_group AS tg ON tg.ID = t.GroupID + WHERE tf.TorrentID = $TorrentID"); + if (G::$DB->has_results()) { + list($GroupID, $Contents) = G::$DB->next_record(MYSQLI_NUM, false); + if (Misc::is_new_torrent($Contents)) { + $Tor = new BencodeTorrent($Contents); + $FilePath = (isset($Tor->Dec['info']['files']) ? Format::make_utf8($Tor->get_name()) : ''); + } else { + $Tor = new TORRENT(unserialize(base64_decode($Contents)), true); + $FilePath = (isset($Tor->Val['info']->Val['files']) ? Format::make_utf8($Tor->get_name()) : ''); + } + list($TotalSize, $FileList) = $Tor->file_list(); + foreach ($FileList as $File) { + $TmpFileList[] = self::filelist_format_file($File); + } + $FileString = implode("\n", $TmpFileList); + G::$DB->query(" + UPDATE torrents + SET Size = $TotalSize, FilePath = '".db_string($FilePath)."', FileList = '".db_string($FileString)."' + WHERE ID = $TorrentID"); + G::$Cache->delete_value("torrents_details_$GroupID"); + } + G::$DB->set_query_id($QueryID); + } + + /** + * Return UTF-8 encoded string to use as file delimiter in torrent file lists + */ + public static function filelist_delim() { + static $FilelistDelimUTF8; + if (isset($FilelistDelimUTF8)) { + return $FilelistDelimUTF8; + } + return $FilelistDelimUTF8 = utf8_encode(chr(self::FILELIST_DELIM)); + } + + /** + * Create a string that contains file info in a format that's easy to use for Sphinx + * + * @param array $File (File size, File name) + * @return string with the format .EXT sSIZEs NAME DELIMITER + */ + public static function filelist_format_file($File) { + list($Size, $Name) = $File; + $Name = Format::make_utf8(strtr($Name, "\n\r\t", ' ')); + $ExtPos = strrpos($Name, '.'); + // Should not be $ExtPos !== false. Extensionless files that start with a . should not get extensions + $Ext = ($ExtPos ? trim(substr($Name, $ExtPos + 1)) : ''); + return sprintf("%s s%ds %s %s", ".$Ext", $Size, $Name, self::filelist_delim()); + } + + /** + * Create a string that contains file info in the old format for the API + * + * @param string $File string with the format .EXT sSIZEs NAME DELIMITER + * @return string with the format NAME{{{SIZE}}} + */ + public static function filelist_old_format($File) { + $File = self::filelist_get_file($File); + return $File['name'] . '{{{' . $File['size'] . '}}}'; + } + + /** + * Translate a formatted file info string into a more useful array structure + * + * @param string $File string with the format .EXT sSIZEs NAME DELIMITER + * @return file info array with the keys 'ext', 'size' and 'name' + */ + public static function filelist_get_file($File) { + // Need this hack because filelists are always display_str()ed + $DelimLen = strlen(display_str(self::filelist_delim())) + 1; + list($FileExt, $Size, $Name) = explode(' ', $File, 3); + if ($Spaces = strspn($Name, ' ')) { + $Name = str_replace(' ', ' ', substr($Name, 0, $Spaces)) . substr($Name, $Spaces); + } + return array( + 'ext' => $FileExt, + 'size' => substr($Size, 1, -1), + 'name' => substr($Name, 0, -$DelimLen) + ); + } + + /** + * Format the information about a torrent. + * @param array $Data an array a subset of the following keys: + * Format, Encoding, HasLog, LogScore HasCue, Media, Scene, RemasterYear + * RemasterTitle, FreeTorrent, PersonalFL + * @param boolean $ShowMedia if false, Media key will be omitted + * @param boolean $ShowEdition if false, RemasterYear/RemasterTitle will be omitted + * @return string + */ + public static function torrent_info($Data, $ShowMedia = false, $ShowEdition = false, $ShowFlags = true, $GroupName = '') { + $Info = array(); + if (!empty($Data['Format'])) { + $Info[] = $Data['Format']; + } + if (!empty($Data['Encoding'])) { + $Info[] = $Data['Encoding']; + } + if (!empty($Data['HasLog'])) { + $Str = 'Log'; + if (!empty($Data['HasLogDB'])) { + $Str .= ' ('.$Data['LogScore'].'%)'; + } + $Info[] = $Str; + } + if (!empty($Data['HasCue'])) { + $Info[] = 'Cue'; + } + if ($ShowMedia && !empty($Data['Media'])) { + $Info[] = $Data['Media']; + } + if (!empty($Data['Scene'])) { + $Info[] = 'Scene'; + } + if (!count($Info) && $GroupName != '') { + $Info[] = $GroupName; + } + if ($ShowEdition) { + $EditionInfo = array(); + if (!empty($Data['RemasterYear'])) { + $EditionInfo[] = $Data['RemasterYear']; + } + if (!empty($Data['RemasterTitle'])) { + $EditionInfo[] = $Data['RemasterTitle']; + } + if (count($EditionInfo)) { + $Info[] = implode(' ', $EditionInfo); + } + } + if (!empty($Data['IsSnatched'])) { + $Info[] = Format::torrent_label('Snatched!'); + } + if (isset($Data['FreeTorrent'])) { + if ($Data['FreeTorrent'] == '1') { + $Info[] = Format::torrent_label('Freeleech!'); + } + if ($Data['FreeTorrent'] == '2') { + $Info[] = Format::torrent_label('Neutral Leech!'); + } + } + if (!empty($Data['PersonalFL'])) { + $Info[] = Format::torrent_label('Personal Freeleech!'); + } + if (!empty($Data['Reported'])) { + $Info[] = Format::torrent_label('Reported'); + } + + if ($ShowFlags) { + if ($Data['HasLog'] && $Data['HasLogDB'] && $Data['LogChecksum'] !== '1') { + $Info[] = Format::torrent_label('Bad/Missing Checksum'); + } + if (!empty($Data['BadTags'])) { + $Info[] = Format::torrent_label('Bad Tags'); + } + if (!empty($Data['BadFolders'])) { + $Info[] = Format::torrent_label('Bad Folders'); + } + if (!empty($Data['MissingLineage'])) { + $Info[] = Format::torrent_label('Missing Lineage'); + } + if (!empty($Data['CassetteApproved'])) { + $Info[] = Format::torrent_label('Cassette Approved'); + } + if (!empty($Data['LossymasterApproved'])) { + $Info[] = Format::torrent_label('Lossy Master Approved'); + } + if (!empty($Data['LossywebApproved'])) { + $Info[] = Format::torrent_label('Lossy WEB Approved'); + } + if (!empty($Data['BadFiles'])) { + $Info[] = Format::torrent_label('Bad File Names'); + } + } + + return implode(' / ', $Info); + } + + + /** + * Will freeleech / neutral leech / normalise a set of torrents + * + * @param array $TorrentIDs An array of torrent IDs to iterate over + * @param int $FreeNeutral 0 = normal, 1 = fl, 2 = nl + * @param int $FreeLeechType 0 = Unknown, 1 = Staff picks, 2 = Perma-FL (Toolbox, etc.), 3 = Vanity House + */ + public static function freeleech_torrents($TorrentIDs, $FreeNeutral = 1, $FreeLeechType = 0) { + if (!is_array($TorrentIDs)) { + $TorrentIDs = array($TorrentIDs); + } + + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + UPDATE torrents + SET FreeTorrent = '$FreeNeutral', FreeLeechType = '$FreeLeechType' + WHERE ID IN (".implode(', ', $TorrentIDs).')'); + + G::$DB->query(' + SELECT ID, GroupID, info_hash + FROM torrents + WHERE ID IN ('.implode(', ', $TorrentIDs).') + ORDER BY GroupID ASC'); + $Torrents = G::$DB->to_array(false, MYSQLI_NUM, false); + $GroupIDs = G::$DB->collect('GroupID'); + G::$DB->set_query_id($QueryID); + + foreach ($Torrents as $Torrent) { + list($TorrentID, $GroupID, $InfoHash) = $Torrent; + Tracker::update_tracker('update_torrent', array('info_hash' => rawurlencode($InfoHash), 'freetorrent' => $FreeNeutral)); + G::$Cache->delete_value("torrent_download_$TorrentID"); + Misc::write_log(G::$LoggedUser['Username']." marked torrent $TorrentID freeleech type $FreeLeechType!"); + Torrents::write_group_log($GroupID, $TorrentID, G::$LoggedUser['ID'], "marked as freeleech type $FreeLeechType!", 0); + } + + foreach ($GroupIDs as $GroupID) { + Torrents::update_hash($GroupID); + } + } + + + /** + * Convenience function to allow for passing groups to Torrents::freeleech_torrents() + * + * @param array $GroupIDs the groups in question + * @param int $FreeNeutral see Torrents::freeleech_torrents() + * @param int $FreeLeechType see Torrents::freeleech_torrents() + */ + public static function freeleech_groups($GroupIDs, $FreeNeutral = 1, $FreeLeechType = 0) { + $QueryID = G::$DB->get_query_id(); + + if (!is_array($GroupIDs)) { + $GroupIDs = array($GroupIDs); + } + + G::$DB->query(' + SELECT ID + FROM torrents + WHERE GroupID IN ('.implode(', ', $GroupIDs).')'); + if (G::$DB->has_results()) { + $TorrentIDs = G::$DB->collect('ID'); + Torrents::freeleech_torrents($TorrentIDs, $FreeNeutral, $FreeLeechType); + } + G::$DB->set_query_id($QueryID); + } + + + /** + * Check if the logged in user has an active freeleech token + * + * @param int $TorrentID + * @return true if an active token exists + */ + public static function has_token($TorrentID) { + if (empty(G::$LoggedUser)) { + return false; + } + + static $TokenTorrents; + $UserID = G::$LoggedUser['ID']; + if (!isset($TokenTorrents)) { + $TokenTorrents = G::$Cache->get_value("users_tokens_$UserID"); + if ($TokenTorrents === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT TorrentID + FROM users_freeleeches + WHERE UserID = $UserID + AND Expired = 0"); + $TokenTorrents = array_fill_keys(G::$DB->collect('TorrentID', false), true); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value("users_tokens_$UserID", $TokenTorrents); + } + } + return isset($TokenTorrents[$TorrentID]); + } + + + /** + * Check if the logged in user can use a freeleech token on this torrent + * + * @param int $Torrent + * @return boolen True if user is allowed to use a token + */ + public static function can_use_token($Torrent) { + if (empty(G::$LoggedUser)) { + return false; + } + return (G::$LoggedUser['FLTokens'] > 0 + && $Torrent['Size'] < 2147483648 + && !$Torrent['PersonalFL'] + && empty($Torrent['FreeTorrent']) + && G::$LoggedUser['CanLeech'] == '1'); + } + + /** + * Build snatchlists and check if a torrent has been snatched + * if a user has the 'ShowSnatched' option enabled + * @param int $TorrentID + * @return bool + */ + public static function has_snatched($TorrentID) { + if (empty(G::$LoggedUser) || empty(G::$LoggedUser['ShowSnatched'])) { + return false; + } + + $UserID = G::$LoggedUser['ID']; + $Buckets = 64; + $LastBucket = $Buckets - 1; + $BucketID = $TorrentID & $LastBucket; + static $SnatchedTorrents = array(), $UpdateTime = array(); + + if (empty($SnatchedTorrents)) { + $SnatchedTorrents = array_fill(0, $Buckets, false); + $UpdateTime = G::$Cache->get_value("users_snatched_{$UserID}_time"); + if ($UpdateTime === false) { + $UpdateTime = array( + 'last' => 0, + 'next' => 0); + } + } elseif (isset($SnatchedTorrents[$BucketID][$TorrentID])) { + return true; + } + + // Torrent was not found in the previously inspected snatch lists + $CurSnatchedTorrents =& $SnatchedTorrents[$BucketID]; + if ($CurSnatchedTorrents === false) { + $CurTime = time(); + // This bucket hasn't been checked before + $CurSnatchedTorrents = G::$Cache->get_value("users_snatched_{$UserID}_$BucketID", true); + if ($CurSnatchedTorrents === false || $CurTime > $UpdateTime['next']) { + $Updated = array(); + $QueryID = G::$DB->get_query_id(); + if ($CurSnatchedTorrents === false || $UpdateTime['last'] == 0) { + for ($i = 0; $i < $Buckets; $i++) { + $SnatchedTorrents[$i] = array(); + } + // Not found in cache. Since we don't have a suitable index, it's faster to update everything + G::$DB->query(" + SELECT fid + FROM xbt_snatched + WHERE uid = '$UserID'"); + while (list($ID) = G::$DB->next_record(MYSQLI_NUM, false)) { + $SnatchedTorrents[$ID & $LastBucket][(int)$ID] = true; + } + $Updated = array_fill(0, $Buckets, true); + } elseif (isset($CurSnatchedTorrents[$TorrentID])) { + // Old cache, but torrent is snatched, so no need to update + return true; + } else { + // Old cache, check if torrent has been snatched recently + G::$DB->query(" + SELECT fid + FROM xbt_snatched + WHERE uid = '$UserID' + AND tstamp >= $UpdateTime[last]"); + while (list($ID) = G::$DB->next_record(MYSQLI_NUM, false)) { + $CurBucketID = $ID & $LastBucket; + if ($SnatchedTorrents[$CurBucketID] === false) { + $SnatchedTorrents[$CurBucketID] = G::$Cache->get_value("users_snatched_{$UserID}_$CurBucketID", true); + if ($SnatchedTorrents[$CurBucketID] === false) { + $SnatchedTorrents[$CurBucketID] = array(); + } + } + $SnatchedTorrents[$CurBucketID][(int)$ID] = true; + $Updated[$CurBucketID] = true; + } + } + G::$DB->set_query_id($QueryID); + for ($i = 0; $i < $Buckets; $i++) { + if (isset($Updated[$i])) { + G::$Cache->cache_value("users_snatched_{$UserID}_$i", $SnatchedTorrents[$i], 0); + } + } + $UpdateTime['last'] = $CurTime; + $UpdateTime['next'] = $CurTime + self::SNATCHED_UPDATE_INTERVAL; + G::$Cache->cache_value("users_snatched_{$UserID}_time", $UpdateTime, 0); + } + } + return isset($CurSnatchedTorrents[$TorrentID]); + } + + /** + * Change the schedule for when the next update to a user's cached snatch list should be performed. + * By default, the change will only be made if the new update would happen sooner than the current + * @param int $Time Seconds until the next update + * @param bool $Force Whether to accept changes that would push back the update + */ + public static function set_snatch_update_time($UserID, $Time, $Force = false) { + if (!$UpdateTime = G::$Cache->get_value("users_snatched_{$UserID}_time")) { + return; + } + $NextTime = time() + $Time; + if ($Force || $NextTime < $UpdateTime['next']) { + // Skip if the change would delay the next update + $UpdateTime['next'] = $NextTime; + G::$Cache->cache_value("users_snatched_{$UserID}_time", $UpdateTime, 0); + } + } + + // Some constants for self::display_string's $Mode parameter + const DISPLAYSTRING_HTML = 1; // Whether or not to use HTML for the output (e.g. VH tooltip) + const DISPLAYSTRING_ARTISTS = 2; // Whether or not to display artists + const DISPLAYSTRING_YEAR = 4; // Whether or not to display the group's year + const DISPLAYSTRING_VH = 8; // Whether or not to display the VH flag + const DISPLAYSTRING_RELEASETYPE = 16; // Whether or not to display the release type + const DISPLAYSTRING_LINKED = 33; // Whether or not to link artists and the group + // The constant for linking is 32, but because linking only works with HTML, this constant is defined as 32|1 = 33, i.e. LINKED also includes HTML + // Keep this in mind when defining presets below! + + // Presets to facilitate the use of $Mode + const DISPLAYSTRING_DEFAULT = 63; // HTML|ARTISTS|YEAR|VH|RELEASETYPE|LINKED = 63 + const DISPLAYSTRING_SHORT = 6; // Very simple format, only artists and year, no linking (e.g. for forum thread titles) + + /** + * Return the display string for a given torrent group $GroupID. + * @param int $GroupID + * @return string + */ + public static function display_string($GroupID, $Mode = self::DISPLAYSTRING_DEFAULT) { + global $ReleaseTypes; // I hate this + + $GroupInfo = self::get_groups(array($GroupID), true, true, false)[$GroupID]; + $ExtendedArtists = $GroupInfo['ExtendedArtists']; + + if ($Mode & self::DISPLAYSTRING_ARTISTS) { + if (!empty($ExtendedArtists[1]) + || !empty($ExtendedArtists[4]) + || !empty($ExtendedArtists[5]) + || !empty($ExtendedArtists[6]) + ) { + unset($ExtendedArtists[2], $ExtendedArtists[3]); + $DisplayName = Artists::display_artists($ExtendedArtists, ($Mode & self::DISPLAYSTRING_LINKED)); + } else { + $DisplayName = ''; + } + } + + if ($Mode & self::DISPLAYSTRING_LINKED) { + $DisplayName .= "$GroupInfo[Name]"; + } else { + $DisplayName .= $GroupInfo['Name']; + } + + if (($Mode & self::DISPLAYSTRING_YEAR) && $GroupInfo['Year'] > 0) { + $DisplayName .= " [$GroupInfo[Year]]"; + } + + if (($Mode & self::DISPLAYSTRING_VH) && $GroupInfo['VanityHouse']) { + if ($Mode & self::DISPLAYSTRING_HTML) { + $DisplayName .= ' [VH]'; + } else { + $DisplayName .= ' [VH]'; + } + } + + if (($Mode & self::DISPLAYSTRING_RELEASETYPE) && $GroupInfo['ReleaseType'] > 0) { + $DisplayName .= ' ['.$ReleaseTypes[$GroupInfo['ReleaseType']].']'; + } + + return $DisplayName; + } + + public static function edition_string(array $Torrent, array $Group = array()) { + if ($Torrent['Remastered'] && $Torrent['RemasterYear'] != 0) { + $EditionName = $Torrent['RemasterYear']; + $AddExtra = ' - '; + if ($Torrent['RemasterRecordLabel']) { + $EditionName .= $AddExtra . display_str($Torrent['RemasterRecordLabel']); + $AddExtra = ' / '; + } + if ($Torrent['RemasterCatalogueNumber']) { + $EditionName .= $AddExtra . display_str($Torrent['RemasterCatalogueNumber']); + $AddExtra = ' / '; + } + if ($Torrent['RemasterTitle']) { + $EditionName .= $AddExtra . display_str($Torrent['RemasterTitle']); + $AddExtra = ' / '; + } + $EditionName .= $AddExtra . display_str($Torrent['Media']); + } else { + $AddExtra = ' / '; + if (!$Torrent['Remastered']) { + $EditionName = 'Original Release'; + if ($Group['RecordLabel']) { + $EditionName .= $AddExtra . $Group['RecordLabel']; + $AddExtra = ' / '; + } + if ($Group['CatalogueNumber']) { + $EditionName .= $AddExtra . $Group['CatalogueNumber']; + $AddExtra = ' / '; + } + } else { + $EditionName = 'Unknown Release(s)'; + } + $EditionName .= $AddExtra . display_str($Torrent['Media']); + } + return $EditionName; + } + + //Used to get reports info on a unison cache in both browsing pages and torrent pages. + public static function get_reports($TorrentID) { + $Reports = G::$Cache->get_value("reports_torrent_$TorrentID"); + if ($Reports === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + ID, + ReporterID, + Type, + UserComment, + ReportedTime + FROM reportsv2 + WHERE TorrentID = $TorrentID + AND Status != 'Resolved'"); + $Reports = G::$DB->to_array(false, MYSQLI_ASSOC, false); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value("reports_torrent_$TorrentID", $Reports, 0); + } + if (!check_perms('admin_reports')) { + $Return = array(); + foreach ($Reports as $Report) { + if ($Report['Type'] !== 'edited') { + $Return[] = $Report; + } + } + return $Return; + } + return $Reports; + } + + /** + * Update the logscore of a torrent. The score is the minimum score of any + * log files that are part of the torrent. + */ + public static function clear_log($TorrentID, $LogID) { + G::$DB->prepared_query(" + DELETE FROM torrents_logs WHERE TorrentID=? AND LogID=? + ", $TorrentID, $LogID + ); + return G::$DB->affected_rows(); + } + + public static function set_logscore($TorrentID, $GroupID) { + G::$DB->prepared_query(" + SELECT COUNT(*) FROM torrents_logs WHERE TorrentID=? + ", $TorrentID + ); + + list($count) = G::$DB->fetch_record(); + if (!$count) { + G::$DB->prepared_query(" + UPDATE torrents SET HasLogDB = 0, LogScore = 100, LogChecksum = 1 WHERE ID=? + ", $TorrentID + ); + } + else { + G::$DB->prepared_query(" + UPDATE torrents AS t + LEFT JOIN ( + SELECT + TorrentID, + MIN(CASE WHEN Adjusted = '1' THEN AdjustedScore ELSE Score END) AS Score, + MIN(CASE WHEN Adjusted = '1' THEN AdjustedChecksum ELSE Checksum END) AS Checksum + FROM torrents_logs + WHERE TorrentID = ? + GROUP BY TorrentID + ) AS tl ON t.ID = tl.TorrentID + SET + t.LogScore = tl.Score, + t.LogChecksum = tl.Checksum + WHERE t.ID = ? + ", $TorrentID, $TorrentID); + } + + G::$Cache->delete_value("torrent_group_{$GroupID}"); + G::$Cache->delete_value("torrents_details_{$GroupID}"); + } } diff --git a/classes/torrentsdl.class.php b/classes/torrentsdl.class.php index 41793fef3..37b935db6 100644 --- a/classes/torrentsdl.class.php +++ b/classes/torrentsdl.class.php @@ -1,242 +1,242 @@ -InternalCache = false; // The internal cache is almost completely useless for this - Zip::unlimit(); // Need more memory and longer timeout - $this->QueryResult = $QueryResult; - $this->Title = $Title; - $this->User = G::$LoggedUser; - $AnnounceURL = (G::$LoggedUser['HttpsTracker']) ? ANNOUNCE_HTTPS_URL : ANNOUNCE_HTTP_URL; - $this->AnnounceURL = $AnnounceURL . '/' . G::$LoggedUser['torrent_pass'] . '/announce'; - $this->Zip = new Zip(Misc::file_string($Title)); - } + /** + * Create a Zip object and store the query results + * + * @param mysqli_result $QueryResult results from a query on the collector pages + * @param string $Title name of the collection that will be created + * @param string $AnnounceURL URL to add to the created torrents + */ + public function __construct(&$QueryResult, $Title) { + G::$Cache->InternalCache = false; // The internal cache is almost completely useless for this + Zip::unlimit(); // Need more memory and longer timeout + $this->QueryResult = $QueryResult; + $this->Title = $Title; + $this->User = G::$LoggedUser; + $AnnounceURL = (G::$LoggedUser['HttpsTracker']) ? ANNOUNCE_HTTPS_URL : ANNOUNCE_HTTP_URL; + $this->AnnounceURL = $AnnounceURL . '/' . G::$LoggedUser['torrent_pass'] . '/announce'; + $this->Zip = new Zip(Misc::file_string($Title)); + } - /** - * Store the results from a DB query in smaller chunks to save memory - * - * @param string $Key the key to use in the result hash map - * @return array with results and torrent group IDs or false if there are no results left - */ - public function get_downloads($Key) { - $GroupIDs = $Downloads = array(); - $OldQuery = G::$DB->get_query_id(); - G::$DB->set_query_id($this->QueryResult); - if (!isset($this->IDBoundaries)) { - if ($Key == 'TorrentID') { - $this->IDBoundaries = false; - } else { - $this->IDBoundaries = G::$DB->to_pair($Key, 'TorrentID', false); - } - } - $Found = 0; - while ($Download = G::$DB->next_record(MYSQLI_ASSOC, false)) { - if (!$this->IDBoundaries || $Download['TorrentID'] == $this->IDBoundaries[$Download[$Key]]) { - $Found++; - $Downloads[$Download[$Key]] = $Download; - $GroupIDs[$Download['TorrentID']] = $Download['GroupID']; - if ($Found >= self::ChunkSize) { - break; - } - } - } - $this->NumFound += $Found; - G::$DB->set_query_id($OldQuery); - if (empty($Downloads)) { - return false; - } - return array($Downloads, $GroupIDs); - } + /** + * Store the results from a DB query in smaller chunks to save memory + * + * @param string $Key the key to use in the result hash map + * @return array with results and torrent group IDs or false if there are no results left + */ + public function get_downloads($Key) { + $GroupIDs = $Downloads = []; + $OldQuery = G::$DB->get_query_id(); + G::$DB->set_query_id($this->QueryResult); + if (!isset($this->IDBoundaries)) { + if ($Key == 'TorrentID') { + $this->IDBoundaries = false; + } else { + $this->IDBoundaries = G::$DB->to_pair($Key, 'TorrentID', false); + } + } + $Found = 0; + while ($Download = G::$DB->next_record(MYSQLI_ASSOC, false)) { + if (!$this->IDBoundaries || $Download['TorrentID'] == $this->IDBoundaries[$Download[$Key]]) { + $Found++; + $Downloads[$Download[$Key]] = $Download; + $GroupIDs[$Download['TorrentID']] = $Download['GroupID']; + if ($Found >= self::ChunkSize) { + break; + } + } + } + $this->NumFound += $Found; + G::$DB->set_query_id($OldQuery); + if (empty($Downloads)) { + return false; + } + return array($Downloads, $GroupIDs); + } - /** - * Add a file to the zip archive - * - * @param string $TorrentData bencoded torrent without announce url (new format) or TORRENT object (old format) - * @param array $Info file info stored as an array with at least the keys - * Artist, Name, Year, Media, Format, Encoding and TorrentID - * @param string $FolderName folder name - */ - public function add_file(&$TorrentData, $Info, $FolderName = '') { - $FolderName = Misc::file_string($FolderName); - $MaxPathLength = $FolderName ? (self::MaxPathLength - strlen($FolderName) - 1) : self::MaxPathLength; - $FileName = self::construct_file_name($Info['Artist'], $Info['Name'], $Info['Year'], $Info['Media'], $Info['Format'], $Info['Encoding'], $Info['TorrentID'], false, $MaxPathLength); - $this->Size += $Info['Size']; - $this->NumAdded++; - $this->Zip->add_file(self::get_file($TorrentData, $this->AnnounceURL, $Info['TorrentID']), ($FolderName ? "$FolderName/" : "") . $FileName); - usleep(25000); // We don't want to send much faster than the client can receive - } + /** + * Add a file to the zip archive + * + * @param string $TorrentData bencoded torrent without announce url (new format) or TORRENT object (old format) + * @param array $Info file info stored as an array with at least the keys + * Artist, Name, Year, Media, Format, Encoding and TorrentID + * @param string $FolderName folder name + */ + public function add_file(&$TorrentData, $Info, $FolderName = '') { + $FolderName = Misc::file_string($FolderName); + $MaxPathLength = $FolderName ? (self::MaxPathLength - strlen($FolderName) - 1) : self::MaxPathLength; + $FileName = self::construct_file_name($Info['Artist'], $Info['Name'], $Info['Year'], $Info['Media'], $Info['Format'], $Info['Encoding'], $Info['TorrentID'], false, $MaxPathLength); + $this->Size += $Info['Size']; + $this->NumAdded++; + $this->Zip->add_file(self::get_file($TorrentData, $this->AnnounceURL, $Info['TorrentID']), ($FolderName ? "$FolderName/" : "") . $FileName); + usleep(25000); // We don't want to send much faster than the client can receive + } - /** - * Add a file to the list of files that could not be downloaded - * - * @param array $Info file info stored as an array with at least the keys Artist, Name and Year - */ - public function fail_file($Info) { - $this->FailedFiles[] = $Info['Artist'] . $Info['Name'] . " $Info[Year]"; - } + /** + * Add a file to the list of files that could not be downloaded + * + * @param array $Info file info stored as an array with at least the keys Artist, Name and Year + */ + public function fail_file($Info) { + $this->FailedFiles[] = $Info['Artist'] . $Info['Name'] . " $Info[Year]"; + } - /** - * Add a file to the list of files that did not match the user's format or quality requirements - * - * @param array $Info file info stored as an array with at least the keys Artist, Name and Year - */ - public function skip_file($Info) { - $this->SkippedFiles[] = $Info['Artist'] . $Info['Name'] . " $Info[Year]"; - } + /** + * Add a file to the list of files that did not match the user's format or quality requirements + * + * @param array $Info file info stored as an array with at least the keys Artist, Name and Year + */ + public function skip_file($Info) { + $this->SkippedFiles[] = $Info['Artist'] . $Info['Name'] . " $Info[Year]"; + } - /** - * Add a summary to the archive and include a list of files that could not be added. Close the zip archive - * - * @param bool $FilterStats whether to include filter stats in the report - */ - public function finalize($FilterStats = true) { - $this->Zip->add_file($this->summary($FilterStats), "Summary.txt"); - if (!empty($this->FailedFiles)) { - $this->Zip->add_file($this->errors(), "Errors.txt"); - } - $this->Zip->close_stream(); - } + /** + * Add a summary to the archive and include a list of files that could not be added. Close the zip archive + * + * @param bool $FilterStats whether to include filter stats in the report + */ + public function finalize($FilterStats = true) { + $this->Zip->add_file($this->summary($FilterStats), "Summary.txt"); + if (!empty($this->FailedFiles)) { + $this->Zip->add_file($this->errors(), "Errors.txt"); + } + $this->Zip->close_stream(); + } - /** - * Produce a summary text over the collector results - * - * @param bool $FilterStats whether to include filter stats in the report - * @return summary text - */ - public function summary($FilterStats) { - global $ScriptStartTime; - $Time = number_format(1000 * (microtime(true) - $ScriptStartTime), 2)." ms"; - $Used = Format::get_size(memory_get_usage(true)); - $Date = date("M d Y, H:i"); - $NumSkipped = count($this->SkippedFiles); - return "Collector Download Summary for $this->Title - " . SITE_NAME . "\r\n" - . "\r\n" - . "User: {$this->User[Username]}\r\n" - . "Passkey: {$this->User[torrent_pass]}\r\n" - . "\r\n" - . "Time: $Time\r\n" - . "Used: $Used\r\n" - . "Date: $Date\r\n" - . "\r\n" - . ($FilterStats !== false - ? "Torrent groups analyzed: $this->NumFound\r\n" - . "Torrent groups filtered: $NumSkipped\r\n" - : "") - . "Torrents downloaded: $this->NumAdded\r\n" - . "\r\n" - . "Total size of torrents (ratio hit): ".Format::get_size($this->Size)."\r\n" - . ($NumSkipped - ? "\r\n" - . "Albums unavailable within your criteria (consider making a request for your desired format):\r\n" - . implode("\r\n", $this->SkippedFiles) . "\r\n" - : ""); - } + /** + * Produce a summary text over the collector results + * + * @param bool $FilterStats whether to include filter stats in the report + * @return summary text + */ + public function summary($FilterStats) { + global $ScriptStartTime; + $Time = number_format(1000 * (microtime(true) - $ScriptStartTime), 2)." ms"; + $Used = Format::get_size(memory_get_usage(true)); + $Date = date("M d Y, H:i"); + $NumSkipped = count($this->SkippedFiles); + return "Collector Download Summary for $this->Title - " . SITE_NAME . "\r\n" + . "\r\n" + . "User: {$this->User[Username]}\r\n" + . "Passkey: {$this->User[torrent_pass]}\r\n" + . "\r\n" + . "Time: $Time\r\n" + . "Used: $Used\r\n" + . "Date: $Date\r\n" + . "\r\n" + . ($FilterStats !== false + ? "Torrent groups analyzed: $this->NumFound\r\n" + . "Torrent groups filtered: $NumSkipped\r\n" + : "") + . "Torrents downloaded: $this->NumAdded\r\n" + . "\r\n" + . "Total size of torrents (ratio hit): ".Format::get_size($this->Size)."\r\n" + . ($NumSkipped + ? "\r\n" + . "Albums unavailable within your criteria (consider making a request for your desired format):\r\n" + . implode("\r\n", $this->SkippedFiles) . "\r\n" + : ""); + } - /** - * Compile a list of files that could not be added to the archive - * - * @return list of files - */ - public function errors() { - return "A server error occurred. Please try again at a later time.\r\n" - . "\r\n" - . "The following torrents could not be downloaded:\r\n" - . implode("\r\n", $this->FailedFiles) . "\r\n"; - } + /** + * Compile a list of files that could not be added to the archive + * + * @return list of files + */ + public function errors() { + return "A server error occurred. Please try again at a later time.\r\n" + . "\r\n" + . "The following torrents could not be downloaded:\r\n" + . implode("\r\n", $this->FailedFiles) . "\r\n"; + } - /** - * Combine a bunch of torrent info into a standardized file name - * - * @params most input variables are self-explanatory - * @param int $TorrentID if given, append "-TorrentID" to torrent name - * @param bool $Txt whether to use .txt or .torrent as file extension - * @param int $MaxLength maximum file name length - * @return string file name with at most $MaxLength characters - */ - public static function construct_file_name($Artist, $Album, $Year, $Media, $Format, $Encoding, $TorrentID = false, $Txt = false, $MaxLength = self::MaxPathLength) { - $MaxLength -= ($Txt ? 4 : 8); - if ($TorrentID !== false) { - $MaxLength -= (strlen($TorrentID) + 1); - } - $TorrentArtist = Misc::file_string($Artist); - $TorrentName = Misc::file_string($Album); - if ($Year > 0) { - $TorrentName .= " - $Year"; - } - $TorrentInfo = array(); - if ($Media != '') { - $TorrentInfo[] = $Media; - } - if ($Format != '') { - $TorrentInfo[] = $Format; - } - if ($Encoding != '') { - $TorrentInfo[] = $Encoding; - } - if (!empty($TorrentInfo)) { - $TorrentInfo = ' (' . Misc::file_string(implode(' - ', $TorrentInfo)) . ')'; - } else { - $TorrentInfo = ''; - } + /** + * Combine a bunch of torrent info into a standardized file name + * + * @params most input variables are self-explanatory + * @param int $TorrentID if given, append "-TorrentID" to torrent name + * @param bool $Txt whether to use .txt or .torrent as file extension + * @param int $MaxLength maximum file name length + * @return string file name with at most $MaxLength characters + */ + public static function construct_file_name($Artist, $Album, $Year, $Media, $Format, $Encoding, $TorrentID = false, $Txt = false, $MaxLength = self::MaxPathLength) { + $MaxLength -= ($Txt ? 4 : 8); + if ($TorrentID !== false) { + $MaxLength -= (strlen($TorrentID) + 1); + } + $TorrentArtist = Misc::file_string($Artist); + $TorrentName = Misc::file_string($Album); + if ($Year > 0) { + $TorrentName .= " - $Year"; + } + $TorrentInfo = []; + if ($Media != '') { + $TorrentInfo[] = $Media; + } + if ($Format != '') { + $TorrentInfo[] = $Format; + } + if ($Encoding != '') { + $TorrentInfo[] = $Encoding; + } + if (!empty($TorrentInfo)) { + $TorrentInfo = ' (' . Misc::file_string(implode(' - ', $TorrentInfo)) . ')'; + } else { + $TorrentInfo = ''; + } - if (!$TorrentName) { - $TorrentName = 'No Name'; - } elseif (mb_strlen($TorrentArtist . $TorrentName . $TorrentInfo, 'UTF-8') <= $MaxLength) { - $TorrentName = $TorrentArtist . $TorrentName; - } + if (!$TorrentName) { + $TorrentName = 'No Name'; + } elseif (mb_strlen($TorrentArtist . $TorrentName . $TorrentInfo, 'UTF-8') <= $MaxLength) { + $TorrentName = $TorrentArtist . $TorrentName; + } - $TorrentName = Format::cut_string($TorrentName . $TorrentInfo, $MaxLength, true, false); - if ($TorrentID !== false) { - $TorrentName .= "-$TorrentID"; - } - if ($Txt) { - return "$TorrentName.txt"; - } - return "$TorrentName.torrent"; - } + $TorrentName = Format::cut_string($TorrentName . $TorrentInfo, $MaxLength, true, false); + if ($TorrentID !== false) { + $TorrentName .= "-$TorrentID"; + } + if ($Txt) { + return "$TorrentName.txt"; + } + return "$TorrentName.torrent"; + } - /** - * Convert a stored torrent into a binary file that can be loaded in a torrent client - * - * @param mixed $TorrentData bencoded torrent without announce URL (new format) or TORRENT object (old format) - * @param string $AnnounceURL - * @param int $TorrentID - * @return string bencoded string - */ - public static function get_file($TorrentData, $AnnounceURL, $TorrentID) { - $Tor = new TORRENT($TorrentData); - $Tor->set_announce_url($AnnounceURL); - // We have to sort the dictionary because of the added comment field. - // "Keys must be strings and appear in sorted order" - // https://wiki.theory.org/BitTorrentSpecification - $Tor->Val['comment'] = site_url()."torrents.php?torrentid={$TorrentID}"; - unset($Tor->Val['announce-list']); - unset($Tor->Val['url-list']); - unset($Tor->Val['libtorrent_resume']); - ksort($Tor->Val); - return $Tor->enc(); - } + /** + * Convert a stored torrent into a binary file that can be loaded in a torrent client + * + * @param mixed $TorrentData bencoded torrent without announce URL (new format) or TORRENT object (old format) + * @param string $AnnounceURL + * @param int $TorrentID + * @return string bencoded string + */ + public static function get_file($TorrentData, $AnnounceURL, $TorrentID) { + $Tor = new TORRENT($TorrentData); + $Tor->set_announce_url($AnnounceURL); + // We have to sort the dictionary because of the added comment field. + // "Keys must be strings and appear in sorted order" + // https://wiki.theory.org/BitTorrentSpecification + $Tor->Val['comment'] = site_url()."torrents.php?torrentid={$TorrentID}"; + unset($Tor->Val['announce-list']); + unset($Tor->Val['url-list']); + unset($Tor->Val['libtorrent_resume']); + ksort($Tor->Val); + return $Tor->enc(); + } } diff --git a/classes/torrentsearch.class.php b/classes/torrentsearch.class.php index d5dec07ee..a7c7f72c7 100644 --- a/classes/torrentsearch.class.php +++ b/classes/torrentsearch.class.php @@ -1,769 +1,769 @@ - attribute name for ungrouped torrent page - */ - public static $SortOrders = array( - 'year' => 'year', - 'time' => 'id', - 'size' => 'size', - 'seeders' => 'seeders', - 'leechers' => 'leechers', - 'snatched' => 'snatched', - 'random' => 1); - - /** - * Map of sort mode => attribute name for grouped torrent page - */ - private static $SortOrdersGrouped = array( - 'year' => 'year', - 'time' => 'id', - 'size' => 'maxsize', - 'seeders' => 'sumseeders', - 'leechers' => 'sumleechers', - 'snatched' => 'sumsnatched', - 'random' => 1); - - /** - * Map of sort mode => aggregate expression required for some grouped sort orders - */ - private static $AggregateExp = array( - 'size' => 'MAX(size) AS maxsize', - 'seeders' => 'SUM(seeders) AS sumseeders', - 'leechers' => 'SUM(leechers) AS sumleechers', - 'snatched' => 'SUM(snatched) AS sumsnatched'); - - /** - * Map of attribute name => global variable name with list of values that can be used for filtering - */ - private static $Attributes = array( - 'filter_cat' => false, - 'releasetype' => 'ReleaseTypes', - 'freetorrent' => false, - 'hascue' => false, - 'haslog' => false, - 'scene' => false, - 'vanityhouse' => false, - 'year' => false); - - /** - * List of fields that can be used for fulltext searches - */ - private static $Fields = array( - 'artistname' => 1, - 'cataloguenumber' => 1, - 'description' => 1, - 'encoding' => 1, - 'filelist' => 1, - 'format' => 1, - 'groupname' => 1, - 'media' => 1, - 'recordlabel' => 1, - 'remastercataloguenumber' => 1, - 'remasterrecordlabel' => 1, - 'remastertitle' => 1, - 'remasteryear' => 1, - 'searchstr' => 1, - 'taglist' => 1); - - /** - * List of torrent-specific fields that can be used for filtering - */ - private static $TorrentFields = array( - 'description' => 1, - 'encoding' => 1, - 'filelist' => 1, - 'format' => 1, - 'media' => 1, - 'remastercataloguenumber' => 1, - 'remasterrecordlabel' => 1, - 'remastertitle' => 1, - 'remasteryear' => 1); - - /** - * Some form field names don't match the ones in the index - */ - private static $FormsToFields = array( - 'searchstr' => '(groupname,artistname,yearfulltext)'); - - /** - * Specify the operator type to use for fields. Empty key sets the default - */ - private static $FieldOperators = array( - '' => self::SPH_BOOL_AND, - 'encoding' => self::SPH_BOOL_OR, - 'format' => self::SPH_BOOL_OR, - 'media' => self::SPH_BOOL_OR); - - /** - * Specify the separator character to use for fields. Empty key sets the default - */ - private static $FieldSeparators = array( - '' => ' ', - 'encoding' => '|', - 'format' => '|', - 'media' => '|', - 'taglist' => ','); - - /** - * Primary SphinxqlQuery object used to get group IDs or torrent IDs for ungrouped searches - */ - private $SphQL; - - /** - * Second SphinxqlQuery object used to get torrent IDs if torrent-specific fulltext filters are used - */ - private $SphQLTor; - - /** - * Ordered result array or false if query resulted in an error - */ - private $SphResults; - - /** - * Requested page - */ - private $Page; - - /** - * Number of results per page - */ - private $PageSize; - - /** - * Number of results - */ - private $NumResults = 0; - - /** - * Array with info from all matching torrent groups - */ - private $Groups = array(); - - /** - * True if the NOT operator can be used. Sphinx needs at least one positive search condition - */ - private $EnableNegation = false; - - /** - * Whether any filters were used - */ - private $Filtered = false; - - /** - * Whether the random sort order is selected - */ - private $Random = false; - - /** - * Storage for fulltext search terms - * ['Field name' => [ - * 'include' => [], - * 'exclude' => [], - * 'operator' => self::SPH_BOOL_AND | self::SPH_BOOL_OR - * ]], ... - */ - private $Terms = array(); - - /** - * Unprocessed search terms for retrieval - */ - private $RawTerms = array(); - - /** - * Storage for used torrent-specific attribute filters - * ['Field name' => 'Search expression', ...] - */ - private $UsedTorrentAttrs = array(); - - /** - * Storage for used torrent-specific fulltext fields - * ['Field name' => 'Search expression', ...] - */ - private $UsedTorrentFields = array(); - - /** - * Initialize and configure a TorrentSearch object - * - * @param bool $GroupResults whether results should be grouped by group id - * @param string $OrderBy attribute to use for sorting the results - * @param string $OrderWay Whether to use ascending or descending order - * @param int $Page Page number to display - * @param int $PageSize Number of results per page - */ - public function __construct($GroupResults, $OrderBy, $OrderWay, $Page, $PageSize) { - if ($GroupResults && !isset(self::$SortOrdersGrouped[$OrderBy]) - || !$GroupResults && !isset(self::$SortOrders[$OrderBy]) - || !in_array($OrderWay, array('asc', 'desc')) - ) { - global $Debug; - $ErrMsg = "TorrentSearch constructor arguments:\n" . print_r(func_get_args(), true); - $Debug->analysis('Bad arguments in TorrentSearch constructor', $ErrMsg, 3600*24); - error('-1'); - } - if (!is_number($Page) || $Page < 1) { - $Page = 1; - } - if (check_perms('site_search_many')) { - $this->Page = $Page; - } else { - $this->Page = min($Page, SPHINX_MAX_MATCHES / $PageSize); - } - $ResultLimit = $PageSize; - $this->PageSize = $PageSize; - $this->GroupResults = $GroupResults; - $this->SphQL = new SphinxqlQuery(); - if ($OrderBy === 'random') { - $this->SphQL->select('id, groupid') - ->order_by('RAND()', ''); - $this->Random = true; - $this->Page = 1; - if ($GroupResults) { - // Get more results because ORDER BY RAND() can't be used in GROUP BY queries - $ResultLimit *= 5; - } - } elseif ($GroupResults) { - $Select = 'groupid'; - if (isset(self::$AggregateExp[$OrderBy])) { - $Select .= ', ' . self::$AggregateExp[$OrderBy]; - } - $this->SphQL->select($Select) - ->group_by('groupid') - ->order_group_by(self::$SortOrdersGrouped[$OrderBy], $OrderWay) - ->order_by(self::$SortOrdersGrouped[$OrderBy], $OrderWay); - } else { - $this->SphQL->select('id, groupid') - ->order_by(self::$SortOrders[$OrderBy], $OrderWay); - } - $Offset = ($this->Page - 1) * $ResultLimit; - $MaxMatches = $Offset + $ResultLimit; - $this->SphQL->from('torrents, delta') - ->limit($Offset, $ResultLimit, $MaxMatches); - } - - /** - * Process search terms and run the main query - * - * @param array $Terms Array containing all search terms (e.g. $_GET) - * @return array List of matching group IDs with torrent ID as key for ungrouped results - */ - public function query($Terms = array()) { - $this->process_search_terms($Terms); - $this->build_query(); - $this->run_query(); - $this->process_results(); - return $this->SphResults; - } - - /** - * Internal function that runs the queries needed to get the desired results - */ - private function run_query() { - $SphQLResult = $this->SphQL->query(); - if ($SphQLResult->Errno > 0) { - $this->SphResults = false; - return; - } - if ($this->Random && $this->GroupResults) { - $TotalCount = $SphQLResult->get_meta('total_found'); - $this->SphResults = $SphQLResult->collect('groupid'); - $GroupIDs = array_keys($this->SphResults); - $GroupCount = count($GroupIDs); - while ($SphQLResult->get_meta('total') < $TotalCount && $GroupCount < $this->PageSize) { - // Make sure we get $PageSize results, or all of them if there are less than $PageSize hits - $this->SphQL->where('groupid', $GroupIDs, true); - $SphQLResult = $this->SphQL->query(); - if (!$SphQLResult->has_results()) { - break; - } - $this->SphResults += $SphQLResult->collect('groupid'); - $GroupIDs = array_keys($this->SphResults); - $GroupCount = count($GroupIDs); - } - if ($GroupCount > $this->PageSize) { - $this->SphResults = array_slice($this->SphResults, 0, $this->PageSize, true); - } - $this->NumResults = count($this->SphResults); - } else { - $this->NumResults = (int)$SphQLResult->get_meta('total_found'); - if ($this->GroupResults) { - $this->SphResults = $SphQLResult->collect('groupid'); - } else { - $this->SphResults = $SphQLResult->to_pair('id', 'groupid'); - } - } - } - - /** - * Process search terms and store the parts in appropriate arrays until we know if - * the NOT operator can be used - */ - private function build_query() { - foreach ($this->Terms as $Field => $Words) { - $SearchString = ''; - if (isset(self::$FormsToFields[$Field])) { - $Field = self::$FormsToFields[$Field]; - } - $QueryParts = array('include' => array(), 'exclude' => array()); - if (!$this->EnableNegation && !empty($Words['exclude'])) { - $Words['include'] = $Words['exclude']; - unset($Words['exclude']); - } - if (!empty($Words['include'])) { - foreach ($Words['include'] as $Word) { - $QueryParts['include'][] = Sphinxql::sph_escape_string($Word); - } - } - if (!empty($Words['exclude'])) { - foreach ($Words['exclude'] as $Word) { - $QueryParts['exclude'][] = '!' . Sphinxql::sph_escape_string(substr($Word, 1)); - } - } - if (!empty($QueryParts)) { - if (isset($Words['operator'])) { - // Is the operator already specified? - $Operator = $Words['operator']; - } elseif(isset(self::$FieldOperators[$Field])) { - // Does this field have a non-standard operator? - $Operator = self::$FieldOperators[$Field]; - } else { - // Go for the default operator - $Operator = self::$FieldOperators['']; - } - if (!empty($QueryParts['include'])) { - $SearchString .= '( ' . implode($Operator, $QueryParts['include']) . ' ) '; - } - if (!empty($QueryParts['exclude'])) { - $SearchString .= implode(' ', $QueryParts['exclude']); - } - $this->SphQL->where_match($SearchString, $Field, false); - if (isset(self::$TorrentFields[$Field])) { - $this->UsedTorrentFields[$Field] = $SearchString; - } - $this->Filtered = true; - } - } - } - - /** - * Look at each search term and figure out what to do with it - * - * @param array $Terms Array with search terms from query() - */ - private function process_search_terms($Terms) { - foreach ($Terms as $Key => $Term) { - if (isset(self::$Fields[$Key])) { - $this->process_field($Key, $Term); - } elseif (isset(self::$Attributes[$Key])) { - $this->process_attribute($Key, $Term); - } - $this->RawTerms[$Key] = $Term; - } - $this->post_process_fields(); - } - - /** - * Process attribute filters and store them in case we need to post-process grouped results - * - * @param string $Attribute Name of the attribute to filter against - * @param mixed $Value The filter's condition for a match - */ - private function process_attribute($Attribute, $Value) { - if ($Value === '') { - return; - } - switch ($Attribute) { - case 'year': - if (!$this->search_year($Value)) { - return; - } - break; - - case 'haslog': - if ($Value == 0) { - $this->SphQL->where('haslog', 0); - } elseif ($Value == 100) { - $this->SphQL->where('logscore', 100); - } elseif ($Value < 0) { - $this->SphQL->where_lt('logscore', 100); - $this->SphQL->where('haslog', 1); - } else { - $this->SphQL->where('haslog', 1); - } - $this->UsedTorrentAttrs['haslog'] = $Value; - break; - - case 'freetorrent': - if ($Value == 3) { - $this->SphQL->where('freetorrent', 0, true); - $this->UsedTorrentAttrs['freetorrent'] = 3; - } elseif ($Value >= 0 && $Value < 3) { - $this->SphQL->where('freetorrent', $Value); - $this->UsedTorrentAttrs[$Attribute] = $Value; - } else { - return; - } - break; - - case 'filter_cat': - if (!is_array($Value)) { - $Value = array_fill_keys(explode('|', $Value), 1); - } - $CategoryFilter = array(); - foreach (array_keys($Value) as $Category) { - if (is_number($Category)) { - $CategoryFilter[] = $Category; - } else { - global $Categories; - $ValidValues = array_map('strtolower', $Categories); - if (($CategoryID = array_search(strtolower($Category), $ValidValues)) !== false) { - $CategoryFilter[] = $CategoryID + 1; - } - } - } - if (empty($CategoryFilter)) { - $CategoryFilter = 0; - } - $this->SphQL->where('categoryid', $CategoryFilter); - break; - - default: - if (!is_number($Value) && self::$Attributes[$Attribute] !== false) { - // Check if the submitted value can be converted to a valid one - $ValidValuesVarname = self::$Attributes[$Attribute]; - global $$ValidValuesVarname; - $ValidValues = array_map('strtolower', $$ValidValuesVarname); - if (($Value = array_search(strtolower($Value), $ValidValues)) === false) { - // Force the query to return 0 results if value is still invalid - $Value = max(array_keys($ValidValues)) + 1; - } - } - $this->SphQL->where($Attribute, $Value); - $this->UsedTorrentAttrs[$Attribute] = $Value; - break; - } - $this->Filtered = true; - } - - /** - * Look at a fulltext search term and figure out if it needs special treatment - * - * @param string $Field Name of the search field - * @param string $Term Search expression for the field - */ - private function process_field($Field, $Term) { - $Term = trim($Term); - if ($Term === '') { - return; - } - if ($Field === 'searchstr') { - $this->search_basic($Term); - } elseif ($Field === 'filelist') { - $this->search_filelist($Term); - } elseif ($Field === 'taglist') { - $this->search_taglist($Term); - } else { - $this->add_field($Field, $Term); - } - } - - /** - * Some fields may require post-processing - */ - private function post_process_fields() { - if (isset($this->Terms['taglist'])) { - // Replace bad tags with tag aliases - $this->Terms['taglist'] = Tags::remove_aliases($this->Terms['taglist']); - if (isset($this->RawTerms['tags_type']) && (int)$this->RawTerms['tags_type'] === self::TAGS_ANY) { - $this->Terms['taglist']['operator'] = self::SPH_BOOL_OR; - } - // Update the RawTerms array so get_terms() can return the corrected search terms - if (isset($this->Terms['taglist']['include'])) { - $AllTags = $this->Terms['taglist']['include']; - } else { - $AllTags = array(); - } - if (isset($this->Terms['taglist']['exclude'])) { - $AllTags = array_merge($AllTags, $this->Terms['taglist']['exclude']); - } - $this->RawTerms['taglist'] = str_replace('_', '.', implode(', ', $AllTags)); - } - } - - /** - * Handle magic keywords in the basic torrent search - * - * @param string $Term Given search expression - */ - private function search_basic($Term) { - global $Bitrates, $Formats, $Media; - $SearchBitrates = array_map('strtolower', $Bitrates); - array_push($SearchBitrates, 'v0', 'v1', 'v2', '24bit'); - $SearchFormats = array_map('strtolower', $Formats); - $SearchMedia = array_map('strtolower', $Media); - - foreach (explode(' ', $Term) as $Word) { - if (in_array($Word, $SearchBitrates)) { - $this->add_word('encoding', $Word); - } elseif (in_array($Word, $SearchFormats)) { - $this->add_word('format', $Word); - } elseif (in_array($Word, $SearchMedia)) { - $this->add_word('media', $Word); - } elseif ($Word === '100%') { - $this->process_attribute('haslog', 100); - } elseif ($Word === '!100%') { - $this->process_attribute('haslog', -1); - } else { - $this->add_word('searchstr', $Word); - } - } - } - - /** - * Use phrase boundary for file searches to make sure we don't count - * partial hits from multiple files - * - * @param string $Term Given search expression - */ - private function search_filelist($Term) { - $SearchString = '"' . Sphinxql::sph_escape_string($Term) . '"~20'; - $this->SphQL->where_match($SearchString, 'filelist', false); - $this->UsedTorrentFields['filelist'] = $SearchString; - $this->EnableNegation = true; - $this->Filtered = true; - } - - /** - * Prepare tag searches before sending them to the normal treatment - * - * @param string $Term Given search expression - */ - private function search_taglist($Term) { - $Term = strtr($Term, '.', '_'); - $this->add_field('taglist', $Term); - } - - /** - * The year filter accepts a range. Figure out how to handle the filter value - * - * @param string $Term Filter condition. Can be an integer or a range with the format X-Y - * @return bool True if parameters are valid - */ - private function search_year($Term) { - $Years = explode('-', $Term); - if (count($Years) === 1 && is_number($Years[0])) { - // Exact year - $this->SphQL->where('year', $Years[0]); - } elseif (count($Years) === 2) { - if (empty($Years[0]) && is_number($Years[1])) { - // Range: 0 - 2005 - $this->SphQL->where_lt('year', $Years[1], true); - } elseif (empty($Years[1]) && is_number($Years[0])) { - // Range: 2005 - 2^32-1 - $this->SphQL->where_gt('year', $Years[0], true); - } elseif (is_number($Years[0]) && is_number($Years[1])) { - // Range: 2005 - 2009 - $this->SphQL->where_between('year', array(min($Years), max($Years))); - } else { - // Invalid input - return false; - } - } else { - // Invalid input - return false; - } - return true; - } - - /** - * Add a field filter that doesn't need special treatment - * - * @param string $Field Name of the search field - * @param string $Term Search expression for the field - */ - private function add_field($Field, $Term) { - if (isset(self::$FieldSeparators[$Field])) { - $Separator = self::$FieldSeparators[$Field]; - } else { - $Separator = self::$FieldSeparators['']; - } - $Words = explode($Separator, $Term); - foreach ($Words as $Word) { - $this->add_word($Field, $Word); - } - } - - /** - * Add a keyword to the array of search terms - * - * @param string $Field Name of the search field - * @param string $Word Keyword - */ - private function add_word($Field, $Word) { - $Word = trim($Word); - // Skip isolated hyphens to enable "Artist - Title" searches - if ($Word === '' || $Word === '-') { - return; - } - if ($Word[0] === '!' && strlen($Word) >= 2 && strpos($Word, '!', 1) === false) { - $this->Terms[$Field]['exclude'][] = $Word; - } else { - $this->Terms[$Field]['include'][] = $Word; - $this->EnableNegation = true; - } - } - - /** - * @return array Torrent group information for the matches from Torrents::get_groups - */ - public function get_groups() { - return $this->Groups; - } - - /** - * @param string $Type Field or attribute name - * @return string Unprocessed search terms - */ - public function get_terms($Type) { - return isset($this->RawTerms[$Type]) ? $this->RawTerms[$Type] : ''; - } - - /** - * @return int Result count - */ - public function record_count() { - return $this->NumResults; - } - - /** - * @return bool Whether any filters were used - */ - public function has_filters() { - return $this->Filtered; - } - - /** - * @return bool Whether any torrent-specific fulltext filters were used - */ - public function need_torrent_ft() { - return $this->GroupResults && $this->NumResults > 0 && !empty($this->UsedTorrentFields); - } - - /** - * Get torrent group info and remove any torrents that don't match - */ - private function process_results() { - if (count($this->SphResults) == 0) { - return; - } - $this->Groups = Torrents::get_groups($this->SphResults); - if ($this->need_torrent_ft()) { - // Query Sphinx for torrent IDs if torrent-specific fulltext filters were used - $this->filter_torrents_sph(); - } elseif ($this->GroupResults) { - // Otherwise, let PHP discard unmatching torrents - $this->filter_torrents_internal(); - } - // Ungrouped searches don't need any additional filtering - } - - /** - * Build and run a query that gets torrent IDs from Sphinx when fulltext filters - * were used to get primary results and they are grouped - */ - private function filter_torrents_sph() { - $AllTorrents = array(); - foreach ($this->Groups as $GroupID => $Group) { - if (!empty($Group['Torrents'])) { - $AllTorrents += array_fill_keys(array_keys($Group['Torrents']), $GroupID); - } - } - $TorrentCount = count($AllTorrents); - $this->SphQLTor = new SphinxqlQuery(); - $this->SphQLTor->select('id')->from('torrents, delta'); - foreach ($this->UsedTorrentFields as $Field => $Term) { - $this->SphQLTor->where_match($Term, $Field, false); - } - $this->SphQLTor->copy_attributes_from($this->SphQL); - $this->SphQLTor->where('id', array_keys($AllTorrents))->limit(0, $TorrentCount, $TorrentCount); - $SphQLResultTor = $this->SphQLTor->query(); - $MatchingTorrentIDs = $SphQLResultTor->to_pair('id', 'id'); - foreach ($AllTorrents as $TorrentID => $GroupID) { - if (!isset($MatchingTorrentIDs[$TorrentID])) { - unset($this->Groups[$GroupID]['Torrents'][$TorrentID]); - } - } - } - - /** - * Non-Sphinx method of collecting IDs of torrents that match any - * torrent-specific attribute filters that were used in the search query - */ - private function filter_torrents_internal() { - foreach ($this->Groups as $GroupID => $Group) { - if (empty($Group['Torrents'])) { - continue; - } - foreach ($Group['Torrents'] as $TorrentID => $Torrent) { - if (!$this->filter_torrent_internal($Torrent)) { - unset($this->Groups[$GroupID]['Torrents'][$TorrentID]); - } - } - } - } - - /** - * Post-processing to determine if a torrent is a real hit or if it was - * returned because another torrent in the group matched. Only used if - * there are no torrent-specific fulltext conditions - * - * @param array $Torrent Torrent array, probably from Torrents::get_groups() - * @return bool True if it's a real hit - */ - private function filter_torrent_internal($Torrent) { - if (isset($this->UsedTorrentAttrs['freetorrent'])) { - $FilterValue = $this->UsedTorrentAttrs['freetorrent']; - if ($FilterValue == '3' && $Torrent['FreeTorrent'] == '0') { - // Either FL or NL is ok - return false; - } elseif ($FilterValue != '3' && $FilterValue != (int)$Torrent['FreeTorrent']) { - return false; - } - } - if (isset($this->UsedTorrentAttrs['hascue'])) { - if ($this->UsedTorrentAttrs['hascue'] != (int)$Torrent['HasCue']) { - return false; - } - } - if (isset($this->UsedTorrentAttrs['haslog'])) { - $FilterValue = $this->UsedTorrentAttrs['haslog']; - if ($FilterValue == '0') { - // No logs - $Pass = empty($Torrent['HasLog']); - } elseif ($FilterValue == '100') { - // 100% logs - $Pass = $Torrent['LogScore'] == '100'; - } elseif ($FilterValue < 0) { - // Unscored or <100% logs - $Pass = !empty($Torrent['HasLog']) && $Torrent['LogScore'] != '100'; - } else { - // Any log score - $Pass = $Torrent['HasLog'] == '1'; - } - if (!$Pass) { - return false; - } - } - if (isset($this->UsedTorrentAttrs['scene'])) { - if ($this->UsedTorrentAttrs['scene'] != (int)$Torrent['Scene']) { - return false; - } - } - return true; - } + const TAGS_ANY = 0; + const TAGS_ALL = 1; + const SPH_BOOL_AND = ' '; + const SPH_BOOL_OR = ' | '; + + /** + * Map of sort mode => attribute name for ungrouped torrent page + */ + public static $SortOrders = array( + 'year' => 'year', + 'time' => 'id', + 'size' => 'size', + 'seeders' => 'seeders', + 'leechers' => 'leechers', + 'snatched' => 'snatched', + 'random' => 1); + + /** + * Map of sort mode => attribute name for grouped torrent page + */ + private static $SortOrdersGrouped = array( + 'year' => 'year', + 'time' => 'id', + 'size' => 'maxsize', + 'seeders' => 'sumseeders', + 'leechers' => 'sumleechers', + 'snatched' => 'sumsnatched', + 'random' => 1); + + /** + * Map of sort mode => aggregate expression required for some grouped sort orders + */ + private static $AggregateExp = array( + 'size' => 'MAX(size) AS maxsize', + 'seeders' => 'SUM(seeders) AS sumseeders', + 'leechers' => 'SUM(leechers) AS sumleechers', + 'snatched' => 'SUM(snatched) AS sumsnatched'); + + /** + * Map of attribute name => global variable name with list of values that can be used for filtering + */ + private static $Attributes = array( + 'filter_cat' => false, + 'releasetype' => 'ReleaseTypes', + 'freetorrent' => false, + 'hascue' => false, + 'haslog' => false, + 'scene' => false, + 'vanityhouse' => false, + 'year' => false); + + /** + * List of fields that can be used for fulltext searches + */ + private static $Fields = array( + 'artistname' => 1, + 'cataloguenumber' => 1, + 'description' => 1, + 'encoding' => 1, + 'filelist' => 1, + 'format' => 1, + 'groupname' => 1, + 'media' => 1, + 'recordlabel' => 1, + 'remastercataloguenumber' => 1, + 'remasterrecordlabel' => 1, + 'remastertitle' => 1, + 'remasteryear' => 1, + 'searchstr' => 1, + 'taglist' => 1); + + /** + * List of torrent-specific fields that can be used for filtering + */ + private static $TorrentFields = array( + 'description' => 1, + 'encoding' => 1, + 'filelist' => 1, + 'format' => 1, + 'media' => 1, + 'remastercataloguenumber' => 1, + 'remasterrecordlabel' => 1, + 'remastertitle' => 1, + 'remasteryear' => 1); + + /** + * Some form field names don't match the ones in the index + */ + private static $FormsToFields = array( + 'searchstr' => '(groupname,artistname,yearfulltext)'); + + /** + * Specify the operator type to use for fields. Empty key sets the default + */ + private static $FieldOperators = array( + '' => self::SPH_BOOL_AND, + 'encoding' => self::SPH_BOOL_OR, + 'format' => self::SPH_BOOL_OR, + 'media' => self::SPH_BOOL_OR); + + /** + * Specify the separator character to use for fields. Empty key sets the default + */ + private static $FieldSeparators = array( + '' => ' ', + 'encoding' => '|', + 'format' => '|', + 'media' => '|', + 'taglist' => ','); + + /** + * Primary SphinxqlQuery object used to get group IDs or torrent IDs for ungrouped searches + */ + private $SphQL; + + /** + * Second SphinxqlQuery object used to get torrent IDs if torrent-specific fulltext filters are used + */ + private $SphQLTor; + + /** + * Ordered result array or false if query resulted in an error + */ + private $SphResults; + + /** + * Requested page + */ + private $Page; + + /** + * Number of results per page + */ + private $PageSize; + + /** + * Number of results + */ + private $NumResults = 0; + + /** + * Array with info from all matching torrent groups + */ + private $Groups = array(); + + /** + * True if the NOT operator can be used. Sphinx needs at least one positive search condition + */ + private $EnableNegation = false; + + /** + * Whether any filters were used + */ + private $Filtered = false; + + /** + * Whether the random sort order is selected + */ + private $Random = false; + + /** + * Storage for fulltext search terms + * ['Field name' => [ + * 'include' => [], + * 'exclude' => [], + * 'operator' => self::SPH_BOOL_AND | self::SPH_BOOL_OR + * ]], ... + */ + private $Terms = array(); + + /** + * Unprocessed search terms for retrieval + */ + private $RawTerms = array(); + + /** + * Storage for used torrent-specific attribute filters + * ['Field name' => 'Search expression', ...] + */ + private $UsedTorrentAttrs = array(); + + /** + * Storage for used torrent-specific fulltext fields + * ['Field name' => 'Search expression', ...] + */ + private $UsedTorrentFields = array(); + + /** + * Initialize and configure a TorrentSearch object + * + * @param bool $GroupResults whether results should be grouped by group id + * @param string $OrderBy attribute to use for sorting the results + * @param string $OrderWay Whether to use ascending or descending order + * @param int $Page Page number to display + * @param int $PageSize Number of results per page + */ + public function __construct($GroupResults, $OrderBy, $OrderWay, $Page, $PageSize) { + if ($GroupResults && !isset(self::$SortOrdersGrouped[$OrderBy]) + || !$GroupResults && !isset(self::$SortOrders[$OrderBy]) + || !in_array($OrderWay, array('asc', 'desc')) + ) { + global $Debug; + $ErrMsg = "TorrentSearch constructor arguments:\n" . print_r(func_get_args(), true); + $Debug->analysis('Bad arguments in TorrentSearch constructor', $ErrMsg, 3600*24); + error('-1'); + } + if (!is_number($Page) || $Page < 1) { + $Page = 1; + } + if (check_perms('site_search_many')) { + $this->Page = $Page; + } else { + $this->Page = min($Page, SPHINX_MAX_MATCHES / $PageSize); + } + $ResultLimit = $PageSize; + $this->PageSize = $PageSize; + $this->GroupResults = $GroupResults; + $this->SphQL = new SphinxqlQuery(); + if ($OrderBy === 'random') { + $this->SphQL->select('id, groupid') + ->order_by('RAND()', ''); + $this->Random = true; + $this->Page = 1; + if ($GroupResults) { + // Get more results because ORDER BY RAND() can't be used in GROUP BY queries + $ResultLimit *= 5; + } + } elseif ($GroupResults) { + $Select = 'groupid'; + if (isset(self::$AggregateExp[$OrderBy])) { + $Select .= ', ' . self::$AggregateExp[$OrderBy]; + } + $this->SphQL->select($Select) + ->group_by('groupid') + ->order_group_by(self::$SortOrdersGrouped[$OrderBy], $OrderWay) + ->order_by(self::$SortOrdersGrouped[$OrderBy], $OrderWay); + } else { + $this->SphQL->select('id, groupid') + ->order_by(self::$SortOrders[$OrderBy], $OrderWay); + } + $Offset = ($this->Page - 1) * $ResultLimit; + $MaxMatches = $Offset + $ResultLimit; + $this->SphQL->from('torrents, delta') + ->limit($Offset, $ResultLimit, $MaxMatches); + } + + /** + * Process search terms and run the main query + * + * @param array $Terms Array containing all search terms (e.g. $_GET) + * @return array List of matching group IDs with torrent ID as key for ungrouped results + */ + public function query($Terms = array()) { + $this->process_search_terms($Terms); + $this->build_query(); + $this->run_query(); + $this->process_results(); + return $this->SphResults; + } + + /** + * Internal function that runs the queries needed to get the desired results + */ + private function run_query() { + $SphQLResult = $this->SphQL->query(); + if ($SphQLResult->Errno > 0) { + $this->SphResults = false; + return; + } + if ($this->Random && $this->GroupResults) { + $TotalCount = $SphQLResult->get_meta('total_found'); + $this->SphResults = $SphQLResult->collect('groupid'); + $GroupIDs = array_keys($this->SphResults); + $GroupCount = count($GroupIDs); + while ($SphQLResult->get_meta('total') < $TotalCount && $GroupCount < $this->PageSize) { + // Make sure we get $PageSize results, or all of them if there are less than $PageSize hits + $this->SphQL->where('groupid', $GroupIDs, true); + $SphQLResult = $this->SphQL->query(); + if (!$SphQLResult->has_results()) { + break; + } + $this->SphResults += $SphQLResult->collect('groupid'); + $GroupIDs = array_keys($this->SphResults); + $GroupCount = count($GroupIDs); + } + if ($GroupCount > $this->PageSize) { + $this->SphResults = array_slice($this->SphResults, 0, $this->PageSize, true); + } + $this->NumResults = count($this->SphResults); + } else { + $this->NumResults = (int)$SphQLResult->get_meta('total_found'); + if ($this->GroupResults) { + $this->SphResults = $SphQLResult->collect('groupid'); + } else { + $this->SphResults = $SphQLResult->to_pair('id', 'groupid'); + } + } + } + + /** + * Process search terms and store the parts in appropriate arrays until we know if + * the NOT operator can be used + */ + private function build_query() { + foreach ($this->Terms as $Field => $Words) { + $SearchString = ''; + if (isset(self::$FormsToFields[$Field])) { + $Field = self::$FormsToFields[$Field]; + } + $QueryParts = array('include' => array(), 'exclude' => array()); + if (!$this->EnableNegation && !empty($Words['exclude'])) { + $Words['include'] = $Words['exclude']; + unset($Words['exclude']); + } + if (!empty($Words['include'])) { + foreach ($Words['include'] as $Word) { + $QueryParts['include'][] = Sphinxql::sph_escape_string($Word); + } + } + if (!empty($Words['exclude'])) { + foreach ($Words['exclude'] as $Word) { + $QueryParts['exclude'][] = '!' . Sphinxql::sph_escape_string(substr($Word, 1)); + } + } + if (!empty($QueryParts)) { + if (isset($Words['operator'])) { + // Is the operator already specified? + $Operator = $Words['operator']; + } elseif(isset(self::$FieldOperators[$Field])) { + // Does this field have a non-standard operator? + $Operator = self::$FieldOperators[$Field]; + } else { + // Go for the default operator + $Operator = self::$FieldOperators['']; + } + if (!empty($QueryParts['include'])) { + $SearchString .= '( ' . implode($Operator, $QueryParts['include']) . ' ) '; + } + if (!empty($QueryParts['exclude'])) { + $SearchString .= implode(' ', $QueryParts['exclude']); + } + $this->SphQL->where_match($SearchString, $Field, false); + if (isset(self::$TorrentFields[$Field])) { + $this->UsedTorrentFields[$Field] = $SearchString; + } + $this->Filtered = true; + } + } + } + + /** + * Look at each search term and figure out what to do with it + * + * @param array $Terms Array with search terms from query() + */ + private function process_search_terms($Terms) { + foreach ($Terms as $Key => $Term) { + if (isset(self::$Fields[$Key])) { + $this->process_field($Key, $Term); + } elseif (isset(self::$Attributes[$Key])) { + $this->process_attribute($Key, $Term); + } + $this->RawTerms[$Key] = $Term; + } + $this->post_process_fields(); + } + + /** + * Process attribute filters and store them in case we need to post-process grouped results + * + * @param string $Attribute Name of the attribute to filter against + * @param mixed $Value The filter's condition for a match + */ + private function process_attribute($Attribute, $Value) { + if ($Value === '') { + return; + } + switch ($Attribute) { + case 'year': + if (!$this->search_year($Value)) { + return; + } + break; + + case 'haslog': + if ($Value == 0) { + $this->SphQL->where('haslog', 0); + } elseif ($Value == 100) { + $this->SphQL->where('logscore', 100); + } elseif ($Value < 0) { + $this->SphQL->where_lt('logscore', 100); + $this->SphQL->where('haslog', 1); + } else { + $this->SphQL->where('haslog', 1); + } + $this->UsedTorrentAttrs['haslog'] = $Value; + break; + + case 'freetorrent': + if ($Value == 3) { + $this->SphQL->where('freetorrent', 0, true); + $this->UsedTorrentAttrs['freetorrent'] = 3; + } elseif ($Value >= 0 && $Value < 3) { + $this->SphQL->where('freetorrent', $Value); + $this->UsedTorrentAttrs[$Attribute] = $Value; + } else { + return; + } + break; + + case 'filter_cat': + if (!is_array($Value)) { + $Value = array_fill_keys(explode('|', $Value), 1); + } + $CategoryFilter = array(); + foreach (array_keys($Value) as $Category) { + if (is_number($Category)) { + $CategoryFilter[] = $Category; + } else { + global $Categories; + $ValidValues = array_map('strtolower', $Categories); + if (($CategoryID = array_search(strtolower($Category), $ValidValues)) !== false) { + $CategoryFilter[] = $CategoryID + 1; + } + } + } + if (empty($CategoryFilter)) { + $CategoryFilter = 0; + } + $this->SphQL->where('categoryid', $CategoryFilter); + break; + + default: + if (!is_number($Value) && self::$Attributes[$Attribute] !== false) { + // Check if the submitted value can be converted to a valid one + $ValidValuesVarname = self::$Attributes[$Attribute]; + global $$ValidValuesVarname; + $ValidValues = array_map('strtolower', $$ValidValuesVarname); + if (($Value = array_search(strtolower($Value), $ValidValues)) === false) { + // Force the query to return 0 results if value is still invalid + $Value = max(array_keys($ValidValues)) + 1; + } + } + $this->SphQL->where($Attribute, $Value); + $this->UsedTorrentAttrs[$Attribute] = $Value; + break; + } + $this->Filtered = true; + } + + /** + * Look at a fulltext search term and figure out if it needs special treatment + * + * @param string $Field Name of the search field + * @param string $Term Search expression for the field + */ + private function process_field($Field, $Term) { + $Term = trim($Term); + if ($Term === '') { + return; + } + if ($Field === 'searchstr') { + $this->search_basic($Term); + } elseif ($Field === 'filelist') { + $this->search_filelist($Term); + } elseif ($Field === 'taglist') { + $this->search_taglist($Term); + } else { + $this->add_field($Field, $Term); + } + } + + /** + * Some fields may require post-processing + */ + private function post_process_fields() { + if (isset($this->Terms['taglist'])) { + // Replace bad tags with tag aliases + $this->Terms['taglist'] = Tags::remove_aliases($this->Terms['taglist']); + if (isset($this->RawTerms['tags_type']) && (int)$this->RawTerms['tags_type'] === self::TAGS_ANY) { + $this->Terms['taglist']['operator'] = self::SPH_BOOL_OR; + } + // Update the RawTerms array so get_terms() can return the corrected search terms + if (isset($this->Terms['taglist']['include'])) { + $AllTags = $this->Terms['taglist']['include']; + } else { + $AllTags = array(); + } + if (isset($this->Terms['taglist']['exclude'])) { + $AllTags = array_merge($AllTags, $this->Terms['taglist']['exclude']); + } + $this->RawTerms['taglist'] = str_replace('_', '.', implode(', ', $AllTags)); + } + } + + /** + * Handle magic keywords in the basic torrent search + * + * @param string $Term Given search expression + */ + private function search_basic($Term) { + global $Bitrates, $Formats, $Media; + $SearchBitrates = array_map('strtolower', $Bitrates); + array_push($SearchBitrates, 'v0', 'v1', 'v2', '24bit'); + $SearchFormats = array_map('strtolower', $Formats); + $SearchMedia = array_map('strtolower', $Media); + + foreach (explode(' ', $Term) as $Word) { + if (in_array($Word, $SearchBitrates)) { + $this->add_word('encoding', $Word); + } elseif (in_array($Word, $SearchFormats)) { + $this->add_word('format', $Word); + } elseif (in_array($Word, $SearchMedia)) { + $this->add_word('media', $Word); + } elseif ($Word === '100%') { + $this->process_attribute('haslog', 100); + } elseif ($Word === '!100%') { + $this->process_attribute('haslog', -1); + } else { + $this->add_word('searchstr', $Word); + } + } + } + + /** + * Use phrase boundary for file searches to make sure we don't count + * partial hits from multiple files + * + * @param string $Term Given search expression + */ + private function search_filelist($Term) { + $SearchString = '"' . Sphinxql::sph_escape_string($Term) . '"~20'; + $this->SphQL->where_match($SearchString, 'filelist', false); + $this->UsedTorrentFields['filelist'] = $SearchString; + $this->EnableNegation = true; + $this->Filtered = true; + } + + /** + * Prepare tag searches before sending them to the normal treatment + * + * @param string $Term Given search expression + */ + private function search_taglist($Term) { + $Term = strtr($Term, '.', '_'); + $this->add_field('taglist', $Term); + } + + /** + * The year filter accepts a range. Figure out how to handle the filter value + * + * @param string $Term Filter condition. Can be an integer or a range with the format X-Y + * @return bool True if parameters are valid + */ + private function search_year($Term) { + $Years = explode('-', $Term); + if (count($Years) === 1 && is_number($Years[0])) { + // Exact year + $this->SphQL->where('year', $Years[0]); + } elseif (count($Years) === 2) { + if (empty($Years[0]) && is_number($Years[1])) { + // Range: 0 - 2005 + $this->SphQL->where_lt('year', $Years[1], true); + } elseif (empty($Years[1]) && is_number($Years[0])) { + // Range: 2005 - 2^32-1 + $this->SphQL->where_gt('year', $Years[0], true); + } elseif (is_number($Years[0]) && is_number($Years[1])) { + // Range: 2005 - 2009 + $this->SphQL->where_between('year', array(min($Years), max($Years))); + } else { + // Invalid input + return false; + } + } else { + // Invalid input + return false; + } + return true; + } + + /** + * Add a field filter that doesn't need special treatment + * + * @param string $Field Name of the search field + * @param string $Term Search expression for the field + */ + private function add_field($Field, $Term) { + if (isset(self::$FieldSeparators[$Field])) { + $Separator = self::$FieldSeparators[$Field]; + } else { + $Separator = self::$FieldSeparators['']; + } + $Words = explode($Separator, $Term); + foreach ($Words as $Word) { + $this->add_word($Field, $Word); + } + } + + /** + * Add a keyword to the array of search terms + * + * @param string $Field Name of the search field + * @param string $Word Keyword + */ + private function add_word($Field, $Word) { + $Word = trim($Word); + // Skip isolated hyphens to enable "Artist - Title" searches + if ($Word === '' || $Word === '-') { + return; + } + if ($Word[0] === '!' && strlen($Word) >= 2 && strpos($Word, '!', 1) === false) { + $this->Terms[$Field]['exclude'][] = $Word; + } else { + $this->Terms[$Field]['include'][] = $Word; + $this->EnableNegation = true; + } + } + + /** + * @return array Torrent group information for the matches from Torrents::get_groups + */ + public function get_groups() { + return $this->Groups; + } + + /** + * @param string $Type Field or attribute name + * @return string Unprocessed search terms + */ + public function get_terms($Type) { + return isset($this->RawTerms[$Type]) ? $this->RawTerms[$Type] : ''; + } + + /** + * @return int Result count + */ + public function record_count() { + return $this->NumResults; + } + + /** + * @return bool Whether any filters were used + */ + public function has_filters() { + return $this->Filtered; + } + + /** + * @return bool Whether any torrent-specific fulltext filters were used + */ + public function need_torrent_ft() { + return $this->GroupResults && $this->NumResults > 0 && !empty($this->UsedTorrentFields); + } + + /** + * Get torrent group info and remove any torrents that don't match + */ + private function process_results() { + if (count($this->SphResults) == 0) { + return; + } + $this->Groups = Torrents::get_groups($this->SphResults); + if ($this->need_torrent_ft()) { + // Query Sphinx for torrent IDs if torrent-specific fulltext filters were used + $this->filter_torrents_sph(); + } elseif ($this->GroupResults) { + // Otherwise, let PHP discard unmatching torrents + $this->filter_torrents_internal(); + } + // Ungrouped searches don't need any additional filtering + } + + /** + * Build and run a query that gets torrent IDs from Sphinx when fulltext filters + * were used to get primary results and they are grouped + */ + private function filter_torrents_sph() { + $AllTorrents = array(); + foreach ($this->Groups as $GroupID => $Group) { + if (!empty($Group['Torrents'])) { + $AllTorrents += array_fill_keys(array_keys($Group['Torrents']), $GroupID); + } + } + $TorrentCount = count($AllTorrents); + $this->SphQLTor = new SphinxqlQuery(); + $this->SphQLTor->select('id')->from('torrents, delta'); + foreach ($this->UsedTorrentFields as $Field => $Term) { + $this->SphQLTor->where_match($Term, $Field, false); + } + $this->SphQLTor->copy_attributes_from($this->SphQL); + $this->SphQLTor->where('id', array_keys($AllTorrents))->limit(0, $TorrentCount, $TorrentCount); + $SphQLResultTor = $this->SphQLTor->query(); + $MatchingTorrentIDs = $SphQLResultTor->to_pair('id', 'id'); + foreach ($AllTorrents as $TorrentID => $GroupID) { + if (!isset($MatchingTorrentIDs[$TorrentID])) { + unset($this->Groups[$GroupID]['Torrents'][$TorrentID]); + } + } + } + + /** + * Non-Sphinx method of collecting IDs of torrents that match any + * torrent-specific attribute filters that were used in the search query + */ + private function filter_torrents_internal() { + foreach ($this->Groups as $GroupID => $Group) { + if (empty($Group['Torrents'])) { + continue; + } + foreach ($Group['Torrents'] as $TorrentID => $Torrent) { + if (!$this->filter_torrent_internal($Torrent)) { + unset($this->Groups[$GroupID]['Torrents'][$TorrentID]); + } + } + } + } + + /** + * Post-processing to determine if a torrent is a real hit or if it was + * returned because another torrent in the group matched. Only used if + * there are no torrent-specific fulltext conditions + * + * @param array $Torrent Torrent array, probably from Torrents::get_groups() + * @return bool True if it's a real hit + */ + private function filter_torrent_internal($Torrent) { + if (isset($this->UsedTorrentAttrs['freetorrent'])) { + $FilterValue = $this->UsedTorrentAttrs['freetorrent']; + if ($FilterValue == '3' && $Torrent['FreeTorrent'] == '0') { + // Either FL or NL is ok + return false; + } elseif ($FilterValue != '3' && $FilterValue != (int)$Torrent['FreeTorrent']) { + return false; + } + } + if (isset($this->UsedTorrentAttrs['hascue'])) { + if ($this->UsedTorrentAttrs['hascue'] != (int)$Torrent['HasCue']) { + return false; + } + } + if (isset($this->UsedTorrentAttrs['haslog'])) { + $FilterValue = $this->UsedTorrentAttrs['haslog']; + if ($FilterValue == '0') { + // No logs + $Pass = empty($Torrent['HasLog']); + } elseif ($FilterValue == '100') { + // 100% logs + $Pass = $Torrent['LogScore'] == '100'; + } elseif ($FilterValue < 0) { + // Unscored or <100% logs + $Pass = !empty($Torrent['HasLog']) && $Torrent['LogScore'] != '100'; + } else { + // Any log score + $Pass = $Torrent['HasLog'] == '1'; + } + if (!$Pass) { + return false; + } + } + if (isset($this->UsedTorrentAttrs['scene'])) { + if ($this->UsedTorrentAttrs['scene'] != (int)$Torrent['Scene']) { + return false; + } + } + return true; + } } diff --git a/classes/tracker.class.php b/classes/tracker.class.php index 49fe49865..10e1a047f 100644 --- a/classes/tracker.class.php +++ b/classes/tracker.class.php @@ -1,181 +1,181 @@ - OLD_PASSKEY, 'newpasskey' => NEW_PASSKEY)) will send the request: - * GET /tracker_32_char_secret_code/update?action=change_passkey&oldpasskey=OLD_PASSKEY&newpasskey=NEW_PASSKEY HTTP/1.1 - * - * @param string $Action The action to send - * @param array $Updates An associative array of key->value pairs to send to the tracker - * @param boolean $ToIRC Sends a message to the channel #tracker with the GET URL. - */ - public static function update_tracker($Action, $Updates, $ToIRC = false) { - // Build request - $Get = TRACKER_SECRET . "/update?action=$Action"; - foreach ($Updates as $Key => $Value) { - $Get .= "&$Key=$Value"; - } + /** + * Send a GET request over a socket directly to the tracker + * For example, Tracker::update_tracker('change_passkey', array('oldpasskey' => OLD_PASSKEY, 'newpasskey' => NEW_PASSKEY)) will send the request: + * GET /tracker_32_char_secret_code/update?action=change_passkey&oldpasskey=OLD_PASSKEY&newpasskey=NEW_PASSKEY HTTP/1.1 + * + * @param string $Action The action to send + * @param array $Updates An associative array of key->value pairs to send to the tracker + * @param boolean $ToIRC Sends a message to the channel #tracker with the GET URL. + */ + public static function update_tracker($Action, $Updates, $ToIRC = false) { + // Build request + $Get = TRACKER_SECRET . "/update?action=$Action"; + foreach ($Updates as $Key => $Value) { + $Get .= "&$Key=$Value"; + } - $MaxAttempts = 3; - $Err = false; - if (self::send_request($Get, $MaxAttempts, $Err) === false) { - send_irc("PRIVMSG #tracker :$MaxAttempts $Err $Get"); - if (G::$Cache->get_value('ocelot_error_reported') === false) { - send_irc('PRIVMSG ' . ADMIN_CHAN . " :Failed to update ocelot: $Err : $Get"); - G::$Cache->cache_value('ocelot_error_reported', true, 3600); - } - return false; - } - return true; - } + $MaxAttempts = 3; + $Err = false; + if (self::send_request($Get, $MaxAttempts, $Err) === false) { + send_irc("PRIVMSG #tracker :$MaxAttempts $Err $Get"); + if (G::$Cache->get_value('ocelot_error_reported') === false) { + send_irc('PRIVMSG ' . ADMIN_CHAN . " :Failed to update ocelot: $Err : $Get"); + G::$Cache->cache_value('ocelot_error_reported', true, 3600); + } + return false; + } + return true; + } - /** - * Get global peer stats from the tracker - * - * @return array(0 => $Leeching, 1 => $Seeding) or false if request failed - */ - public static function global_peer_count() { - $Stats = self::get_stats(self::STATS_MAIN); - if (isset($Stats['leechers tracked']) && isset($Stats['seeders tracked'])) { - $Leechers = $Stats['leechers tracked']; - $Seeders = $Stats['seeders tracked']; - } else { - return false; - } - return array($Leechers, $Seeders); - } + /** + * Get global peer stats from the tracker + * + * @return array(0 => $Leeching, 1 => $Seeding) or false if request failed + */ + public static function global_peer_count() { + $Stats = self::get_stats(self::STATS_MAIN); + if (isset($Stats['leechers tracked']) && isset($Stats['seeders tracked'])) { + $Leechers = $Stats['leechers tracked']; + $Seeders = $Stats['seeders tracked']; + } else { + return false; + } + return [$Leechers, $Seeders]; + } - /** - * Get peer stats for a user from the tracker - * - * @param string $TorrentPass The user's pass key - * @return array(0 => $Leeching, 1 => $Seeding) or false if the request failed - */ - public static function user_peer_count($TorrentPass) { - $Stats = self::get_stats(self::STATS_USER, array('key' => $TorrentPass)); - if ($Stats === false) { - return false; - } - if (isset($Stats['leeching']) && isset($Stats['seeding'])) { - $Leeching = $Stats['leeching']; - $Seeding = $Stats['seeding']; - } else { - // User doesn't exist, but don't tell anyone - $Leeching = $Seeding = 0; - } - return array($Leeching, $Seeding); - } + /** + * Get peer stats for a user from the tracker + * + * @param string $TorrentPass The user's pass key + * @return array(0 => $Leeching, 1 => $Seeding) or false if the request failed + */ + public static function user_peer_count($TorrentPass) { + $Stats = self::get_stats(self::STATS_USER, array('key' => $TorrentPass)); + if ($Stats === false) { + return false; + } + if (isset($Stats['leeching']) && isset($Stats['seeding'])) { + $Leeching = $Stats['leeching']; + $Seeding = $Stats['seeding']; + } else { + // User doesn't exist, but don't tell anyone + $Leeching = $Seeding = 0; + } + return array($Leeching, $Seeding); + } - /** - * Get whatever info the tracker has to report - * - * @return results from get_stats() - */ - public static function info() { - return self::get_stats(self::STATS_MAIN); - } + /** + * Get whatever info the tracker has to report + * + * @return results from get_stats() + */ + public static function info() { + return self::get_stats(self::STATS_MAIN); + } - /** - * Send a stats request to the tracker and process the results - * - * @param int $Type Stats type to get - * @param array $Params Parameters required by stats type - * @return array with stats in named keys or false if the request failed - */ - private static function get_stats($Type, $Params = false) { - if (!defined('TRACKER_REPORTKEY')) { - return false; - } - $Get = TRACKER_REPORTKEY . '/report?'; - if ($Type === self::STATS_MAIN) { - $Get .= 'get=stats'; - } elseif ($Type === self::STATS_USER && !empty($Params['key'])) { - $Get .= "get=user&key=$Params[key]"; - } else { - return false; - } - $Response = self::send_request($Get); - if ($Response === false) { - return false; - } - $Stats = array(); - foreach (explode("\n", $Response) as $Stat) { - list($Val, $Key) = explode(" ", $Stat, 2); - $Stats[$Key] = $Val; - } - return $Stats; - } + /** + * Send a stats request to the tracker and process the results + * + * @param int $Type Stats type to get + * @param array $Params Parameters required by stats type + * @return array with stats in named keys or false if the request failed + */ + private static function get_stats($Type, $Params = false) { + if (!defined('TRACKER_REPORTKEY')) { + return false; + } + $Get = TRACKER_REPORTKEY . '/report?'; + if ($Type === self::STATS_MAIN) { + $Get .= 'get=stats'; + } elseif ($Type === self::STATS_USER && !empty($Params['key'])) { + $Get .= "get=user&key=$Params[key]"; + } else { + return false; + } + $Response = self::send_request($Get); + if ($Response === false) { + return false; + } + $Stats = []; + foreach (explode("\n", $Response) as $Stat) { + list($Val, $Key) = explode(" ", $Stat, 2); + $Stats[$Key] = $Val; + } + return $Stats; + } - /** - * Send a request to the tracker - * - * @param string $Get GET string to send to the tracker - * @param int $MaxAttempts Maximum number of failed attempts before giving up - * @param bool $Err Variable to use as storage for the error string if the request fails - * @return tracker response message or false if the request failed - */ - private static function send_request($Get, $MaxAttempts = 1, &$Err = false) { - if (defined('DISABLE_TRACKER') && DISABLE_TRACKER === true) { - return false; - } - $Header = "GET /$Get HTTP/1.1\r\nConnection: Close\r\n\r\n"; - $Attempts = 0; - $Sleep = 0; - $Success = false; - $StartTime = microtime(true); - while (!$Success && $Attempts++ < $MaxAttempts) { - if ($Sleep) { - sleep($Sleep); - } - $Sleep = 6; + /** + * Send a request to the tracker + * + * @param string $Get GET string to send to the tracker + * @param int $MaxAttempts Maximum number of failed attempts before giving up + * @param bool $Err Variable to use as storage for the error string if the request fails + * @return tracker response message or false if the request failed + */ + private static function send_request($Get, $MaxAttempts = 1, &$Err = false) { + if (defined('DISABLE_TRACKER') && DISABLE_TRACKER === true) { + return false; + } + $Header = "GET /$Get HTTP/1.1\r\nConnection: Close\r\n\r\n"; + $Attempts = 0; + $Sleep = 0; + $Success = false; + $StartTime = microtime(true); + while (!$Success && $Attempts++ < $MaxAttempts) { + if ($Sleep) { + sleep($Sleep); + } + $Sleep = 6; - // Send request - $File = fsockopen(TRACKER_HOST, TRACKER_PORT, $ErrorNum, $ErrorString); - if ($File) { - if (fwrite($File, $Header) === false) { - $Err = "Failed to fwrite()"; - $Sleep = 3; - continue; - } - } else { - $Err = "Failed to fsockopen() - $ErrorNum - $ErrorString"; - continue; - } + // Send request + $File = fsockopen(TRACKER_HOST, TRACKER_PORT, $ErrorNum, $ErrorString); + if ($File) { + if (fwrite($File, $Header) === false) { + $Err = "Failed to fwrite()"; + $Sleep = 3; + continue; + } + } else { + $Err = "Failed to fsockopen() - $ErrorNum - $ErrorString"; + continue; + } - // Check for response. - $Response = ''; - while (!feof($File)) { - $Response .= fread($File, 1024); - } - $DataStart = strpos($Response, "\r\n\r\n") + 4; - $DataEnd = strrpos($Response, "\n"); - if ($DataEnd > $DataStart) { - $Data = substr($Response, $DataStart, $DataEnd - $DataStart); - } else { - $Data = ""; - } - $Status = substr($Response, $DataEnd + 1); - if ($Status == "success") { - $Success = true; - } - } - $Request = array( - 'path' => substr($Get, strpos($Get, '/')), - 'response' => ($Success ? $Data : $Response), - 'status' => ($Success ? 'ok' : 'failed'), - 'time' => 1000 * (microtime(true) - $StartTime) - ); - self::$Requests[] = $Request; - if ($Success) { - return $Data; - } - return false; - } + // Check for response. + $Response = ''; + while (!feof($File)) { + $Response .= fread($File, 1024); + } + $DataStart = strpos($Response, "\r\n\r\n") + 4; + $DataEnd = strrpos($Response, "\n"); + if ($DataEnd > $DataStart) { + $Data = substr($Response, $DataStart, $DataEnd - $DataStart); + } else { + $Data = ""; + } + $Status = substr($Response, $DataEnd + 1); + if ($Status == "success") { + $Success = true; + } + } + $Request = array( + 'path' => substr($Get, strpos($Get, '/')), + 'response' => ($Success ? $Data : $Response), + 'status' => ($Success ? 'ok' : 'failed'), + 'time' => 1000 * (microtime(true) - $StartTime) + ); + self::$Requests[] = $Request; + if ($Success) { + return $Data; + } + return false; + } } ?> diff --git a/classes/userrank.class.php b/classes/userrank.class.php index d5211816b..138ffc262 100644 --- a/classes/userrank.class.php +++ b/classes/userrank.class.php @@ -1,161 +1,163 @@ -get_query_id(); - - G::$DB->query(" - DROP TEMPORARY TABLE IF EXISTS temp_stats"); - - G::$DB->query(" - CREATE TEMPORARY TABLE temp_stats ( - ID int(10) NOT NULL PRIMARY KEY AUTO_INCREMENT, - Val bigint(20) NOT NULL - );"); - - G::$DB->query(" - INSERT INTO temp_stats (Val) ". - $Query); - - G::$DB->query(" - SELECT COUNT(ID) - FROM temp_stats"); - list($UserCount) = G::$DB->next_record(); - - G::$DB->query(" - SELECT MIN(Val) - FROM temp_stats - GROUP BY CEIL(ID / (".(int)$UserCount." / 100));"); - - $Table = G::$DB->to_array(); - - G::$DB->set_query_id($QueryID); - - // Give a little variation to the cache length, so all the tables don't expire at the same time - G::$Cache->cache_value($MemKey, $Table, 3600 * 24 * rand(800, 1000) * 0.001); - - return $Table; - } - - private static function table_query($TableName) { - switch ($TableName) { - case 'uploaded': - $Query = " - SELECT Uploaded - FROM users_main - WHERE Enabled = '1' - AND Uploaded > 0 - ORDER BY Uploaded;"; - break; - case 'downloaded': - $Query = " - SELECT Downloaded - FROM users_main - WHERE Enabled = '1' - AND Downloaded > 0 - ORDER BY Downloaded;"; - break; - case 'uploads': - $Query = " - SELECT COUNT(t.ID) AS Uploads - FROM users_main AS um - JOIN torrents AS t ON t.UserID = um.ID - WHERE um.Enabled = '1' - GROUP BY um.ID - ORDER BY Uploads;"; - break; - case 'requests': - $Query = " - SELECT COUNT(r.ID) AS Requests - FROM users_main AS um - JOIN requests AS r ON r.FillerID = um.ID - WHERE um.Enabled = '1' - GROUP BY um.ID - ORDER BY Requests;"; - break; - case 'posts': - $Query = " - SELECT COUNT(p.ID) AS Posts - FROM users_main AS um - JOIN forums_posts AS p ON p.AuthorID = um.ID - WHERE um.Enabled = '1' - GROUP BY um.ID - ORDER BY Posts;"; - break; - case 'bounty': - $Query = " - SELECT SUM(rv.Bounty) AS Bounty - FROM users_main AS um - JOIN requests_votes AS rv ON rv.UserID = um.ID - WHERE um.Enabled = '1' " . - "GROUP BY um.ID - ORDER BY Bounty;"; - break; - case 'artists': - $Query = " - SELECT COUNT(ta.ArtistID) AS Artists - FROM torrents_artists AS ta - JOIN torrents_group AS tg ON tg.ID = ta.GroupID - JOIN torrents AS t ON t.GroupID = tg.ID - WHERE t.UserID != ta.UserID - GROUP BY tg.ID - ORDER BY Artists ASC"; - break; - } - return $Query; - } - - public static function get_rank($TableName, $Value) { - if ($Value == 0) { - return 0; - } - - $Table = G::$Cache->get_value(self::PREFIX.$TableName); - if (!$Table) { - //Cache lock! - $Lock = G::$Cache->get_value(self::PREFIX.$TableName.'_lock'); - if ($Lock) { - return false; - } else { - G::$Cache->cache_value(self::PREFIX.$TableName.'_lock', '1', 300); - $Table = self::build_table(self::PREFIX.$TableName, self::table_query($TableName)); - G::$Cache->delete_value(self::PREFIX.$TableName.'_lock'); - } - } - $LastPercentile = 0; - foreach ($Table as $Row) { - list($CurValue) = $Row; - if ($CurValue >= $Value) { - return $LastPercentile; - } - $LastPercentile++; - } - return 100; // 100th percentile - } - - public static function overall_score($Uploaded, $Downloaded, $Uploads, $Requests, $Posts, $Bounty, $Artists, $Ratio) { - // We can do this all in 1 line, but it's easier to read this way - if ($Ratio > 1) { - $Ratio = 1; - } - $TotalScore = 0; - if (in_array(false, func_get_args(), true)) { - return false; - } - $TotalScore += $Uploaded * 15; - $TotalScore += $Downloaded * 8; - $TotalScore += $Uploads * 25; - $TotalScore += $Requests * 2; - $TotalScore += $Posts; - $TotalScore += $Bounty; - $TotalScore += $Artists; - $TotalScore /= (15 + 8 + 25 + 2 + 1 + 1 + 1); - $TotalScore *= $Ratio; - return $TotalScore; - } + const PREFIX = 'percentiles_'; // Prefix for memcache keys, to make life easier + + // Returns a 101 row array (101 percentiles - 0 - 100), with the minimum value for that percentile as the value for each row + // BTW - ingenious + private static function build_table($MemKey, $Query) { + $QueryID = G::$DB->get_query_id(); + + G::$DB->query(" + DROP TEMPORARY TABLE IF EXISTS temp_stats"); + + G::$DB->query(" + CREATE TEMPORARY TABLE temp_stats ( + ID int(10) NOT NULL PRIMARY KEY AUTO_INCREMENT, + Val bigint(20) NOT NULL + );"); + + G::$DB->query(" + INSERT INTO temp_stats (Val) ". + $Query); + + G::$DB->query(" + SELECT COUNT(ID) + FROM temp_stats"); + list($UserCount) = G::$DB->next_record(); + + G::$DB->query(" + SELECT MIN(Val) + FROM temp_stats + GROUP BY CEIL(ID / (".(int)$UserCount." / 100));"); + + $Table = G::$DB->to_array(); + + G::$DB->set_query_id($QueryID); + + // Give a little variation to the cache length, so all the tables don't expire at the same time + G::$Cache->cache_value($MemKey, $Table, 3600 * 24 * rand(800, 1000) * 0.001); + + return $Table; + } + + private static function table_query($TableName) { + switch ($TableName) { + case 'uploaded': + $Query = " + SELECT uls.Uploaded + FROM users_main um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + WHERE um.Enabled = '1' + AND uls.Uploaded > 0 + ORDER BY uls.Uploaded"; + break; + case 'downloaded': + $Query = " + SELECT uls.Downloaded + FROM users_main um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + WHERE um.Enabled = '1' + AND uls.Downloaded > 0 + ORDER BY uls.Downloaded"; + break; + case 'uploads': + $Query = " + SELECT count(*) AS Uploads + FROM users_main AS um + INNER JOIN torrents AS t ON (t.UserID = um.ID) + WHERE um.Enabled = '1' + GROUP BY um.ID + ORDER BY Uploads"; + break; + case 'requests': + $Query = " + SELECT count(*) AS Requests + FROM users_main AS um + INNER JOIN requests AS r ON (r.FillerID = um.ID) + WHERE um.Enabled = '1' + GROUP BY um.ID + ORDER BY Requests"; + break; + case 'posts': + $Query = " + SELECT count(*) AS Posts + FROM users_main AS um + INNER JOIN forums_posts AS p ON (p.AuthorID = um.ID) + WHERE um.Enabled = '1' + GROUP BY um.ID + ORDER BY Posts"; + break; + case 'bounty': + $Query = " + SELECT SUM(rv.Bounty) AS Bounty + FROM users_main AS um + INNER JOIN requests_votes AS rv ON (rv.UserID = um.ID) + WHERE um.Enabled = '1' + GROUP BY um.ID + ORDER BY Bounty"; + break; + case 'artists': + $Query = " + SELECT count(*) AS Artists + FROM torrents_artists AS ta + INNER JOIN torrents_group AS tg ON (tg.ID = ta.GroupID) + INNER JOIN torrents AS t ON (t.GroupID = tg.ID) + WHERE t.UserID != ta.UserID + GROUP BY tg.ID + ORDER BY Artists ASC"; + break; + } + return $Query; + } + + public static function get_rank($TableName, $Value) { + if ($Value == 0) { + return 0; + } + + $Table = G::$Cache->get_value(self::PREFIX.$TableName); + if (!$Table) { + //Cache lock! + $Lock = G::$Cache->get_value(self::PREFIX.$TableName.'_lock'); + if ($Lock) { + return false; + } else { + G::$Cache->cache_value(self::PREFIX.$TableName.'_lock', '1', 300); + $Table = self::build_table(self::PREFIX.$TableName, self::table_query($TableName)); + G::$Cache->delete_value(self::PREFIX.$TableName.'_lock'); + } + } + $LastPercentile = 0; + foreach ($Table as $Row) { + list($CurValue) = $Row; + if ($CurValue >= $Value) { + return $LastPercentile; + } + $LastPercentile++; + } + return 100; // 100th percentile + } + + public static function overall_score($Uploaded, $Downloaded, $Uploads, $Requests, $Posts, $Bounty, $Artists, $Ratio) { + // We can do this all in 1 line, but it's easier to read this way + if ($Ratio > 1) { + $Ratio = 1; + } + $TotalScore = 0; + if (in_array(false, func_get_args(), true)) { + return false; + } + $TotalScore += $Uploaded * 15; + $TotalScore += $Downloaded * 8; + $TotalScore += $Uploads * 25; + $TotalScore += $Requests * 2; + $TotalScore += $Posts; + $TotalScore += $Bounty; + $TotalScore += $Artists; + $TotalScore /= (15 + 8 + 25 + 2 + 1 + 1 + 1); + $TotalScore *= $Ratio; + return $TotalScore; + } } diff --git a/classes/users.class.php b/classes/users.class.php index 3346120d0..e04d00e9b 100644 --- a/classes/users.class.php +++ b/classes/users.class.php @@ -1,884 +1,927 @@ -get_value('classes'); - if (!$Classes || !$ClassLevels) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(' - SELECT ID, Name, Level, Secondary - FROM permissions - ORDER BY Level'); - $Classes = G::$DB->to_array('ID'); - $ClassLevels = G::$DB->to_array('Level'); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value('classes', array($Classes, $ClassLevels), 0); - } - $Debug->set_flag('Loaded permissions'); - - return array($Classes, $ClassLevels); - } - - public static function user_stats($UserID, $refresh = false) { - global $Cache, $DB; - if ($refresh) { - $Cache->delete_value('user_stats_'.$UserID); - } - $UserStats = $Cache->get_value('user_stats_'.$UserID); - if (!is_array($UserStats)) { - $DB->query(" - SELECT Uploaded AS BytesUploaded, Downloaded AS BytesDownloaded, BonusPoints, RequiredRatio - FROM users_main - WHERE ID = '$UserID'"); - $UserStats = $DB->next_record(MYSQLI_ASSOC); - $Cache->cache_value('user_stats_'.$UserID, $UserStats, 3600); - } - return $UserStats; - } - - /** - * Get user info, is used for the current user and usernames all over the site. - * - * @param $UserID int The UserID to get info for - * @return array with the following keys: - * int ID - * string Username - * int PermissionID - * array Paranoia - $Paranoia array sent to paranoia.class - * boolean Artist - * boolean Donor - * string Warned - When their warning expires in international time format - * string Avatar - URL - * boolean Enabled - * string Title - * string CatchupTime - When they last caught up on forums - * boolean Visible - If false, they don't show up on peer lists - * array ExtraClasses - Secondary classes. - * int EffectiveClass - the highest level of their main and secondary classes - */ - public static function user_info($UserID) { - global $Classes, $SSL; - $UserInfo = G::$Cache->get_value("user_info_$UserID"); - // the !isset($UserInfo['Paranoia']) can be removed after a transition period - if (empty($UserInfo) || empty($UserInfo['ID']) || !isset($UserInfo['Paranoia']) || empty($UserInfo['Class'])) { - $OldQueryID = G::$DB->get_query_id(); - - G::$DB->query(" - SELECT - m.ID, - m.Username, - m.PermissionID, - m.Paranoia, - i.Artist, - i.Donor, - i.Warned, - i.Avatar, - m.Enabled, - m.Title, - i.CatchupTime, - m.Visible, - la.Type AS LockedAccount, - GROUP_CONCAT(ul.PermissionID SEPARATOR ',') AS Levels - FROM users_main AS m - INNER JOIN users_info AS i ON i.UserID = m.ID - LEFT JOIN locked_accounts AS la ON la.UserID = m.ID - LEFT JOIN users_levels AS ul ON ul.UserID = m.ID - WHERE m.ID = '$UserID' - GROUP BY m.ID"); - - if (!G::$DB->has_results()) { // Deleted user, maybe? - $UserInfo = array( - 'ID' => $UserID, - 'Username' => '', - 'PermissionID' => 0, - 'Paranoia' => array(), - 'Artist' => false, - 'Donor' => false, - 'Warned' => '0000-00-00 00:00:00', - 'Avatar' => '', - 'Enabled' => 0, - 'Title' => '', - 'CatchupTime' => 0, - 'Visible' => '1', - 'Levels' => '', - 'Class' => 0); - } else { - $UserInfo = G::$DB->next_record(MYSQLI_ASSOC, array('Paranoia', 'Title')); - $UserInfo['CatchupTime'] = strtotime($UserInfo['CatchupTime']); - $UserInfo['Paranoia'] = unserialize_array($UserInfo['Paranoia']); - if ($UserInfo['Paranoia'] === false) { - $UserInfo['Paranoia'] = array(); - } - $UserInfo['Class'] = $Classes[$UserInfo['PermissionID']]['Level']; - } - - if ($UserInfo['LockedAccount'] == "") { - unset($UserInfo['LockedAccount']); - } - - if (!empty($UserInfo['Levels'])) { - $UserInfo['ExtraClasses'] = array_fill_keys(explode(',', $UserInfo['Levels']), 1); - } else { - $UserInfo['ExtraClasses'] = array(); - } - unset($UserInfo['Levels']); - $EffectiveClass = $UserInfo['Class']; - foreach ($UserInfo['ExtraClasses'] as $Class => $Val) { - $EffectiveClass = max($EffectiveClass, $Classes[$Class]['Level']); - } - $UserInfo['EffectiveClass'] = $EffectiveClass; - - G::$Cache->cache_value("user_info_$UserID", $UserInfo, 2592000); - G::$DB->set_query_id($OldQueryID); - } - if (strtotime($UserInfo['Warned']) < time()) { - $UserInfo['Warned'] = '0000-00-00 00:00:00'; - G::$Cache->cache_value("user_info_$UserID", $UserInfo, 2592000); - } - - return $UserInfo; - } - - /** - * Gets the heavy user info - * Only used for current user - * - * @param string $UserID The userid to get the information for - * @return array fetched heavy info. - * Just read the goddamn code, I don't have time to comment this shit. - */ - public static function user_heavy_info($UserID) { - - $HeavyInfo = G::$Cache->get_value("user_info_heavy_$UserID"); - if (empty($HeavyInfo)) { - - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - m.Invites, - m.torrent_pass, - m.IP, - m.CustomPermissions, - m.can_leech AS CanLeech, - m.IRCKey, - i.AuthKey, - i.RatioWatchEnds, - i.RatioWatchDownload, - i.StyleID, - i.StyleURL, - i.DisableInvites, - i.DisablePosting, - i.DisableUpload, - i.DisablePoints, - i.DisableWiki, - i.DisableAvatar, - i.DisablePM, - i.DisableRequests, - i.DisableForums, - i.DisableIRC, - i.DisableTagging," . " - i.SiteOptions, - i.DownloadAlt, - i.LastReadNews, - i.LastReadBlog, - i.RestrictedForums, - i.PermittedForums, - i.NavItems, - m.FLTokens, - m.PermissionID - FROM users_main AS m - INNER JOIN users_info AS i ON i.UserID = m.ID - WHERE m.ID = '$UserID'"); - $HeavyInfo = G::$DB->next_record(MYSQLI_ASSOC, array('CustomPermissions', 'SiteOptions')); - - $HeavyInfo['CustomPermissions'] = unserialize_array($HeavyInfo['CustomPermissions']); - - if (!empty($HeavyInfo['RestrictedForums'])) { - $RestrictedForums = array_map('trim', explode(',', $HeavyInfo['RestrictedForums'])); - } else { - $RestrictedForums = array(); - } - unset($HeavyInfo['RestrictedForums']); - if (!empty($HeavyInfo['PermittedForums'])) { - $PermittedForums = array_map('trim', explode(',', $HeavyInfo['PermittedForums'])); - } else { - $PermittedForums = array(); - } - unset($HeavyInfo['PermittedForums']); - if (!empty($HeavyInfo['NavItems'])) { - $NavItems = array_map('trim', explode(',', $HeavyInfo['NavItems'])); - } else { - $NavItems = []; - } - $HeavyInfo['NavItems'] = $NavItems; - - G::$DB->query(" - SELECT PermissionID - FROM users_levels - WHERE UserID = $UserID"); - $PermIDs = G::$DB->collect('PermissionID'); - foreach ($PermIDs AS $PermID) { - $Perms = Permissions::get_permissions($PermID); - if (!empty($Perms['PermittedForums'])) { - $PermittedForums = array_merge($PermittedForums, array_map('trim', explode(',', $Perms['PermittedForums']))); - } - } - $Perms = Permissions::get_permissions($HeavyInfo['PermissionID']); - unset($HeavyInfo['PermissionID']); - if (!empty($Perms['PermittedForums'])) { - $PermittedForums = array_merge($PermittedForums, array_map('trim', explode(',', $Perms['PermittedForums']))); - } - - if (!empty($PermittedForums) || !empty($RestrictedForums)) { - $HeavyInfo['CustomForums'] = array(); - foreach ($RestrictedForums as $ForumID) { - $HeavyInfo['CustomForums'][$ForumID] = 0; - } - foreach ($PermittedForums as $ForumID) { - $HeavyInfo['CustomForums'][$ForumID] = 1; - } - } else { - $HeavyInfo['CustomForums'] = null; - } - if (isset($HeavyInfo['CustomForums'][''])) { - unset($HeavyInfo['CustomForums']['']); - } - - $HeavyInfo['SiteOptions'] = unserialize_array($HeavyInfo['SiteOptions']); - $HeavyInfo['SiteOptions'] = array_merge(static::default_site_options(), $HeavyInfo['SiteOptions']); - $HeavyInfo = array_merge($HeavyInfo, $HeavyInfo['SiteOptions']); - - unset($HeavyInfo['SiteOptions']); - - G::$DB->set_query_id($QueryID); - - G::$Cache->cache_value("user_info_heavy_$UserID", $HeavyInfo, 0); - } - return $HeavyInfo; - } - - public static function get_user_nav_items($userId) { - $Info = self::user_heavy_info($userId); - - $UserIds = !empty($Info['NavItems']) ? $Info['NavItems'] : []; - $NavItems = self::get_nav_items(); - - if (!count($UserIds)) { - return $NavItems; - } - - $UserItems = []; - foreach ($NavItems as $n) { - if ($n['Mandatory'] || in_array($n['ID'], $UserIds)) { - $UserItems[] = $n; - } - } - - return $UserItems; - } - - public static function get_nav_items() { - $Items = G::$Cache->get_value("nav_items"); - if (!$Items) { - $QueryID = G::$DB->get_query_id(); - G::$DB->prepared_query(" - SELECT ID, `Key`, Title, Target, Tests, TestUser, Mandatory - FROM nav_items"); - $Items = G::$DB->to_array("ID", MYSQLI_ASSOC); - G::$Cache->cache_value("nav_items", $Items, 0); - G::$DB->set_query_id($QueryID); - } - return $Items; - } - - /** - * Return the ID of a Username - * @param string Username - * @return userID if exists, null otherwise - */ - public static function ID_from_username($name) { - $digest = base64_encode(md5($name, true)); - $key = "username_id_$digest"; - $ID = G::$Cache->get_value($key); - if ($ID == -1) { - return null; - } - elseif ($ID === false) { - G::$DB->prepared_query("SELECT ID FROM users_main WHERE Username=?", $name); - if (!G::$DB->has_results()) { - // cache negative hits for a while - G::$Cache->cache_value($key, -1, 300); - return null; - } - list($ID) = G::$DB->next_record(); - G::$Cache->cache_value($key, $ID, 300); - } - return $ID; - } - - /** - * Default settings to use for SiteOptions - * @return array - */ - public static function default_site_options() { - return array( - 'HttpsTracker' => true - ); - } - - /** - * Updates the site options in the database - * - * @param int $UserID the UserID to set the options for - * @param array $NewOptions the new options to set - * @return false if $NewOptions is empty, true otherwise - */ - public static function update_site_options($UserID, $NewOptions) { - if (!is_number($UserID)) { - error(0); - } - if (empty($NewOptions)) { - return false; - } - - $QueryID = G::$DB->get_query_id(); - - // Get SiteOptions - G::$DB->query(" - SELECT SiteOptions - FROM users_info - WHERE UserID = $UserID"); - list($SiteOptions) = G::$DB->next_record(MYSQLI_NUM, false); - $SiteOptions = unserialize_array($SiteOptions); - $SiteOptions = array_merge(static::default_site_options(), $SiteOptions); - - // Get HeavyInfo - $HeavyInfo = Users::user_heavy_info($UserID); - - // Insert new/replace old options - $SiteOptions = array_merge($SiteOptions, $NewOptions); - $HeavyInfo = array_merge($HeavyInfo, $NewOptions); - - // Update DB - G::$DB->query(" - UPDATE users_info - SET SiteOptions = '".db_string(serialize($SiteOptions))."' - WHERE UserID = $UserID"); - G::$DB->set_query_id($QueryID); - - // Update cache - G::$Cache->cache_value("user_info_heavy_$UserID", $HeavyInfo, 0); - - // Update G::$LoggedUser if the options are changed for the current - if (G::$LoggedUser['ID'] == $UserID) { - G::$LoggedUser = array_merge(G::$LoggedUser, $NewOptions); - G::$LoggedUser['ID'] = $UserID; // We don't want to allow userid switching - } - return true; - } - - /** - * Generates a check list of release types, ordered by the user or default - * @param array $SiteOptions - * @param boolean $Default Returns the default list if true - */ - public static function release_order(&$SiteOptions, $Default = false) { - global $ReleaseTypes; - - $RT = $ReleaseTypes + array( - 1024 => 'Guest Appearance', - 1023 => 'Remixed By', - 1022 => 'Composition', - 1021 => 'Produced By'); - - if ($Default || empty($SiteOptions['SortHide'])) { - $Sort =& $RT; - $Defaults = !empty($SiteOptions['HideTypes']); - } else { - $Sort =& $SiteOptions['SortHide']; - $MissingTypes = array_diff_key($RT, $Sort); - if (!empty($MissingTypes)) { - foreach (array_keys($MissingTypes) as $Missing) { - $Sort[$Missing] = 0; - } - } - } - - foreach ($Sort as $Key => $Val) { - if (isset($Defaults)) { - $Checked = $Defaults && isset($SiteOptions['HideTypes'][$Key]) ? ' checked="checked"' : ''; - } else { - if (!isset($RT[$Key])) { - continue; - } - $Checked = $Val ? ' checked="checked"' : ''; - $Val = $RT[$Key]; - } - - $ID = $Key. '_' . (int)(!!$Checked); - - // The HTML is indented this far for proper indentation in the generated HTML - // on user.php?action=edit + /** + * Get $Classes (list of classes keyed by ID) and $ClassLevels + * (list of classes keyed by level) + * @return array ($Classes, $ClassLevels) + */ + public static function get_classes() { + global $Debug; + // Get permissions + list($Classes, $ClassLevels) = G::$Cache->get_value('classes'); + if (!$Classes || !$ClassLevels) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(' + SELECT ID, Name, Level, Secondary + FROM permissions + ORDER BY Level'); + $Classes = G::$DB->to_array('ID'); + $ClassLevels = G::$DB->to_array('Level'); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value('classes', array($Classes, $ClassLevels), 0); + } + $Debug->set_flag('Loaded permissions'); + + return array($Classes, $ClassLevels); + } + + public static function user_stats($UserID, $refresh = false) { + global $Cache, $DB; + if ($refresh) { + $Cache->delete_value('user_stats_'.$UserID); + } + $UserStats = $Cache->get_value('user_stats_'.$UserID); + if (!is_array($UserStats)) { + $DB->prepared_query(' + SELECT + uls.Uploaded AS BytesUploaded, + uls.Downloaded AS BytesDownloaded, + um.BonusPoints, + um.RequiredRatio + FROM users_main um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + WHERE um.ID = ? + ', $UserID + ); + $UserStats = $DB->next_record(MYSQLI_ASSOC); + $Cache->cache_value('user_stats_'.$UserID, $UserStats, 3600); + } + return $UserStats; + } + + /** + * Get user info, is used for the current user and usernames all over the site. + * + * @param $UserID int The UserID to get info for + * @return array with the following keys: + * int ID + * string Username + * int PermissionID + * array Paranoia - $Paranoia array sent to paranoia.class + * boolean Artist + * boolean Donor + * string Warned - When their warning expires in international time format + * string Avatar - URL + * boolean Enabled + * string Title + * string CatchupTime - When they last caught up on forums + * boolean Visible - If false, they don't show up on peer lists + * array ExtraClasses - Secondary classes. + * int EffectiveClass - the highest level of their main and secondary classes + */ + public static function user_info($UserID) { + global $Classes, $SSL; + $UserInfo = G::$Cache->get_value("user_info_$UserID"); + // the !isset($UserInfo['Paranoia']) can be removed after a transition period + if (empty($UserInfo) || empty($UserInfo['ID']) || !isset($UserInfo['Paranoia']) || empty($UserInfo['Class'])) { + $OldQueryID = G::$DB->get_query_id(); + + G::$DB->query(" + SELECT + m.ID, + m.Username, + m.PermissionID, + m.Paranoia, + i.Artist, + i.Donor, + i.Warned, + i.Avatar, + m.Enabled, + m.Title, + i.CatchupTime, + m.Visible, + la.Type AS LockedAccount, + GROUP_CONCAT(ul.PermissionID SEPARATOR ',') AS Levels + FROM users_main AS m + INNER JOIN users_info AS i ON i.UserID = m.ID + LEFT JOIN locked_accounts AS la ON la.UserID = m.ID + LEFT JOIN users_levels AS ul ON ul.UserID = m.ID + WHERE m.ID = '$UserID' + GROUP BY m.ID"); + + if (!G::$DB->has_results()) { // Deleted user, maybe? + $UserInfo = array( + 'ID' => $UserID, + 'Username' => '', + 'PermissionID' => 0, + 'Paranoia' => [], + 'Artist' => false, + 'Donor' => false, + 'Warned' => '0000-00-00 00:00:00', + 'Avatar' => '', + 'Enabled' => 0, + 'Title' => '', + 'CatchupTime' => 0, + 'Visible' => '1', + 'Levels' => '', + 'Class' => 0); + } else { + $UserInfo = G::$DB->next_record(MYSQLI_ASSOC, array('Paranoia', 'Title')); + $UserInfo['CatchupTime'] = strtotime($UserInfo['CatchupTime']); + $UserInfo['Paranoia'] = unserialize_array($UserInfo['Paranoia']); + if ($UserInfo['Paranoia'] === false) { + $UserInfo['Paranoia'] = []; + } + $UserInfo['Class'] = $Classes[$UserInfo['PermissionID']]['Level']; + } + + if ($UserInfo['LockedAccount'] == "") { + unset($UserInfo['LockedAccount']); + } + + if (!empty($UserInfo['Levels'])) { + $UserInfo['ExtraClasses'] = array_fill_keys(explode(',', $UserInfo['Levels']), 1); + } else { + $UserInfo['ExtraClasses'] = []; + } + unset($UserInfo['Levels']); + $EffectiveClass = $UserInfo['Class']; + foreach ($UserInfo['ExtraClasses'] as $Class => $Val) { + $EffectiveClass = max($EffectiveClass, $Classes[$Class]['Level']); + } + $UserInfo['EffectiveClass'] = $EffectiveClass; + + G::$Cache->cache_value("user_info_$UserID", $UserInfo, 2592000); + G::$DB->set_query_id($OldQueryID); + } + if (strtotime($UserInfo['Warned']) < time()) { + $UserInfo['Warned'] = '0000-00-00 00:00:00'; + G::$Cache->cache_value("user_info_$UserID", $UserInfo, 2592000); + } + + return $UserInfo; + } + + /** + * Gets the heavy user info + * Only used for current user + * + * @param string $UserID The userid to get the information for + * @return array fetched heavy info. + * Just read the goddamn code, I don't have time to comment this shit. + */ + public static function user_heavy_info($UserID) { + + $HeavyInfo = G::$Cache->get_value("user_info_heavy_$UserID"); + if (empty($HeavyInfo)) { + + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + m.Invites, + m.torrent_pass, + m.IP, + m.CustomPermissions, + m.can_leech AS CanLeech, + m.IRCKey, + i.AuthKey, + i.RatioWatchEnds, + i.RatioWatchDownload, + i.StyleID, + i.StyleURL, + i.DisableInvites, + i.DisablePosting, + i.DisableUpload, + i.DisablePoints, + i.DisableWiki, + i.DisableAvatar, + i.DisablePM, + i.DisableRequests, + i.DisableForums, + i.DisableIRC, + i.DisableTagging," . " + i.SiteOptions, + i.DownloadAlt, + i.LastReadNews, + i.LastReadBlog, + i.RestrictedForums, + i.PermittedForums, + i.NavItems, + m.FLTokens, + m.PermissionID + FROM users_main AS m + INNER JOIN users_info AS i ON i.UserID = m.ID + WHERE m.ID = '$UserID'"); + $HeavyInfo = G::$DB->next_record(MYSQLI_ASSOC, array('CustomPermissions', 'SiteOptions')); + + $HeavyInfo['CustomPermissions'] = unserialize_array($HeavyInfo['CustomPermissions']); + + if (!empty($HeavyInfo['RestrictedForums'])) { + $RestrictedForums = array_map('trim', explode(',', $HeavyInfo['RestrictedForums'])); + } else { + $RestrictedForums = []; + } + unset($HeavyInfo['RestrictedForums']); + if (!empty($HeavyInfo['PermittedForums'])) { + $PermittedForums = array_map('trim', explode(',', $HeavyInfo['PermittedForums'])); + } else { + $PermittedForums = []; + } + unset($HeavyInfo['PermittedForums']); + if (!empty($HeavyInfo['NavItems'])) { + $NavItems = array_map('trim', explode(',', $HeavyInfo['NavItems'])); + } else { + $NavItems = []; + } + $HeavyInfo['NavItems'] = $NavItems; + + G::$DB->query(" + SELECT PermissionID + FROM users_levels + WHERE UserID = $UserID"); + $PermIDs = G::$DB->collect('PermissionID'); + foreach ($PermIDs AS $PermID) { + $Perms = Permissions::get_permissions($PermID); + if (!empty($Perms['PermittedForums'])) { + $PermittedForums = array_merge($PermittedForums, array_map('trim', explode(',', $Perms['PermittedForums']))); + } + } + $Perms = Permissions::get_permissions($HeavyInfo['PermissionID']); + unset($HeavyInfo['PermissionID']); + if (!empty($Perms['PermittedForums'])) { + $PermittedForums = array_merge($PermittedForums, array_map('trim', explode(',', $Perms['PermittedForums']))); + } + + if (!empty($PermittedForums) || !empty($RestrictedForums)) { + $HeavyInfo['CustomForums'] = []; + foreach ($RestrictedForums as $ForumID) { + $HeavyInfo['CustomForums'][$ForumID] = 0; + } + foreach ($PermittedForums as $ForumID) { + $HeavyInfo['CustomForums'][$ForumID] = 1; + } + } else { + $HeavyInfo['CustomForums'] = null; + } + if (isset($HeavyInfo['CustomForums'][''])) { + unset($HeavyInfo['CustomForums']['']); + } + + $HeavyInfo['SiteOptions'] = unserialize_array($HeavyInfo['SiteOptions']); + $HeavyInfo['SiteOptions'] = array_merge(static::default_site_options(), $HeavyInfo['SiteOptions']); + $HeavyInfo = array_merge($HeavyInfo, $HeavyInfo['SiteOptions']); + + unset($HeavyInfo['SiteOptions']); + + G::$DB->set_query_id($QueryID); + + G::$Cache->cache_value("user_info_heavy_$UserID", $HeavyInfo, 0); + } + return $HeavyInfo; + } + + public static function get_user_nav_items($userId) { + $Info = self::user_heavy_info($userId); + + $UserIds = !empty($Info['NavItems']) ? $Info['NavItems'] : []; + $NavItems = self::get_nav_items(); + + + $UserItems = []; + foreach ($NavItems as $n) { + if (($n['mandatory'] || in_array($n['id'], $UserIds)) || + (!count($UserIds) && $n['initial'])) { + $UserItems[] = $n; + } + } + + return $UserItems; + } + + public static function get_nav_items() { + $Items = G::$Cache->get_value("nav_items"); + if (!$Items) { + $QueryID = G::$DB->get_query_id(); + G::$DB->prepared_query(" + SELECT id, tag, title, target, tests, test_user, mandatory, initial + FROM nav_items"); + $Items = G::$DB->to_array("id", MYSQLI_ASSOC); + G::$Cache->cache_value("nav_items", $Items, 0); + G::$DB->set_query_id($QueryID); + } + return $Items; + } + + /** + * Return the ID of a Username + * @param string Username + * @return userID if exists, null otherwise + */ + public static function ID_from_username($name) { + $digest = base64_encode(md5($name, true)); + $key = "username_id_$digest"; + $ID = G::$Cache->get_value($key); + if ($ID == -1) { + return null; + } + elseif ($ID === false) { + G::$DB->prepared_query("SELECT ID FROM users_main WHERE Username=?", $name); + if (!G::$DB->has_results()) { + // cache negative hits for a while + G::$Cache->cache_value($key, -1, 300); + return null; + } + list($ID) = G::$DB->next_record(); + G::$Cache->cache_value($key, $ID, 300); + } + return $ID; + } + + /** + * Does this ID point to an existing user? + * @param integer ID + * @return boolean + */ + public static function exists($ID) { + G::$DB->prepared_query("SELECT 1 FROM users_main WHERE ID = ?", $ID); + return G::$DB->has_results(); + } + + /** + * Default settings to use for SiteOptions + * @return array + */ + public static function default_site_options() { + return array( + 'HttpsTracker' => true + ); + } + + /** + * Updates the site options in the database + * + * @param int $UserID the UserID to set the options for + * @param array $NewOptions the new options to set + * @return false if $NewOptions is empty, true otherwise + */ + public static function update_site_options($UserID, $NewOptions) { + if (!is_number($UserID)) { + error(0); + } + if (empty($NewOptions)) { + return false; + } + + $QueryID = G::$DB->get_query_id(); + + // Get SiteOptions + G::$DB->query(" + SELECT SiteOptions + FROM users_info + WHERE UserID = $UserID"); + list($SiteOptions) = G::$DB->next_record(MYSQLI_NUM, false); + $SiteOptions = unserialize_array($SiteOptions); + $SiteOptions = array_merge(static::default_site_options(), $SiteOptions); + + // Get HeavyInfo + $HeavyInfo = Users::user_heavy_info($UserID); + + // Insert new/replace old options + $SiteOptions = array_merge($SiteOptions, $NewOptions); + $HeavyInfo = array_merge($HeavyInfo, $NewOptions); + + // Update DB + G::$DB->query(" + UPDATE users_info + SET SiteOptions = '".db_string(serialize($SiteOptions))."' + WHERE UserID = $UserID"); + G::$DB->set_query_id($QueryID); + + // Update cache + G::$Cache->cache_value("user_info_heavy_$UserID", $HeavyInfo, 0); + + // Update G::$LoggedUser if the options are changed for the current + if (G::$LoggedUser['ID'] == $UserID) { + G::$LoggedUser = array_merge(G::$LoggedUser, $NewOptions); + G::$LoggedUser['ID'] = $UserID; // We don't want to allow userid switching + } + return true; + } + + /** + * Generates a check list of release types, ordered by the user or default + * @param array $SiteOptions + * @param boolean $Default Returns the default list if true + */ + public static function release_order(&$SiteOptions, $Default = false) { + global $ReleaseTypes; + + $RT = $ReleaseTypes + array( + 1024 => 'Guest Appearance', + 1023 => 'Remixed By', + 1022 => 'Composition', + 1021 => 'Produced By'); + + if ($Default || empty($SiteOptions['SortHide'])) { + $Sort =& $RT; + $Defaults = !empty($SiteOptions['HideTypes']); + } else { + $Sort =& $SiteOptions['SortHide']; + $MissingTypes = array_diff_key($RT, $Sort); + if (!empty($MissingTypes)) { + foreach (array_keys($MissingTypes) as $Missing) { + $Sort[$Missing] = 0; + } + } + } + + foreach ($Sort as $Key => $Val) { + if (isset($Defaults)) { + $Checked = $Defaults && isset($SiteOptions['HideTypes'][$Key]) ? ' checked="checked"' : ''; + } else { + if (!isset($RT[$Key])) { + continue; + } + $Checked = $Val ? ' checked="checked"' : ''; + $Val = $RT[$Key]; + } + + $ID = $Key. '_' . (int)(!!$Checked); + + // The HTML is indented this far for proper indentation in the generated HTML + // on user.php?action=edit ?> -
    • - -
    • - ShortForm - $SecondaryClasses = array( - '23' => 'FLS', // First Line Support - '30' => 'IN', // Interviewer - '31' => 'TC', // Torrent Celebrity - '32' => 'D', // Designer - '33' => 'ST', // Security Team - '37' => 'AR', // Archive Team - '36' => 'AT', // Alpha Team - '48' => 'BT', // Beta TEam - '38' => 'CT', // Charlie Team - '39' => 'DT', // Delta Team - ); - - if ($UserID == 0) { - return 'System'; - } - - $UserInfo = self::user_info($UserID); - if ($UserInfo['Username'] == '') { - return "Unknown [$UserID]"; - } - - $Str = ''; - - $Username = $UserInfo['Username']; - $Paranoia = $UserInfo['Paranoia']; - - if ($UserInfo['Class'] < $Classes[MOD]['Level']) { - $OverrideParanoia = check_perms('users_override_paranoia', $UserInfo['Class']); - } else { - // Don't override paranoia for mods who don't want to show their donor heart - $OverrideParanoia = false; - } - $ShowDonorIcon = (!in_array('hide_donor_heart', $Paranoia) || $OverrideParanoia); - - if ($IsDonorForum) { - list($Prefix, $Suffix, $HasComma) = Donations::get_titles($UserID); - $Username = "$Prefix $Username" . ($HasComma ? ', ' : ' ') . "$Suffix "; - } - - if ($Title) { - $Str .= "$Username"; - } else { - $Str .= "$Username"; - } - if ($Badges) { - $DonorRank = Donations::get_rank($UserID); - if ($DonorRank == 0 && $UserInfo['Donor'] == 1) { - $DonorRank = 1; - } - if ($ShowDonorIcon && $DonorRank > 0) { - $IconLink = 'donate.php'; - $IconImage = 'donor.png'; - $IconText = 'Donor'; - $DonorHeart = $DonorRank; - $SpecialRank = Donations::get_special_rank($UserID); - $EnabledRewards = Donations::get_enabled_rewards($UserID); - $DonorRewards = Donations::get_rewards($UserID); - if ($EnabledRewards['HasDonorIconMouseOverText'] && !empty($DonorRewards['IconMouseOverText'])) { - $IconText = display_str($DonorRewards['IconMouseOverText']); - } - if ($EnabledRewards['HasDonorIconLink'] && !empty($DonorRewards['CustomIconLink'])) { - $IconLink = display_str($DonorRewards['CustomIconLink']); - } - if ($EnabledRewards['HasCustomDonorIcon'] && !empty($DonorRewards['CustomIcon'])) { - $IconImage = ImageTools::process($DonorRewards['CustomIcon'], false, 'donoricon', $UserID); - } else { - if ($SpecialRank === MAX_SPECIAL_RANK) { - $DonorHeart = 6; - } elseif ($DonorRank === 5) { - $DonorHeart = 4; // Two points between rank 4 and 5 - } elseif ($DonorRank >= MAX_RANK) { - $DonorHeart = 5; - } - if ($DonorHeart === 1) { - $IconImage = STATIC_SERVER . 'common/symbols/donor.png'; - } else { - $IconImage = STATIC_SERVER . "common/symbols/donor_{$DonorHeart}.png"; - } - } - $Str .= "\"$IconText\""; - } - } - - $Str .= ($IsWarned && $UserInfo['Warned'] != '0000-00-00 00:00:00') ? 'Warned' : ''; - $Str .= ($IsEnabled && $UserInfo['Enabled'] == 2) ? 'Banned' : ''; - - if ($Badges) { - $ClassesDisplay = array(); - foreach (array_intersect_key($SecondaryClasses, $UserInfo['ExtraClasses']) as $PermID => $PermShort) { - $ClassesDisplay[] = ''.$PermShort.''; - } - if (!empty($ClassesDisplay)) { - $Str .= ' '.implode(' ', $ClassesDisplay); - } - } - - if ($Class) { - if ($Title) { - $Str .= ' ('.Users::make_class_string($UserInfo['PermissionID']).')'; - } else { - $Str .= ' ('.Users::make_class_string($UserInfo['PermissionID']).')'; - } - } - - if ($Title) { - // Image proxy CTs - if (check_perms('site_proxy_images') && !empty($UserInfo['Title'])) { - $UserInfo['Title'] = preg_replace_callback('~src=("?)(http.+?)(["\s>])~', - function($Matches) { - return 'src=' . $Matches[1] . ImageTools::process($Matches[2]) . $Matches[3]; - }, - $UserInfo['Title']); - } - - if ($UserInfo['Title']) { - $Str .= ' ('.$UserInfo['Title'].')'; - } - } - return $Str; - } - - /** - * Given a class ID, return its name. - * - * @param int $ClassID - * @return string name - */ - public static function make_class_string($ClassID) { - global $Classes; - return $Classes[$ClassID]['Name']; - } - - /** - * Returns an array with User Bookmark data: group IDs, collage data, torrent data - * @param string|int $UserID - * @return array Group IDs, Bookmark Data, Torrent List - */ - public static function get_bookmarks($UserID) { - $UserID = (int)$UserID; - - if (($Data = G::$Cache->get_value("bookmarks_group_ids_$UserID"))) { - list($GroupIDs, $BookmarkData) = $Data; - } else { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT GroupID, Sort, `Time` - FROM bookmarks_torrents - WHERE UserID = $UserID - ORDER BY Sort, `Time` ASC"); - $GroupIDs = G::$DB->collect('GroupID'); - $BookmarkData = G::$DB->to_array('GroupID', MYSQLI_ASSOC); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value("bookmarks_group_ids_$UserID", - array($GroupIDs, $BookmarkData), 3600); - } - - $TorrentList = Torrents::get_groups($GroupIDs); - - return array($GroupIDs, $BookmarkData, $TorrentList); - } - - /** - * Generate HTML for a user's avatar or just return the avatar URL - * @param unknown $Avatar - * @param unknown $UserID - * @param unknown $Username - * @param unknown $Setting - * @param number $Size - * @param string $ReturnHTML - * @return string - */ - public static function show_avatar($Avatar, $UserID, $Username, $Setting, $Size = 150, $ReturnHTML = True) { - $Avatar = ImageTools::process($Avatar, false, 'avatar', $UserID); - $AvatarMouseOverText = ''; - $FirstAvatar = ''; - $SecondAvatar = ''; - $EnabledRewards = Donations::get_enabled_rewards($UserID); - - if ($EnabledRewards['HasAvatarMouseOverText']) { - $Rewards = Donations::get_rewards($UserID); - $AvatarMouseOverText = $Rewards['AvatarMouseOverText']; - } - if (!empty($AvatarMouseOverText)) { - $AvatarMouseOverText = "title=\"$AvatarMouseOverText\" alt=\"$AvatarMouseOverText\""; - } else { - $AvatarMouseOverText = "alt=\"$Username's avatar\""; - } - if ($EnabledRewards['HasSecondAvatar'] && !empty($Rewards['SecondAvatar'])) { - $SecondAvatar = ImageTools::process($Rewards['SecondAvatar'], false, 'avatar2', $UserID); - } - $Attrs = "width=\"$Size\" $AvatarMouseOverText"; - // purpose of the switch is to set $FirstAvatar (URL) - // case 1 is avatars disabled - switch ($Setting) { - case 0: - if (!empty($Avatar)) { - $FirstAvatar = $Avatar; - } else { - $FirstAvatar = STATIC_SERVER.'common/avatars/default.png'; - } - break; - case 2: - $ShowAvatar = true; - // Fallthrough - case 3: - if ($ShowAvatar && !empty($Avatar)) { - $FirstAvatar = $Avatar; - break; - } - switch (G::$LoggedUser['Identicons']) { - case 0: - $Type = 'identicon'; - break; - case 1: - $Type = 'monsterid'; - break; - case 2: - $Type = 'wavatar'; - break; - case 3: - $Type = 'retro'; - break; - case 4: - $Type = '1'; - $Robot = true; - break; - case 5: - $Type = '2'; - $Robot = true; - break; - case 6: - $Type = '3'; - $Robot = true; - break; - default: - $Type = 'identicon'; - } - $Rating = 'pg'; - if (!$Robot) { - $FirstAvatar = 'https://secure.gravatar.com/avatar/'.md5(strtolower(trim($Username)))."?s=$Size&d=$Type&r=$Rating"; - } else { - $FirstAvatar = 'https://robohash.org/'.md5($Username)."?set=set$Type&size={$Size}x$Size"; - } - break; - default: - $FirstAvatar = STATIC_SERVER.'common/avatars/default.png'; - } - // in this case, $Attrs is actually just a URL - if (!$ReturnHTML) { - return $FirstAvatar; - } - $ToReturn = '
      '; - foreach ([$FirstAvatar, $SecondAvatar] as $AvatarNum => $CurAvatar) { - if ($CurAvatar) { - $ToReturn .= "
      "; - } - } - $ToReturn .= '
      '; - return $ToReturn; - } - - public static function has_avatars_enabled() { - global $HeavyInfo; - return $HeavyInfo['DisableAvatars'] != 1; - } - - /** - * Checks whether user has autocomplete enabled - * - * 0 - Enabled everywhere (default), 1 - Disabled, 2 - Searches only - * - * @param string $Type the type of the input. - * @param boolean $Output echo out HTML - * @return boolean - */ - public static function has_autocomplete_enabled($Type, $Output = true) { - $Enabled = false; - if (empty(G::$LoggedUser['AutoComplete'])) { - $Enabled = true; - } elseif (G::$LoggedUser['AutoComplete'] !== 1) { - switch ($Type) { - case 'search': - if (G::$LoggedUser['AutoComplete'] == 2) { - $Enabled = true; - } - break; - case 'other': - if (G::$LoggedUser['AutoComplete'] != 2) { - $Enabled = true; - } - break; - } - } - if ($Enabled && $Output) { - echo ' data-gazelle-autocomplete="true"'; - } - if (!$Output) { - // don't return a boolean if you're echoing HTML - return $Enabled; - } - } - - /** - * Initiate a password reset - * - * @param int $UserID The user ID - * @param string $Username The username - * @param string $Email The email address - */ - public static function resetPassword($UserID, $Username, $Email) - { - $ResetKey = Users::make_secret(); - G::$DB->query(" - UPDATE users_info - SET - ResetKey = '" . db_string($ResetKey) . "', - ResetExpires = '" . time_plus(60 * 60) . "' - WHERE UserID = '$UserID'"); - - require(SERVER_ROOT . '/classes/templates.class.php'); - $TPL = NEW TEMPLATE; - $TPL->open(SERVER_ROOT . '/templates/password_reset.tpl'); // Password reset template - - $TPL->set('Username', $Username); - $TPL->set('ResetKey', $ResetKey); - $TPL->set('IP', $_SERVER['REMOTE_ADDR']); - $TPL->set('SITE_NAME', SITE_NAME); - $TPL->set('SITE_URL', NONSSL_SITE_URL); - - Misc::send_email($Email, 'Password reset information for ' . SITE_NAME, $TPL->get(), 'noreply'); - } - - /** - * Removes the custom title of a user - * - * @param integer $ID The id of the user in users_main - */ - public static function removeCustomTitle($ID) { - G::$DB->prepared_query("UPDATE users_main SET Title='' WHERE ID = ? ", $ID); - G::$Cache->delete_value("user_info_{$ID}"); - G::$Cache->delete_value("user_stats_{$ID}"); - } - - /** - * Purchases the custom title for a user - * - * @param integer $ID The id of the user in users_main - * @param string $Title The text of the title (may contain BBcode) - * @return boolean false if insufficient funds, otherwise true - */ - public static function setCustomTitle($ID, $Title) { - G::$DB->prepared_query("UPDATE users_main SET Title = ? WHERE ID = ?", - $Title, $ID); - if (G::$DB->affected_rows() == 1) { - G::$Cache->delete_value("user_info_{$ID}"); - G::$Cache->delete_value("user_stats_{$ID}"); - return true; - } - return false; - } - - /** - * Checks whether a user is allowed to purchase an invite. User classes up to Elite are capped, - * users above this class will always return true. - * - * @param integer $ID The id of the user in users_main - * @param integer $MinClass Minimum class level necessary to purchase invites - * @return boolean false if insufficient funds, otherwise true - */ - public static function canPurchaseInvite($ID, $MinClass) { - $heavy = self::user_heavy_info($ID); - if ($heavy['DisableInvites']) { - return false; - } - $info = self::user_info($ID); - return $info['EffectiveClass'] >= $MinClass; - } +
    • + +
    • + ShortForm + $SecondaryClasses = array( + '23' => 'FLS', // First Line Support + '30' => 'IN', // Interviewer + '31' => 'TC', // Torrent Celebrity + '32' => 'D', // Designer + '33' => 'ST', // Security Team + '37' => 'AR', // Archive Team + '36' => 'AT', // Alpha Team + '48' => 'BT', // Beta TEam + '38' => 'CT', // Charlie Team + '39' => 'DT', // Delta Team + ); + + if ($UserID == 0) { + return 'System'; + } + + $UserInfo = self::user_info($UserID); + if ($UserInfo['Username'] == '') { + return "Unknown [$UserID]"; + } + + $Str = ''; + + $Username = $UserInfo['Username']; + $Paranoia = $UserInfo['Paranoia']; + + if ($UserInfo['Class'] < $Classes[MOD]['Level']) { + $OverrideParanoia = check_perms('users_override_paranoia', $UserInfo['Class']); + } else { + // Don't override paranoia for mods who don't want to show their donor heart + $OverrideParanoia = false; + } + $ShowDonorIcon = (!in_array('hide_donor_heart', $Paranoia) || $OverrideParanoia); + + if ($IsDonorForum) { + list($Prefix, $Suffix, $HasComma) = Donations::get_titles($UserID); + $Username = "$Prefix $Username" . ($HasComma ? ', ' : ' ') . "$Suffix "; + } + + if ($Title) { + $Str .= "$Username"; + } else { + $Str .= "$Username"; + } + if ($Badges) { + $DonorRank = Donations::get_rank($UserID); + if ($DonorRank == 0 && $UserInfo['Donor'] == 1) { + $DonorRank = 1; + } + if ($ShowDonorIcon && $DonorRank > 0) { + $IconLink = 'donate.php'; + $IconImage = 'donor.png'; + $IconText = 'Donor'; + $DonorHeart = $DonorRank; + $SpecialRank = Donations::get_special_rank($UserID); + $EnabledRewards = Donations::get_enabled_rewards($UserID); + $DonorRewards = Donations::get_rewards($UserID); + if ($EnabledRewards['HasDonorIconMouseOverText'] && !empty($DonorRewards['IconMouseOverText'])) { + $IconText = display_str($DonorRewards['IconMouseOverText']); + } + if ($EnabledRewards['HasDonorIconLink'] && !empty($DonorRewards['CustomIconLink'])) { + $IconLink = display_str($DonorRewards['CustomIconLink']); + } + if ($EnabledRewards['HasCustomDonorIcon'] && !empty($DonorRewards['CustomIcon'])) { + $IconImage = ImageTools::process($DonorRewards['CustomIcon'], false, 'donoricon', $UserID); + } else { + if ($SpecialRank === MAX_SPECIAL_RANK) { + $DonorHeart = 6; + } elseif ($DonorRank === 5) { + $DonorHeart = 4; // Two points between rank 4 and 5 + } elseif ($DonorRank >= MAX_RANK) { + $DonorHeart = 5; + } + if ($DonorHeart === 1) { + $IconImage = STATIC_SERVER . 'common/symbols/donor.png'; + } else { + $IconImage = STATIC_SERVER . "common/symbols/donor_{$DonorHeart}.png"; + } + } + $Str .= "\"$IconText\""; + } + } + + $Str .= ($IsWarned && $UserInfo['Warned'] != '0000-00-00 00:00:00') ? 'Warned' : ''; + $Str .= ($IsEnabled && $UserInfo['Enabled'] == 2) ? 'Banned' : ''; + + if ($Badges) { + $ClassesDisplay = []; + foreach (array_intersect_key($SecondaryClasses, $UserInfo['ExtraClasses']) as $PermID => $PermShort) { + $ClassesDisplay[] = ''.$PermShort.''; + } + if (!empty($ClassesDisplay)) { + $Str .= ' '.implode(' ', $ClassesDisplay); + } + } + + if ($Class) { + if ($Title) { + $Str .= ' ('.Users::make_class_string($UserInfo['PermissionID']).')'; + } else { + $Str .= ' ('.Users::make_class_string($UserInfo['PermissionID']).')'; + } + } + + if ($Title) { + // Image proxy CTs + if (check_perms('site_proxy_images') && !empty($UserInfo['Title'])) { + $UserInfo['Title'] = preg_replace_callback('~src=("?)(http.+?)(["\s>])~', + function($Matches) { + return 'src=' . $Matches[1] . ImageTools::process($Matches[2]) . $Matches[3]; + }, + $UserInfo['Title']); + } + + if ($UserInfo['Title']) { + $Str .= ' ('.$UserInfo['Title'].')'; + } + } + return $Str; + } + + /** + * Given a class ID, return its name. + * + * @param int $ClassID + * @return string name + */ + public static function make_class_string($ClassID) { + global $Classes; + return $Classes[$ClassID]['Name']; + } + + /** + * Returns an array with User Bookmark data: group IDs, collage data, torrent data + * @param string|int $UserID + * @return array Group IDs, Bookmark Data, Torrent List + */ + public static function get_bookmarks($UserID) { + $UserID = (int)$UserID; + + if (($Data = G::$Cache->get_value("bookmarks_group_ids_$UserID"))) { + list($GroupIDs, $BookmarkData) = $Data; + } else { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT GroupID, Sort, `Time` + FROM bookmarks_torrents + WHERE UserID = $UserID + ORDER BY Sort, `Time` ASC"); + $GroupIDs = G::$DB->collect('GroupID'); + $BookmarkData = G::$DB->to_array('GroupID', MYSQLI_ASSOC); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value("bookmarks_group_ids_$UserID", + array($GroupIDs, $BookmarkData), 3600); + } + + $TorrentList = Torrents::get_groups($GroupIDs); + + return array($GroupIDs, $BookmarkData, $TorrentList); + } + + /** + * Generate HTML for a user's avatar or just return the avatar URL + * @param unknown $Avatar + * @param unknown $UserID + * @param unknown $Username + * @param unknown $Setting + * @param number $Size + * @param string $ReturnHTML + * @return string + */ + public static function show_avatar($Avatar, $UserID, $Username, $Setting, $Size = 150, $ReturnHTML = True) { + $Avatar = ImageTools::process($Avatar, false, 'avatar', $UserID); + $AvatarMouseOverText = ''; + $FirstAvatar = ''; + $SecondAvatar = ''; + $EnabledRewards = Donations::get_enabled_rewards($UserID); + + if ($EnabledRewards['HasAvatarMouseOverText']) { + $Rewards = Donations::get_rewards($UserID); + $AvatarMouseOverText = $Rewards['AvatarMouseOverText']; + } + if (!empty($AvatarMouseOverText)) { + $AvatarMouseOverText = "title=\"$AvatarMouseOverText\" alt=\"$AvatarMouseOverText\""; + } else { + $AvatarMouseOverText = "alt=\"$Username's avatar\""; + } + if ($EnabledRewards['HasSecondAvatar'] && !empty($Rewards['SecondAvatar'])) { + $SecondAvatar = ImageTools::process($Rewards['SecondAvatar'], false, 'avatar2', $UserID); + } + $Attrs = "width=\"$Size\" $AvatarMouseOverText"; + // purpose of the switch is to set $FirstAvatar (URL) + // case 1 is avatars disabled + switch ($Setting) { + case 0: + if (!empty($Avatar)) { + $FirstAvatar = $Avatar; + } else { + $FirstAvatar = STATIC_SERVER.'common/avatars/default.png'; + } + break; + case 2: + $ShowAvatar = true; + // Fallthrough + case 3: + if ($ShowAvatar && !empty($Avatar)) { + $FirstAvatar = $Avatar; + break; + } + switch (G::$LoggedUser['Identicons']) { + case 0: + $Type = 'identicon'; + break; + case 1: + $Type = 'monsterid'; + break; + case 2: + $Type = 'wavatar'; + break; + case 3: + $Type = 'retro'; + break; + case 4: + $Type = '1'; + $Robot = true; + break; + case 5: + $Type = '2'; + $Robot = true; + break; + case 6: + $Type = '3'; + $Robot = true; + break; + default: + $Type = 'identicon'; + } + $Rating = 'pg'; + if (!$Robot) { + $FirstAvatar = 'https://secure.gravatar.com/avatar/'.md5(strtolower(trim($Username)))."?s=$Size&d=$Type&r=$Rating"; + } else { + $FirstAvatar = 'https://robohash.org/'.md5($Username)."?set=set$Type&size={$Size}x$Size"; + } + break; + default: + $FirstAvatar = STATIC_SERVER.'common/avatars/default.png'; + } + // in this case, $Attrs is actually just a URL + if (!$ReturnHTML) { + return $FirstAvatar; + } + $ToReturn = '
      '; + foreach ([$FirstAvatar, $SecondAvatar] as $AvatarNum => $CurAvatar) { + if ($CurAvatar) { + $ToReturn .= "
      "; + } + } + $ToReturn .= '
      '; + return $ToReturn; + } + + public static function has_avatars_enabled() { + global $HeavyInfo; + return $HeavyInfo['DisableAvatars'] != 1; + } + + /** + * Checks whether user has autocomplete enabled + * + * 0 - Enabled everywhere (default), 1 - Disabled, 2 - Searches only + * + * @param string $Type the type of the input. + * @param boolean $Output echo out HTML + * @return boolean + */ + public static function has_autocomplete_enabled($Type, $Output = true) { + $Enabled = false; + if (empty(G::$LoggedUser['AutoComplete'])) { + $Enabled = true; + } elseif (G::$LoggedUser['AutoComplete'] !== 1) { + switch ($Type) { + case 'search': + if (G::$LoggedUser['AutoComplete'] == 2) { + $Enabled = true; + } + break; + case 'other': + if (G::$LoggedUser['AutoComplete'] != 2) { + $Enabled = true; + } + break; + } + } + if ($Enabled && $Output) { + echo ' data-gazelle-autocomplete="true"'; + } + if (!$Output) { + // don't return a boolean if you're echoing HTML + return $Enabled; + } + } + + /** + * Initiate a password reset + * + * @param int $UserID The user ID + * @param string $Username The username + * @param string $Email The email address + */ + public static function resetPassword($UserID, $Username, $Email) + { + $ResetKey = Users::make_secret(); + G::$DB->prepared_query(" + UPDATE users_info + SET + ResetKey = ?, + ResetExpires = ? + WHERE UserID = ?", $ResetKey, time_plus(60 * 60), $UserID); + + $template = G::$Twig->render('emails/password_reset.twig', [ + 'Username' => $Username, + 'ResetKey' => $ResetKey, + 'IP' => $_SERVER['REMOTE_ADDR'], + 'SITE_NAME' => SITE_NAME, + 'SITE_URL' => SITE_URL + ]); + + Misc::send_email($Email, 'Password reset information for ' . SITE_NAME, $template, 'noreply'); + } + + /** + * Removes the custom title of a user + * + * @param integer $ID The id of the user in users_main + */ + public static function removeCustomTitle($ID) { + G::$DB->prepared_query("UPDATE users_main SET Title='' WHERE ID = ? ", $ID); + G::$Cache->delete_value("user_info_{$ID}"); + G::$Cache->delete_value("user_stats_{$ID}"); + } + + /** + * Purchases the custom title for a user + * + * @param integer $ID The id of the user in users_main + * @param string $Title The text of the title (may contain BBcode) + * @return boolean false if insufficient funds, otherwise true + */ + public static function setCustomTitle($ID, $Title) { + G::$DB->prepared_query("UPDATE users_main SET Title = ? WHERE ID = ?", + $Title, $ID); + if (G::$DB->affected_rows() == 1) { + G::$Cache->delete_value("user_info_{$ID}"); + G::$Cache->delete_value("user_stats_{$ID}"); + return true; + } + return false; + } + + /** + * Checks whether a user is allowed to purchase an invite. User classes up to Elite are capped, + * users above this class will always return true. + * + * @param integer $ID The id of the user in users_main + * @param integer $MinClass Minimum class level necessary to purchase invites + * @return boolean false if insufficient funds, otherwise true + */ + public static function canPurchaseInvite($ID, $MinClass) { + $heavy = self::user_heavy_info($ID); + if ($heavy['DisableInvites']) { + return false; + } + $info = self::user_info($ID); + return $info['EffectiveClass'] >= $MinClass; + } + + /** + * Get the count of enabled users. + * + * @return integer Number of enabled users (this is cached). + */ + public static function get_enabled_users_count() { + $count = G::$Cache->get_value('stats_user_count'); + if (!$count) { + G::$DB->query("SELECT count(*) FROM users_main WHERE Enabled = '1'"); + list($count) = G::$DB->next_record(); + G::$Cache->cache_value('stats_user_count', $count, 0); + } + return $count; + } + + /** + * Flush the count of enabled users. Call a user is enabled or disabled. + */ + public static function flush_enabled_users_count() { + G::$Cache->delete_value('stats_user_count'); + } } diff --git a/classes/util.php b/classes/util.php index 02eb31f81..195b2acc1 100644 --- a/classes/util.php +++ b/classes/util.php @@ -1,4 +1,4 @@ -", - '€','‚','ƒ','„','…','†','‡','ˆ', - '‰','Š','‹','Œ','Ž','‘','’','“', - '”','•','–','—','˜','™','š','›', - 'œ','ž','Ÿ' - ); - - $With = array( - ''','"','<','>', - '€','‚','ƒ','„','…','†','‡','ˆ', - '‰','Š','‹','Œ','Ž','‘','’','“', - '”','•','–','—','˜','™','š','›', - 'œ','ž','Ÿ' - ); - - $Str = str_replace($Replace, $With, $Str); - } - return $Str; + if ($Str === null || $Str === false || is_array($Str)) { + return ''; + } + if ($Str != '' && !is_number($Str)) { + $Str = Format::make_utf8($Str); + $Str = mb_convert_encoding($Str, 'HTML-ENTITIES', 'UTF-8'); + $Str = preg_replace("/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,6};)/m", '&', $Str); + + $Replace = array( + "'",'"',"<",">", + '€','‚','ƒ','„','…','†','‡','ˆ', + '‰','Š','‹','Œ','Ž','‘','’','“', + '”','•','–','—','˜','™','š','›', + 'œ','ž','Ÿ' + ); + + $With = array( + ''','"','<','>', + '€','‚','ƒ','„','…','†','‡','ˆ', + '‰','Š','‹','Œ','Ž','‘','’','“', + '”','•','–','—','˜','™','š','›', + 'œ','ž','Ÿ' + ); + + $Str = str_replace($Replace, $With, $Str); + } + return $Str; } +/** + * Un-HTML-escape a string for output. + * + * It's like the above function, but in reverse. + * + * @param string $Str + * @return string unescaped string + */ +function reverse_display_str($Str) { + if ($Str === null || $Str === false || is_array($Str)) { + return ''; + } + if ($Str != '' && !is_number($Str)) { + $Replace = array( + ''','"','<','>', + '€','‚','ƒ','„','…','†','‡','ˆ', + '‰','Š','‹','Œ','Ž','‘','’','“', + '”','•','–','—','˜','™','š','›', + 'œ','ž','Ÿ' + ); + + $With = array( + "'",'"',"<",">", + '€','‚','ƒ','„','…','†','‡','ˆ', + '‰','Š','‹','Œ','Ž','‘','’','“', + '”','•','–','—','˜','™','š','›', + 'œ','ž','Ÿ' + ); + $Str = str_replace($Replace, $With, $Str); + + $Str = str_replace("&", "&", $Str); + $Str = mb_convert_encoding($Str, 'UTF-8', 'HTML-ENTITIES'); + } + return $Str; +} /** * Send a message to an IRC bot listening on SOCKET_LISTEN_PORT @@ -108,13 +143,13 @@ function display_str($Str) { * @param string $Raw An IRC protocol snippet to send. */ function send_irc($Raw) { - if (defined('DISABLE_IRC') && DISABLE_IRC === true) { - return; - } - $IRCSocket = fsockopen(SOCKET_LISTEN_ADDRESS, SOCKET_LISTEN_PORT); - $Raw = str_replace(array("\n", "\r"), '', $Raw); - fwrite($IRCSocket, $Raw); - fclose($IRCSocket); + if (defined('DISABLE_IRC') && DISABLE_IRC === true) { + return; + } + $IRCSocket = fsockopen(SOCKET_LISTEN_ADDRESS, SOCKET_LISTEN_PORT); + $Raw = str_replace(array("\n", "\r"), '', $Raw); + fwrite($IRCSocket, $Raw); + fclose($IRCSocket); } @@ -122,16 +157,16 @@ function send_irc($Raw) { * Display a critical error and kills the page. * * @param string $Error Error type. Automatically supported: - * 403, 404, 0 (invalid input), -1 (invalid request) - * If you use your own string for Error, it becomes the error description. + * 403, 404, 0 (invalid input), -1 (invalid request) + * If you use your own string for Error, it becomes the error description. * @param boolean $NoHTML If true, the header/footer won't be shown, just the description. * @param string $Log If true, the user is given a link to search $Log in the site log. */ function error($Error, $NoHTML = false, $Log = false) { - global $Debug; - require(SERVER_ROOT.'/sections/error/index.php'); - $Debug->profile(); - die(); + global $Debug; + require(SERVER_ROOT.'/sections/error/index.php'); + $Debug->profile(); + die(); } @@ -145,29 +180,29 @@ function error($Error, $NoHTML = false, $Log = false) { * @return bool */ function check_perms($PermissionName, $MinClass = 0) { - return Permissions::check_perms($PermissionName, $MinClass); + return Permissions::check_perms($PermissionName, $MinClass); } /** * Print JSON status result with an optional message and die. * DO NOT USE THIS FUNCTION! */ -function json_die($Status, $Message) { - json_print($Status, $Message); - die(); +function json_die($Status, $Message="bad parameters") { + json_print($Status, $Message); + die(); } /** * Print JSON status result with an optional message. */ function json_print($Status, $Message) { - if ($Status == 'success' && $Message) { - print json_encode(array('status' => $Status, 'response' => $Message)); - } elseif ($Message) { - print json_encode(array('status' => $Status, 'error' => $Message)); - } else { - print json_encode(array('status' => $Status, 'response' => array())); - } + if ($Status == 'success' && $Message) { + print json_encode(array('status' => $Status, 'response' => $Message)); + } elseif ($Message) { + print json_encode(array('status' => $Status, 'error' => $Message)); + } else { + print json_encode(array('status' => $Status, 'response' => [])); + } } /** @@ -177,7 +212,7 @@ function json_print($Status, $Message) { * @return url for site */ function site_url($SSL = true) { - return $SSL ? 'https://' . SSL_SITE_URL . '/' : 'http://' . NONSSL_SITE_URL . '/'; + return $SSL ? 'https://' . SSL_SITE_URL . '/' : 'http://' . NONSSL_SITE_URL . '/'; } /** @@ -204,8 +239,8 @@ function FL_confirmation_msg($seeders) { * @return array */ function unserialize_array($array) { - $array = empty($array) ? array() : unserialize($array); - return (empty($array)) ? array() : $array; + $array = empty($array) ? [] : unserialize($array); + return (empty($array)) ? [] : $array; } /** @@ -214,5 +249,5 @@ function unserialize_array($array) { * @return string */ function isset_array_checked($array, $value) { - return (isset($array[$value])) ? "checked" : ""; + return (isset($array[$value])) ? "checked" : ""; } diff --git a/classes/validate.class.php b/classes/validate.class.php index 1a3fb7aa8..66bb3fbb1 100644 --- a/classes/validate.class.php +++ b/classes/validate.class.php @@ -1,4 +1,4 @@ -Fields[$FieldName]['Type'] = strtolower($FieldType); - $this->Fields[$FieldName]['Required'] = $Required; - $this->Fields[$FieldName]['ErrorMessage'] = $ErrorMessage; - if (!empty($Options['maxlength'])) { - $this->Fields[$FieldName]['MaxLength'] = $Options['maxlength']; - } - if (!empty($Options['minlength'])) { - $this->Fields[$FieldName]['MinLength'] = $Options['minlength']; - } - if (!empty($Options['comparefield'])) { - $this->Fields[$FieldName]['CompareField'] = $Options['comparefield']; - } - if (!empty($Options['allowperiod'])) { - $this->Fields[$FieldName]['AllowPeriod'] = $Options['allowperiod']; - } - if (!empty($Options['allowcomma'])) { - $this->Fields[$FieldName]['AllowComma'] = $Options['allowcomma']; - } - if (!empty($Options['inarray'])) { - $this->Fields[$FieldName]['InArray'] = $Options['inarray']; - } - if (!empty($Options['regex'])) { - $this->Fields[$FieldName]['Regex'] = $Options['regex']; - } - } - - /** - * Given an associate array, iterate through each key checking to see if we've set the field to be validated. If - * the field is not blank or it's required or it's a date, then we must validate, else we can skip this field. - * - * Note: Regular expression constants can be found in classes/regex.php - * Note: All checks against length (value for number type) is inclusive of the Min/Max lengths - * - * Field types and how we validate them (see above for options for each field type): - * - string: make sure the string's length is within the set MinLength and MaxLength - * - number: perform regular expression for digits + periods (if set) + commas (if set), and check that the numeric - * falls within MinLength and MaxLength (using weak type coercion as necessary). This field cannot be left - * empty. - * - email: Checks to make sure the length of the email falls within MinLength and MaxLength and performs a - * preg_match using the EMAIL_REGEX constant against the field to check that it passes - * - link: Makes sure the length of the link falls between MinLength and MaxLength and performs a preg_match - * using the URL_REGEX constant against the field - * - username: checks that the length of the username falls within MinLength and MaxLength and performs a preg_match - * using the USERNAME_REGEX constant against the field - * - checkbox: just checks if the field exists within the associate array, doesn't matter the value - * - compare: compares the field against field specified in the CompareField option. Useful for stuff like password - * where you have to input it twice, check that the second password equals the first one - * - inarray: checks that the value specified in InArray option is in the field (which we assume is an array) - * - regex: performs a preg_match of the value of Regex option and the field - * - * TODO: date fields are not actually validated, need to figure out what the proper validation syntax should be. - * - * @param array $ValidateArray - * @return string|null - */ - function ValidateForm($ValidateArray) { - reset($this->Fields); - foreach ($this->Fields as $FieldKey => $Field) { - $ValidateVar = $ValidateArray[$FieldKey]; - - if ($ValidateVar != '' || !empty($Field['Required']) || $Field['Type'] == 'date') { - if ($Field['Type'] == 'string') { - if (isset($Field['MaxLength'])) { - $MaxLength = $Field['MaxLength']; - } else { - $MaxLength = 255; - } - if (isset($Field['MinLength'])) { - $MinLength = $Field['MinLength']; - } else { - $MinLength = 1; - } - - if ($MaxLength !== -1 && strlen($ValidateVar) > $MaxLength) { - return $Field['ErrorMessage']; - } elseif ($MinLength !== -1 && strlen($ValidateVar) < $MinLength) { - return $Field['ErrorMessage']; - } - - } elseif ($Field['Type'] == 'number') { - if (isset($Field['MaxLength'])) { - $MaxLength = $Field['MaxLength']; - } else { - $MaxLength = ''; - } - if (isset($Field['MinLength'])) { - $MinLength = $Field['MinLength']; - } else { - $MinLength = 0; - } - - $Match = '0-9'; - if (isset($Field['AllowPeriod'])) { - $Match .= '.'; - } - if (isset($Field['AllowComma'])) { - $Match .= ','; - } - - if (preg_match('/[^'.$Match.']/', $ValidateVar) || strlen($ValidateVar) < 1) { - return $Field['ErrorMessage']; - } elseif ($MaxLength != '' && $ValidateVar > $MaxLength) { - return $Field['ErrorMessage'].'!!'; - } elseif ($ValidateVar < $MinLength) { - return $Field['ErrorMessage']."$MinLength"; - } - - } elseif ($Field['Type'] == 'email') { - if (isset($Field['MaxLength'])) { - $MaxLength = $Field['MaxLength']; - } else { - $MaxLength = 255; - } - if (isset($Field['MinLength'])) { - $MinLength = $Field['MinLength']; - } else { - $MinLength = 6; - } - - if (!preg_match("/^".EMAIL_REGEX."$/i", $ValidateVar)) { - return $Field['ErrorMessage']; - } elseif (strlen($ValidateVar) > $MaxLength) { - return $Field['ErrorMessage']; - } elseif (strlen($ValidateVar) < $MinLength) { - return $Field['ErrorMessage']; - } - - } elseif ($Field['Type'] == 'link') { - if (isset($Field['MaxLength'])) { - $MaxLength = $Field['MaxLength']; - } else { - $MaxLength = 255; - } - if (isset($Field['MinLength'])) { - $MinLength = $Field['MinLength']; - } else { - $MinLength = 10; - } - - if (!preg_match('/^'.URL_REGEX.'$/i', $ValidateVar)) { - return $Field['ErrorMessage']; - } elseif (strlen($ValidateVar) > $MaxLength) { - return $Field['ErrorMessage']; - } elseif (strlen($ValidateVar) < $MinLength) { - return $Field['ErrorMessage']; - } - - } elseif ($Field['Type'] == 'username') { - if (isset($Field['MaxLength'])) { - $MaxLength = $Field['MaxLength']; - } else { - $MaxLength = 20; - } - if (isset($Field['MinLength'])) { - $MinLength = $Field['MinLength']; - } else { - $MinLength = 1; - } - - if (!preg_match(USERNAME_REGEX, $ValidateVar)) { - return $Field['ErrorMessage']; - } elseif (strlen($ValidateVar) > $MaxLength) { - return $Field['ErrorMessage']; - } elseif (strlen($ValidateVar) < $MinLength) { - return $Field['ErrorMessage']; - } - - } elseif ($Field['Type'] == 'checkbox') { - if (!isset($ValidateArray[$FieldKey])) { - return $Field['ErrorMessage']; - } - - } elseif ($Field['Type'] == 'compare') { - if ($ValidateArray[$Field['CompareField']] != $ValidateVar) { - return $Field['ErrorMessage']; - } - - } elseif ($Field['Type'] == 'inarray') { - if (array_search($ValidateVar, $Field['InArray']) === false) { - return $Field['ErrorMessage']; - } - - } elseif ($Field['Type'] == 'regex') { - if (!preg_match($Field['Regex'], $ValidateVar)) { - return $Field['ErrorMessage']; - } - } - } - } // while - } // function - - function GenerateJS($FormID) { - $ReturnJS = "\r\n"; - return $ReturnJS; - } + var $Fields = []; + + /** + * Add a new field to be validated (or used for JS form generation) from the associated array to be validated. + * For each field, you need to give it a name (its key in the array), whether the field is required or not (fields + * that are not required and blank and not a date won't be checked, else it'll always be validated), the type of + * field (see below), the error message to show users if validation fails, and any options (while you can set + * all options, certain ones will only affect certain types). + * + * Listed here are all the allowed field types, as well as then the options that are used for that particular type. + * See below for how exactly each type is checked. + * - string + * - MaxLength (if not set, defaults to 255) + * - MinLength (if not set, defaults to 1) + * - number + * - MaxLength (if not set, defaults to no upper bound) + * - MinLength (if not set, defaults to 0 + * - AllowPeriod (allow a period in the number) + * - AllowComma (allow a comma in the number) + * - email + * - MaxLength (if not set, defaults to 255) + * - MinLength (if not set, defaults to 6) + * - link + * - MaxLength (if not set, defaults to 255) + * - MinLength (if not set, defaults to 10) + * - username + * - MaxLength (if not set, defaults to 20) + * - MinLength (if not set, defaults to 1) + * - checkbox + * - compare + * - CompareField (required), what other field should this one be compared to in validation array + * - inarray + * - InArray (required), what value to check for within the value of the field/key in the validation array + * - regex + * - Regex (required), regular expression string to use within preg_match + * + * @param string $FieldName + * @param bool $Required + * @param string $FieldType + * @param string $ErrorMessage + * @param array $Options + */ + function SetFields($FieldName, $Required, $FieldType, $ErrorMessage, $Options = []) { + $this->Fields[$FieldName]['Type'] = strtolower($FieldType); + $this->Fields[$FieldName]['Required'] = $Required; + $this->Fields[$FieldName]['ErrorMessage'] = $ErrorMessage; + if (!empty($Options['maxlength'])) { + $this->Fields[$FieldName]['MaxLength'] = $Options['maxlength']; + } + if (!empty($Options['minlength'])) { + $this->Fields[$FieldName]['MinLength'] = $Options['minlength']; + } + if (!empty($Options['comparefield'])) { + $this->Fields[$FieldName]['CompareField'] = $Options['comparefield']; + } + if (!empty($Options['allowperiod'])) { + $this->Fields[$FieldName]['AllowPeriod'] = $Options['allowperiod']; + } + if (!empty($Options['allowcomma'])) { + $this->Fields[$FieldName]['AllowComma'] = $Options['allowcomma']; + } + if (!empty($Options['inarray'])) { + $this->Fields[$FieldName]['InArray'] = $Options['inarray']; + } + if (!empty($Options['regex'])) { + $this->Fields[$FieldName]['Regex'] = $Options['regex']; + } + } + + /** + * Given an associate array, iterate through each key checking to see if we've set the field to be validated. If + * the field is not blank or it's required or it's a date, then we must validate, else we can skip this field. + * + * Note: Regular expression constants can be found in classes/regex.php + * Note: All checks against length (value for number type) is inclusive of the Min/Max lengths + * + * Field types and how we validate them (see above for options for each field type): + * - string: make sure the string's length is within the set MinLength and MaxLength + * - number: perform regular expression for digits + periods (if set) + commas (if set), and check that the numeric + * falls within MinLength and MaxLength (using weak type coercion as necessary). This field cannot be left + * empty. + * - email: Checks to make sure the length of the email falls within MinLength and MaxLength and performs a + * preg_match using the EMAIL_REGEX constant against the field to check that it passes + * - link: Makes sure the length of the link falls between MinLength and MaxLength and performs a preg_match + * using the URL_REGEX constant against the field + * - username: checks that the length of the username falls within MinLength and MaxLength and performs a preg_match + * using the USERNAME_REGEX constant against the field + * - checkbox: just checks if the field exists within the associate array, doesn't matter the value + * - compare: compares the field against field specified in the CompareField option. Useful for stuff like password + * where you have to input it twice, check that the second password equals the first one + * - inarray: checks that the value specified in InArray option is in the field (which we assume is an array) + * - regex: performs a preg_match of the value of Regex option and the field + * + * TODO: date fields are not actually validated, need to figure out what the proper validation syntax should be. + * + * @param array $ValidateArray + * @return string|null + */ + function ValidateForm($ValidateArray) { + reset($this->Fields); + foreach ($this->Fields as $FieldKey => $Field) { + $ValidateVar = $ValidateArray[$FieldKey] ?? null; + + if ($ValidateVar != '' || !empty($Field['Required']) || $Field['Type'] == 'date') { + if ($Field['Type'] == 'string') { + if (isset($Field['MaxLength'])) { + $MaxLength = $Field['MaxLength']; + } else { + $MaxLength = 255; + } + if (isset($Field['MinLength'])) { + $MinLength = $Field['MinLength']; + } else { + $MinLength = 1; + } + + if ($MaxLength !== -1 && strlen($ValidateVar) > $MaxLength) { + return $Field['ErrorMessage']; + } elseif ($MinLength !== -1 && strlen($ValidateVar) < $MinLength) { + return $Field['ErrorMessage']; + } + + } elseif ($Field['Type'] == 'number') { + if (isset($Field['MaxLength'])) { + $MaxLength = $Field['MaxLength']; + } else { + $MaxLength = ''; + } + if (isset($Field['MinLength'])) { + $MinLength = $Field['MinLength']; + } else { + $MinLength = 0; + } + + $Match = '0-9'; + if (isset($Field['AllowPeriod'])) { + $Match .= '.'; + } + if (isset($Field['AllowComma'])) { + $Match .= ','; + } + + if (preg_match('/[^'.$Match.']/', $ValidateVar) || strlen($ValidateVar) < 1) { + return $Field['ErrorMessage']; + } elseif ($MaxLength != '' && $ValidateVar > $MaxLength) { + return $Field['ErrorMessage'].'!!'; + } elseif ($ValidateVar < $MinLength) { + return $Field['ErrorMessage']."$MinLength"; + } + + } elseif ($Field['Type'] == 'email') { + if (isset($Field['MaxLength'])) { + $MaxLength = $Field['MaxLength']; + } else { + $MaxLength = 255; + } + if (isset($Field['MinLength'])) { + $MinLength = $Field['MinLength']; + } else { + $MinLength = 6; + } + + if (!preg_match("/^".EMAIL_REGEX."$/i", $ValidateVar)) { + return $Field['ErrorMessage']; + } elseif (strlen($ValidateVar) > $MaxLength) { + return $Field['ErrorMessage']; + } elseif (strlen($ValidateVar) < $MinLength) { + return $Field['ErrorMessage']; + } + + } elseif ($Field['Type'] == 'link') { + if (isset($Field['MaxLength'])) { + $MaxLength = $Field['MaxLength']; + } else { + $MaxLength = 255; + } + if (isset($Field['MinLength'])) { + $MinLength = $Field['MinLength']; + } else { + $MinLength = 10; + } + + if (!preg_match('/^'.URL_REGEX.'$/i', $ValidateVar)) { + return $Field['ErrorMessage']; + } elseif (strlen($ValidateVar) > $MaxLength) { + return $Field['ErrorMessage']; + } elseif (strlen($ValidateVar) < $MinLength) { + return $Field['ErrorMessage']; + } + + } elseif ($Field['Type'] == 'username') { + if (isset($Field['MaxLength'])) { + $MaxLength = $Field['MaxLength']; + } else { + $MaxLength = 20; + } + if (isset($Field['MinLength'])) { + $MinLength = $Field['MinLength']; + } else { + $MinLength = 1; + } + + if (!preg_match(USERNAME_REGEX, $ValidateVar)) { + return $Field['ErrorMessage']; + } elseif (strlen($ValidateVar) > $MaxLength) { + return $Field['ErrorMessage']; + } elseif (strlen($ValidateVar) < $MinLength) { + return $Field['ErrorMessage']; + } + + } elseif ($Field['Type'] == 'checkbox') { + if (!isset($ValidateArray[$FieldKey])) { + return $Field['ErrorMessage']; + } + + } elseif ($Field['Type'] == 'compare') { + if ($ValidateArray[$Field['CompareField']] != $ValidateVar) { + return $Field['ErrorMessage']; + } + + } elseif ($Field['Type'] == 'inarray') { + if (array_search($ValidateVar, $Field['InArray']) === false) { + return $Field['ErrorMessage']; + } + + } elseif ($Field['Type'] == 'regex') { + if (!preg_match($Field['Regex'], $ValidateVar)) { + + return $Field['ErrorMessage']; + } + elseif (isset($Field['MaxLength']) && strlen($ValidateVar) > $Field['MaxLength']) { + return $Field['ErrorMessage']; + } + elseif (isset($Field['MinLength']) && strlen($ValidateVar) < $Field['MinLength']) { + return $Field['ErrorMessage']; + } + } + } + } // while + } // function + + function GenerateJS($FormID) { + $ReturnJS = "\r\n"; + return $ReturnJS; + } } ?> diff --git a/classes/view.class.php b/classes/view.class.php index c61ae2638..e9dbfec0d 100644 --- a/classes/view.class.php +++ b/classes/view.class.php @@ -1,130 +1,130 @@ - $Part) { - $ClassNameParts[$Index] = ucfirst($Part); - } - $ClassName = implode($ClassNameParts). 'Template'; - $LoadedTemplates[$TemplateName] = $ClassName; - } - $ClassName::render($Args); - } + // Turn template_name into TemplateName + $ClassNameParts = explode('_', $TemplateName); + foreach ($ClassNameParts as $Index => $Part) { + $ClassNameParts[$Index] = ucfirst($Part); + } + $ClassName = implode($ClassNameParts). 'Template'; + $LoadedTemplates[$TemplateName] = $ClassName; + } + $ClassName::render($Args); + } - /** - * This method is similar to render_template, but does not require a - * template class. - * - * Instead, this method simply renders a PHP file (PHTML) with the supplied - * variables. - * - * All files must be placed within {self::IncludePath}. Create and organize - * new paths and files. (e.g.: /design/views/artist/, design/view/forums/, etc.) - * - * @static - * @param string $TemplateFile A relative path to a PHTML file - * @param array $Variables Assoc. array of variables to extract for the template - * @param boolean $Buffer enables Output Buffer - * @return boolean|string - * - * @example
      ">Data

      - * - * // The variable $id within box.phtml will be filled by $some_id - * View::parse('section/box.phtml', array('id' => $some_id)); - * - * // Parse a template without outputing it - * $SavedTemplate = View::parse('sec/tion/eg.php', $DataArray, true); - * // later . . . - * echo $SavedTemplate; // Output the buffer - *
      - */ - public static function parse($TemplateFile, array $Variables = array(), $Buffer = false) { - $Template = self::IncludePath . $TemplateFile; - if (file_exists($Template)) { - extract($Variables); - if ($Buffer) { - ob_start(); - include $Template; - $Content = ob_get_contents(); - ob_end_clean(); - return $Content; - } - return include $Template; - } - } + /** + * This method is similar to render_template, but does not require a + * template class. + * + * Instead, this method simply renders a PHP file (PHTML) with the supplied + * variables. + * + * All files must be placed within {self::IncludePath}. Create and organize + * new paths and files. (e.g.: /design/views/artist/, design/view/forums/, etc.) + * + * @static + * @param string $TemplateFile A relative path to a PHTML file + * @param array $Variables Assoc. array of variables to extract for the template + * @param boolean $Buffer enables Output Buffer + * @return boolean|string + * + * @example
      ">Data

      + * + * // The variable $id within box.phtml will be filled by $some_id + * View::parse('section/box.phtml', array('id' => $some_id)); + * + * // Parse a template without outputing it + * $SavedTemplate = View::parse('sec/tion/eg.php', $DataArray, true); + * // later . . . + * echo $SavedTemplate; // Output the buffer + *
      + */ + public static function parse($TemplateFile, array $Variables = [], $Buffer = false) { + $Template = self::IncludePath . $TemplateFile; + if (file_exists($Template)) { + extract($Variables); + if ($Buffer) { + ob_start(); + include $Template; + $Content = ob_get_contents(); + ob_end_clean(); + return $Content; + } + return include $Template; + } + } } diff --git a/classes/votes.class.php b/classes/votes.class.php index 21f708889..20f74a31f 100644 --- a/classes/votes.class.php +++ b/classes/votes.class.php @@ -1,316 +1,316 @@ - - - Vote: - - - - - x - Score: - -(GroupID, 'Up'|'Down') - */ - public static function get_user_votes($UserID) { - if ((int)$UserID == 0) { - return array(); - } - - $UserVotes = G::$Cache->get_value("voted_albums_$UserID"); - if ($UserVotes === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT GroupID, Type - FROM users_votes - WHERE UserID = $UserID"); - $UserVotes = G::$DB->to_array('GroupID', MYSQLI_ASSOC, false); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value("voted_albums_$UserID", $UserVotes); - } - return $UserVotes; - } - - /** - * Returns an array with torrent group vote data - * @param string|int $GroupID - * @return array (Upvotes, Total Votes) - */ - public static function get_group_votes($GroupID) { - if (!is_number($GroupID)) { - return array('Ups' => 0, 'Total' => 0); - } - $GroupVotes = G::$Cache->get_value("votes_$GroupID"); - if ($GroupVotes === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT Ups AS Ups, Total AS Total - FROM torrents_votes - WHERE GroupID = $GroupID"); - if (!G::$DB->has_results()) { - $GroupVotes = array('Ups' => 0, 'Total' => 0); - } else { - $GroupVotes = G::$DB->next_record(MYSQLI_ASSOC, false); - } - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value("votes_$GroupID", $GroupVotes, 259200); // 3 days - } - return $GroupVotes; - } - - /** - * Computes the inverse normal CDF of a p-value - * @param float $GroupID - * @return float Inverse Normal CDF - */ - private function inverse_ncdf($p) { - /*************************************************************************** - * inverse_ncdf.php - * ------------------- - * begin : Friday, January 16, 2004 - * copyright : (C) 2004 Michael Nickerson - * email : nickersonm@yahoo.com - * - ***************************************************************************/ - - //Inverse ncdf approximation by Peter John Acklam, implementation adapted to - //PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as - //a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html - //I have not checked the accuracy of this implementation. Be aware that PHP - //will truncate the coeficcients to 14 digits. - - //You have permission to use and distribute this function freely for - //whatever purpose you want, but please show common courtesy and give credit - //where credit is due. - - //Input paramater is $p - probability - where 0 < p < 1. - - //Coefficients in rational approximations - $a = array(1 => -3.969683028665376e+01, 2 => 2.209460984245205e+02, - 3 => -2.759285104469687e+02, 4 => 1.383577518672690e+02, - 5 => -3.066479806614716e+01, 6 => 2.506628277459239e+00); - - $b = array(1 => -5.447609879822406e+01, 2 => 1.615858368580409e+02, - 3 => -1.556989798598866e+02, 4 => 6.680131188771972e+01, - 5 => -1.328068155288572e+01); - - $c = array(1 => -7.784894002430293e-03, 2 => -3.223964580411365e-01, - 3 => -2.400758277161838e+00, 4 => -2.549732539343734e+00, - 5 => 4.374664141464968e+00, 6 => 2.938163982698783e+00); - - $d = array(1 => 7.784695709041462e-03, 2 => 3.224671290700398e-01, - 3 => 2.445134137142996e+00, 4 => 3.754408661907416e+00); - - //Define break-points. - $p_low = 0.02425; //Use lower region approx. below this - $p_high = 1 - $p_low; //Use upper region approx. above this - - //Define/list variables (doesn't really need a definition) - //$p (probability), $sigma (std. deviation), and $mu (mean) are user inputs - $q = null; $x = null; $y = null; $r = null; - - //Rational approximation for lower region. - if (0 < $p && $p < $p_low) { - $q = sqrt(-2 * log($p)); - $x = ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * - $q + $c[6]) / (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * - $q + 1); - } - - //Rational approximation for central region. - elseif ($p_low <= $p && $p <= $p_high) { - $q = $p - 0.5; - $r = $q * $q; - $x = ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * - $r + $a[6]) * $q / ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + - $b[4]) * $r + $b[5]) * $r + 1); - } - - //Rational approximation for upper region. - elseif ($p_high < $p && $p < 1) { - $q = sqrt(-2 * log(1 - $p)); - $x = -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + - $c[5]) * $q + $c[6]) / (((($d[1] * $q + $d[2]) * $q + $d[3]) * - $q + $d[4]) * $q + 1); - } - - //If 0 < p < 1, return a null value - else { - $x = null; - } - - return $x; - //END inverse ncdf implementation. - } - - /** - * Implementation of the algorithm described at http://www.evanmiller.org/how-not-to-sort-by-average-rating.html - * @param int $Ups Number of upvotes - * @param int $Total Number of total votes - * @return float Ranking score - */ - public static function binomial_score($Ups, $Total) { - if (($Total <= 0) || ($Ups < 0)) { - return 0; - } - $phat = $Ups / $Total; - $Numerator = ($phat + self::Z_VAL * self::Z_VAL / (2 * $Total) - self::Z_VAL * sqrt(($phat * (1 - $phat) + self::Z_VAL * self::Z_VAL / (4 * $Total)) / $Total)); - $Denominator = (1 + self::Z_VAL * self::Z_VAL / $Total); - return ($Numerator / $Denominator); - } - - /** - * Gets where this album ranks overall, for its year, and for its decade. This is really just a wrapper. - * @param int $GroupID GroupID of the album - * @param int $Year Year it was released - * @return array ('overall'=>, 'year'=>, 'decade'=>) - */ - public static function get_ranking($GroupID, $Year) { - $GroupID = (int)$GroupID; - $Year = (int)$Year; - if ($GroupID <= 0 || $Year <= 0) { - return false; - } - - return array( - 'overall' => Votes::get_rank_all($GroupID), - 'year' => Votes::get_rank_year($GroupID, $Year), - 'decade' => Votes::get_rank_decade($GroupID, $Year)); - } - - /** - * Gets where this album ranks overall. - * @param int $GroupID GroupID of the album - * @return int Rank - */ - public static function get_rank_all($GroupID) { - $GroupID = (int)$GroupID; - if ($GroupID <= 0) { - return false; - } - - $Rankings = G::$Cache->get_value('voting_ranks_overall'); - if ($Rankings === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(' - SELECT GroupID, Score - FROM torrents_votes - ORDER BY Score DESC - LIMIT 100'); - $Rankings = self::calc_ranks(G::$DB->to_pair(0, 1, false)); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value('voting_ranks_overall', $Rankings, 259200); // 3 days - } - - return (isset($Rankings[$GroupID]) ? $Rankings[$GroupID] : false); - } - - /** - * Gets where this album ranks in its year. - * @param int $GroupID GroupID of the album - * @param int $Year Year it was released - * @return int Rank for its year - */ - public static function get_rank_year($GroupID, $Year) { - $GroupID = (int)$GroupID; - $Year = (int)$Year; - if ($GroupID <= 0 || $Year <= 0) { - return false; - } - - $Rankings = G::$Cache->get_value("voting_ranks_year_$Year"); - if ($Rankings === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT GroupID, Score - FROM torrents_votes AS v - JOIN torrents_group AS g ON g.ID = v.GroupID - WHERE g.Year = $Year - ORDER BY Score DESC - LIMIT 100"); - $Rankings = self::calc_ranks(G::$DB->to_pair(0, 1, false)); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value("voting_ranks_year_$Year", $Rankings, 259200); // 3 days - } - - return (isset($Rankings[$GroupID]) ? $Rankings[$GroupID] : false); - } - - /** - * Gets where this album ranks in its decade. - * @param int $GroupID GroupID of the album - * @param int $Year Year it was released - * @return int Rank for its year - */ - public static function get_rank_decade($GroupID, $Year) { - $GroupID = (int)$GroupID; - $Year = (int)$Year; - if ($GroupID <= 0 || $Year <= 0) { - return false; - } - - // First year of the decade - $Year = $Year - ($Year % 10); - - $Rankings = G::$Cache->get_value("voting_ranks_decade_$Year"); - if ($Rankings === false) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT GroupID, Score - FROM torrents_votes AS v - JOIN torrents_group AS g ON g.ID = v.GroupID - WHERE g.Year BETWEEN $Year AND " . ($Year + 9) . " - AND g.CategoryID = 1 - ORDER BY Score DESC - LIMIT 100"); - $Rankings = self::calc_ranks(G::$DB->to_pair(0, 1, false)); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value("voting_ranks_decade_$Year", $Rankings, 259200); // 3 days - } - - return (isset($Rankings[$GroupID]) ? $Rankings[$GroupID] : false); - } - - /** - * Turn vote scores into vote ranks. This basically only sorts out tied ranks - * - * @param array $GroupScores array ( => ) ordered by Score - * @return array ( => ) - */ - public static function calc_ranks($GroupScores) { - $Rankings = array(); - $PrevScore = $PrevRank = false; - $Rank = 1; - foreach ($GroupScores as $GroupID => $Score) { - if ($Score === $PrevScore) { - $Rankings[$GroupID] = $PrevRank; - } else { - $Rankings[$GroupID] = $Rank; - $PrevRank = $Rank; - $PrevScore = $Score; - } - $Rank++; - } - return $Rankings; - } + /** + * Confidence level for binomial scoring + */ + //const Z_VAL = 1.645211440143815; // p-value .95 + const Z_VAL = 1.281728756502709; // p-value .90 + + /** + * Generate voting links for torrent pages, etc. + * @param $GroupID + * @param $Vote The pre-existing vote, if it exists 'Up'|'Down' + */ + public static function vote_link($GroupID, $Vote = '') { + if ((!isset(G::$LoggedUser['NoVoteLinks']) || !G::$LoggedUser['NoVoteLinks']) && check_perms('site_album_votes')) { + $GroupVotes = self::get_group_votes($GroupID); ?> + + Vote: + + + + + x + Score: + +(GroupID, 'Up'|'Down') + */ + public static function get_user_votes($UserID) { + if ((int)$UserID == 0) { + return []; + } + + $UserVotes = G::$Cache->get_value("voted_albums_$UserID"); + if ($UserVotes === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT GroupID, Type + FROM users_votes + WHERE UserID = $UserID"); + $UserVotes = G::$DB->to_array('GroupID', MYSQLI_ASSOC, false); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value("voted_albums_$UserID", $UserVotes); + } + return $UserVotes; + } + + /** + * Returns an array with torrent group vote data + * @param string|int $GroupID + * @return array (Upvotes, Total Votes) + */ + public static function get_group_votes($GroupID) { + if (!is_number($GroupID)) { + return array('Ups' => 0, 'Total' => 0); + } + $GroupVotes = G::$Cache->get_value("votes_$GroupID"); + if ($GroupVotes === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT Ups AS Ups, Total AS Total + FROM torrents_votes + WHERE GroupID = $GroupID"); + if (!G::$DB->has_results()) { + $GroupVotes = array('Ups' => 0, 'Total' => 0); + } else { + $GroupVotes = G::$DB->next_record(MYSQLI_ASSOC, false); + } + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value("votes_$GroupID", $GroupVotes, 259200); // 3 days + } + return $GroupVotes; + } + + /** + * Computes the inverse normal CDF of a p-value + * @param float $GroupID + * @return float Inverse Normal CDF + */ + private function inverse_ncdf($p) { + /*************************************************************************** + * inverse_ncdf.php + * ------------------- + * begin : Friday, January 16, 2004 + * copyright : (C) 2004 Michael Nickerson + * email : nickersonm@yahoo.com + * + ***************************************************************************/ + + //Inverse ncdf approximation by Peter John Acklam, implementation adapted to + //PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as + //a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html + //I have not checked the accuracy of this implementation. Be aware that PHP + //will truncate the coeficcients to 14 digits. + + //You have permission to use and distribute this function freely for + //whatever purpose you want, but please show common courtesy and give credit + //where credit is due. + + //Input paramater is $p - probability - where 0 < p < 1. + + //Coefficients in rational approximations + $a = array(1 => -3.969683028665376e+01, 2 => 2.209460984245205e+02, + 3 => -2.759285104469687e+02, 4 => 1.383577518672690e+02, + 5 => -3.066479806614716e+01, 6 => 2.506628277459239e+00); + + $b = array(1 => -5.447609879822406e+01, 2 => 1.615858368580409e+02, + 3 => -1.556989798598866e+02, 4 => 6.680131188771972e+01, + 5 => -1.328068155288572e+01); + + $c = array(1 => -7.784894002430293e-03, 2 => -3.223964580411365e-01, + 3 => -2.400758277161838e+00, 4 => -2.549732539343734e+00, + 5 => 4.374664141464968e+00, 6 => 2.938163982698783e+00); + + $d = array(1 => 7.784695709041462e-03, 2 => 3.224671290700398e-01, + 3 => 2.445134137142996e+00, 4 => 3.754408661907416e+00); + + //Define break-points. + $p_low = 0.02425; //Use lower region approx. below this + $p_high = 1 - $p_low; //Use upper region approx. above this + + //Define/list variables (doesn't really need a definition) + //$p (probability), $sigma (std. deviation), and $mu (mean) are user inputs + $q = null; $x = null; $y = null; $r = null; + + //Rational approximation for lower region. + if (0 < $p && $p < $p_low) { + $q = sqrt(-2 * log($p)); + $x = ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * + $q + $c[6]) / (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * + $q + 1); + } + + //Rational approximation for central region. + elseif ($p_low <= $p && $p <= $p_high) { + $q = $p - 0.5; + $r = $q * $q; + $x = ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * + $r + $a[6]) * $q / ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + + $b[4]) * $r + $b[5]) * $r + 1); + } + + //Rational approximation for upper region. + elseif ($p_high < $p && $p < 1) { + $q = sqrt(-2 * log(1 - $p)); + $x = -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + + $c[5]) * $q + $c[6]) / (((($d[1] * $q + $d[2]) * $q + $d[3]) * + $q + $d[4]) * $q + 1); + } + + //If 0 < p < 1, return a null value + else { + $x = null; + } + + return $x; + //END inverse ncdf implementation. + } + + /** + * Implementation of the algorithm described at http://www.evanmiller.org/how-not-to-sort-by-average-rating.html + * @param int $Ups Number of upvotes + * @param int $Total Number of total votes + * @return float Ranking score + */ + public static function binomial_score($Ups, $Total) { + if (($Total <= 0) || ($Ups < 0)) { + return 0; + } + $phat = $Ups / $Total; + $Numerator = ($phat + self::Z_VAL * self::Z_VAL / (2 * $Total) - self::Z_VAL * sqrt(($phat * (1 - $phat) + self::Z_VAL * self::Z_VAL / (4 * $Total)) / $Total)); + $Denominator = (1 + self::Z_VAL * self::Z_VAL / $Total); + return ($Numerator / $Denominator); + } + + /** + * Gets where this album ranks overall, for its year, and for its decade. This is really just a wrapper. + * @param int $GroupID GroupID of the album + * @param int $Year Year it was released + * @return array ('overall'=>, 'year'=>, 'decade'=>) + */ + public static function get_ranking($GroupID, $Year) { + $GroupID = (int)$GroupID; + $Year = (int)$Year; + if ($GroupID <= 0 || $Year <= 0) { + return false; + } + + return array( + 'overall' => Votes::get_rank_all($GroupID), + 'year' => Votes::get_rank_year($GroupID, $Year), + 'decade' => Votes::get_rank_decade($GroupID, $Year)); + } + + /** + * Gets where this album ranks overall. + * @param int $GroupID GroupID of the album + * @return int Rank + */ + public static function get_rank_all($GroupID) { + $GroupID = (int)$GroupID; + if ($GroupID <= 0) { + return false; + } + + $Rankings = G::$Cache->get_value('voting_ranks_overall'); + if ($Rankings === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(' + SELECT GroupID, Score + FROM torrents_votes + ORDER BY Score DESC + LIMIT 100'); + $Rankings = self::calc_ranks(G::$DB->to_pair(0, 1, false)); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value('voting_ranks_overall', $Rankings, 259200); // 3 days + } + + return (isset($Rankings[$GroupID]) ? $Rankings[$GroupID] : false); + } + + /** + * Gets where this album ranks in its year. + * @param int $GroupID GroupID of the album + * @param int $Year Year it was released + * @return int Rank for its year + */ + public static function get_rank_year($GroupID, $Year) { + $GroupID = (int)$GroupID; + $Year = (int)$Year; + if ($GroupID <= 0 || $Year <= 0) { + return false; + } + + $Rankings = G::$Cache->get_value("voting_ranks_year_$Year"); + if ($Rankings === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT GroupID, Score + FROM torrents_votes AS v + JOIN torrents_group AS g ON g.ID = v.GroupID + WHERE g.Year = $Year + ORDER BY Score DESC + LIMIT 100"); + $Rankings = self::calc_ranks(G::$DB->to_pair(0, 1, false)); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value("voting_ranks_year_$Year", $Rankings, 259200); // 3 days + } + + return (isset($Rankings[$GroupID]) ? $Rankings[$GroupID] : false); + } + + /** + * Gets where this album ranks in its decade. + * @param int $GroupID GroupID of the album + * @param int $Year Year it was released + * @return int Rank for its year + */ + public static function get_rank_decade($GroupID, $Year) { + $GroupID = (int)$GroupID; + $Year = (int)$Year; + if ($GroupID <= 0 || $Year <= 0) { + return false; + } + + // First year of the decade + $Year = $Year - ($Year % 10); + + $Rankings = G::$Cache->get_value("voting_ranks_decade_$Year"); + if ($Rankings === false) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT GroupID, Score + FROM torrents_votes AS v + JOIN torrents_group AS g ON g.ID = v.GroupID + WHERE g.Year BETWEEN $Year AND " . ($Year + 9) . " + AND g.CategoryID = 1 + ORDER BY Score DESC + LIMIT 100"); + $Rankings = self::calc_ranks(G::$DB->to_pair(0, 1, false)); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value("voting_ranks_decade_$Year", $Rankings, 259200); // 3 days + } + + return (isset($Rankings[$GroupID]) ? $Rankings[$GroupID] : false); + } + + /** + * Turn vote scores into vote ranks. This basically only sorts out tied ranks + * + * @param array $GroupScores array ( => ) ordered by Score + * @return array ( => ) + */ + public static function calc_ranks($GroupScores) { + $Rankings = []; + $PrevScore = $PrevRank = false; + $Rank = 1; + foreach ($GroupScores as $GroupID => $Score) { + if ($Score === $PrevScore) { + $Rankings[$GroupID] = $PrevRank; + } else { + $Rankings[$GroupID] = $Rank; + $PrevRank = $Rank; + $PrevScore = $Score; + } + $Rank++; + } + return $Rankings; + } } ?> diff --git a/classes/wiki.class.php b/classes/wiki.class.php index a42316640..99f7b7c22 100644 --- a/classes/wiki.class.php +++ b/classes/wiki.class.php @@ -1,100 +1,100 @@ - ArticleID - * @return array - */ - public static function get_aliases() { - $Aliases = G::$Cache->get_value('wiki_aliases'); - if (!$Aliases) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT Alias, ArticleID - FROM wiki_aliases"); - $Aliases = G::$DB->to_pair('Alias', 'ArticleID'); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value('wiki_aliases', $Aliases, 3600 * 24 * 14); // 2 weeks - } - return $Aliases; - } + /** + * Get all aliases in an associative array of Alias => ArticleID + * @return array + */ + public static function get_aliases() { + $Aliases = G::$Cache->get_value('wiki_aliases'); + if (!$Aliases) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT Alias, ArticleID + FROM wiki_aliases"); + $Aliases = G::$DB->to_pair('Alias', 'ArticleID'); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value('wiki_aliases', $Aliases, 3600 * 24 * 14); // 2 weeks + } + return $Aliases; + } - /** - * Flush the alias cache. Call this whenever you touch the wiki_aliases table. - */ - public static function flush_aliases() { - G::$Cache->delete_value('wiki_aliases'); - } + /** + * Flush the alias cache. Call this whenever you touch the wiki_aliases table. + */ + public static function flush_aliases() { + G::$Cache->delete_value('wiki_aliases'); + } - /** - * Get the ArticleID corresponding to an alias - * @param string $Alias - * @return int - */ - public static function alias_to_id($Alias) { - $Aliases = self::get_aliases(); - $Alias = self::normalize_alias($Alias); - if (!isset($Aliases[$Alias])) { - return false; - } else { - return (int)$Aliases[$Alias]; - } - } + /** + * Get the ArticleID corresponding to an alias + * @param string $Alias + * @return int + */ + public static function alias_to_id($Alias) { + $Aliases = self::get_aliases(); + $Alias = self::normalize_alias($Alias); + if (!isset($Aliases[$Alias])) { + return false; + } else { + return (int)$Aliases[$Alias]; + } + } - /** - * Get an article; returns false on error if $Error = false - * @param int $ArticleID - * @param bool $Error - * @return array|bool - */ - public static function get_article($ArticleID, $Error = true) { - $Contents = G::$Cache->get_value('wiki_article_'.$ArticleID); - if (!$Contents) { - $QueryID = G::$DB->get_query_id(); - G::$DB->query(" - SELECT - w.Revision, - w.Title, - w.Body, - w.MinClassRead, - w.MinClassEdit, - w.Date, - w.Author, - u.Username, - GROUP_CONCAT(a.Alias), - GROUP_CONCAT(a.UserID) - FROM wiki_articles AS w - LEFT JOIN wiki_aliases AS a ON w.ID=a.ArticleID - LEFT JOIN users_main AS u ON u.ID=w.Author - WHERE w.ID='$ArticleID' - GROUP BY w.ID"); - if (!G::$DB->has_results()) { - if ($Error) { - error(404); - } else { - return false; - } - } - $Contents = G::$DB->to_array(); - G::$DB->set_query_id($QueryID); - G::$Cache->cache_value('wiki_article_'.$ArticleID, $Contents, 3600 * 24 * 14); // 2 weeks - } - return $Contents; - } + /** + * Get an article; returns false on error if $Error = false + * @param int $ArticleID + * @param bool $Error + * @return array|bool + */ + public static function get_article($ArticleID, $Error = true) { + $Contents = G::$Cache->get_value('wiki_article_'.$ArticleID); + if (!$Contents) { + $QueryID = G::$DB->get_query_id(); + G::$DB->query(" + SELECT + w.Revision, + w.Title, + w.Body, + w.MinClassRead, + w.MinClassEdit, + w.Date, + w.Author, + u.Username, + GROUP_CONCAT(a.Alias), + GROUP_CONCAT(a.UserID) + FROM wiki_articles AS w + LEFT JOIN wiki_aliases AS a ON w.ID=a.ArticleID + LEFT JOIN users_main AS u ON u.ID=w.Author + WHERE w.ID='$ArticleID' + GROUP BY w.ID"); + if (!G::$DB->has_results()) { + if ($Error) { + error(404); + } else { + return false; + } + } + $Contents = G::$DB->to_array(); + G::$DB->set_query_id($QueryID); + G::$Cache->cache_value('wiki_article_'.$ArticleID, $Contents, 3600 * 24 * 14); // 2 weeks + } + return $Contents; + } - /** - * Flush an article's cache. Call this whenever you edited a wiki article or its aliases. - * @param int $ArticleID - */ - public static function flush_article($ArticleID) { - G::$Cache->delete_value('wiki_article_'.$ArticleID); - } + /** + * Flush an article's cache. Call this whenever you edited a wiki article or its aliases. + * @param int $ArticleID + */ + public static function flush_article($ArticleID) { + G::$Cache->delete_value('wiki_article_'.$ArticleID); + } } diff --git a/classes/zip.class.php b/classes/zip.class.php index 678de8932..e4bf84a52 100644 --- a/classes/zip.class.php +++ b/classes/zip.class.php @@ -1,4 +1,4 @@ -unlimit(); - A simple shortcut function for raising the basic PHP limits, time and memory for larger archives. + A simple shortcut function for raising the basic PHP limits, time and memory for larger archives. ----- @@ -34,11 +34,11 @@ $Zip->add_file(file_get_contents("data/file.txt"), "File.txt"); - Adds the contents of data/file.txt into File.txt in the archive root. + Adds the contents of data/file.txt into File.txt in the archive root. $Zip->add_file($TorrentData, "Bookmarks/Artist - Album [2008].torrent"); - Adds the parsed torrent to the archive in the Bookmarks folder (created simply by placing it in the path). + Adds the parsed torrent to the archive in the Bookmarks folder (created simply by placing it in the path). ----- @@ -46,15 +46,15 @@ $Zip->close_stream(); - This collects everything put together thus far in the archive, and streams it to the user in the form of Test7.zip + This collects everything put together thus far in the archive, and streams it to the user in the form of Test7.zip //------ Explanation of basic functions ------// add_file(Contents, Internal Path) - Adds the contents to the archive, where it will be extracted to Internal Path. + Adds the contents to the archive, where it will be extracted to Internal Path. close_stream(); - Collect and stream to the user. + Collect and stream to the user. //------------- Detailed example -------------// @@ -74,113 +74,113 @@ |*************************************************************************/ if (!extension_loaded('zlib')) { - error('Zlib Extension not loaded.'); + error('Zlib Extension not loaded.'); } /* //Handles timestamps function dostime($TimeStamp = 0) { - if (!is_number($TimeStamp)) { // Assume that $TimeStamp is SQL timestamp - if ($TimeStamp == '0000-00-00 00:00:00') { - return 'Never'; - } - $TimeStamp = strtotime($TimeStamp); - } - $Date = (($TimeStamp == 0) ? getdate() : getdate($TimeStamp)); - $Hex = dechex((($Date['year'] - 1980) << 25) | ($Date['mon'] << 21) | ($Date['mday'] << 16) | ($Date['hours'] << 11) | ($Date['minutes'] << 5) | ($Date['seconds'] >> 1)); - eval("\$Return = \"\x$Hex[6]$Hex[7]\x$Hex[4]$Hex[5]\x$Hex[2]$Hex[3]\x$Hex[0]$Hex[1]\";"); - return $Return; + if (!is_number($TimeStamp)) { // Assume that $TimeStamp is SQL timestamp + if ($TimeStamp == '0000-00-00 00:00:00') { + return 'Never'; + } + $TimeStamp = strtotime($TimeStamp); + } + $Date = (($TimeStamp == 0) ? getdate() : getdate($TimeStamp)); + $Hex = dechex((($Date['year'] - 1980) << 25) | ($Date['mon'] << 21) | ($Date['mday'] << 16) | ($Date['hours'] << 11) | ($Date['minutes'] << 5) | ($Date['seconds'] >> 1)); + eval("\$Return = \"\x$Hex[6]$Hex[7]\x$Hex[4]$Hex[5]\x$Hex[2]$Hex[3]\x$Hex[0]$Hex[1]\";"); + return $Return; } */ class Zip { - public $ArchiveSize = 0; // Total size - public $ArchiveFiles = 0; // Total files - private $Structure = ''; // Structure saved to memory - private $FileOffset = 0; // Offset to write data - private $Data = ''; //An idea - - public function __construct ($ArchiveName = 'Archive') { - header("Content-type: application/octet-stream"); // Stream download - header("Content-disposition: attachment; filename=\"$ArchiveName.zip\""); // Name the archive - Should not be urlencoded - } - - public static function unlimit () { - ob_end_clean(); - set_time_limit(3600); // Limit 1 hour - ini_set('memory_limit', '1024M'); // Because the buffers can get extremely large - } - - public function add_file ($FileData, $ArchivePath, $TimeStamp = 0) { - /* File header */ - $this->Data = "\x50\x4b\x03\x04"; // PK signature - $this->Data .= "\x14\x00"; // Version requirements - $this->Data .= "\x00\x08"; // Bit flag - 0x8 = UTF-8 file names - $this->Data .= "\x08\x00"; // Compression - //$this->Data .= dostime($TimeStamp); // Last modified - $this->Data .= "\x00\x00\x00\x00"; - $DataLength = strlen($FileData); // Saved as variable to avoid wasting CPU calculating it multiple times. - $CRC32 = crc32($FileData); // Ditto. - $ZipData = gzcompress($FileData); // Ditto. - $ZipData = substr ($ZipData, 2, (strlen($ZipData) - 6)); // Checksum resolution - $ZipLength = strlen($ZipData); // Ditto. - $this->Data .= pack('V', $CRC32); // CRC-32 - $this->Data .= pack('V', $ZipLength); // Compressed file size - $this->Data .= pack('V', $DataLength); // Uncompressed file size - $this->Data .= pack('v', strlen($ArchivePath)); // Path name length - $this->Data .="\x00\x00"; // Extra field length (0'd so we can ignore this) - $this->Data .= $ArchivePath; // File name & Extra Field (length set to 0 so ignored) - /* END file header */ - - /* File data */ - $this->Data .= $ZipData; // File data - /* END file data */ - - /* Data descriptor - Not needed (only needed when 3rd bitflag is set), causes problems with OS X archive utility - $this->Data .= pack('V', $CRC32); // CRC-32 - $this->Data .= pack('V', $ZipLength); // Compressed file size - $this->Data .= pack('V', $DataLength); // Uncompressed file size - END data descriptor */ - - $FileDataLength = strlen($this->Data); - $this->ArchiveSize = $this->ArchiveSize + $FileDataLength; // All we really need is the size - $CurrentOffset = $this->ArchiveSize; // Update offsets - echo $this->Data; // Get this out to reduce our memory consumption - - /* Central Directory Structure */ - $CDS = "\x50\x4b\x01\x02"; // CDS signature - $CDS .="\x14\x00"; // Constructor version - $CDS .="\x14\x00"; // Version requirements - $CDS .="\x00\x08"; // Bit flag - 0x8 = UTF-8 file names - $CDS .="\x08\x00"; // Compression - $CDS .="\x00\x00\x00\x00"; // Last modified - $CDS .= pack('V', $CRC32); // CRC-32 - $CDS .= pack('V', $ZipLength); // Compressed file size - $CDS .= pack('V', $DataLength); // Uncompressed file size - $CDS .= pack('v', strlen($ArchivePath)); // Path name length - $CDS .="\x00\x00"; // Extra field length (0'd so we can ignore this) - $CDS .="\x00\x00"; // File comment length (no comment, 0'd) - $CDS .="\x00\x00"; // Disk number start (0 seems valid) - $CDS .="\x00\x00"; // Internal file attributes (again with the 0's) - $CDS .="\x20\x00\x00\x00"; // External file attributes - $CDS .= pack('V', $this->FileOffset); // Offsets - $CDS .= $ArchivePath; // File name & Extra Field (length set to 0 so ignored) - /* END central Directory Structure */ - - $this->FileOffset = $CurrentOffset; // Update offsets - $this->Structure .= $CDS; // Append to structure - $this->ArchiveFiles++; // Increment file count - } - - public function close_stream() { - echo $this->Structure; // Structure Root - echo "\x50\x4b\x05\x06"; // End of central directory signature - echo "\x00\x00"; // This disk - echo "\x00\x00"; // CDS start - echo pack('v', $this->ArchiveFiles); // Handle the number of entries - echo pack('v', $this->ArchiveFiles); // Ditto - echo pack('V', strlen($this->Structure)); //Size - echo pack('V', $this->ArchiveSize); // Offset - echo "\x00\x00"; // No comment, close it off - } + public $ArchiveSize = 0; // Total size + public $ArchiveFiles = 0; // Total files + private $Structure = ''; // Structure saved to memory + private $FileOffset = 0; // Offset to write data + private $Data = ''; //An idea + + public function __construct ($ArchiveName = 'Archive') { + header("Content-type: application/octet-stream"); // Stream download + header("Content-disposition: attachment; filename=\"$ArchiveName.zip\""); // Name the archive - Should not be urlencoded + } + + public static function unlimit () { + ob_end_clean(); + set_time_limit(3600); // Limit 1 hour + ini_set('memory_limit', '1024M'); // Because the buffers can get extremely large + } + + public function add_file ($FileData, $ArchivePath, $TimeStamp = 0) { + /* File header */ + $this->Data = "\x50\x4b\x03\x04"; // PK signature + $this->Data .= "\x14\x00"; // Version requirements + $this->Data .= "\x00\x08"; // Bit flag - 0x8 = UTF-8 file names + $this->Data .= "\x08\x00"; // Compression + //$this->Data .= dostime($TimeStamp); // Last modified + $this->Data .= "\x00\x00\x00\x00"; + $DataLength = strlen($FileData); // Saved as variable to avoid wasting CPU calculating it multiple times. + $CRC32 = crc32($FileData); // Ditto. + $ZipData = gzcompress($FileData); // Ditto. + $ZipData = substr ($ZipData, 2, (strlen($ZipData) - 6)); // Checksum resolution + $ZipLength = strlen($ZipData); // Ditto. + $this->Data .= pack('V', $CRC32); // CRC-32 + $this->Data .= pack('V', $ZipLength); // Compressed file size + $this->Data .= pack('V', $DataLength); // Uncompressed file size + $this->Data .= pack('v', strlen($ArchivePath)); // Path name length + $this->Data .="\x00\x00"; // Extra field length (0'd so we can ignore this) + $this->Data .= $ArchivePath; // File name & Extra Field (length set to 0 so ignored) + /* END file header */ + + /* File data */ + $this->Data .= $ZipData; // File data + /* END file data */ + + /* Data descriptor + Not needed (only needed when 3rd bitflag is set), causes problems with OS X archive utility + $this->Data .= pack('V', $CRC32); // CRC-32 + $this->Data .= pack('V', $ZipLength); // Compressed file size + $this->Data .= pack('V', $DataLength); // Uncompressed file size + END data descriptor */ + + $FileDataLength = strlen($this->Data); + $this->ArchiveSize = $this->ArchiveSize + $FileDataLength; // All we really need is the size + $CurrentOffset = $this->ArchiveSize; // Update offsets + echo $this->Data; // Get this out to reduce our memory consumption + + /* Central Directory Structure */ + $CDS = "\x50\x4b\x01\x02"; // CDS signature + $CDS .="\x14\x00"; // Constructor version + $CDS .="\x14\x00"; // Version requirements + $CDS .="\x00\x08"; // Bit flag - 0x8 = UTF-8 file names + $CDS .="\x08\x00"; // Compression + $CDS .="\x00\x00\x00\x00"; // Last modified + $CDS .= pack('V', $CRC32); // CRC-32 + $CDS .= pack('V', $ZipLength); // Compressed file size + $CDS .= pack('V', $DataLength); // Uncompressed file size + $CDS .= pack('v', strlen($ArchivePath)); // Path name length + $CDS .="\x00\x00"; // Extra field length (0'd so we can ignore this) + $CDS .="\x00\x00"; // File comment length (no comment, 0'd) + $CDS .="\x00\x00"; // Disk number start (0 seems valid) + $CDS .="\x00\x00"; // Internal file attributes (again with the 0's) + $CDS .="\x20\x00\x00\x00"; // External file attributes + $CDS .= pack('V', $this->FileOffset); // Offsets + $CDS .= $ArchivePath; // File name & Extra Field (length set to 0 so ignored) + /* END central Directory Structure */ + + $this->FileOffset = $CurrentOffset; // Update offsets + $this->Structure .= $CDS; // Append to structure + $this->ArchiveFiles++; // Increment file count + } + + public function close_stream() { + echo $this->Structure; // Structure Root + echo "\x50\x4b\x05\x06"; // End of central directory signature + echo "\x00\x00"; // This disk + echo "\x00\x00"; // CDS start + echo pack('v', $this->ArchiveFiles); // Handle the number of entries + echo pack('v', $this->ArchiveFiles); // Ditto + echo pack('V', strlen($this->Structure)); //Size + echo pack('V', $this->ArchiveSize); // Offset + echo "\x00\x00"; // No comment, close it off + } } diff --git a/composer.json b/composer.json index e85234bb4..a3751e2ce 100644 --- a/composer.json +++ b/composer.json @@ -18,13 +18,15 @@ }, "require": { "php": ">=7.1", - "robmorgan/phinx": "^0.10.0", + "robmorgan/phinx": "^0.11.0", "whichbrowser/parser": "^2.0", "d11wtq/boris": "^1.0", "orpheusnet/bencode-torrent": "^0.11.0", - "orpheusnet/logchecker": "^0.8.3" + "orpheusnet/logchecker": "^0.8.6", + "twig/twig": "^2.12" }, "require-dev": { - "phpunit/phpunit": "^7.5.12" + "phpunit/phpunit": "^7.5.12", + "squizlabs/php_codesniffer": "^3.5" } } diff --git a/composer.lock b/composer.lock index 717600cd8..037215675 100644 --- a/composer.lock +++ b/composer.lock @@ -4,11 +4,11 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d7dc14e0aaa9b2f19c1dd14ac9e25421", + "content-hash": "85c9db2416d01abe38cd7c1d95378e16", "packages": [ { "name": "cakephp/cache", - "version": "3.8.4", + "version": "3.8.5", "source": { "type": "git", "url": "https://github.com/cakephp/cache.git", @@ -52,7 +52,7 @@ }, { "name": "cakephp/collection", - "version": "3.8.4", + "version": "3.8.5", "source": { "type": "git", "url": "https://github.com/cakephp/collection.git", @@ -98,16 +98,16 @@ }, { "name": "cakephp/core", - "version": "3.8.4", + "version": "3.8.5", "source": { "type": "git", "url": "https://github.com/cakephp/core.git", - "reference": "47c3045326c92adbecd14cc92a2b7515dd9a30b9" + "reference": "bd893c7103f1b87d3fa41d80fc60024e1a18cf5f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/core/zipball/47c3045326c92adbecd14cc92a2b7515dd9a30b9", - "reference": "47c3045326c92adbecd14cc92a2b7515dd9a30b9", + "url": "https://api.github.com/repos/cakephp/core/zipball/bd893c7103f1b87d3fa41d80fc60024e1a18cf5f", + "reference": "bd893c7103f1b87d3fa41d80fc60024e1a18cf5f", "shasum": "" }, "require": { @@ -144,20 +144,20 @@ "core", "framework" ], - "time": "2019-09-09T19:30:50+00:00" + "time": "2019-10-05T19:33:15+00:00" }, { "name": "cakephp/database", - "version": "3.8.4", + "version": "3.8.5", "source": { "type": "git", "url": "https://github.com/cakephp/database.git", - "reference": "f3f7f8c8da67064667a88681cbd856da4e96cade" + "reference": "2fe4d3a5c300794211744193e4c1667c1aa760ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/database/zipball/f3f7f8c8da67064667a88681cbd856da4e96cade", - "reference": "f3f7f8c8da67064667a88681cbd856da4e96cade", + "url": "https://api.github.com/repos/cakephp/database/zipball/2fe4d3a5c300794211744193e4c1667c1aa760ec", + "reference": "2fe4d3a5c300794211744193e4c1667c1aa760ec", "shasum": "" }, "require": { @@ -192,11 +192,11 @@ "database abstraction", "pdo" ], - "time": "2019-09-10T00:18:05+00:00" + "time": "2019-10-07T00:50:17+00:00" }, { "name": "cakephp/datasource", - "version": "3.8.4", + "version": "3.8.5", "source": { "type": "git", "url": "https://github.com/cakephp/datasource.git", @@ -246,7 +246,7 @@ }, { "name": "cakephp/log", - "version": "3.8.4", + "version": "3.8.5", "source": { "type": "git", "url": "https://github.com/cakephp/log.git", @@ -291,7 +291,7 @@ }, { "name": "cakephp/utility", - "version": "3.8.4", + "version": "3.8.5", "source": { "type": "git", "url": "https://github.com/cakephp/utility.git", @@ -605,29 +605,31 @@ }, { "name": "robmorgan/phinx", - "version": "0.10.8", + "version": "0.11.1", "source": { "type": "git", "url": "https://github.com/cakephp/phinx.git", - "reference": "1960e93169707096fdfde04904a204970077f4be" + "reference": "a6cced878695d26396b26dfd62ce300aea07de05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/phinx/zipball/1960e93169707096fdfde04904a204970077f4be", - "reference": "1960e93169707096fdfde04904a204970077f4be", + "url": "https://api.github.com/repos/cakephp/phinx/zipball/a6cced878695d26396b26dfd62ce300aea07de05", + "reference": "a6cced878695d26396b26dfd62ce300aea07de05", "shasum": "" }, "require": { "cakephp/collection": "^3.6", + "cakephp/core": "^3.6", "cakephp/database": "^3.6", + "cakephp/datasource": "^3.6", "php": ">=5.6", - "symfony/config": "^2.8|^3.0|^4.0", - "symfony/console": "^2.8|^3.0|^4.0", - "symfony/yaml": "^2.8|^3.0|^4.0" + "symfony/config": "^3.4|^4.0", + "symfony/console": "^3.4|^4.0", + "symfony/yaml": "^3.4|^4.0" }, "require-dev": { "cakephp/cakephp-codesniffer": "^3.0", - "phpunit/phpunit": ">=5.7,<7.0", + "phpunit/phpunit": ">=5.7,<8.0", "sebastian/comparator": ">=1.2.3" }, "bin": [ @@ -644,18 +646,18 @@ "MIT" ], "authors": [ - { - "name": "Woody Gilk", - "email": "woody.gilk@gmail.com", - "homepage": "http://shadowhand.me", - "role": "Developer" - }, { "name": "Rob Morgan", "email": "robbym@gmail.com", "homepage": "https://robmorgan.id.au", "role": "Lead Developer" }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com", + "homepage": "https://shadowhand.me", + "role": "Developer" + }, { "name": "Richard Quadling", "email": "rquadling@gmail.com", @@ -663,7 +665,8 @@ }, { "name": "CakePHP Community", - "homepage": "https://github.com/cakephp/phinx/graphs/contributors" + "homepage": "https://github.com/cakephp/phinx/graphs/contributors", + "role": "Developer" } ], "description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.", @@ -675,20 +678,20 @@ "migrations", "phinx" ], - "time": "2019-07-08T16:59:55+00:00" + "time": "2019-08-28T12:24:19+00:00" }, { "name": "symfony/config", - "version": "v4.3.4", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "07d49c0f823e0bc367c6d84e35b61419188a5ece" + "reference": "0acb26407a9e1a64a275142f0ae5e36436342720" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/07d49c0f823e0bc367c6d84e35b61419188a5ece", - "reference": "07d49c0f823e0bc367c6d84e35b61419188a5ece", + "url": "https://api.github.com/repos/symfony/config/zipball/0acb26407a9e1a64a275142f0ae5e36436342720", + "reference": "0acb26407a9e1a64a275142f0ae5e36436342720", "shasum": "" }, "require": { @@ -739,20 +742,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2019-08-26T08:26:39+00:00" + "time": "2019-09-19T15:51:53+00:00" }, { "name": "symfony/console", - "version": "v3.4.31", + "version": "v3.4.32", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "4510f04e70344d70952566e4262a0b11df39cb10" + "reference": "4727d7f3c99b9dea0ae70ed4f34645728aa90453" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/4510f04e70344d70952566e4262a0b11df39cb10", - "reference": "4510f04e70344d70952566e4262a0b11df39cb10", + "url": "https://api.github.com/repos/symfony/console/zipball/4727d7f3c99b9dea0ae70ed4f34645728aa90453", + "reference": "4727d7f3c99b9dea0ae70ed4f34645728aa90453", "shasum": "" }, "require": { @@ -811,20 +814,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2019-08-26T07:52:58+00:00" + "time": "2019-10-06T19:52:09+00:00" }, { "name": "symfony/debug", - "version": "v3.4.31", + "version": "v3.4.32", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "0b600300918780001e2821db77bc28b677794486" + "reference": "b3e7ce815d82196435d16dc458023f8fb6b36ceb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/0b600300918780001e2821db77bc28b677794486", - "reference": "0b600300918780001e2821db77bc28b677794486", + "url": "https://api.github.com/repos/symfony/debug/zipball/b3e7ce815d82196435d16dc458023f8fb6b36ceb", + "reference": "b3e7ce815d82196435d16dc458023f8fb6b36ceb", "shasum": "" }, "require": { @@ -867,11 +870,11 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2019-08-20T13:31:17+00:00" + "time": "2019-09-19T15:32:51+00:00" }, { "name": "symfony/filesystem", - "version": "v4.3.4", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -1038,16 +1041,16 @@ }, { "name": "symfony/yaml", - "version": "v3.4.31", + "version": "v3.4.32", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "3dc414b7db30695bae671a1d86013d03f4ae9834" + "reference": "768f817446da74a776a31eea335540f9dcb53942" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/3dc414b7db30695bae671a1d86013d03f4ae9834", - "reference": "3dc414b7db30695bae671a1d86013d03f4ae9834", + "url": "https://api.github.com/repos/symfony/yaml/zipball/768f817446da74a776a31eea335540f9dcb53942", + "reference": "768f817446da74a776a31eea335540f9dcb53942", "shasum": "" }, "require": { @@ -1093,7 +1096,74 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2019-08-20T13:31:17+00:00" + "time": "2019-09-10T10:38:46+00:00" + }, + { + "name": "twig/twig", + "version": "v2.12.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "c7a85fd08348ca04b4d8f234f49583d9910906aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/c7a85fd08348ca04b4d8f234f49583d9910906aa", + "reference": "c7a85fd08348ca04b4d8f234f49583d9910906aa", + "shasum": "" + }, + "require": { + "php": "^7.0", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0", + "symfony/debug": "^3.4|^4.2", + "symfony/phpunit-bridge": "^4.4@dev|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.12-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_": "lib/" + }, + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "homepage": "https://twig.symfony.com/contributors", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "time": "2019-10-05T16:42:38+00:00" }, { "name": "whichbrowser/parser", @@ -1514,22 +1584,22 @@ }, { "name": "phpspec/prophecy", - "version": "1.8.1", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76" + "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76", - "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/f6811d96d97bdf400077a0cc100ae56aa32b9203", + "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", "sebastian/comparator": "^1.1|^2.0|^3.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, @@ -1573,7 +1643,7 @@ "spy", "stub" ], - "time": "2019-06-13T12:50:23+00:00" + "time": "2019-10-03T11:07:50+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1780,16 +1850,16 @@ }, { "name": "phpunit/php-token-stream", - "version": "3.1.0", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a" + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e899757bb3df5ff6e95089132f32cd59aac2220a", - "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", "shasum": "" }, "require": { @@ -1825,7 +1895,7 @@ "keywords": [ "tokenizer" ], - "time": "2019-07-25T05:29:42+00:00" + "time": "2019-09-17T06:23:10+00:00" }, { "name": "phpunit/phpunit", @@ -2477,6 +2547,57 @@ "homepage": "https://github.com/sebastianbergmann/version", "time": "2016-10-03T07:35:21+00:00" }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.5.0", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "0afebf16a2e7f1e434920fa976253576151effe9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/0afebf16a2e7f1e434920fa976253576151effe9", + "reference": "0afebf16a2e7f1e434920fa976253576151effe9", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards" + ], + "time": "2019-09-26T23:12:26+00:00" + }, { "name": "theseer/tokenizer", "version": "1.1.3", diff --git a/db/data/data.sql b/db/data/data.sql index 17a045db0..23fe56012 100644 --- a/db/data/data.sql +++ b/db/data/data.sql @@ -114,4 +114,4 @@ INSERT INTO forums (ID, CategoryID, Sort, Name, Description, MinClassRead, MinCl INSERT INTO forums (ID, CategoryID, Sort, Name, Description, MinClassRead, MinClassWrite, MinClassCreate) VALUES (37,21,3400,'Implemented','The Suggestion I made has been implemented',100,800,800); INSERT INTO forums (ID, CategoryID, Sort, Name, Description, MinClassRead, MinClassWrite, MinClassCreate) VALUES (38,5,1650,'VIP','Very Important Phorum',601,601,601); INSERT INTO forums (ID, CategoryID, Sort, Name, Description, MinClassRead, MinClassWrite, MinClassCreate) VALUES (39,5,1670,'Invitations','Stairway to Heaven',250,250,250); -INSERT INTO forums (ID, CategoryID, Sort, Name, Description, MinClassRead, MinClassWrite, MinClassCreate) VALUES (40,5,1610,'Torrent Masters','The Holy Grail',400,400,400); \ No newline at end of file +INSERT INTO forums (ID, CategoryID, Sort, Name, Description, MinClassRead, MinClassWrite, MinClassCreate) VALUES (40,5,1610,'Torrent Masters','The Holy Grail',400,400,400); diff --git a/db/migrations/20180104060449_tables.php b/db/migrations/20180104060449_tables.php index 5980a394a..aafaaded7 100644 --- a/db/migrations/20180104060449_tables.php +++ b/db/migrations/20180104060449_tables.php @@ -1,10 +1,11 @@ execute(" + public function down() { + $this->execute(" SET FOREIGN_KEY_CHECKS = 0; SET @tables = NULL; SET GROUP_CONCAT_MAX_LEN=32768; @@ -20,88 +21,88 @@ public function down() { DEALLOCATE PREPARE stmt; SET FOREIGN_KEY_CHECKS = 1; DROP FUNCTION binomial_ci;"); - } - - /** - * TODO: Migrate from gazelle.sql to a proper change() method - */ - public function up() { - $this->execute("SET FOREIGN_KEY_CHECKS = 0;"); - - $this->table('api_applications', ['id' => false, 'primary_key' => 'ID']) - ->addColumn('ID', 'integer', ['limit' => 10, 'identity' => true]) - ->addColumn('UserID', 'integer', ['limit' => 10]) - ->addColumn('Token', 'char', ['limit' => 32]) - ->addColumn('Name', 'string', ['limit' => 50]) - ->create(); - - $this->table('api_users', ['id' => false, 'primary_key' => ['UserID', 'AppID']]) - ->addColumn('UserID', 'integer', ['limit' => 10]) - ->addColumn('AppID', 'integer', ['limit' => 10]) - ->addColumn('Token', 'char', ['limit' => 32]) - ->addColumn('State', 'enum', ['values' => ['0', '1', '2'], 'default' => '0']) - ->addColumn('Time', 'timestamp') - ->addColumn('Access', 'text') - ->create(); - - $this->table('artists_alias', ['id' => false, 'primary_key' => 'AliasID']) - ->addColumn('AliasID', 'integer', ['limit' => 10, 'identity' => true]) - ->addColumn('ArtistID', 'integer', ['limit' => 10]) - ->addColumn('Name', 'string', ['limit' => 200, 'null' => true, 'default' => null]) - ->addColumn('Redirect', 'integer', ['limit' => 10, 'default' => 0]) - ->addColumn('UserID', 'integer', ['limit' => 10, 'signed' => false, 'default' => 0]) - ->addIndex(['ArtistID', 'Name']) - ->create(); - - $this->table('artists_group', ['id' => false, 'primary_key' => 'ArtistID']) - ->addColumn('ArtistID', 'integer', ['limit' => 10, 'identity' => true]) - ->addColumn('Name', 'string', ['limit' => 200, 'null' => true, 'default' => null]) - ->addColumn('RevisionID', 'integer', ['limit' => 12, 'null' => true, 'default' => null]) - ->addColumn('VanityHouse', 'boolean') - ->addColumn('LastCommentID', 'integer', ['limit' => 10, 'default' => 0]) - ->addIndex(['Name', 'RevisionID']) - ->create(); - - $this->table('artists_similar', ['id' => false, 'primary_key' => ['ArtistID', 'SimilarID']]) - ->addColumn('ArtistID', 'integer', ['limit' => 10, 'default' => 0]) - ->addColumn('SimilarID', 'integer', ['limit' => 12, 'default' => 0]) - ->addIndex(['ArtistID', 'SimilarID']) - ->create(); - - $this->table('artists_similar_scores', ['id' => false, 'primary_key' => 'SimilarID']) - ->addColumn('SimilarID', 'integer', ['limit' => 12, 'identity' => true]) - ->addColumn('Score', 'integer', ['limit' => 10, 'default' => 0]) - ->addIndex('Score') - ->create(); - - $this->table('artists_similar_votes', ['id' => false, 'primary_key' => ['SimilarID', 'UserID', 'Way']]) - ->addColumn('SimilarID', 'integer', ['limit' => 12]) - ->addColumn('UserID', 'integer', ['limit' => 10]) - ->addColumn('Way', 'enum', ['values' => ['up', 'down'], 'default' => 'up']) - ->create(); - - $this->table('artists_tags', ['id' => false, 'primary_key' => ['TagID', 'ArtistID']]) - ->addColumn('TagID', 'integer', ['limit' => 10, 'default' => 0]) - ->addColumn('ArtistID', 'integer', ['limit' => 10, 'default' => 0]) - ->addColumn('PositiveVotes', 'integer', ['limit' => 6, 'default' => 1]) - ->addColumn('NegativeVotes', 'integer', ['limit' => 6, 'default' => 1]) - ->addColumn('UserID', 'integer', ['limit' => 10, 'default' => null]) - ->addIndex(['TagID', 'ArtistID', 'PositiveVotes', 'NegativeVotes', 'UserID']) - ->create(); - - $this->table('bad_passwords', ['id' => false, 'primary_key' => 'Password']) - ->addColumn('Password', 'char', ['limit' => 32]) - ->create(); - - /* - $this->table('blog', ['id' => false, 'primary_key' => 'ID']) - ->addColumn('ID', 'integer', ['limit' => 10, 'signed' => false, 'identity' => true]) - ->addColumn('UserID', 'integer', ['limit' => 10, 'signed' => false]) - ->addColumn('Title', 'string', ['limit' => 255]) - ->addColumn('Body', 'text') - */ - - $this->execute(" + } + + /** + * TODO: Migrate from gazelle.sql to a proper change() method + */ + public function up() { + $this->execute("SET FOREIGN_KEY_CHECKS = 0;"); + + $this->table('api_applications', ['id' => false, 'primary_key' => 'ID']) + ->addColumn('ID', 'integer', ['limit' => 10, 'identity' => true]) + ->addColumn('UserID', 'integer', ['limit' => 10]) + ->addColumn('Token', 'char', ['limit' => 32]) + ->addColumn('Name', 'string', ['limit' => 50]) + ->create(); + + $this->table('api_users', ['id' => false, 'primary_key' => ['UserID', 'AppID']]) + ->addColumn('UserID', 'integer', ['limit' => 10]) + ->addColumn('AppID', 'integer', ['limit' => 10]) + ->addColumn('Token', 'char', ['limit' => 32]) + ->addColumn('State', 'enum', ['values' => ['0', '1', '2'], 'default' => '0']) + ->addColumn('Time', 'timestamp') + ->addColumn('Access', 'text') + ->create(); + + $this->table('artists_alias', ['id' => false, 'primary_key' => 'AliasID']) + ->addColumn('AliasID', 'integer', ['limit' => 10, 'identity' => true]) + ->addColumn('ArtistID', 'integer', ['limit' => 10]) + ->addColumn('Name', 'string', ['limit' => 200, 'null' => true, 'default' => null]) + ->addColumn('Redirect', 'integer', ['limit' => 10, 'default' => 0]) + ->addColumn('UserID', 'integer', ['limit' => 10, 'signed' => false, 'default' => 0]) + ->addIndex(['ArtistID', 'Name']) + ->create(); + + $this->table('artists_group', ['id' => false, 'primary_key' => 'ArtistID']) + ->addColumn('ArtistID', 'integer', ['limit' => 10, 'identity' => true]) + ->addColumn('Name', 'string', ['limit' => 200, 'null' => true, 'default' => null]) + ->addColumn('RevisionID', 'integer', ['limit' => 12, 'null' => true, 'default' => null]) + ->addColumn('VanityHouse', 'boolean') + ->addColumn('LastCommentID', 'integer', ['limit' => 10, 'default' => 0]) + ->addIndex(['Name', 'RevisionID']) + ->create(); + + $this->table('artists_similar', ['id' => false, 'primary_key' => ['ArtistID', 'SimilarID']]) + ->addColumn('ArtistID', 'integer', ['limit' => 10, 'default' => 0]) + ->addColumn('SimilarID', 'integer', ['limit' => 12, 'default' => 0]) + ->addIndex(['ArtistID', 'SimilarID']) + ->create(); + + $this->table('artists_similar_scores', ['id' => false, 'primary_key' => 'SimilarID']) + ->addColumn('SimilarID', 'integer', ['limit' => 12, 'identity' => true]) + ->addColumn('Score', 'integer', ['limit' => 10, 'default' => 0]) + ->addIndex('Score') + ->create(); + + $this->table('artists_similar_votes', ['id' => false, 'primary_key' => ['SimilarID', 'UserID', 'Way']]) + ->addColumn('SimilarID', 'integer', ['limit' => 12]) + ->addColumn('UserID', 'integer', ['limit' => 10]) + ->addColumn('Way', 'enum', ['values' => ['up', 'down'], 'default' => 'up']) + ->create(); + + $this->table('artists_tags', ['id' => false, 'primary_key' => ['TagID', 'ArtistID']]) + ->addColumn('TagID', 'integer', ['limit' => 10, 'default' => 0]) + ->addColumn('ArtistID', 'integer', ['limit' => 10, 'default' => 0]) + ->addColumn('PositiveVotes', 'integer', ['limit' => 6, 'default' => 1]) + ->addColumn('NegativeVotes', 'integer', ['limit' => 6, 'default' => 1]) + ->addColumn('UserID', 'integer', ['limit' => 10, 'default' => null]) + ->addIndex(['TagID', 'ArtistID', 'PositiveVotes', 'NegativeVotes', 'UserID']) + ->create(); + + $this->table('bad_passwords', ['id' => false, 'primary_key' => 'Password']) + ->addColumn('Password', 'char', ['limit' => 32]) + ->create(); + + /* + $this->table('blog', ['id' => false, 'primary_key' => 'ID']) + ->addColumn('ID', 'integer', ['limit' => 10, 'signed' => false, 'identity' => true]) + ->addColumn('UserID', 'integer', ['limit' => 10, 'signed' => false]) + ->addColumn('Title', 'string', ['limit' => 255]) + ->addColumn('Body', 'text') + */ + + $this->execute(" CREATE TABLE `blog` ( `ID` int(10) unsigned NOT NULL AUTO_INCREMENT, `UserID` int(10) unsigned NOT NULL, @@ -1325,28 +1326,28 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `torrents_lossymaster_approved` ( - `TorrentID` int(10) NOT NULL DEFAULT '0', + `TorrentID` int(10) NOT NULL DEFAULT '0', `UserID` int(10) NOT NULL DEFAULT '0', `TimeAdded` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', KEY `TimeAdded` (`TimeAdded`) ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `torrents_lossyweb_approved` ( - `TorrentID` int(10) NOT NULL DEFAULT '0', + `TorrentID` int(10) NOT NULL DEFAULT '0', `UserID` int(10) NOT NULL DEFAULT '0', `TimeAdded` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', KEY `TimeAdded` (`TimeAdded`) ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE torrents_missing_lineage ( - `TorrentID` int(10) NOT NULL DEFAULT '0', + `TorrentID` int(10) NOT NULL DEFAULT '0', `UserID` int(10) NOT NULL DEFAULT '0', `TimeAdded` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', KEY TimeAdded (TimeAdded) ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `torrents_peerlists` ( - `TorrentID` int(11) NOT NULL, + `TorrentID` int(11) NOT NULL, `GroupID` int(11) DEFAULT NULL, `Seeders` int(11) DEFAULT NULL, `Leechers` int(11) DEFAULT NULL, @@ -1357,7 +1358,7 @@ public function up() { ) ENGINE=MyISAM CHARSET utf8; CREATE TABLE `torrents_peerlists_compare` ( - `TorrentID` int(11) NOT NULL, + `TorrentID` int(11) NOT NULL, `GroupID` int(11) DEFAULT NULL, `Seeders` int(11) DEFAULT NULL, `Leechers` int(11) DEFAULT NULL, @@ -1368,7 +1369,7 @@ public function up() { ) ENGINE=MyISAM CHARSET utf8; CREATE TABLE `torrents_recommended` ( - `GroupID` int(10) NOT NULL, + `GroupID` int(10) NOT NULL, `UserID` int(10) NOT NULL, `Time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', PRIMARY KEY (`GroupID`), @@ -1376,7 +1377,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `torrents_tags` ( - `TagID` int(10) NOT NULL DEFAULT '0', + `TagID` int(10) NOT NULL DEFAULT '0', `GroupID` int(10) NOT NULL DEFAULT '0', `PositiveVotes` int(6) NOT NULL DEFAULT '1', `NegativeVotes` int(6) NOT NULL DEFAULT '1', @@ -1390,7 +1391,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `torrents_tags_votes` ( - `GroupID` int(10) NOT NULL, + `GroupID` int(10) NOT NULL, `TagID` int(10) NOT NULL, `UserID` int(10) NOT NULL, `Way` enum('up','down') NOT NULL DEFAULT 'up', @@ -1398,7 +1399,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `torrents_votes` ( - `GroupID` int(10) NOT NULL, + `GroupID` int(10) NOT NULL, `Ups` int(10) unsigned NOT NULL DEFAULT '0', `Total` int(10) unsigned NOT NULL DEFAULT '0', `Score` float NOT NULL DEFAULT '0', @@ -1408,7 +1409,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `upload_contest` ( - `TorrentID` int(10) unsigned NOT NULL, + `TorrentID` int(10) unsigned NOT NULL, `UserID` int(10) unsigned NOT NULL, PRIMARY KEY (`TorrentID`), KEY `UserID` (`UserID`), @@ -1416,7 +1417,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `user_questions` ( - `ID` int(10) NOT NULL AUTO_INCREMENT, + `ID` int(10) NOT NULL AUTO_INCREMENT, `Question` mediumtext NOT NULL, `UserID` int(10) NOT NULL, `Date` datetime NOT NULL, @@ -1425,7 +1426,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_collage_subs` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `CollageID` int(10) NOT NULL, `LastVisit` datetime DEFAULT NULL, PRIMARY KEY (`UserID`,`CollageID`), @@ -1433,7 +1434,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_comments_last_read` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `Page` enum('artist','collages','requests','torrents') NOT NULL, `PageID` int(10) NOT NULL, `PostID` int(10) NOT NULL, @@ -1442,7 +1443,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_donor_ranks` ( - `UserID` int(10) NOT NULL DEFAULT '0', + `UserID` int(10) NOT NULL DEFAULT '0', `Rank` tinyint(2) NOT NULL DEFAULT '0', `DonationTime` datetime DEFAULT NULL, `Hidden` tinyint(2) NOT NULL DEFAULT '0', @@ -1458,7 +1459,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_downloads` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `TorrentID` int(1) NOT NULL, `Time` datetime NOT NULL, PRIMARY KEY (`UserID`,`TorrentID`,`Time`), @@ -1467,7 +1468,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_dupes` ( - `GroupID` int(10) unsigned NOT NULL, + `GroupID` int(10) unsigned NOT NULL, `UserID` int(10) unsigned NOT NULL, UNIQUE KEY `UserID` (`UserID`), KEY `GroupID` (`GroupID`), @@ -1476,14 +1477,14 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_enable_recommendations` ( - `ID` int(10) NOT NULL, + `ID` int(10) NOT NULL, `Enable` tinyint(1) DEFAULT NULL, PRIMARY KEY (`ID`), KEY `Enable` (`Enable`) ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_enable_requests` ( - `ID` int(11) NOT NULL AUTO_INCREMENT, + `ID` int(11) NOT NULL AUTO_INCREMENT, `UserID` int(10) unsigned NOT NULL, `Email` varchar(255) NOT NULL, `IP` varchar(15) NOT NULL DEFAULT '0.0.0.0', @@ -1501,7 +1502,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_freeleeches` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `TorrentID` int(10) NOT NULL, `Time` datetime NOT NULL, `Expired` tinyint(1) NOT NULL DEFAULT '0', @@ -1513,12 +1514,12 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_geodistribution` ( - `Code` varchar(2) NOT NULL, + `Code` varchar(2) NOT NULL, `Users` int(10) NOT NULL ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_history_emails` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `Email` varchar(255) DEFAULT NULL, `Time` datetime DEFAULT NULL, `IP` varchar(15) DEFAULT NULL, @@ -1526,7 +1527,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_history_ips` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `IP` varchar(15) NOT NULL DEFAULT '0.0.0.0', `StartTime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `EndTime` datetime DEFAULT NULL, @@ -1538,7 +1539,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_history_passkeys` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `OldPassKey` varchar(32) DEFAULT NULL, `NewPassKey` varchar(32) DEFAULT NULL, `ChangeTime` datetime DEFAULT NULL, @@ -1546,14 +1547,14 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_history_passwords` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `ChangeTime` datetime DEFAULT NULL, `ChangerIP` varchar(15) DEFAULT NULL, KEY `User_Time` (`UserID`,`ChangeTime`) ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_info` ( - `UserID` int(10) unsigned NOT NULL, + `UserID` int(10) unsigned NOT NULL, `StyleID` int(10) unsigned NOT NULL, `StyleURL` varchar(255) DEFAULT NULL, `Info` text NOT NULL, @@ -1615,14 +1616,14 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_levels` ( - `UserID` int(10) unsigned NOT NULL, + `UserID` int(10) unsigned NOT NULL, `PermissionID` int(10) unsigned NOT NULL, PRIMARY KEY (`UserID`,`PermissionID`), KEY `PermissionID` (`PermissionID`) ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_main` ( - `ID` int(10) unsigned NOT NULL AUTO_INCREMENT, + `ID` int(10) unsigned NOT NULL AUTO_INCREMENT, `Username` varchar(20) NOT NULL, `Email` varchar(255) NOT NULL, `PassHash` varchar(60) NOT NULL, @@ -1670,7 +1671,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_notifications_settings` ( - `UserID` int(10) NOT NULL DEFAULT '0', + `UserID` int(10) NOT NULL DEFAULT '0', `Inbox` tinyint(1) DEFAULT '1', `StaffPM` tinyint(1) DEFAULT '1', `News` tinyint(1) DEFAULT '1', @@ -1688,7 +1689,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_notify_filters` ( - `ID` int(12) NOT NULL AUTO_INCREMENT, + `ID` int(12) NOT NULL AUTO_INCREMENT, `UserID` int(10) NOT NULL, `Label` varchar(128) NOT NULL DEFAULT '', `Artists` mediumtext NOT NULL, @@ -1712,7 +1713,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_notify_quoted` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `QuoterID` int(10) NOT NULL, `Page` enum('forums','artist','collages','requests','torrents') NOT NULL, `PageID` int(10) NOT NULL, @@ -1723,7 +1724,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_notify_torrents` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `FilterID` int(10) NOT NULL, `GroupID` int(10) NOT NULL, `TorrentID` int(10) NOT NULL, @@ -1734,7 +1735,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_points` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `GroupID` int(10) NOT NULL, `Points` tinyint(1) NOT NULL DEFAULT '1', PRIMARY KEY (`UserID`,`GroupID`), @@ -1743,7 +1744,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_points_requests` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `RequestID` int(10) NOT NULL, `Points` tinyint(1) NOT NULL DEFAULT '1', PRIMARY KEY (`RequestID`), @@ -1752,14 +1753,14 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_push_notifications` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `PushService` tinyint(1) NOT NULL DEFAULT '0', `PushOptions` text NOT NULL, PRIMARY KEY (`UserID`) ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_sessions` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `SessionID` char(32) NOT NULL, `KeepLogged` enum('0','1') NOT NULL DEFAULT '0', `Browser` varchar(40) DEFAULT NULL, @@ -1776,20 +1777,20 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_subscriptions` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `TopicID` int(10) NOT NULL, PRIMARY KEY (`UserID`,`TopicID`) ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_subscriptions_comments` ( - `UserID` int(10) NOT NULL, + `UserID` int(10) NOT NULL, `Page` enum('artist','collages','requests','torrents') NOT NULL, `PageID` int(10) NOT NULL, PRIMARY KEY (`UserID`,`Page`,`PageID`) ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_torrent_history` ( - `UserID` int(10) unsigned NOT NULL, + `UserID` int(10) unsigned NOT NULL, `NumTorrents` int(6) unsigned NOT NULL, `Date` int(8) unsigned NOT NULL, `Time` int(11) unsigned NOT NULL DEFAULT '0', @@ -1802,14 +1803,14 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_torrent_history_snatch` ( - `UserID` int(10) unsigned NOT NULL, + `UserID` int(10) unsigned NOT NULL, `NumSnatches` int(10) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`UserID`), KEY `NumSnatches` (`NumSnatches`) ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_torrent_history_temp` ( - `UserID` int(10) unsigned NOT NULL, + `UserID` int(10) unsigned NOT NULL, `NumTorrents` int(6) unsigned NOT NULL DEFAULT '0', `SumTime` bigint(20) unsigned NOT NULL DEFAULT '0', `SeedingAvg` int(6) unsigned NOT NULL DEFAULT '0', @@ -1817,7 +1818,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_votes` ( - `UserID` int(10) unsigned NOT NULL, + `UserID` int(10) unsigned NOT NULL, `GroupID` int(10) NOT NULL, `Type` enum('Up','Down') DEFAULT NULL, `Time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, @@ -1831,7 +1832,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `users_warnings_forums` ( - `UserID` int(10) unsigned NOT NULL, + `UserID` int(10) unsigned NOT NULL, `Comment` text NOT NULL, PRIMARY KEY (`UserID`) ) ENGINE=InnoDB CHARSET utf8; @@ -1856,7 +1857,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `wiki_artists` ( - `RevisionID` int(12) NOT NULL AUTO_INCREMENT, + `RevisionID` int(12) NOT NULL AUTO_INCREMENT, `PageID` int(10) NOT NULL DEFAULT '0', `Body` text, `UserID` int(10) NOT NULL DEFAULT '0', @@ -1870,7 +1871,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `wiki_revisions` ( - `ID` int(10) NOT NULL, + `ID` int(10) NOT NULL, `Revision` int(10) NOT NULL, `Title` varchar(100) DEFAULT NULL, `Body` mediumtext, @@ -1880,7 +1881,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `wiki_torrents` ( - `RevisionID` int(12) NOT NULL AUTO_INCREMENT, + `RevisionID` int(12) NOT NULL AUTO_INCREMENT, `PageID` int(10) NOT NULL DEFAULT '0', `Body` text, `UserID` int(10) NOT NULL DEFAULT '0', @@ -1894,7 +1895,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `xbt_client_whitelist` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `peer_id` varchar(20) DEFAULT NULL, `vstring` varchar(200) DEFAULT '', PRIMARY KEY (`id`), @@ -1902,7 +1903,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `xbt_files_history` ( - `uid` int(11) NOT NULL, + `uid` int(11) NOT NULL, `fid` int(11) NOT NULL, `seedtime` int(11) NOT NULL DEFAULT '0', `downloaded` bigint(20) NOT NULL DEFAULT '0', @@ -1910,7 +1911,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `xbt_files_users` ( - `uid` int(11) NOT NULL, + `uid` int(11) NOT NULL, `active` tinyint(1) NOT NULL DEFAULT '1', `announced` int(11) NOT NULL DEFAULT '0', `completed` tinyint(1) NOT NULL DEFAULT '0', @@ -1935,7 +1936,7 @@ public function up() { ) ENGINE=InnoDB CHARSET utf8; CREATE TABLE `xbt_snatched` ( - `uid` int(11) NOT NULL DEFAULT '0', + `uid` int(11) NOT NULL DEFAULT '0', `tstamp` int(11) NOT NULL, `fid` int(11) NOT NULL, `IP` varchar(15) NOT NULL, @@ -1950,1056 +1951,1065 @@ public function up() { SQL SECURITY INVOKER RETURN IF(n = 0,0.0,((p + 1.35336) / n - 1.6452 * SQRT((p * (n-p)) / n + 0.67668) / n) / (1 + 2.7067 / n));"); - $this->insert('contest_type', [['Name' => 'upload_flac'], ['Name' => 'request_fill']]); - - $this->insert('forums_categories', [ - ['ID' => 1, 'Name' => 'Site', 'Sort' => 1], - ['ID' => 21, 'Name' => 'Suggestions', 'Sort' => 3], - ['ID' => 5, 'Name' => 'Community', 'Sort' => 5], - ['ID' => 8, 'Name' => 'Music', 'Sort' => 8], - ['ID' => 10, 'Name' => 'Help', 'Sort' => 10], - ['ID' => 20, 'Name' => 'Trash', 'Sort' => 20]]); - - $this->insert('forums', [ - ['ID' => 7, 'CategoryID' => 1, 'Sort' => 100, 'Name' => 'Pharmacy', 'Description' => 'Get your medication dispensed here', 'MinClassRead' => 1000, 'MinClassWrite' => 1000, 'MinClassCreate' => 1000], - ['ID' => 5, 'CategoryID' => 1, 'Sort' => 200, 'Name' => 'Staff', 'Description' => 'No place like home', 'MinClassRead' => 800, 'MinClassWrite' => 800, 'MinClassCreate' => 800], - ['ID' => 35, 'CategoryID' => 1, 'Sort' => 250, 'Name' => 'Developers', 'Description' => 'Developers forum', 'MinClassRead' => 800, 'MinClassWrite' => 800, 'MinClassCreate' => 800], - ['ID' => 33, 'CategoryID' => 1, 'Sort' => 750, 'Name' => 'Designers', 'Description' => 'Designers', 'MinClassRead' => 800, 'MinClassWrite' => 800, 'MinClassCreate' => 800], - ['ID' => 28, 'CategoryID' => 1, 'Sort' => 800, 'Name' => 'First Line Support', 'Description' => 'Special Support Operations Command (SSOC)', 'MinClassRead' => 900, 'MinClassWrite' => 900, 'MinClassCreate' => 900], - ['ID' => 30, 'CategoryID' => 1, 'Sort' => 900, 'Name' => 'Interviewers', 'Description' => 'The Candidates', 'MinClassRead' => 900, 'MinClassWrite' => 900, 'MinClassCreate' => 900], - - ['ID' => 31, 'CategoryID' => 1, 'Sort' => 1000, 'Name' => 'Charlie Team', 'Description' => 'Quality Assurance', 'MinClassRead' => 850, 'MinClassWrite' => 850, 'MinClassCreate' => 850], - ['ID' => 1, 'CategoryID' => 1, 'Sort' => 300, 'Name' => 'APOLLO', 'Description' => 'apollo.rip', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 12, 'CategoryID' => 1, 'Sort' => 600, 'Name' => 'Announcements', 'Description' => 'Public service announcements', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 800], - ['ID' => 6, 'CategoryID' => 1, 'Sort' => 400, 'Name' => 'Bugs', 'Description' => 'I found a non critical bug', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 24, 'CategoryID' => 5, 'Sort' => 2000, 'Name' => 'Projects', 'Description' => 'I\'m working on a project', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - - ['ID' => 13, 'CategoryID' => 21, 'Sort' => 2990, 'Name' => 'Suggestions', 'Description' => 'I have an idea', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 36, 'CategoryID' => 21, 'Sort' => 3000, 'Name' => 'Approved', 'Description' => 'Self explanatory...', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 800], - ['ID' => 37, 'CategoryID' => 21, 'Sort' => 3400, 'Name' => 'Implemented', 'Description' => 'The Suggestion I made has been implemented', 'MinClassRead' => 100, 'MinClassWrite' => 800, 'MinClassCreate' => 800], - ['ID' => 15, 'CategoryID' => 21, 'Sort' => 3500, 'Name' => 'Denied', 'Description' => 'The Suggestion I made has been denied', 'MinClassRead' => 100, 'MinClassWrite' => 800, 'MinClassCreate' => 800], - - ['ID' => 2, 'CategoryID' => 5, 'Sort' => 1200, 'Name' => 'Chat', 'Description' => 'General chat', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 25, 'CategoryID' => 5, 'Sort' => 2100, 'Name' => 'Games', 'Description' => 'I\'m a gamer', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 27, 'CategoryID' => 5, 'Sort' => 1100, 'Name' => 'Serious Discussions', 'Description' => 'The Library', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 29, 'CategoryID' => 5, 'Sort' => 1300, 'Name' => 'Power User', 'Description' => 'PU Forum <3', 'MinClassRead' => 200, 'MinClassWrite' => 200, 'MinClassCreate' => 200], - ['ID' => 11, 'CategoryID' => 5, 'Sort' => 1600, 'Name' => 'Elites', 'Description' => 'I\'m 1337', 'MinClassRead' => 250, 'MinClassWrite' => 250, 'MinClassCreate' => 250], - ['ID' => 40, 'CategoryID' => 5, 'Sort' => 1610, 'Name' => 'Torrent Masters', 'Description' => 'The Holy Grail', 'MinClassRead' => 400, 'MinClassWrite' => 400, 'MinClassCreate' => 400], - ['ID' => 38, 'CategoryID' => 5, 'Sort' => 1650, 'Name' => 'VIP', 'Description' => 'Very Important Phorum', 'MinClassRead' => 601, 'MinClassWrite' => 601, 'MinClassCreate' => 601], - ['ID' => 10, 'CategoryID' => 5, 'Sort' => 1500, 'Name' => 'Donors', 'Description' => 'I have a heart', 'MinClassRead' => 800, 'MinClassWrite' => 800, 'MinClassCreate' => 800], - ['ID' => 39, 'CategoryID' => 5, 'Sort' => 1670, 'Name' => 'Invitations', 'Description' => 'Stairway to Heaven', 'MinClassRead' => 250, 'MinClassWrite' => 250, 'MinClassCreate' => 250], - ['ID' => 22, 'CategoryID' => 5, 'Sort' => 1800, 'Name' => 'Comics', 'Description' => 'I read comics', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 23, 'CategoryID' => 5, 'Sort' => 1900, 'Name' => 'Technology', 'Description' => 'I like technology', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - - ['ID' => 8, 'CategoryID' => 8, 'Sort' => 30, 'Name' => 'Music', 'Description' => 'For the masses', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 18, 'CategoryID' => 8, 'Sort' => 31, 'Name' => 'Vanity House', 'Description' => 'I have some of my work to share', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 32, 'CategoryID' => 8, 'Sort' => 20, 'Name' => 'Audiophile', 'Description' => 'For the audiophile', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 19, 'CategoryID' => 8, 'Sort' => 32, 'Name' => 'The Studio', 'Description' => 'I\'m a DJ', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 26, 'CategoryID' => 8, 'Sort' => 34, 'Name' => 'Vinyl', 'Description' => 'Vinyl \'s are here to stay', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 20, 'CategoryID' => 8, 'Sort' => 33, 'Name' => 'Offered', 'Description' => 'I have some music to offer', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 9, 'CategoryID' => 5, 'Sort' => 1400, 'Name' => 'Artists', 'Description' => 'For the artistics', 'MinClassRead' => 800, 'MinClassWrite' => 800, 'MinClassCreate' => 800], - ['ID' => 21, 'CategoryID' => 5, 'Sort' => 1700, 'Name' => 'Concerts and Events', 'Description' => 'I\'m off to a gig', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - - ['ID' => 3, 'CategoryID' => 10, 'Sort' => 40, 'Name' => 'Help!', 'Description' => 'I fell down and I cant get up', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 34, 'CategoryID' => 10, 'Sort' => 41, 'Name' => 'Editing', 'Description' => 'Quality Control', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 16, 'CategoryID' => 10, 'Sort' => 42, 'Name' => 'Tutorials', 'Description' => 'I would like to share my knowledge', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - ['ID' => 17, 'CategoryID' => 10, 'Sort' => 43, 'Name' => 'BitTorrent', 'Description' => 'I need to talk about BitTorrent', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], - - ['ID' => 4, 'CategoryID' => 20, 'Sort' => 5, 'Name' => 'Trash', 'Description' => 'Every thread ends up here eventually', 'MinClassRead' => 100, 'MinClassWrite' => 800, 'MinClassCreate' => 800], - ['ID' => 14, 'CategoryID' => 20, 'Sort' => 101, 'Name' => 'Resolved Bugs', 'Description' => 'The bug I reported has been fixed', 'MinClassRead' => 100, 'MinClassWrite' => 800, 'MinClassCreate' => 800], - ]); - - $this->insert('permissions', [ - [ - 'ID' => 2, - 'Level' => 100, - 'Name' => 'User', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_album_votes' => 1, - 'site_edit_wiki' => 1, - 'torrents_add_artist' => 1, - 'MaxCollages' => 0 - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '', - 'Secondary' => 0 - ], - [ - 'ID' => 3, - 'Level' => 150, - 'Name' => 'Member', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_collages_manage' => 1, - 'site_collages_subscribe' => 1, - 'site_advanced_top10' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'zip_downloader' => 1, - 'torrents_add_artist' => 1, - 'edit_unknowns' => 1, - 'MaxCollages' => 0 - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '', - 'Secondary' => 0 - ], - [ - 'ID' => 4, - 'Level' => 200, - 'Name' => 'Power User', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_manage' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'forums_polls_create' => 1, - 'zip_downloader' => 1, - 'torrents_add_artist' => 1, - 'MaxCollages' => 1, - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '', - 'Secondary' => 0 - ], - [ - 'ID' => 5, - 'Level' => 250, - 'Name' => 'Elite', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_manage' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_advanced_top10' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'forums_polls_create' => 1, - 'site_delete_tag' => 1, - 'zip_downloader' => 1, - 'torrents_edit' => 1, - 'torrents_add_artist' => 1, - 'edit_unknowns' => 1, - 'MaxCollages' => 3, - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '', - 'Secondary' => 0 - ], - [ - 'ID' => 19, - 'Level' => 201, - 'Name' => 'Artist', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'site_recommend_own' => 1, - 'MaxCollages' => 0, - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '9', - 'Secondary' => 0 - ], - [ - 'ID' => 20, - 'Level' => 202, - 'Name' => 'Donor', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_manage' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'forums_polls_create' => 1, - 'zip_downloader' => 1, - 'MaxCollages' => 1, - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '10', - 'Secondary' => 0 - ], - [ - 'ID' => 42, - 'Level' => 205, - 'Name' => 'Donor', - 'Values' => serialize([ - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_top10' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_manage' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'forums_polls_create' => 1, - 'zip_downloader' => 1, - 'MaxCollages' => 1, - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '10', - 'Secondary' => 1 - ], - [ - 'ID' => 23, - 'Level' => 255, - 'Name' => 'First Line Support', - 'Values' => serialize([ - 'site_collages_personal' => 1, - 'site_advanced_top10' => 1, - 'MaxCollages' => 1, - ]), - 'DisplayStaff' => '1', - 'PermittedForums' => '28', - 'Secondary' => 1 - ], - [ - 'ID' => 41, - 'Level' => 257, - 'Name' => 'Recruiter', - 'Values' => serialize([ - 'site_send_unlimited_invites' => 1, - 'MaxCollages' => 0, - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '', - 'Secondary' => 1 - ], - [ - 'ID' => 30, - 'Level' => 300, - 'Name' => 'Interviewer', - 'Values' => serialize([ - 'MaxCollages' => 0 - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '30', - 'Secondary' => 1 - ], - [ - 'ID' => 31, - 'Level' => 310, - 'Name' => 'Torrent Celebrity', - 'Values' => serialize([ - 'MaxCollages' => 0 - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '', - 'Secondary' => 1 - ], - [ - 'ID' => 32, - 'Level' => 320, - 'Name' => 'Designer', - 'Values' => serialize([ - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_collages_create' => 1, - 'site_collages_manage' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_advanced_top10' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'forums_polls_create' => 1, - 'MaxCollages' => 5, - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '33', - 'Secondary' => 1 - ], - [ - 'ID' => 33, - 'Level' => 330, - 'Name' => 'Security Team', - 'Values' => serialize([ - 'site_send_unlimited_invites' => 1, - 'forums_polls_create' => 1, - 'MaxCollages' => 5, - ]), - 'DisplayStaff' => '1', - 'PermittedForums' => '', - 'Secondary' => 1 - ], - [ - 'ID' => 34, - 'Level' => 340, - 'Name' => 'IRC Team', - 'Values' => serialize([ - 'MaxCollages' => 0 - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '', - 'Secondary' => 1 - ], - [ - 'ID' => 35, - 'Level' => 350, - 'Name' => 'Shadow Team', - 'Values' => serialize([ - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_advanced_top10' => 1, - 'site_can_invite_always' => 1, - 'site_send_unlimited_invites' => 1, - 'site_disable_ip_history' => 1, - 'users_edit_profiles' => 1, - 'users_view_friends' => 1, - 'users_disable_users' => 1, - 'users_disable_posts' => 1, - 'users_disable_any' => 1, - 'users_view_invites' => 1, - 'users_view_email' => 1, - 'users_mod' => 1, - 'admin_advanced_user_search' => 1, - 'MaxCollages' => 0, - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '', - 'Secondary' => 1 - ], - [ - 'ID' => 36, - 'Level' => 360, - 'Name' => 'Alpha Team', - 'Values' => serialize([ - 'admin_reports' => 1, - 'MaxCollages' => 0, - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '', - 'Secondary' => 1 - ], - [ - 'ID' => 37, - 'Level' => 370, - 'Name' => 'Bravo Team', - 'Values' => serialize([ - 'MaxCollages' => 0 - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '', - 'Secondary' => 1 - ], - [ - 'ID' => 38, - 'Level' => 380, - 'Name' => 'Charlie Team', - 'Values' => serialize([ - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_manage' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_moderate_requests' => 1, - 'site_delete_artist' => 1, - 'site_delete_tag' => 1, - 'zip_downloader' => 1, - 'site_tag_aliases_read' => 1, - 'torrents_edit' => 1, - 'torrents_delete' => 1, - 'torrents_add_artist' => 1, - 'edit_unknowns' => 1, - 'torrents_fix_ghosts' => 1, - 'MaxCollages' => 2, - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '31', - 'Secondary' => 1 - ], - [ - 'ID' => 39, - 'Level' => 395, - 'Name' => 'Delta Team', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'site_can_invite_always' => 1, - 'MaxCollages' => 1, - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '35', - 'Secondary' => 1 - ], - [ - 'ID' => 25, - 'Level' => 400, - 'Name' => 'Torrent Master', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_manage' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_advanced_top10' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'forums_polls_create' => 1, - 'site_delete_tag' => 1, - 'zip_downloader' => 1, - 'torrents_edit' => 1, - 'torrents_add_artist' => 1, - 'edit_unknowns' => 1, - 'MaxCollages' => 6, - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '', - 'Secondary' => 0 - ], - [ - 'ID' => 29, - 'Level' => 450, - 'Name' => 'Power TM', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_manage' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_advanced_top10' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'forums_polls_create' => 1, - 'site_delete_tag' => 1, - 'zip_downloader' => 1, - 'torrents_edit' => 1, - 'torrents_add_artist' => 1, - 'edit_unknowns' => 1, - 'MaxCollages' => 5, - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '', - 'Secondary' => 0 - ], - [ - 'ID' => 28, - 'Level' => 500, - 'Name' => 'Elite TM', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_manage' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_advanced_top10' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'site_send_unlimited_invites' => 1, - 'forums_polls_create' => 1, - 'site_delete_tag' => 1, - 'zip_downloader' => 1, - 'torrents_edit' => 1, - 'torrents_add_artist' => 1, - 'edit_unknowns' => 1, - 'MaxCollages' => 6, - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '', - 'Secondary' => 0 - ], - [ - 'ID' => 26, - 'Level' => 601, - 'Name' => 'VIP', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_manage' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_advanced_top10' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'site_send_unlimited_invites' => 1, - 'forums_polls_create' => 1, - 'site_delete_tag' => 1, - 'zip_downloader' => 1, - 'torrents_edit' => 1, - 'torrents_add_artist' => 1, - 'edit_unknowns' => 1, - 'MaxCollages' => 6, - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '', - 'Secondary' => 0 - ], - [ - 'ID' => 27, - 'Level' => 605, - 'Name' => 'Legend', - 'Values' => serialize([ - 'MaxCollages' => 1 - ]), - 'DisplayStaff' => '0', - 'PermittedForums' => '', - 'Secondary' => 0 - ], - [ - 'ID' => 21, - 'Level' => 800, - 'Name' => 'Forum Moderator', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_manage' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_advanced_top10' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'site_send_unlimited_invites' => 1, - 'forums_polls_create' => 1, - 'site_moderate_forums' => 1, - 'site_admin_forums' => 1, - 'site_delete_tag' => 1, - 'site_disable_ip_history' => 1, - 'zip_downloader' => 1, - 'site_proxy_images' => 1, - 'site_search_many' => 1, - 'site_forums_double_post' => 1, - 'project_team' => 1, - 'site_tag_aliases_read' => 1, - 'users_edit_titles' => 1, - 'users_edit_avatars' => 1, - 'users_warn' => 1, - 'users_disable_posts' => 1, - 'users_override_paranoia' => 1, - 'torrents_edit' => 1, - 'torrents_delete' => 1, - 'torrents_add_artist' => 1, - 'edit_unknowns' => 1, - 'torrents_fix_ghosts' => 1, - 'admin_reports' => 1, - 'MaxCollages' => 6, - ]), - 'DisplayStaff' => '1', - 'PermittedForums' => '', - 'Secondary' => 0 - ], - [ - 'ID' => 22, - 'Level' => 850, - 'Name' => 'Torrent Moderator', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_manage' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_advanced_top10' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'site_send_unlimited_invites' => 1, - 'site_delete_artist' => 1, - 'forums_polls_create' => 1, - 'site_moderate_forums' => 1, - 'site_admin_forums' => 1, - 'site_view_torrent_snatchlist' => 1, - 'site_delete_tag' => 1, - 'site_disable_ip_history' => 1, - 'zip_downloader' => 1, - 'site_proxy_images' => 1, - 'site_search_many' => 1, - 'site_forums_double_post' => 1, - 'project_team' => 1, - 'site_tag_aliases_read' => 1, - 'users_edit_avatars' => 1, - 'users_edit_reset_keys' => 1, - 'users_view_friends' => 1, - 'users_warn' => 1, - 'users_disable_users' => 1, - 'users_disable_posts' => 1, - 'users_view_seedleech' => 1, - 'users_view_uploaded' => 1, - 'users_view_keys' => 1, - 'users_view_ips' => 1, - 'users_view_email' => 1, - 'users_invite_notes' => 1, - 'users_override_paranoia' => 1, - 'users_mod' => 1, - 'torrents_edit' => 1, - 'torrents_delete' => 1, - 'torrents_delete_fast' => 1, - 'torrents_search_fast' => 1, - 'torrents_add_artist' => 1, - 'edit_unknowns' => 1, - 'torrents_fix_ghosts' => 1, - 'admin_reports' => 1, - 'admin_advanced_user_search' => 1, - 'admin_clear_cache' => 1, - 'admin_whitelist' => 1, - 'MaxCollages' => 6, - ]), - 'DisplayStaff' => '1', - 'PermittedForums' => '', - 'Secondary' => 0 - ], - [ - 'ID' => 11, - 'Level' => 900, - 'Name' => 'Moderator', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_manage' => 1, - 'site_collages_delete' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_advanced_top10' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'site_send_unlimited_invites' => 1, - 'site_moderate_requests' => 1, - 'site_delete_artist' => 1, - 'forums_polls_create' => 1, - 'site_moderate_forums' => 1, - 'site_admin_forums' => 1, - 'site_view_torrent_snatchlist' => 1, - 'site_delete_tag' => 1, - 'site_disable_ip_history' => 1, - 'zip_downloader' => 1, - 'site_proxy_images' => 1, - 'site_search_many' => 1, - 'site_forums_double_post' => 1, - 'project_team' => 1, - 'site_tag_aliases_read' => 1, - 'users_edit_titles' => 1, - 'users_edit_avatars' => 1, - 'users_edit_invites' => 1, - 'users_edit_reset_keys' => 1, - 'users_view_friends' => 1, - 'users_warn' => 1, - 'users_disable_users' => 1, - 'users_disable_posts' => 1, - 'users_disable_any' => 1, - 'users_view_invites' => 1, - 'users_view_seedleech' => 1, - 'users_view_uploaded' => 1, - 'users_view_keys' => 1, - 'users_view_ips' => 1, - 'users_view_email' => 1, - 'users_invite_notes' => 1, - 'users_override_paranoia' => 1, - 'users_logout' => 1, - 'users_mod' => 1, - 'torrents_edit' => 1, - 'torrents_delete' => 1, - 'torrents_delete_fast' => 1, - 'torrents_freeleech' => 1, - 'torrents_search_fast' => 1, - 'torrents_add_artist' => 1, - 'edit_unknowns' => 1, - 'torrents_fix_ghosts' => 1, - 'admin_manage_fls' => 1, - 'admin_reports' => 1, - 'admin_advanced_user_search' => 1, - 'admin_clear_cache' => 1, - 'admin_whitelist' => 1, - 'MaxCollages' => 6, - ]), - 'DisplayStaff' => '1', - 'PermittedForums' => '', - 'Secondary' => 0 - ], - [ - 'ID' => 24, - 'Level' => 950, - 'Name' => 'Developer', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_top10' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_advanced_top10' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'site_can_invite_always' => 1, - 'site_send_unlimited_invites' => 1, - 'forums_polls_create' => 1, - 'site_view_flow' => 1, - 'site_view_full_log' => 1, - 'site_view_torrent_snatchlist' => 1, - 'site_recommend_own' => 1, - 'site_manage_recommendations' => 1, - 'site_delete_tag' => 1, - 'zip_downloader' => 1, - 'site_forums_double_post' => 1, - 'MaxCollages' => 1, - ]), - 'DisplayStaff' => '1', - 'PermittedForums' => '35', - 'Secondary' => 0 - ], - [ - 'ID' => 40, - 'Level' => 980, - 'Name' => 'Administrator', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_manage' => 1, - 'site_collages_delete' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_advanced_top10' => 1, - 'site_album_votes' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'site_can_invite_always' => 1, - 'site_send_unlimited_invites' => 1, - 'site_moderate_requests' => 1, - 'site_delete_artist' => 1, - 'forums_polls_create' => 1, - 'forums_polls_moderate' => 1, - 'site_moderate_forums' => 1, - 'site_admin_forums' => 1, - 'site_view_flow' => 1, - 'site_view_full_log' => 1, - 'site_view_torrent_snatchlist' => 1, - 'site_recommend_own' => 1, - 'site_manage_recommendations' => 1, - 'site_delete_tag' => 1, - 'site_disable_ip_history' => 1, - 'zip_downloader' => 1, - 'site_proxy_images' => 1, - 'site_search_many' => 1, - 'site_collages_recover' => 1, - 'site_forums_double_post' => 1, - 'project_team' => 1, - 'site_tag_aliases_read' => 1, - 'users_edit_ratio' => 1, - 'users_edit_titles' => 1, - 'users_edit_avatars' => 1, - 'users_edit_invites' => 1, - 'users_edit_watch_hours' => 1, - 'users_edit_reset_keys' => 1, - 'users_edit_profiles' => 1, - 'users_view_friends' => 1, - 'users_reset_own_keys' => 1, - 'users_edit_password' => 1, - 'users_promote_below' => 1, - 'users_warn' => 1, - 'users_disable_users' => 1, - 'users_disable_posts' => 1, - 'users_disable_any' => 1, - 'users_delete_users' => 1, - 'users_view_invites' => 1, - 'users_view_seedleech' => 1, - 'users_view_uploaded' => 1, - 'users_view_keys' => 1, - 'users_view_ips' => 1, - 'users_view_email' => 1, - 'users_invite_notes' => 1, - 'users_override_paranoia' => 1, - 'users_logout' => 1, - 'users_mod' => 1, - 'torrents_edit' => 1, - 'torrents_delete' => 1, - 'torrents_delete_fast' => 1, - 'torrents_freeleech' => 1, - 'torrents_search_fast' => 1, - 'torrents_add_artist' => 1, - 'edit_unknowns' => 1, - 'torrents_edit_vanityhouse' => 1, - 'artist_edit_vanityhouse' => 1, - 'torrents_fix_ghosts' => 1, - 'admin_manage_blog' => 1, - 'admin_manage_fls' => 1, - 'admin_reports' => 1, - 'admin_advanced_user_search' => 1, - 'admin_manage_ipbans' => 1, - 'admin_dnu' => 1, - 'admin_clear_cache' => 1, - 'admin_whitelist' => 1, - 'admin_manage_wiki' => 1, - 'MaxCollages' => 5, - ]), - 'DisplayStaff' => '1', - 'PermittedForums' => '', - 'Secondary' => 0 - ], - [ - 'ID' => 15, - 'Level' => 1000, - 'Name' => 'Sysop', - 'Values' => serialize([ - 'site_leech' => 1, - 'site_upload' => 1, - 'site_vote' => 1, - 'site_submit_requests' => 1, - 'site_advanced_search' => 1, - 'site_top10' => 1, - 'site_advanced_top10' => 1, - 'site_album_votes' => 1, - 'site_torrents_notify' => 1, - 'site_collages_create' => 1, - 'site_collages_manage' => 1, - 'site_collages_delete' => 1, - 'site_collages_subscribe' => 1, - 'site_collages_personal' => 1, - 'site_collages_renamepersonal' => 1, - 'site_make_bookmarks' => 1, - 'site_edit_wiki' => 1, - 'site_can_invite_always' => 1, - 'site_send_unlimited_invites' => 1, - 'site_moderate_requests' => 1, - 'site_delete_artist' => 1, - 'site_moderate_forums' => 1, - 'site_admin_forums' => 1, - 'site_forums_double_post' => 1, - 'site_view_flow' => 1, - 'site_view_full_log' => 1, - 'site_view_torrent_snatchlist' => 1, - 'site_recommend_own' => 1, - 'site_manage_recommendations' => 1, - 'site_delete_tag' => 1, - 'site_disable_ip_history' => 1, - 'zip_downloader' => 1, - 'site_debug' => 1, - 'site_proxy_images' => 1, - 'site_search_many' => 1, - 'users_edit_usernames' => 1, - 'users_edit_ratio' => 1, - 'users_edit_own_ratio' => 1, - 'users_edit_titles' => 1, - 'users_edit_avatars' => 1, - 'users_edit_invites' => 1, - 'users_edit_watch_hours' => 1, - 'users_edit_reset_keys' => 1, - 'users_edit_profiles' => 1, - 'users_view_friends' => 1, - 'users_reset_own_keys' => 1, - 'users_edit_password' => 1, - 'users_promote_below' => 1, - 'users_promote_to' => 1, - 'users_give_donor' => 1, - 'users_warn' => 1, - 'users_disable_users' => 1, - 'users_disable_posts' => 1, - 'users_disable_any' => 1, - 'users_delete_users' => 1, - 'users_view_invites' => 1, - 'users_view_seedleech' => 1, - 'users_view_uploaded' => 1, - 'users_view_keys' => 1, - 'users_view_ips' => 1, - 'users_view_email' => 1, - 'users_invite_notes' => 1, - 'users_override_paranoia' => 1, - 'users_logout' => 1, - 'users_make_invisible' => 1, - 'users_mod' => 1, - 'torrents_edit' => 1, - 'torrents_delete' => 1, - 'torrents_delete_fast' => 1, - 'torrents_freeleech' => 1, - 'torrents_search_fast' => 1, - 'torrents_hide_dnu' => 1, - 'torrents_fix_ghosts' => 1, - 'admin_manage_news' => 1, - 'admin_manage_blog' => 1, - 'admin_manage_polls' => 1, - 'admin_manage_forums' => 1, - 'admin_manage_fls' => 1, - 'admin_reports' => 1, - 'admin_advanced_user_search' => 1, - 'admin_create_users' => 1, - 'admin_donor_log' => 1, - 'admin_manage_ipbans' => 1, - 'admin_dnu' => 1, - 'admin_clear_cache' => 1, - 'admin_whitelist' => 1, - 'admin_manage_permissions' => 1, - 'admin_schedule' => 1, - 'admin_login_watch' => 1, - 'admin_manage_wiki' => 1, - 'admin_update_geoip' => 1, - 'site_collages_recover' => 1, - 'torrents_add_artist' => 1, - 'edit_unknowns' => 1, - 'forums_polls_create' => 1, - 'forums_polls_moderate' => 1, - 'project_team' => 1, - 'torrents_edit_vanityhouse' => 1, - 'artist_edit_vanityhouse' => 1, - 'site_tag_aliases_read' => 1, - ]), - 'DisplayStaff' => '1', - 'PermittedForums' => '', - 'Secondary' => 0 - ], - ]); - - $this->insert('wiki_articles', [['Title' => 'Wiki', 'Body' => 'Welcome to your new wiki! Hope this works.', 'MinClassRead' => 100, 'MinClassEdit' => 475, 'Date' => 'NOW()', 'Author' => 1]]); - $this->insert('wiki_aliases', [['Alias' => 'wiki', 'UserID' => 1, 'ArticleID' => 1]]); - $this->insert('wiki_revisions', [['ID' => 1, 'Revision' => 1, 'Title' => 'Wiki', 'Body' => 'Welcome to your new wiki! Hope this works.', 'Date' => 'NOW()', 'Author' => 1]]); - $this->insert('tags', [ - ['Name' => 'rock', 'TagType' => 'genre', 'Uses' => 0, 'UserID' => 1], - ['Name' => 'pop', 'TagType' => 'genre', 'Uses' => 0, 'UserID' => 1], - ['Name' => 'female.fronted.symphonic.death.metal', 'TagType' => 'genre', 'Uses' => 0, 'UserID' => 1] - ]); - - $this->insert('stylesheets', [ - ['Name' => 'Layer cake', 'Description' => 'Grey stylesheet by Emm'], - ['Name' => 'Proton', 'Description' => 'Proton by Protiek'], - ['Name' => 'postmod', 'Description' => 'Upgrade by anorex'], - ['Name' => 'Hydro', 'Description' => 'Hydro'], - ['Name' => 'Kuro', 'Description' => 'Kuro'], - ['Name' => 'Anorex', 'Description' => 'Anorex'], - ['Name' => 'Mono', 'Description' => 'Mono'], - ['Name' => 'Shiro', 'Description' => 'Shiro'], - ['Name' => 'Minimal', 'Description' => 'Minimal'], - ['Name' => 'Whatlove', 'Description' => 'Whatlove'], - ['Name' => 'White.cd', 'Description' => 'White.cd'], - ['Name' => 'GTFO Spaceship', 'Description' => 'gtfo spaceship'], - ['Name' => 'Dark Ambient', 'Description' => 'dark ambient'], - ['Name' => 'Xanax cake', 'Description' => 'Xanax cake'], - ['Name' => 'Haze', 'Description' => 'Haze by Exanurous & apopagasm'], - ['Name' => 'Post Office', 'Description' => 'Post Office by dannymichel'], - ['Name' => 'LinoHaze', 'Description' => 'LinoHaze by linotype'], - ['Name' => 'ApolloStage', 'Description' => 'ApolloStage by burtoo', 'Default' => '1'], - ['Name' => 'ApolloStage Coffee', 'Description' => 'ApolloStage by burtoo'], - ['Name' => 'ApolloStage Sunset', 'Description' => 'ApolloStage Sunset by burtoo'], - ['Name' => 'Apollo Mat', 'Description' => 'Apollo Mat by salem'] - ]); - - $this->insert('schedule', [['NextHour' => 0, 'NextDay' => 0, 'NextBiWeekly' => 0]]); - - $this->execute("SET FOREIGN_KEY_CHECKS = 1;"); - } + $this->table('contest_type')->insert([ + ['Name' => 'upload_flac'], ['Name' => 'request_fill'] + ])->save(); + + $this->table('forums_categories')->insert([ + ['ID' => 1, 'Name' => 'Site', 'Sort' => 1], + ['ID' => 21, 'Name' => 'Suggestions', 'Sort' => 3], + ['ID' => 5, 'Name' => 'Community', 'Sort' => 5], + ['ID' => 8, 'Name' => 'Music', 'Sort' => 8], + ['ID' => 10, 'Name' => 'Help', 'Sort' => 10], + ['ID' => 20, 'Name' => 'Trash', 'Sort' => 20] + ])->save(); + + $this->table('forums')->insert([ + ['ID' => 7, 'CategoryID' => 1, 'Sort' => 100, 'Name' => 'Pharmacy', 'Description' => 'Get your medication dispensed here', 'MinClassRead' => 1000, 'MinClassWrite' => 1000, 'MinClassCreate' => 1000], + ['ID' => 5, 'CategoryID' => 1, 'Sort' => 200, 'Name' => 'Staff', 'Description' => 'No place like home', 'MinClassRead' => 800, 'MinClassWrite' => 800, 'MinClassCreate' => 800], + ['ID' => 35, 'CategoryID' => 1, 'Sort' => 250, 'Name' => 'Developers', 'Description' => 'Developers forum', 'MinClassRead' => 800, 'MinClassWrite' => 800, 'MinClassCreate' => 800], + ['ID' => 33, 'CategoryID' => 1, 'Sort' => 750, 'Name' => 'Designers', 'Description' => 'Designers', 'MinClassRead' => 800, 'MinClassWrite' => 800, 'MinClassCreate' => 800], + ['ID' => 28, 'CategoryID' => 1, 'Sort' => 800, 'Name' => 'First Line Support', 'Description' => 'Special Support Operations Command (SSOC)', 'MinClassRead' => 900, 'MinClassWrite' => 900, 'MinClassCreate' => 900], + ['ID' => 30, 'CategoryID' => 1, 'Sort' => 900, 'Name' => 'Interviewers', 'Description' => 'The Candidates', 'MinClassRead' => 900, 'MinClassWrite' => 900, 'MinClassCreate' => 900], + + ['ID' => 31, 'CategoryID' => 1, 'Sort' => 1000, 'Name' => 'Charlie Team', 'Description' => 'Quality Assurance', 'MinClassRead' => 850, 'MinClassWrite' => 850, 'MinClassCreate' => 850], + ['ID' => 1, 'CategoryID' => 1, 'Sort' => 300, 'Name' => 'APOLLO', 'Description' => 'apollo.rip', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 12, 'CategoryID' => 1, 'Sort' => 600, 'Name' => 'Announcements', 'Description' => 'Public service announcements', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 800], + ['ID' => 6, 'CategoryID' => 1, 'Sort' => 400, 'Name' => 'Bugs', 'Description' => 'I found a non critical bug', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 24, 'CategoryID' => 5, 'Sort' => 2000, 'Name' => 'Projects', 'Description' => 'I\'m working on a project', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + + ['ID' => 13, 'CategoryID' => 21, 'Sort' => 2990, 'Name' => 'Suggestions', 'Description' => 'I have an idea', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 36, 'CategoryID' => 21, 'Sort' => 3000, 'Name' => 'Approved', 'Description' => 'Self explanatory...', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 800], + ['ID' => 37, 'CategoryID' => 21, 'Sort' => 3400, 'Name' => 'Implemented', 'Description' => 'The Suggestion I made has been implemented', 'MinClassRead' => 100, 'MinClassWrite' => 800, 'MinClassCreate' => 800], + ['ID' => 15, 'CategoryID' => 21, 'Sort' => 3500, 'Name' => 'Denied', 'Description' => 'The Suggestion I made has been denied', 'MinClassRead' => 100, 'MinClassWrite' => 800, 'MinClassCreate' => 800], + + ['ID' => 2, 'CategoryID' => 5, 'Sort' => 1200, 'Name' => 'Chat', 'Description' => 'General chat', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 25, 'CategoryID' => 5, 'Sort' => 2100, 'Name' => 'Games', 'Description' => 'I\'m a gamer', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 27, 'CategoryID' => 5, 'Sort' => 1100, 'Name' => 'Serious Discussions', 'Description' => 'The Library', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 29, 'CategoryID' => 5, 'Sort' => 1300, 'Name' => 'Power User', 'Description' => 'PU Forum <3', 'MinClassRead' => 200, 'MinClassWrite' => 200, 'MinClassCreate' => 200], + ['ID' => 11, 'CategoryID' => 5, 'Sort' => 1600, 'Name' => 'Elites', 'Description' => 'I\'m 1337', 'MinClassRead' => 250, 'MinClassWrite' => 250, 'MinClassCreate' => 250], + ['ID' => 40, 'CategoryID' => 5, 'Sort' => 1610, 'Name' => 'Torrent Masters', 'Description' => 'The Holy Grail', 'MinClassRead' => 400, 'MinClassWrite' => 400, 'MinClassCreate' => 400], + ['ID' => 38, 'CategoryID' => 5, 'Sort' => 1650, 'Name' => 'VIP', 'Description' => 'Very Important Phorum', 'MinClassRead' => 601, 'MinClassWrite' => 601, 'MinClassCreate' => 601], + ['ID' => 10, 'CategoryID' => 5, 'Sort' => 1500, 'Name' => 'Donors', 'Description' => 'I have a heart', 'MinClassRead' => 800, 'MinClassWrite' => 800, 'MinClassCreate' => 800], + ['ID' => 39, 'CategoryID' => 5, 'Sort' => 1670, 'Name' => 'Invitations', 'Description' => 'Stairway to Heaven', 'MinClassRead' => 250, 'MinClassWrite' => 250, 'MinClassCreate' => 250], + ['ID' => 22, 'CategoryID' => 5, 'Sort' => 1800, 'Name' => 'Comics', 'Description' => 'I read comics', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 23, 'CategoryID' => 5, 'Sort' => 1900, 'Name' => 'Technology', 'Description' => 'I like technology', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + + ['ID' => 8, 'CategoryID' => 8, 'Sort' => 30, 'Name' => 'Music', 'Description' => 'For the masses', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 18, 'CategoryID' => 8, 'Sort' => 31, 'Name' => 'Vanity House', 'Description' => 'I have some of my work to share', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 32, 'CategoryID' => 8, 'Sort' => 20, 'Name' => 'Audiophile', 'Description' => 'For the audiophile', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 19, 'CategoryID' => 8, 'Sort' => 32, 'Name' => 'The Studio', 'Description' => 'I\'m a DJ', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 26, 'CategoryID' => 8, 'Sort' => 34, 'Name' => 'Vinyl', 'Description' => 'Vinyl \'s are here to stay', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 20, 'CategoryID' => 8, 'Sort' => 33, 'Name' => 'Offered', 'Description' => 'I have some music to offer', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 9, 'CategoryID' => 5, 'Sort' => 1400, 'Name' => 'Artists', 'Description' => 'For the artistics', 'MinClassRead' => 800, 'MinClassWrite' => 800, 'MinClassCreate' => 800], + ['ID' => 21, 'CategoryID' => 5, 'Sort' => 1700, 'Name' => 'Concerts and Events', 'Description' => 'I\'m off to a gig', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + + ['ID' => 3, 'CategoryID' => 10, 'Sort' => 40, 'Name' => 'Help!', 'Description' => 'I fell down and I cant get up', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 34, 'CategoryID' => 10, 'Sort' => 41, 'Name' => 'Editing', 'Description' => 'Quality Control', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 16, 'CategoryID' => 10, 'Sort' => 42, 'Name' => 'Tutorials', 'Description' => 'I would like to share my knowledge', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + ['ID' => 17, 'CategoryID' => 10, 'Sort' => 43, 'Name' => 'BitTorrent', 'Description' => 'I need to talk about BitTorrent', 'MinClassRead' => 100, 'MinClassWrite' => 100, 'MinClassCreate' => 100], + + ['ID' => 4, 'CategoryID' => 20, 'Sort' => 5, 'Name' => 'Trash', 'Description' => 'Every thread ends up here eventually', 'MinClassRead' => 100, 'MinClassWrite' => 800, 'MinClassCreate' => 800], + ['ID' => 14, 'CategoryID' => 20, 'Sort' => 101, 'Name' => 'Resolved Bugs', 'Description' => 'The bug I reported has been fixed', 'MinClassRead' => 100, 'MinClassWrite' => 800, 'MinClassCreate' => 800], + ])->save(); + + $this->table('permissions')->insert([ + [ + 'ID' => 2, + 'Level' => 100, + 'Name' => 'User', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_album_votes' => 1, + 'site_edit_wiki' => 1, + 'torrents_add_artist' => 1, + 'MaxCollages' => 0 + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '', + 'Secondary' => 0 + ], + [ + 'ID' => 3, + 'Level' => 150, + 'Name' => 'Member', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_collages_manage' => 1, + 'site_collages_subscribe' => 1, + 'site_advanced_top10' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'zip_downloader' => 1, + 'torrents_add_artist' => 1, + 'edit_unknowns' => 1, + 'MaxCollages' => 0 + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '', + 'Secondary' => 0 + ], + [ + 'ID' => 4, + 'Level' => 200, + 'Name' => 'Power User', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_manage' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'forums_polls_create' => 1, + 'zip_downloader' => 1, + 'torrents_add_artist' => 1, + 'MaxCollages' => 1, + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '', + 'Secondary' => 0 + ], + [ + 'ID' => 5, + 'Level' => 250, + 'Name' => 'Elite', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_manage' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_advanced_top10' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'forums_polls_create' => 1, + 'site_delete_tag' => 1, + 'zip_downloader' => 1, + 'torrents_edit' => 1, + 'torrents_add_artist' => 1, + 'edit_unknowns' => 1, + 'MaxCollages' => 3, + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '', + 'Secondary' => 0 + ], + [ + 'ID' => 19, + 'Level' => 201, + 'Name' => 'Artist', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'site_recommend_own' => 1, + 'MaxCollages' => 0, + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '9', + 'Secondary' => 0 + ], + [ + 'ID' => 20, + 'Level' => 202, + 'Name' => 'Donor', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_manage' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'forums_polls_create' => 1, + 'zip_downloader' => 1, + 'MaxCollages' => 1, + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '10', + 'Secondary' => 0 + ], + [ + 'ID' => 42, + 'Level' => 205, + 'Name' => 'Donor', + 'Values' => serialize([ + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_top10' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_manage' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'forums_polls_create' => 1, + 'zip_downloader' => 1, + 'MaxCollages' => 1, + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '10', + 'Secondary' => 1 + ], + [ + 'ID' => 23, + 'Level' => 255, + 'Name' => 'First Line Support', + 'Values' => serialize([ + 'site_collages_personal' => 1, + 'site_advanced_top10' => 1, + 'MaxCollages' => 1, + ]), + 'DisplayStaff' => '1', + 'PermittedForums' => '28', + 'Secondary' => 1 + ], + [ + 'ID' => 41, + 'Level' => 257, + 'Name' => 'Recruiter', + 'Values' => serialize([ + 'site_send_unlimited_invites' => 1, + 'MaxCollages' => 0, + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '', + 'Secondary' => 1 + ], + [ + 'ID' => 30, + 'Level' => 300, + 'Name' => 'Interviewer', + 'Values' => serialize([ + 'MaxCollages' => 0 + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '30', + 'Secondary' => 1 + ], + [ + 'ID' => 31, + 'Level' => 310, + 'Name' => 'Torrent Celebrity', + 'Values' => serialize([ + 'MaxCollages' => 0 + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '', + 'Secondary' => 1 + ], + [ + 'ID' => 32, + 'Level' => 320, + 'Name' => 'Designer', + 'Values' => serialize([ + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_collages_create' => 1, + 'site_collages_manage' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_advanced_top10' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'forums_polls_create' => 1, + 'MaxCollages' => 5, + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '33', + 'Secondary' => 1 + ], + [ + 'ID' => 33, + 'Level' => 330, + 'Name' => 'Security Team', + 'Values' => serialize([ + 'site_send_unlimited_invites' => 1, + 'forums_polls_create' => 1, + 'MaxCollages' => 5, + ]), + 'DisplayStaff' => '1', + 'PermittedForums' => '', + 'Secondary' => 1 + ], + [ + 'ID' => 34, + 'Level' => 340, + 'Name' => 'IRC Team', + 'Values' => serialize([ + 'MaxCollages' => 0 + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '', + 'Secondary' => 1 + ], + [ + 'ID' => 35, + 'Level' => 350, + 'Name' => 'Shadow Team', + 'Values' => serialize([ + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_advanced_top10' => 1, + 'site_can_invite_always' => 1, + 'site_send_unlimited_invites' => 1, + 'site_disable_ip_history' => 1, + 'users_edit_profiles' => 1, + 'users_view_friends' => 1, + 'users_disable_users' => 1, + 'users_disable_posts' => 1, + 'users_disable_any' => 1, + 'users_view_invites' => 1, + 'users_view_email' => 1, + 'users_mod' => 1, + 'admin_advanced_user_search' => 1, + 'MaxCollages' => 0, + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '', + 'Secondary' => 1 + ], + [ + 'ID' => 36, + 'Level' => 360, + 'Name' => 'Alpha Team', + 'Values' => serialize([ + 'admin_reports' => 1, + 'MaxCollages' => 0, + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '', + 'Secondary' => 1 + ], + [ + 'ID' => 37, + 'Level' => 370, + 'Name' => 'Bravo Team', + 'Values' => serialize([ + 'MaxCollages' => 0 + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '', + 'Secondary' => 1 + ], + [ + 'ID' => 38, + 'Level' => 380, + 'Name' => 'Charlie Team', + 'Values' => serialize([ + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_manage' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_moderate_requests' => 1, + 'site_delete_artist' => 1, + 'site_delete_tag' => 1, + 'zip_downloader' => 1, + 'site_tag_aliases_read' => 1, + 'torrents_edit' => 1, + 'torrents_delete' => 1, + 'torrents_add_artist' => 1, + 'edit_unknowns' => 1, + 'torrents_fix_ghosts' => 1, + 'MaxCollages' => 2, + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '31', + 'Secondary' => 1 + ], + [ + 'ID' => 39, + 'Level' => 395, + 'Name' => 'Delta Team', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'site_can_invite_always' => 1, + 'MaxCollages' => 1, + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '35', + 'Secondary' => 1 + ], + [ + 'ID' => 25, + 'Level' => 400, + 'Name' => 'Torrent Master', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_manage' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_advanced_top10' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'forums_polls_create' => 1, + 'site_delete_tag' => 1, + 'zip_downloader' => 1, + 'torrents_edit' => 1, + 'torrents_add_artist' => 1, + 'edit_unknowns' => 1, + 'MaxCollages' => 6, + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '', + 'Secondary' => 0 + ], + [ + 'ID' => 29, + 'Level' => 450, + 'Name' => 'Power TM', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_manage' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_advanced_top10' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'forums_polls_create' => 1, + 'site_delete_tag' => 1, + 'zip_downloader' => 1, + 'torrents_edit' => 1, + 'torrents_add_artist' => 1, + 'edit_unknowns' => 1, + 'MaxCollages' => 5, + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '', + 'Secondary' => 0 + ], + [ + 'ID' => 28, + 'Level' => 500, + 'Name' => 'Elite TM', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_manage' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_advanced_top10' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'site_send_unlimited_invites' => 1, + 'forums_polls_create' => 1, + 'site_delete_tag' => 1, + 'zip_downloader' => 1, + 'torrents_edit' => 1, + 'torrents_add_artist' => 1, + 'edit_unknowns' => 1, + 'MaxCollages' => 6, + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '', + 'Secondary' => 0 + ], + [ + 'ID' => 26, + 'Level' => 601, + 'Name' => 'VIP', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_manage' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_advanced_top10' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'site_send_unlimited_invites' => 1, + 'forums_polls_create' => 1, + 'site_delete_tag' => 1, + 'zip_downloader' => 1, + 'torrents_edit' => 1, + 'torrents_add_artist' => 1, + 'edit_unknowns' => 1, + 'MaxCollages' => 6, + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '', + 'Secondary' => 0 + ], + [ + 'ID' => 27, + 'Level' => 605, + 'Name' => 'Legend', + 'Values' => serialize([ + 'MaxCollages' => 1 + ]), + 'DisplayStaff' => '0', + 'PermittedForums' => '', + 'Secondary' => 0 + ], + [ + 'ID' => 21, + 'Level' => 800, + 'Name' => 'Forum Moderator', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_manage' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_advanced_top10' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'site_send_unlimited_invites' => 1, + 'forums_polls_create' => 1, + 'site_moderate_forums' => 1, + 'site_admin_forums' => 1, + 'site_delete_tag' => 1, + 'site_disable_ip_history' => 1, + 'zip_downloader' => 1, + 'site_proxy_images' => 1, + 'site_search_many' => 1, + 'site_forums_double_post' => 1, + 'project_team' => 1, + 'site_tag_aliases_read' => 1, + 'users_edit_titles' => 1, + 'users_edit_avatars' => 1, + 'users_warn' => 1, + 'users_disable_posts' => 1, + 'users_override_paranoia' => 1, + 'torrents_edit' => 1, + 'torrents_delete' => 1, + 'torrents_add_artist' => 1, + 'edit_unknowns' => 1, + 'torrents_fix_ghosts' => 1, + 'admin_reports' => 1, + 'MaxCollages' => 6, + ]), + 'DisplayStaff' => '1', + 'PermittedForums' => '', + 'Secondary' => 0 + ], + [ + 'ID' => 22, + 'Level' => 850, + 'Name' => 'Torrent Moderator', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_manage' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_advanced_top10' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'site_send_unlimited_invites' => 1, + 'site_delete_artist' => 1, + 'forums_polls_create' => 1, + 'site_moderate_forums' => 1, + 'site_admin_forums' => 1, + 'site_view_torrent_snatchlist' => 1, + 'site_delete_tag' => 1, + 'site_disable_ip_history' => 1, + 'zip_downloader' => 1, + 'site_proxy_images' => 1, + 'site_search_many' => 1, + 'site_forums_double_post' => 1, + 'project_team' => 1, + 'site_tag_aliases_read' => 1, + 'users_edit_avatars' => 1, + 'users_edit_reset_keys' => 1, + 'users_view_friends' => 1, + 'users_warn' => 1, + 'users_disable_users' => 1, + 'users_disable_posts' => 1, + 'users_view_seedleech' => 1, + 'users_view_uploaded' => 1, + 'users_view_keys' => 1, + 'users_view_ips' => 1, + 'users_view_email' => 1, + 'users_invite_notes' => 1, + 'users_override_paranoia' => 1, + 'users_mod' => 1, + 'torrents_edit' => 1, + 'torrents_delete' => 1, + 'torrents_delete_fast' => 1, + 'torrents_search_fast' => 1, + 'torrents_add_artist' => 1, + 'edit_unknowns' => 1, + 'torrents_fix_ghosts' => 1, + 'admin_reports' => 1, + 'admin_advanced_user_search' => 1, + 'admin_clear_cache' => 1, + 'admin_whitelist' => 1, + 'MaxCollages' => 6, + ]), + 'DisplayStaff' => '1', + 'PermittedForums' => '', + 'Secondary' => 0 + ], + [ + 'ID' => 11, + 'Level' => 900, + 'Name' => 'Moderator', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_manage' => 1, + 'site_collages_delete' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_advanced_top10' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'site_send_unlimited_invites' => 1, + 'site_moderate_requests' => 1, + 'site_delete_artist' => 1, + 'forums_polls_create' => 1, + 'site_moderate_forums' => 1, + 'site_admin_forums' => 1, + 'site_view_torrent_snatchlist' => 1, + 'site_delete_tag' => 1, + 'site_disable_ip_history' => 1, + 'zip_downloader' => 1, + 'site_proxy_images' => 1, + 'site_search_many' => 1, + 'site_forums_double_post' => 1, + 'project_team' => 1, + 'site_tag_aliases_read' => 1, + 'users_edit_titles' => 1, + 'users_edit_avatars' => 1, + 'users_edit_invites' => 1, + 'users_edit_reset_keys' => 1, + 'users_view_friends' => 1, + 'users_warn' => 1, + 'users_disable_users' => 1, + 'users_disable_posts' => 1, + 'users_disable_any' => 1, + 'users_view_invites' => 1, + 'users_view_seedleech' => 1, + 'users_view_uploaded' => 1, + 'users_view_keys' => 1, + 'users_view_ips' => 1, + 'users_view_email' => 1, + 'users_invite_notes' => 1, + 'users_override_paranoia' => 1, + 'users_logout' => 1, + 'users_mod' => 1, + 'torrents_edit' => 1, + 'torrents_delete' => 1, + 'torrents_delete_fast' => 1, + 'torrents_freeleech' => 1, + 'torrents_search_fast' => 1, + 'torrents_add_artist' => 1, + 'edit_unknowns' => 1, + 'torrents_fix_ghosts' => 1, + 'admin_manage_fls' => 1, + 'admin_reports' => 1, + 'admin_advanced_user_search' => 1, + 'admin_clear_cache' => 1, + 'admin_whitelist' => 1, + 'MaxCollages' => 6, + ]), + 'DisplayStaff' => '1', + 'PermittedForums' => '', + 'Secondary' => 0 + ], + [ + 'ID' => 24, + 'Level' => 950, + 'Name' => 'Developer', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_top10' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_advanced_top10' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'site_can_invite_always' => 1, + 'site_send_unlimited_invites' => 1, + 'forums_polls_create' => 1, + 'site_view_flow' => 1, + 'site_view_full_log' => 1, + 'site_view_torrent_snatchlist' => 1, + 'site_recommend_own' => 1, + 'site_manage_recommendations' => 1, + 'site_delete_tag' => 1, + 'zip_downloader' => 1, + 'site_forums_double_post' => 1, + 'MaxCollages' => 1, + ]), + 'DisplayStaff' => '1', + 'PermittedForums' => '35', + 'Secondary' => 0 + ], + [ + 'ID' => 40, + 'Level' => 980, + 'Name' => 'Administrator', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_manage' => 1, + 'site_collages_delete' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_advanced_top10' => 1, + 'site_album_votes' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'site_can_invite_always' => 1, + 'site_send_unlimited_invites' => 1, + 'site_moderate_requests' => 1, + 'site_delete_artist' => 1, + 'forums_polls_create' => 1, + 'forums_polls_moderate' => 1, + 'site_moderate_forums' => 1, + 'site_admin_forums' => 1, + 'site_view_flow' => 1, + 'site_view_full_log' => 1, + 'site_view_torrent_snatchlist' => 1, + 'site_recommend_own' => 1, + 'site_manage_recommendations' => 1, + 'site_delete_tag' => 1, + 'site_disable_ip_history' => 1, + 'zip_downloader' => 1, + 'site_proxy_images' => 1, + 'site_search_many' => 1, + 'site_collages_recover' => 1, + 'site_forums_double_post' => 1, + 'project_team' => 1, + 'site_tag_aliases_read' => 1, + 'users_edit_ratio' => 1, + 'users_edit_titles' => 1, + 'users_edit_avatars' => 1, + 'users_edit_invites' => 1, + 'users_edit_watch_hours' => 1, + 'users_edit_reset_keys' => 1, + 'users_edit_profiles' => 1, + 'users_view_friends' => 1, + 'users_reset_own_keys' => 1, + 'users_edit_password' => 1, + 'users_promote_below' => 1, + 'users_warn' => 1, + 'users_disable_users' => 1, + 'users_disable_posts' => 1, + 'users_disable_any' => 1, + 'users_delete_users' => 1, + 'users_view_invites' => 1, + 'users_view_seedleech' => 1, + 'users_view_uploaded' => 1, + 'users_view_keys' => 1, + 'users_view_ips' => 1, + 'users_view_email' => 1, + 'users_invite_notes' => 1, + 'users_override_paranoia' => 1, + 'users_logout' => 1, + 'users_mod' => 1, + 'torrents_edit' => 1, + 'torrents_delete' => 1, + 'torrents_delete_fast' => 1, + 'torrents_freeleech' => 1, + 'torrents_search_fast' => 1, + 'torrents_add_artist' => 1, + 'edit_unknowns' => 1, + 'torrents_edit_vanityhouse' => 1, + 'artist_edit_vanityhouse' => 1, + 'torrents_fix_ghosts' => 1, + 'admin_manage_blog' => 1, + 'admin_manage_fls' => 1, + 'admin_reports' => 1, + 'admin_advanced_user_search' => 1, + 'admin_manage_ipbans' => 1, + 'admin_dnu' => 1, + 'admin_clear_cache' => 1, + 'admin_whitelist' => 1, + 'admin_manage_wiki' => 1, + 'MaxCollages' => 5, + ]), + 'DisplayStaff' => '1', + 'PermittedForums' => '', + 'Secondary' => 0 + ], + [ + 'ID' => 15, + 'Level' => 1000, + 'Name' => 'Sysop', + 'Values' => serialize([ + 'site_leech' => 1, + 'site_upload' => 1, + 'site_vote' => 1, + 'site_submit_requests' => 1, + 'site_advanced_search' => 1, + 'site_top10' => 1, + 'site_advanced_top10' => 1, + 'site_album_votes' => 1, + 'site_torrents_notify' => 1, + 'site_collages_create' => 1, + 'site_collages_manage' => 1, + 'site_collages_delete' => 1, + 'site_collages_subscribe' => 1, + 'site_collages_personal' => 1, + 'site_collages_renamepersonal' => 1, + 'site_make_bookmarks' => 1, + 'site_edit_wiki' => 1, + 'site_can_invite_always' => 1, + 'site_send_unlimited_invites' => 1, + 'site_moderate_requests' => 1, + 'site_delete_artist' => 1, + 'site_moderate_forums' => 1, + 'site_admin_forums' => 1, + 'site_forums_double_post' => 1, + 'site_view_flow' => 1, + 'site_view_full_log' => 1, + 'site_view_torrent_snatchlist' => 1, + 'site_recommend_own' => 1, + 'site_manage_recommendations' => 1, + 'site_delete_tag' => 1, + 'site_disable_ip_history' => 1, + 'zip_downloader' => 1, + 'site_debug' => 1, + 'site_proxy_images' => 1, + 'site_search_many' => 1, + 'users_edit_usernames' => 1, + 'users_edit_ratio' => 1, + 'users_edit_own_ratio' => 1, + 'users_edit_titles' => 1, + 'users_edit_avatars' => 1, + 'users_edit_invites' => 1, + 'users_edit_watch_hours' => 1, + 'users_edit_reset_keys' => 1, + 'users_edit_profiles' => 1, + 'users_view_friends' => 1, + 'users_reset_own_keys' => 1, + 'users_edit_password' => 1, + 'users_promote_below' => 1, + 'users_promote_to' => 1, + 'users_give_donor' => 1, + 'users_warn' => 1, + 'users_disable_users' => 1, + 'users_disable_posts' => 1, + 'users_disable_any' => 1, + 'users_delete_users' => 1, + 'users_view_invites' => 1, + 'users_view_seedleech' => 1, + 'users_view_uploaded' => 1, + 'users_view_keys' => 1, + 'users_view_ips' => 1, + 'users_view_email' => 1, + 'users_invite_notes' => 1, + 'users_override_paranoia' => 1, + 'users_logout' => 1, + 'users_make_invisible' => 1, + 'users_mod' => 1, + 'torrents_edit' => 1, + 'torrents_delete' => 1, + 'torrents_delete_fast' => 1, + 'torrents_freeleech' => 1, + 'torrents_search_fast' => 1, + 'torrents_hide_dnu' => 1, + 'torrents_fix_ghosts' => 1, + 'admin_manage_news' => 1, + 'admin_manage_blog' => 1, + 'admin_manage_polls' => 1, + 'admin_manage_forums' => 1, + 'admin_manage_fls' => 1, + 'admin_reports' => 1, + 'admin_advanced_user_search' => 1, + 'admin_create_users' => 1, + 'admin_donor_log' => 1, + 'admin_manage_ipbans' => 1, + 'admin_dnu' => 1, + 'admin_clear_cache' => 1, + 'admin_whitelist' => 1, + 'admin_manage_permissions' => 1, + 'admin_schedule' => 1, + 'admin_login_watch' => 1, + 'admin_manage_wiki' => 1, + 'admin_update_geoip' => 1, + 'site_collages_recover' => 1, + 'torrents_add_artist' => 1, + 'edit_unknowns' => 1, + 'forums_polls_create' => 1, + 'forums_polls_moderate' => 1, + 'project_team' => 1, + 'torrents_edit_vanityhouse' => 1, + 'artist_edit_vanityhouse' => 1, + 'site_tag_aliases_read' => 1, + ]), + 'DisplayStaff' => '1', + 'PermittedForums' => '', + 'Secondary' => 0 + ], + ])->save(); + + $this->table('wiki_articles')->insert([ + ['Title' => 'Wiki', 'Body' => 'Welcome to your new wiki! Hope this works.', 'MinClassRead' => 100, 'MinClassEdit' => 475, 'Date' => '2019-01-01 12:59:59', 'Author' => 1] + ])->save(); + $this->table('wiki_aliases')->insert([['Alias' => 'wiki', 'UserID' => 1, 'ArticleID' => 1]])->save(); + $this->table('wiki_revisions')->insert([ + ['ID' => 1, 'Revision' => 1, 'Title' => 'Wiki', 'Body' => 'Welcome to your new wiki! Hope this works.', 'Date' => '2019-01-01 12:59:59', 'Author' => 1] + ])->save(); + $this->table('tags')->insert([ + ['Name' => 'rock', 'TagType' => 'genre', 'Uses' => 0, 'UserID' => 1], + ['Name' => 'pop', 'TagType' => 'genre', 'Uses' => 0, 'UserID' => 1], + ['Name' => 'female.fronted.symphonic.death.metal', 'TagType' => 'genre', 'Uses' => 0, 'UserID' => 1] + ])->save(); + + $this->table('stylesheets')->insert([ + ['Name' => 'Layer cake', 'Description' => 'Grey stylesheet by Emm'], + ['Name' => 'Proton', 'Description' => 'Proton by Protiek'], + ['Name' => 'postmod', 'Description' => 'Upgrade by anorex'], + ['Name' => 'Hydro', 'Description' => 'Hydro'], + ['Name' => 'Kuro', 'Description' => 'Kuro'], + ['Name' => 'Anorex', 'Description' => 'Anorex'], + ['Name' => 'Mono', 'Description' => 'Mono'], + ['Name' => 'Shiro', 'Description' => 'Shiro'], + ['Name' => 'Minimal', 'Description' => 'Minimal'], + ['Name' => 'Whatlove', 'Description' => 'Whatlove'], + ['Name' => 'White.cd', 'Description' => 'White.cd'], + ['Name' => 'GTFO Spaceship', 'Description' => 'gtfo spaceship'], + ['Name' => 'Dark Ambient', 'Description' => 'dark ambient'], + ['Name' => 'Xanax cake', 'Description' => 'Xanax cake'], + ['Name' => 'Haze', 'Description' => 'Haze by Exanurous & apopagasm'], + ['Name' => 'Post Office', 'Description' => 'Post Office by dannymichel'], + ['Name' => 'LinoHaze', 'Description' => 'LinoHaze by linotype'], + ['Name' => 'ApolloStage', 'Description' => 'ApolloStage by burtoo', 'Default' => '1'], + ['Name' => 'ApolloStage Coffee', 'Description' => 'ApolloStage by burtoo'], + ['Name' => 'ApolloStage Sunset', 'Description' => 'ApolloStage Sunset by burtoo'], + ['Name' => 'Apollo Mat', 'Description' => 'Apollo Mat by salem'] + ])->save(); + + $this->table('schedule')->insert([ + ['NextHour' => 0, 'NextDay' => 0, 'NextBiWeekly' => 0] + ])->save(); + + $this->execute("SET FOREIGN_KEY_CHECKS = 1;"); + } } diff --git a/db/migrations/20180111234647_applicant.php b/db/migrations/20180111234647_applicant.php index 51fba56f4..420e95872 100644 --- a/db/migrations/20180111234647_applicant.php +++ b/db/migrations/20180111234647_applicant.php @@ -4,68 +4,68 @@ use Phinx\Migration\AbstractMigration; class Applicant extends AbstractMigration { - public function up() { - $this->table('thread_type', ['id' => false, 'primary_key' => 'ID']) - ->addColumn('ID', 'integer', ['limit' => 6, 'signed' => false, 'identity' => true]) - ->addColumn('Name', 'string', ['limit' => 20]) - ->addIndex(['Name'], ['unique' => true]) - ->create(); + public function up() { + $this->table('thread_type', ['id' => false, 'primary_key' => 'ID']) + ->addColumn('ID', 'integer', ['limit' => 6, 'signed' => false, 'identity' => true]) + ->addColumn('Name', 'string', ['limit' => 20]) + ->addIndex(['Name'], ['unique' => true]) + ->create(); - $this->table('thread', ['id' => false, 'primary_key' => 'ID']) - ->addColumn('ID', 'integer', ['limit' => 6, 'signed' => false, 'identity' => true]) - ->addColumn('ThreadTypeID', 'integer', ['limit' => 6, 'signed' => false]) - ->addColumn('Created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'update' => 'CURRENT_TIMESTAMP']) - ->addForeignKey('ThreadTypeID', 'thread_type', 'ID') - ->create(); + $this->table('thread', ['id' => false, 'primary_key' => 'ID']) + ->addColumn('ID', 'integer', ['limit' => 6, 'signed' => false, 'identity' => true]) + ->addColumn('ThreadTypeID', 'integer', ['limit' => 6, 'signed' => false]) + ->addColumn('Created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'update' => 'CURRENT_TIMESTAMP']) + ->addForeignKey('ThreadTypeID', 'thread_type', 'ID') + ->create(); - $this->table('thread_note', ['id' => false, 'primary_key' => 'ID']) - ->addColumn('ID', 'integer', ['limit' => 6, 'signed' => false, 'identity' => true]) - ->addColumn('ThreadID', 'integer', ['limit' => 6, 'signed' => false]) - ->addColumn('Created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'update' => 'CURRENT_TIMESTAMP']) - ->addColumn('UserID', 'integer', ['limit' => 10, 'signed' => false]) - ->addColumn('Body', 'text', ['limit' => MysqlAdapter::TEXT_MEDIUM]) - ->addColumn('Visibility', 'enum', ['values' => ['staff', 'public']]) - ->addForeignKey('ThreadID', 'thread', 'ID') - ->addForeignKey('UserID', 'users_main', 'ID') - ->create(); + $this->table('thread_note', ['id' => false, 'primary_key' => 'ID']) + ->addColumn('ID', 'integer', ['limit' => 6, 'signed' => false, 'identity' => true]) + ->addColumn('ThreadID', 'integer', ['limit' => 6, 'signed' => false]) + ->addColumn('Created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP', 'update' => 'CURRENT_TIMESTAMP']) + ->addColumn('UserID', 'integer', ['limit' => 10, 'signed' => false]) + ->addColumn('Body', 'text', ['limit' => MysqlAdapter::TEXT_MEDIUM]) + ->addColumn('Visibility', 'enum', ['values' => ['staff', 'public']]) + ->addForeignKey('ThreadID', 'thread', 'ID') + ->addForeignKey('UserID', 'users_main', 'ID') + ->create(); - $this->table('applicant_role', ['id' => false, 'primary_key' => 'ID']) - ->addColumn('ID', 'integer', ['limit' => 4, 'signed' => false, 'identity' => true]) - ->addColumn('Title', 'string', ['limit' => 40]) - ->addColumn('Published', 'integer', ['limit' => MysqlAdapter::INT_TINY, 'default' => 0]) - ->addColumn('Description', 'text') - ->addColumn('UserID', 'integer', ['limit' => 10, 'signed' => false]) - ->addColumn('Created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP']) - ->addColumn('Modified', 'timestamp', ['default' => 'CURRENT_TIMESTAMP']) - ->addForeignKey('UserID', 'users_main', 'ID') - ->create(); + $this->table('applicant_role', ['id' => false, 'primary_key' => 'ID']) + ->addColumn('ID', 'integer', ['limit' => 4, 'signed' => false, 'identity' => true]) + ->addColumn('Title', 'string', ['limit' => 40]) + ->addColumn('Published', 'integer', ['limit' => MysqlAdapter::INT_TINY, 'default' => 0]) + ->addColumn('Description', 'text') + ->addColumn('UserID', 'integer', ['limit' => 10, 'signed' => false]) + ->addColumn('Created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP']) + ->addColumn('Modified', 'timestamp', ['default' => 'CURRENT_TIMESTAMP']) + ->addForeignKey('UserID', 'users_main', 'ID') + ->create(); - $this->table('applicant', ['id' => false, 'primary_key' => 'ID']) - ->addColumn('ID', 'integer', ['limit' => 4, 'signed' => false, 'identity' => true]) - ->addColumn('RoleID', 'integer', ['limit' => 4, 'signed' => false]) - ->addColumn('UserID', 'integer', ['limit' => 10, 'signed' => false]) - ->addColumn('ThreadID', 'integer', ['limit' => 6, 'signed' => false]) - ->addColumn('Body', 'text') - ->addColumn('Created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP']) - ->addColumn('Modified', 'timestamp', ['default' => 'CURRENT_TIMESTAMP']) - ->addColumn('Resolved', 'integer', ['limit' => MysqlAdapter::INT_TINY, 'default' => 0]) - ->addForeignKey('RoleID', 'applicant_role', 'ID') - ->addForeignKey('ThreadID', 'thread', 'ID') - ->addForeignKey('UserID', 'users_main', 'ID') - ->create(); + $this->table('applicant', ['id' => false, 'primary_key' => 'ID']) + ->addColumn('ID', 'integer', ['limit' => 4, 'signed' => false, 'identity' => true]) + ->addColumn('RoleID', 'integer', ['limit' => 4, 'signed' => false]) + ->addColumn('UserID', 'integer', ['limit' => 10, 'signed' => false]) + ->addColumn('ThreadID', 'integer', ['limit' => 6, 'signed' => false]) + ->addColumn('Body', 'text') + ->addColumn('Created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP']) + ->addColumn('Modified', 'timestamp', ['default' => 'CURRENT_TIMESTAMP']) + ->addColumn('Resolved', 'integer', ['limit' => MysqlAdapter::INT_TINY, 'default' => 0]) + ->addForeignKey('RoleID', 'applicant_role', 'ID') + ->addForeignKey('ThreadID', 'thread', 'ID') + ->addForeignKey('UserID', 'users_main', 'ID') + ->create(); - $this->insert('thread_type', [ - ['name' => 'staff-pm'], - ['name' => 'staff-role'], - ['name' => 'torrent-report'] - ]); - } + $this->table('thread_type')->insert([ + ['name' => 'staff-pm'], + ['name' => 'staff-role'], + ['name' => 'torrent-report'] + ])->save(); + } - public function down() { - $this->dropTable('applicant'); - $this->dropTable('applicant_role'); - $this->dropTable('thread_note'); - $this->dropTable('thread'); - $this->dropTable('thread_type'); - } + public function down() { + $this->dropTable('applicant'); + $this->dropTable('applicant_role'); + $this->dropTable('thread_note'); + $this->dropTable('thread'); + $this->dropTable('thread_type'); + } } diff --git a/db/migrations/20180214180452_platform_versions.php b/db/migrations/20180214180452_platform_versions.php index b1b775603..022d4399a 100644 --- a/db/migrations/20180214180452_platform_versions.php +++ b/db/migrations/20180214180452_platform_versions.php @@ -3,10 +3,10 @@ use Phinx\Migration\AbstractMigration; class PlatformVersions extends AbstractMigration { - public function change() { - $this->table('users_sessions') - ->addColumn('BrowserVersion', 'string', ['limit' => 40, 'null' => true, 'default' => null]) - ->addColumn('OperatingSystemVersion', 'string', ['limit' => 40, 'null' => true, 'default' => null]) - ->update(); - } + public function change() { + $this->table('users_sessions') + ->addColumn('BrowserVersion', 'string', ['limit' => 40, 'null' => true, 'default' => null]) + ->addColumn('OperatingSystemVersion', 'string', ['limit' => 40, 'null' => true, 'default' => null]) + ->update(); + } } diff --git a/db/migrations/20180303180452_bonus_points_migration.php b/db/migrations/20180303180452_bonus_points_migration.php index 24f4f9bc3..a56ab41d0 100644 --- a/db/migrations/20180303180452_bonus_points_migration.php +++ b/db/migrations/20180303180452_bonus_points_migration.php @@ -5,47 +5,47 @@ class BonusPointsMigration extends AbstractMigration { - public function up() { - $this->table('bonus_item', ['id' => false, 'primary_key' => 'ID']) - ->addColumn('ID', 'integer', ['limit' => 6, 'signed' => false, 'identity' => true]) - ->addColumn('Price', 'integer', ['limit' => 10, 'signed' => false]) - ->addColumn('Amount', 'integer', ['limit' => 2, 'signed' => false, 'null' => true]) - ->addColumn('MinClass', 'integer', ['limit' => 6, 'signed' => false, 'default' => 0]) - ->addColumn('FreeClass', 'integer', ['limit' => 6, 'signed' => false, 'default' => 999999]) - ->addColumn('Label', 'string', ['limit' => 32]) - ->addColumn('Title', 'string', ['limit' => 64]) - ->addIndex(['Label'], ['unique' => true]) - ->create(); - - $this->table('bonus_history', ['id' => false, 'primary_key' => 'ID']) - ->addColumn('ID', 'integer', ['limit' => 6, 'signed' => false, 'identity' => true]) - ->addColumn('ItemID', 'integer', ['limit' => 6, 'signed' => false]) - ->addColumn('UserID', 'integer', ['limit' => 10, 'signed' => false]) - ->addColumn('Price', 'integer', ['limit' => 10, 'signed' => false]) - ->addColumn('OtherUserID', 'integer', ['limit' => 10, 'signed' => false, 'null' => true]) - ->addColumn('PurchaseDate', 'datetime', ['default' => 'CURRENT_TIMESTAMP']) - ->addForeignKey('UserID', 'users_main', 'ID', ['constraint' => 'bonus_history_fk_user']) - ->addForeignKey('ItemID', 'bonus_item', 'ID', ['constraint' => 'bonus_history_fk_item']) - ->create(); - - $this->insert('bonus_item', [ - ['Price' => 1000, 'Amount' => 1, 'Label' => 'token-1', 'Title' => '1 Freeleech Token'], - ['Price' => 9500, 'Amount' => 10, 'Label' => 'token-2', 'Title' => '10 Freeleech Tokens'], - ['Price' => 45000, 'Amount' => 50, 'Label' => 'token-3', 'Title' => '50 Freeleech Tokens'], - ['Price' => 2500, 'Amount' => 1, 'Label' => 'other-1', 'Title' => '1 Freeleech Token to Other'], - ['Price' => 24000, 'Amount' => 10, 'Label' => 'other-2', 'Title' => '10 Freeleech Tokens to Other'], - ['Price' => 115000, 'Amount' => 50, 'Label' => 'other-3', 'Title' => '50 Freeleech Tokens to Other'], - - ['Price' => 20000, 'Amount' => 1, 'Label' => 'invite', 'MinClass' => 150, 'title' => 'Buy an Invite'], - - ['Price' => 50000, 'Label' => 'title-bb-n', 'FreeClass' => 400, 'Title' => 'Custom Title (No BBCode)'], - ['Price' => 150000, 'Label' => 'title-bb-y', 'FreeClass' => 400, 'Title' => 'Custom Title (BBCode Allowed)'], - ['Price' => 0, 'Label' => 'title-off', 'Title' => 'Remove Custom Title'], - ]); - } - - public function down() { - $this->dropTable('bonus_history'); - $this->dropTable('bonus_item'); - } + public function up() { + $this->table('bonus_item', ['id' => false, 'primary_key' => 'ID']) + ->addColumn('ID', 'integer', ['limit' => 6, 'signed' => false, 'identity' => true]) + ->addColumn('Price', 'integer', ['limit' => 10, 'signed' => false]) + ->addColumn('Amount', 'integer', ['limit' => 2, 'signed' => false, 'null' => true]) + ->addColumn('MinClass', 'integer', ['limit' => 6, 'signed' => false, 'default' => 0]) + ->addColumn('FreeClass', 'integer', ['limit' => 6, 'signed' => false, 'default' => 999999]) + ->addColumn('Label', 'string', ['limit' => 32]) + ->addColumn('Title', 'string', ['limit' => 64]) + ->addIndex(['Label'], ['unique' => true]) + ->create(); + + $this->table('bonus_history', ['id' => false, 'primary_key' => 'ID']) + ->addColumn('ID', 'integer', ['limit' => 6, 'signed' => false, 'identity' => true]) + ->addColumn('ItemID', 'integer', ['limit' => 6, 'signed' => false]) + ->addColumn('UserID', 'integer', ['limit' => 10, 'signed' => false]) + ->addColumn('Price', 'integer', ['limit' => 10, 'signed' => false]) + ->addColumn('OtherUserID', 'integer', ['limit' => 10, 'signed' => false, 'null' => true]) + ->addColumn('PurchaseDate', 'datetime', ['default' => 'CURRENT_TIMESTAMP']) + ->addForeignKey('UserID', 'users_main', 'ID', ['constraint' => 'bonus_history_fk_user']) + ->addForeignKey('ItemID', 'bonus_item', 'ID', ['constraint' => 'bonus_history_fk_item']) + ->create(); + + $this->table('bonus_item')->insert([ + ['Price' => 1000, 'Amount' => 1, 'Label' => 'token-1', 'Title' => '1 Freeleech Token'], + ['Price' => 9500, 'Amount' => 10, 'Label' => 'token-2', 'Title' => '10 Freeleech Tokens'], + ['Price' => 45000, 'Amount' => 50, 'Label' => 'token-3', 'Title' => '50 Freeleech Tokens'], + ['Price' => 2500, 'Amount' => 1, 'Label' => 'other-1', 'Title' => '1 Freeleech Token to Other'], + ['Price' => 24000, 'Amount' => 10, 'Label' => 'other-2', 'Title' => '10 Freeleech Tokens to Other'], + ['Price' => 115000, 'Amount' => 50, 'Label' => 'other-3', 'Title' => '50 Freeleech Tokens to Other'], + + ['Price' => 20000, 'Amount' => 1, 'Label' => 'invite', 'MinClass' => 150, 'title' => 'Buy an Invite'], + + ['Price' => 50000, 'Label' => 'title-bb-n', 'FreeClass' => 400, 'Title' => 'Custom Title (No BBCode)'], + ['Price' => 150000, 'Label' => 'title-bb-y', 'FreeClass' => 400, 'Title' => 'Custom Title (BBCode Allowed)'], + ['Price' => 0, 'Label' => 'title-off', 'Title' => 'Remove Custom Title'], + ])->save(); + } + + public function down() { + $this->dropTable('bonus_history'); + $this->dropTable('bonus_item'); + } } diff --git a/db/migrations/20180408161224_remove_library_contest.php b/db/migrations/20180408161224_remove_library_contest.php index fc92685ec..1aab9c19e 100644 --- a/db/migrations/20180408161224_remove_library_contest.php +++ b/db/migrations/20180408161224_remove_library_contest.php @@ -4,8 +4,8 @@ class RemoveLibraryContest extends AbstractMigration { public function up() { - if ($this->table('library_contest')->exists()) { - $this->dropTable('library_contest'); - } + if ($this->table('library_contest')->exists()) { + $this->table('library_contest')->drop()->update(); + } } } diff --git a/db/migrations/20180410205600_vanity_house.php b/db/migrations/20180410205600_vanity_house.php index 087708380..eca592aaf 100644 --- a/db/migrations/20180410205600_vanity_house.php +++ b/db/migrations/20180410205600_vanity_house.php @@ -10,4 +10,4 @@ public function change() ->addColumn('Type', 'integer', ['default' => 0, 'limit' => \Phinx\Db\Adapter\MysqlAdapter::INT_TINY]) ->update(); } -} \ No newline at end of file +} diff --git a/db/migrations/20180426104109_irc_channels.php b/db/migrations/20180426104109_irc_channels.php new file mode 100644 index 000000000..9e1618df6 --- /dev/null +++ b/db/migrations/20180426104109_irc_channels.php @@ -0,0 +1,17 @@ +table('irc_channels', ['id' => false, 'primary_key' => 'ID']) + ->addColumn('ID', 'integer', ['limit' => 10, 'signed' => false, 'identity' => true]) + ->addColumn('Name', 'string', ['limit' => 50]) + ->addColumn('Sort', 'integer', ['limit' => 11, 'default' => 0]) + ->addColumn('MinLevel', 'integer', ['limit' => 10, 'signed' => false, 'default' => 0]) + ->addColumn('Classes', 'string', ['limit' => 100, 'default' => '']) + ->addIndex('Name', ['unique' => true]) + ->create(); + } +} diff --git a/db/migrations/20180618045849_pm_on_delete.php b/db/migrations/20180618045849_pm_on_delete.php index 4e44c2d43..4d3bcfb0b 100644 --- a/db/migrations/20180618045849_pm_on_delete.php +++ b/db/migrations/20180618045849_pm_on_delete.php @@ -3,12 +3,12 @@ use Phinx\Migration\AbstractMigration; class PmOnDelete extends AbstractMigration { - public function change() { - // boolean => tinyint(1) - $this->table('users_info') - ->addColumn('NotifyOnDeleteSeeding', 'enum', ['values' => ['0', '1'], 'default' => '1']) - ->addColumn('NotifyOnDeleteSnatched', 'enum', ['values' => ['0', '1'], 'default' => '1']) - ->addColumn('NotifyOnDeleteDownloaded', 'enum', ['values' => ['0', '1'], 'default' => '1']) - ->update(); - } + public function change() { + // boolean => tinyint(1) + $this->table('users_info') + ->addColumn('NotifyOnDeleteSeeding', 'enum', ['values' => ['0', '1'], 'default' => '1']) + ->addColumn('NotifyOnDeleteSnatched', 'enum', ['values' => ['0', '1'], 'default' => '1']) + ->addColumn('NotifyOnDeleteDownloaded', 'enum', ['values' => ['0', '1'], 'default' => '1']) + ->update(); + } } diff --git a/db/migrations/20180714171241_referral.php b/db/migrations/20180714171241_referral.php new file mode 100644 index 000000000..cb26814db --- /dev/null +++ b/db/migrations/20180714171241_referral.php @@ -0,0 +1,41 @@ +table('referral_accounts', ['id' => false, 'primary_key' => 'ID']) + ->addColumn('ID', 'integer', ['limit' => 10, 'signed' => false, 'identity' => true]) + ->addColumn('Site', 'string', ['limit' => 100]) + ->addColumn('URL', 'string', ['limit' => 100]) + ->addColumn('User', 'string', ['limit' => 100]) + ->addColumn('Password', 'string', ['limit' => 196]) + ->addColumn('Active', 'boolean') + ->addColumn('Cookie', 'string', ['limit' => 1024]) + ->addColumn('Type', 'integer', ['limit' => 3, 'signed' => false]) + ->create(); + } +} diff --git a/db/migrations/20180729085427_referral_tracking.php b/db/migrations/20180729085427_referral_tracking.php new file mode 100644 index 000000000..710116fcb --- /dev/null +++ b/db/migrations/20180729085427_referral_tracking.php @@ -0,0 +1,42 @@ +table('referral_users', ['id' => false, 'primary_key' => 'ID']) + ->addColumn('ID', 'integer', ['limit' => 10, 'signed' => false, 'identity' => true]) + ->addColumn('UserID', 'integer', ['limit' => 10, 'signed' => false, 'default' => 0]) + ->addColumn('Username', 'string', ['limit' => 100]) + ->addColumn('Site', 'string', ['limit' => 100]) + ->addColumn('Created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP']) + ->addColumn('Joined', 'timestamp', ['default' => '0000-00-00 00:00:00']) + ->addColumn('IP', 'string', ['limit' => 15]) + ->addColumn('InviteKey', 'string', ['limit' => 32]) + ->addColumn('Active', 'boolean', ['default' => false]) + ->create(); + } +} diff --git a/db/migrations/20181219213631_release_type.php b/db/migrations/20181219213631_release_type.php index 248d02983..f89a04f3e 100644 --- a/db/migrations/20181219213631_release_type.php +++ b/db/migrations/20181219213631_release_type.php @@ -28,10 +28,10 @@ class ReleaseType extends AbstractMigration */ public function change() { - $table = $this->table('release_type', ['id' => false, 'primary_key' => 'ID']) - ->addColumn('ID', 'integer', ['limit' => 10, 'identity' => true]) - ->addColumn('Name', 'string', ['limit' => 50]) - ->addIndex(['Name'], ['unique' => true]) + $table = $this->table('release_type', ['id' => false, 'primary_key' => 'ID']) + ->addColumn('ID', 'integer', ['limit' => 10, 'identity' => true]) + ->addColumn('Name', 'string', ['limit' => 50]) + ->addIndex(['Name'], ['unique' => true]) ->create(); $data = [ diff --git a/db/migrations/20181219220422_request_checksum.php b/db/migrations/20181219220422_request_checksum.php index 7122609b4..a9f1fd5be 100644 --- a/db/migrations/20181219220422_request_checksum.php +++ b/db/migrations/20181219220422_request_checksum.php @@ -26,19 +26,21 @@ class RequestChecksum extends AbstractMigration * Remember to call "create()" or "update()" and NOT "save()" when working * with the Table class. */ - public function change() - { - $this->table('requests')->addColumn('Checksum', 'boolean', ['default' => false]) - ->update(); - } public function up() { + $this->table('requests') + ->addColumn('Checksum', 'boolean', ['default' => false]) + ->update(); $this->execute('ALTER TABLE requests MODIFY TimeFilled datetime'); - $this->execute("UPDATE requests SET timefilled = null WHERE timefilled = '0000-00-00 00:00:00'"); + $this->execute("UPDATE requests SET TimeFilled = null WHERE TimeFilled = '0000-00-00 00:00:00'"); } + public function down() { - $this->execute("ALTER TABLE requests MODIFY TimeFilled datetime NOT NULL DEFAULT '0000-00-00 00:00:00'"); $this->execute("UPDATE requests SET TimeFilled = '0000-00-00 00:00:00' WHERE TimeFilled IS NULL"); + $this->execute("ALTER TABLE requests MODIFY TimeFilled datetime NOT NULL DEFAULT '0000-00-00 00:00:00'"); + $this->table('requests') + ->removeColumn('Checksum') + ->update(); } } diff --git a/db/migrations/20181229185143_delete_torrent.php b/db/migrations/20181229185143_delete_torrent.php index 07cfd6d49..e0bdc5ad9 100644 --- a/db/migrations/20181229185143_delete_torrent.php +++ b/db/migrations/20181229185143_delete_torrent.php @@ -27,7 +27,13 @@ class DeleteTorrent extends AbstractMigration * with the Table class. */ public function up() { - $this->execute(" + $this->table('torrents') + ->changeColumn('last_action', 'datetime', ['null' => true]) + ->save(); + + $this->execute(" +UPDATE torrents SET last_action = NULL WHERE last_action='0000-00-00 00:00:00'; + CREATE TABLE `deleted_torrents` ( `ID` int(10) NOT NULL, `GroupID` int(10) NOT NULL, @@ -53,7 +59,7 @@ public function up() { `Size` bigint(12) NOT NULL, `Leechers` int(6) NOT NULL, `Seeders` int(6) NOT NULL, - `last_action` datetime NOT NULL, + `last_action` datetime, `FreeTorrent` enum('0','1','2') NOT NULL, `FreeLeechType` enum('0','1','2','3','4','5','6','7') NOT NULL, `Time` datetime NOT NULL, @@ -129,11 +135,11 @@ public function up() { PRIMARY KEY (`TorrentID`) ); - "); + "); } public function down() { - $this->execute(" + $this->execute(" DROP TABLE `deleted_torrents`; DROP TABLE `deleted_users_notify_torrents`; DROP TABLE `deleted_torrents_files`; diff --git a/db/migrations/20190212082403_bonus_point_pool.php b/db/migrations/20190212082403_bonus_point_pool.php new file mode 100644 index 000000000..d988e84ba --- /dev/null +++ b/db/migrations/20190212082403_bonus_point_pool.php @@ -0,0 +1,59 @@ +table('bonus_pool', ['id' => false, 'primary_key' => 'ID']) + ->addColumn('ID', 'integer', ['limit' => 6, 'signed' => false, 'identity' => true]) + ->addColumn('Name', 'string', ['limit' => 80]) + ->addColumn('SinceDate', 'timestamp') + ->addColumn('UntilDate', 'timestamp') + ->addColumn('Total', 'float', ['default' => 0]) + ->create(); + + $this->table('bonus_pool_contrib', ['id' => false, 'primary_key' => 'ID']) + ->addColumn('ID', 'integer', ['limit' => 6, 'signed' => false, 'identity' => true]) + ->addColumn('BonusPoolID', 'integer', ['limit' => 6, 'signed' => false]) + ->addColumn('UserID', 'integer', ['limit' => 10, 'signed' => false]) + ->addColumn('AmountRecv', 'float') + ->addColumn('AmountSent', 'float') + ->addColumn('Created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP']) + ->addForeignKey('BonusPoolID', 'bonus_pool', 'ID') + ->addForeignKey('UserID', 'users_main', 'ID') + ->create(); + + $this->table('contest_has_bonus_pool', ['id' => false, 'primary_key' => ['BonusPoolID', 'ContestID']]) + ->addColumn('BonusPoolID', 'integer', ['limit' => 6, 'signed' => false]) + ->addColumn('ContestID', 'integer', ['limit' => 11]) + ->addForeignKey('BonusPoolID', 'bonus_pool', 'ID') + ->addForeignKey('ContestID', 'contest', 'ID') + ->create(); + + $this->table('contest_type')->insert([['Name' => 'upload_flac_no_single']])->save(); + } +} diff --git a/db/migrations/20190422111146_torrent_stats_tables.php b/db/migrations/20190422111146_torrent_stats_tables.php new file mode 100644 index 000000000..e6ac0b22c --- /dev/null +++ b/db/migrations/20190422111146_torrent_stats_tables.php @@ -0,0 +1,71 @@ +table('users_leech_stats', ['id' => false, 'primary_key' => 'UserID']) + ->addColumn('UserID', 'integer', ['limit' => 10, 'signed' => false]) + ->addColumn('Uploaded', 'biginteger', ['limit' => 20, 'signed' => false, 'default' => 0]) + ->addColumn('Downloaded', 'biginteger', ['limit' => 20, 'signed' => false, 'default' => 0]) + ->addIndex(['Uploaded'], ['name' => 'uls_uploaded_idx']) + ->addIndex(['Downloaded'], ['name' => 'uls_downloaded_idx']) + ->addForeignKey('UserID', 'users_main', 'ID') + ->create(); + + $this->table('torrents_leech_stats', ['id' => false, 'primary_key' => 'TorrentID']) + ->addColumn('TorrentID', 'integer', ['limit' => 10]) + ->addColumn('Seeders', 'integer', ['limit' => 6, 'signed' => false, 'default' => 0]) + ->addColumn('Leechers', 'integer', ['limit' => 6, 'signed' => false, 'default' => 0]) + ->addColumn('Snatched', 'integer', ['limit' => 6, 'signed' => false, 'default' => 0]) + ->addColumn('Balance', 'biginteger', ['limit' => 20, 'default' => 0]) + ->addColumn('last_action', 'datetime', ['null' => true]) + ->addIndex(['Seeders'], ['name' => 'tls_seeders_idx']) + ->addIndex(['Leechers'], ['name' => 'tls_leechers_idx']) + ->addIndex(['Snatched'], ['name' => 'tls_snatched_idx']) + ->addIndex(['last_action'], ['name' => 'tls_last_action_idx']) + ->addForeignKey('TorrentID', 'torrents', 'ID', ['delete' => 'CASCADE']) + ->create(); + + $this->table('deleted_torrents_leech_stats', ['id' => false, 'primary_key' => 'TorrentID']) + ->addColumn('TorrentID', 'integer', ['limit' => 10]) + ->addColumn('Seeders', 'integer', ['limit' => 6, 'signed' => false, 'default' => 0]) + ->addColumn('Leechers', 'integer', ['limit' => 6, 'signed' => false, 'default' => 0]) + ->addColumn('Snatched', 'integer', ['limit' => 6, 'signed' => false, 'default' => 0]) + ->addColumn('Balance', 'biginteger', ['limit' => 20, 'default' => 0]) + ->addColumn('last_action', 'datetime', ['null' => true]) + ->addForeignKey('TorrentID', 'deleted_torrents', 'ID', ['delete' => 'CASCADE']) + ->create(); + + $this->execute("INSERT INTO torrents_leech_stats (TorrentID, Seeders, Leechers, Snatched, Balance, last_action) SELECT ID, Seeders, Leechers, Snatched, balance, last_action FROM torrents"); + $this->execute("INSERT INTO users_leech_stats (UserID, Uploaded, Downloaded) SELECT ID, Uploaded, Downloaded FROM users_main"); + } +} diff --git a/db/migrations/20190719073623_add_contest_payout.php b/db/migrations/20190719073623_add_contest_payout.php new file mode 100644 index 000000000..d199da04d --- /dev/null +++ b/db/migrations/20190719073623_add_contest_payout.php @@ -0,0 +1,40 @@ +table('contest_has_bonus_pool') + ->addColumn('Status', 'enum', ['values' => ['open', 'ready', 'paid'], 'default' => 'open']) + ->update(); + + } +} diff --git a/db/migrations/20190824010935_user_nav.php b/db/migrations/20190824010935_user_nav.php index 7f4bbcac6..939640726 100644 --- a/db/migrations/20190824010935_user_nav.php +++ b/db/migrations/20190824010935_user_nav.php @@ -6,28 +6,9 @@ class UserNav extends AbstractMigration { /** - * Change Method. - * - * Write your reversible migrations using this method. - * - * More information on writing migrations is available here: - * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class - * - * The following commands can be used in this method and Phinx will - * automatically reverse them when rolling back: - * - * createTable - * renameTable - * addColumn - * renameColumn - * addIndex - * addForeignKey - * - * Remember to call "create()" or "update()" and NOT "save()" when working - * with the Table class. + * Change Method. ...is what I WOULD say, if that was possible. */ - public function change() - { + public function up() { $this->table('nav_items', ['id' => false, 'primary_key' => 'ID']) ->addColumn('ID', 'integer', ['limit' => 10, 'signed' => false, 'identity' => true]) ->addColumn('Key', 'string', ['limit' => 20]) @@ -36,10 +17,123 @@ public function change() ->addColumn('Tests', 'string', ['limit' => 100]) ->addColumn('TestUser', 'boolean') ->addColumn('Mandatory', 'boolean') - ->create(); + ->save(); + + $fixtures = [ + [ + 'Key' => 'inbox', + 'Title' => 'Inbox', + 'Target' => 'inbox.php', + 'Tests' => 'inbox', + 'TestUser' => false, + 'Mandatory' => true + ], + [ + 'Key' => 'staffinbox', + 'Title' => 'Staff Inbox', + 'Target' => 'staffpm.php', + 'Tests' => 'staffpm', + 'TestUser' => false, + 'Mandatory' => true + ], + [ + 'Key' => 'uploaded', + 'Title' => 'Uploads', + 'Target' => 'torrents.php?type=uploaded', + 'Tests' => 'torrents,false,uploaded', + 'TestUser' => true, + 'Mandatory' => false + ], + [ + 'Key' => 'bookmarks', + 'Title' => 'Bookmarks', + 'Target' => 'bookmarks.php?type=torrents', + 'Tests' => 'bookmarks', + 'TestUser' => false, + 'Mandatory' => false + ], + [ + 'Key' => 'notifications', + 'Title' => 'Notifications', + 'Target' => 'user.php?action=notify', + 'Tests' => 'torrents:notify,user:notify', + 'TestUser' => true, + 'Mandatory' => false + ], + [ + 'Key' => 'subscriptions', + 'Title' => 'Subscriptions', + 'Target' => 'userhistory.php?action=subscriptions', + 'Tests' => '', + 'TestUser' => false, + 'Mandatory' => false + ], + [ + 'Key' => 'comments', + 'Title' => 'Comments', + 'Target' => 'comments.php', + 'Tests' => 'comments', + 'TestUser' => true, + 'Mandatory' => false + ], + [ + 'Key' => 'friends', + 'Title' => 'Friends', + 'Target' => 'friends.php', + 'Tests' => 'friends', + 'TestUser' => false, + 'Mandatory' => false + ], + [ + 'Key' => 'better', + 'Title' => 'Better', + 'Target' => 'better.php', + 'Tests' => 'better', + 'TestUser' => false, + 'Mandatory' => false + ], + [ + 'Key' => 'random', + 'Title' => 'Random Album', + 'Target' => 'random.php', + 'Tests' => 'random', + 'TestUser' => false, + 'Mandatory' => false + ], + [ + 'Key' => 'logchecker', + 'Title' => 'Log Checker', + 'Target' => 'logchecker.php', + 'Tests' => 'logchecker', + 'TestUser' => false, + 'Mandatory' => false + ], + [ + 'Key' => 'posts', + 'Title' => 'Posts', + 'Target' => 'userhistory.php?action=posts', + 'Tests' => 'userhistory,posts', + 'TestUser' => false, + 'Mandatory' => false + ] + ]; + + $this->table('nav_items')->insert($fixtures)->save(); $this->table('users_info') ->addColumn('NavItems', 'string', ['limit' => 200]) - ->update(); + ->save(); + + $this->execute(" + UPDATE users_info + SET NavItems = '1,2,3,4,5,6,7,8,9,10'"); + } + + public function down() { + $this->table('users_info') + ->removeColumn('NavItems') + ->save(); + + $this->dropTable('nav_items'); } } diff --git a/db/migrations/20190921082622_forum_edit_permissions.php b/db/migrations/20190921082622_forum_edit_permissions.php new file mode 100644 index 000000000..615f8b1f9 --- /dev/null +++ b/db/migrations/20190921082622_forum_edit_permissions.php @@ -0,0 +1,53 @@ +table('forums_transitions', ['id' => false, 'primary_key' => 'forums_transitions_id']); + + $table + ->addColumn('forums_transitions_id', 'integer', ['limit' => 10, 'identity' => true]) + ->addColumn('source', 'integer', ['limit' => 6, 'signed' => false]) + ->addColumn('destination', 'integer', ['limit' => 6, 'signed' => false]) + ->addColumn('label', 'string', ['limit' => 20]) + ->addColumn('permission_levels', 'string', ['limit' => 50]) + ->addForeignKey('source', 'forums', 'ID', ['delete' => 'CASCADE', 'update' => 'CASCADE']) + ->addForeignKey('destination', 'forums', 'ID', ['delete' => 'CASCADE', 'update' => 'CASCADE']); + + if (defined('TRASH_FORUM_ID')) { + $builder = $this->getQueryBuilder(); + $statement = $builder + ->select('f.ID') + ->from(['f' => 'forums']) + ->join(['fc' => [ + 'table' => 'forums_categories', + 'conditions' => 'f.CategoryID = fc.ID' + ]]) + ->orderAsc('fc.Sort') + ->orderAsc('f.Sort') + ->execute(); + $insertData = []; + foreach($statement->fetchAll('assoc') as $row) { + if ($row['ID'] === TRASH_FORUM_ID) { + continue; + } + $insertData[] = [ + 'source' => (int) $row['ID'], + 'destination' => TRASH_FORUM_ID, + 'label' => 'Trash', + 'permission_levels' => '' + ]; + } + $table->insert($insertData); + } + $table->create(); + } + + public function down() + { + $this->table('forums_transitions')->drop()->update(); + } +} diff --git a/db/migrations/20190930092524_new_user_nav.php b/db/migrations/20190930092524_new_user_nav.php new file mode 100644 index 000000000..2a6058ced --- /dev/null +++ b/db/migrations/20190930092524_new_user_nav.php @@ -0,0 +1,66 @@ +table('nav_items') + ->addColumn('initial', 'boolean', ['default' => false]) + ->renameColumn('ID', 'id') + ->renameColumn('Key', 'tag') + ->renameColumn('Title', 'title') + ->renameColumn('Target', 'target') + ->renameColumn('Tests', 'tests') + ->renameColumn('TestUser', 'test_user') + ->renameColumn('Mandatory', 'mandatory') + ->update(); + + $this->execute(" + UPDATE nav_items + SET initial = 1 + WHERE id IN (1,2,3,4,5,6,7,8,9,10)"); + + $this->execute(" + UPDATE users_info + SET NavItems = '1,2,3,4,5,6,7,8,9,10' + WHERE NavItems IN ('', '1,2')"); + } + + public function down() + { + $this->table('nav_items') + ->removeColumn('initial') + ->renameColumn('id', 'ID') + ->renameColumn('tag', 'Key') + ->renameColumn('title', 'Title') + ->renameColumn('target', 'Target') + ->renameColumn('tests', 'Tests') + ->renameColumn('test_user', 'Test_Tser') + ->renameColumn('mandatory', 'Mandatory') + ->update(); + } +} diff --git a/db/recover.sql b/db/recover.sql new file mode 100644 index 000000000..fb232e93c --- /dev/null +++ b/db/recover.sql @@ -0,0 +1,24 @@ +CREATE OR REPLACE TABLE recovery ( + recovery_id integer(10) unsigned not null auto_increment, + state enum ('PENDING', 'VALIDATED', 'CLAIMED', 'ACCEPTED', 'DENIED') not null default 'PENDING', + admin_user_id int(10) unsigned, + created_dt datetime not null default current_timestamp, + updated_dt datetime not null default current_timestamp, + token varchar(19) not null, + username varchar(20) not null, + ipaddr varchar(40) not null, + passhash varchar(60) CHARACTER SET ASCII, + email varchar(255) CHARACTER SET ASCII, + email_clean varchar(255) CHARACTER SET ASCII, + announce varchar(100) CHARACTER SET ASCII, + screenshot char(40) CHARACTER SET ASCII, + invite mediumtext, + info mediumtext, + log mediumtext, + PRIMARY KEY (recovery_id), + UNIQUE KEY (username), + UNIQUE KEY (email_clean), + KEY (email), + KEY (state), + KEY (admin_user_id) +); diff --git a/db/seeds/InitialUserSeeder.php b/db/seeds/InitialUserSeeder.php index adfe17b21..fb3430e2e 100644 --- a/db/seeds/InitialUserSeeder.php +++ b/db/seeds/InitialUserSeeder.php @@ -3,84 +3,92 @@ use Phinx\Seed\AbstractSeed; class InitialUserSeeder extends AbstractSeed { - public function run() { - $stmt = $this->query("SELECT COUNT(*) AS count FROM users_main WHERE Username='admin'"); - if (((int) $stmt->fetch()['count']) > 0) { - return; - } + public function run() { + $stmt = $this->query("SELECT COUNT(*) AS count FROM users_main WHERE Username='admin'"); + if (((int) $stmt->fetch()['count']) > 0) { + return; + } - $now = (new \DateTime("now"))->format("Y-m-d H:i:s"); - $this->table('users_main')->insert([ - [ - 'Username' => 'admin', - 'Email' => 'admin@example.com', - 'PassHash' => password_hash(hash('sha256','password'), PASSWORD_DEFAULT), - 'Class' => 5, - 'Uploaded' => 3221225472, - 'Enabled' => '1', - 'Visible' => 1, - 'Invites' => 0, - 'PermissionID' => 15, - 'can_leech' => 1, - 'torrent_pass' => '86519d75682397913039534ea21a4e45', - 'LastAccess' => $now - ], - [ - 'Username' => 'user', - 'Email' => 'user@example.com', - 'PassHash' => password_hash(hash('sha256','password'), PASSWORD_DEFAULT), - 'Class' => 5, - 'Uploaded' => 3221225472, - 'Enabled' => '1', - 'Visible' => 1, - 'Invites' => 0, - 'PermissionID' => 2, - 'can_leech' => 1, - 'torrent_pass' => '86519d75682397913039534ea21a4e45', - 'LastAccess' => $now - ], - ])->saveData(); + $now = (new \DateTime("now"))->format("Y-m-d H:i:s"); + $this->table('users_main')->insert([ + [ + 'Username' => 'admin', + 'Email' => 'admin@example.com', + 'PassHash' => password_hash(hash('sha256','password'), PASSWORD_DEFAULT), + 'Class' => 5, + 'Enabled' => '1', + 'Visible' => 1, + 'Invites' => 0, + 'PermissionID' => 15, + 'can_leech' => 1, + 'torrent_pass' => '86519d75682397913039534ea21a4e45', + 'LastAccess' => $now + ], + [ + 'Username' => 'user', + 'Email' => 'user@example.com', + 'PassHash' => password_hash(hash('sha256','password'), PASSWORD_DEFAULT), + 'Class' => 5, + 'Enabled' => '1', + 'Visible' => 1, + 'Invites' => 0, + 'PermissionID' => 2, + 'can_leech' => 1, + 'torrent_pass' => '86519d75682397913039534ea21a4e45', + 'LastAccess' => $now + ], + ])->saveData(); + $this->table('users_leech_stats')->insert([ + [ + 'UserID' => 1, + 'Uploaded' => STARTING_UPLOAD + ], + [ + 'UserID' => 2, + 'Uploaded' => STARTING_UPLOAD + ] + ])->saveData(); - $this->table('users_info')->insert([ - [ - 'UserID' => 1, - 'StyleID' => 18, - 'TorrentGrouping' => 0, - 'ShowTags' => 1, - 'AuthKey' => '7d3b4750ea71502d25051875a250b71a', - 'JoinDate' => '2018-03-08 15:50:31', - 'Inviter' => 0, - ], - [ - 'UserID' => 2, - 'StyleID' => 1, - 'TorrentGrouping' => 0, - 'ShowTags' => 1, - 'AuthKey' => 'a1189fa8554776c6de31b6b4e2d0faea', - 'JoinDate' => '2018-03-09 05:04:07', - 'Inviter' => 0, - ] - ])->saveData(); + $this->table('users_info')->insert([ + [ + 'UserID' => 1, + 'StyleID' => 18, + 'TorrentGrouping' => 0, + 'ShowTags' => 1, + 'AuthKey' => '7d3b4750ea71502d25051875a250b71a', + 'JoinDate' => '2018-03-08 15:50:31', + 'Inviter' => 0, + ], + [ + 'UserID' => 2, + 'StyleID' => 1, + 'TorrentGrouping' => 0, + 'ShowTags' => 1, + 'AuthKey' => 'a1189fa8554776c6de31b6b4e2d0faea', + 'JoinDate' => '2018-03-09 05:04:07', + 'Inviter' => 0, + ] + ])->saveData(); - $this->table('users_history_emails')->insert([ - [ - 'UserID' => 1, - 'Email' => 'admin@example.com', - 'Time' => null, - 'IP' => '127.0.0.1' - ], - [ - 'UserID' => 2, - 'Email' => 'user@example.com', - 'Time' => null, - 'IP' => '127.0.0.1' - ] - ])->saveData(); + $this->table('users_history_emails')->insert([ + [ + 'UserID' => 1, + 'Email' => 'admin@example.com', + 'Time' => null, + 'IP' => '127.0.0.1' + ], + [ + 'UserID' => 2, + 'Email' => 'user@example.com', + 'Time' => null, + 'IP' => '127.0.0.1' + ] + ])->saveData(); - $this->table('users_notifications_settings')->insert([ - ['UserID' => 1], - ['UserID' => 2] - ])->saveData(); - } -} \ No newline at end of file + $this->table('users_notifications_settings')->insert([ + ['UserID' => 1], + ['UserID' => 2] + ])->saveData(); + } +} diff --git a/db/seeds/TorrentSeeder.php b/db/seeds/TorrentSeeder.php index 77bdf5eba..6a0594e4a 100644 --- a/db/seeds/TorrentSeeder.php +++ b/db/seeds/TorrentSeeder.php @@ -3,8 +3,8 @@ use Phinx\Seed\AbstractSeed; class TorrentSeeder extends AbstractSeed { - /** - * To reset and rerun this seed: + /** + * To reset and rerun this seed: SET FOREIGN_KEY_CHECKS = 0; TRUNCATE torrents_files; @@ -25,195 +25,195 @@ class TorrentSeeder extends AbstractSeed { TRUNCATE users_notifications_settings; SET FOREIGN_KEY_CHECKS = 1; - */ - const DISCOGS_MAX = 11747136; - const TORRENTS = 20; - - private function getRandomDiscogsAlbum() { - $id = rand(1, self::DISCOGS_MAX); - $ch = curl_init(); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch,CURLOPT_USERAGENT,'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13'); - curl_setopt($ch, CURLOPT_URL, 'https://api.discogs.com/releases/'.$id); - $result = curl_exec($ch); - curl_close($ch); - return json_decode($result, true); - } - - public function run() { - $stmt = $this->query("SELECT COUNT(*) AS count FROM users_main"); - $user_count = (int) $stmt->fetch()['count']; - - $bencode = new OrpheusNET\BencodeTorrent\BencodeTorrent(); - - $insert_data = [ - 'artists_group' => [], - 'artists_alias' => [], - 'torrents_group' => [], - 'wiki_torrents' => [], - 'torrents_artists' => [], - 'tags' => [], - 'torrents_tags' => [], - 'torrents' => [], - 'torrents_files' => [] - ]; - - $artists = []; - $groups = []; - $tags = [ - 'rock' => ['id' => 1, 'genre' => 'rock'], - 'pop' => ['id' => 2, 'genre' => 'pop'], - 'female.fronted.symphonic.death.metal' => ['id' => 3, 'genre' => 'female.fronted.symphonic.death.metal'] - ]; - - $i = 0; - while ($i < self::TORRENTS) { - // Avoid rate limit of 25 albums per minute - sleep(2); - $album = $this->getRandomDiscogsAlbum(); - if (!property_exists($album, 'year') || (!empty($album->message) && $album->message === 'Release not found.') || $album->year == 0) { - continue; - } - $user_id = rand(1, $user_count); - $this->output->writeln("Found torrent {$i}..."); - - $artist = $album->artists[0]; - if (!isset($artists[$artist->name])) { - $artists[$artist->name] = [ - 'id' => count($artists) + 1, - 'obj' => $artist, - ]; - $insert_data['artists_group'][] = ['Name' => $artist->name]; - $insert_data['artists_alias'][] = ['ArtistID' => $artists[$artist->name]['id'], 'Name' => $artist->name]; - } - - foreach ($album->genres as $idx => $genre) { - $genre = str_replace(array(' ', '&'), array('.', ''), strtolower($genre)); - $album->genres[$idx] = $genre; - if (!isset($tags[$genre])) { - $insert_data['tags'][] = [ - 'Name' => $genre, - 'TagType' => 'genre', - 'Uses' => 1, - 'UserID' => $user_id - ]; - $tags[$genre] = ['id' => (count($tags) + 1), 'genre' => $genre, 'idx' => count($insert_data)]; - } - } - if (!isset($groups[$album->title])) { - $wiki_body = !empty($album->notes) ? $album->notes . "\n\n" : ''; - foreach ($album->tracklist as $track) { - $wiki_body .= "{$track->position}. {$track->title} ({$track->duration})\n"; - } - $insert_data['torrents_group'][] = [ - 'ArtistID' => 0, - 'CategoryID' => 1, - 'Name' => $album->title, - 'Year' => $album->year, - 'CatalogueNumber' => $album->labels[0]->catno, - 'RecordLabel' => $album->labels[0]->name, - 'Time' => '2018-03-22 02:24:19', - 'RevisionID' => count($groups) + 1, - 'WikiBody' => $wiki_body, - 'WikiImage' => '', - 'ReleaseType' => 1, - 'VanityHouse' => 0 - ]; - - $insert_data['wiki_torrents'][] = [ - 'PageID' => count($groups) + 1, - 'Body' => $wiki_body, - 'UserID' => $user_id, - 'Summary' => 'Uploaded new torrent', - 'Time' => '2018-03-22 02:24:19', - 'Image' => '' - ]; - - foreach ($album->genres as $genre) { - $insert_data['torrents_tags'][] = [ - 'TagID' => $tags[$genre]['id'], - 'GroupID' => count($groups) + 1, - 'PositiveVotes' => 10 - ]; - } - - $insert_data['torrents_artists'][] = [ - 'GroupID' => count($groups) + 1, - 'ArtistID' => $artists[$album->artists[0]->name]['id'], - 'AliasID' => $artists[$album->artists[0]->name]['id'], - 'UserID' => $user_id, - 'Importance' => 1 - ]; - $groups[$album->title] = ['id' => count($groups) + 1, 'album' => $album]; - } - - $media = ($album->formats[0]->name === 'Vinyl') ? 'Vinyl' : 'CD'; - - $torrent_id = count($insert_data['torrents']) + 1; - $files = []; - $file_list = []; - $delim = utf8_encode(chr(0xF7)); - foreach ($album->tracklist as $track) { - $length = rand(1, 45573573); - $name = "{$track->position}. {$track->title}.mp3"; - $files[] = ['length' => $length, 'path' => [$name]]; - $file_list[] = ".mp3 s{$length} {$name} {$delim}"; - } - $file_list = implode('\n', $file_list); - /** @noinspection PhpUnhandledExceptionInspection */ - $bencode->setData([ - 'announce' => 'https://localhost:34000/hash_pass/announce', - 'comment' => "https://localhost:8080//torrents.php?torrentid={$torrent_id}", - 'creation date' => 1489617624, - 'encoding' => 'UTF-8', - 'info' => [ - 'files' => $files, - 'name' => "{$album->artists[0]->name} - {$album->title} ({$album->year})", - 'piece length' => 262144, - 'pieces' => 'string of pieces' - ] - - ]); - $insert_data['torrents'][] = [ - 'GroupID' => $groups[$album->title]['id'], - 'UserID' => $user_id, - 'Media' => $media, - 'Format' => 'MP3', - 'Encoding' => '320', - 'Remastered' => 0, - 'RemasterYear' => 0, - 'RemasterTitle' => '', - 'RemasterRecordLabel' => '', - 'RemasterCatalogueNumber' => '', - 'Scene' => 0, - 'HasLog' => 0, - 'HasCue' => 0, - 'HasLogDB' => 0, - 'LogScore' => 100, - 'LogChecksum' => 1, - 'info_hash' => $bencode->getHexInfoHash(), - 'FileCount' => count($album->tracklist), - 'FileList' => $file_list, - 'FilePath' => "{$album->artists[0]->name} - {$album->title} ({$album->year})", - 'Size' => '253809759', - 'Time' => '2018-03-22 02:24:19', - 'Description' => '', - 'FreeTorrent' => 0, - 'FreeLeechType' => 0 - ]; - - $insert_data['torrents_files'][] = [ - 'TorrentID' => count($insert_data['torrents']) + 1, - 'File' => b'd8:announce65:https://localhost:34000/4f9587fbcb06fe09165e4f84d35d0403/announce7:comment53:https://localhost:8080//torrents.php?id=1&torrentid=113:creation datei1489617624e8:encoding5:UTF-84:infod5:filesld6:lengthi45573573e4:pathl19:01 - Nightmare.flaceed6:lengthi31675140e4:pathl31:02 - Welcome to the Family.flaceed6:lengthi36911187e4:pathl21:03 - Danger Line.flaceed6:lengthi47893264e4:pathl22:04 - Buried Alive.flaceed6:lengthi40839480e4:pathl29:05 - Natural Born Killer.flaceed6:lengthi40787176e4:pathl21:06 - So Far Away.flaceed6:lengthi34999093e4:pathl22:07 - God Hates Us.flaceed6:lengthi52152693e4:pathl16:08 - Victim.flaceed6:lengthi34464081e4:pathl32:09 - Tonight the World Dies.flaceed6:lengthi34485391e4:pathl17:10 - Fiction.flaceed6:lengthi81953099e4:pathl17:11 - Save Me.flaceed6:lengthi1100e4:pathl33:Avenged Sevenfold - Nightmare.cueeed6:lengthi12890e4:pathl33:Avenged Sevenfold - Nightmare.logeed6:lengthi66260e4:pathl10:folder.jpgeee4:name73:Avenged Sevenfold \xe2\x80\x8e- Nightmare (2010) [CD - FLAC - Lossless] {524026-2}12:piece lengthi262144e6:pieces19:fake torrent pieces7:privatei1e6:source3:APLee' - ]; - $i++; - } - - foreach ($insert_data as $table => $data) { - $this->table($table)->insert($data)->saveData(); - } - - $this->execute('UPDATE tags SET Uses = ( SELECT COUNT(*) FROM torrents_tags WHERE torrents_tags.TagID = tags.ID GROUP BY TagID)'); - } + */ + const DISCOGS_MAX = 11747136; + const TORRENTS = 20; + + private function getRandomDiscogsAlbum() { + $id = rand(1, self::DISCOGS_MAX); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch,CURLOPT_USERAGENT,'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13'); + curl_setopt($ch, CURLOPT_URL, 'https://api.discogs.com/releases/'.$id); + $result = curl_exec($ch); + curl_close($ch); + return json_decode($result, true); + } + + public function run() { + $stmt = $this->query("SELECT COUNT(*) AS count FROM users_main"); + $user_count = (int) $stmt->fetch()['count']; + + $bencode = new OrpheusNET\BencodeTorrent\BencodeTorrent(); + + $insert_data = [ + 'artists_group' => [], + 'artists_alias' => [], + 'torrents_group' => [], + 'wiki_torrents' => [], + 'torrents_artists' => [], + 'tags' => [], + 'torrents_tags' => [], + 'torrents' => [], + 'torrents_files' => [] + ]; + + $artists = []; + $groups = []; + $tags = [ + 'rock' => ['id' => 1, 'genre' => 'rock'], + 'pop' => ['id' => 2, 'genre' => 'pop'], + 'female.fronted.symphonic.death.metal' => ['id' => 3, 'genre' => 'female.fronted.symphonic.death.metal'] + ]; + + $i = 0; + while ($i < self::TORRENTS) { + // Avoid rate limit of 25 albums per minute + sleep(2); + $album = $this->getRandomDiscogsAlbum(); + if (!property_exists($album, 'year') || (!empty($album->message) && $album->message === 'Release not found.') || $album->year == 0) { + continue; + } + $user_id = rand(1, $user_count); + $this->output->writeln("Found torrent {$i}..."); + + $artist = $album->artists[0]; + if (!isset($artists[$artist->name])) { + $artists[$artist->name] = [ + 'id' => count($artists) + 1, + 'obj' => $artist, + ]; + $insert_data['artists_group'][] = ['Name' => $artist->name]; + $insert_data['artists_alias'][] = ['ArtistID' => $artists[$artist->name]['id'], 'Name' => $artist->name]; + } + + foreach ($album->genres as $idx => $genre) { + $genre = str_replace(array(' ', '&'), array('.', ''), strtolower($genre)); + $album->genres[$idx] = $genre; + if (!isset($tags[$genre])) { + $insert_data['tags'][] = [ + 'Name' => $genre, + 'TagType' => 'genre', + 'Uses' => 1, + 'UserID' => $user_id + ]; + $tags[$genre] = ['id' => (count($tags) + 1), 'genre' => $genre, 'idx' => count($insert_data)]; + } + } + if (!isset($groups[$album->title])) { + $wiki_body = !empty($album->notes) ? $album->notes . "\n\n" : ''; + foreach ($album->tracklist as $track) { + $wiki_body .= "{$track->position}. {$track->title} ({$track->duration})\n"; + } + $insert_data['torrents_group'][] = [ + 'ArtistID' => 0, + 'CategoryID' => 1, + 'Name' => $album->title, + 'Year' => $album->year, + 'CatalogueNumber' => $album->labels[0]->catno, + 'RecordLabel' => $album->labels[0]->name, + 'Time' => '2018-03-22 02:24:19', + 'RevisionID' => count($groups) + 1, + 'WikiBody' => $wiki_body, + 'WikiImage' => '', + 'ReleaseType' => 1, + 'VanityHouse' => 0 + ]; + + $insert_data['wiki_torrents'][] = [ + 'PageID' => count($groups) + 1, + 'Body' => $wiki_body, + 'UserID' => $user_id, + 'Summary' => 'Uploaded new torrent', + 'Time' => '2018-03-22 02:24:19', + 'Image' => '' + ]; + + foreach ($album->genres as $genre) { + $insert_data['torrents_tags'][] = [ + 'TagID' => $tags[$genre]['id'], + 'GroupID' => count($groups) + 1, + 'PositiveVotes' => 10 + ]; + } + + $insert_data['torrents_artists'][] = [ + 'GroupID' => count($groups) + 1, + 'ArtistID' => $artists[$album->artists[0]->name]['id'], + 'AliasID' => $artists[$album->artists[0]->name]['id'], + 'UserID' => $user_id, + 'Importance' => 1 + ]; + $groups[$album->title] = ['id' => count($groups) + 1, 'album' => $album]; + } + + $media = ($album->formats[0]->name === 'Vinyl') ? 'Vinyl' : 'CD'; + + $torrent_id = count($insert_data['torrents']) + 1; + $files = []; + $file_list = []; + $delim = utf8_encode(chr(0xF7)); + foreach ($album->tracklist as $track) { + $length = rand(1, 45573573); + $name = "{$track->position}. {$track->title}.mp3"; + $files[] = ['length' => $length, 'path' => [$name]]; + $file_list[] = ".mp3 s{$length} {$name} {$delim}"; + } + $file_list = implode('\n', $file_list); + /** @noinspection PhpUnhandledExceptionInspection */ + $bencode->setData([ + 'announce' => 'https://localhost:34000/hash_pass/announce', + 'comment' => "https://localhost:8080//torrents.php?torrentid={$torrent_id}", + 'creation date' => 1489617624, + 'encoding' => 'UTF-8', + 'info' => [ + 'files' => $files, + 'name' => "{$album->artists[0]->name} - {$album->title} ({$album->year})", + 'piece length' => 262144, + 'pieces' => 'string of pieces' + ] + + ]); + $insert_data['torrents'][] = [ + 'GroupID' => $groups[$album->title]['id'], + 'UserID' => $user_id, + 'Media' => $media, + 'Format' => 'MP3', + 'Encoding' => '320', + 'Remastered' => 0, + 'RemasterYear' => 0, + 'RemasterTitle' => '', + 'RemasterRecordLabel' => '', + 'RemasterCatalogueNumber' => '', + 'Scene' => 0, + 'HasLog' => 0, + 'HasCue' => 0, + 'HasLogDB' => 0, + 'LogScore' => 100, + 'LogChecksum' => 1, + 'info_hash' => $bencode->getHexInfoHash(), + 'FileCount' => count($album->tracklist), + 'FileList' => $file_list, + 'FilePath' => "{$album->artists[0]->name} - {$album->title} ({$album->year})", + 'Size' => '253809759', + 'Time' => '2018-03-22 02:24:19', + 'Description' => '', + 'FreeTorrent' => 0, + 'FreeLeechType' => 0 + ]; + + $insert_data['torrents_files'][] = [ + 'TorrentID' => count($insert_data['torrents']) + 1, + 'File' => b'd8:announce65:https://localhost:34000/4f9587fbcb06fe09165e4f84d35d0403/announce7:comment53:https://localhost:8080//torrents.php?id=1&torrentid=113:creation datei1489617624e8:encoding5:UTF-84:infod5:filesld6:lengthi45573573e4:pathl19:01 - Nightmare.flaceed6:lengthi31675140e4:pathl31:02 - Welcome to the Family.flaceed6:lengthi36911187e4:pathl21:03 - Danger Line.flaceed6:lengthi47893264e4:pathl22:04 - Buried Alive.flaceed6:lengthi40839480e4:pathl29:05 - Natural Born Killer.flaceed6:lengthi40787176e4:pathl21:06 - So Far Away.flaceed6:lengthi34999093e4:pathl22:07 - God Hates Us.flaceed6:lengthi52152693e4:pathl16:08 - Victim.flaceed6:lengthi34464081e4:pathl32:09 - Tonight the World Dies.flaceed6:lengthi34485391e4:pathl17:10 - Fiction.flaceed6:lengthi81953099e4:pathl17:11 - Save Me.flaceed6:lengthi1100e4:pathl33:Avenged Sevenfold - Nightmare.cueeed6:lengthi12890e4:pathl33:Avenged Sevenfold - Nightmare.logeed6:lengthi66260e4:pathl10:folder.jpgeee4:name73:Avenged Sevenfold \xe2\x80\x8e- Nightmare (2010) [CD - FLAC - Lossless] {524026-2}12:piece lengthi262144e6:pieces19:fake torrent pieces7:privatei1e6:source3:APLee' + ]; + $i++; + } + + foreach ($insert_data as $table => $data) { + $this->table($table)->insert($data)->saveData(); + } + + $this->execute('UPDATE tags SET Uses = ( SELECT COUNT(*) FROM torrents_tags WHERE torrents_tags.TagID = tags.ID GROUP BY TagID)'); + } } diff --git a/design/privatefooter.php b/design/privatefooter.php index 6c4bced54..89f567dbc 100644 --- a/design/privatefooter.php +++ b/design/privatefooter.php @@ -1,44 +1,47 @@
      - - -
      - +
      + + +
      +perf_table(); $Debug->flag_table(); $Debug->error_table(); @@ -48,19 +51,19 @@ $Debug->vars_table(); $Debug->ocelot_table(); ?> -
      - - +
    + + - diff --git a/design/privateheader.php b/design/privateheader.php index da46ef600..85b0fe813 100644 --- a/design/privateheader.php +++ b/design/privateheader.php @@ -1,4 +1,4 @@ - - <?=display_str($PageTitle)?> - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - -_&user=&auth=&passkey=&authkey=&name=" + title=" - " /> + - - - - - - - - - - - - - - -&auth=&passkey=&authkey=" + title=" - All Torrents" /> + + + + + + + + + + + + + + - - - -styles/tooltipster/style.css?v=" type="text/css" media="screen" /> + + + - -" media="screen" + href="styles//style.css?v=" /> + - -" title="External CSS" /> + - -styles/opendyslexic/style.css?v=" /> + - " /> -" /> + - - + //"; + var userid = ; + //]]> + + - -functions/.js?v=" + type="text/javascript"> +get_notifications(); $UseNoty = $NotificationsManager->use_noty(); $NewSubscriptions = false; -$NotificationSpans = array(); +$NotificationSpans = []; foreach ($Notifications as $Type => $Notification) { - if ($Type === NotificationsManager::SUBSCRIPTIONS) { - $NewSubscriptions = true; - } - if ($UseNoty) { - $NotificationSpans[] = "$Notification[message]"; - } + if ($Type === NotificationsManager::SUBSCRIPTIONS) { + $NewSubscriptions = true; + } + if ($UseNoty) { + $NotificationSpans[] = "$Notification[message]"; + } } if ($UseNoty && !empty($NotificationSpans)) { - NotificationsManagerView::load_js(); + NotificationsManagerView::load_js(); } if ($NotificationsManager->is_skipped(NotificationsManager::SUBSCRIPTIONS)) { - $NewSubscriptions = Subscriptions::has_new_subscriptions(); + $NewSubscriptions = Subscriptions::has_new_subscriptions(); } $NavItems = Users::get_user_nav_items(G::$LoggedUser['ID']); ?> - - - - - -
    -

    - + -
    -
      -
    • - -
      - - - - -
      -
    • -
    • - -
      - accesskey="a" - spellcheck="false" autocomplete="off" - onfocus="if (this.value == 'Artists') { this.value = ''; }" - onblur="if (this.value == '') { this.value = 'Artists'; }" - value="Artists" placeholder="Artists" type="text" name="artistname" size="17" /> -
      -
    • -
    • - -
      - -
      -
    • -
    • - -
      - - -
      -
    • +
      +
        +
      • + +
        + + + + +
        +
      • +
      • + +
        + accesskey="a" + spellcheck="false" autocomplete="off" + onfocus="if (this.value == 'Artists') { this.value = ''; }" + onblur="if (this.value == '') { this.value = 'Artists'; }" + value="Artists" placeholder="Artists" type="text" name="artistname" size="17" /> +
        +
      • +
      • + +
        + +
        +
      • +
      • + +
        + + +
        +
      • -
      • - -
        - -
        -
      • -
      • - -
        - - -
        -
      • -
      -
      -
    -
    +
  • + +
    + +
    +
  • +
  • + +
    + + +
    +
  • + +
    +
    +
    diff --git a/design/publicfooter.php b/design/publicfooter.php index 42631f6b5..b85c21578 100644 --- a/design/publicfooter.php +++ b/design/publicfooter.php @@ -1,8 +1,8 @@ - - + + diff --git a/design/publicheader.php b/design/publicheader.php index 86ca164f9..195d15999 100644 --- a/design/publicheader.php +++ b/design/publicheader.php @@ -1,42 +1,45 @@ - - <?=display_str($PageTitle)?> - - - - - - - " rel="stylesheet" type="text/css" /> - - - - - - + <?=display_str($PageTitle)?> + + + + + + + " rel="stylesheet" type="text/css" /> + + + + + + - - + + + + + + + +
    -
    + + +
  • Recovery
  • + + + diff --git a/design/views/generic/reply/quickreply.php b/design/views/generic/reply/quickreply.php index 20dd6c07b..e44c96b58 100644 --- a/design/views/generic/reply/quickreply.php +++ b/design/views/generic/reply/quickreply.php @@ -5,13 +5,13 @@ * * To include it in a section use this example. - View::parse('generic/reply/quickreply.php', array( - 'InputTitle' => 'Post reply', - 'InputName' => 'thread', - 'InputID' => $ThreadID, - 'ForumID' => $ForumID, - 'TextareaCols' => 90 - )); + View::parse('generic/reply/quickreply.php', array( + 'InputTitle' => 'Post reply', + 'InputName' => 'thread', + 'InputID' => $ThreadID, + 'ForumID' => $ForumID, + 'TextareaCols' => 90 + )); * Note that InputName and InputID are the only required variables * They're used to construct the $_POST. @@ -27,136 +27,140 @@ * comments), add a key 'SubscribeBox' to the array passed to View::parse. * Example: - View::parse('generic/reply/quickreply.php', array( - 'InputTitle' => 'Post comment', - 'InputName' => 'groupid', - 'InputID' => $GroupID, - 'TextareaCols' => 65, - 'SubscribeBox' => true - )); + View::parse('generic/reply/quickreply.php', array( + 'InputTitle' => 'Post comment', + 'InputName' => 'groupid', + 'InputID' => $GroupID, + 'TextareaCols' => 65, + 'SubscribeBox' => true + )); */ - global $HeavyInfo, $UserSubscriptions, $ThreadInfo, $ForumsDoublePost, $Document; + global $HeavyInfo, $UserSubscriptions, $ThreadInfo, $ForumsDoublePost, $Document; - if (G::$LoggedUser['DisablePosting']) { - return; - } - if (!isset($TextareaCols)) { - $TextareaCols = 70; - } - if (!isset($TextareaRows)) { - $TextareaRows = 8; - } - if (!isset($InputAction)) { - $InputAction = 'reply'; - } - if (!isset($InputTitle)) { - $InputTitle = 'Post comment'; - } - if (!isset($Action)) { - $Action = ''; - } + if (G::$LoggedUser['DisablePosting']) { + return; + } + if (!isset($TextareaCols)) { + $TextareaCols = 70; + } + if (!isset($TextareaRows)) { + $TextareaRows = 8; + } + if (!isset($InputAction)) { + $InputAction = 'reply'; + } + if (!isset($InputTitle)) { + $InputTitle = 'Post comment'; + } + if (!isset($Action)) { + $Action = ''; + } - // TODO: Remove inline styles + // TODO: Remove inline styles - // Old to do? - // TODO: Preview, come up with a standard, make it look like post or just a - // block of formatted BBcode, but decide and write some proper XHTML + // Old to do? + // TODO: Preview, come up with a standard, make it look like post or just a + // block of formatted BBcode, but decide and write some proper XHTML - $ReplyText = new TEXTAREA_PREVIEW('body', 'quickpost', '', - $TextareaCols, $TextareaRows, false, false, true, array( - 'tabindex="1"', - 'onkeyup="resize(\'quickpost\')"' - )); + $ReplyText = new TEXTAREA_PREVIEW('body', 'quickpost', '', + $TextareaCols, $TextareaRows, false, false, true, array( + 'tabindex="1"', + 'onkeyup="resize(\'quickpost\')"' + )); ?> -
    -
    -

    -
    - - - - - - - - - - - - - - - - - -
    onsubmit="quickpostform.submit_button.disabled = true;"> - - - -
    -getBuffer(); +
    +
    +

    +
    + + + + + + + + + + + + + + + + + + onsubmit="quickpostform.submit_button.disabled = true;"> + + + +
    +getBuffer(); ?> -
    -
    -
    - +
    +
    + - tabindex="2" /> - - tabindex="2" /> + + - tabindex="2" /> - - tabindex="2" /> + +add(new DateInterval("PT1H")); - $TestDate = date_create(); - $Checked = ($PostDate >= $TestDate) ? "checked" : ""; + if ($ThreadInfo['LastPostAuthorID'] == G::$LoggedUser['ID']) { + // Test to see if the post was made an hour ago, if so, auto-check merge box + /** @noinspection PhpUnhandledExceptionInspection */ + $PostDate = date_create($ThreadInfo['LastPostTime'])->add(new DateInterval("PT1H")); + $TestDate = date_create(); + $Checked = ($PostDate >= $TestDate) ? "checked" : ""; ?> - /> - -/> + + - - + var storedTempTextarea = new StoreText('quickpost', 'quickpostform', ); + + - - -
    - -
    -
    + + +
    + +
    +
    diff --git a/design/views/generic/reply/staffpm.php b/design/views/generic/reply/staffpm.php index bbb2e0db1..62a323265 100644 --- a/design/views/generic/reply/staffpm.php +++ b/design/views/generic/reply/staffpm.php @@ -1,27 +1,30 @@ -
    -
    - -

    - -
    +
    + + +

    + +
    -

    - + -
    +
    - Send to: - + Send to: + - - - - -
    + + + + +
    diff --git a/design/views/generic/textarea/buttons.phtml b/design/views/generic/textarea/buttons.phtml index 6e06ff0dd..680c5d0f5 100644 --- a/design/views/generic/textarea/buttons.phtml +++ b/design/views/generic/textarea/buttons.phtml @@ -1,3 +1,3 @@
    - +
    diff --git a/design/views/generic/textarea/preview.phtml b/design/views/generic/textarea/preview.phtml index 2ba5e7030..7fe7febb7 100644 --- a/design/views/generic/textarea/preview.phtml +++ b/design/views/generic/textarea/preview.phtml @@ -1,4 +1,4 @@ diff --git a/design/views/generic/textarea/script_factory.phtml b/design/views/generic/textarea/script_factory.phtml index 5bcea0672..181ea1f08 100644 --- a/design/views/generic/textarea/script_factory.phtml +++ b/design/views/generic/textarea/script_factory.phtml @@ -1,4 +1,4 @@ diff --git a/design/views/generic/textarea/textarea.phtml b/design/views/generic/textarea/textarea.phtml index 9ca494abe..9912ba410 100644 --- a/design/views/generic/textarea/textarea.phtml +++ b/design/views/generic/textarea/textarea.phtml @@ -1,4 +1,4 @@
    - +
    diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..207bd6447 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,35 @@ +version: '3' + +services: + web: + build: . + ports: + - 8080:80 + depends_on: + - memcached + - mysql + volumes: + - .:/var/www + + memcached: + image: memcached:1.5-alpine + + mysql: + image: mariadb:10.3 + volumes: + - ./.docker/mysql/mysqld_sql_mode.cnf:/etc/mysql/conf.d/mysqld_sql_mode.cnf + - ./.docker/data/mysql:/var/lib/mysql + environment: + - MYSQL_DATABASE=gazelle + - MYSQL_USER=gazelle + - MYSQL_PASSWORD=password + - MYSQL_ROOT_PASSWORD=em%G9Lrey4^N + + sphinxsearch: + build: ./.docker/sphinxsearch + depends_on: + - mysql + - web + volumes: + - ./.docker/data/sphinxsearch:/var/lib/sphinxsearch/data/ + - ./.docker/sphinxsearch/sphinx.conf:/var/lib/sphinxsearch/conf/sphinx.conf diff --git a/docs/CodingStandards.txt b/docs/CodingStandards.txt index 23ebeea4a..846868772 100644 --- a/docs/CodingStandards.txt +++ b/docs/CodingStandards.txt @@ -36,30 +36,30 @@ step in properly enforcing coding standards throughout the project. == TABLE OF CONTENTS == -1. FILE FORMATTING -2. CODE STYLING -2.1 Code styling for PHP and JavaScript -2.2 Code styling for CSS -2.3 Code styling for SQL -3. NAMING CONVENTIONS -3.1 Function names -3.2 Variable names -3.3 Class names -3.4 SQL names -3.5 Miscellaneous names -4. COMMENTS -5. USER INTERFACE -6. EXAMPLES -6.1 PHP examples -6.2 CSS examples -6.3 SQL examples +1. FILE FORMATTING +2. CODE STYLING +2.1 Code styling for PHP and JavaScript +2.2 Code styling for CSS +2.3 Code styling for SQL +3. NAMING CONVENTIONS +3.1 Function names +3.2 Variable names +3.3 Class names +3.4 SQL names +3.5 Miscellaneous names +4. COMMENTS +5. USER INTERFACE +6. EXAMPLES +6.1 PHP examples +6.2 CSS examples +6.3 SQL examples # FILE FORMATTING Tabs shall always be used for indentation. -Files shall be encoded in ASCII. +Files shall be encoded in UTF-8. Files shall always use Unix-style line endings (LF). The program dos2unix will take care of this for you, if you have files that use Windows-style @@ -93,8 +93,10 @@ only one statement. In loops and conditional blocks, the statement(s) shall be placed on the following lines. -When opening a PHP statement, `= 0) { - $SomeString = "this is a string $DiffString with more text"; - } elseif ($Foo == 0) { - // other things happen - } else { - // more magic - } + if ($Foo >= 0) { + $SomeString = "this is a string $DiffString with more text"; + } elseif ($Foo == 0) { + // other things happen + } else { + // more magic + } - function works_magic($Foo, $Bar) { - return $Foo; - } + function works_magic($Foo, $Bar) { + return $Foo; + } - echo ($Foo == true ? 'foo is true' : 'foo is false'); + echo ($Foo == true ? 'foo is true' : 'foo is false'); - if ($Foo == true) { - // magic happens - } + if ($Foo == true) { + // magic happens + } - /* - * This is a good, multi-line comment. - * - * Please comment your code! - */ + /* + * This is a good, multi-line comment. + * + * Please comment your code! + */ - // This is a good, single-line comment. + // This is a good, single-line comment. ## CSS examples link text - .spellcheck { - margin: 10px 0; - font-size: 1.25em; - font-weight: bold; - } + .spellcheck { + margin: 10px 0; + font-size: 1.25em; + font-weight: bold; + } - .linkbox .brackets:before, - .linkbox .brackets:after, - .top10_quantity_links .brackets:before, - .top10_quantity_links .brackets:after { - color: #757575; - } + .linkbox .brackets:before, + .linkbox .brackets:after, + .top10_quantity_links .brackets:before, + .top10_quantity_links .brackets:after { + color: #757575; + } # SQL examples - SELECT - r.ID, e.EditionID, r.Title, r.Year, r.CatalogueNumber, - l.Name AS Label, r.LabelID, r.Image, r.MusicBrainzID - FROM releases AS r - JOIN editions AS e ON e.ReleaseID = r.ID - LEFT JOIN labels AS l ON l.ID = r.LabelID - WHERE r.ID IN ($ReleaseIDsSQL) - ORDER BY e.EditionID, r.Year ASC - - - SELECT SQL_CALC_FOUND_ROWS c.ID, c.Subject, cu.Unread, cu.Sticky, - cu.ForwardedTo, cu2.UserID, cu.ReceivedDate AS Date - FROM pm_conversations AS c - LEFT JOIN pm_conversations_users AS cu ON cu.ConvID = c.ID - AND cu.UserID = '1' - LEFT JOIN pm_conversations_users AS cu2 ON cu2.ConvID = c.ID - AND cu2.UserID != '1' - AND cu2.ForwardedTo = 0 - LEFT JOIN users_main AS um ON um.ID = cu2.UserID - WHERE um.Username LIKE 'test' - AND cu.InInbox = '1' - GROUP BY c.ID - ORDER BY cu.Sticky, Date DESC - LIMIT 25 - - - SELECT RequestID AS ID, UserID FROM bookmarks_requests + SELECT + r.ID, e.EditionID, r.Title, r.Year, r.CatalogueNumber, + l.Name AS Label, r.LabelID, r.Image, r.MusicBrainzID + FROM releases AS r + JOIN editions AS e ON e.ReleaseID = r.ID + LEFT JOIN labels AS l ON l.ID = r.LabelID + WHERE r.ID IN ($ReleaseIDsSQL) + ORDER BY e.EditionID, r.Year ASC + + + SELECT SQL_CALC_FOUND_ROWS c.ID, c.Subject, cu.Unread, cu.Sticky, + cu.ForwardedTo, cu2.UserID, cu.ReceivedDate AS Date + FROM pm_conversations AS c + LEFT JOIN pm_conversations_users AS cu ON cu.ConvID = c.ID + AND cu.UserID = '1' + LEFT JOIN pm_conversations_users AS cu2 ON cu2.ConvID = c.ID + AND cu2.UserID != '1' + AND cu2.ForwardedTo = 0 + LEFT JOIN users_main AS um ON um.ID = cu2.UserID + WHERE um.Username LIKE 'test' + AND cu.InInbox = '1' + GROUP BY c.ID + ORDER BY cu.Sticky, Date DESC + LIMIT 25 + + + SELECT RequestID AS ID, UserID FROM bookmarks_requests diff --git a/docs/INSTALL.txt b/docs/INSTALL.txt index e6fa5a4b7..638309a38 100644 --- a/docs/INSTALL.txt +++ b/docs/INSTALL.txt @@ -1,20 +1,20 @@ INSTALLATION NOTES 1. Set up MySQL and memcached. memcached can be started with the command: - memcached -d -m 5120 -s /var/run/memcached.sock -a 0777 -t4 -C -u nobody - This starts 4 threads and gives it 5 gigs of RAM + memcached -d -m 5120 -s /var/run/memcached.sock -a 0777 -t4 -C -u nobody + This starts 4 threads and gives it 5 gigs of RAM 2. Run gazelle.sql (preferably as root) to create the database, the table, and the default data. 3. Install Sphinx - we recommend you use the included sphinx.conf. You can copy this to /etc/sphinx/sphinx.conf. You need to fill in the details of the SQL server though! You might also need to create the /var/lib/sphinx folder. - For documentation, read http://www.sphinxsearch.com/docs/current.html + For documentation, read http://www.sphinxsearch.com/docs/current.html - After you've installed sphinx, create the indices: - /usr/bin/indexer -c /etc/sphinx/sphinx.conf --all + After you've installed sphinx, create the indices: + /usr/bin/indexer -c /etc/sphinx/sphinx.conf --all 4. Move classes/config.template to classes/config.php. Edit the config.php as needed. - We use http://grc.com/passwords.html for our passwords - you'll be generating a lot of these. + We use http://grc.com/passwords.html for our passwords - you'll be generating a lot of these. 5. Sign up. The first user is made a SysOp! 6. Set up cron jobs. You need a cron job for the schedule, a cron job for the peerupdate (all groups are cached, but the peer counts change often, @@ -27,8 +27,8 @@ These are our cron jobs. SCHEDULE_KEY is the same as in classes/config.php: 5 0,12 * * * /usr/bin/indexer -c /etc/sphinx/sphinx.conf --rotate --all >>/root/sphinx-indexer.log 7. You're probably going to want IP geolocation information, so first you need to fill in the geoip_country tables by visiting /tools.php?action=update_geoip - After that finishes parsing information from MaxMind, you may want to map users to countries by running: - "INSERT INTO users_geodistribution (Code, Users) SELECT g.Code, COUNT(u.ID) AS Users FROM geoip_country AS g JOIN users_main AS u ON INET_ATON(u.IP) BETWEEN g.StartIP AND g.EndIP WHERE u.Enabled = '1' GROUP BY g.Code ORDER BY Users DESC" - This will fill in the table needed for stats. + After that finishes parsing information from MaxMind, you may want to map users to countries by running: + "INSERT INTO users_geodistribution (Code, Users) SELECT g.Code, COUNT(u.ID) AS Users FROM geoip_country AS g JOIN users_main AS u ON INET_ATON(u.IP) BETWEEN g.StartIP AND g.EndIP WHERE u.Enabled = '1' GROUP BY g.Code ORDER BY Users DESC" + This will fill in the table needed for stats. 8. Start modifying stuff. Hopefully, everything will have gone smoothly so far and nothing will have exploded (ha ha ha) diff --git a/feeds.php b/feeds.php index 22a4d163c..fa8406d5f 100644 --- a/feeds.php +++ b/feeds.php @@ -1,14 +1,14 @@ ", - '€','‚','ƒ','„','…','†','‡','ˆ', - '‰','Š','‹','Œ','Ž','‘','’','“', - '”','•','–','—','˜','™','š','›', - 'œ','ž','Ÿ' - ); + $Replace = array( + "'",'"',"<",">", + '€','‚','ƒ','„','…','†','‡','ˆ', + '‰','Š','‹','Œ','Ž','‘','’','“', + '”','•','–','—','˜','™','š','›', + 'œ','ž','Ÿ' + ); - $With = array( - ''','"','<','>', - '€','‚','ƒ','„','…','†','‡','ˆ', - '‰','Š','‹','Œ','Ž','‘','’','“', - '”','•','–','—','˜','™','š','›', - 'œ','ž','Ÿ' - ); + $With = array( + ''','"','<','>', + '€','‚','ƒ','„','…','†','‡','ˆ', + '‰','Š','‹','Œ','Ž','‘','’','“', + '”','•','–','—','˜','™','š','›', + 'œ','ž','Ÿ' + ); - $Str = str_replace($Replace, $With, $Str); - } - return $Str; + $Str = str_replace($Replace, $With, $Str); + } + return $Str; } function make_utf8($Str) { - if ($Str != '') { - if (is_utf8($Str)) { - $Encoding = 'UTF-8'; - } - if (empty($Encoding)) { - $Encoding = mb_detect_encoding($Str, 'UTF-8, ISO-8859-1'); - } - if (empty($Encoding)) { - $Encoding = 'ISO-8859-1'; - } - if ($Encoding == 'UTF-8') { - return $Str; - } else { - return @mb_convert_encoding($Str, 'UTF-8', $Encoding); - } - } + if ($Str != '') { + if (is_utf8($Str)) { + $Encoding = 'UTF-8'; + } + if (empty($Encoding)) { + $Encoding = mb_detect_encoding($Str, 'UTF-8, ISO-8859-1'); + } + if (empty($Encoding)) { + $Encoding = 'ISO-8859-1'; + } + if ($Encoding == 'UTF-8') { + return $Str; + } else { + return @mb_convert_encoding($Str, 'UTF-8', $Encoding); + } + } } function is_utf8($Str) { - return preg_match('%^(?: + return preg_match('%^(?: [\x09\x0A\x0D\x20-\x7E] // ASCII | [\xC2-\xDF][\x80-\xBF] // non-overlong 2-byte | \xE0[\xA0-\xBF][\x80-\xBF] // excluding overlongs @@ -88,16 +88,16 @@ function is_utf8($Str) { | [\xF1-\xF3][\x80-\xBF]{3} // planes 4-15 | \xF4[\x80-\x8F][\x80-\xBF]{2} // plane 16 )*$%xs', $Str - ); + ); } -function display_array($Array, $Escape = array()) { - foreach ($Array as $Key => $Val) { - if ((!is_array($Escape) && $Escape == true) || !in_array($Key, $Escape)) { - $Array[$Key] = display_str($Val); - } - } - return $Array; +function display_array($Array, $Escape = []) { + foreach ($Array as $Key => $Val) { + if ((!is_array($Escape) && $Escape == true) || !in_array($Key, $Escape)) { + $Array[$Key] = display_str($Val); + } + } + return $Array; } /** @@ -106,7 +106,7 @@ function display_array($Array, $Escape = array()) { * @param bool $SSL - whether the URL should be crafted for HTTPS or regular HTTP */ function site_url($SSL = true) { - return $SSL ? 'https://' . SSL_SITE_URL . '/' : 'http://' . NONSSL_SITE_URL . '/'; + return $SSL ? 'https://' . SSL_SITE_URL . '/' : 'http://' . NONSSL_SITE_URL . '/'; } header('Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0'); diff --git a/image.php b/image.php index 66159121e..264d857ab 100644 --- a/image.php +++ b/image.php @@ -3,98 +3,98 @@ error_reporting(E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR); if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { - header("HTTP/1.1 304 Not Modified"); - die(); + header("HTTP/1.1 304 Not Modified"); + die(); } header('Expires: '.date('D, d-M-Y H:i:s \U\T\C', time() + 3600 * 24 * 120)); // 120 days header('Last-Modified: '.date('D, d-M-Y H:i:s \U\T\C', time())); if (!extension_loaded('gd')) { - error('nogd'); + error('nogd'); } function img_error($Type) { - header('Content-type: image/gif'); - die(file_get_contents(SERVER_ROOT.'/sections/image/'.$Type.'.gif')); + header('Content-type: image/gif'); + die(file_get_contents(SERVER_ROOT.'/sections/image/'.$Type.'.gif')); } function invisible($Image) { - $Count = imagecolorstotal($Image); - if ($Count == 0) { - return false; - } - $TotalAlpha = 0; - for ($i = 0; $i < $Count; ++$i) { - $Color = imagecolorsforindex($Image, $i); - $TotalAlpha += $Color['alpha']; - } - return (($TotalAlpha / $Count) == 127) ? true : false; + $Count = imagecolorstotal($Image); + if ($Count == 0) { + return false; + } + $TotalAlpha = 0; + for ($i = 0; $i < $Count; ++$i) { + $Color = imagecolorsforindex($Image, $i); + $TotalAlpha += $Color['alpha']; + } + return (($TotalAlpha / $Count) == 127) ? true : false; } function verysmall($Image) { - return ((imagesx($Image) * imagesy($Image)) < 25) ? true : false; + return ((imagesx($Image) * imagesy($Image)) < 25) ? true : false; } function image_type($Data) { - if (!strncmp($Data, 'GIF', 3)) { - return 'gif'; - } - if (!strncmp($Data, pack('H*', '89504E47'), 4)) { - return 'png'; - } - if (!strncmp($Data, pack('H*', 'FFD8'), 2)) { - return 'jpeg'; - } - if (!strncmp($Data, 'BM', 2)) { - return 'bmp'; - } - if (!strncmp($Data, 'II', 2) || !strncmp($Data, 'MM', 2)) { - return 'tiff'; - } + if (!strncmp($Data, 'GIF', 3)) { + return 'gif'; + } + if (!strncmp($Data, pack('H*', '89504E47'), 4)) { + return 'png'; + } + if (!strncmp($Data, pack('H*', 'FFD8'), 2)) { + return 'jpeg'; + } + if (!strncmp($Data, 'BM', 2)) { + return 'bmp'; + } + if (!strncmp($Data, 'II', 2) || !strncmp($Data, 'MM', 2)) { + return 'tiff'; + } } function image_height($Type, $Data) { - $Length = strlen($Data); - global $URL, $_GET; - switch ($Type) { - case 'jpeg': - // See http://www.obrador.com/essentialjpeg/headerinfo.htm - $i = 4; - $Data = (substr($Data, $i)); - $Block = unpack('nLength', $Data); - $Data = substr($Data, $Block['Length']); - $i += $Block['Length']; - $Str []= "Started 4, + ".$Block['Length']; - while ($Data != '') { // iterate through the blocks until we find the start of frame marker (FFC0) - $Block = unpack('CBlock/CType/nLength', $Data); // Get info about the block - if ($Block['Block'] != '255') { // We should be at the start of a new block - break; - } - if ($Block['Type'] != '192') { // C0 - $Data = substr($Data, $Block['Length'] + 2); // Next block - $Str []= "Started $i, + ".($Block['Length'] + 2); - $i += ($Block['Length'] + 2); - } else { // We're at the FFC0 block - $Data = substr($Data, 5); // Skip FF C0 Length(2) precision(1) - $i += 5; - $Height = unpack('nHeight', $Data); - return $Height['Height']; - } - } - break; - case 'gif': - $Data = substr($Data, 8); - $Height = unpack('vHeight', $Data); - return $Height['Height']; - case 'png': - $Data = substr($Data, 20); - $Height = unpack('NHeight', $Data); - return $Height['Height']; - default: - return 0; - } + $Length = strlen($Data); + global $URL, $_GET; + switch ($Type) { + case 'jpeg': + // See http://www.obrador.com/essentialjpeg/headerinfo.htm + $i = 4; + $Data = (substr($Data, $i)); + $Block = unpack('nLength', $Data); + $Data = substr($Data, $Block['Length']); + $i += $Block['Length']; + $Str []= "Started 4, + ".$Block['Length']; + while ($Data != '') { // iterate through the blocks until we find the start of frame marker (FFC0) + $Block = unpack('CBlock/CType/nLength', $Data); // Get info about the block + if ($Block['Block'] != '255') { // We should be at the start of a new block + break; + } + if ($Block['Type'] != '192') { // C0 + $Data = substr($Data, $Block['Length'] + 2); // Next block + $Str []= "Started $i, + ".($Block['Length'] + 2); + $i += ($Block['Length'] + 2); + } else { // We're at the FFC0 block + $Data = substr($Data, 5); // Skip FF C0 Length(2) precision(1) + $i += 5; + $Height = unpack('nHeight', $Data); + return $Height['Height']; + } + } + break; + case 'gif': + $Data = substr($Data, 8); + $Height = unpack('vHeight', $Data); + return $Height['Height']; + case 'png': + $Data = substr($Data, 20); + $Height = unpack('NHeight', $Data); + return $Height['Height']; + default: + return 0; + } } define('SKIP_NO_CACHE_HEADERS', 1); diff --git a/index.php b/index.php index 8fc0ff80e..e257b3749 100644 --- a/index.php +++ b/index.php @@ -1,90 +1,91 @@ 'torrents.php', - 'collage' => 'collages.php', - 'signup' => 'register.php', - 'whitelist' => 'rules.php?p=clients', - 'forum' => 'forums.php', - 'randomcollage' => 'random.php?action=collage' + 'browse' => 'torrents.php', + 'collage' => 'collages.php', + 'signup' => 'register.php', + 'whitelist' => 'rules.php?p=clients', + 'forum' => 'forums.php', + 'randomcollage' => 'random.php?action=collage' ]; $PathInfo = pathinfo($_SERVER['SCRIPT_NAME']); $Document = $PathInfo['filename']; if ($PathInfo['dirname'] !== '/') { - header("Location: /index.php"); + header("Location: /index.php"); } elseif (isset($Redirects[$Document])) { - $Seperator = (strpos($Redirects[$Document], "?") === false) ? "?" : "&"; - $Rest = (!empty($_SERVER['QUERY_STRING'])) ? $Seperator.$_SERVER['QUERY_STRING'] : ""; - header("Location: {$Redirects[$Document]}{$Rest}"); + $Seperator = (strpos($Redirects[$Document], "?") === false) ? "?" : "&"; + $Rest = (!empty($_SERVER['QUERY_STRING'])) ? $Seperator.$_SERVER['QUERY_STRING'] : ""; + header("Location: {$Redirects[$Document]}{$Rest}"); } elseif (in_array($Document, ['announce', 'scrape'])) { - echo "d14:failure reason40:Invalid .torrent, try downloading again.e"; - die(); + echo "d14:failure reason40:Invalid .torrent, try downloading again.e"; + die(); } $Valid = false; switch ($Document) { - case 'peerupdate': - /** @noinspection PhpMissingBreakStatementInspection */ - case 'schedule': - define('MEMORY_EXCEPTION', true); - define('TIME_EXCEPTION', true); - case 'artist': - case 'better': - case 'bookmarks': - case 'collages': - case 'comments': - case 'forums': - case 'friends': - case 'torrents': - case 'upload': - case 'user': - case 'userhistory': - /** @noinspection PhpMissingBreakStatementInspection */ - case 'wiki': - define('ERROR_EXCEPTION', true); - case 'ajax': - case 'apply': - case 'blog': - case 'bonus': - case 'captcha': - case 'chat': - case 'contest': - case 'donate': - case 'enable': - case 'error': - case 'inbox': - case 'index': - case 'irc': - case 'locked': - case 'log': - case 'logchecker': - case 'login': - case 'logout': - case 'questions': - case 'random': - case 'referral': - case 'register': - case 'reports': - case 'reportsv2': - case 'requests': - case 'rules': - case 'signup': - case 'sitehistory': - case 'staff': - case 'staffblog': - case 'staffpm': - case 'stats': - case 'top10': - $Valid = true; - break; + case 'peerupdate': + /** @noinspection PhpMissingBreakStatementInspection */ + case 'schedule': + define('MEMORY_EXCEPTION', true); + define('TIME_EXCEPTION', true); + case 'artist': + case 'better': + case 'bookmarks': + case 'collages': + case 'comments': + case 'forums': + case 'friends': + case 'torrents': + case 'upload': + case 'user': + case 'userhistory': + /** @noinspection PhpMissingBreakStatementInspection */ + case 'wiki': + define('ERROR_EXCEPTION', true); + case 'ajax': + case 'apply': + case 'blog': + case 'bonus': + case 'captcha': + case 'chat': + case 'contest': + case 'donate': + case 'enable': + case 'error': + case 'inbox': + case 'index': + case 'irc': + case 'locked': + case 'log': + case 'logchecker': + case 'login': + case 'logout': + case 'questions': + case 'random': + case 'recovery': + case 'referral': + case 'register': + case 'reports': + case 'reportsv2': + case 'requests': + case 'rules': + case 'signup': + case 'sitehistory': + case 'staff': + case 'staffblog': + case 'staffpm': + case 'stats': + case 'top10': + $Valid = true; + break; } if (!$Valid) { - $_SERVER['SCRIPT_NAME'] = 'error.php'; - $_SERVER['SCRIPT_FILENAME'] = 'error.php'; - $Error = 404; + $_SERVER['SCRIPT_NAME'] = 'error.php'; + $_SERVER['SCRIPT_FILENAME'] = 'error.php'; + $Error = 404; } require('classes/script_start.php'); diff --git a/opensearch.php b/opensearch.php index b0a2c5a1b..1b05d7fe2 100644 --- a/opensearch.php +++ b/opensearch.php @@ -15,58 +15,58 @@ echo ''; ?> - - Search for - - http:///favicon.ico + + Search for + + http:///favicon.ico - - http:///torrents.php?action=advanced + + http:///torrents.php?action=advanced - - http:///torrents.php + + http:///torrents.php - - http:///requests.php + + http:///requests.php - - http:///forums.php?action=search + + http:///forums.php?action=search - - http:///user.php?action=search + + http:///user.php?action=search - - http:///wiki.php?action=search + + http:///wiki.php?action=search - - http:///log.php + + http:///log.php - - en-us - UTF-8 - UTF-8 + + en-us + UTF-8 + UTF-8 diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..05da611c7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2122 @@ +{ + "name": "gazelle", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@samverschueren/stream-to-observable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz", + "integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==", + "dev": true, + "requires": { + "any-observable": "^0.3.0" + } + }, + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "dev": true + }, + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "12.7.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.11.tgz", + "integrity": "sha512-Otxmr2rrZLKRYIybtdG/sgeO+tHY20GxeDjcGmUnmmlCWyEnv2a2x1ZXBo3BTec4OiTXMQCiazB8NMBf0iRlFw==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "acorn": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", + "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", + "dev": true + }, + "acorn-jsx": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz", + "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==", + "dev": true + }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "any-observable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", + "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + }, + "dependencies": { + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + } + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-truncate": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", + "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", + "dev": true, + "requires": { + "slice-ansi": "0.0.4", + "string-width": "^1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz", + "integrity": "sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "dependencies": { + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + } + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "date-fns": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", + "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "del": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-5.1.0.tgz", + "integrity": "sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==", + "dev": true, + "requires": { + "globby": "^10.0.1", + "graceful-fs": "^4.2.2", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.1", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0" + }, + "dependencies": { + "rimraf": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "elegant-spinner": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", + "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.5.1.tgz", + "integrity": "sha512-32h99BoLYStT1iq1v2P9uwpyznQ4M2jRiFB6acitKz52Gqn+vPaMDUTB1bYi1WN4Nquj2w+t+bimYUG83DC55A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.2", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.4.1", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + } + }, + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", + "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.0.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "espree": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.1.tgz", + "integrity": "sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ==", + "dev": true, + "requires": { + "acorn": "^7.0.0", + "acorn-jsx": "^5.0.2", + "eslint-visitor-keys": "^1.1.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.0.tgz", + "integrity": "sha512-TrUz3THiq2Vy3bjfQUB2wNyPdGBeGmdjbzzBLhfHN4YFurYptCKwGq/TfiRavbGywFRzY6U2CdmQ1zmsY5yYaw==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2" + } + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastq": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz", + "integrity": "sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA==", + "dev": true, + "requires": { + "reusify": "^1.0.0" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.1.tgz", + "integrity": "sha512-09/VS4iek66Dh2bctjRkowueRJbY1JDGR1L/zRxO1Qk8Uxs6PnqaNSqalpizPT+CDjre3hnEsuzvhgomz9qYrA==", + "dev": true + }, + "get-stdin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", + "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globby": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", + "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "dev": true + } + } + }, + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", + "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", + "dev": true + }, + "husky": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/husky/-/husky-3.0.8.tgz", + "integrity": "sha512-HFOsgcyrX3qe/rBuqyTt+P4Gxn5P0seJmr215LAZ/vnwK3jWB3r0ck7swbzGRUbufCf9w/lgHPVbF/YXQALgfQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "cosmiconfig": "^5.2.1", + "execa": "^1.0.0", + "get-stdin": "^7.0.0", + "is-ci": "^2.0.0", + "opencollective-postinstall": "^2.0.2", + "pkg-dir": "^4.2.0", + "please-upgrade-node": "^3.2.0", + "read-pkg": "^5.1.1", + "run-node": "^1.0.0", + "slash": "^3.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", + "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-observable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", + "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", + "dev": true, + "requires": { + "symbol-observable": "^1.1.0" + } + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "lint-staged": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-9.4.1.tgz", + "integrity": "sha512-zFRbo1bAJEVf1m33paTTjDVfy2v3lICCqHfmQSgNoI+lWpi7HPG5y/R2Y7Whdce+FKxlZYs/U1sDSx8+nmQdDA==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "commander": "^2.20.0", + "cosmiconfig": "^5.2.1", + "debug": "^4.1.1", + "dedent": "^0.7.0", + "del": "^5.0.0", + "execa": "^2.0.3", + "listr": "^0.14.3", + "log-symbols": "^3.0.0", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "please-upgrade-node": "^3.1.1", + "string-argv": "^0.3.0", + "stringify-object": "^3.3.0" + }, + "dependencies": { + "execa": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/execa/-/execa-2.0.5.tgz", + "integrity": "sha512-SwmwZZyJjflcqLSgllk4EQlMLst2p9muyzwNugKGFlpAz6rZ7M+s2nBR97GAq4Vzjwx2y9rcMcmqzojwN+xwNA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.5", + "get-stream": "^5.0.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^3.0.0", + "onetime": "^5.1.0", + "p-finally": "^2.0.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "npm-run-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz", + "integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "p-finally": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", + "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", + "dev": true + }, + "path-key": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.0.tgz", + "integrity": "sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg==", + "dev": true + } + } + }, + "listr": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", + "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", + "dev": true, + "requires": { + "@samverschueren/stream-to-observable": "^0.3.0", + "is-observable": "^1.1.0", + "is-promise": "^2.1.0", + "is-stream": "^1.1.0", + "listr-silent-renderer": "^1.1.1", + "listr-update-renderer": "^0.5.0", + "listr-verbose-renderer": "^0.5.0", + "p-map": "^2.0.0", + "rxjs": "^6.3.3" + }, + "dependencies": { + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + } + } + }, + "listr-silent-renderer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", + "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", + "dev": true + }, + "listr-update-renderer": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", + "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "elegant-spinner": "^1.0.1", + "figures": "^1.7.0", + "indent-string": "^3.0.0", + "log-symbols": "^1.0.2", + "log-update": "^2.3.0", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "dev": true, + "requires": { + "chalk": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "listr-verbose-renderer": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", + "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "cli-cursor": "^2.1.0", + "date-fns": "^1.27.2", + "figures": "^2.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2" + } + }, + "log-update": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", + "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "cli-cursor": "^2.0.0", + "wrap-ansi": "^3.0.1" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "opencollective-postinstall": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz", + "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==", + "dev": true + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", + "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "parse-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" + } + } + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/run-node/-/run-node-1.0.0.tgz", + "integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==", + "dev": true + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, + "rxjs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", + "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrap-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", + "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..1985a4bfb --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "gazelle", + "version": "0.0.0", + "description": "", + "scripts": { + "lint:js": "eslint static/functions", + "lint:php:internal": "find . -path './vendor' -prune -o -path ./node_modules -prune -o -type f -name '*.php' -print0 | xargs -0 -n1 -P4 php -l -n | (! grep -v \"No syntax errors detected\" )", + "lint:php:phpcs": "vendor/bin/phpcs -p", + "lint:php": "npm run lint:php:internal && npm run lint:php:phpcs", + "lint:php:fix": "./.bin/phpcbf", + "pre-commit": "npm run lint:php:fix" + }, + "license": "Unlicense", + "devDependencies": { + "eslint": "^6.5.1", + "husky": "^3.0.8", + "lint-staged": "^9.4.1" + }, + "dependencies": {}, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "*.php": [ + "php -l", + "bash .bin/phpcbf", + "vendor/bin/phpcs", + "git add" + ] + } +} diff --git a/phinx.php b/phinx.php index c645f42c3..4920fd5a3 100644 --- a/phinx.php +++ b/phinx.php @@ -4,31 +4,22 @@ return [ - 'paths' => [ - 'migrations' => '%%PHINX_CONFIG_DIR%%/db/migrations', - 'seeds' => '%%PHINX_CONFIG_DIR%%/db/seeds' - ], - 'environments' => [ - 'default_migration_table' => 'phinxlog', - 'default_database' => 'gazelle', - 'gazelle' => [ - 'adapter' => 'mysql', - 'host' => SQLHOST, - 'name' => SQLDB, - 'user' => SQLLOGIN, - 'pass' => SQLPASS, - 'port' => SQLPORT, - 'charset' => 'utf8' - ], - 'vagrant_external' => [ - 'adapter' => 'mysql', - 'host' => '127.0.0.1', - 'name' => 'gazelle', - 'user' => 'gazelle', - 'pass' => 'password', - 'port' => 36000, - 'charset' => 'utf8' - ] - ], - 'version_order' => 'creation' + 'paths' => [ + 'migrations' => '%%PHINX_CONFIG_DIR%%/db/migrations', + 'seeds' => '%%PHINX_CONFIG_DIR%%/db/seeds' + ], + 'environments' => [ + 'default_migration_table' => 'phinxlog', + 'default_database' => 'gazelle', + 'gazelle' => [ + 'adapter' => 'mysql', + 'host' => SQLHOST, + 'name' => SQLDB, + 'user' => SQL_PHINX_USER, + 'pass' => SQL_PHINX_PASS, + 'port' => SQLPORT, + 'charset' => 'utf8' + ] + ], + 'version_order' => 'creation' ]; diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 000000000..6e3f78385 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,28 @@ + + + The standard for writing code in gazelle + + + + + . + */vendor/* + */node_modules/* + */static/* + + + + + + + + + + + + + diff --git a/phpunit.xml b/phpunit.xml index 77e1ef797..c6eb315d4 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -16,4 +16,4 @@ - \ No newline at end of file + diff --git a/sections/ajax/announcements.php b/sections/ajax/announcements.php index 586b1097f..83705667a 100644 --- a/sections/ajax/announcements.php +++ b/sections/ajax/announcements.php @@ -1,83 +1,83 @@ -get_value('news')) { - $DB->query(" - SELECT - ID, - Title, - Body, - Time - FROM news - ORDER BY Time DESC - LIMIT 5"); - $News = $DB->to_array(false, MYSQLI_NUM, false); - $Cache->cache_value('news', $News, 3600 * 24 * 30); - $Cache->cache_value('news_latest_id', $News[0][0], 0); + $DB->query(" + SELECT + ID, + Title, + Body, + Time + FROM news + ORDER BY Time DESC + LIMIT 5"); + $News = $DB->to_array(false, MYSQLI_NUM, false); + $Cache->cache_value('news', $News, 3600 * 24 * 30); + $Cache->cache_value('news_latest_id', $News[0][0], 0); } if ($LoggedUser['LastReadNews'] != $News[0][0]) { - $Cache->begin_transaction("user_info_heavy_$UserID"); - $Cache->update_row(false, array('LastReadNews' => $News[0][0])); - $Cache->commit_transaction(0); - $DB->query(" - UPDATE users_info - SET LastReadNews = '".$News[0][0]."' - WHERE UserID = $UserID"); - $LoggedUser['LastReadNews'] = $News[0][0]; + $Cache->begin_transaction("user_info_heavy_$UserID"); + $Cache->update_row(false, array('LastReadNews' => $News[0][0])); + $Cache->commit_transaction(0); + $DB->query(" + UPDATE users_info + SET LastReadNews = '".$News[0][0]."' + WHERE UserID = $UserID"); + $LoggedUser['LastReadNews'] = $News[0][0]; } if (($Blog = $Cache->get_value('blog')) === false) { - $DB->query(" - SELECT - b.ID, - um.Username, - b.UserID, - b.Title, - b.Body, - b.Time, - b.ThreadID - FROM blog AS b - LEFT JOIN users_main AS um ON b.UserID = um.ID - ORDER BY Time DESC - LIMIT 20"); - $Blog = $DB->to_array(); - $Cache->cache_value('blog', $Blog, 1209600); + $DB->query(" + SELECT + b.ID, + um.Username, + b.UserID, + b.Title, + b.Body, + b.Time, + b.ThreadID + FROM blog AS b + LEFT JOIN users_main AS um ON b.UserID = um.ID + ORDER BY Time DESC + LIMIT 20"); + $Blog = $DB->to_array(); + $Cache->cache_value('blog', $Blog, 1209600); } -$JsonBlog = array(); +$JsonBlog = []; for ($i = 0; $i < 5; $i++) { - list($BlogID, $Author, $AuthorID, $Title, $Body, $BlogTime, $ThreadID) = $Blog[$i]; - $JsonBlog[] = array( - 'blogId' => (int)$BlogID, - 'author' => $Author, - 'title' => $Title, - 'bbBody' => $Body, - 'body' => Text::full_format($Body), - 'blogTime' => $BlogTime, - 'threadId' => (int)$ThreadID - ); + list($BlogID, $Author, $AuthorID, $Title, $Body, $BlogTime, $ThreadID) = $Blog[$i]; + $JsonBlog[] = array( + 'blogId' => (int)$BlogID, + 'author' => $Author, + 'title' => $Title, + 'bbBody' => $Body, + 'body' => Text::full_format($Body), + 'blogTime' => $BlogTime, + 'threadId' => (int)$ThreadID + ); } -$JsonAnnouncements = array(); +$JsonAnnouncements = []; $Count = 0; foreach ($News as $NewsItem) { - list($NewsID, $Title, $Body, $NewsTime) = $NewsItem; - if (strtotime($NewsTime) > time()) { - continue; - } + list($NewsID, $Title, $Body, $NewsTime) = $NewsItem; + if (strtotime($NewsTime) > time()) { + continue; + } - $JsonAnnouncements[] = array( - 'newsId' => (int)$NewsID, - 'title' => $Title, - 'bbBody' => $Body, - 'body' => Text::full_format($Body), - 'newsTime' => $NewsTime - ); + $JsonAnnouncements[] = array( + 'newsId' => (int)$NewsID, + 'title' => $Title, + 'bbBody' => $Body, + 'body' => Text::full_format($Body), + 'newsTime' => $NewsTime + ); - if (++$Count > 4) { - break; - } + if (++$Count > 4) { + break; + } } json_print("success", array( - 'announcements' => $JsonAnnouncements, - 'blogPosts' => $JsonBlog + 'announcements' => $JsonAnnouncements, + 'blogPosts' => $JsonBlog )); diff --git a/sections/ajax/artist.php b/sections/ajax/artist.php index e882abb81..276ee0eba 100644 --- a/sections/ajax/artist.php +++ b/sections/ajax/artist.php @@ -1,366 +1,366 @@ -query(" - SELECT ArtistID - FROM artists_alias - WHERE Name LIKE '$Name'"); - if (!(list($ArtistID) = $DB->next_record(MYSQLI_NUM, false))) { - json_die("failure"); - } - // If we get here, we got the ID! - } + if (!empty($_GET['artistname'])) { + $Name = db_string(trim($_GET['artistname'])); + $DB->query(" + SELECT ArtistID + FROM artists_alias + WHERE Name LIKE '$Name'"); + if (!(list($ArtistID) = $DB->next_record(MYSQLI_NUM, false))) { + json_die("failure"); + } + // If we get here, we got the ID! + } } if (!empty($_GET['revisionid'])) { // if they're viewing an old revision - $RevisionID = $_GET['revisionid']; - if (!is_number($RevisionID)) { - error(0); - } - $Data = $Cache->get_value("artist_$ArtistID"."_revision_$RevisionID"); + $RevisionID = $_GET['revisionid']; + if (!is_number($RevisionID)) { + error(0); + } + $Data = $Cache->get_value("artist_$ArtistID"."_revision_$RevisionID"); } else { // viewing the live version - $Data = $Cache->get_value("artist_$ArtistID"); - $RevisionID = false; + $Data = $Cache->get_value("artist_$ArtistID"); + $RevisionID = false; } if ($Data) { - list($K, list($Name, $Image, $Body, $NumSimilar, $SimilarArray, , , $VanityHouseArtist)) = each($Data); + list($K, list($Name, $Image, $Body, $NumSimilar, $SimilarArray, , , $VanityHouseArtist)) = each($Data); } else { - if ($RevisionID) { - $sql = " - SELECT - a.Name, - wiki.Image, - wiki.body, - a.VanityHouse - FROM wiki_artists AS wiki - LEFT JOIN artists_group AS a ON wiki.RevisionID = a.RevisionID - WHERE wiki.RevisionID = '$RevisionID' "; - } else { - $sql = " - SELECT - a.Name, - wiki.Image, - wiki.body, - a.VanityHouse - FROM artists_group AS a - LEFT JOIN wiki_artists AS wiki ON wiki.RevisionID = a.RevisionID - WHERE a.ArtistID = '$ArtistID' "; - } - $sql .= " GROUP BY a.ArtistID"; - $DB->query($sql); + if ($RevisionID) { + $sql = " + SELECT + a.Name, + wiki.Image, + wiki.body, + a.VanityHouse + FROM wiki_artists AS wiki + LEFT JOIN artists_group AS a ON wiki.RevisionID = a.RevisionID + WHERE wiki.RevisionID = '$RevisionID' "; + } else { + $sql = " + SELECT + a.Name, + wiki.Image, + wiki.body, + a.VanityHouse + FROM artists_group AS a + LEFT JOIN wiki_artists AS wiki ON wiki.RevisionID = a.RevisionID + WHERE a.ArtistID = '$ArtistID' "; + } + $sql .= " GROUP BY a.ArtistID"; + $DB->query($sql); - if (!$DB->has_results()) { - json_die("failure"); - } + if (!$DB->has_results()) { + json_die("failure"); + } - list($Name, $Image, $Body, $VanityHouseArtist) = $DB->next_record(MYSQLI_NUM, array(0)); + list($Name, $Image, $Body, $VanityHouseArtist) = $DB->next_record(MYSQLI_NUM, array(0)); } // Requests -$Requests = array(); +$Requests = []; if (empty($LoggedUser['DisableRequests'])) { - $Requests = $Cache->get_value("artists_requests_$ArtistID"); - if (!is_array($Requests)) { - $DB->query(" - SELECT - r.ID, - r.CategoryID, - r.Title, - r.Year, - r.TimeAdded, - COUNT(rv.UserID) AS Votes, - SUM(rv.Bounty) AS Bounty - FROM requests AS r - LEFT JOIN requests_votes AS rv ON rv.RequestID = r.ID - LEFT JOIN requests_artists AS ra ON r.ID = ra.RequestID - WHERE ra.ArtistID = $ArtistID - AND r.TorrentID = 0 - GROUP BY r.ID - ORDER BY Votes DESC"); - - if ($DB->has_results()) { - $Requests = $DB->to_array('ID', MYSQLI_ASSOC, false); - } else { - $Requests = array(); - } - $Cache->cache_value("artists_requests_$ArtistID", $Requests); - } + $Requests = $Cache->get_value("artists_requests_$ArtistID"); + if (!is_array($Requests)) { + $DB->query(" + SELECT + r.ID, + r.CategoryID, + r.Title, + r.Year, + r.TimeAdded, + COUNT(rv.UserID) AS Votes, + SUM(rv.Bounty) AS Bounty + FROM requests AS r + LEFT JOIN requests_votes AS rv ON rv.RequestID = r.ID + LEFT JOIN requests_artists AS ra ON r.ID = ra.RequestID + WHERE ra.ArtistID = $ArtistID + AND r.TorrentID = 0 + GROUP BY r.ID + ORDER BY Votes DESC"); + + if ($DB->has_results()) { + $Requests = $DB->to_array('ID', MYSQLI_ASSOC, false); + } else { + $Requests = []; + } + $Cache->cache_value("artists_requests_$ArtistID", $Requests); + } } $NumRequests = count($Requests); if (($Importances = $Cache->get_value("artist_groups_$ArtistID")) === false) { - $DB->query(" - SELECT - DISTINCTROW ta.GroupID, ta.Importance, tg.VanityHouse, tg.Year - FROM torrents_artists AS ta - JOIN torrents_group AS tg ON tg.ID = ta.GroupID - WHERE ta.ArtistID = '$ArtistID' - ORDER BY tg.Year DESC, tg.Name DESC"); - $GroupIDs = $DB->collect('GroupID'); - $Importances = $DB->to_array(false, MYSQLI_BOTH, false); - $Cache->cache_value("artist_groups_$ArtistID", $Importances, 0); + $DB->query(" + SELECT + DISTINCTROW ta.GroupID, ta.Importance, tg.VanityHouse, tg.Year + FROM torrents_artists AS ta + JOIN torrents_group AS tg ON tg.ID = ta.GroupID + WHERE ta.ArtistID = '$ArtistID' + ORDER BY tg.Year DESC, tg.Name DESC"); + $GroupIDs = $DB->collect('GroupID'); + $Importances = $DB->to_array(false, MYSQLI_BOTH, false); + $Cache->cache_value("artist_groups_$ArtistID", $Importances, 0); } else { - $GroupIDs = array(); - foreach ($Importances as $Group) { - $GroupIDs[] = $Group['GroupID']; - } + $GroupIDs = []; + foreach ($Importances as $Group) { + $GroupIDs[] = $Group['GroupID']; + } } if (count($GroupIDs) > 0) { - $TorrentList = Torrents::get_groups($GroupIDs, true, true); + $TorrentList = Torrents::get_groups($GroupIDs, true, true); } else { - $TorrentList = array(); + $TorrentList = []; } $NumGroups = count($TorrentList); //Get list of used release types -$UsedReleases = array(); +$UsedReleases = []; foreach ($TorrentList as $GroupID=>$Group) { - if ($Importances[$GroupID]['Importance'] == '2') { - $TorrentList[$GroupID]['ReleaseType'] = 1024; - $GuestAlbums = true; - } - if ($Importances[$GroupID]['Importance'] == '3') { - $TorrentList[$GroupID]['ReleaseType'] = 1023; - $RemixerAlbums = true; - } - if ($Importances[$GroupID]['Importance'] == '4') { - $TorrentList[$GroupID]['ReleaseType'] = 1022; - $ComposerAlbums = true; - } - if ($Importances[$GroupID]['Importance'] == '7') { - $TorrentList[$GroupID]['ReleaseType'] = 1021; - $ProducerAlbums = true; - } - if (!in_array($TorrentList[$GroupID]['ReleaseType'], $UsedReleases)) { - $UsedReleases[] = $TorrentList[$GroupID]['ReleaseType']; - } + if ($Importances[$GroupID]['Importance'] == '2') { + $TorrentList[$GroupID]['ReleaseType'] = 1024; + $GuestAlbums = true; + } + if ($Importances[$GroupID]['Importance'] == '3') { + $TorrentList[$GroupID]['ReleaseType'] = 1023; + $RemixerAlbums = true; + } + if ($Importances[$GroupID]['Importance'] == '4') { + $TorrentList[$GroupID]['ReleaseType'] = 1022; + $ComposerAlbums = true; + } + if ($Importances[$GroupID]['Importance'] == '7') { + $TorrentList[$GroupID]['ReleaseType'] = 1021; + $ProducerAlbums = true; + } + if (!in_array($TorrentList[$GroupID]['ReleaseType'], $UsedReleases)) { + $UsedReleases[] = $TorrentList[$GroupID]['ReleaseType']; + } } if (!empty($GuestAlbums)) { - $ReleaseTypes[1024] = 'Guest Appearance'; + $ReleaseTypes[1024] = 'Guest Appearance'; } if (!empty($RemixerAlbums)) { - $ReleaseTypes[1023] = 'Remixed By'; + $ReleaseTypes[1023] = 'Remixed By'; } if (!empty($ComposerAlbums)) { - $ReleaseTypes[1022] = 'Composition'; + $ReleaseTypes[1022] = 'Composition'; } if (!empty($ProducerAlbums)) { - $ReleaseTypes[1021] = 'Produced By'; + $ReleaseTypes[1021] = 'Produced By'; } reset($TorrentList); -$JsonTorrents = array(); -$Tags = array(); +$JsonTorrents = []; +$Tags = []; $NumTorrents = $NumSeeders = $NumLeechers = $NumSnatches = 0; foreach ($GroupIDs as $GroupID) { - if (!isset($TorrentList[$GroupID])) { - continue; - } - $Group = $TorrentList[$GroupID]; - extract(Torrents::array_group($Group)); + if (!isset($TorrentList[$GroupID])) { + continue; + } + $Group = $TorrentList[$GroupID]; + extract(Torrents::array_group($Group)); - foreach ($Artists as &$Artist) { - $Artist['id'] = (int)$Artist['id']; - $Artist['aliasid'] = (int)$Artist['aliasid']; - } + foreach ($Artists as &$Artist) { + $Artist['id'] = (int)$Artist['id']; + $Artist['aliasid'] = (int)$Artist['aliasid']; + } - foreach ($ExtendedArtists as &$ArtistGroup) { - foreach ($ArtistGroup as &$Artist) { - $Artist['id'] = (int)$Artist['id']; - $Artist['aliasid'] = (int)$Artist['aliasid']; - } - } + foreach ($ExtendedArtists as &$ArtistGroup) { + foreach ($ArtistGroup as &$Artist) { + $Artist['id'] = (int)$Artist['id']; + $Artist['aliasid'] = (int)$Artist['aliasid']; + } + } - $Found = Misc::search_array($Artists, 'id', $ArtistID); - if (isset($OnlyArtistReleases) && empty($Found)) { - continue; - } + $Found = Misc::search_array($Artists, 'id', $ArtistID); + if (isset($OnlyArtistReleases) && empty($Found)) { + continue; + } - $GroupVanityHouse = $Importances[$GroupID]['VanityHouse']; + $GroupVanityHouse = $Importances[$GroupID]['VanityHouse']; - $TagList = explode(' ',str_replace('_', '.', $TagList)); + $TagList = explode(' ',str_replace('_', '.', $TagList)); - // $Tags array is for the sidebar on the right - foreach ($TagList as $Tag) { - if (!isset($Tags[$Tag])) { - $Tags[$Tag] = array('name' => $Tag, 'count' => 1); - } else { - $Tags[$Tag]['count']++; - } - } - $InnerTorrents = array(); - foreach ($Torrents as $Torrent) { - $NumTorrents++; - $NumSeeders += $Torrent['Seeders']; - $NumLeechers += $Torrent['Leechers']; - $NumSnatches += $Torrent['Snatched']; + // $Tags array is for the sidebar on the right + foreach ($TagList as $Tag) { + if (!isset($Tags[$Tag])) { + $Tags[$Tag] = array('name' => $Tag, 'count' => 1); + } else { + $Tags[$Tag]['count']++; + } + } + $InnerTorrents = []; + foreach ($Torrents as $Torrent) { + $NumTorrents++; + $NumSeeders += $Torrent['Seeders']; + $NumLeechers += $Torrent['Leechers']; + $NumSnatches += $Torrent['Snatched']; - $InnerTorrents[] = array( - 'id' => (int)$Torrent['ID'], - 'groupId' => (int)$Torrent['GroupID'], - 'media' => $Torrent['Media'], - 'format' => $Torrent['Format'], - 'encoding' => $Torrent['Encoding'], - 'remasterYear' => (int)$Torrent['RemasterYear'], - 'remastered' => $Torrent['Remastered'] == 1, - 'remasterTitle' => $Torrent['RemasterTitle'], - 'remasterRecordLabel' => $Torrent['RemasterRecordLabel'], - 'scene' => $Torrent['Scene'] == 1, - 'hasLog' => $Torrent['HasLog'] == 1, - 'hasCue' => $Torrent['HasCue'] == 1, - 'logScore' => (int)$Torrent['LogScore'], - 'fileCount' => (int)$Torrent['FileCount'], - 'freeTorrent' => $Torrent['FreeTorrent'] == 1, - 'size' => (int)$Torrent['Size'], - 'leechers' => (int)$Torrent['Leechers'], - 'seeders' => (int)$Torrent['Seeders'], - 'snatched' => (int)$Torrent['Snatched'], - 'time' => $Torrent['Time'], - 'hasFile' => (int)$Torrent['HasFile'] - ); - } - $JsonTorrents[] = array( - 'groupId' => (int)$GroupID, - 'groupName' => $GroupName, - 'groupYear' => (int)$GroupYear, - 'groupRecordLabel' => $GroupRecordLabel, - 'groupCatalogueNumber' => $GroupCatalogueNumber, - 'groupCategoryID' => $GroupCategoryID, - 'tags' => $TagList, - 'releaseType' => (int)$ReleaseType, - 'wikiImage' => $WikiImage, - 'groupVanityHouse' => $GroupVanityHouse == 1, - 'hasBookmarked' => Bookmarks::has_bookmarked('torrent', $GroupID), - 'artists' => $Artists, - 'extendedArtists' => $ExtendedArtists, - 'torrent' => $InnerTorrents, + $InnerTorrents[] = array( + 'id' => (int)$Torrent['ID'], + 'groupId' => (int)$Torrent['GroupID'], + 'media' => $Torrent['Media'], + 'format' => $Torrent['Format'], + 'encoding' => $Torrent['Encoding'], + 'remasterYear' => (int)$Torrent['RemasterYear'], + 'remastered' => $Torrent['Remastered'] == 1, + 'remasterTitle' => $Torrent['RemasterTitle'], + 'remasterRecordLabel' => $Torrent['RemasterRecordLabel'], + 'scene' => $Torrent['Scene'] == 1, + 'hasLog' => $Torrent['HasLog'] == 1, + 'hasCue' => $Torrent['HasCue'] == 1, + 'logScore' => (int)$Torrent['LogScore'], + 'fileCount' => (int)$Torrent['FileCount'], + 'freeTorrent' => $Torrent['FreeTorrent'] == 1, + 'size' => (int)$Torrent['Size'], + 'leechers' => (int)$Torrent['Leechers'], + 'seeders' => (int)$Torrent['Seeders'], + 'snatched' => (int)$Torrent['Snatched'], + 'time' => $Torrent['Time'], + 'hasFile' => (int)$Torrent['HasFile'] + ); + } + $JsonTorrents[] = array( + 'groupId' => (int)$GroupID, + 'groupName' => $GroupName, + 'groupYear' => (int)$GroupYear, + 'groupRecordLabel' => $GroupRecordLabel, + 'groupCatalogueNumber' => $GroupCatalogueNumber, + 'groupCategoryID' => $GroupCategoryID, + 'tags' => $TagList, + 'releaseType' => (int)$ReleaseType, + 'wikiImage' => $WikiImage, + 'groupVanityHouse' => $GroupVanityHouse == 1, + 'hasBookmarked' => Bookmarks::has_bookmarked('torrent', $GroupID), + 'artists' => $Artists, + 'extendedArtists' => $ExtendedArtists, + 'torrent' => $InnerTorrents, - ); + ); } -$JsonSimilar = array(); +$JsonSimilar = []; if (empty($SimilarArray)) { - $DB->query(" - SELECT - s2.ArtistID, - a.Name, - ass.Score, - ass.SimilarID - FROM artists_similar AS s1 - JOIN artists_similar AS s2 ON s1.SimilarID = s2.SimilarID AND s1.ArtistID != s2.ArtistID - JOIN artists_similar_scores AS ass ON ass.SimilarID = s1.SimilarID - JOIN artists_group AS a ON a.ArtistID = s2.ArtistID - WHERE s1.ArtistID = '$ArtistID' - ORDER BY ass.Score DESC - LIMIT 30 - "); - $SimilarArray = $DB->to_array(); - foreach ($SimilarArray as $Similar) { - $JsonSimilar[] = array( - 'artistId' => (int)$Similar['ArtistID'], - 'name' => $Similar['Name'], - 'score' => (int)$Similar['Score'], - 'similarId' => (int)$Similar['SimilarID'] - ); - } - $NumSimilar = count($SimilarArray); + $DB->query(" + SELECT + s2.ArtistID, + a.Name, + ass.Score, + ass.SimilarID + FROM artists_similar AS s1 + JOIN artists_similar AS s2 ON s1.SimilarID = s2.SimilarID AND s1.ArtistID != s2.ArtistID + JOIN artists_similar_scores AS ass ON ass.SimilarID = s1.SimilarID + JOIN artists_group AS a ON a.ArtistID = s2.ArtistID + WHERE s1.ArtistID = '$ArtistID' + ORDER BY ass.Score DESC + LIMIT 30 + "); + $SimilarArray = $DB->to_array(); + foreach ($SimilarArray as $Similar) { + $JsonSimilar[] = array( + 'artistId' => (int)$Similar['ArtistID'], + 'name' => $Similar['Name'], + 'score' => (int)$Similar['Score'], + 'similarId' => (int)$Similar['SimilarID'] + ); + } + $NumSimilar = count($SimilarArray); } else { - //If data already exists, use it - foreach ($SimilarArray as $Similar) { - $JsonSimilar[] = array( - 'artistId' => (int)$Similar['ArtistID'], - 'name' => $Similar['Name'], - 'score' => (int)$Similar['Score'], - 'similarId' => (int)$Similar['SimilarID'] - ); - } + //If data already exists, use it + foreach ($SimilarArray as $Similar) { + $JsonSimilar[] = array( + 'artistId' => (int)$Similar['ArtistID'], + 'name' => $Similar['Name'], + 'score' => (int)$Similar['Score'], + 'similarId' => (int)$Similar['SimilarID'] + ); + } } -$JsonRequests = array(); +$JsonRequests = []; foreach ($Requests as $RequestID => $Request) { - $JsonRequests[] = array( - 'requestId' => (int)$RequestID, - 'categoryId' => (int)$Request['CategoryID'], - 'title' => $Request['Title'], - 'year' => (int)$Request['Year'], - 'timeAdded' => $Request['TimeAdded'], - 'votes' => (int)$Request['Votes'], - 'bounty' => (int)$Request['Bounty'] - ); + $JsonRequests[] = array( + 'requestId' => (int)$RequestID, + 'categoryId' => (int)$Request['CategoryID'], + 'title' => $Request['Title'], + 'year' => (int)$Request['Year'], + 'timeAdded' => $Request['TimeAdded'], + 'votes' => (int)$Request['Votes'], + 'bounty' => (int)$Request['Bounty'] + ); } //notifications disabled by default $notificationsEnabled = false; if (check_perms('site_torrents_notify')) { - if (($Notify = $Cache->get_value('notify_artists_'.$LoggedUser['ID'])) === false) { - $DB->query(" - SELECT ID, Artists - FROM users_notify_filters - WHERE UserID = '$LoggedUser[ID]' - AND Label = 'Artist notifications' - LIMIT 1"); - $Notify = $DB->next_record(MYSQLI_ASSOC, false); - $Cache->cache_value('notify_artists_'.$LoggedUser['ID'], $Notify, 0); - } - if (stripos($Notify['Artists'], "|$Name|") === false) { - $notificationsEnabled = false; - } else { - $notificationsEnabled = true; - } + if (($Notify = $Cache->get_value('notify_artists_'.$LoggedUser['ID'])) === false) { + $DB->query(" + SELECT ID, Artists + FROM users_notify_filters + WHERE UserID = '$LoggedUser[ID]' + AND Label = 'Artist notifications' + LIMIT 1"); + $Notify = $DB->next_record(MYSQLI_ASSOC, false); + $Cache->cache_value('notify_artists_'.$LoggedUser['ID'], $Notify, 0); + } + if (stripos($Notify['Artists'], "|$Name|") === false) { + $notificationsEnabled = false; + } else { + $notificationsEnabled = true; + } } // Cache page for later use if ($RevisionID) { - $Key = "artist_$ArtistID"."_revision_$RevisionID"; + $Key = "artist_$ArtistID"."_revision_$RevisionID"; } else { - $Key = "artist_$ArtistID"; + $Key = "artist_$ArtistID"; } -$Data = array(array($Name, $Image, $Body, $NumSimilar, $SimilarArray, array(), array(), $VanityHouseArtist)); +$Data = array(array($Name, $Image, $Body, $NumSimilar, $SimilarArray, [], [], $VanityHouseArtist)); $Cache->cache_value($Key, $Data, 3600); json_print("success", array( - 'id' => (int)$ArtistID, - 'name' => $Name, - 'notificationsEnabled' => $notificationsEnabled, - 'hasBookmarked' => Bookmarks::has_bookmarked('artist', $ArtistID), - 'image' => $Image, - 'body' => Text::full_format($Body), - 'vanityHouse' => $VanityHouseArtist == 1, - 'tags' => array_values($Tags), - 'similarArtists' => $JsonSimilar, - 'statistics' => array( - 'numGroups' => $NumGroups, - 'numTorrents' => $NumTorrents, - 'numSeeders' => $NumSeeders, - 'numLeechers' => $NumLeechers, - 'numSnatches' => $NumSnatches - ), - 'torrentgroup' => $JsonTorrents, - 'requests' => $JsonRequests + 'id' => (int)$ArtistID, + 'name' => $Name, + 'notificationsEnabled' => $notificationsEnabled, + 'hasBookmarked' => Bookmarks::has_bookmarked('artist', $ArtistID), + 'image' => $Image, + 'body' => Text::full_format($Body), + 'vanityHouse' => $VanityHouseArtist == 1, + 'tags' => array_values($Tags), + 'similarArtists' => $JsonSimilar, + 'statistics' => array( + 'numGroups' => $NumGroups, + 'numTorrents' => $NumTorrents, + 'numSeeders' => $NumSeeders, + 'numLeechers' => $NumLeechers, + 'numSnatches' => $NumSnatches + ), + 'torrentgroup' => $JsonTorrents, + 'requests' => $JsonRequests )); ?> diff --git a/sections/ajax/better/index.php b/sections/ajax/better/index.php index 0e5509290..3b00adfb9 100644 --- a/sections/ajax/better/index.php +++ b/sections/ajax/better/index.php @@ -1,38 +1,38 @@ - 'failure')); - break; - } + switch ($_GET['method']) { + case 'transcode': + include(SERVER_ROOT.'/sections/ajax/better/transcode.php'); + break; + case 'single': + include(SERVER_ROOT.'/sections/ajax/better/single.php'); + break; + case 'snatch': + include(SERVER_ROOT.'/sections/ajax/better/snatch.php'); + break; + case 'artistless': + include(SERVER_ROOT.'/sections/ajax/better/artistless.php'); + break; + case 'tags': + include(SERVER_ROOT.'/sections/ajax/better/tags.php'); + break; + case 'folders': + include(SERVER_ROOT.'/sections/ajax/better/folders.php'); + break; + case 'files': + include(SERVER_ROOT.'/sections/ajax/better/files.php'); + break; + case 'upload': + include(SERVER_ROOT.'/sections/ajax/better/upload.php'); + break; + default: + print json_encode(array('status' => 'failure')); + break; + } } else { - print json_encode(array('status' => 'failure')); + print json_encode(array('status' => 'failure')); } ?> diff --git a/sections/ajax/better/single.php b/sections/ajax/better/single.php index c828e9499..0de6d8704 100644 --- a/sections/ajax/better/single.php +++ b/sections/ajax/better/single.php @@ -1,55 +1,55 @@ -get_value('better_single_groupids')) === false) { - $DB->query(" - SELECT - t.ID AS TorrentID, - t.GroupID AS GroupID - FROM xbt_files_users AS x - JOIN torrents AS t ON t.ID=x.fid - WHERE t.Format='FLAC' - GROUP BY x.fid - HAVING COUNT(x.uid) = 1 - ORDER BY t.LogScore DESC, t.Time ASC - LIMIT 30"); + $DB->query(" + SELECT + t.ID AS TorrentID, + t.GroupID AS GroupID + FROM xbt_files_users AS x + JOIN torrents AS t ON t.ID=x.fid + WHERE t.Format='FLAC' + GROUP BY x.fid + HAVING COUNT(x.uid) = 1 + ORDER BY t.LogScore DESC, t.Time ASC + LIMIT 30"); - $Results = $DB->to_pair('GroupID', 'TorrentID', false); - $Cache->cache_value('better_single_groupids', $Results, 30 * 60); + $Results = $DB->to_pair('GroupID', 'TorrentID', false); + $Cache->cache_value('better_single_groupids', $Results, 30 * 60); } $Groups = Torrents::get_groups(array_keys($Results)); -$JsonResults = array(); +$JsonResults = []; foreach ($Results as $GroupID => $FlacID) { - if (!isset($Groups[$GroupID])) { - continue; - } - $Group = $Groups[$GroupID]; - extract(Torrents::array_group($Group)); + if (!isset($Groups[$GroupID])) { + continue; + } + $Group = $Groups[$GroupID]; + extract(Torrents::array_group($Group)); - $JsonArtists = array(); - if (count($Artists) > 0) { - foreach ($Artists as $Artist) { - $JsonArtists[] = array( - 'id' => (int)$Artist['id'], - 'name' => $Artist['name'], - 'aliasId' => (int)$Artist['aliasid'] - ); - } - } + $JsonArtists = []; + if (count($Artists) > 0) { + foreach ($Artists as $Artist) { + $JsonArtists[] = array( + 'id' => (int)$Artist['id'], + 'name' => $Artist['name'], + 'aliasId' => (int)$Artist['aliasid'] + ); + } + } - $JsonResults[] = array( - 'torrentId' => (int)$FlacID, - 'groupId' => (int)$GroupID, - 'artist' => $JsonArtists, - 'groupName' => $GroupName, - 'groupYear' => (int)$GroupYear, - 'downloadUrl' => "torrents.php?action=download&id=$FlacID&authkey=".$LoggedUser['AuthKey'].'&torrent_pass='.$LoggedUser['torrent_pass'] - ); + $JsonResults[] = array( + 'torrentId' => (int)$FlacID, + 'groupId' => (int)$GroupID, + 'artist' => $JsonArtists, + 'groupName' => $GroupName, + 'groupYear' => (int)$GroupYear, + 'downloadUrl' => "torrents.php?action=download&id=$FlacID&authkey=".$LoggedUser['AuthKey'].'&torrent_pass='.$LoggedUser['torrent_pass'] + ); } print json_encode( - array( - 'status' => 'success', - 'response' => $JsonResults - ) + array( + 'status' => 'success', + 'response' => $JsonResults + ) ); diff --git a/sections/ajax/better/transcode.php b/sections/ajax/better/transcode.php index 785f04a0d..eb9e5b9b2 100644 --- a/sections/ajax/better/transcode.php +++ b/sections/ajax/better/transcode.php @@ -1,6 +1,6 @@ - 3) { - error(0); + error(0); } $Options = array('v0', 'v2', '320'); @@ -8,124 +8,124 @@ $EncodingKeys = array_fill_keys($Encodings, true); if ($_GET['type'] === '3') { - $List = "!(v0 | v2 | 320)"; + $List = "!(v0 | v2 | 320)"; } else { - $List = '!'.$Options[$_GET['type']]; - if ($_GET['type'] !== '0') { - $_GET['type'] = display_str($_GET['type']); - } + $List = '!'.$Options[$_GET['type']]; + if ($_GET['type'] !== '0') { + $_GET['type'] = display_str($_GET['type']); + } } $SphQL = new SphinxqlQuery(); $SphQL->select('id, groupid') - ->from('better_transcode') - ->where('logscore', 100) - ->where_match('FLAC', 'format') - ->where_match($List, 'encoding', false) - ->order_by('RAND()') - ->limit(0, TORRENTS_PER_PAGE, TORRENTS_PER_PAGE); + ->from('better_transcode') + ->where('logscore', 100) + ->where_match('FLAC', 'format') + ->where_match($List, 'encoding', false) + ->order_by('RAND()') + ->limit(0, TORRENTS_PER_PAGE, TORRENTS_PER_PAGE); if (!empty($_GET['search'])) { - $SphQL->where_match($_GET['search'], '(groupname,artistname,year,taglist)'); + $SphQL->where_match($_GET['search'], '(groupname,artistname,year,taglist)'); } $SphQLResult = $SphQL->query(); $TorrentCount = $SphQLResult->get_meta('total'); if ($TorrentCount == 0) { - error('No results found!'); + error('No results found!'); } $Results = $SphQLResult->to_array('groupid'); $Groups = Torrents::get_groups(array_keys($Results)); -$TorrentGroups = array(); +$TorrentGroups = []; foreach ($Groups as $GroupID => $Group) { - if (empty($Group['Torrents'])) { - unset($Groups[$GroupID]); - continue; - } - foreach ($Group['Torrents'] as $Torrent) { - $TorRemIdent = "$Torrent[Media] $Torrent[RemasterYear] $Torrent[RemasterTitle] $Torrent[RemasterRecordLabel] $Torrent[RemasterCatalogueNumber]"; - if (!isset($TorrentGroups[$Group['ID']])) { - $TorrentGroups[$Group['ID']] = array( - $TorRemIdent => array( - 'FlacID' => 0, - 'Formats' => array(), - 'RemasterTitle' => $Torrent['RemasterTitle'], - 'RemasterYear' => $Torrent['RemasterYear'], - 'RemasterRecordLabel' => $Torrent['RemasterRecordLabel'], - 'RemasterCatalogueNumber' => $Torrent['RemasterCatalogueNumber'], - 'IsSnatched' => false - ) - ); - } elseif (!isset($TorrentGroups[$Group['ID']][$TorRemIdent])) { - $TorrentGroups[$Group['ID']][$TorRemIdent] = array( - 'FlacID' => 0, - 'Formats' => array(), - 'RemasterTitle' => $Torrent['RemasterTitle'], - 'RemasterYear' => $Torrent['RemasterYear'], - 'RemasterRecordLabel' => $Torrent['RemasterRecordLabel'], - 'RemasterCatalogueNumber' => $Torrent['RemasterCatalogueNumber'], - 'IsSnatched' => false - ); - } - if (isset($EncodingKeys[$Torrent['Encoding']])) { - $TorrentGroups[$Group['ID']][$TorRemIdent]['Formats'][$Torrent['Encoding']] = true; - } elseif ($TorrentGroups[$Group['ID']][$TorRemIdent]['FlacID'] == 0 && $Torrent['Format'] == 'FLAC' && $Torrent['LogScore'] == 100) { - $TorrentGroups[$Group['ID']][$TorRemIdent]['FlacID'] = $Torrent['ID']; - $TorrentGroups[$Group['ID']][$TorRemIdent]['IsSnatched'] = $Torrent['IsSnatched']; - } - } + if (empty($Group['Torrents'])) { + unset($Groups[$GroupID]); + continue; + } + foreach ($Group['Torrents'] as $Torrent) { + $TorRemIdent = "$Torrent[Media] $Torrent[RemasterYear] $Torrent[RemasterTitle] $Torrent[RemasterRecordLabel] $Torrent[RemasterCatalogueNumber]"; + if (!isset($TorrentGroups[$Group['ID']])) { + $TorrentGroups[$Group['ID']] = array( + $TorRemIdent => array( + 'FlacID' => 0, + 'Formats' => [], + 'RemasterTitle' => $Torrent['RemasterTitle'], + 'RemasterYear' => $Torrent['RemasterYear'], + 'RemasterRecordLabel' => $Torrent['RemasterRecordLabel'], + 'RemasterCatalogueNumber' => $Torrent['RemasterCatalogueNumber'], + 'IsSnatched' => false + ) + ); + } elseif (!isset($TorrentGroups[$Group['ID']][$TorRemIdent])) { + $TorrentGroups[$Group['ID']][$TorRemIdent] = array( + 'FlacID' => 0, + 'Formats' => [], + 'RemasterTitle' => $Torrent['RemasterTitle'], + 'RemasterYear' => $Torrent['RemasterYear'], + 'RemasterRecordLabel' => $Torrent['RemasterRecordLabel'], + 'RemasterCatalogueNumber' => $Torrent['RemasterCatalogueNumber'], + 'IsSnatched' => false + ); + } + if (isset($EncodingKeys[$Torrent['Encoding']])) { + $TorrentGroups[$Group['ID']][$TorRemIdent]['Formats'][$Torrent['Encoding']] = true; + } elseif ($TorrentGroups[$Group['ID']][$TorRemIdent]['FlacID'] == 0 && $Torrent['Format'] == 'FLAC' && $Torrent['LogScore'] == 100) { + $TorrentGroups[$Group['ID']][$TorRemIdent]['FlacID'] = $Torrent['ID']; + $TorrentGroups[$Group['ID']][$TorRemIdent]['IsSnatched'] = $Torrent['IsSnatched']; + } + } } -$JsonResults = array(); +$JsonResults = []; foreach ($TorrentGroups as $GroupID => $Editions) { - $GroupInfo = $Groups[$GroupID]; - $GroupYear = $GroupInfo['Year']; - $ExtendedArtists = $GroupInfo['ExtendedArtists']; - $GroupCatalogueNumber = $GroupInfo['CatalogueNumber']; - $GroupName = $GroupInfo['Name']; - $GroupRecordLabel = $GroupInfo['RecordLabel']; - $ReleaseType = $GroupInfo['ReleaseType']; + $GroupInfo = $Groups[$GroupID]; + $GroupYear = $GroupInfo['Year']; + $ExtendedArtists = $GroupInfo['ExtendedArtists']; + $GroupCatalogueNumber = $GroupInfo['CatalogueNumber']; + $GroupName = $GroupInfo['Name']; + $GroupRecordLabel = $GroupInfo['RecordLabel']; + $ReleaseType = $GroupInfo['ReleaseType']; - if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5]) || !empty($ExtendedArtists[6])) { - unset($ExtendedArtists[2]); - unset($ExtendedArtists[3]); - $ArtistNames = Artists::display_artists($ExtendedArtists, false, false, false); - } else { - $ArtistNames = ''; - } + if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5]) || !empty($ExtendedArtists[6])) { + unset($ExtendedArtists[2]); + unset($ExtendedArtists[3]); + $ArtistNames = Artists::display_artists($ExtendedArtists, false, false, false); + } else { + $ArtistNames = ''; + } - $TagList = array(); - $TagList = explode(' ', str_replace('_', '.', $GroupInfo['TagList'])); - $TorrentTags = array(); - foreach ($TagList as $Tag) { - $TorrentTags[] = "$Tag"; - } - $TorrentTags = implode(', ', $TorrentTags); - foreach ($Editions as $RemIdent => $Edition) { - if (!$Edition['FlacID'] - || !empty($Edition['Formats']) && $_GET['type'] === '3' - || $Edition['Formats'][$Encodings[$_GET['type']]] == true) { - continue; - } + $TagList = []; + $TagList = explode(' ', str_replace('_', '.', $GroupInfo['TagList'])); + $TorrentTags = []; + foreach ($TagList as $Tag) { + $TorrentTags[] = "$Tag"; + } + $TorrentTags = implode(', ', $TorrentTags); + foreach ($Editions as $RemIdent => $Edition) { + if (!$Edition['FlacID'] + || !empty($Edition['Formats']) && $_GET['type'] === '3' + || $Edition['Formats'][$Encodings[$_GET['type']]] == true) { + continue; + } - $JsonResults[] = array( - 'torrentId' => (int)$Edition['FlacID'], - 'groupId' => (int)$GroupID, - 'artist' => $ArtistNames, - 'groupName' => $GroupName, - 'groupYear' => (int)$GroupYear, - 'missingV2' => !isset($Edition['Formats']['V2 (VBR)']), - 'missingV0' => !isset($Edition['Formats']['V0 (VBR)']), - 'missing320' => !isset($Encodings['Formats']['320']), - 'downloadUrl' => 'torrents.php?action=download&id='.$Edition['FlacID'].'&authkey='.$LoggedUser['AuthKey'].'&torrent_pass='.$LoggedUser['torrent_pass'] - ); - } + $JsonResults[] = array( + 'torrentId' => (int)$Edition['FlacID'], + 'groupId' => (int)$GroupID, + 'artist' => $ArtistNames, + 'groupName' => $GroupName, + 'groupYear' => (int)$GroupYear, + 'missingV2' => !isset($Edition['Formats']['V2 (VBR)']), + 'missingV0' => !isset($Edition['Formats']['V0 (VBR)']), + 'missing320' => !isset($Encodings['Formats']['320']), + 'downloadUrl' => 'torrents.php?action=download&id='.$Edition['FlacID'].'&authkey='.$LoggedUser['AuthKey'].'&torrent_pass='.$LoggedUser['torrent_pass'] + ); + } } print json_encode( - array( - 'status' => 'success', - 'response' => $JsonResults - ) + array( + 'status' => 'success', + 'response' => $JsonResults + ) ); diff --git a/sections/ajax/bookmarks/artists.php b/sections/ajax/bookmarks/artists.php index 911d3eec6..00ccd7747 100644 --- a/sections/ajax/bookmarks/artists.php +++ b/sections/ajax/bookmarks/artists.php @@ -1,33 +1,33 @@ - 'failure' - ) - ); - die(); - } - $UserID = $_GET['userid']; - $Sneaky = ($UserID != $LoggedUser['ID']); - if (!is_number($UserID)) { - print - json_encode( - array( - 'status' => 'failure' - ) - ); - die(); - } - $DB->query(" - SELECT Username - FROM users_main - WHERE ID = '$UserID'"); - list($Username) = $DB->next_record(); + if (!check_perms('users_override_paranoia')) { + print + json_encode( + array( + 'status' => 'failure' + ) + ); + die(); + } + $UserID = $_GET['userid']; + $Sneaky = ($UserID != $LoggedUser['ID']); + if (!is_number($UserID)) { + print + json_encode( + array( + 'status' => 'failure' + ) + ); + die(); + } + $DB->query(" + SELECT Username + FROM users_main + WHERE ID = '$UserID'"); + list($Username) = $DB->next_record(); } else { - $UserID = $LoggedUser['ID']; + $UserID = $LoggedUser['ID']; } $Sneaky = ($UserID != $LoggedUser['ID']); @@ -35,30 +35,30 @@ //$ArtistList = Bookmarks::all_bookmarks('artist', $UserID); $DB->query(" - SELECT ag.ArtistID, ag.Name - FROM bookmarks_artists AS ba - INNER JOIN artists_group AS ag ON ba.ArtistID = ag.ArtistID - WHERE ba.UserID = $UserID"); + SELECT ag.ArtistID, ag.Name + FROM bookmarks_artists AS ba + INNER JOIN artists_group AS ag ON ba.ArtistID = ag.ArtistID + WHERE ba.UserID = $UserID"); $ArtistList = $DB->to_array(); -$JsonArtists = array(); +$JsonArtists = []; foreach ($ArtistList as $Artist) { - list($ArtistID, $Name) = $Artist; - $JsonArtists[] = array( - 'artistId' => (int)$ArtistID, - 'artistName' => $Name - ); + list($ArtistID, $Name) = $Artist; + $JsonArtists[] = array( + 'artistId' => (int)$ArtistID, + 'artistName' => $Name + ); } print - json_encode( - array( - 'status' => 'success', - 'response' => array( - 'artists' => $JsonArtists - ) - ) - ); + json_encode( + array( + 'status' => 'success', + 'response' => array( + 'artists' => $JsonArtists + ) + ) + ); ?> diff --git a/sections/ajax/bookmarks/index.php b/sections/ajax/bookmarks/index.php index 4ac10e8db..70f104c43 100644 --- a/sections/ajax/bookmarks/index.php +++ b/sections/ajax/bookmarks/index.php @@ -1,4 +1,4 @@ - 'failure' - ) - ); - die(); + case 'torrents': + require(SERVER_ROOT.'/sections/ajax/bookmarks/torrents.php'); + break; + case 'artists': + require(SERVER_ROOT.'/sections/ajax/bookmarks/artists.php'); + break; + case 'collages': + $_GET['bookmarks'] = 1; + require(SERVER_ROOT.'/sections/ajax/collages/browse.php'); + break; + case 'requests': + $_GET['type'] = 'bookmarks'; + require(SERVER_ROOT.'/sections/ajax/requests/requests.php'); + break; + default: + print + json_encode( + array( + 'status' => 'failure' + ) + ); + die(); } ?> diff --git a/sections/ajax/bookmarks/torrents.php b/sections/ajax/bookmarks/torrents.php index f13028609..8ef38acb8 100644 --- a/sections/ajax/bookmarks/torrents.php +++ b/sections/ajax/bookmarks/torrents.php @@ -1,87 +1,87 @@ -query(" - SELECT Username - FROM users_main - WHERE ID = '$UserID'"); - list($Username) = $DB->next_record(); + if (!check_perms('users_override_paranoia')) { + error(403); + } + $UserID = $_GET['userid']; + if (!is_number($UserID)) { + error(404); + } + $DB->query(" + SELECT Username + FROM users_main + WHERE ID = '$UserID'"); + list($Username) = $DB->next_record(); } else { - $UserID = $LoggedUser['ID']; + $UserID = $LoggedUser['ID']; } $Sneaky = ($UserID != $LoggedUser['ID']); -$JsonBookmarks = array(); +$JsonBookmarks = []; list($GroupIDs, $CollageDataList, $GroupList) = Users::get_bookmarks($UserID); foreach($GroupIDs as $GroupID) { - if (!isset($GroupList[$GroupID])) { - continue; - } - $Group = $GroupList[$GroupID]; - $JsonTorrents = array(); - foreach ($Group['Torrents'] as $Torrent) { - $JsonTorrents[] = array( - 'id' => (int)$Torrent['ID'], - 'groupId' => (int)$Torrent['GroupID'], - 'media' => $Torrent['Media'], - 'format' => $Torrent['Format'], - 'encoding' => $Torrent['Encoding'], - 'remasterYear' => (int)$Torrent['RemasterYear'], - 'remastered' => $Torrent['Remastered'] == 1, - 'remasterTitle' => $Torrent['RemasterTitle'], - 'remasterRecordLabel' => $Torrent['RemasterRecordLabel'], - 'remasterCatalogueNumber' => $Torrent['RemasterCatalogueNumber'], - 'scene' => $Torrent['Scene'] == 1, - 'hasLog' => $Torrent['HasLog'] == 1, - 'hasCue' => $Torrent['HasCue'] == 1, - 'logScore' => (float)$Torrent['LogScore'], - 'fileCount' => (int)$Torrent['FileCount'], - 'freeTorrent' => $Torrent['FreeTorrent'] == 1, - 'size' => (float)$Torrent['Size'], - 'leechers' => (int)$Torrent['Leechers'], - 'seeders' => (int)$Torrent['Seeders'], - 'snatched' => (int)$Torrent['Snatched'], - 'time' => $Torrent['Time'], - 'hasFile' => (int)$Torrent['HasFile'] - ); - } - $JsonBookmarks[] = array( - 'id' => (int)$Group['ID'], - 'name' => $Group['Name'], - 'year' => (int)$Group['Year'], - 'recordLabel' => $Group['RecordLabel'], - 'catalogueNumber' => $Group['CatalogueNumber'], - 'tagList' => $Group['TagList'], - 'releaseType' => $Group['ReleaseType'], - 'vanityHouse' => $Group['VanityHouse'] == 1, - 'image' => $Group['WikiImage'], - 'torrents' => $JsonTorrents - ); + if (!isset($GroupList[$GroupID])) { + continue; + } + $Group = $GroupList[$GroupID]; + $JsonTorrents = []; + foreach ($Group['Torrents'] as $Torrent) { + $JsonTorrents[] = array( + 'id' => (int)$Torrent['ID'], + 'groupId' => (int)$Torrent['GroupID'], + 'media' => $Torrent['Media'], + 'format' => $Torrent['Format'], + 'encoding' => $Torrent['Encoding'], + 'remasterYear' => (int)$Torrent['RemasterYear'], + 'remastered' => $Torrent['Remastered'] == 1, + 'remasterTitle' => $Torrent['RemasterTitle'], + 'remasterRecordLabel' => $Torrent['RemasterRecordLabel'], + 'remasterCatalogueNumber' => $Torrent['RemasterCatalogueNumber'], + 'scene' => $Torrent['Scene'] == 1, + 'hasLog' => $Torrent['HasLog'] == 1, + 'hasCue' => $Torrent['HasCue'] == 1, + 'logScore' => (float)$Torrent['LogScore'], + 'fileCount' => (int)$Torrent['FileCount'], + 'freeTorrent' => $Torrent['FreeTorrent'] == 1, + 'size' => (float)$Torrent['Size'], + 'leechers' => (int)$Torrent['Leechers'], + 'seeders' => (int)$Torrent['Seeders'], + 'snatched' => (int)$Torrent['Snatched'], + 'time' => $Torrent['Time'], + 'hasFile' => (int)$Torrent['HasFile'] + ); + } + $JsonBookmarks[] = array( + 'id' => (int)$Group['ID'], + 'name' => $Group['Name'], + 'year' => (int)$Group['Year'], + 'recordLabel' => $Group['RecordLabel'], + 'catalogueNumber' => $Group['CatalogueNumber'], + 'tagList' => $Group['TagList'], + 'releaseType' => $Group['ReleaseType'], + 'vanityHouse' => $Group['VanityHouse'] == 1, + 'image' => $Group['WikiImage'], + 'torrents' => $JsonTorrents + ); } print - json_encode( - array( - 'status' => 'success', - 'response' => array( - 'bookmarks' => $JsonBookmarks - ) - ) - ); + json_encode( + array( + 'status' => 'success', + 'response' => array( + 'bookmarks' => $JsonBookmarks + ) + ) + ); diff --git a/sections/ajax/browse.php b/sections/ajax/browse.php index f890a1741..875760ffe 100644 --- a/sections/ajax/browse.php +++ b/sections/ajax/browse.php @@ -1,16 +1,16 @@ -record_count(); if ($Results === false) { - json_die('error', 'Search returned an error. Make sure all parameters are valid and of the expected types.'); + json_die('error', 'Search returned an error. Make sure all parameters are valid and of the expected types.'); } if ($NumResults == 0) { - json_die('success', array( - 'results' => array(), - 'youMightLike' => array() // This slow and broken feature has been removed - )); + json_die('success', array( + 'results' => [], + 'youMightLike' => [] // This slow and broken feature has been removed + )); } $Bookmarks = Bookmarks::all_bookmarks('torrent'); -$JsonGroups = array(); +$JsonGroups = []; foreach ($Results as $Key => $GroupID) { - $GroupInfo = $Groups[$GroupID]; - if (empty($GroupInfo['Torrents'])) { - continue; - } - $CategoryID = $GroupInfo['CategoryID']; - $GroupYear = $GroupInfo['Year']; - $ExtendedArtists = $GroupInfo['ExtendedArtists']; - $GroupCatalogueNumber = $GroupInfo['CatalogueNumber']; - $GroupName = $GroupInfo['Name']; - $GroupRecordLabel = $GroupInfo['RecordLabel']; - $ReleaseType = $GroupInfo['ReleaseType']; - if ($GroupResults) { - $Torrents = $GroupInfo['Torrents']; - $GroupTime = $MaxSize = $TotalLeechers = $TotalSeeders = $TotalSnatched = 0; - foreach ($Torrents as $T) { - $GroupTime = max($GroupTime, strtotime($T['Time'])); - $MaxSize = max($MaxSize, $T['Size']); - $TotalLeechers += $T['Leechers']; - $TotalSeeders += $T['Seeders']; - $TotalSnatched += $T['Snatched']; - } - } else { - $TorrentID = $Key; - $Torrents = array($TorrentID => $GroupInfo['Torrents'][$TorrentID]); - } - - $TagList = explode(' ', str_replace('_', '.', $GroupInfo['TagList'])); - $JsonArtists = array(); - if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5]) || !empty($ExtendedArtists[6])) { - unset($ExtendedArtists[2]); - unset($ExtendedArtists[3]); - $DisplayName = Artists::display_artists($ExtendedArtists, false, false, false); - foreach ($ExtendedArtists[1] as $Artist) { - $JsonArtists[] = array( - 'id' => (int)$Artist['id'], - 'name' => $Artist['name'], - 'aliasid' => (int)$Artist['aliasid']); - } - } else { - $DisplayName = ''; - } - if ($GroupResults && (count($Torrents) > 1 || isset($GroupedCategories[$CategoryID - 1]))) { - // These torrents are in a group - $LastRemasterYear = '-'; - $LastRemasterTitle = ''; - $LastRemasterRecordLabel = ''; - $LastRemasterCatalogueNumber = ''; - $LastMedia = ''; - - $EditionID = 0; - unset($FirstUnknown); - - $JsonTorrents = array(); - foreach ($Torrents as $TorrentID => $Data) { - // All of the individual torrents in the group - - if ($Data['Remastered'] && !$Data['RemasterYear']) { - $FirstUnknown = !isset($FirstUnknown); - } - - if (isset($GroupedCategories[$CategoryID - 1]) - && ($Data['RemasterTitle'] != $LastRemasterTitle - || $Data['RemasterYear'] != $LastRemasterYear - || $Data['RemasterRecordLabel'] != $LastRemasterRecordLabel - || $Data['RemasterCatalogueNumber'] != $LastRemasterCatalogueNumber) - || (isset($FirstUnknown) && $FirstUnknown) - || $Data['Media'] != $LastMedia) { - $EditionID++; - - if ($Data['Remastered'] && $Data['RemasterYear'] != 0) { - - $RemasterName = $Data['RemasterYear']; - $AddExtra = ' - '; - if ($Data['RemasterRecordLabel']) { - $RemasterName .= $AddExtra.display_str($Data['RemasterRecordLabel']); - $AddExtra = ' / '; - } - if ($Data['RemasterCatalogueNumber']) { - $RemasterName .= $AddExtra.display_str($Data['RemasterCatalogueNumber']); - $AddExtra = ' / '; - } - if ($Data['RemasterTitle']) { - $RemasterName .= $AddExtra.display_str($Data['RemasterTitle']); - $AddExtra = ' / '; - } - $RemasterName .= $AddExtra.display_str($Data['Media']); - } else { - $AddExtra = ' / '; - if (!$Data['Remastered']) { - $MasterName = 'Original Release'; - if ($GroupRecordLabel) { - $MasterName .= $AddExtra.$GroupRecordLabel; - $AddExtra = ' / '; - } - if ($GroupCatalogueNumber) { - $MasterName .= $AddExtra.$GroupCatalogueNumber; - $AddExtra = ' / '; - } - } else { - $MasterName = 'Unknown Release(s)'; - } - $MasterName .= $AddExtra.display_str($Data['Media']); - } - } - $LastRemasterTitle = $Data['RemasterTitle']; - $LastRemasterYear = $Data['RemasterYear']; - $LastRemasterRecordLabel = $Data['RemasterRecordLabel']; - $LastRemasterCatalogueNumber = $Data['RemasterCatalogueNumber']; - $LastMedia = $Data['Media']; - - $JsonTorrents[] = array( - 'torrentId' => (int)$TorrentID, - 'editionId' => (int)$EditionID, - 'artists' => $JsonArtists, - 'remastered' => $Data['Remastered'] == '1', - 'remasterYear' => (int)$Data['RemasterYear'], - 'remasterCatalogueNumber' => $Data['RemasterCatalogueNumber'], - 'remasterTitle' => $Data['RemasterTitle'], - 'media' => $Data['Media'], - 'encoding' => $Data['Encoding'], - 'format' => $Data['Format'], - 'hasLog' => $Data['HasLog'] == '1', - 'logScore' => (int)$Data['LogScore'], - 'hasCue' => $Data['HasCue'] == '1', - 'scene' => $Data['Scene'] == '1', - 'vanityHouse' => $GroupInfo['VanityHouse'] == '1', - 'fileCount' => (int)$Data['FileCount'], - 'time' => $Data['Time'], - 'size' => (int)$Data['Size'], - 'snatches' => (int)$Data['Snatched'], - 'seeders' => (int)$Data['Seeders'], - 'leechers' => (int)$Data['Leechers'], - 'isFreeleech' => $Data['FreeTorrent'] == '1', - 'isNeutralLeech' => $Data['FreeTorrent'] == '2', - 'isPersonalFreeleech' => $Data['PersonalFL'], - 'canUseToken' => Torrents::can_use_token($Data), - 'hasSnatched' => $Data['IsSnatched'] - ); - } - - $JsonGroups[] = array( - 'groupId' => (int)$GroupID, - 'groupName' => $GroupName, - 'artist' => $DisplayName, - 'cover' => $GroupInfo['WikiImage'], - 'tags' => $TagList, - 'bookmarked' => in_array($GroupID, $Bookmarks), - 'vanityHouse' => $GroupInfo['VanityHouse'] == '1', - 'groupYear' => (int)$GroupYear, - 'releaseType' => $ReleaseTypes[$ReleaseType], - 'groupTime' => (string)$GroupTime, - 'maxSize' => (int)$MaxSize, - 'totalSnatched' => (int)$TotalSnatched, - 'totalSeeders' => (int)$TotalSeeders, - 'totalLeechers' => (int)$TotalLeechers, - 'torrents' => $JsonTorrents - ); - } else { - // Viewing a type that does not require grouping - - list($TorrentID, $Data) = each($Torrents); - - $JsonGroups[] = array( - 'groupId' => (int)$GroupID, - 'groupName' => $GroupName, - 'torrentId' => (int)$TorrentID, - 'tags' => $TagList, - 'category' => $Categories[$CategoryID - 1], - 'fileCount' => (int)$Data['FileCount'], - 'groupTime' => (string)strtotime($Data['Time']), - 'size' => (int)$Data['Size'], - 'snatches' => (int)$Data['Snatched'], - 'seeders' => (int)$Data['Seeders'], - 'leechers' => (int)$Data['Leechers'], - 'isFreeleech' => $Data['FreeTorrent'] == '1', - 'isNeutralLeech' => $Data['FreeTorrent'] == '2', - 'isPersonalFreeleech' => $Data['PersonalFL'], - 'canUseToken' => Torrents::can_use_token($Data), - 'hasSnatched' => $Data['IsSnatched'] - ); - } + $GroupInfo = $Groups[$GroupID]; + if (empty($GroupInfo['Torrents'])) { + continue; + } + $CategoryID = $GroupInfo['CategoryID']; + $GroupYear = $GroupInfo['Year']; + $ExtendedArtists = $GroupInfo['ExtendedArtists']; + $GroupCatalogueNumber = $GroupInfo['CatalogueNumber']; + $GroupName = $GroupInfo['Name']; + $GroupRecordLabel = $GroupInfo['RecordLabel']; + $ReleaseType = $GroupInfo['ReleaseType']; + if ($GroupResults) { + $Torrents = $GroupInfo['Torrents']; + $GroupTime = $MaxSize = $TotalLeechers = $TotalSeeders = $TotalSnatched = 0; + foreach ($Torrents as $T) { + $GroupTime = max($GroupTime, strtotime($T['Time'])); + $MaxSize = max($MaxSize, $T['Size']); + $TotalLeechers += $T['Leechers']; + $TotalSeeders += $T['Seeders']; + $TotalSnatched += $T['Snatched']; + } + } else { + $TorrentID = $Key; + $Torrents = array($TorrentID => $GroupInfo['Torrents'][$TorrentID]); + } + + $TagList = explode(' ', str_replace('_', '.', $GroupInfo['TagList'])); + $JsonArtists = []; + if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5]) || !empty($ExtendedArtists[6])) { + unset($ExtendedArtists[2]); + unset($ExtendedArtists[3]); + $DisplayName = Artists::display_artists($ExtendedArtists, false, false, false); + foreach ($ExtendedArtists[1] as $Artist) { + $JsonArtists[] = array( + 'id' => (int)$Artist['id'], + 'name' => $Artist['name'], + 'aliasid' => (int)$Artist['aliasid']); + } + } else { + $DisplayName = ''; + } + if ($GroupResults && (count($Torrents) > 1 || isset($GroupedCategories[$CategoryID - 1]))) { + // These torrents are in a group + $LastRemasterYear = '-'; + $LastRemasterTitle = ''; + $LastRemasterRecordLabel = ''; + $LastRemasterCatalogueNumber = ''; + $LastMedia = ''; + + $EditionID = 0; + unset($FirstUnknown); + + $JsonTorrents = []; + foreach ($Torrents as $TorrentID => $Data) { + // All of the individual torrents in the group + + if ($Data['Remastered'] && !$Data['RemasterYear']) { + $FirstUnknown = !isset($FirstUnknown); + } + + if (isset($GroupedCategories[$CategoryID - 1]) + && ($Data['RemasterTitle'] != $LastRemasterTitle + || $Data['RemasterYear'] != $LastRemasterYear + || $Data['RemasterRecordLabel'] != $LastRemasterRecordLabel + || $Data['RemasterCatalogueNumber'] != $LastRemasterCatalogueNumber) + || (isset($FirstUnknown) && $FirstUnknown) + || $Data['Media'] != $LastMedia) { + $EditionID++; + + if ($Data['Remastered'] && $Data['RemasterYear'] != 0) { + + $RemasterName = $Data['RemasterYear']; + $AddExtra = ' - '; + if ($Data['RemasterRecordLabel']) { + $RemasterName .= $AddExtra.display_str($Data['RemasterRecordLabel']); + $AddExtra = ' / '; + } + if ($Data['RemasterCatalogueNumber']) { + $RemasterName .= $AddExtra.display_str($Data['RemasterCatalogueNumber']); + $AddExtra = ' / '; + } + if ($Data['RemasterTitle']) { + $RemasterName .= $AddExtra.display_str($Data['RemasterTitle']); + $AddExtra = ' / '; + } + $RemasterName .= $AddExtra.display_str($Data['Media']); + } else { + $AddExtra = ' / '; + if (!$Data['Remastered']) { + $MasterName = 'Original Release'; + if ($GroupRecordLabel) { + $MasterName .= $AddExtra.$GroupRecordLabel; + $AddExtra = ' / '; + } + if ($GroupCatalogueNumber) { + $MasterName .= $AddExtra.$GroupCatalogueNumber; + $AddExtra = ' / '; + } + } else { + $MasterName = 'Unknown Release(s)'; + } + $MasterName .= $AddExtra.display_str($Data['Media']); + } + } + $LastRemasterTitle = $Data['RemasterTitle']; + $LastRemasterYear = $Data['RemasterYear']; + $LastRemasterRecordLabel = $Data['RemasterRecordLabel']; + $LastRemasterCatalogueNumber = $Data['RemasterCatalogueNumber']; + $LastMedia = $Data['Media']; + + $JsonTorrents[] = array( + 'torrentId' => (int)$TorrentID, + 'editionId' => (int)$EditionID, + 'artists' => $JsonArtists, + 'remastered' => $Data['Remastered'] == '1', + 'remasterYear' => (int)$Data['RemasterYear'], + 'remasterCatalogueNumber' => $Data['RemasterCatalogueNumber'], + 'remasterTitle' => $Data['RemasterTitle'], + 'media' => $Data['Media'], + 'encoding' => $Data['Encoding'], + 'format' => $Data['Format'], + 'hasLog' => $Data['HasLog'] == '1', + 'logScore' => (int)$Data['LogScore'], + 'hasCue' => $Data['HasCue'] == '1', + 'scene' => $Data['Scene'] == '1', + 'vanityHouse' => $GroupInfo['VanityHouse'] == '1', + 'fileCount' => (int)$Data['FileCount'], + 'time' => $Data['Time'], + 'size' => (int)$Data['Size'], + 'snatches' => (int)$Data['Snatched'], + 'seeders' => (int)$Data['Seeders'], + 'leechers' => (int)$Data['Leechers'], + 'isFreeleech' => $Data['FreeTorrent'] == '1', + 'isNeutralLeech' => $Data['FreeTorrent'] == '2', + 'isPersonalFreeleech' => $Data['PersonalFL'], + 'canUseToken' => Torrents::can_use_token($Data), + 'hasSnatched' => $Data['IsSnatched'] + ); + } + + $JsonGroups[] = array( + 'groupId' => (int)$GroupID, + 'groupName' => $GroupName, + 'artist' => $DisplayName, + 'cover' => $GroupInfo['WikiImage'], + 'tags' => $TagList, + 'bookmarked' => in_array($GroupID, $Bookmarks), + 'vanityHouse' => $GroupInfo['VanityHouse'] == '1', + 'groupYear' => (int)$GroupYear, + 'releaseType' => $ReleaseTypes[$ReleaseType], + 'groupTime' => (string)$GroupTime, + 'maxSize' => (int)$MaxSize, + 'totalSnatched' => (int)$TotalSnatched, + 'totalSeeders' => (int)$TotalSeeders, + 'totalLeechers' => (int)$TotalLeechers, + 'torrents' => $JsonTorrents + ); + } else { + // Viewing a type that does not require grouping + + list($TorrentID, $Data) = each($Torrents); + + $JsonGroups[] = array( + 'groupId' => (int)$GroupID, + 'groupName' => $GroupName, + 'torrentId' => (int)$TorrentID, + 'tags' => $TagList, + 'category' => $Categories[$CategoryID - 1], + 'fileCount' => (int)$Data['FileCount'], + 'groupTime' => (string)strtotime($Data['Time']), + 'size' => (int)$Data['Size'], + 'snatches' => (int)$Data['Snatched'], + 'seeders' => (int)$Data['Seeders'], + 'leechers' => (int)$Data['Leechers'], + 'isFreeleech' => $Data['FreeTorrent'] == '1', + 'isNeutralLeech' => $Data['FreeTorrent'] == '2', + 'isPersonalFreeleech' => $Data['PersonalFL'], + 'canUseToken' => Torrents::can_use_token($Data), + 'hasSnatched' => $Data['IsSnatched'] + ); + } } json_print('success', array( - 'currentPage' => intval($Page), - 'pages' => ceil($NumResults / TORRENTS_PER_PAGE), - 'results' => $JsonGroups)); + 'currentPage' => intval($Page), + 'pages' => ceil($NumResults / TORRENTS_PER_PAGE), + 'results' => $JsonGroups)); diff --git a/sections/ajax/clear_user_notification.php b/sections/ajax/clear_user_notification.php index 835154088..61d155401 100644 --- a/sections/ajax/clear_user_notification.php +++ b/sections/ajax/clear_user_notification.php @@ -1,39 +1,39 @@ -get_value($CacheKey); if ($CollageData) { - list($Name, $Description, $CommentList, $Deleted, $CollageCategoryID, $CreatorID, $Locked, $MaxGroups, $MaxGroupsPerUser, $Updated, $Subscribers) = $CollageData; + list($Name, $Description, $CommentList, $Deleted, $CollageCategoryID, $CreatorID, $Locked, $MaxGroups, $MaxGroupsPerUser, $Updated, $Subscribers) = $CollageData; } else { - $DB->query(" - SELECT - Name, - Description, - UserID, - Deleted, - CategoryID, - Locked, - MaxGroups, - MaxGroupsPerUser, - Updated, - Subscribers - FROM collages - WHERE ID = '$CollageID'"); - if (!$DB->has_results()) { - json_die("failure"); - } - list($Name, $Description, $CreatorID, $Deleted, $CollageCategoryID, $Locked, $MaxGroups, $MaxGroupsPerUser, $Updated, $Subscribers) = $DB->next_record(MYSQLI_NUM); - $CommentList = null; - $SetCache = true; + $DB->query(" + SELECT + Name, + Description, + UserID, + Deleted, + CategoryID, + Locked, + MaxGroups, + MaxGroupsPerUser, + Updated, + Subscribers + FROM collages + WHERE ID = '$CollageID'"); + if (!$DB->has_results()) { + json_die("failure"); + } + list($Name, $Description, $CreatorID, $Deleted, $CollageCategoryID, $Locked, $MaxGroups, $MaxGroupsPerUser, $Updated, $Subscribers) = $DB->next_record(MYSQLI_NUM); + $CommentList = null; + $SetCache = true; } // TODO: Cache this $DB->query(" - SELECT GroupID - FROM collages_torrents - WHERE CollageID = $CollageID"); + SELECT GroupID + FROM collages_torrents + WHERE CollageID = $CollageID"); $TorrentGroups = $DB->collect('GroupID'); $JSON = array( - 'id' => (int)$CollageID, - 'name' => $Name, - 'description' => Text::full_format($Description), - 'creatorID' => (int)$CreatorID, - 'deleted' => (bool)$Deleted, - 'collageCategoryID' => (int)$CollageCategoryID, - 'collageCategoryName' => $CollageCats[(int)$CollageCategoryID], - 'locked' => (bool)$Locked, - 'maxGroups' => (int)$MaxGroups, - 'maxGroupsPerUser' => (int)$MaxGroupsPerUser, - 'hasBookmarked' => Bookmarks::has_bookmarked('collage', $CollageID), - 'subscriberCount' => (int)$Subscribers, - 'torrentGroupIDList' => $TorrentGroups + 'id' => (int)$CollageID, + 'name' => $Name, + 'description' => Text::full_format($Description), + 'creatorID' => (int)$CreatorID, + 'deleted' => (bool)$Deleted, + 'collageCategoryID' => (int)$CollageCategoryID, + 'collageCategoryName' => $CollageCats[(int)$CollageCategoryID], + 'locked' => (bool)$Locked, + 'maxGroups' => (int)$MaxGroups, + 'maxGroupsPerUser' => (int)$MaxGroupsPerUser, + 'hasBookmarked' => Bookmarks::has_bookmarked('collage', $CollageID), + 'subscriberCount' => (int)$Subscribers, + 'torrentGroupIDList' => $TorrentGroups ); if ($CollageCategoryID != array_search(ARTIST_COLLAGE, $CollageCats)) { - // torrent collage - $TorrentGroups = array(); - $DB->query(" - SELECT - ct.GroupID - FROM collages_torrents AS ct - JOIN torrents_group AS tg ON tg.ID = ct.GroupID - WHERE ct.CollageID = '$CollageID' - ORDER BY ct.Sort"); - $GroupIDs = $DB->collect('GroupID'); - $GroupList = Torrents::get_groups($GroupIDs); - foreach ($GroupIDs as $GroupID) { - if (!empty($GroupList[$GroupID])) { - $GroupDetails = Torrents::array_group($GroupList[$GroupID]); - if ($GroupDetails['GroupCategoryID'] > 0 && $Categories[$GroupDetails['GroupCategoryID'] - 1] == 'Music') { - $ArtistForm = $GroupDetails['ExtendedArtists']; - $JsonMusicInfo = array( - 'composers' => isset($ArtistForm[4]) ? pullmediainfo($ArtistForm[4]) : array(), - 'dj' => isset($ArtistForm[6]) ? pullmediainfo($ArtistForm[6]) : array(), - 'artists' => isset($ArtistForm[1]) ? pullmediainfo($ArtistForm[1]) : array(), - 'with' => isset($ArtistForm[2]) ? pullmediainfo($ArtistForm[2]) : array(), - 'conductor' => isset($ArtistForm[5]) ? pullmediainfo($ArtistForm[5]) : array(), - 'remixedBy' => isset($ArtistForm[3]) ? pullmediainfo($ArtistForm[3]) : array(), - 'producer' => isset($ArtistForm[7]) ? pullmediainfo($ArtistForm[7]) : array() - ); - } else { - $JsonMusicInfo = null; - } - $TorrentList = array(); - foreach ($GroupDetails['Torrents'] as $Torrent) { - $TorrentList[] = array( - 'torrentid' => (int)$Torrent['ID'], - 'media' => $Torrent['Media'], - 'format' => $Torrent['Format'], - 'encoding' => $Torrent['Encoding'], - 'remastered' => ($Torrent['Remastered'] == 1), - 'remasterYear' => (int)$Torrent['RemasterYear'], - 'remasterTitle' => $Torrent['RemasterTitle'], - 'remasterRecordLabel' => $Torrent['RemasterRecordLabel'], - 'remasterCatalogueNumber' => $Torrent['RemasterCatalogueNumber'], - 'scene' => ($Torrent['Scene'] == 1), - 'hasLog' => ($Torrent['HasLog'] == 1), - 'hasCue' => ($Torrent['HasCue'] == 1), - 'logScore' => (int)$Torrent['LogScore'], - 'fileCount' => (int)$Torrent['FileCount'], - 'size' => (int)$Torrent['Size'], - 'seeders' => (int)$Torrent['Seeders'], - 'leechers' => (int)$Torrent['Leechers'], - 'snatched' => (int)$Torrent['Snatched'], - 'freeTorrent' => ($Torrent['FreeTorrent'] == 1), - 'reported' => (count(Torrents::get_reports((int)$Torrent['ID'])) > 0), - 'time' => $Torrent['Time'] - ); - } - $TorrentGroups[] = array( - 'id' => $GroupDetails['GroupID'], - 'name' => $GroupDetails['GroupName'], - 'year' => $GroupDetails['GroupYear'], - 'categoryId' => $GroupDetails['GroupCategoryID'], - 'recordLabel' => $GroupDetails['GroupRecordLabel'], - 'catalogueNumber' => $GroupDetails['GroupCatalogueNumber'], - 'vanityHouse' => $GroupDetails['GroupVanityHouse'], - 'tagList' => $GroupDetails['TagList'], - 'releaseType' => $GroupDetails['ReleaseType'], - 'wikiImage' => $GroupDetails['WikiImage'], - 'musicInfo' => $JsonMusicInfo, - 'torrents' => $TorrentList - ); - } - } - $JSON['torrentgroups'] = $TorrentGroups; + // torrent collage + $TorrentGroups = []; + $DB->query(" + SELECT + ct.GroupID + FROM collages_torrents AS ct + JOIN torrents_group AS tg ON tg.ID = ct.GroupID + WHERE ct.CollageID = '$CollageID' + ORDER BY ct.Sort"); + $GroupIDs = $DB->collect('GroupID'); + $GroupList = Torrents::get_groups($GroupIDs); + foreach ($GroupIDs as $GroupID) { + if (!empty($GroupList[$GroupID])) { + $GroupDetails = Torrents::array_group($GroupList[$GroupID]); + if ($GroupDetails['GroupCategoryID'] > 0 && $Categories[$GroupDetails['GroupCategoryID'] - 1] == 'Music') { + $ArtistForm = $GroupDetails['ExtendedArtists']; + $JsonMusicInfo = array( + 'composers' => isset($ArtistForm[4]) ? pullmediainfo($ArtistForm[4]) : [], + 'dj' => isset($ArtistForm[6]) ? pullmediainfo($ArtistForm[6]) : [], + 'artists' => isset($ArtistForm[1]) ? pullmediainfo($ArtistForm[1]) : [], + 'with' => isset($ArtistForm[2]) ? pullmediainfo($ArtistForm[2]) : [], + 'conductor' => isset($ArtistForm[5]) ? pullmediainfo($ArtistForm[5]) : [], + 'remixedBy' => isset($ArtistForm[3]) ? pullmediainfo($ArtistForm[3]) : [], + 'producer' => isset($ArtistForm[7]) ? pullmediainfo($ArtistForm[7]) : [] + ); + } else { + $JsonMusicInfo = null; + } + $TorrentList = []; + foreach ($GroupDetails['Torrents'] as $Torrent) { + $TorrentList[] = array( + 'torrentid' => (int)$Torrent['ID'], + 'media' => $Torrent['Media'], + 'format' => $Torrent['Format'], + 'encoding' => $Torrent['Encoding'], + 'remastered' => ($Torrent['Remastered'] == 1), + 'remasterYear' => (int)$Torrent['RemasterYear'], + 'remasterTitle' => $Torrent['RemasterTitle'], + 'remasterRecordLabel' => $Torrent['RemasterRecordLabel'], + 'remasterCatalogueNumber' => $Torrent['RemasterCatalogueNumber'], + 'scene' => ($Torrent['Scene'] == 1), + 'hasLog' => ($Torrent['HasLog'] == 1), + 'hasCue' => ($Torrent['HasCue'] == 1), + 'logScore' => (int)$Torrent['LogScore'], + 'fileCount' => (int)$Torrent['FileCount'], + 'size' => (int)$Torrent['Size'], + 'seeders' => (int)$Torrent['Seeders'], + 'leechers' => (int)$Torrent['Leechers'], + 'snatched' => (int)$Torrent['Snatched'], + 'freeTorrent' => ($Torrent['FreeTorrent'] == 1), + 'reported' => (count(Torrents::get_reports((int)$Torrent['ID'])) > 0), + 'time' => $Torrent['Time'] + ); + } + $TorrentGroups[] = array( + 'id' => $GroupDetails['GroupID'], + 'name' => $GroupDetails['GroupName'], + 'year' => $GroupDetails['GroupYear'], + 'categoryId' => $GroupDetails['GroupCategoryID'], + 'recordLabel' => $GroupDetails['GroupRecordLabel'], + 'catalogueNumber' => $GroupDetails['GroupCatalogueNumber'], + 'vanityHouse' => $GroupDetails['GroupVanityHouse'], + 'tagList' => $GroupDetails['TagList'], + 'releaseType' => $GroupDetails['ReleaseType'], + 'wikiImage' => $GroupDetails['WikiImage'], + 'musicInfo' => $JsonMusicInfo, + 'torrents' => $TorrentList + ); + } + } + $JSON['torrentgroups'] = $TorrentGroups; } else { - // artist collage - $DB->query(" - SELECT - ca.ArtistID, - ag.Name, - aw.Image - FROM collages_artists AS ca - JOIN artists_group AS ag ON ag.ArtistID=ca.ArtistID - LEFT JOIN wiki_artists AS aw ON aw.RevisionID = ag.RevisionID - WHERE ca.CollageID='$CollageID' - ORDER BY ca.Sort"); - $Artists = array(); - while (list($ArtistID, $ArtistName, $ArtistImage) = $DB->next_record()) { - $Artists[] = array( - 'id' => (int)$ArtistID, - 'name' => $ArtistName, - 'image' => $ArtistImage - ); - } - $JSON['artists'] = $Artists; + // artist collage + $DB->query(" + SELECT + ca.ArtistID, + ag.Name, + aw.Image + FROM collages_artists AS ca + JOIN artists_group AS ag ON ag.ArtistID=ca.ArtistID + LEFT JOIN wiki_artists AS aw ON aw.RevisionID = ag.RevisionID + WHERE ca.CollageID='$CollageID' + ORDER BY ca.Sort"); + $Artists = []; + while (list($ArtistID, $ArtistName, $ArtistImage) = $DB->next_record()) { + $Artists[] = array( + 'id' => (int)$ArtistID, + 'name' => $ArtistName, + 'image' => $ArtistImage + ); + } + $JSON['artists'] = $Artists; } if (isset($SetCache)) { - $CollageData = array( - $Name, - $Description, - $CommentList, - (bool)$Deleted, - (int)$CollageCategoryID, - (int)$CreatorID, - (bool)$Locked, - (int)$MaxGroups, - (int)$MaxGroupsPerUser, - $Updated, - (int)$Subscribers); - $Cache->cache_value($CacheKey, $CollageData, 3600); + $CollageData = array( + $Name, + $Description, + $CommentList, + (bool)$Deleted, + (int)$CollageCategoryID, + (int)$CreatorID, + (bool)$Locked, + (int)$MaxGroups, + (int)$MaxGroupsPerUser, + $Updated, + (int)$Subscribers); + $Cache->cache_value($CacheKey, $CollageData, 3600); } json_print("success", $JSON); diff --git a/sections/ajax/community_stats.php b/sections/ajax/community_stats.php index 220274562..817454928 100644 --- a/sections/ajax/community_stats.php +++ b/sections/ajax/community_stats.php @@ -1,67 +1,67 @@ - false, - 'seeding' => false, - 'snatched' => false, - 'usnatched' => false, - 'downloaded' => false, - 'udownloaded' => false, - 'seedingperc' => false, + 'leeching' => false, + 'seeding' => false, + 'snatched' => false, + 'usnatched' => false, + 'downloaded' => false, + 'udownloaded' => false, + 'seedingperc' => false, ); $User = Users::user_info($UserID); function check_paranoia_here($Setting) { - global $User; - return check_paranoia($Setting, $User['Paranoia'], $User['Class'], $User['ID']); + global $User; + return check_paranoia($Setting, $User['Paranoia'], $User['Class'], $User['ID']); } if (check_paranoia_here('seeding+') || check_paranoia_here('leeching+')) { - $DB->query(" - SELECT IF(remaining = 0, 'Seeding', 'Leeching') AS Type, COUNT(x.uid) - FROM xbt_files_users AS x - INNER JOIN torrents AS t ON t.ID = x.fid - WHERE x.uid = '$UserID' - AND x.active = 1 - GROUP BY Type"); - $PeerCount = $DB->to_array(0, MYSQLI_NUM, false); - if (check_paranoia_here('seeding+')) { - $Seeding = isset($PeerCount['Seeding']) ? $PeerCount['Seeding'][1] : 0; - $CommStats['seeding'] = number_format($Seeding); - } - if (check_paranoia_here('leeching+')) { - $CommStats['leeching'] = isset($PeerCount['Leeching']) ? number_format($PeerCount['Leeching'][1]) : 0; - } + $DB->query(" + SELECT IF(remaining = 0, 'Seeding', 'Leeching') AS Type, COUNT(x.uid) + FROM xbt_files_users AS x + INNER JOIN torrents AS t ON t.ID = x.fid + WHERE x.uid = '$UserID' + AND x.active = 1 + GROUP BY Type"); + $PeerCount = $DB->to_array(0, MYSQLI_NUM, false); + if (check_paranoia_here('seeding+')) { + $Seeding = isset($PeerCount['Seeding']) ? $PeerCount['Seeding'][1] : 0; + $CommStats['seeding'] = number_format($Seeding); + } + if (check_paranoia_here('leeching+')) { + $CommStats['leeching'] = isset($PeerCount['Leeching']) ? number_format($PeerCount['Leeching'][1]) : 0; + } } if (check_paranoia_here('snatched+')) { - $DB->query(" - SELECT COUNT(x.uid), COUNT(DISTINCT x.fid) - FROM xbt_snatched AS x - INNER JOIN torrents AS t ON t.ID = x.fid - WHERE x.uid = '$UserID'"); - list($Snatched, $UniqueSnatched) = $DB->next_record(MYSQLI_NUM, false); - $CommStats['snatched'] = number_format($Snatched); - if (check_perms('site_view_torrent_snatchlist', $User['Class'])) { - $CommStats['usnatched'] = number_format($UniqueSnatched); - } - if (check_paranoia_here('seeding+') && check_paranoia_here('snatched+') && $UniqueSnatched > 0) { - $CommStats['seedingperc'] = 100 * min(1, round($Seeding / $UniqueSnatched, 2)); - } + $DB->query(" + SELECT COUNT(x.uid), COUNT(DISTINCT x.fid) + FROM xbt_snatched AS x + INNER JOIN torrents AS t ON t.ID = x.fid + WHERE x.uid = '$UserID'"); + list($Snatched, $UniqueSnatched) = $DB->next_record(MYSQLI_NUM, false); + $CommStats['snatched'] = number_format($Snatched); + if (check_perms('site_view_torrent_snatchlist', $User['Class'])) { + $CommStats['usnatched'] = number_format($UniqueSnatched); + } + if (check_paranoia_here('seeding+') && check_paranoia_here('snatched+') && $UniqueSnatched > 0) { + $CommStats['seedingperc'] = 100 * min(1, round($Seeding / $UniqueSnatched, 2)); + } } if (check_perms('site_view_torrent_snatchlist', $Class)) { - $DB->query(" - SELECT COUNT(ud.UserID), COUNT(DISTINCT ud.TorrentID) - FROM users_downloads AS ud - JOIN torrents AS t ON t.ID = ud.TorrentID - WHERE ud.UserID = '$UserID'"); - list($NumDownloads, $UniqueDownloads) = $DB->next_record(MYSQLI_NUM, false); - $CommStats['downloaded'] = number_format($NumDownloads); - $CommStats['udownloaded'] = number_format($UniqueDownloads); + $DB->query(" + SELECT COUNT(ud.UserID), COUNT(DISTINCT ud.TorrentID) + FROM users_downloads AS ud + JOIN torrents AS t ON t.ID = ud.TorrentID + WHERE ud.UserID = '$UserID'"); + list($NumDownloads, $UniqueDownloads) = $DB->next_record(MYSQLI_NUM, false); + $CommStats['downloaded'] = number_format($NumDownloads); + $CommStats['udownloaded'] = number_format($UniqueDownloads); } json_die('success', $CommStats); diff --git a/sections/ajax/forum/forum.php b/sections/ajax/forum/forum.php index 32a6a5258..aa2c3a92a 100644 --- a/sections/ajax/forum/forum.php +++ b/sections/ajax/forum/forum.php @@ -1,12 +1,12 @@ - 'failure')); - die(); + print json_encode(array('status' => 'failure')); + die(); } if (isset($_GET['pp'])) { - $PerPage = (int) $_GET['pp']; + $PerPage = (int) $_GET['pp']; } elseif (isset($LoggedUser['PostsPerPage'])) { - $PerPage = $LoggedUser['PostsPerPage']; + $PerPage = $LoggedUser['PostsPerPage']; } else { - $PerPage = POSTS_PER_PAGE; + $PerPage = POSTS_PER_PAGE; } list($Page, $Limit) = Format::page_limit(TOPICS_PER_PAGE); @@ -34,146 +34,146 @@ // Caching anything beyond the first page of any given forum is just wasting ram // users are more likely to search then to browse to page 2 if ($Page == 1) { - list($Forum,,,$Stickies) = $Cache->get_value("forums_$ForumID"); + list($Forum,,,$Stickies) = $Cache->get_value("forums_$ForumID"); } if (!isset($Forum) || !is_array($Forum)) { - $DB->query(" - SELECT - ID, - Title, - AuthorID, - IsLocked, - IsSticky, - NumPosts, - LastPostID, - LastPostTime, - LastPostAuthorID - FROM forums_topics - WHERE ForumID = '$ForumID' - ORDER BY IsSticky DESC, LastPostTime DESC - LIMIT $Limit"); // Can be cached until someone makes a new post - $Forum = $DB->to_array('ID',MYSQLI_ASSOC, false); - if ($Page == 1) { - $DB->query(" - SELECT COUNT(ID) - FROM forums_topics - WHERE ForumID = '$ForumID' - AND IsSticky = '1'"); - list($Stickies) = $DB->next_record(); - $Cache->cache_value("forums_$ForumID", array($Forum, '', 0, $Stickies), 0); - } + $DB->query(" + SELECT + ID, + Title, + AuthorID, + IsLocked, + IsSticky, + NumPosts, + LastPostID, + LastPostTime, + LastPostAuthorID + FROM forums_topics + WHERE ForumID = '$ForumID' + ORDER BY IsSticky DESC, LastPostTime DESC + LIMIT $Limit"); // Can be cached until someone makes a new post + $Forum = $DB->to_array('ID',MYSQLI_ASSOC, false); + if ($Page == 1) { + $DB->query(" + SELECT COUNT(ID) + FROM forums_topics + WHERE ForumID = '$ForumID' + AND IsSticky = '1'"); + list($Stickies) = $DB->next_record(); + $Cache->cache_value("forums_$ForumID", array($Forum, '', 0, $Stickies), 0); + } } if (!isset($Forums[$ForumID])) { - json_die("failure"); + json_die("failure"); } // Make sure they're allowed to look at the page if (!check_perms('site_moderate_forums')) { - if (isset($LoggedUser['CustomForums'][$ForumID]) && $LoggedUser['CustomForums'][$ForumID] === 0) { - json_die("failure", "insufficient permissions to view page"); - } + if (isset($LoggedUser['CustomForums'][$ForumID]) && $LoggedUser['CustomForums'][$ForumID] === 0) { + json_die("failure", "insufficient permissions to view page"); + } } if ($LoggedUser['CustomForums'][$ForumID] != 1 && $Forums[$ForumID]['MinClassRead'] > $LoggedUser['Class']) { - json_die("failure", "insufficient permissions to view page"); + json_die("failure", "insufficient permissions to view page"); } $ForumName = display_str($Forums[$ForumID]['Name']); -$JsonSpecificRules = array(); +$JsonSpecificRules = []; foreach ($Forums[$ForumID]['SpecificRules'] as $ThreadIDs) { - $Thread = Forums::get_thread_info($ThreadIDs); - $JsonSpecificRules[] = array( - 'threadId' => (int)$ThreadIDs, - 'thread' => display_str($Thread['Title']) - ); + $Thread = Forums::get_thread_info($ThreadIDs); + $JsonSpecificRules[] = array( + 'threadId' => (int)$ThreadIDs, + 'thread' => display_str($Thread['Title']) + ); } $Pages = Format::get_pages($Page, $Forums[$ForumID]['NumTopics'], TOPICS_PER_PAGE, 9); if (count($Forum) === 0) { - print - json_encode( - array( - 'status' => 'success', - 'forumName' => $ForumName, - 'threads' => array() - ) - ); + print + json_encode( + array( + 'status' => 'success', + 'forumName' => $ForumName, + 'threads' => [] + ) + ); } else { - // forums_last_read_topics is a record of the last post a user read in a topic, and what page that was on - $DB->query(" - SELECT - l.TopicID, - l.PostID, - CEIL( - ( - SELECT COUNT(p.ID) - FROM forums_posts AS p - WHERE p.TopicID = l.TopicID - AND p.ID <= l.PostID - ) / $PerPage - ) AS Page - FROM forums_last_read_topics AS l - WHERE l.TopicID IN(".implode(', ', array_keys($Forum)).') - AND l.UserID = \''.$LoggedUser['ID'].'\''); - - // Turns the result set into a multi-dimensional array, with - // forums_last_read_topics.TopicID as the key. - // This is done here so we get the benefit of the caching, and we - // don't have to make a database query for each topic on the page - $LastRead = $DB->to_array('TopicID'); - - $JsonTopics = array(); - foreach ($Forum as $Topic) { - list($TopicID, $Title, $AuthorID, $Locked, $Sticky, $PostCount, $LastID, $LastTime, $LastAuthorID) = array_values($Topic); - - // handle read/unread posts - the reason we can't cache the whole page - if ((!$Locked || $Sticky) - && ((empty($LastRead[$TopicID]) || $LastRead[$TopicID]['PostID'] < $LastID) - && strtotime($LastTime) > $LoggedUser['CatchupTime']) - ) { - $Read = 'unread'; - } else { - $Read = 'read'; - } - $UserInfo = Users::user_info($AuthorID); - $AuthorName = $UserInfo['Username']; - $UserInfo = Users::user_info($LastAuthorID); - $LastAuthorName = $UserInfo['Username']; - // Bug fix for no last time available - if ($LastTime == '0000-00-00 00:00:00') { - $LastTime = ''; - } - - $JsonTopics[] = array( - 'topicId' => (int)$TopicID, - 'title' => display_str($Title), - 'authorId' => (int)$AuthorID, - 'authorName' => $AuthorName, - 'locked' => $Locked == 1, - 'sticky' => $Sticky == 1, - 'postCount' => (int)$PostCount, - 'lastID' => ($LastID == null) ? 0 : (int)$LastID, - 'lastTime' => $LastTime, - 'lastAuthorId' => ($LastAuthorID == null) ? 0 : (int)$LastAuthorID, - 'lastAuthorName' => ($LastAuthorName == null) ? '' : $LastAuthorName, - 'lastReadPage' => ($LastRead[$TopicID]['Page'] == null) ? 0 : (int)$LastRead[$TopicID]['Page'], - 'lastReadPostId' => ($LastRead[$TopicID]['PostID'] == null) ? 0 : (int)$LastRead[$TopicID]['PostID'], - 'read' => $Read == 'read' - ); - } - - print - json_encode( - array( - 'status' => 'success', - 'response' => array( - 'forumName' => $ForumName, - 'specificRules' => $JsonSpecificRules, - 'currentPage' => (int)$Page, - 'pages' => ceil($Forums[$ForumID]['NumTopics'] / TOPICS_PER_PAGE), - 'threads' => $JsonTopics - ) - ) - ); + // forums_last_read_topics is a record of the last post a user read in a topic, and what page that was on + $DB->query(" + SELECT + l.TopicID, + l.PostID, + CEIL( + ( + SELECT COUNT(p.ID) + FROM forums_posts AS p + WHERE p.TopicID = l.TopicID + AND p.ID <= l.PostID + ) / $PerPage + ) AS Page + FROM forums_last_read_topics AS l + WHERE l.TopicID IN(".implode(', ', array_keys($Forum)).') + AND l.UserID = \''.$LoggedUser['ID'].'\''); + + // Turns the result set into a multi-dimensional array, with + // forums_last_read_topics.TopicID as the key. + // This is done here so we get the benefit of the caching, and we + // don't have to make a database query for each topic on the page + $LastRead = $DB->to_array('TopicID'); + + $JsonTopics = []; + foreach ($Forum as $Topic) { + list($TopicID, $Title, $AuthorID, $Locked, $Sticky, $PostCount, $LastID, $LastTime, $LastAuthorID) = array_values($Topic); + + // handle read/unread posts - the reason we can't cache the whole page + if ((!$Locked || $Sticky) + && ((empty($LastRead[$TopicID]) || $LastRead[$TopicID]['PostID'] < $LastID) + && strtotime($LastTime) > $LoggedUser['CatchupTime']) + ) { + $Read = 'unread'; + } else { + $Read = 'read'; + } + $UserInfo = Users::user_info($AuthorID); + $AuthorName = $UserInfo['Username']; + $UserInfo = Users::user_info($LastAuthorID); + $LastAuthorName = $UserInfo['Username']; + // Bug fix for no last time available + if ($LastTime == '0000-00-00 00:00:00') { + $LastTime = ''; + } + + $JsonTopics[] = array( + 'topicId' => (int)$TopicID, + 'title' => display_str($Title), + 'authorId' => (int)$AuthorID, + 'authorName' => $AuthorName, + 'locked' => $Locked == 1, + 'sticky' => $Sticky == 1, + 'postCount' => (int)$PostCount, + 'lastID' => ($LastID == null) ? 0 : (int)$LastID, + 'lastTime' => $LastTime, + 'lastAuthorId' => ($LastAuthorID == null) ? 0 : (int)$LastAuthorID, + 'lastAuthorName' => ($LastAuthorName == null) ? '' : $LastAuthorName, + 'lastReadPage' => ($LastRead[$TopicID]['Page'] == null) ? 0 : (int)$LastRead[$TopicID]['Page'], + 'lastReadPostId' => ($LastRead[$TopicID]['PostID'] == null) ? 0 : (int)$LastRead[$TopicID]['PostID'], + 'read' => $Read == 'read' + ); + } + + print + json_encode( + array( + 'status' => 'success', + 'response' => array( + 'forumName' => $ForumName, + 'specificRules' => $JsonSpecificRules, + 'currentPage' => (int)$Page, + 'pages' => ceil($Forums[$ForumID]['NumTopics'] / TOPICS_PER_PAGE), + 'threads' => $JsonTopics + ) + ) + ); } ?> diff --git a/sections/ajax/forum/index.php b/sections/ajax/forum/index.php index baeba883e..bbe5eaed4 100644 --- a/sections/ajax/forum/index.php +++ b/sections/ajax/forum/index.php @@ -1,30 +1,30 @@ - 'failure')); - die(); + print json_encode(array('status' => 'failure')); + die(); } else { - // Replace the old hard-coded forum categories - $ForumCats = Forums::get_forum_categories(); + // Replace the old hard-coded forum categories + $ForumCats = Forums::get_forum_categories(); - //This variable contains all our lovely forum data - $Forums = Forums::get_forums(); + //This variable contains all our lovely forum data + $Forums = Forums::get_forums(); - if (empty($_GET['type']) || $_GET['type'] == 'main') { - include(SERVER_ROOT.'/sections/ajax/forum/main.php'); - } else { - switch ($_GET['type']) { - case 'viewforum': - include(SERVER_ROOT.'/sections/ajax/forum/forum.php'); - break; - case 'viewthread': - include(SERVER_ROOT.'/sections/ajax/forum/thread.php'); - break; - default: - print json_encode(array('status' => 'failure')); - break; - } - } + if (empty($_GET['type']) || $_GET['type'] == 'main') { + include(SERVER_ROOT.'/sections/ajax/forum/main.php'); + } else { + switch ($_GET['type']) { + case 'viewforum': + include(SERVER_ROOT.'/sections/ajax/forum/forum.php'); + break; + case 'viewthread': + include(SERVER_ROOT.'/sections/ajax/forum/thread.php'); + break; + default: + print json_encode(array('status' => 'failure')); + break; + } + } } diff --git a/sections/ajax/forum/main.php b/sections/ajax/forum/main.php index 2f8d00026..f0868caa5 100644 --- a/sections/ajax/forum/main.php +++ b/sections/ajax/forum/main.php @@ -1,115 +1,115 @@ -query(" - SELECT - l.TopicID, - l.PostID, - CEIL( - ( - SELECT COUNT(p.ID) - FROM forums_posts AS p - WHERE p.TopicID = l.TopicID - AND p.ID <= l.PostID - ) / $PerPage - ) AS Page - FROM forums_last_read_topics AS l - WHERE l.TopicID IN(".implode(',', $TopicIDs).") - AND l.UserID = '$LoggedUser[ID]'"); - $LastRead = $DB->to_array('TopicID', MYSQLI_ASSOC); + $DB->query(" + SELECT + l.TopicID, + l.PostID, + CEIL( + ( + SELECT COUNT(p.ID) + FROM forums_posts AS p + WHERE p.TopicID = l.TopicID + AND p.ID <= l.PostID + ) / $PerPage + ) AS Page + FROM forums_last_read_topics AS l + WHERE l.TopicID IN(".implode(',', $TopicIDs).") + AND l.UserID = '$LoggedUser[ID]'"); + $LastRead = $DB->to_array('TopicID', MYSQLI_ASSOC); } else { - $LastRead = array(); + $LastRead = []; } $DB->query(" - SELECT RestrictedForums - FROM users_info - WHERE UserID = ".$LoggedUser['ID']); + SELECT RestrictedForums + FROM users_info + WHERE UserID = ".$LoggedUser['ID']); list($RestrictedForums) = $DB->next_record(); $RestrictedForums = explode(',', $RestrictedForums); $PermittedForums = array_keys($LoggedUser['PermittedForums']); -$JsonCategories = array(); -$JsonCategory = array(); -$JsonForums = array(); +$JsonCategories = []; +$JsonCategory = []; +$JsonForums = []; foreach ($Forums as $Forum) { - list($ForumID, $CategoryID, $ForumName, $ForumDescription, $MinRead, $MinWrite, $MinCreate, $NumTopics, $NumPosts, $LastPostID, $LastAuthorID, $LastTopicID, $LastTime, $SpecificRules, $LastTopic, $Locked, $Sticky) = array_values($Forum); - if ($LoggedUser['CustomForums'][$ForumID] != 1 - && ($MinRead > $LoggedUser['Class'] - || array_search($ForumID, $RestrictedForums) !== false) - ) { - continue; - } - $ForumDescription = display_str($ForumDescription); + list($ForumID, $CategoryID, $ForumName, $ForumDescription, $MinRead, $MinWrite, $MinCreate, $NumTopics, $NumPosts, $LastPostID, $LastAuthorID, $LastTopicID, $LastTime, $SpecificRules, $LastTopic, $Locked, $Sticky) = array_values($Forum); + if ($LoggedUser['CustomForums'][$ForumID] != 1 + && ($MinRead > $LoggedUser['Class'] + || array_search($ForumID, $RestrictedForums) !== false) + ) { + continue; + } + $ForumDescription = display_str($ForumDescription); - if ($CategoryID != $LastCategoryID) { - if (!empty($JsonForums) && !empty($JsonCategory)) { - $JsonCategory['forums'] = $JsonForums; - $JsonCategories[] = $JsonCategory; - } - $LastCategoryID = $CategoryID; - $JsonCategory = array( - 'categoryID' => (int)$CategoryID, - 'categoryName' => $ForumCats[$CategoryID] - ); - $JsonForums = array(); - } + if ($CategoryID != $LastCategoryID) { + if (!empty($JsonForums) && !empty($JsonCategory)) { + $JsonCategory['forums'] = $JsonForums; + $JsonCategories[] = $JsonCategory; + } + $LastCategoryID = $CategoryID; + $JsonCategory = array( + 'categoryID' => (int)$CategoryID, + 'categoryName' => $ForumCats[$CategoryID] + ); + $JsonForums = []; + } - if ((!$Locked || $Sticky) - && $LastPostID != 0 - && ((empty($LastRead[$LastTopicID]) || $LastRead[$LastTopicID]['PostID'] < $LastPostID) - && strtotime($LastTime) > $LoggedUser['CatchupTime']) - ) { - $Read = 'unread'; - } else { - $Read = 'read'; - } - $UserInfo = Users::user_info($LastAuthorID); + if ((!$Locked || $Sticky) + && $LastPostID != 0 + && ((empty($LastRead[$LastTopicID]) || $LastRead[$LastTopicID]['PostID'] < $LastPostID) + && strtotime($LastTime) > $LoggedUser['CatchupTime']) + ) { + $Read = 'unread'; + } else { + $Read = 'read'; + } + $UserInfo = Users::user_info($LastAuthorID); - $JsonForums[] = array( - 'forumId' => (int)$ForumID, - 'forumName' => $ForumName, - 'forumDescription' => $ForumDescription, - 'numTopics' => (float)$NumTopics, - 'numPosts' => (float)$NumPosts, - 'lastPostId' => (float)$LastPostID, - 'lastAuthorId' => (float)$LastAuthorID, - 'lastPostAuthorName' => $UserInfo['Username'], - 'lastTopicId' => (float)$LastTopicID, - 'lastTime' => $LastTime, - 'specificRules' => $SpecificRules, - 'lastTopic' => display_str($LastTopic), - 'read' => $Read == 1, - 'locked' => $Locked == 1, - 'sticky' => $Sticky == 1 - ); + $JsonForums[] = array( + 'forumId' => (int)$ForumID, + 'forumName' => $ForumName, + 'forumDescription' => $ForumDescription, + 'numTopics' => (float)$NumTopics, + 'numPosts' => (float)$NumPosts, + 'lastPostId' => (float)$LastPostID, + 'lastAuthorId' => (float)$LastAuthorID, + 'lastPostAuthorName' => $UserInfo['Username'], + 'lastTopicId' => (float)$LastTopicID, + 'lastTime' => $LastTime, + 'specificRules' => $SpecificRules, + 'lastTopic' => display_str($LastTopic), + 'read' => $Read == 1, + 'locked' => $Locked == 1, + 'sticky' => $Sticky == 1 + ); } // ...And an extra one to catch the last category. if (!empty($JsonForums) && !empty($JsonCategory)) { - $JsonCategory['forums'] = $JsonForums; - $JsonCategories[] = $JsonCategory; + $JsonCategory['forums'] = $JsonForums; + $JsonCategories[] = $JsonCategory; } print json_encode( - array( - 'status' => 'success', - 'response' => array( - 'categories' => $JsonCategories - ) - ) + array( + 'status' => 'success', + 'response' => array( + 'categories' => $JsonCategories + ) + ) ); diff --git a/sections/ajax/forum/thread.php b/sections/ajax/forum/thread.php index dd801d556..fd023034b 100644 --- a/sections/ajax/forum/thread.php +++ b/sections/ajax/forum/thread.php @@ -3,9 +3,9 @@ /**********|| Page to show individual threads || ********************************\ Things to expect in $_GET: - ThreadID: ID of the forum curently being browsed - page: The page the user's on. - page = 1 is the same as no page + ThreadID: ID of the forum curently being browsed + page: The page the user's on. + page = 1 is the same as no page ********************************************************************************/ @@ -13,36 +13,36 @@ // Check for lame SQL injection attempts if (!isset($_GET['threadid']) || !is_number($_GET['threadid'])) { - if (isset($_GET['topicid']) && is_number($_GET['topicid'])) { - $ThreadID = $_GET['topicid']; - } elseif (isset($_GET['postid']) && is_number($_GET['postid'])) { - $DB->query(" - SELECT TopicID - FROM forums_posts - WHERE ID = $_GET[postid]"); - list($ThreadID) = $DB->next_record(); - if ($ThreadID) { - //Redirect postid to threadid when necessary. - header("Location: ajax.php?action=forum&type=viewthread&threadid=$ThreadID&postid=$_GET[postid]"); - die(); - } else { - print json_encode(array('status' => 'failure')); - die(); - } - } else { - print json_encode(array('status' => 'failure')); - die(); - } + if (isset($_GET['topicid']) && is_number($_GET['topicid'])) { + $ThreadID = $_GET['topicid']; + } elseif (isset($_GET['postid']) && is_number($_GET['postid'])) { + $DB->query(" + SELECT TopicID + FROM forums_posts + WHERE ID = $_GET[postid]"); + list($ThreadID) = $DB->next_record(); + if ($ThreadID) { + //Redirect postid to threadid when necessary. + header("Location: ajax.php?action=forum&type=viewthread&threadid=$ThreadID&postid=$_GET[postid]"); + die(); + } else { + print json_encode(array('status' => 'failure')); + die(); + } + } else { + print json_encode(array('status' => 'failure')); + die(); + } } else { - $ThreadID = $_GET['threadid']; + $ThreadID = $_GET['threadid']; } if (isset($_GET['pp'])) { - $PerPage = $_GET['pp']; + $PerPage = $_GET['pp']; } elseif (isset($LoggedUser['PostsPerPage'])) { - $PerPage = $LoggedUser['PostsPerPage']; + $PerPage = $LoggedUser['PostsPerPage']; } else { - $PerPage = POSTS_PER_PAGE; + $PerPage = POSTS_PER_PAGE; } @@ -52,240 +52,240 @@ // Thread information, constant across all pages $ThreadInfo = Forums::get_thread_info($ThreadID, true, true); if ($ThreadInfo === null) { - json_die('failure', 'no such thread exists'); + json_die('failure', 'no such thread exists'); } $ForumID = $ThreadInfo['ForumID']; // Make sure they're allowed to look at the page if (!Forums::check_forumperm($ForumID)) { - print json_encode(array('status' => 'failure')); - die(); + print json_encode(array('status' => 'failure')); + die(); } //Post links utilize the catalogue & key params to prevent issues with custom posts per page if ($ThreadInfo['Posts'] > $PerPage) { - if (isset($_GET['post']) && is_number($_GET['post'])) { - $PostNum = $_GET['post']; - } elseif (isset($_GET['postid']) && is_number($_GET['postid'])) { - $DB->query(" - SELECT COUNT(ID) - FROM forums_posts - WHERE TopicID = $ThreadID - AND ID <= $_GET[postid]"); - list($PostNum) = $DB->next_record(); - } else { - $PostNum = 1; - } + if (isset($_GET['post']) && is_number($_GET['post'])) { + $PostNum = $_GET['post']; + } elseif (isset($_GET['postid']) && is_number($_GET['postid'])) { + $DB->query(" + SELECT COUNT(ID) + FROM forums_posts + WHERE TopicID = $ThreadID + AND ID <= $_GET[postid]"); + list($PostNum) = $DB->next_record(); + } else { + $PostNum = 1; + } } else { - $PostNum = 1; + $PostNum = 1; } list($Page, $Limit) = Format::page_limit($PerPage, min($ThreadInfo['Posts'], $PostNum)); if (($Page - 1) * $PerPage > $ThreadInfo['Posts']) { - $Page = ceil($ThreadInfo['Posts'] / $PerPage); + $Page = ceil($ThreadInfo['Posts'] / $PerPage); } list($CatalogueID,$CatalogueLimit) = Format::catalogue_limit($Page, $PerPage, THREAD_CATALOGUE); // Cache catalogue from which the page is selected, allows block caches and future ability to specify posts per page if (!$Catalogue = $Cache->get_value("thread_$ThreadID"."_catalogue_$CatalogueID")) { - $DB->query(" - SELECT - p.ID, - p.AuthorID, - p.AddedTime, - p.Body, - p.EditedUserID, - p.EditedTime - FROM forums_posts AS p - WHERE p.TopicID = '$ThreadID' - AND p.ID != '".$ThreadInfo['StickyPostID']."' - LIMIT $CatalogueLimit"); - $Catalogue = $DB->to_array(false, MYSQLI_ASSOC); - if (!$ThreadInfo['IsLocked'] || $ThreadInfo['IsSticky']) { - $Cache->cache_value("thread_$ThreadID"."_catalogue_$CatalogueID", $Catalogue, 0); - } + $DB->query(" + SELECT + p.ID, + p.AuthorID, + p.AddedTime, + p.Body, + p.EditedUserID, + p.EditedTime + FROM forums_posts AS p + WHERE p.TopicID = '$ThreadID' + AND p.ID != '".$ThreadInfo['StickyPostID']."' + LIMIT $CatalogueLimit"); + $Catalogue = $DB->to_array(false, MYSQLI_ASSOC); + if (!$ThreadInfo['IsLocked'] || $ThreadInfo['IsSticky']) { + $Cache->cache_value("thread_$ThreadID"."_catalogue_$CatalogueID", $Catalogue, 0); + } } $Thread = Format::catalogue_select($Catalogue, $Page, $PerPage, THREAD_CATALOGUE); if ($_GET['updatelastread'] !== '0') { - $LastPost = end($Thread); - $LastPost = $LastPost['ID']; - reset($Thread); - if ($ThreadInfo['Posts'] <= $PerPage * $Page && $ThreadInfo['StickyPostID'] > $LastPost) { - $LastPost = $ThreadInfo['StickyPostID']; - } - //Handle last read - if (!$ThreadInfo['IsLocked'] || $ThreadInfo['IsSticky']) { - $DB->query(" - SELECT PostID - FROM forums_last_read_topics - WHERE UserID = '$LoggedUser[ID]' - AND TopicID = '$ThreadID'"); - list($LastRead) = $DB->next_record(); - if ($LastRead < $LastPost) { - $DB->query(" - INSERT INTO forums_last_read_topics - (UserID, TopicID, PostID) - VALUES - ('$LoggedUser[ID]', '$ThreadID', '".db_string($LastPost)."') - ON DUPLICATE KEY UPDATE - PostID = '$LastPost'"); - } - } + $LastPost = end($Thread); + $LastPost = $LastPost['ID']; + reset($Thread); + if ($ThreadInfo['Posts'] <= $PerPage * $Page && $ThreadInfo['StickyPostID'] > $LastPost) { + $LastPost = $ThreadInfo['StickyPostID']; + } + //Handle last read + if (!$ThreadInfo['IsLocked'] || $ThreadInfo['IsSticky']) { + $DB->query(" + SELECT PostID + FROM forums_last_read_topics + WHERE UserID = '$LoggedUser[ID]' + AND TopicID = '$ThreadID'"); + list($LastRead) = $DB->next_record(); + if ($LastRead < $LastPost) { + $DB->query(" + INSERT INTO forums_last_read_topics + (UserID, TopicID, PostID) + VALUES + ('$LoggedUser[ID]', '$ThreadID', '".db_string($LastPost)."') + ON DUPLICATE KEY UPDATE + PostID = '$LastPost'"); + } + } } //Handle subscriptions $UserSubscriptions = Subscriptions::get_subscriptions(); if (empty($UserSubscriptions)) { - $UserSubscriptions = array(); + $UserSubscriptions = []; } if (in_array($ThreadID, $UserSubscriptions)) { - $Cache->delete_value('subscriptions_user_new_'.$LoggedUser['ID']); + $Cache->delete_value('subscriptions_user_new_'.$LoggedUser['ID']); } -$JsonPoll = array(); +$JsonPoll = []; if ($ThreadInfo['NoPoll'] == 0) { - if (!list($Question, $Answers, $Votes, $Featured, $Closed) = $Cache->get_value("polls_$ThreadID")) { - $DB->query(" - SELECT Question, Answers, Featured, Closed - FROM forums_polls - WHERE TopicID = '$ThreadID'"); - list($Question, $Answers, $Featured, $Closed) = $DB->next_record(MYSQLI_NUM, array(1)); - $Answers = unserialize($Answers); - $DB->query(" - SELECT Vote, COUNT(UserID) - FROM forums_polls_votes - WHERE TopicID = '$ThreadID' - GROUP BY Vote"); - $VoteArray = $DB->to_array(false, MYSQLI_NUM); - - $Votes = array(); - foreach ($VoteArray as $VoteSet) { - list($Key, $Value) = $VoteSet; - $Votes[$Key] = $Value; - } - - foreach (array_keys($Answers) as $i) { - if (!isset($Votes[$i])) { - $Votes[$i] = 0; - } - } - $Cache->cache_value("polls_$ThreadID", array($Question, $Answers, $Votes, $Featured, $Closed), 0); - } - - if (!empty($Votes)) { - $TotalVotes = array_sum($Votes); - $MaxVotes = max($Votes); - } else { - $TotalVotes = 0; - $MaxVotes = 0; - } - - $RevealVoters = in_array($ForumID, $ForumsRevealVoters); - //Polls lose the you voted arrow thingy - $DB->query(" - SELECT Vote - FROM forums_polls_votes - WHERE UserID = '".$LoggedUser['ID']."' - AND TopicID = '$ThreadID'"); - list($UserResponse) = $DB->next_record(); - if (!empty($UserResponse) && $UserResponse != 0) { - $Answers[$UserResponse] = '» '.$Answers[$UserResponse]; - } else { - if (!empty($UserResponse) && $RevealVoters) { - $Answers[$UserResponse] = '» '.$Answers[$UserResponse]; - } - } - - $JsonPoll['closed'] = ($Closed == 1); - $JsonPoll['featured'] = $Featured; - $JsonPoll['question'] = $Question; - $JsonPoll['maxVotes'] = (int)$MaxVotes; - $JsonPoll['totalVotes'] = $TotalVotes; - $JsonPollAnswers = array(); - - foreach ($Answers as $i => $Answer) { - if (!empty($Votes[$i]) && $TotalVotes > 0) { - $Ratio = $Votes[$i] / $MaxVotes; - $Percent = $Votes[$i] / $TotalVotes; - } else { - $Ratio = 0; - $Percent = 0; - } - $JsonPollAnswers[] = array( - 'answer' => $Answer, - 'ratio' => $Ratio, - 'percent' => $Percent - ); - } - - if ($UserResponse !== null || $Closed || $ThreadInfo['IsLocked'] || $LoggedUser['Class'] < $Forums[$ForumID]['MinClassWrite']) { - $JsonPoll['voted'] = True; - } else { - $JsonPoll['voted'] = False; - } - - $JsonPoll['answers'] = $JsonPollAnswers; + if (!list($Question, $Answers, $Votes, $Featured, $Closed) = $Cache->get_value("polls_$ThreadID")) { + $DB->query(" + SELECT Question, Answers, Featured, Closed + FROM forums_polls + WHERE TopicID = '$ThreadID'"); + list($Question, $Answers, $Featured, $Closed) = $DB->next_record(MYSQLI_NUM, array(1)); + $Answers = unserialize($Answers); + $DB->query(" + SELECT Vote, COUNT(UserID) + FROM forums_polls_votes + WHERE TopicID = '$ThreadID' + GROUP BY Vote"); + $VoteArray = $DB->to_array(false, MYSQLI_NUM); + + $Votes = []; + foreach ($VoteArray as $VoteSet) { + list($Key, $Value) = $VoteSet; + $Votes[$Key] = $Value; + } + + foreach (array_keys($Answers) as $i) { + if (!isset($Votes[$i])) { + $Votes[$i] = 0; + } + } + $Cache->cache_value("polls_$ThreadID", array($Question, $Answers, $Votes, $Featured, $Closed), 0); + } + + if (!empty($Votes)) { + $TotalVotes = array_sum($Votes); + $MaxVotes = max($Votes); + } else { + $TotalVotes = 0; + $MaxVotes = 0; + } + + $RevealVoters = in_array($ForumID, $ForumsRevealVoters); + //Polls lose the you voted arrow thingy + $DB->query(" + SELECT Vote + FROM forums_polls_votes + WHERE UserID = '".$LoggedUser['ID']."' + AND TopicID = '$ThreadID'"); + list($UserResponse) = $DB->next_record(); + if (!empty($UserResponse) && $UserResponse != 0) { + $Answers[$UserResponse] = '» '.$Answers[$UserResponse]; + } else { + if (!empty($UserResponse) && $RevealVoters) { + $Answers[$UserResponse] = '» '.$Answers[$UserResponse]; + } + } + + $JsonPoll['closed'] = ($Closed == 1); + $JsonPoll['featured'] = $Featured; + $JsonPoll['question'] = $Question; + $JsonPoll['maxVotes'] = (int)$MaxVotes; + $JsonPoll['totalVotes'] = $TotalVotes; + $JsonPollAnswers = []; + + foreach ($Answers as $i => $Answer) { + if (!empty($Votes[$i]) && $TotalVotes > 0) { + $Ratio = $Votes[$i] / $MaxVotes; + $Percent = $Votes[$i] / $TotalVotes; + } else { + $Ratio = 0; + $Percent = 0; + } + $JsonPollAnswers[] = array( + 'answer' => $Answer, + 'ratio' => $Ratio, + 'percent' => $Percent + ); + } + + if ($UserResponse !== null || $Closed || $ThreadInfo['IsLocked'] || $LoggedUser['Class'] < $Forums[$ForumID]['MinClassWrite']) { + $JsonPoll['voted'] = True; + } else { + $JsonPoll['voted'] = False; + } + + $JsonPoll['answers'] = $JsonPollAnswers; } //Sqeeze in stickypost if ($ThreadInfo['StickyPostID']) { - if ($ThreadInfo['StickyPostID'] != $Thread[0]['ID']) { - array_unshift($Thread, $ThreadInfo['StickyPost']); - } - if ($ThreadInfo['StickyPostID'] != $Thread[count($Thread) - 1]['ID']) { - $Thread[] = $ThreadInfo['StickyPost']; - } + if ($ThreadInfo['StickyPostID'] != $Thread[0]['ID']) { + array_unshift($Thread, $ThreadInfo['StickyPost']); + } + if ($ThreadInfo['StickyPostID'] != $Thread[count($Thread) - 1]['ID']) { + $Thread[] = $ThreadInfo['StickyPost']; + } } -$JsonPosts = array(); +$JsonPosts = []; foreach ($Thread as $Key => $Post) { - list($PostID, $AuthorID, $AddedTime, $Body, $EditedUserID, $EditedTime) = array_values($Post); - list($AuthorID, $Username, $PermissionID, $Paranoia, $Artist, $Donor, $Warned, $Avatar, $Enabled, $UserTitle) = array_values(Users::user_info($AuthorID)); - - - - $UserInfo = Users::user_info($EditedUserID); - $JsonPosts[] = array( - 'postId' => (int)$PostID, - 'addedTime' => $AddedTime, - 'bbBody' => $Body, - 'body' => Text::full_format($Body), - 'editedUserId' => (int)$EditedUserID, - 'editedTime' => $EditedTime, - 'editedUsername' => $UserInfo['Username'], - 'author' => array( - 'authorId' => (int)$AuthorID, - 'authorName' => $Username, - 'paranoia' => $Paranoia, - 'artist' => $Artist === '1', - 'donor' => $Donor === '1', - 'warned' => $Warned !== '0000-00-00 00:00:00', - 'avatar' => $Avatar, - 'enabled' => $Enabled === '2' ? false : true, - 'userTitle' => $UserTitle - ), - - ); + list($PostID, $AuthorID, $AddedTime, $Body, $EditedUserID, $EditedTime) = array_values($Post); + list($AuthorID, $Username, $PermissionID, $Paranoia, $Artist, $Donor, $Warned, $Avatar, $Enabled, $UserTitle) = array_values(Users::user_info($AuthorID)); + + + + $UserInfo = Users::user_info($EditedUserID); + $JsonPosts[] = array( + 'postId' => (int)$PostID, + 'addedTime' => $AddedTime, + 'bbBody' => $Body, + 'body' => Text::full_format($Body), + 'editedUserId' => (int)$EditedUserID, + 'editedTime' => $EditedTime, + 'editedUsername' => $UserInfo['Username'], + 'author' => array( + 'authorId' => (int)$AuthorID, + 'authorName' => $Username, + 'paranoia' => $Paranoia, + 'artist' => $Artist === '1', + 'donor' => $Donor === '1', + 'warned' => $Warned !== '0000-00-00 00:00:00', + 'avatar' => $Avatar, + 'enabled' => $Enabled === '2' ? false : true, + 'userTitle' => $UserTitle + ), + + ); } print - json_encode( - array( - 'status' => 'success', - 'response' => array( - 'forumId' => (int)$ForumID, - 'forumName' => $Forums[$ForumID]['Name'], - 'threadId' => (int)$ThreadID, - 'threadTitle' => display_str($ThreadInfo['Title']), - 'subscribed' => in_array($ThreadID, $UserSubscriptions), - 'locked' => $ThreadInfo['IsLocked'] == 1, - 'sticky' => $ThreadInfo['IsSticky'] == 1, - 'currentPage' => (int)$Page, - 'pages' => ceil($ThreadInfo['Posts'] / $PerPage), - 'poll' => empty($JsonPoll) ? null : $JsonPoll, - 'posts' => $JsonPosts - ) - ) - ); + json_encode( + array( + 'status' => 'success', + 'response' => array( + 'forumId' => (int)$ForumID, + 'forumName' => $Forums[$ForumID]['Name'], + 'threadId' => (int)$ThreadID, + 'threadTitle' => display_str($ThreadInfo['Title']), + 'subscribed' => in_array($ThreadID, $UserSubscriptions), + 'locked' => $ThreadInfo['IsLocked'] == 1, + 'sticky' => $ThreadInfo['IsSticky'] == 1, + 'currentPage' => (int)$Page, + 'pages' => ceil($ThreadInfo['Posts'] / $PerPage), + 'poll' => empty($JsonPoll) ? null : $JsonPoll, + 'posts' => $JsonPosts + ) + ) + ); diff --git a/sections/ajax/get_friends.php b/sections/ajax/get_friends.php index e4fe2d8ac..ff1932e3e 100644 --- a/sections/ajax/get_friends.php +++ b/sections/ajax/get_friends.php @@ -1,16 +1,16 @@ query(" - SELECT - f.FriendID, - u.Username - FROM friends AS f - RIGHT JOIN users_enable_recommendations AS r - ON r.ID = f.FriendID - AND r.Enable = 1 - RIGHT JOIN users_main AS u - ON u.ID = f.FriendID - WHERE f.UserID = '$LoggedUser[ID]' - ORDER BY u.Username ASC"); + SELECT + f.FriendID, + u.Username + FROM friends AS f + RIGHT JOIN users_enable_recommendations AS r + ON r.ID = f.FriendID + AND r.Enable = 1 + RIGHT JOIN users_main AS u + ON u.ID = f.FriendID + WHERE f.UserID = '$LoggedUser[ID]' + ORDER BY u.Username ASC"); echo json_encode($DB->to_array(false, MYSQLI_ASSOC)); die(); diff --git a/sections/ajax/get_user_notifications.php b/sections/ajax/get_user_notifications.php index 79c669673..252af8948 100644 --- a/sections/ajax/get_user_notifications.php +++ b/sections/ajax/get_user_notifications.php @@ -1,9 +1,9 @@ -get_notifications()); -//echo '{"status":"success","response":[[{"message":"1st notification","url":"https:\/\/www.google.com\/","importance":"alert","AutoExpire":false},{"message":"2nd notification","url":"","importance":"alert","AutoExpire":true}]]}'; \ No newline at end of file +//echo '{"status":"success","response":[[{"message":"1st notification","url":"https:\/\/www.google.com\/","importance":"alert","AutoExpire":false},{"message":"2nd notification","url":"","importance":"alert","AutoExpire":true}]]}'; diff --git a/sections/ajax/inbox/inbox.php b/sections/ajax/inbox/inbox.php index 9b8a3aee2..4bdeb4f3b 100644 --- a/sections/ajax/inbox/inbox.php +++ b/sections/ajax/inbox/inbox.php @@ -4,18 +4,18 @@ if (empty($_GET['type'])) { - $Section = 'inbox'; + $Section = 'inbox'; } else { - $Section = $_GET['type']; // either 'inbox' or 'sentbox' + $Section = $_GET['type']; // either 'inbox' or 'sentbox' } if (!in_array($Section, array('inbox', 'sentbox'))) { - print - json_encode( - array( - 'status' => 'failure' - ) - ); - die(); + print + json_encode( + array( + 'status' => 'failure' + ) + ); + die(); } list($Page, $Limit) = Format::page_limit(MESSAGES_PER_PAGE); @@ -23,52 +23,52 @@ $Sort = empty($_GET['sort']) || $_GET['sort'] != "unread" ? "Date DESC" : "cu.Unread = '1' DESC, DATE DESC"; $sql = " - SELECT - SQL_CALC_FOUND_ROWS - c.ID, - c.Subject, - cu.Unread, - cu.Sticky, - cu.ForwardedTo, - um2.Username AS ForwardedName, - cu2.UserID, - um.Username, - ui.Donor, - ui.Warned, - um.Enabled, - ui.Avatar,"; + SELECT + SQL_CALC_FOUND_ROWS + c.ID, + c.Subject, + cu.Unread, + cu.Sticky, + cu.ForwardedTo, + um2.Username AS ForwardedName, + cu2.UserID, + um.Username, + ui.Donor, + ui.Warned, + um.Enabled, + ui.Avatar,"; $sql .= $Section === 'sentbox' ? ' cu.SentDate ' : ' cu.ReceivedDate '; $sql .= "AS Date - FROM pm_conversations AS c - LEFT JOIN pm_conversations_users AS cu ON cu.ConvID = c.ID AND cu.UserID = '$UserID' - LEFT JOIN pm_conversations_users AS cu2 ON cu2.ConvID = c.ID AND cu2.UserID != '$UserID' AND cu2.ForwardedTo = 0 - LEFT JOIN users_main AS um ON um.ID = cu2.UserID - LEFT JOIN users_info AS ui ON ui.UserID = um.ID - LEFT JOIN users_main AS um2 ON um2.ID = cu.ForwardedTo"; + FROM pm_conversations AS c + LEFT JOIN pm_conversations_users AS cu ON cu.ConvID = c.ID AND cu.UserID = '$UserID' + LEFT JOIN pm_conversations_users AS cu2 ON cu2.ConvID = c.ID AND cu2.UserID != '$UserID' AND cu2.ForwardedTo = 0 + LEFT JOIN users_main AS um ON um.ID = cu2.UserID + LEFT JOIN users_info AS ui ON ui.UserID = um.ID + LEFT JOIN users_main AS um2 ON um2.ID = cu.ForwardedTo"; if (!empty($_GET['search']) && $_GET['searchtype'] === 'message') { - $sql .= ' JOIN pm_messages AS m ON c.ID = m.ConvID'; + $sql .= ' JOIN pm_messages AS m ON c.ID = m.ConvID'; } $sql .= " WHERE "; if (!empty($_GET['search'])) { - $Search = db_string($_GET['search']); - if ($_GET['searchtype'] === 'user') { - $sql .= "um.Username LIKE '$Search' AND "; - } elseif ($_GET['searchtype'] === 'subject') { - $Words = explode(' ', $Search); - $sql .= "c.Subject LIKE '%".implode("%' AND c.Subject LIKE '%", $Words)."%' AND "; - } elseif ($_GET['searchtype'] === 'message') { - $Words = explode(' ', $Search); - $sql .= "m.Body LIKE '%".implode("%' AND m.Body LIKE '%", $Words)."%' AND "; - } + $Search = db_string($_GET['search']); + if ($_GET['searchtype'] === 'user') { + $sql .= "um.Username LIKE '$Search' AND "; + } elseif ($_GET['searchtype'] === 'subject') { + $Words = explode(' ', $Search); + $sql .= "c.Subject LIKE '%".implode("%' AND c.Subject LIKE '%", $Words)."%' AND "; + } elseif ($_GET['searchtype'] === 'message') { + $Words = explode(' ', $Search); + $sql .= "m.Body LIKE '%".implode("%' AND m.Body LIKE '%", $Words)."%' AND "; + } } $sql .= $Section === 'sentbox' ? ' cu.InSentbox' : ' cu.InInbox'; $sql .= " = '1'"; $sql .= " - GROUP BY c.ID - ORDER BY cu.Sticky, $Sort - LIMIT $Limit"; + GROUP BY c.ID + ORDER BY cu.Sticky, $Sort + LIMIT $Limit"; $Results = $DB->query($sql); $DB->query('SELECT FOUND_ROWS()'); list($NumResults) = $DB->next_record(); @@ -76,42 +76,42 @@ $CurURL = Format::get_url(array('sort')); if (empty($CurURL)) { - $CurURL = "inbox.php?"; + $CurURL = "inbox.php?"; } else { - $CurURL = "inbox.php?".$CurURL."&"; + $CurURL = "inbox.php?".$CurURL."&"; } $Pages = Format::get_pages($Page, $NumResults, MESSAGES_PER_PAGE, 9); -$JsonMessages = array(); +$JsonMessages = []; while (list($ConvID, $Subject, $Unread, $Sticky, $ForwardedID, $ForwardedName, $SenderID, $Username, $Donor, $Warned, $Enabled, $Avatar, $Date) = $DB->next_record()) { - $JsonMessage = array( - 'convId' => (int)$ConvID, - 'subject' => $Subject, - 'unread' => $Unread == 1, - 'sticky' => $Sticky == 1, - 'forwardedId' => (int)$ForwardedID, - 'forwardedName' => $ForwardedName, - 'senderId' => (int)$SenderID, - 'username' => $Username, - 'avatar' => $Avatar, - 'donor' => $Donor == 1, - 'warned' => $Warned == 1, - 'enabled' => $Enabled == 2 ? false : true, - 'date' => $Date - ); - $JsonMessages[] = $JsonMessage; + $JsonMessage = array( + 'convId' => (int)$ConvID, + 'subject' => $Subject, + 'unread' => $Unread == 1, + 'sticky' => $Sticky == 1, + 'forwardedId' => (int)$ForwardedID, + 'forwardedName' => $ForwardedName, + 'senderId' => (int)$SenderID, + 'username' => $Username, + 'avatar' => $Avatar, + 'donor' => $Donor == 1, + 'warned' => $Warned == 1, + 'enabled' => $Enabled == 2 ? false : true, + 'date' => $Date + ); + $JsonMessages[] = $JsonMessage; } print - json_encode( - array( - 'status' => 'success', - 'response' => array( - 'currentPage' => (int)$Page, - 'pages' => ceil($NumResults / MESSAGES_PER_PAGE), - 'messages' => $JsonMessages - ) - ) - ); + json_encode( + array( + 'status' => 'success', + 'response' => array( + 'currentPage' => (int)$Page, + 'pages' => ceil($NumResults / MESSAGES_PER_PAGE), + 'messages' => $JsonMessages + ) + ) + ); ?> diff --git a/sections/ajax/inbox/index.php b/sections/ajax/inbox/index.php index 02542eee4..4dbd9b7ec 100644 --- a/sections/ajax/inbox/index.php +++ b/sections/ajax/inbox/index.php @@ -1,12 +1,12 @@ - 'failure')); - die(); + print json_encode(array('status' => 'failure')); + die(); } ?> diff --git a/sections/ajax/inbox/viewconv.php b/sections/ajax/inbox/viewconv.php index f35229ff7..922fdb577 100644 --- a/sections/ajax/inbox/viewconv.php +++ b/sections/ajax/inbox/viewconv.php @@ -1,21 +1,21 @@ - 'failure')); - die(); + print json_encode(array('status' => 'failure')); + die(); } $UserID = $LoggedUser['ID']; $DB->query(" - SELECT InInbox, InSentbox - FROM pm_conversations_users - WHERE UserID = '$UserID' - AND ConvID = '$ConvID'"); + SELECT InInbox, InSentbox + FROM pm_conversations_users + WHERE UserID = '$UserID' + AND ConvID = '$ConvID'"); if (!$DB->has_results()) { - print json_encode(array('status' => 'failure')); - die(); + print json_encode(array('status' => 'failure')); + die(); } list($InInbox, $InSentbox) = $DB->next_record(); @@ -23,83 +23,83 @@ if (!$InInbox && !$InSentbox) { - print json_encode(array('status' => 'failure')); - die(); + print json_encode(array('status' => 'failure')); + die(); } // Get information on the conversation $DB->query(" - SELECT - c.Subject, - cu.Sticky, - cu.UnRead, - cu.ForwardedTo, - um.Username - FROM pm_conversations AS c - JOIN pm_conversations_users AS cu ON c.ID = cu.ConvID - LEFT JOIN users_main AS um ON um.ID = cu.ForwardedTo - WHERE c.ID = '$ConvID' - AND UserID = '$UserID'"); + SELECT + c.Subject, + cu.Sticky, + cu.UnRead, + cu.ForwardedTo, + um.Username + FROM pm_conversations AS c + JOIN pm_conversations_users AS cu ON c.ID = cu.ConvID + LEFT JOIN users_main AS um ON um.ID = cu.ForwardedTo + WHERE c.ID = '$ConvID' + AND UserID = '$UserID'"); list($Subject, $Sticky, $UnRead, $ForwardedID, $ForwardedName) = $DB->next_record(); $DB->query(" - SELECT um.ID, Username - FROM pm_messages AS pm - JOIN users_main AS um ON um.ID = pm.SenderID - WHERE pm.ConvID = '$ConvID'"); + SELECT um.ID, Username + FROM pm_messages AS pm + JOIN users_main AS um ON um.ID = pm.SenderID + WHERE pm.ConvID = '$ConvID'"); while (list($PMUserID, $Username) = $DB->next_record()) { - $PMUserID = (int)$PMUserID; - $Users[$PMUserID]['UserStr'] = Users::format_username($PMUserID, true, true, true, true); - $Users[$PMUserID]['Username'] = $Username; - $UserInfo = Users::user_info($PMUserID); - $Users[$PMUserID]['Avatar'] = $UserInfo['Avatar']; + $PMUserID = (int)$PMUserID; + $Users[$PMUserID]['UserStr'] = Users::format_username($PMUserID, true, true, true, true); + $Users[$PMUserID]['Username'] = $Username; + $UserInfo = Users::user_info($PMUserID); + $Users[$PMUserID]['Avatar'] = $UserInfo['Avatar']; } $Users[0]['UserStr'] = 'System'; // in case it's a message from the system $Users[0]['Username'] = 'System'; $Users[0]['Avatar'] = ''; if ($UnRead == '1') { - $DB->query(" - UPDATE pm_conversations_users - SET UnRead = '0' - WHERE ConvID = '$ConvID' - AND UserID = '$UserID'"); - // Clear the caches of the inbox and sentbox - $Cache->decrement("inbox_new_$UserID"); + $DB->query(" + UPDATE pm_conversations_users + SET UnRead = '0' + WHERE ConvID = '$ConvID' + AND UserID = '$UserID'"); + // Clear the caches of the inbox and sentbox + $Cache->decrement("inbox_new_$UserID"); } // Get messages $DB->query(" - SELECT SentDate, SenderID, Body, ID - FROM pm_messages - WHERE ConvID = '$ConvID' - ORDER BY ID"); + SELECT SentDate, SenderID, Body, ID + FROM pm_messages + WHERE ConvID = '$ConvID' + ORDER BY ID"); -$JsonMessages = array(); +$JsonMessages = []; while (list($SentDate, $SenderID, $Body, $MessageID) = $DB->next_record()) { - $JsonMessage = array( - 'messageId' => (int)$MessageID, - 'senderId' => (int)$SenderID, - 'senderName' => $Users[(int)$SenderID]['Username'], - 'sentDate' => $SentDate, - 'avatar' => $Users[(int)$SenderID]['Avatar'], - 'bbBody' => $Body, - 'body' => Text::full_format($Body) - ); - $JsonMessages[] = $JsonMessage; + $JsonMessage = array( + 'messageId' => (int)$MessageID, + 'senderId' => (int)$SenderID, + 'senderName' => $Users[(int)$SenderID]['Username'], + 'sentDate' => $SentDate, + 'avatar' => $Users[(int)$SenderID]['Avatar'], + 'bbBody' => $Body, + 'body' => Text::full_format($Body) + ); + $JsonMessages[] = $JsonMessage; } print - json_encode( - array( - 'status' => 'success', - 'response' => array( - 'convId' => (int)$ConvID, - 'subject' => $Subject.($ForwardedID > 0 ? " (Forwarded to $ForwardedName)" : ''), - 'sticky' => $Sticky == 1, - 'messages' => $JsonMessages - ) - ) - ); + json_encode( + array( + 'status' => 'success', + 'response' => array( + 'convId' => (int)$ConvID, + 'subject' => $Subject.($ForwardedID > 0 ? " (Forwarded to $ForwardedName)" : ''), + 'sticky' => $Sticky == 1, + 'messages' => $JsonMessages + ) + ) + ); ?> diff --git a/sections/ajax/index.php b/sections/ajax/index.php index 8670b0b7e..a80332740 100644 --- a/sections/ajax/index.php +++ b/sections/ajax/index.php @@ -1,4 +1,4 @@ -get_value('ajax_requests_'.$UserID)) { - $UserRequests = 0; - $Cache->cache_value('ajax_requests_'.$UserID, '0', $AJAX_LIMIT[1]); - } - if ($UserRequests > $AJAX_LIMIT[0]) { - json_die("failure", "rate limit exceeded"); - } else { - $Cache->increment_value('ajax_requests_'.$UserID); - } +// Enforce rate limiting everywhere except info.php +if (!check_perms('site_unlimit_ajax') && isset($_GET['action']) && in_array($_GET['action'], $LimitedPages)) { + if (!$UserRequests = $Cache->get_value('ajax_requests_'.$UserID)) { + $UserRequests = 0; + $Cache->cache_value('ajax_requests_'.$UserID, '0', $AJAX_LIMIT[1]); + } + if ($UserRequests > $AJAX_LIMIT[0]) { + json_die("failure", "rate limit exceeded"); + } else { + $Cache->increment_value('ajax_requests_'.$UserID); + } } switch ($_GET['action']) { - // things that (may be) used on the site - case 'upload_section': - // Gets one of the upload forms - require(SERVER_ROOT . '/sections/ajax/upload.php'); - break; - case 'preview': - require('preview.php'); - break; - case 'torrent_info': - require('torrent_info.php'); - break; - case 'stats': - require(SERVER_ROOT . '/sections/ajax/stats.php'); - break; + // things that (may be) used on the site + case 'upload_section': + // Gets one of the upload forms + require(SERVER_ROOT . '/sections/ajax/upload.php'); + break; + case 'preview': + require('preview.php'); + break; + case 'torrent_info': + require('torrent_info.php'); + break; + case 'stats': + require(SERVER_ROOT . '/sections/ajax/stats.php'); + break; - case 'checkprivate': - include('checkprivate.php'); - break; - // things not yet used on the site - case 'torrent': - require('torrent.php'); - break; - case 'torrentgroup': - require('torrentgroup.php'); - break; - case 'torrentgroupalbumart': // so the album art script can function without breaking the ratelimit - require(SERVER_ROOT . '/sections/ajax/torrentgroupalbumart.php'); - break; - case 'tcomments': - require(SERVER_ROOT . '/sections/ajax/tcomments.php'); - break; - case 'user': - require(SERVER_ROOT . '/sections/ajax/user.php'); - break; - case 'forum': - require(SERVER_ROOT . '/sections/ajax/forum/index.php'); - break; - case 'top10': - require(SERVER_ROOT . '/sections/ajax/top10/index.php'); - break; - case 'browse': - require(SERVER_ROOT . '/sections/ajax/browse.php'); - break; - case 'usersearch': - require(SERVER_ROOT . '/sections/ajax/usersearch.php'); - break; - case 'requests': - require(SERVER_ROOT . '/sections/ajax/requests.php'); - break; - case 'artist': - require(SERVER_ROOT . '/sections/ajax/artist.php'); - break; - case 'inbox': - require(SERVER_ROOT . '/sections/ajax/inbox/index.php'); - break; - case 'subscriptions': - require(SERVER_ROOT . '/sections/ajax/subscriptions.php'); - break; - case 'index': - require(SERVER_ROOT . '/sections/ajax/info.php'); - break; - case 'bookmarks': - require(SERVER_ROOT . '/sections/ajax/bookmarks/index.php'); - break; - case 'announcements': - require(SERVER_ROOT . '/sections/ajax/announcements.php'); - break; - case 'notifications': - require(SERVER_ROOT . '/sections/ajax/notifications.php'); - break; - case 'request': - require(SERVER_ROOT . '/sections/ajax/request.php'); - break; - case 'loadavg': - require(SERVER_ROOT . '/sections/ajax/loadavg.php'); - break; - case 'better': - require(SERVER_ROOT . '/sections/ajax/better/index.php'); - break; - case 'password_validate': - require(SERVER_ROOT . '/sections/ajax/password_validate.php'); - break; - case 'similar_artists': - require(SERVER_ROOT . '/sections/ajax/similar_artists.php'); - break; - case 'userhistory': - require(SERVER_ROOT . '/sections/ajax/userhistory/index.php'); - break; - case 'votefavorite': - require(SERVER_ROOT . '/sections/ajax/takevote.php'); - break; - case 'wiki': - require(SERVER_ROOT . '/sections/ajax/wiki.php'); - break; - case 'send_recommendation': - require(SERVER_ROOT . '/sections/ajax/send_recommendation.php'); - break; - case 'get_friends': - require(SERVER_ROOT . '/sections/ajax/get_friends.php'); - break; - case 'news_ajax': - require(SERVER_ROOT . '/sections/ajax/news_ajax.php'); - break; - case 'community_stats': - require(SERVER_ROOT . '/sections/ajax/community_stats.php'); - break; - case 'user_recents': - require(SERVER_ROOT . '/sections/ajax/user_recents.php'); - break; - case 'collage': - require(SERVER_ROOT . '/sections/ajax/collage.php'); - break; - case 'raw_bbcode': - require(SERVER_ROOT . '/sections/ajax/raw_bbcode.php'); - break; - case 'get_user_notifications': - require(SERVER_ROOT . '/sections/ajax/get_user_notifications.php'); - break; - case 'clear_user_notification': - require(SERVER_ROOT . '/sections/ajax/clear_user_notification.php'); - break; - case 'pushbullet_devices': - require(SERVER_ROOT . '/sections/ajax/pushbullet_devices.php'); - break; - case 'user_stats': - require(SERVER_ROOT . '/sections/ajax/stats/users.php'); - break; - case 'torrent_stats': - require(SERVER_ROOT . '/sections/ajax/stats/torrents.php'); - break; - default: - // If they're screwing around with the query string - json_die("failure"); + case 'checkprivate': + include('checkprivate.php'); + break; + // things not yet used on the site + case 'torrent': + require('torrent.php'); + break; + case 'torrentgroup': + require('torrentgroup.php'); + break; + case 'torrentgroupalbumart': // so the album art script can function without breaking the ratelimit + require(SERVER_ROOT . '/sections/ajax/torrentgroupalbumart.php'); + break; + case 'tcomments': + require(SERVER_ROOT . '/sections/ajax/tcomments.php'); + break; + case 'user': + require(SERVER_ROOT . '/sections/ajax/user.php'); + break; + case 'forum': + require(SERVER_ROOT . '/sections/ajax/forum/index.php'); + break; + case 'top10': + require(SERVER_ROOT . '/sections/ajax/top10/index.php'); + break; + case 'browse': + require(SERVER_ROOT . '/sections/ajax/browse.php'); + break; + case 'usersearch': + require(SERVER_ROOT . '/sections/ajax/usersearch.php'); + break; + case 'requests': + require(SERVER_ROOT . '/sections/ajax/requests.php'); + break; + case 'artist': + require(SERVER_ROOT . '/sections/ajax/artist.php'); + break; + case 'inbox': + require(SERVER_ROOT . '/sections/ajax/inbox/index.php'); + break; + case 'subscriptions': + require(SERVER_ROOT . '/sections/ajax/subscriptions.php'); + break; + case 'index': + require(SERVER_ROOT . '/sections/ajax/info.php'); + break; + case 'bookmarks': + require(SERVER_ROOT . '/sections/ajax/bookmarks/index.php'); + break; + case 'announcements': + require(SERVER_ROOT . '/sections/ajax/announcements.php'); + break; + case 'notifications': + require(SERVER_ROOT . '/sections/ajax/notifications.php'); + break; + case 'request': + require(SERVER_ROOT . '/sections/ajax/request.php'); + break; + case 'loadavg': + require(SERVER_ROOT . '/sections/ajax/loadavg.php'); + break; + case 'better': + require(SERVER_ROOT . '/sections/ajax/better/index.php'); + break; + case 'password_validate': + require(SERVER_ROOT . '/sections/ajax/password_validate.php'); + break; + case 'similar_artists': + require(SERVER_ROOT . '/sections/ajax/similar_artists.php'); + break; + case 'userhistory': + require(SERVER_ROOT . '/sections/ajax/userhistory/index.php'); + break; + case 'votefavorite': + require(SERVER_ROOT . '/sections/ajax/takevote.php'); + break; + case 'wiki': + require(SERVER_ROOT . '/sections/ajax/wiki.php'); + break; + case 'send_recommendation': + require(SERVER_ROOT . '/sections/ajax/send_recommendation.php'); + break; + case 'get_friends': + require(SERVER_ROOT . '/sections/ajax/get_friends.php'); + break; + case 'news_ajax': + require(SERVER_ROOT . '/sections/ajax/news_ajax.php'); + break; + case 'community_stats': + require(SERVER_ROOT . '/sections/ajax/community_stats.php'); + break; + case 'user_recents': + require(SERVER_ROOT . '/sections/ajax/user_recents.php'); + break; + case 'collage': + require(SERVER_ROOT . '/sections/ajax/collage.php'); + break; + case 'raw_bbcode': + require(SERVER_ROOT . '/sections/ajax/raw_bbcode.php'); + break; + case 'get_user_notifications': + require(SERVER_ROOT . '/sections/ajax/get_user_notifications.php'); + break; + case 'clear_user_notification': + require(SERVER_ROOT . '/sections/ajax/clear_user_notification.php'); + break; + case 'pushbullet_devices': + require(SERVER_ROOT . '/sections/ajax/pushbullet_devices.php'); + break; + case 'loggy': + require(SERVER_ROOT . '/sections/ajax/loggy.php'); + break; + case 'user_stats': + require(SERVER_ROOT . '/sections/ajax/stats/users.php'); + break; + case 'torrent_stats': + require(SERVER_ROOT . '/sections/ajax/stats/torrents.php'); + break; + default: + // If they're screwing around with the query string + json_die("failure"); } function pullmediainfo($Array) { - $NewArray = array(); - foreach ($Array as $Item) { - $NewArray[] = array( - 'id' => (int)$Item['id'], - 'name' => $Item['name'] - ); - } - return $NewArray; + $NewArray = []; + foreach ($Array as $Item) { + $NewArray[] = array( + 'id' => (int)$Item['id'], + 'name' => $Item['name'] + ); + } + return $NewArray; } ?> diff --git a/sections/ajax/info.php b/sections/ajax/info.php index 103d68770..c246c304b 100644 --- a/sections/ajax/info.php +++ b/sections/ajax/info.php @@ -1,117 +1,117 @@ -get_value('news_latest_id'); if ($CurrentNews === false) { - $DB->query(" - SELECT ID - FROM news - ORDER BY Time DESC - LIMIT 1"); - if ($DB->record_count() === 1) { - list($CurrentNews) = $DB->next_record(); - } else { - $CurrentNews = -1; - } - $Cache->cache_value('news_latest_id', $CurrentNews, 0); + $DB->query(" + SELECT ID + FROM news + ORDER BY Time DESC + LIMIT 1"); + if ($DB->record_count() === 1) { + list($CurrentNews) = $DB->next_record(); + } else { + $CurrentNews = -1; + } + $Cache->cache_value('news_latest_id', $CurrentNews, 0); } $NewMessages = $Cache->get_value('inbox_new_' . $LoggedUser['ID']); if ($NewMessages === false) { - $DB->query(" - SELECT COUNT(UnRead) - FROM pm_conversations_users - WHERE UserID = '" . $LoggedUser['ID'] . "' - AND UnRead = '1' - AND InInbox = '1'"); - list($NewMessages) = $DB->next_record(); - $Cache->cache_value('inbox_new_' . $LoggedUser['ID'], $NewMessages, 0); + $DB->query(" + SELECT COUNT(UnRead) + FROM pm_conversations_users + WHERE UserID = '" . $LoggedUser['ID'] . "' + AND UnRead = '1' + AND InInbox = '1'"); + list($NewMessages) = $DB->next_record(); + $Cache->cache_value('inbox_new_' . $LoggedUser['ID'], $NewMessages, 0); } if (check_perms('site_torrents_notify')) { - $NewNotifications = $Cache->get_value('notifications_new_' . $LoggedUser['ID']); - if ($NewNotifications === false) { - $DB->query(" - SELECT COUNT(UserID) - FROM users_notify_torrents - WHERE UserID = '$LoggedUser[ID]' - AND UnRead = '1'"); - list($NewNotifications) = $DB->next_record(); - /* if ($NewNotifications && !check_perms('site_torrents_notify')) { - $DB->query("DELETE FROM users_notify_torrents WHERE UserID='$LoggedUser[ID]'"); - $DB->query("DELETE FROM users_notify_filters WHERE UserID='$LoggedUser[ID]'"); - } */ - $Cache->cache_value('notifications_new_' . $LoggedUser['ID'], $NewNotifications, 0); - } + $NewNotifications = $Cache->get_value('notifications_new_' . $LoggedUser['ID']); + if ($NewNotifications === false) { + $DB->query(" + SELECT COUNT(UserID) + FROM users_notify_torrents + WHERE UserID = '$LoggedUser[ID]' + AND UnRead = '1'"); + list($NewNotifications) = $DB->next_record(); + /* if ($NewNotifications && !check_perms('site_torrents_notify')) { + $DB->query("DELETE FROM users_notify_torrents WHERE UserID='$LoggedUser[ID]'"); + $DB->query("DELETE FROM users_notify_filters WHERE UserID='$LoggedUser[ID]'"); + } */ + $Cache->cache_value('notifications_new_' . $LoggedUser['ID'], $NewNotifications, 0); + } } // News $MyNews = $LoggedUser['LastReadNews']; $CurrentNews = $Cache->get_value('news_latest_id'); if ($CurrentNews === false) { - $DB->query(" - SELECT ID - FROM news - ORDER BY Time DESC - LIMIT 1"); - if ($DB->record_count() === 1) { - list($CurrentNews) = $DB->next_record(); - } else { - $CurrentNews = -1; - } - $Cache->cache_value('news_latest_id', $CurrentNews, 0); + $DB->query(" + SELECT ID + FROM news + ORDER BY Time DESC + LIMIT 1"); + if ($DB->record_count() === 1) { + list($CurrentNews) = $DB->next_record(); + } else { + $CurrentNews = -1; + } + $Cache->cache_value('news_latest_id', $CurrentNews, 0); } // Blog $MyBlog = $LoggedUser['LastReadBlog']; $CurrentBlog = $Cache->get_value('blog_latest_id'); if ($CurrentBlog === false) { - $DB->query(" - SELECT ID - FROM blog - WHERE Important = 1 - ORDER BY Time DESC - LIMIT 1"); - if ($DB->record_count() === 1) { - list($CurrentBlog) = $DB->next_record(); - } else { - $CurrentBlog = -1; - } - $Cache->cache_value('blog_latest_id', $CurrentBlog, 0); + $DB->query(" + SELECT ID + FROM blog + WHERE Important = 1 + ORDER BY Time DESC + LIMIT 1"); + if ($DB->record_count() === 1) { + list($CurrentBlog) = $DB->next_record(); + } else { + $CurrentBlog = -1; + } + $Cache->cache_value('blog_latest_id', $CurrentBlog, 0); } // Subscriptions $NewSubscriptions = Subscriptions::has_new_subscriptions(); json_print("success", array( - 'username' => $LoggedUser['Username'], - 'id' => (int)$LoggedUser['ID'], - 'authkey' => $LoggedUser['AuthKey'], - 'passkey' => $LoggedUser['torrent_pass'], - 'notifications' => array( - 'messages' => (int)$NewMessages, - 'notifications' => (int)$NewNotifications, - 'newAnnouncement' => $MyNews < $CurrentNews, - 'newBlog' => $MyBlog < $CurrentBlog, - 'newSubscriptions' => $NewSubscriptions == 1 - ), - 'userstats' => array( - 'uploaded' => (int)$LoggedUser['BytesUploaded'], - 'downloaded' => (int)$LoggedUser['BytesDownloaded'], - 'ratio' => (float)$Ratio, - 'requiredratio' => (float)$LoggedUser['RequiredRatio'], - 'class' => $ClassLevels[$LoggedUser['Class']]['Name'] - ) + 'username' => $LoggedUser['Username'], + 'id' => (int)$LoggedUser['ID'], + 'authkey' => $LoggedUser['AuthKey'], + 'passkey' => $LoggedUser['torrent_pass'], + 'notifications' => array( + 'messages' => (int)$NewMessages, + 'notifications' => (int)$NewNotifications, + 'newAnnouncement' => $MyNews < $CurrentNews, + 'newBlog' => $MyBlog < $CurrentBlog, + 'newSubscriptions' => $NewSubscriptions == 1 + ), + 'userstats' => array( + 'uploaded' => (int)$LoggedUser['BytesUploaded'], + 'downloaded' => (int)$LoggedUser['BytesDownloaded'], + 'ratio' => (float)$Ratio, + 'requiredratio' => (float)$LoggedUser['RequiredRatio'], + 'class' => $ClassLevels[$LoggedUser['Class']]['Name'] + ) )); ?> diff --git a/sections/ajax/loadavg.php b/sections/ajax/loadavg.php index ed872cb1c..2092baeeb 100644 --- a/sections/ajax/loadavg.php +++ b/sections/ajax/loadavg.php @@ -1,15 +1,15 @@ - 'success', - 'response' => array( - 'loadAverage' => sys_getloadavg() - ) - ) - ); + json_encode( + array( + 'status' => 'success', + 'response' => array( + 'loadAverage' => sys_getloadavg() + ) + ) + ); ?> diff --git a/sections/ajax/loggy.php b/sections/ajax/loggy.php new file mode 100644 index 000000000..47006213f --- /dev/null +++ b/sections/ajax/loggy.php @@ -0,0 +1,15 @@ + '".(new \DateTime())->sub(new \DateInterval('P14D'))->format('Y-m-d')."'"; +} +else { + $Where[] = 'tls.Seeders > 0'; +} + +$Where = implode(' AND ', $Where); +$DB->prepared_query("SELECT t.ID FROM torrents t INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID) WHERE {$Where}"); + +json_print('success', ['IDs' => $DB->collect('ID', false)]); diff --git a/sections/ajax/news_ajax.php b/sections/ajax/news_ajax.php index 6e8e7bcb1..13895e260 100644 --- a/sections/ajax/news_ajax.php +++ b/sections/ajax/news_ajax.php @@ -1,4 +1,4 @@ - $SizeLimit) { - json_die('failure'); + json_die('failure'); } Text::$TOC = true; global $DB; $DB->query(" - SELECT - ID, - Title, - Body, - Time - FROM news - ORDER BY Time DESC - LIMIT $Offset, $Count"); + SELECT + ID, + Title, + Body, + Time + FROM news + ORDER BY Time DESC + LIMIT $Offset, $Count"); $News = $DB->to_array(false, MYSQLI_NUM, false); -$NewsResponse = array(); +$NewsResponse = []; foreach ($News as $NewsItem) { - list($NewsID, $Title, $Body, $NewsTime) = $NewsItem; - array_push( - $NewsResponse, - array( - $NewsID, - Text::full_format($Title), - time_diff($NewsTime), - Text::full_format($Body) - ) - ); + list($NewsID, $Title, $Body, $NewsTime) = $NewsItem; + array_push( + $NewsResponse, + array( + $NewsID, + Text::full_format($Title), + time_diff($NewsTime), + Text::full_format($Body) + ) + ); } json_print('success', json_encode($NewsResponse)); diff --git a/sections/ajax/notifications.php b/sections/ajax/notifications.php index 65bbb9803..879efa078 100644 --- a/sections/ajax/notifications.php +++ b/sections/ajax/notifications.php @@ -1,107 +1,107 @@ query(" - SELECT - SQL_CALC_FOUND_ROWS - unt.TorrentID, - unt.UnRead, - unt.FilterID, - unf.Label, - t.GroupID - FROM users_notify_torrents AS unt - JOIN torrents AS t ON t.ID = unt.TorrentID - LEFT JOIN users_notify_filters AS unf ON unf.ID = unt.FilterID - WHERE unt.UserID = $LoggedUser[ID]". - ((!empty($_GET['filterid']) && is_number($_GET['filterid'])) - ? " AND unf.ID = '$_GET[filterid]'" - : '')." - ORDER BY TorrentID DESC - LIMIT $Limit"); + SELECT + SQL_CALC_FOUND_ROWS + unt.TorrentID, + unt.UnRead, + unt.FilterID, + unf.Label, + t.GroupID + FROM users_notify_torrents AS unt + JOIN torrents AS t ON t.ID = unt.TorrentID + LEFT JOIN users_notify_filters AS unf ON unf.ID = unt.FilterID + WHERE unt.UserID = $LoggedUser[ID]". + ((!empty($_GET['filterid']) && is_number($_GET['filterid'])) + ? " AND unf.ID = '$_GET[filterid]'" + : '')." + ORDER BY TorrentID DESC + LIMIT $Limit"); $GroupIDs = array_unique($DB->collect('GroupID')); $DB->query('SELECT FOUND_ROWS()'); list($TorrentCount) = $DB->next_record(); if (count($GroupIDs)) { - $TorrentGroups = Torrents::get_groups($GroupIDs); - $DB->query(" - UPDATE users_notify_torrents - SET UnRead = '0' - WHERE UserID = $LoggedUser[ID]"); - $Cache->delete_value("notifications_new_$LoggedUser[ID]"); + $TorrentGroups = Torrents::get_groups($GroupIDs); + $DB->query(" + UPDATE users_notify_torrents + SET UnRead = '0' + WHERE UserID = $LoggedUser[ID]"); + $Cache->delete_value("notifications_new_$LoggedUser[ID]"); } $DB->set_query_id($Results); -$JsonNotifications = array(); +$JsonNotifications = []; $NumNew = 0; -$FilterGroups = array(); +$FilterGroups = []; while ($Result = $DB->next_record(MYSQLI_ASSOC)) { - if (!$Result['FilterID']) { - $Result['FilterID'] = 0; - } - if (!isset($FilterGroups[$Result['FilterID']])) { - $FilterGroups[$Result['FilterID']] = array(); - $FilterGroups[$Result['FilterID']]['FilterLabel'] = ($Result['Label'] ? $Result['Label'] : false); - } - array_push($FilterGroups[$Result['FilterID']], $Result); + if (!$Result['FilterID']) { + $Result['FilterID'] = 0; + } + if (!isset($FilterGroups[$Result['FilterID']])) { + $FilterGroups[$Result['FilterID']] = []; + $FilterGroups[$Result['FilterID']]['FilterLabel'] = ($Result['Label'] ? $Result['Label'] : false); + } + array_push($FilterGroups[$Result['FilterID']], $Result); } unset($Result); foreach ($FilterGroups as $FilterID => $FilterResults) { - unset($FilterResults['FilterLabel']); - foreach ($FilterResults as $Result) { - $TorrentID = $Result['TorrentID']; -// $GroupID = $Result['GroupID']; + unset($FilterResults['FilterLabel']); + foreach ($FilterResults as $Result) { + $TorrentID = $Result['TorrentID']; +// $GroupID = $Result['GroupID']; - $GroupInfo = $TorrentGroups[$Result['GroupID']]; - extract(Torrents::array_group($GroupInfo)); // all group data - $TorrentInfo = $GroupInfo['Torrents'][$TorrentID]; + $GroupInfo = $TorrentGroups[$Result['GroupID']]; + extract(Torrents::array_group($GroupInfo)); // all group data + $TorrentInfo = $GroupInfo['Torrents'][$TorrentID]; - if ($Result['UnRead'] == 1) { - $NumNew++; - } + if ($Result['UnRead'] == 1) { + $NumNew++; + } - $JsonNotifications[] = array( - 'torrentId' => (int)$TorrentID, - 'groupId' => (int)$GroupID, - 'groupName' => $GroupName, - 'groupCategoryId' => (int)$GroupCategoryID, - 'wikiImage' => $WikiImage, - 'torrentTags' => $TagList, - 'size' => (float)$TorrentInfo['Size'], - 'fileCount' => (int)$TorrentInfo['FileCount'], - 'format' => $TorrentInfo['Format'], - 'encoding' => $TorrentInfo['Encoding'], - 'media' => $TorrentInfo['Media'], - 'scene' => $TorrentInfo['Scene'] == 1, - 'groupYear' => (int)$GroupYear, - 'remasterYear' => (int)$TorrentInfo['RemasterYear'], - 'remasterTitle' => $TorrentInfo['RemasterTitle'], - 'snatched' => (int)$TorrentInfo['Snatched'], - 'seeders' => (int)$TorrentInfo['Seeders'], - 'leechers' => (int)$TorrentInfo['Leechers'], - 'notificationTime' => $TorrentInfo['Time'], - 'hasLog' => $TorrentInfo['HasLog'] == 1, - 'hasCue' => $TorrentInfo['HasCue'] == 1, - 'logScore' => (float)$TorrentInfo['LogScore'], - 'freeTorrent' => $TorrentInfo['FreeTorrent'] == 1, - 'logInDb' => $TorrentInfo['HasLog'] == 1, - 'unread' => $Result['UnRead'] == 1 - ); - } + $JsonNotifications[] = array( + 'torrentId' => (int)$TorrentID, + 'groupId' => (int)$GroupID, + 'groupName' => $GroupName, + 'groupCategoryId' => (int)$GroupCategoryID, + 'wikiImage' => $WikiImage, + 'torrentTags' => $TagList, + 'size' => (float)$TorrentInfo['Size'], + 'fileCount' => (int)$TorrentInfo['FileCount'], + 'format' => $TorrentInfo['Format'], + 'encoding' => $TorrentInfo['Encoding'], + 'media' => $TorrentInfo['Media'], + 'scene' => $TorrentInfo['Scene'] == 1, + 'groupYear' => (int)$GroupYear, + 'remasterYear' => (int)$TorrentInfo['RemasterYear'], + 'remasterTitle' => $TorrentInfo['RemasterTitle'], + 'snatched' => (int)$TorrentInfo['Snatched'], + 'seeders' => (int)$TorrentInfo['Seeders'], + 'leechers' => (int)$TorrentInfo['Leechers'], + 'notificationTime' => $TorrentInfo['Time'], + 'hasLog' => $TorrentInfo['HasLog'] == 1, + 'hasCue' => $TorrentInfo['HasCue'] == 1, + 'logScore' => (float)$TorrentInfo['LogScore'], + 'freeTorrent' => $TorrentInfo['FreeTorrent'] == 1, + 'logInDb' => $TorrentInfo['HasLog'] == 1, + 'unread' => $Result['UnRead'] == 1 + ); + } } json_print("success", array( - 'currentPages' => intval($Page), - 'pages' => ceil($TorrentCount / NOTIFICATIONS_PER_PAGE), - 'numNew' => $NumNew, - 'results' => $JsonNotifications + 'currentPages' => intval($Page), + 'pages' => ceil($TorrentCount / NOTIFICATIONS_PER_PAGE), + 'numNew' => $NumNew, + 'results' => $JsonNotifications )); diff --git a/sections/ajax/password_validate.php b/sections/ajax/password_validate.php index c8b204d91..f2cb29aaf 100644 --- a/sections/ajax/password_validate.php +++ b/sections/ajax/password_validate.php @@ -3,12 +3,12 @@ $IsGoodPassword = false; $DB->query(" - SELECT Password - FROM bad_passwords - WHERE Password='$Password'"); + SELECT Password + FROM bad_passwords + WHERE Password='$Password'"); if (!$DB->has_results()) { - $IsGoodPassword = true; + $IsGoodPassword = true; } echo ($IsGoodPassword ? 'true' : 'false'); diff --git a/sections/ajax/preview.php b/sections/ajax/preview.php index 156a25230..dc8e5900b 100644 --- a/sections/ajax/preview.php +++ b/sections/ajax/preview.php @@ -1,12 +1,12 @@ - 'https://api.pushbullet.com/api/devices', - CURLOPT_HTTPAUTH => CURLAUTH_BASIC, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_USERPWD => $ApiKey . ':' + CURLOPT_URL => 'https://api.pushbullet.com/api/devices', + CURLOPT_HTTPAUTH => CURLAUTH_BASIC, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_USERPWD => $ApiKey . ':' )); $Result = curl_exec($Ch); diff --git a/sections/ajax/raw_bbcode.php b/sections/ajax/raw_bbcode.php index 686aa11c3..48ecfb032 100644 --- a/sections/ajax/raw_bbcode.php +++ b/sections/ajax/raw_bbcode.php @@ -1,24 +1,24 @@ -query(" - SELECT t.ForumID, p.Body - FROM forums_posts AS p - JOIN forums_topics AS t ON p.TopicID = t.ID - WHERE p.ID = '$PostID'"); + SELECT t.ForumID, p.Body + FROM forums_posts AS p + JOIN forums_topics AS t ON p.TopicID = t.ID + WHERE p.ID = '$PostID'"); if (!$DB->has_results()) { - json_die("error", "no results"); + json_die("error", "no results"); } list($ForumID, $Body) = $DB->next_record(); if (!Forums::check_forumperm($ForumID)) { - json_die("error", "assholes"); + json_die("error", "assholes"); } json_die("success", array("body" => nl2br($Body))); diff --git a/sections/ajax/request.php b/sections/ajax/request.php index 84e1dbac4..58eb45d35 100644 --- a/sections/ajax/request.php +++ b/sections/ajax/request.php @@ -1,4 +1,4 @@ - $ArtistForm[4] != null ? $ArtistForm[4] : array(), - 'dj' => $ArtistForm[6] != null ? $ArtistForm[6] : array(), - 'artists' => $ArtistForm[1] != null ? $ArtistForm[1] : array(), - 'with' => $ArtistForm[2] != null ? $ArtistForm[2] : array(), - 'conductor' => $ArtistForm[5] != null ? $ArtistForm[5] : array(), - 'remixedBy' => $ArtistForm[3] != null ? $ArtistForm[3] : array()*/ - 'composers' => isset($ArtistForm[4]) ? pullmediainfo($ArtistForm[4]) : array(), - 'dj' => isset($ArtistForm[6]) ? pullmediainfo($ArtistForm[6]) : array(), - 'artists' => isset($ArtistForm[1]) ? pullmediainfo($ArtistForm[1]) : array(), - 'with' => isset($ArtistForm[2]) ? pullmediainfo($ArtistForm[2]) : array(), - 'conductor' => isset($ArtistForm[5]) ? pullmediainfo($ArtistForm[5]) : array(), - 'remixedBy' => isset($ArtistForm[3]) ? pullmediainfo($ArtistForm[3]) : array(), - 'producer' => isset($ArtistForm[7]) ? pullmediainfo($ArtistForm[7]) : array() - ); + $JsonMusicInfo = array( + /*'composers' => $ArtistForm[4] != null ? $ArtistForm[4] : [], + 'dj' => $ArtistForm[6] != null ? $ArtistForm[6] : [], + 'artists' => $ArtistForm[1] != null ? $ArtistForm[1] : [], + 'with' => $ArtistForm[2] != null ? $ArtistForm[2] : [], + 'conductor' => $ArtistForm[5] != null ? $ArtistForm[5] : [], + 'remixedBy' => $ArtistForm[3] != null ? $ArtistForm[3] : []*/ + 'composers' => isset($ArtistForm[4]) ? pullmediainfo($ArtistForm[4]) : [], + 'dj' => isset($ArtistForm[6]) ? pullmediainfo($ArtistForm[6]) : [], + 'artists' => isset($ArtistForm[1]) ? pullmediainfo($ArtistForm[1]) : [], + 'with' => isset($ArtistForm[2]) ? pullmediainfo($ArtistForm[2]) : [], + 'conductor' => isset($ArtistForm[5]) ? pullmediainfo($ArtistForm[5]) : [], + 'remixedBy' => isset($ArtistForm[3]) ? pullmediainfo($ArtistForm[3]) : [], + 'producer' => isset($ArtistForm[7]) ? pullmediainfo($ArtistForm[7]) : [] + ); } else { - $JsonMusicInfo = new stdClass; //json_encodes into an empty object: {} + $JsonMusicInfo = new stdClass; //json_encodes into an empty object: {} } -$JsonTopContributors = array(); +$JsonTopContributors = []; $VoteMax = ($VoteCount < 5 ? $VoteCount : 5); for ($i = 0; $i < $VoteMax; $i++) { - $User = array_shift($RequestVotes['Voters']); - $JsonTopContributors[] = array( - 'userId' => (int)$User['UserID'], - 'userName' => $User['Username'], - 'bounty' => (int)$User['Bounty'] - ); + $User = array_shift($RequestVotes['Voters']); + $JsonTopContributors[] = array( + 'userId' => (int)$User['UserID'], + 'userName' => $User['Username'], + 'bounty' => (int)$User['Bounty'] + ); } reset($RequestVotes['Voters']); list($NumComments, $Page, $Thread) = Comments::load('requests', $RequestID, false); -$JsonRequestComments = array(); +$JsonRequestComments = []; foreach ($Thread as $Key => $Post) { - list($PostID, $AuthorID, $AddedTime, $Body, $EditedUserID, $EditedTime, $EditedUsername) = array_values($Post); - list($AuthorID, $Username, $PermissionID, $Paranoia, $Artist, $Donor, $Warned, $Avatar, $Enabled, $UserTitle) = array_values(Users::user_info($AuthorID)); - $JsonRequestComments[] = array( - 'postId' => (int)$PostID, - 'authorId' => (int)$AuthorID, - 'name' => $Username, - 'donor' => $Donor == 1, - 'warned' => ($Warned != '0000-00-00 00:00:00'), - 'enabled' => ($Enabled == 2 ? false : true), - 'class' => Users::make_class_string($PermissionID), - 'addedTime' => $AddedTime, - 'avatar' => $Avatar, - 'comment' => Text::full_format($Body), - 'editedUserId' => (int)$EditedUserID, - 'editedUsername' => $EditedUsername, - 'editedTime' => $EditedTime - ); + list($PostID, $AuthorID, $AddedTime, $Body, $EditedUserID, $EditedTime, $EditedUsername) = array_values($Post); + list($AuthorID, $Username, $PermissionID, $Paranoia, $Artist, $Donor, $Warned, $Avatar, $Enabled, $UserTitle) = array_values(Users::user_info($AuthorID)); + $JsonRequestComments[] = array( + 'postId' => (int)$PostID, + 'authorId' => (int)$AuthorID, + 'name' => $Username, + 'donor' => $Donor == 1, + 'warned' => ($Warned != '0000-00-00 00:00:00'), + 'enabled' => ($Enabled == 2 ? false : true), + 'class' => Users::make_class_string($PermissionID), + 'addedTime' => $AddedTime, + 'avatar' => $Avatar, + 'comment' => Text::full_format($Body), + 'editedUserId' => (int)$EditedUserID, + 'editedUsername' => $EditedUsername, + 'editedTime' => $EditedTime + ); } -$JsonTags = array(); +$JsonTags = []; foreach ($Request['Tags'] as $Tag) { - $JsonTags[] = $Tag; + $JsonTags[] = $Tag; } json_print('success', array( - 'requestId' => (int)$RequestID, - 'requestorId' => (int)$Request['UserID'], - 'requestorName' => $Requestor['Username'], - 'isBookmarked' => Bookmarks::has_bookmarked('request', $RequestID), - 'requestTax' => $RequestTax, - 'timeAdded' => $Request['TimeAdded'], - 'canEdit' => $CanEdit, - 'canVote' => $CanVote, - 'minimumVote' => $MinimumVote, - 'voteCount' => $VoteCount, - 'lastVote' => $Request['LastVote'], - 'topContributors' => $JsonTopContributors, - 'totalBounty' => (int)$RequestVotes['TotalBounty'], - 'categoryId' => (int)$CategoryID, - 'categoryName' => $CategoryName, - 'title' => $Request['Title'], - 'year' => (int)$Request['Year'], - 'image' => $Request['Image'], - 'bbDescription' => $Request['Description'], - 'description' => Text::full_format($Request['Description']), - 'musicInfo' => $JsonMusicInfo, - 'catalogueNumber' => $Request['CatalogueNumber'], - 'releaseType' => (int)$Request['ReleaseType'], - 'releaseName' => $ReleaseName, - 'bitrateList' => preg_split('/\|/', $Request['BitrateList'], null, PREG_SPLIT_NO_EMPTY), - 'formatList' => preg_split('/\|/', $Request['FormatList'], null, PREG_SPLIT_NO_EMPTY), - 'mediaList' => preg_split('/\|/', $Request['MediaList'], null, PREG_SPLIT_NO_EMPTY), - 'logCue' => html_entity_decode($Request['LogCue']), - 'isFilled' => $IsFilled, - 'fillerId' => (int)$Request['FillerID'], - 'fillerName' => ($Filler ? $Filler['Username'] : ''), - 'torrentId' => (int)$Request['TorrentID'], - 'timeFilled' => $Request['TimeFilled'], - 'tags' => $JsonTags, - 'comments' => $JsonRequestComments, - 'commentPage' => (int)$Page, - 'commentPages' => (int)ceil($NumComments / TORRENT_COMMENTS_PER_PAGE), - 'recordLabel' => $Request['RecordLabel'], - 'oclc' => $Request['OCLC'] + 'requestId' => (int)$RequestID, + 'requestorId' => (int)$Request['UserID'], + 'requestorName' => $Requestor['Username'], + 'isBookmarked' => Bookmarks::has_bookmarked('request', $RequestID), + 'requestTax' => $RequestTax, + 'timeAdded' => $Request['TimeAdded'], + 'canEdit' => $CanEdit, + 'canVote' => $CanVote, + 'minimumVote' => $MinimumVote, + 'voteCount' => $VoteCount, + 'lastVote' => $Request['LastVote'], + 'topContributors' => $JsonTopContributors, + 'totalBounty' => (int)$RequestVotes['TotalBounty'], + 'categoryId' => (int)$CategoryID, + 'categoryName' => $CategoryName, + 'title' => $Request['Title'], + 'year' => (int)$Request['Year'], + 'image' => $Request['Image'], + 'bbDescription' => $Request['Description'], + 'description' => Text::full_format($Request['Description']), + 'musicInfo' => $JsonMusicInfo, + 'catalogueNumber' => $Request['CatalogueNumber'], + 'releaseType' => (int)$Request['ReleaseType'], + 'releaseName' => $ReleaseName, + 'bitrateList' => preg_split('/\|/', $Request['BitrateList'], null, PREG_SPLIT_NO_EMPTY), + 'formatList' => preg_split('/\|/', $Request['FormatList'], null, PREG_SPLIT_NO_EMPTY), + 'mediaList' => preg_split('/\|/', $Request['MediaList'], null, PREG_SPLIT_NO_EMPTY), + 'logCue' => html_entity_decode($Request['LogCue']), + 'isFilled' => $IsFilled, + 'fillerId' => (int)$Request['FillerID'], + 'fillerName' => ($Filler ? $Filler['Username'] : ''), + 'torrentId' => (int)$Request['TorrentID'], + 'timeFilled' => $Request['TimeFilled'], + 'tags' => $JsonTags, + 'comments' => $JsonRequestComments, + 'commentPage' => (int)$Page, + 'commentPages' => (int)ceil($NumComments / TORRENT_COMMENTS_PER_PAGE), + 'recordLabel' => $Request['RecordLabel'], + 'oclc' => $Request['OCLC'] )); ?> diff --git a/sections/ajax/requests.php b/sections/ajax/requests.php index 1add4ec1f..4255bc93c 100644 --- a/sections/ajax/requests.php +++ b/sections/ajax/requests.php @@ -3,388 +3,388 @@ $SphQL->select('id, votes, bounty')->from('requests, requests_delta'); $SortOrders = array( - 'votes' => 'votes', - 'bounty' => 'bounty', - 'lastvote' => 'lastvote', - 'filled' => 'timefilled', - 'year' => 'year', - 'created' => 'timeadded', - 'random' => false); + 'votes' => 'votes', + 'bounty' => 'bounty', + 'lastvote' => 'lastvote', + 'filled' => 'timefilled', + 'year' => 'year', + 'created' => 'timeadded', + 'random' => false); if (empty($_GET['order']) || !isset($SortOrders[$_GET['order']])) { - $_GET['order'] = 'created'; + $_GET['order'] = 'created'; } $OrderBy = $_GET['order']; if (!empty($_GET['sort']) && $_GET['sort'] === 'asc') { - $OrderWay = 'asc'; + $OrderWay = 'asc'; } else { - $_GET['sort'] = 'desc'; - $OrderWay = 'desc'; + $_GET['sort'] = 'desc'; + $OrderWay = 'desc'; } $NewSort = $_GET['sort'] === 'asc' ? 'desc' : 'asc'; if ($OrderBy === 'random') { - $SphQL->order_by('RAND()', ''); - unset($_GET['page']); + $SphQL->order_by('RAND()', ''); + unset($_GET['page']); } else { - $SphQL->order_by($SortOrders[$OrderBy], $OrderWay); + $SphQL->order_by($SortOrders[$OrderBy], $OrderWay); } $Submitted = !empty($_GET['submit']); //Paranoia if (!empty($_GET['userid'])) { - if (!is_number($_GET['userid'])) { - json_die("failure"); - } - $UserInfo = Users::user_info($_GET['userid']); - if (empty($UserInfo)) { - json_die("failure"); - } - $Perms = Permissions::get_permissions($UserInfo['PermissionID']); - $UserClass = $Perms['Class']; + if (!is_number($_GET['userid'])) { + json_die("failure"); + } + $UserInfo = Users::user_info($_GET['userid']); + if (empty($UserInfo)) { + json_die("failure"); + } + $Perms = Permissions::get_permissions($UserInfo['PermissionID']); + $UserClass = $Perms['Class']; } $BookmarkView = false; if (empty($_GET['type'])) { - $Title = 'Requests'; - if (empty($_GET['showall'])) { - $SphQL->where('visible', 1); - } + $Title = 'Requests'; + if (empty($_GET['showall'])) { + $SphQL->where('visible', 1); + } } else { - switch ($_GET['type']) { - case 'created': - if (!empty($UserInfo)) { - if (!check_paranoia('requestsvoted_list', $UserInfo['Paranoia'], $Perms['Class'], $UserInfo['ID'])) { - json_die("failure"); - } - $Title = "Requests created by $UserInfo[Username]"; - $SphQL->where('userid', $UserInfo['ID']); - } else { - $Title = 'My requests'; - $SphQL->where('userid', $LoggedUser['ID']); - } - break; - case 'voted': - if (!empty($UserInfo)) { - if (!check_paranoia('requestsvoted_list', $UserInfo['Paranoia'], $Perms['Class'], $UserInfo['ID'])) { - json_die("failure"); - } - $Title = "Requests voted for by $UserInfo[Username]"; - $SphQL->where('voter', $UserInfo['ID']); - } else { - $Title = 'Requests I have voted on'; - $SphQL->where('voter', $LoggedUser['ID']); - } - break; - case 'filled': - if (!empty($UserInfo)) { - if (!check_paranoia('requestsfilled_list', $UserInfo['Paranoia'], $Perms['Class'], $UserInfo['ID'])) { - json_die("failure"); - } - $Title = "Requests filled by $UserInfo[Username]"; - $SphQL->where('fillerid', $UserInfo['ID']); - } else { - $Title = 'Requests I have filled'; - $SphQL->where('fillerid', $LoggedUser['ID']); - } - break; - case 'bookmarks': - $Title = 'Your bookmarked requests'; - $BookmarkView = true; - $SphQL->where('bookmarker', $LoggedUser['ID']); - break; - default: - json_die("failure"); - } + switch ($_GET['type']) { + case 'created': + if (!empty($UserInfo)) { + if (!check_paranoia('requestsvoted_list', $UserInfo['Paranoia'], $Perms['Class'], $UserInfo['ID'])) { + json_die("failure"); + } + $Title = "Requests created by $UserInfo[Username]"; + $SphQL->where('userid', $UserInfo['ID']); + } else { + $Title = 'My requests'; + $SphQL->where('userid', $LoggedUser['ID']); + } + break; + case 'voted': + if (!empty($UserInfo)) { + if (!check_paranoia('requestsvoted_list', $UserInfo['Paranoia'], $Perms['Class'], $UserInfo['ID'])) { + json_die("failure"); + } + $Title = "Requests voted for by $UserInfo[Username]"; + $SphQL->where('voter', $UserInfo['ID']); + } else { + $Title = 'Requests I have voted on'; + $SphQL->where('voter', $LoggedUser['ID']); + } + break; + case 'filled': + if (!empty($UserInfo)) { + if (!check_paranoia('requestsfilled_list', $UserInfo['Paranoia'], $Perms['Class'], $UserInfo['ID'])) { + json_die("failure"); + } + $Title = "Requests filled by $UserInfo[Username]"; + $SphQL->where('fillerid', $UserInfo['ID']); + } else { + $Title = 'Requests I have filled'; + $SphQL->where('fillerid', $LoggedUser['ID']); + } + break; + case 'bookmarks': + $Title = 'Your bookmarked requests'; + $BookmarkView = true; + $SphQL->where('bookmarker', $LoggedUser['ID']); + break; + default: + json_die("failure"); + } } if ($Submitted && empty($_GET['show_filled'])) { - $SphQL->where('torrentid', 0); + $SphQL->where('torrentid', 0); } $EnableNegation = false; // Sphinx needs at least one positive search condition to support the NOT operator if (!empty($_GET['formats'])) { - $FormatArray = $_GET['formats']; - if (count($FormatArray) !== count($Formats)) { - $FormatNameArray = array(); - foreach ($FormatArray as $Index => $MasterIndex) { - if (isset($Formats[$MasterIndex])) { - $FormatNameArray[$Index] = '"' . strtr(Sphinxql::sph_escape_string($Formats[$MasterIndex]), '-.', ' ') . '"'; - } - } - if (count($FormatNameArray) >= 1) { - $EnableNegation = true; - if (!empty($_GET['formats_strict'])) { - $SearchString = '(' . implode(' | ', $FormatNameArray) . ')'; - } else { - $SearchString = '(any | ' . implode(' | ', $FormatNameArray) . ')'; - } - $SphQL->where_match($SearchString, 'formatlist', false); - } - } + $FormatArray = $_GET['formats']; + if (count($FormatArray) !== count($Formats)) { + $FormatNameArray = array(); + foreach ($FormatArray as $Index => $MasterIndex) { + if (isset($Formats[$MasterIndex])) { + $FormatNameArray[$Index] = '"' . strtr(Sphinxql::sph_escape_string($Formats[$MasterIndex]), '-.', ' ') . '"'; + } + } + if (count($FormatNameArray) >= 1) { + $EnableNegation = true; + if (!empty($_GET['formats_strict'])) { + $SearchString = '(' . implode(' | ', $FormatNameArray) . ')'; + } else { + $SearchString = '(any | ' . implode(' | ', $FormatNameArray) . ')'; + } + $SphQL->where_match($SearchString, 'formatlist', false); + } + } } if (!empty($_GET['media'])) { - $MediaArray = $_GET['media']; - if (count($MediaArray) !== count($Media)) { - $MediaNameArray = array(); - foreach ($MediaArray as $Index => $MasterIndex) { - if (isset($Media[$MasterIndex])) { - $MediaNameArray[$Index] = '"' . strtr(Sphinxql::sph_escape_string($Media[$MasterIndex]), '-.', ' ') . '"'; - } - } + $MediaArray = $_GET['media']; + if (count($MediaArray) !== count($Media)) { + $MediaNameArray = array(); + foreach ($MediaArray as $Index => $MasterIndex) { + if (isset($Media[$MasterIndex])) { + $MediaNameArray[$Index] = '"' . strtr(Sphinxql::sph_escape_string($Media[$MasterIndex]), '-.', ' ') . '"'; + } + } - if (count($MediaNameArray) >= 1) { - $EnableNegation = true; - if (!empty($_GET['media_strict'])) { - $SearchString = '(' . implode(' | ', $MediaNameArray) . ')'; - } else { - $SearchString = '(any | ' . implode(' | ', $MediaNameArray) . ')'; - } - $SphQL->where_match($SearchString, 'medialist', false); - } - } + if (count($MediaNameArray) >= 1) { + $EnableNegation = true; + if (!empty($_GET['media_strict'])) { + $SearchString = '(' . implode(' | ', $MediaNameArray) . ')'; + } else { + $SearchString = '(any | ' . implode(' | ', $MediaNameArray) . ')'; + } + $SphQL->where_match($SearchString, 'medialist', false); + } + } } if (!empty($_GET['bitrates'])) { - $BitrateArray = $_GET['bitrates']; - if (count($BitrateArray) !== count($Bitrates)) { - $BitrateNameArray = array(); - foreach ($BitrateArray as $Index => $MasterIndex) { - if (isset($Bitrates[$MasterIndex])) { - $BitrateNameArray[$Index] = '"' . strtr(Sphinxql::sph_escape_string($Bitrates[$MasterIndex]), '-.', ' ') . '"'; - } - } + $BitrateArray = $_GET['bitrates']; + if (count($BitrateArray) !== count($Bitrates)) { + $BitrateNameArray = array(); + foreach ($BitrateArray as $Index => $MasterIndex) { + if (isset($Bitrates[$MasterIndex])) { + $BitrateNameArray[$Index] = '"' . strtr(Sphinxql::sph_escape_string($Bitrates[$MasterIndex]), '-.', ' ') . '"'; + } + } - if (count($BitrateNameArray) >= 1) { - $EnableNegation = true; - if (!empty($_GET['bitrate_strict'])) { - $SearchString = '(' . implode(' | ', $BitrateNameArray) . ')'; - } else { - $SearchString = '(any | ' . implode(' | ', $BitrateNameArray) . ')'; - } - $SphQL->where_match($SearchString, 'bitratelist', false); - } - } + if (count($BitrateNameArray) >= 1) { + $EnableNegation = true; + if (!empty($_GET['bitrate_strict'])) { + $SearchString = '(' . implode(' | ', $BitrateNameArray) . ')'; + } else { + $SearchString = '(any | ' . implode(' | ', $BitrateNameArray) . ')'; + } + $SphQL->where_match($SearchString, 'bitratelist', false); + } + } } if (!empty($_GET['search'])) { - $SearchString = trim($_GET['search']); - if ($SearchString !== '') { - $SearchWords = array('include' => array(), 'exclude' => array()); - $Words = explode(' ', $SearchString); - foreach ($Words as $Word) { - $Word = trim($Word); - // Skip isolated hyphens to enable "Artist - Title" searches - if ($Word === '-') { - continue; - } - if ($Word[0] === '!' && strlen($Word) >= 2) { - if (strpos($Word, '!', 1) === false) { - $SearchWords['exclude'][] = $Word; - } else { - $SearchWords['include'][] = $Word; - $EnableNegation = true; - } - } elseif ($Word !== '') { - $SearchWords['include'][] = $Word; - $EnableNegation = true; - } - } - } + $SearchString = trim($_GET['search']); + if ($SearchString !== '') { + $SearchWords = array('include' => array(), 'exclude' => array()); + $Words = explode(' ', $SearchString); + foreach ($Words as $Word) { + $Word = trim($Word); + // Skip isolated hyphens to enable "Artist - Title" searches + if ($Word === '-') { + continue; + } + if ($Word[0] === '!' && strlen($Word) >= 2) { + if (strpos($Word, '!', 1) === false) { + $SearchWords['exclude'][] = $Word; + } else { + $SearchWords['include'][] = $Word; + $EnableNegation = true; + } + } elseif ($Word !== '') { + $SearchWords['include'][] = $Word; + $EnableNegation = true; + } + } + } } if (!isset($_GET['tags_type']) || $_GET['tags_type'] === '1') { - $TagType = 1; - $_GET['tags_type'] = '1'; + $TagType = 1; + $_GET['tags_type'] = '1'; } else { - $TagType = 0; - $_GET['tags_type'] = '0'; + $TagType = 0; + $_GET['tags_type'] = '0'; } if (!empty($_GET['tags'])) { - $SearchTags = array('include' => array(), 'exclude' => array()); - $Tags = explode(',', $_GET['tags']); - foreach ($Tags as $Tag) { - $Tag = trim($Tag); - if ($Tag[0] === '!' && strlen($Tag) >= 2) { - if (strpos($Tag, '!', 1) === false) { - $SearchTags['exclude'][] = $Tag; - } else { - $SearchTags['include'][] = $Tag; - $EnableNegation = true; - } - } elseif ($Tag !== '') { - $SearchTags['include'][] = $Tag; - $EnableNegation = true; - } - } + $SearchTags = array('include' => array(), 'exclude' => array()); + $Tags = explode(',', $_GET['tags']); + foreach ($Tags as $Tag) { + $Tag = trim($Tag); + if ($Tag[0] === '!' && strlen($Tag) >= 2) { + if (strpos($Tag, '!', 1) === false) { + $SearchTags['exclude'][] = $Tag; + } else { + $SearchTags['include'][] = $Tag; + $EnableNegation = true; + } + } elseif ($Tag !== '') { + $SearchTags['include'][] = $Tag; + $EnableNegation = true; + } + } - $TagFilter = Tags::tag_filter_sph($SearchTags, $EnableNegation, $TagType); + $TagFilter = Tags::tag_filter_sph($SearchTags, $EnableNegation, $TagType); - if (!empty($TagFilter['predicate'])) { - $SphQL->where_match($TagFilter['predicate'], 'taglist', false); - } + if (!empty($TagFilter['predicate'])) { + $SphQL->where_match($TagFilter['predicate'], 'taglist', false); + } } elseif (!isset($_GET['tags_type']) || $_GET['tags_type'] !== '0') { - $_GET['tags_type'] = 1; + $_GET['tags_type'] = 1; } else { - $_GET['tags_type'] = 0; + $_GET['tags_type'] = 0; } if (isset($SearchWords)) { - $QueryParts = array(); - if (!$EnableNegation && !empty($SearchWords['exclude'])) { - $SearchWords['include'] = array_merge($SearchWords['include'], $SearchWords['exclude']); - unset($SearchWords['exclude']); - } - foreach ($SearchWords['include'] as $Word) { - $QueryParts[] = Sphinxql::sph_escape_string($Word); - } - if (!empty($SearchWords['exclude'])) { - foreach ($SearchWords['exclude'] as $Word) { - $QueryParts[] = '!' . Sphinxql::sph_escape_string(substr($Word, 1)); - } - } - if (!empty($QueryParts)) { - $SearchString = implode(' ', $QueryParts); - $SphQL->where_match($SearchString, '*', false); - } + $QueryParts = array(); + if (!$EnableNegation && !empty($SearchWords['exclude'])) { + $SearchWords['include'] = array_merge($SearchWords['include'], $SearchWords['exclude']); + unset($SearchWords['exclude']); + } + foreach ($SearchWords['include'] as $Word) { + $QueryParts[] = Sphinxql::sph_escape_string($Word); + } + if (!empty($SearchWords['exclude'])) { + foreach ($SearchWords['exclude'] as $Word) { + $QueryParts[] = '!' . Sphinxql::sph_escape_string(substr($Word, 1)); + } + } + if (!empty($QueryParts)) { + $SearchString = implode(' ', $QueryParts); + $SphQL->where_match($SearchString, '*', false); + } } if (!empty($_GET['filter_cat'])) { - $CategoryArray = array_keys($_GET['filter_cat']); - if (count($CategoryArray) !== count($Categories)) { - foreach ($CategoryArray as $Key => $Index) { - if (!isset($Categories[$Index - 1])) { - unset($CategoryArray[$Key]); - } - } - if (count($CategoryArray) >= 1) { - $SphQL->where('categoryid', $CategoryArray); - } - } + $CategoryArray = array_keys($_GET['filter_cat']); + if (count($CategoryArray) !== count($Categories)) { + foreach ($CategoryArray as $Key => $Index) { + if (!isset($Categories[$Index - 1])) { + unset($CategoryArray[$Key]); + } + } + if (count($CategoryArray) >= 1) { + $SphQL->where('categoryid', $CategoryArray); + } + } } if (!empty($_GET['releases'])) { - $ReleaseArray = $_GET['releases']; - if (count($ReleaseArray) !== count($ReleaseTypes)) { - foreach ($ReleaseArray as $Index => $Value) { - if (!isset($ReleaseTypes[$Value])) { - unset($ReleaseArray[$Index]); - } - } - if (count($ReleaseArray) >= 1) { - $SphQL->where('releasetype', $ReleaseArray); - } - } + $ReleaseArray = $_GET['releases']; + if (count($ReleaseArray) !== count($ReleaseTypes)) { + foreach ($ReleaseArray as $Index => $Value) { + if (!isset($ReleaseTypes[$Value])) { + unset($ReleaseArray[$Index]); + } + } + if (count($ReleaseArray) >= 1) { + $SphQL->where('releasetype', $ReleaseArray); + } + } } if (!empty($_GET['requestor'])) { - if (is_number($_GET['requestor'])) { - $SphQL->where('userid', $_GET['requestor']); - } else { - error(404); - } + if (is_number($_GET['requestor'])) { + $SphQL->where('userid', $_GET['requestor']); + } else { + error(404); + } } if (isset($_GET['year'])) { - if (is_number($_GET['year']) || $_GET['year'] === '0') { - $SphQL->where('year', $_GET['year']); - } else { - error(404); - } + if (is_number($_GET['year']) || $_GET['year'] === '0') { + $SphQL->where('year', $_GET['year']); + } else { + error(404); + } } if (!empty($_GET['page']) && is_number($_GET['page']) && $_GET['page'] > 0) { - $Page = $_GET['page']; - $Offset = ($Page - 1) * REQUESTS_PER_PAGE; - $SphQL->limit($Offset, REQUESTS_PER_PAGE, $Offset + REQUESTS_PER_PAGE); + $Page = $_GET['page']; + $Offset = ($Page - 1) * REQUESTS_PER_PAGE; + $SphQL->limit($Offset, REQUESTS_PER_PAGE, $Offset + REQUESTS_PER_PAGE); } else { - $Page = 1; - $SphQL->limit(0, REQUESTS_PER_PAGE, REQUESTS_PER_PAGE); + $Page = 1; + $SphQL->limit(0, REQUESTS_PER_PAGE, REQUESTS_PER_PAGE); } $SphQLResult = $SphQL->query(); $NumResults = (int)$SphQLResult->get_meta('total_found'); if ($NumResults > 0) { - $SphRequests = $SphQLResult->to_array('id'); - if ($OrderBy === 'random') { - $NumResults = count($RequestIDs); - } - if ($NumResults > REQUESTS_PER_PAGE) { - if (($Page - 1) * REQUESTS_PER_PAGE > $NumResults) { - $Page = 0; - } - } + $SphRequests = $SphQLResult->to_array('id'); + if ($OrderBy === 'random') { + $NumResults = count($RequestIDs); + } + if ($NumResults > REQUESTS_PER_PAGE) { + if (($Page - 1) * REQUESTS_PER_PAGE > $NumResults) { + $Page = 0; + } + } } if ($NumResults == 0) { - json_print("success", array( - 'currentPage' => 1, - 'pages' => 1, - 'results' => array() - )); + json_print("success", array( + 'currentPage' => 1, + 'pages' => 1, + 'results' => array() + )); } else { - $JsonResults = array(); - $TimeCompare = 1267643718; // Requests v2 was implemented 2010-03-03 20:15:18 - $Requests = Requests::get_requests(array_keys($SphRequests)); - foreach ($SphRequests as $RequestID => $SphRequest) { - $Request = $Requests[$RequestID]; - $VoteCount = $SphRequest['votes']; - $Bounty = $SphRequest['bounty'] * 1024; // Sphinx stores bounty in kB - $Requestor = Users::user_info($Request['UserID']); - $Filler = $Request['FillerID'] ? Users::user_info($Request['FillerID']) : null; + $JsonResults = array(); + $TimeCompare = 1267643718; // Requests v2 was implemented 2010-03-03 20:15:18 + $Requests = Requests::get_requests(array_keys($SphRequests)); + foreach ($SphRequests as $RequestID => $SphRequest) { + $Request = $Requests[$RequestID]; + $VoteCount = $SphRequest['votes']; + $Bounty = $SphRequest['bounty'] * 1024; // Sphinx stores bounty in kB + $Requestor = Users::user_info($Request['UserID']); + $Filler = $Request['FillerID'] ? Users::user_info($Request['FillerID']) : null; - if ($Request['CategoryID'] == 0) { - $CategoryName = 'Unknown'; - } else { - $CategoryName = $Categories[$Request['CategoryID'] - 1]; - } + if ($Request['CategoryID'] == 0) { + $CategoryName = 'Unknown'; + } else { + $CategoryName = $Categories[$Request['CategoryID'] - 1]; + } - $JsonArtists = array(); - if ($CategoryName == 'Music') { - $ArtistForm = Requests::get_artists($RequestID); - $JsonArtists = array_values($ArtistForm); - } + $JsonArtists = array(); + if ($CategoryName == 'Music') { + $ArtistForm = Requests::get_artists($RequestID); + $JsonArtists = array_values($ArtistForm); + } - $Tags = $Request['Tags']; + $Tags = $Request['Tags']; - $JsonResults[] = array( - 'requestId' => (int)$RequestID, - 'requestorId' => (int)$Requestor['ID'], - 'requestorName' => $Requestor['Username'], - 'timeAdded' => $Request['TimeAdded'], - 'lastVote' => $Request['LastVote'], - 'voteCount' => (int)$VoteCount, - 'bounty' => (int)$Bounty, - 'categoryId' => (int)$Request['CategoryID'], - 'categoryName' => $CategoryName, - 'artists' => $JsonArtists, - 'title' => $Request['Title'], - 'year' => (int)$Request['Year'], - 'image' => $Request['Image'], - 'description' => $Request['Description'], - 'recordLabel' => $Request['RecordLabel'], - 'catalogueNumber' => $Request['CatalogueNumber'], - 'releaseType' => $ReleaseTypes[$Request['ReleaseType']], - 'bitrateList' => $Request['BitrateList'], - 'formatList' => $Request['FormatList'], - 'mediaList' => $Request['MediaList'], - 'logCue' => $Request['LogCue'], - 'isFilled' => ($Request['TorrentID'] > 0), - 'fillerId' => (int)$Request['FillerID'], - 'fillerName' => $Filler ? $Filler['Username'] : '', - 'torrentId' => (int)$Request['TorrentID'], - 'timeFilled' => $Request['TimeFilled'] == 0 ? '' : $Request['TimeFilled'] - ); - } + $JsonResults[] = array( + 'requestId' => (int)$RequestID, + 'requestorId' => (int)$Requestor['ID'], + 'requestorName' => $Requestor['Username'], + 'timeAdded' => $Request['TimeAdded'], + 'lastVote' => $Request['LastVote'], + 'voteCount' => (int)$VoteCount, + 'bounty' => (int)$Bounty, + 'categoryId' => (int)$Request['CategoryID'], + 'categoryName' => $CategoryName, + 'artists' => $JsonArtists, + 'title' => $Request['Title'], + 'year' => (int)$Request['Year'], + 'image' => $Request['Image'], + 'description' => $Request['Description'], + 'recordLabel' => $Request['RecordLabel'], + 'catalogueNumber' => $Request['CatalogueNumber'], + 'releaseType' => $ReleaseTypes[$Request['ReleaseType']], + 'bitrateList' => $Request['BitrateList'], + 'formatList' => $Request['FormatList'], + 'mediaList' => $Request['MediaList'], + 'logCue' => $Request['LogCue'], + 'isFilled' => ($Request['TorrentID'] > 0), + 'fillerId' => (int)$Request['FillerID'], + 'fillerName' => $Filler ? $Filler['Username'] : '', + 'torrentId' => (int)$Request['TorrentID'], + 'timeFilled' => $Request['TimeFilled'] == 0 ? '' : $Request['TimeFilled'] + ); + } - json_print("success", array( - 'currentPage' => intval($Page), - 'pages' => ceil($NumResults / REQUESTS_PER_PAGE), - 'results' => $JsonResults - )); + json_print("success", array( + 'currentPage' => intval($Page), + 'pages' => ceil($NumResults / REQUESTS_PER_PAGE), + 'results' => $JsonResults + )); } ?> diff --git a/sections/ajax/send_recommendation.php b/sections/ajax/send_recommendation.php index 13d5642d3..32f5e124b 100644 --- a/sections/ajax/send_recommendation.php +++ b/sections/ajax/send_recommendation.php @@ -5,23 +5,23 @@ $Note = $_POST['note']; if (empty($FriendID) || empty($Type) || empty($ID)) { - echo json_encode(array('status' => 'error', 'response' => 'Error.')); - die(); + echo json_encode(array('status' => 'error', 'response' => 'Error.')); + die(); } // Make sure the recipient is on your friends list and not some random dude. $DB->query(" - SELECT f.FriendID, u.Username - FROM friends AS f - RIGHT JOIN users_enable_recommendations AS r - ON r.ID = f.FriendID AND r.Enable = 1 - RIGHT JOIN users_main AS u - ON u.ID = f.FriendID - WHERE f.UserID = '$LoggedUser[ID]' - AND f.FriendID = '$FriendID'"); + SELECT f.FriendID, u.Username + FROM friends AS f + RIGHT JOIN users_enable_recommendations AS r + ON r.ID = f.FriendID AND r.Enable = 1 + RIGHT JOIN users_main AS u + ON u.ID = f.FriendID + WHERE f.UserID = '$LoggedUser[ID]' + AND f.FriendID = '$FriendID'"); if (!$DB->has_results()) { - echo json_encode(array('status' => 'error', 'response' => 'Not on friend list.')); - die(); + echo json_encode(array('status' => 'error', 'response' => 'Not on friend list.')); + die(); } $Type = strtolower($Type); @@ -30,34 +30,34 @@ // https://en.wikipedia.org/wiki/English_articles#Distinction_between_a_and_an $Article = 'a'; switch ($Type) { - case 'torrent': - $Link = "torrents.php?id=$ID"; - $DB->query(" - SELECT Name - FROM torrents_group - WHERE ID = '$ID'"); - break; - case 'artist': - $Article = 'an'; - $Link = "artist.php?id=$ID"; - $DB->query(" - SELECT Name - FROM artists_group - WHERE ArtistID = '$ID'"); - break; - case 'collage': - $Link = "collages.php?id=$ID"; - $DB->query(" - SELECT Name - FROM collages - WHERE ID = '$ID'"); - break; + case 'torrent': + $Link = "torrents.php?id=$ID"; + $DB->query(" + SELECT Name + FROM torrents_group + WHERE ID = '$ID'"); + break; + case 'artist': + $Article = 'an'; + $Link = "artist.php?id=$ID"; + $DB->query(" + SELECT Name + FROM artists_group + WHERE ArtistID = '$ID'"); + break; + case 'collage': + $Link = "collages.php?id=$ID"; + $DB->query(" + SELECT Name + FROM collages + WHERE ID = '$ID'"); + break; } list($Name) = $DB->next_record(); $Subject = $LoggedUser['Username'] . " recommended you $Article $Type!"; $Body = $LoggedUser['Username'] . " recommended you the $Type [url=".site_url()."$Link]$Name".'[/url].'; if (!empty($Note)) { - $Body = "$Body\n\n$Note"; + $Body = "$Body\n\n$Note"; } Misc::send_pm($FriendID, $LoggedUser['ID'], $Subject, $Body); diff --git a/sections/ajax/similar_artists.php b/sections/ajax/similar_artists.php index 93558a832..b84f60616 100644 --- a/sections/ajax/similar_artists.php +++ b/sections/ajax/similar_artists.php @@ -1,41 +1,41 @@ 'failure' - ) - ); - die(); + print + json_encode( + array( + 'status' => 'failure' + ) + ); + die(); } $artist_id = $_GET["id"]; $artist_limit = $_GET["limit"]; $DB->query(" - SELECT - s2.ArtistID, - ag.Name, - ass.Score - FROM artists_similar AS s1 - JOIN artists_similar AS s2 ON s1.SimilarID = s2.SimilarID AND s1.ArtistID != s2.ArtistID - JOIN artists_similar_scores AS ass ON ass.SimilarID = s1.SimilarID - JOIN artists_group AS ag ON ag.ArtistID = s2.ArtistID - WHERE s1.ArtistID = $artist_id - ORDER BY ass.Score DESC - LIMIT $artist_limit"); + SELECT + s2.ArtistID, + ag.Name, + ass.Score + FROM artists_similar AS s1 + JOIN artists_similar AS s2 ON s1.SimilarID = s2.SimilarID AND s1.ArtistID != s2.ArtistID + JOIN artists_similar_scores AS ass ON ass.SimilarID = s1.SimilarID + JOIN artists_group AS ag ON ag.ArtistID = s2.ArtistID + WHERE s1.ArtistID = $artist_id + ORDER BY ass.Score DESC + LIMIT $artist_limit"); - while (list($ArtistID, $Name, $Score) = $DB->next_record(MYSQLI_NUM, false)) { - if ($Score < 0) { - continue; - } - $results[] = array( - 'id' => (int)$ArtistID, - 'name' => $Name, - 'score' => (int)$Score); - } + while (list($ArtistID, $Name, $Score) = $DB->next_record(MYSQLI_NUM, false)) { + if ($Score < 0) { + continue; + } + $results[] = array( + 'id' => (int)$ArtistID, + 'name' => $Name, + 'score' => (int)$Score); + } print json_encode($results); exit(); diff --git a/sections/ajax/stats.php b/sections/ajax/stats.php index 3001e7da7..9a696ef34 100644 --- a/sections/ajax/stats.php +++ b/sections/ajax/stats.php @@ -1,134 +1,125 @@ -get_value('stats_user_count')) === false) { - $DB->query(" - SELECT COUNT(ID) - FROM users_main - WHERE Enabled = '1'"); - list($UserCount) = $DB->next_record(); - $Cache->cache_value('stats_user_count', $UserCount, 0); //inf cache -} +$UserCount = Users::get_enabled_users_count(); if (($UserStats = $Cache->get_value('stats_users')) === false) { - $DB->query(" - SELECT COUNT(ID) - FROM users_main - WHERE Enabled = '1' - AND LastAccess > '".time_minus(3600 * 24)."'"); - list($UserStats['Day']) = $DB->next_record(); + $DB->query(" + SELECT COUNT(ID) + FROM users_main + WHERE Enabled = '1' + AND LastAccess > '".time_minus(3600 * 24)."'"); + list($UserStats['Day']) = $DB->next_record(); - $DB->query(" - SELECT COUNT(ID) - FROM users_main - WHERE Enabled = '1' - AND LastAccess > '".time_minus(3600 * 24 * 7)."'"); - list($UserStats['Week']) = $DB->next_record(); + $DB->query(" + SELECT COUNT(ID) + FROM users_main + WHERE Enabled = '1' + AND LastAccess > '".time_minus(3600 * 24 * 7)."'"); + list($UserStats['Week']) = $DB->next_record(); - $DB->query(" - SELECT COUNT(ID) - FROM users_main - WHERE Enabled = '1' - AND LastAccess > '".time_minus(3600 * 24 * 30)."'"); - list($UserStats['Month']) = $DB->next_record(); + $DB->query(" + SELECT COUNT(ID) + FROM users_main + WHERE Enabled = '1' + AND LastAccess > '".time_minus(3600 * 24 * 30)."'"); + list($UserStats['Month']) = $DB->next_record(); - $Cache->cache_value('stats_users', $UserStats, 0); + $Cache->cache_value('stats_users', $UserStats, 0); } // Begin torrent stats if (($TorrentCount = $Cache->get_value('stats_torrent_count')) === false) { - $DB->query(" - SELECT COUNT(ID) - FROM torrents"); - list($TorrentCount) = $DB->next_record(); - $Cache->cache_value('stats_torrent_count', $TorrentCount, 604800); // staggered 1 week cache + $DB->query(" + SELECT COUNT(ID) + FROM torrents"); + list($TorrentCount) = $DB->next_record(); + $Cache->cache_value('stats_torrent_count', $TorrentCount, 604800); // staggered 1 week cache } if (($AlbumCount = $Cache->get_value('stats_album_count')) === false) { - $DB->query(" - SELECT COUNT(ID) - FROM torrents_group - WHERE CategoryID = '1'"); - list($AlbumCount) = $DB->next_record(); - $Cache->cache_value('stats_album_count', $AlbumCount, 604830); // staggered 1 week cache + $DB->query(" + SELECT COUNT(ID) + FROM torrents_group + WHERE CategoryID = '1'"); + list($AlbumCount) = $DB->next_record(); + $Cache->cache_value('stats_album_count', $AlbumCount, 604830); // staggered 1 week cache } if (($ArtistCount = $Cache->get_value('stats_artist_count')) === false) { - $DB->query(" - SELECT COUNT(ArtistID) - FROM artists_group"); - list($ArtistCount) = $DB->next_record(); - $Cache->cache_value('stats_artist_count', $ArtistCount, 604860); // staggered 1 week cache + $DB->query(" + SELECT COUNT(ArtistID) + FROM artists_group"); + list($ArtistCount) = $DB->next_record(); + $Cache->cache_value('stats_artist_count', $ArtistCount, 604860); // staggered 1 week cache } if (($PerfectCount = $Cache->get_value('stats_perfect_count')) === false) { - $DB->query(" - SELECT COUNT(ID) - FROM torrents - WHERE ((LogScore = 100 AND Format = 'FLAC') - OR (Media = 'Vinyl' AND Format = 'FLAC') - OR (Media = 'WEB' AND Format = 'FLAC') - OR (Media = 'DVD' AND Format = 'FLAC') - OR (Media = 'Soundboard' AND Format = 'FLAC') + $DB->query(" + SELECT COUNT(ID) + FROM torrents + WHERE ((LogScore = 100 AND Format = 'FLAC') + OR (Media = 'Vinyl' AND Format = 'FLAC') + OR (Media = 'WEB' AND Format = 'FLAC') + OR (Media = 'DVD' AND Format = 'FLAC') + OR (Media = 'Soundboard' AND Format = 'FLAC') OR (Media = 'BD' AND Format = 'FLAC') - )"); - list($PerfectCount) = $DB->next_record(); - $Cache->cache_value('stats_perfect_count', $PerfectCount, 3600); // staggered 1 week cache + )"); + list($PerfectCount) = $DB->next_record(); + $Cache->cache_value('stats_perfect_count', $PerfectCount, 3600); // staggered 1 week cache } // Begin request stats if (($RequestStats = $Cache->get_value('stats_requests')) === false) { - $DB->query(" - SELECT COUNT(ID) - FROM requests"); - list($RequestCount) = $DB->next_record(); - $DB->query(" - SELECT COUNT(ID) - FROM requests - WHERE FillerID > 0"); - list($FilledCount) = $DB->next_record(); - $Cache->cache_value('stats_requests', array($RequestCount, $FilledCount), 11280); + $DB->query(" + SELECT COUNT(ID) + FROM requests"); + list($RequestCount) = $DB->next_record(); + $DB->query(" + SELECT COUNT(ID) + FROM requests + WHERE FillerID > 0"); + list($FilledCount) = $DB->next_record(); + $Cache->cache_value('stats_requests', array($RequestCount, $FilledCount), 11280); } else { - list($RequestCount, $FilledCount) = $RequestStats; + list($RequestCount, $FilledCount) = $RequestStats; } - // Begin swarm stats if (($PeerStats = $Cache->get_value('stats_peers')) === false) { - //Cache lock! - if ($Cache->get_query_lock('peer_stats')) { - $DB->query(" - SELECT IF(remaining=0,'Seeding','Leeching') AS Type, COUNT(uid) - FROM xbt_files_users - WHERE active = 1 - GROUP BY Type"); - $PeerCount = $DB->to_array(0, MYSQLI_NUM, false); - $LeecherCount = isset($PeerCount['Leeching']) ? $PeerCount['Leeching'][1] : 0; - $SeederCount = isset($PeerCount['Seeding']) ? $PeerCount['Seeding'][1] : 0; - $Cache->cache_value('stats_peers', array($LeecherCount, $SeederCount), 1209600); // 2 week cache - $Cache->clear_query_lock('peer_stats'); - } else { - $LeecherCount = $SeederCount = 0; - } + //Cache lock! + if ($Cache->get_query_lock('peer_stats')) { + $DB->query(" + SELECT IF(remaining=0,'Seeding','Leeching') AS Type, COUNT(uid) + FROM xbt_files_users + WHERE active = 1 + GROUP BY Type"); + $PeerCount = $DB->to_array(0, MYSQLI_NUM, false); + $LeecherCount = isset($PeerCount['Leeching']) ? $PeerCount['Leeching'][1] : 0; + $SeederCount = isset($PeerCount['Seeding']) ? $PeerCount['Seeding'][1] : 0; + $Cache->cache_value('stats_peers', array($LeecherCount, $SeederCount), 1209600); // 2 week cache + $Cache->clear_query_lock('peer_stats'); + } else { + $LeecherCount = $SeederCount = 0; + } } else { - list($LeecherCount, $SeederCount) = $PeerStats; + list($LeecherCount, $SeederCount) = $PeerStats; } json_print("success", array( - 'maxUsers' => USER_LIMIT, - 'enabledUsers' => (int) $UserCount, - 'usersActiveThisDay' => (int) $UserStats['Day'], - 'usersActiveThisWeek' => (int) $UserStats['Week'], - 'usersActiveThisMonth' => (int) $UserStats['Month'], + 'maxUsers' => USER_LIMIT, + 'enabledUsers' => (int) $UserCount, + 'usersActiveThisDay' => (int) $UserStats['Day'], + 'usersActiveThisWeek' => (int) $UserStats['Week'], + 'usersActiveThisMonth' => (int) $UserStats['Month'], - 'torrentCount' => (int) $TorrentCount, - 'releaseCount' => (int) $AlbumCount, - 'artistCount' => (int) $ArtistCount, - 'perfectFlacCount' => (int) $PerfectCount, + 'torrentCount' => (int) $TorrentCount, + 'releaseCount' => (int) $AlbumCount, + 'artistCount' => (int) $ArtistCount, + 'perfectFlacCount' => (int) $PerfectCount, - 'requestCount' => (int) $RequestCount, - 'filledRequestCount' => (int) $FilledCount, + 'requestCount' => (int) $RequestCount, + 'filledRequestCount' => (int) $FilledCount, - 'seederCount' => (int) $SeederCount, - 'leecherCount' => (int) $LeecherCount + 'seederCount' => (int) $SeederCount, + 'leecherCount' => (int) $LeecherCount )); -?> diff --git a/sections/ajax/stats/torrents.php b/sections/ajax/stats/torrents.php index 7dcd0b710..ec87f3423 100644 --- a/sections/ajax/stats/torrents.php +++ b/sections/ajax/stats/torrents.php @@ -6,42 +6,42 @@ $OutFlow = []; $NetFlow = []; - $DB->prepared_query(" - SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, COUNT(ID) - FROM log - WHERE Message LIKE 'Torrent % was uploaded by %' - GROUP BY Month - ORDER BY Time DESC - LIMIT 1, 12"); - $TimelineIn = array_reverse($DB->to_array(false, MYSQLI_BOTH, false)); - $DB->prepared_query(" - SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, COUNT(ID) - FROM log - WHERE Message LIKE 'Torrent % was deleted %' - GROUP BY Month - ORDER BY Time DESC - LIMIT 1, 12"); - $TimelineOut = array_reverse($DB->to_array(false, MYSQLI_BOTH, false)); - $DB->prepared_query(" - SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, COUNT(ID) - FROM torrents - GROUP BY Month - ORDER BY Time DESC - LIMIT 1, 12"); - $TimelineNet = array_reverse($DB->to_array(false, MYSQLI_BOTH, false)); + $DB->prepared_query(" + SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, COUNT(ID) + FROM log + WHERE Message LIKE 'Torrent % was uploaded by %' + GROUP BY Month + ORDER BY Time DESC + LIMIT 1, 12"); + $TimelineIn = array_reverse($DB->to_array(false, MYSQLI_BOTH, false)); + $DB->prepared_query(" + SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, COUNT(ID) + FROM log + WHERE Message LIKE 'Torrent % was deleted %' + GROUP BY Month + ORDER BY Time DESC + LIMIT 1, 12"); + $TimelineOut = array_reverse($DB->to_array(false, MYSQLI_BOTH, false)); + $DB->prepared_query(" + SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, COUNT(ID) + FROM torrents + GROUP BY Month + ORDER BY Time DESC + LIMIT 1, 12"); + $TimelineNet = array_reverse($DB->to_array(false, MYSQLI_BOTH, false)); - foreach ($TimelineIn as $Month) { - list($Label, $Amount) = $Month; - $Labels[] = $Label; - $InFlow[$Label] = $Amount; - } - foreach ($TimelineOut as $Month) { - list($Label, $Amount) = $Month; - $OutFlow[$Label] = $Amount; - } - foreach ($TimelineNet as $Month) { - list($Label, $Amount) = $Month; - $NetFlow[$Label] = $Amount; + foreach ($TimelineIn as $Month) { + list($Label, $Amount) = $Month; + $Labels[] = $Label; + $InFlow[$Label] = $Amount; + } + foreach ($TimelineOut as $Month) { + list($Label, $Amount) = $Month; + $OutFlow[$Label] = $Amount; + } + foreach ($TimelineNet as $Month) { + list($Label, $Amount) = $Month; + $NetFlow[$Label] = $Amount; } $ByMonth = []; for ($i = 0; $i < count($Labels); $i++) { @@ -52,7 +52,7 @@ 'remaining' => isset($NetFlow[$Label]) ? $NetFlow[$Label] : 0 ]; } - $Cache->cache_value('stats_torrents_upload', $ByMonth, mktime(0, 0, 0, date('n') + 1, 2)); //Tested: fine for dec -> jan + $Cache->cache_value('stats_torrents_upload', $ByMonth, mktime(0, 0, 0, date('n') + 1, 2)); //Tested: fine for dec -> jan } if (!$ByCategory = $Cache->get_value('stats_torrents_category')) { diff --git a/sections/ajax/stats/users.php b/sections/ajax/stats/users.php index 1bcc2a6ae..c7bcd0be3 100644 --- a/sections/ajax/stats/users.php +++ b/sections/ajax/stats/users.php @@ -3,76 +3,76 @@ /* Disabled until we fix Geographical Data if (!list($Countries, $Rank, $CountryUsers, $CountryMax, $CountryMin, $LogIncrements) = $Cache->get_value('geodistribution')) { - include_once(SERVER_ROOT.'/classes/charts.class.php'); - $DB->query(' - SELECT Code, Users - FROM users_geodistribution'); - $Data = $DB->to_array(); - $Count = $DB->record_count() - 1; + include_once(SERVER_ROOT.'/classes/charts.class.php'); + $DB->query(' + SELECT Code, Users + FROM users_geodistribution'); + $Data = $DB->to_array(); + $Count = $DB->record_count() - 1; - if ($Count < 30) { - $CountryMinThreshold = $Count; - } else { - $CountryMinThreshold = 30; - } + if ($Count < 30) { + $CountryMinThreshold = $Count; + } else { + $CountryMinThreshold = 30; + } - $CountryMax = ceil(log(Max(1, $Data[0][1])) / log(2)) + 1; - $CountryMin = floor(log(Max(1, $Data[$CountryMinThreshold][1])) / log(2)); + $CountryMax = ceil(log(Max(1, $Data[0][1])) / log(2)) + 1; + $CountryMin = floor(log(Max(1, $Data[$CountryMinThreshold][1])) / log(2)); - $CountryRegions = array('RS' => array('RS-KM')); // Count Kosovo as Serbia as it doesn't have a TLD - foreach ($Data as $Key => $Item) { - list($Country, $UserCount) = $Item; - $Countries[] = $Country; - $CountryUsers[] = number_format((((log($UserCount) / log(2)) - $CountryMin) / ($CountryMax - $CountryMin)) * 100, 2); - $Rank[] = round((1 - ($Key / $Count)) * 100); + $CountryRegions = array('RS' => array('RS-KM')); // Count Kosovo as Serbia as it doesn't have a TLD + foreach ($Data as $Key => $Item) { + list($Country, $UserCount) = $Item; + $Countries[] = $Country; + $CountryUsers[] = number_format((((log($UserCount) / log(2)) - $CountryMin) / ($CountryMax - $CountryMin)) * 100, 2); + $Rank[] = round((1 - ($Key / $Count)) * 100); - if (isset($CountryRegions[$Country])) { - foreach ($CountryRegions[$Country] as $Region) { - $Countries[] = $Region; - $Rank[] = end($Rank); - } - } - } - reset($Rank); + if (isset($CountryRegions[$Country])) { + foreach ($CountryRegions[$Country] as $Region) { + $Countries[] = $Region; + $Rank[] = end($Rank); + } + } + } + reset($Rank); - for ($i = $CountryMin; $i <= $CountryMax; $i++) { - $LogIncrements[] = Format::human_format(pow(2, $i)); - } - $Cache->cache_value('geodistribution', array($Countries, $Rank, $CountryUsers, $CountryMax, $CountryMin, $LogIncrements), 0); + for ($i = $CountryMin; $i <= $CountryMax; $i++) { + $LogIncrements[] = Format::human_format(pow(2, $i)); + } + $Cache->cache_value('geodistribution', array($Countries, $Rank, $CountryUsers, $CountryMax, $CountryMin, $LogIncrements), 0); } */ if (!$UserClasses = $Cache->get_value('stats_users_classes')) { - $DB->prepared_query(" - SELECT p.Name, COUNT(m.ID) AS Users - FROM users_main AS m - JOIN permissions AS p ON m.PermissionID = p.ID - WHERE m.Enabled = '1' - GROUP BY p.Name - ORDER BY Users DESC"); + $DB->prepared_query(" + SELECT p.Name, COUNT(m.ID) AS Users + FROM users_main AS m + JOIN permissions AS p ON m.PermissionID = p.ID + WHERE m.Enabled = '1' + GROUP BY p.Name + ORDER BY Users DESC"); $UserClasses = $DB->to_pair('Name', 'Users', false); $Cache->cache_value('stats_users_classes', $UserClasses, 3600 * 24 * 14); } if (!$Platforms = $Cache->get_value('stats_users_platforms')) { - $DB->prepared_query(" - SELECT OperatingSystem, COUNT(UserID) AS Users - FROM users_sessions - GROUP BY OperatingSystem - ORDER BY Users DESC"); - $Platforms = $DB->to_pair('OperatingSystem', 'Users'); - $Cache->cache_value('stats_users_platforms', $Platforms, 3600 * 24 * 14); + $DB->prepared_query(" + SELECT OperatingSystem, COUNT(UserID) AS Users + FROM users_sessions + GROUP BY OperatingSystem + ORDER BY Users DESC"); + $Platforms = $DB->to_pair('OperatingSystem', 'Users'); + $Cache->cache_value('stats_users_platforms', $Platforms, 3600 * 24 * 14); } if (!$Browsers = $Cache->get_value('stats_users_browsers')) { - $DB->prepared_query(" - SELECT Browser, COUNT(UserID) AS Users - FROM users_sessions - GROUP BY Browser - ORDER BY Users DESC"); + $DB->prepared_query(" + SELECT Browser, COUNT(UserID) AS Users + FROM users_sessions + GROUP BY Browser + ORDER BY Users DESC"); - $Browsers = $DB->to_pair('Browser', 'Users'); - $Cache->cache_value('stats_users_browsers', $Browsers, 3600 * 24 * 14); + $Browsers = $DB->to_pair('Browser', 'Users'); + $Cache->cache_value('stats_users_browsers', $Browsers, 3600 * 24 * 14); } //Timeline generation @@ -81,29 +81,29 @@ $InFlow = []; $OutFlow = []; $Flow = []; - $DB->prepared_query(" - SELECT DATE_FORMAT(JoinDate,'%b \'%y') AS Month, COUNT(UserID) - FROM users_info - GROUP BY Month - ORDER BY JoinDate DESC - LIMIT 1, 12"); + $DB->prepared_query(" + SELECT DATE_FORMAT(JoinDate,'%b \'%y') AS Month, COUNT(UserID) + FROM users_info + GROUP BY Month + ORDER BY JoinDate DESC + LIMIT 1, 12"); $TimelineIn = array_reverse($DB->to_array(false, MYSQLI_BOTH, false)); - $DB->prepared_query(" - SELECT DATE_FORMAT(BanDate,'%b \'%y') AS Month, COUNT(UserID) - FROM users_info - GROUP BY Month - ORDER BY BanDate DESC - LIMIT 1, 12"); - $TimelineOut = array_reverse($DB->to_array(false, MYSQLI_BOTH, false)); + $DB->prepared_query(" + SELECT DATE_FORMAT(BanDate,'%b \'%y') AS Month, COUNT(UserID) + FROM users_info + GROUP BY Month + ORDER BY BanDate DESC + LIMIT 1, 12"); + $TimelineOut = array_reverse($DB->to_array(false, MYSQLI_BOTH, false)); - foreach ($TimelineIn as $Month) { - list($Label, $Amount) = $Month; - $Labels[] = $Label; - $InFlow[] = $Amount; - } - foreach ($TimelineOut as $Month) { - list($Label, $Amount) = $Month; - $OutFlow[] = $Amount; + foreach ($TimelineIn as $Month) { + list($Label, $Amount) = $Month; + $Labels[] = $Label; + $InFlow[] = $Amount; + } + foreach ($TimelineOut as $Month) { + list($Label, $Amount) = $Month; + $OutFlow[] = $Amount; } for ($i = 0; $i < count($Labels); $i++) { $Flow[$Labels[$i]] = [ @@ -113,7 +113,7 @@ } // Tested: fine for Dec -> Jan - $Cache->cache_value('stats_users_flow', $Flow, mktime(0, 0, 0, date('n') + 1, 2)); + $Cache->cache_value('stats_users_flow', $Flow, mktime(0, 0, 0, date('n') + 1, 2)); } print(json_encode( diff --git a/sections/ajax/subscriptions.php b/sections/ajax/subscriptions.php index c8daeeb58..faa3f963b 100644 --- a/sections/ajax/subscriptions.php +++ b/sections/ajax/subscriptions.php @@ -4,89 +4,89 @@ */ if (!empty($LoggedUser['DisableForums'])) { - json_die('failure'); + json_die('failure'); } if (isset($LoggedUser['PostsPerPage'])) { - $PerPage = $LoggedUser['PostsPerPage']; + $PerPage = $LoggedUser['PostsPerPage']; } else { - $PerPage = POSTS_PER_PAGE; + $PerPage = POSTS_PER_PAGE; } list($Page, $Limit) = Format::page_limit($PerPage); $ShowUnread = (!isset($_GET['showunread']) && !isset($HeavyInfo['SubscriptionsUnread']) || isset($HeavyInfo['SubscriptionsUnread']) && !!$HeavyInfo['SubscriptionsUnread'] || isset($_GET['showunread']) && !!$_GET['showunread']); $ShowCollapsed = (!isset($_GET['collapse']) && !isset($HeavyInfo['SubscriptionsCollapse']) || isset($HeavyInfo['SubscriptionsCollapse']) && !!$HeavyInfo['SubscriptionsCollapse'] || isset($_GET['collapse']) && !!$_GET['collapse']); $sql = ' - SELECT - SQL_CALC_FOUND_ROWS - MAX(p.ID) AS ID - FROM forums_posts AS p - LEFT JOIN forums_topics AS t ON t.ID = p.TopicID - JOIN users_subscriptions AS s ON s.TopicID = t.ID - LEFT JOIN forums AS f ON f.ID = t.ForumID - LEFT JOIN forums_last_read_topics AS l ON p.TopicID = l.TopicID AND l.UserID = s.UserID - WHERE s.UserID = '.$LoggedUser['ID'].' - AND p.ID <= IFNULL(l.PostID, t.LastPostID) - AND ' . Forums::user_forums_sql(); + SELECT + SQL_CALC_FOUND_ROWS + MAX(p.ID) AS ID + FROM forums_posts AS p + LEFT JOIN forums_topics AS t ON t.ID = p.TopicID + JOIN users_subscriptions AS s ON s.TopicID = t.ID + LEFT JOIN forums AS f ON f.ID = t.ForumID + LEFT JOIN forums_last_read_topics AS l ON p.TopicID = l.TopicID AND l.UserID = s.UserID + WHERE s.UserID = '.$LoggedUser['ID'].' + AND p.ID <= IFNULL(l.PostID, t.LastPostID) + AND ' . Forums::user_forums_sql(); if ($ShowUnread) { - $sql .= ' - AND IF(l.PostID IS NULL OR (t.IsLocked = \'1\' && t.IsSticky = \'0\'), t.LastPostID, l.PostID) < t.LastPostID'; + $sql .= ' + AND IF(l.PostID IS NULL OR (t.IsLocked = \'1\' && t.IsSticky = \'0\'), t.LastPostID, l.PostID) < t.LastPostID'; } $sql .= " - GROUP BY t.ID - ORDER BY t.LastPostID DESC - LIMIT $Limit"; + GROUP BY t.ID + ORDER BY t.LastPostID DESC + LIMIT $Limit"; $PostIDs = $DB->query($sql); $DB->query('SELECT FOUND_ROWS()'); list($NumResults) = $DB->next_record(); if ($NumResults > $PerPage * ($Page - 1)) { - $DB->set_query_id($PostIDs); - $PostIDs = $DB->collect('ID'); - $sql = ' - SELECT - f.ID AS ForumID, - f.Name AS ForumName, - p.TopicID, - t.Title, - p.Body, - t.LastPostID, - t.IsLocked, - t.IsSticky, - p.ID, - um.ID, - um.Username, - ui.Avatar, - p.EditedUserID, - p.EditedTime, - ed.Username AS EditedUsername - FROM forums_posts AS p - LEFT JOIN forums_topics AS t ON t.ID = p.TopicID - LEFT JOIN forums AS f ON f.ID = t.ForumID - LEFT JOIN users_main AS um ON um.ID = p.AuthorID - LEFT JOIN users_info AS ui ON ui.UserID = um.ID - LEFT JOIN users_main AS ed ON ed.ID = um.ID - WHERE p.ID IN ('.implode(',', $PostIDs).') - ORDER BY f.Name ASC, t.LastPostID DESC'; - $DB->query($sql); + $DB->set_query_id($PostIDs); + $PostIDs = $DB->collect('ID'); + $sql = ' + SELECT + f.ID AS ForumID, + f.Name AS ForumName, + p.TopicID, + t.Title, + p.Body, + t.LastPostID, + t.IsLocked, + t.IsSticky, + p.ID, + um.ID, + um.Username, + ui.Avatar, + p.EditedUserID, + p.EditedTime, + ed.Username AS EditedUsername + FROM forums_posts AS p + LEFT JOIN forums_topics AS t ON t.ID = p.TopicID + LEFT JOIN forums AS f ON f.ID = t.ForumID + LEFT JOIN users_main AS um ON um.ID = p.AuthorID + LEFT JOIN users_info AS ui ON ui.UserID = um.ID + LEFT JOIN users_main AS ed ON ed.ID = um.ID + WHERE p.ID IN ('.implode(',', $PostIDs).') + ORDER BY f.Name ASC, t.LastPostID DESC'; + $DB->query($sql); } -$JsonPosts = array(); +$JsonPosts = []; while (list($ForumID, $ForumName, $TopicID, $ThreadTitle, $Body, $LastPostID, $Locked, $Sticky, $PostID, $AuthorID, $AuthorName, $AuthorAvatar, $EditedUserID, $EditedTime, $EditedUsername) = $DB->next_record()) { - $JsonPost = array( - 'forumId' => (int)$ForumID, - 'forumName' => $ForumName, - 'threadId' => (int)$TopicID, - 'threadTitle' => $ThreadTitle, - 'postId' => (int)$PostID, - 'lastPostId' => (int)$LastPostID, - 'locked' => $Locked == 1, - 'new' => ($PostID < $LastPostID && !$Locked) - ); - $JsonPosts[] = $JsonPost; + $JsonPost = array( + 'forumId' => (int)$ForumID, + 'forumName' => $ForumName, + 'threadId' => (int)$TopicID, + 'threadTitle' => $ThreadTitle, + 'postId' => (int)$PostID, + 'lastPostId' => (int)$LastPostID, + 'locked' => $Locked == 1, + 'new' => ($PostID < $LastPostID && !$Locked) + ); + $JsonPosts[] = $JsonPost; } json_print('success', array( - 'threads' => $JsonPosts + 'threads' => $JsonPosts )); ?> diff --git a/sections/ajax/takevote.php b/sections/ajax/takevote.php index 5ac3949f2..b63d59095 100644 --- a/sections/ajax/takevote.php +++ b/sections/ajax/takevote.php @@ -1,10 +1,10 @@ -query(" - INSERT IGNORE INTO users_votes (UserID, GroupID, Type) - VALUES ($UserID, $GroupID, '$Type')"); - if ($DB->affected_rows() == 0) { - echo 'noaction'; - die(); - } - - // Update the group's cache key - $GroupVotes['Total'] += 1; - if ($Type == 'Up') { - $GroupVotes['Ups'] += 1; - } - $Cache->cache_value("votes_$GroupID", $GroupVotes); - - // If the group has no votes yet, we need an insert, otherwise an update - // so we can cut corners and use the magic of INSERT...ON DUPLICATE KEY UPDATE... - // to accomplish both in one query - $DB->query(" - INSERT INTO torrents_votes - (GroupID, Total, Ups, Score) - VALUES - ($GroupID, 1, ".($Type == 'Up' ? 1 : 0).", 0) - ON DUPLICATE KEY UPDATE - Total = Total + 1, - Score = IFNULL(binomial_ci(Ups".($Type == 'Up' ? '+1' : '').", Total), 0)". - ($Type == 'Up' ? ', Ups = Ups + 1' : '')); - - $UserVotes[$GroupID] = array('GroupID' => $GroupID, 'Type' => $Type); - - // Update this guy's cache key - $Cache->cache_value('voted_albums_'.$LoggedUser['ID'], $UserVotes); - - // Update the paired cache keys for "people who liked" - // First update this album's paired votes. If this keys is magically not set, - // our life just got a bit easier. We're only tracking paired votes on upvotes. - if ($Type == 'Up') { - $VotePairs = $Cache->get_value("vote_pairs_$GroupID", true); - if ($VotePairs !== false) { - foreach ($UserVotes as $Vote) { - if ($Vote['GroupID'] == $GroupID) { - continue; - } - // Go through each of his other votes, incrementing the - // corresponding keys in this groups vote_pairs array - if (isset($VotePairs[$Vote['GroupID']])) { - $VotePairs[$Vote['GroupID']]['Total'] += 1; - if ($Vote['Type'] == 'Up') { - $VotePairs[$Vote['GroupID']]['Ups'] += 1; - } - } else { - $VotePairs[$Vote['GroupID']] = array( - 'GroupID' => $Vote['GroupID'], - 'Total' => 1, - 'Ups' => ($Type == 'Up') ? 1 : 0); - } - } - } - $Cache->cache_value("vote_pairs_$GroupID", $VotePairs, 21600); - } - - // Now do the paired votes keys for all of this guy's other votes - foreach ($UserVotes as $VGID => $Vote) { - if ($Vote['Type'] != 'Up') { - // We're only track paired votes on upvotes - continue; - } - if ($VGID == $GroupID) { - continue; - } - // Again, if the cache key is not set, move along - $VotePairs = $Cache->get_value("vote_pairs_$VGID", true); - if ($VotePairs !== false) { - // Go through all of the other albums paired to this one, and update - // this group's entry in their vote_pairs keys - if (isset($VotePairs[$GroupID])) { - $VotePairs[$GroupID]['Total']++; - if ($Type == 'Up') { - $VotePairs[$GroupID]['Ups']++; - } - } else { - $VotePairs[$GroupID] = array( - 'GroupID' => $GroupID, - 'Total' => 1, - 'Ups' => ($Type == 'Up') ? 1 : 0); - } - $Cache->cache_value("vote_pairs_$VGID", $VotePairs, 21600); - } - } - - echo 'success'; + if (isset($UserVotes[$GroupID]) || !check_perms('site_album_votes')) { + echo 'noaction'; + die(); + } + if ($_REQUEST['vote'] != 'up' && $_REQUEST['vote'] != 'down') { + echo 'badvote'; + die(); + } + $Type = ($_REQUEST['vote'] == 'up') ? 'Up' : 'Down'; + + // Update the two votes tables if needed + $DB->query(" + INSERT IGNORE INTO users_votes (UserID, GroupID, Type) + VALUES ($UserID, $GroupID, '$Type')"); + if ($DB->affected_rows() == 0) { + echo 'noaction'; + die(); + } + + // Update the group's cache key + $GroupVotes['Total'] += 1; + if ($Type == 'Up') { + $GroupVotes['Ups'] += 1; + } + $Cache->cache_value("votes_$GroupID", $GroupVotes); + + // If the group has no votes yet, we need an insert, otherwise an update + // so we can cut corners and use the magic of INSERT...ON DUPLICATE KEY UPDATE... + // to accomplish both in one query + $DB->query(" + INSERT INTO torrents_votes + (GroupID, Total, Ups, Score) + VALUES + ($GroupID, 1, ".($Type == 'Up' ? 1 : 0).", 0) + ON DUPLICATE KEY UPDATE + Total = Total + 1, + Score = IFNULL(binomial_ci(Ups".($Type == 'Up' ? '+1' : '').", Total), 0)". + ($Type == 'Up' ? ', Ups = Ups + 1' : '')); + + $UserVotes[$GroupID] = array('GroupID' => $GroupID, 'Type' => $Type); + + // Update this guy's cache key + $Cache->cache_value('voted_albums_'.$LoggedUser['ID'], $UserVotes); + + // Update the paired cache keys for "people who liked" + // First update this album's paired votes. If this keys is magically not set, + // our life just got a bit easier. We're only tracking paired votes on upvotes. + if ($Type == 'Up') { + $VotePairs = $Cache->get_value("vote_pairs_$GroupID", true); + if ($VotePairs !== false) { + foreach ($UserVotes as $Vote) { + if ($Vote['GroupID'] == $GroupID) { + continue; + } + // Go through each of his other votes, incrementing the + // corresponding keys in this groups vote_pairs array + if (isset($VotePairs[$Vote['GroupID']])) { + $VotePairs[$Vote['GroupID']]['Total'] += 1; + if ($Vote['Type'] == 'Up') { + $VotePairs[$Vote['GroupID']]['Ups'] += 1; + } + } else { + $VotePairs[$Vote['GroupID']] = array( + 'GroupID' => $Vote['GroupID'], + 'Total' => 1, + 'Ups' => ($Type == 'Up') ? 1 : 0); + } + } + } + $Cache->cache_value("vote_pairs_$GroupID", $VotePairs, 21600); + } + + // Now do the paired votes keys for all of this guy's other votes + foreach ($UserVotes as $VGID => $Vote) { + if ($Vote['Type'] != 'Up') { + // We're only track paired votes on upvotes + continue; + } + if ($VGID == $GroupID) { + continue; + } + // Again, if the cache key is not set, move along + $VotePairs = $Cache->get_value("vote_pairs_$VGID", true); + if ($VotePairs !== false) { + // Go through all of the other albums paired to this one, and update + // this group's entry in their vote_pairs keys + if (isset($VotePairs[$GroupID])) { + $VotePairs[$GroupID]['Total']++; + if ($Type == 'Up') { + $VotePairs[$GroupID]['Ups']++; + } + } else { + $VotePairs[$GroupID] = array( + 'GroupID' => $GroupID, + 'Total' => 1, + 'Ups' => ($Type == 'Up') ? 1 : 0); + } + $Cache->cache_value("vote_pairs_$VGID", $VotePairs, 21600); + } + } + + echo 'success'; } elseif ($_REQUEST['do'] == 'unvote') { - if (!isset($UserVotes[$GroupID])) { - echo 'noaction'; - die(); - } - $Type = $UserVotes[$GroupID]['Type']; - - $DB->query(" - DELETE FROM users_votes - WHERE UserID = $UserID - AND GroupID = $GroupID"); - - // Update personal cache key - unset($UserVotes[$GroupID]); - $Cache->cache_value('voted_albums_'.$LoggedUser['ID'], $UserVotes); - - // Update the group's cache key - $GroupVotes['Total'] -= 1; - if ($Type == 'Up') { - $GroupVotes['Ups'] -= 1; - } - $Cache->cache_value("votes_$GroupID", $GroupVotes); - - $DB->query(' - UPDATE torrents_votes - SET - Total = GREATEST(0, Total - 1), - Score = IFNULL(binomial_ci(GREATEST(0, Ups'.($Type == 'Up' ? '-1' : '').'), GREATEST(0, Total)), 0)'. - ($Type == 'Up' ? ', Ups = GREATEST(0, Ups - 1)' : '')." - WHERE GroupID=$GroupID"); - // Update paired cache keys - // First update this album's paired votes. If this keys is magically not set, - // our life just got a bit easier. We're only tracking paired votes on upvotes. - if ($Type == 'Up') { - $VotePairs = $Cache->get_value("vote_pairs_$GroupID", true); - if ($VotePairs !== false) { - foreach ($UserVotes as $Vote) { - if (isset($VotePairs[$Vote['GroupID']])) { - if ($VotePairs[$Vote['GroupID']]['Total'] == 0) { - // Something is screwy - $Cache->delete_value("vote_pairs_$GroupID"); - continue; - } - $VotePairs[$Vote['GroupID']]['Total'] -= 1; - if ($Vote['Type'] == 'Up') { - $VotePairs[$Vote['GroupID']]['Ups'] -= 1; - } - } else { - // Something is screwy, kill the key and move on - $Cache->delete_value("vote_pairs_$GroupID"); - break; - } - } - } - $Cache->cache_value("vote_pairs_$GroupID", $VotePairs, 21600); - } - - // Now do the paired votes keys for all of this guy's other votes - foreach ($UserVotes as $VGID => $Vote) { - if ($Vote['Type'] != 'Up') { - // We're only track paired votes on upvotes - continue; - } - if ($VGID == $GroupID) { - continue; - } - // Again, if the cache key is not set, move along - $VotePairs = $Cache->get_value("vote_pairs_$VGID", true); - if ($VotePairs !== false) { - if (isset($VotePairs[$GroupID])) { - if ($VotePairs[$GroupID]['Total'] == 0) { - // Something is screwy - $Cache->delete_value("vote_pairs_$VGID"); - continue; - } - $VotePairs[$GroupID]['Total'] -= 1; - if ($Type == 'Up') { - $VotePairs[$GroupID]['Ups'] -= 1; - } - $Cache->cache_value("vote_pairs_$VGID", $VotePairs, 21600); - } else { - // Something is screwy, kill the key and move on - $Cache->delete_value("vote_pairs_$VGID"); - } - } - } - - // Let the script know what happened - if ($Type == 'Up') { - echo 'success-up'; - } else { - echo 'success-down'; - } + if (!isset($UserVotes[$GroupID])) { + echo 'noaction'; + die(); + } + $Type = $UserVotes[$GroupID]['Type']; + + $DB->query(" + DELETE FROM users_votes + WHERE UserID = $UserID + AND GroupID = $GroupID"); + + // Update personal cache key + unset($UserVotes[$GroupID]); + $Cache->cache_value('voted_albums_'.$LoggedUser['ID'], $UserVotes); + + // Update the group's cache key + $GroupVotes['Total'] -= 1; + if ($Type == 'Up') { + $GroupVotes['Ups'] -= 1; + } + $Cache->cache_value("votes_$GroupID", $GroupVotes); + + $DB->query(' + UPDATE torrents_votes + SET + Total = GREATEST(0, Total - 1), + Score = IFNULL(binomial_ci(GREATEST(0, Ups'.($Type == 'Up' ? '-1' : '').'), GREATEST(0, Total)), 0)'. + ($Type == 'Up' ? ', Ups = GREATEST(0, Ups - 1)' : '')." + WHERE GroupID=$GroupID"); + // Update paired cache keys + // First update this album's paired votes. If this keys is magically not set, + // our life just got a bit easier. We're only tracking paired votes on upvotes. + if ($Type == 'Up') { + $VotePairs = $Cache->get_value("vote_pairs_$GroupID", true); + if ($VotePairs !== false) { + foreach ($UserVotes as $Vote) { + if (isset($VotePairs[$Vote['GroupID']])) { + if ($VotePairs[$Vote['GroupID']]['Total'] == 0) { + // Something is screwy + $Cache->delete_value("vote_pairs_$GroupID"); + continue; + } + $VotePairs[$Vote['GroupID']]['Total'] -= 1; + if ($Vote['Type'] == 'Up') { + $VotePairs[$Vote['GroupID']]['Ups'] -= 1; + } + } else { + // Something is screwy, kill the key and move on + $Cache->delete_value("vote_pairs_$GroupID"); + break; + } + } + } + $Cache->cache_value("vote_pairs_$GroupID", $VotePairs, 21600); + } + + // Now do the paired votes keys for all of this guy's other votes + foreach ($UserVotes as $VGID => $Vote) { + if ($Vote['Type'] != 'Up') { + // We're only track paired votes on upvotes + continue; + } + if ($VGID == $GroupID) { + continue; + } + // Again, if the cache key is not set, move along + $VotePairs = $Cache->get_value("vote_pairs_$VGID", true); + if ($VotePairs !== false) { + if (isset($VotePairs[$GroupID])) { + if ($VotePairs[$GroupID]['Total'] == 0) { + // Something is screwy + $Cache->delete_value("vote_pairs_$VGID"); + continue; + } + $VotePairs[$GroupID]['Total'] -= 1; + if ($Type == 'Up') { + $VotePairs[$GroupID]['Ups'] -= 1; + } + $Cache->cache_value("vote_pairs_$VGID", $VotePairs, 21600); + } else { + // Something is screwy, kill the key and move on + $Cache->delete_value("vote_pairs_$VGID"); + } + } + } + + // Let the script know what happened + if ($Type == 'Up') { + echo 'success-up'; + } else { + echo 'success-down'; + } } ?> diff --git a/sections/ajax/tcomments.php b/sections/ajax/tcomments.php index 9dc513f74..66e27f60c 100644 --- a/sections/ajax/tcomments.php +++ b/sections/ajax/tcomments.php @@ -1,38 +1,38 @@ - $Post) { - list($PostID, $AuthorID, $AddedTime, $Body, $EditedUserID, $EditedTime, $EditedUsername) = array_values($Post); - list($AuthorID, $Username, $PermissionID, $Paranoia, $Artist, $Donor, $Warned, $Avatar, $Enabled, $UserTitle) = array_values(Users::user_info($AuthorID)); - $JsonComments[] = array( - 'postId' => (int)$PostID, - 'addedTime' => $AddedTime, - 'bbBody' => $Body, - 'body' => Text::full_format($Body), - 'editedUserId' => (int)$EditedUserID, - 'editedTime' => $EditedTime, - 'editedUsername' => $EditedUsername, - 'userinfo' => array( - 'authorId' => (int)$AuthorID, - 'authorName' => $Username, - 'artist' => $Artist == 1, - 'donor' => $Donor == 1, - 'warned' => ($Warned != '0000-00-00 00:00:00'), - 'avatar' => $Avatar, - 'enabled' => ($Enabled == 2 ? false : true), - 'userTitle' => $UserTitle - ) - ); + list($PostID, $AuthorID, $AddedTime, $Body, $EditedUserID, $EditedTime, $EditedUsername) = array_values($Post); + list($AuthorID, $Username, $PermissionID, $Paranoia, $Artist, $Donor, $Warned, $Avatar, $Enabled, $UserTitle) = array_values(Users::user_info($AuthorID)); + $JsonComments[] = array( + 'postId' => (int)$PostID, + 'addedTime' => $AddedTime, + 'bbBody' => $Body, + 'body' => Text::full_format($Body), + 'editedUserId' => (int)$EditedUserID, + 'editedTime' => $EditedTime, + 'editedUsername' => $EditedUsername, + 'userinfo' => array( + 'authorId' => (int)$AuthorID, + 'authorName' => $Username, + 'artist' => $Artist == 1, + 'donor' => $Donor == 1, + 'warned' => ($Warned != '0000-00-00 00:00:00'), + 'avatar' => $Avatar, + 'enabled' => ($Enabled == 2 ? false : true), + 'userTitle' => $UserTitle + ) + ); } json_print("success", array( - 'page' => (int)$Page, - 'pages' => ceil($NumComments / TORRENT_COMMENTS_PER_PAGE), - 'comments' => $JsonComments + 'page' => (int)$Page, + 'pages' => ceil($NumComments / TORRENT_COMMENTS_PER_PAGE), + 'comments' => $JsonComments )); diff --git a/sections/ajax/top10/index.php b/sections/ajax/top10/index.php index b3d238ef6..5c1ef3ec7 100644 --- a/sections/ajax/top10/index.php +++ b/sections/ajax/top10/index.php @@ -1,29 +1,29 @@ - 'failure')); - die(); + print json_encode(array('status' => 'failure')); + die(); } if (empty($_GET['type']) || $_GET['type'] == 'torrents') { - include(SERVER_ROOT.'/sections/ajax/top10/torrents.php'); + include(SERVER_ROOT.'/sections/ajax/top10/torrents.php'); } else { - switch ($_GET['type']) { - case 'users': - include(SERVER_ROOT.'/sections/ajax/top10/users.php'); - break; - case 'tags': - include(SERVER_ROOT.'/sections/ajax/top10/tags.php'); - break; - case 'history': - include(SERVER_ROOT.'/sections/ajax/top10/history.php'); - break; - default: - print json_encode(array('status' => 'failure')); - break; - } + switch ($_GET['type']) { + case 'users': + include(SERVER_ROOT.'/sections/ajax/top10/users.php'); + break; + case 'tags': + include(SERVER_ROOT.'/sections/ajax/top10/tags.php'); + break; + case 'history': + include(SERVER_ROOT.'/sections/ajax/top10/history.php'); + break; + default: + print json_encode(array('status' => 'failure')); + break; + } } ?> diff --git a/sections/ajax/top10/tags.php b/sections/ajax/top10/tags.php index 0c1559d13..c35aa2d01 100644 --- a/sections/ajax/top10/tags.php +++ b/sections/ajax/top10/tags.php @@ -1,108 +1,108 @@ - 'failure')); - die(); - } + if (in_array($_GET['details'],array('ut','ur','v'))) { + $Details = $_GET['details']; + } else { + print json_encode(array('status' => 'failure')); + die(); + } } else { - $Details = 'all'; + $Details = 'all'; } // defaults to 10 (duh) $Limit = isset($_GET['limit']) ? intval($_GET['limit']) : 10; $Limit = in_array($Limit, array(10, 100, 250)) ? $Limit : 10; -$OuterResults = array(); +$OuterResults = []; if ($Details == 'all' || $Details == 'ut') { - if (!$TopUsedTags = $Cache->get_value("topusedtag_$Limit")) { - $DB->query(" - SELECT - t.ID, - t.Name, - COUNT(tt.GroupID) AS Uses, - SUM(tt.PositiveVotes - 1) AS PosVotes, - SUM(tt.NegativeVotes - 1) AS NegVotes - FROM tags AS t - JOIN torrents_tags AS tt ON tt.TagID = t.ID - GROUP BY tt.TagID - ORDER BY Uses DESC - LIMIT $Limit"); - $TopUsedTags = $DB->to_array(); - $Cache->cache_value("topusedtag_$Limit", $TopUsedTags, 3600 * 12); - } + if (!$TopUsedTags = $Cache->get_value("topusedtag_$Limit")) { + $DB->query(" + SELECT + t.ID, + t.Name, + COUNT(tt.GroupID) AS Uses, + SUM(tt.PositiveVotes - 1) AS PosVotes, + SUM(tt.NegativeVotes - 1) AS NegVotes + FROM tags AS t + JOIN torrents_tags AS tt ON tt.TagID = t.ID + GROUP BY tt.TagID + ORDER BY Uses DESC + LIMIT $Limit"); + $TopUsedTags = $DB->to_array(); + $Cache->cache_value("topusedtag_$Limit", $TopUsedTags, 3600 * 12); + } - $OuterResults[] = generate_tag_json('Most Used Torrent Tags', 'ut', $TopUsedTags, $Limit); + $OuterResults[] = generate_tag_json('Most Used Torrent Tags', 'ut', $TopUsedTags, $Limit); } if ($Details == 'all' || $Details == 'ur') { - if (!$TopRequestTags = $Cache->get_value("toprequesttag_$Limit")) { - $DB->query(" - SELECT - t.ID, - t.Name, - COUNT(r.RequestID) AS Uses, - '','' - FROM tags AS t - JOIN requests_tags AS r ON r.TagID = t.ID - GROUP BY r.TagID - ORDER BY Uses DESC - LIMIT $Limit"); - $TopRequestTags = $DB->to_array(); - $Cache->cache_value("toprequesttag_$Limit", $TopRequestTags, 3600 * 12); - } + if (!$TopRequestTags = $Cache->get_value("toprequesttag_$Limit")) { + $DB->query(" + SELECT + t.ID, + t.Name, + COUNT(r.RequestID) AS Uses, + '','' + FROM tags AS t + JOIN requests_tags AS r ON r.TagID = t.ID + GROUP BY r.TagID + ORDER BY Uses DESC + LIMIT $Limit"); + $TopRequestTags = $DB->to_array(); + $Cache->cache_value("toprequesttag_$Limit", $TopRequestTags, 3600 * 12); + } - $OuterResults[] = generate_tag_json('Most Used Request Tags', 'ur', $TopRequestTags, $Limit); + $OuterResults[] = generate_tag_json('Most Used Request Tags', 'ur', $TopRequestTags, $Limit); } if ($Details == 'all' || $Details == 'v') { - if (!$TopVotedTags = $Cache->get_value("topvotedtag_$Limit")) { - $DB->query(" - SELECT - t.ID, - t.Name, - COUNT(tt.GroupID) AS Uses, - SUM(tt.PositiveVotes - 1) AS PosVotes, - SUM(tt.NegativeVotes - 1) AS NegVotes - FROM tags AS t - JOIN torrents_tags AS tt ON tt.TagID = t.ID - GROUP BY tt.TagID - ORDER BY PosVotes DESC - LIMIT $Limit"); - $TopVotedTags = $DB->to_array(); - $Cache->cache_value("topvotedtag_$Limit", $TopVotedTags, 3600 * 12); - } + if (!$TopVotedTags = $Cache->get_value("topvotedtag_$Limit")) { + $DB->query(" + SELECT + t.ID, + t.Name, + COUNT(tt.GroupID) AS Uses, + SUM(tt.PositiveVotes - 1) AS PosVotes, + SUM(tt.NegativeVotes - 1) AS NegVotes + FROM tags AS t + JOIN torrents_tags AS tt ON tt.TagID = t.ID + GROUP BY tt.TagID + ORDER BY PosVotes DESC + LIMIT $Limit"); + $TopVotedTags = $DB->to_array(); + $Cache->cache_value("topvotedtag_$Limit", $TopVotedTags, 3600 * 12); + } - $OuterResults[] = generate_tag_json('Most Highly Voted Tags', 'v', $TopVotedTags, $Limit); + $OuterResults[] = generate_tag_json('Most Highly Voted Tags', 'v', $TopVotedTags, $Limit); } print - json_encode( - array( - 'status' => 'success', - 'response' => $OuterResults - ) - ); + json_encode( + array( + 'status' => 'success', + 'response' => $OuterResults + ) + ); function generate_tag_json($Caption, $Tag, $Details, $Limit) { - $results = array(); - foreach ($Details as $Detail) { - $results[] = array( - 'name' => $Detail['Name'], - 'uses' => (int)$Detail['Uses'], - 'posVotes' => (int)$Detail['PosVotes'], - 'negVotes' => (int)$Detail['NegVotes'] - ); - } + $results = []; + foreach ($Details as $Detail) { + $results[] = array( + 'name' => $Detail['Name'], + 'uses' => (int)$Detail['Uses'], + 'posVotes' => (int)$Detail['PosVotes'], + 'negVotes' => (int)$Detail['NegVotes'] + ); + } - return array( - 'caption' => $Caption, - 'tag' => $Tag, - 'limit' => (int)$Limit, - 'results' => $results - ); + return array( + 'caption' => $Caption, + 'tag' => $Tag, + 'limit' => (int)$Limit, + 'results' => $results + ); } diff --git a/sections/ajax/top10/torrents.php b/sections/ajax/top10/torrents.php index 9b2b5ca08..4bf7f3fca 100644 --- a/sections/ajax/top10/torrents.php +++ b/sections/ajax/top10/torrents.php @@ -1,208 +1,116 @@ - 'failure')); - die(); - } -} else { - $Details = 'all'; +getTopTorrents($_GET, 'day', $limit); + $OuterResults[] = generate_torrent_json('Most Active Torrents Uploaded in the Past Day', 'day', $topTorrentsActiveLastDay, $limit); } -// defaults to 10 (duh) -$Limit = isset($_GET['limit']) ? intval($_GET['limit']) : 10; -$Limit = in_array($Limit, array(10, 100, 250)) ? $Limit : 10; - -$WhereSum = (empty($Where)) ? '' : md5($Where); -$BaseQuery = "SELECT - t.ID, - g.ID, - g.Name, - g.CategoryID, - g.wikiImage, - g.TagList, - t.Format, - t.Encoding, - t.Media, - t.Scene, - t.HasLog, - t.HasCue, - t.HasLogDB, - t.LogScore, - t.LogChecksum, - t.RemasterYear, - g.Year, - t.RemasterTitle, - t.Snatched, - t.Seeders, - t.Leechers, - ((t.Size * t.Snatched) + (t.Size * 0.5 * t.Leechers)) AS Data, - g.ReleaseType, - t.Size - FROM torrents AS t - LEFT JOIN torrents_group AS g ON g.ID = t.GroupID "; - -$OuterResults = array(); - -if ($Details == 'all' || $Details == 'day') { - if (!$TopTorrentsActiveLastDay = $Cache->get_value('top10tor_day_'.$Limit.$WhereSum)) { - $DayAgo = time_minus(86400); - $Query = $BaseQuery.' WHERE t.Seeders>0 AND '; - if (!empty($Where)) { $Query .= $Where.' AND '; } - $Query .= " - t.Time>'$DayAgo' - ORDER BY (t.Seeders + t.Leechers) DESC - LIMIT $Limit;"; - $DB->query($Query); - $TopTorrentsActiveLastDay = $DB->to_array(); // TODO: MYSQLI_NUM to avoid duplicate data in the cache (does that break something with generate_torrent_json?) - $Cache->cache_value('top10tor_day_'.$Limit.$WhereSum,$TopTorrentsActiveLastDay,3600*2); - } - $OuterResults[] = generate_torrent_json('Most Active Torrents Uploaded in the Past Day', 'day', $TopTorrentsActiveLastDay, $Limit); +if ($details == 'all' || $details == 'week') { + $topTorrentsActiveLastWeek = $torrent->getTopTorrents($_GET, 'week', $limit); + $OuterResults[] = generate_torrent_json('Most Active Torrents Uploaded in the Past Week', 'week', $topTorrentsActiveLastWeek, $limit); } -if ($Details == 'all' || $Details == 'week') { - if (!$TopTorrentsActiveLastWeek = $Cache->get_value('top10tor_week_'.$Limit.$WhereSum)) { - $WeekAgo = time_minus(604800); - $Query = $BaseQuery.' WHERE '; - if (!empty($Where)) { $Query .= $Where.' AND '; } - $Query .= " - t.Time>'$WeekAgo' - ORDER BY (t.Seeders + t.Leechers) DESC - LIMIT $Limit;"; - $DB->query($Query); - $TopTorrentsActiveLastWeek = $DB->to_array(); - $Cache->cache_value('top10tor_week_'.$Limit.$WhereSum,$TopTorrentsActiveLastWeek,3600*6); - } - $OuterResults[] = generate_torrent_json('Most Active Torrents Uploaded in the Past Week', 'week', $TopTorrentsActiveLastWeek, $Limit); + +if ($details == 'all' || $details == 'month') { + $topTorrentsActiveLastMonth = $torrent->getTopTorrents($_GET, 'month', $limit); + $OuterResults[] = generate_torrent_json('Most Active Torrents Uploaded in the Past Week', 'week', $topTorrentsActiveLastMonth, $limit); +} + +if ($details == 'all' || $details == 'year') { + $topTorrentsActiveLastYear = $torrent->getTopTorrents($_GET, 'year', $limit); + $OuterResults[] = generate_torrent_json('Most Active Torrents Uploaded in the Past Week', 'week', $topTorrentsActiveLastYear, $limit); } -if ($Details == 'all' || $Details == 'overall') { - if (!$TopTorrentsActiveAllTime = $Cache->get_value("top10tor_overall_$Limit$WhereSum")) { - // IMPORTANT NOTE - we use WHERE t.Seeders>500 in order to speed up this query. You should remove it! - $Query = $BaseQuery; - if (!empty($Where)) { - $Query .= " WHERE $Where"; - } elseif ($Details == 'all') { - $Query .= " WHERE t.Seeders > 500 "; - } - $Query .= " - ORDER BY (t.Seeders + t.Leechers) DESC - LIMIT $Limit;"; - $DB->query($Query); - $TopTorrentsActiveAllTime = $DB->to_array(); - $Cache->cache_value("top10tor_overall_$Limit$WhereSum", $TopTorrentsActiveAllTime, 3600 * 6); - } - $OuterResults[] = generate_torrent_json('Most Active Torrents of All Time', 'overall', $TopTorrentsActiveAllTime, $Limit); +if ($details == 'all' || $details == 'overall') { + $topTorrentsActiveAllTime = $torrent->getTopTorrents($_GET, 'overall', $limit); + $OuterResults[] = generate_torrent_json('Most Active Torrents of All Time', 'overall', $topTorrentsActiveAllTime, $limit); } -if (($Details == 'all' || $Details == 'snatched') && empty($Where)) { - if (!$TopTorrentsSnatched = $Cache->get_value("top10tor_snatched_$Limit$WhereSum")) { - $Query = $BaseQuery; - $Query .= " - ORDER BY t.Snatched DESC - LIMIT $Limit;"; - $DB->query($Query); - $TopTorrentsSnatched = $DB->to_array(); - $Cache->cache_value("top10tor_snatched_$Limit$WhereSum", $TopTorrentsSnatched, 3600 * 6); - } - $OuterResults[] = generate_torrent_json('Most Snatched Torrents', 'snatched', $TopTorrentsSnatched, $Limit); +if (($details == 'all' || $details == 'snatched')) { + $topTorrentsSnatched = $torrent->getTopTorrents($_GET, 'snatched', $limit); + $OuterResults[] = generate_torrent_json('Most Snatched Torrents', 'snatched', $topTorrentsSnatched, $limit); } -if (($Details == 'all' || $Details == 'data') && empty($Where)) { - if (!$TopTorrentsTransferred = $Cache->get_value("top10tor_data_$Limit$WhereSum")) { - // IMPORTANT NOTE - we use WHERE t.Snatched>100 in order to speed up this query. You should remove it! - $Query = $BaseQuery; - if ($Details == 'all') { - $Query .= " WHERE t.Snatched > 100 "; - } - $Query .= " - ORDER BY Data DESC - LIMIT $Limit;"; - $DB->query($Query); - $TopTorrentsTransferred = $DB->to_array(); - $Cache->cache_value("top10tor_data_$Limit$WhereSum", $TopTorrentsTransferred, 3600 * 6); - } - $OuterResults[] = generate_torrent_json('Most Data Transferred Torrents', 'data', $TopTorrentsTransferred, $Limit); +if (($details == 'all' || $details == 'data')) { + $topTorrentsTransferred = $torrent->getTopTorrents($_GET, 'data', $limit); + $OuterResults[] = generate_torrent_json('Most Data Transferred Torrents', 'data', $topTorrentsTransferred, $limit); } -if (($Details == 'all' || $Details == 'seeded') && empty($Where)) { - if (!$TopTorrentsSeeded = $Cache->get_value("top10tor_seeded_$Limit$WhereSum")) { - $Query = $BaseQuery." - ORDER BY t.Seeders DESC - LIMIT $Limit;"; - $DB->query($Query); - $TopTorrentsSeeded = $DB->to_array(); - $Cache->cache_value("top10tor_seeded_$Limit$WhereSum", $TopTorrentsSeeded, 3600 * 6); - } - $OuterResults[] = generate_torrent_json('Best Seeded Torrents', 'seeded', $TopTorrentsSeeded, $Limit); +if (($details == 'all' || $details == 'seeded')) { + $topTorrentsSeeded = $torrent->getTopTorrents($_GET, 'seeded', $limit); + $OuterResults[] = generate_torrent_json('Best Seeded Torrents', 'seeded', $topTorrentsSeeded, $limit); } print - json_encode( - array( - 'status' => 'success', - 'response' => $OuterResults - ) - ); - - -function generate_torrent_json($Caption, $Tag, $Details, $Limit) { - global $LoggedUser, $Categories; - $results = array(); - foreach ($Details as $Detail) { - list($TorrentID, $GroupID, $GroupName, $GroupCategoryID, $WikiImage, $TorrentTags, - $Format, $Encoding, $Media, $Scene, $HasLog, $HasCue, $HasLogDB, $LogScore, $LogChecksum, $Year, $GroupYear, - $RemasterTitle, $Snatched, $Seeders, $Leechers, $Data, $ReleaseType, $Size) = $Detail; - - $Artist = Artists::display_artists(Artists::get_artist($GroupID), false, true); - $TruncArtist = substr($Artist, 0, strlen($Artist) - 3); - - $TagList = array(); - - if ($TorrentTags != '') { - $TorrentTags = explode(' ', $TorrentTags); - foreach ($TorrentTags as $TagKey => $TagName) { - $TagName = str_replace('_', '.', $TagName); - $TagList[] = $TagName; - } - } - - // Append to the existing array. - $results[] = array( - 'torrentId' => (int)$TorrentID, - 'groupId' => (int)$GroupID, - 'artist' => $TruncArtist, - 'groupName' => $GroupName, - 'groupCategory' => (int)$GroupCategoryID, - 'groupYear' => (int)$GroupYear, - 'remasterTitle' => $RemasterTitle, - 'format' => $Format, - 'encoding' => $Encoding, - 'hasLog' => $HasLog == 1, - 'hasCue' => $HasCue == 1, - 'hasLogDB' => $HasLogDB == 1, - 'logScore' => $LogScore, - 'logChecksum' => $LogChecksum, - 'media' => $Media, - 'scene' => $Scene == 1, - 'year' => (int)$Year, - 'tags' => $TagList, - 'snatched' => (int)$Snatched, - 'seeders' => (int)$Seeders, - 'leechers' => (int)$Leechers, - 'data' => (int)$Data, - 'size' => (int)$Size, - 'wikiImage' => $WikiImage, - 'releaseType' => $ReleaseType, - ); - } - - return array( - 'caption' => $Caption, - 'tag' => $Tag, - 'limit' => (int)$Limit, - 'results' => $results - ); + json_encode( + array( + 'status' => 'success', + 'response' => $OuterResults + ) + ); + + +function generate_torrent_json($caption, $tag, $details, $limit) { + global $LoggedUser, $Categories; + $results = []; + foreach ($details as $detail) { + list($torrentID, $groupID, $groupName, $groupCategoryID, $wikiImage, $tagsList, + $format, $encoding, $media, $scene, $hasLog, $hasCue, $hasLogDB, $logScore, $logChecksum, $year, $groupYear, + $remasterTitle, $snatched, $seeders, $leechers, $data, $releaseType, $size) = $detail; + + $artist = Artists::display_artists(Artists::get_artist($groupID), false, true); + $truncatedArtist = substr($artist, 0, strlen($artist) - 3); + + $tagList = []; + + if ($tagsList != '') { + $tagsList = explode(' ', $tagsList); + foreach ($tagsList as $tagKey => $tagName) { + $tagName = str_replace('_', '.', $tagName); + $tagList[] = $tagName; + } + } + + // Append to the existing array. + $results[] = [ + 'torrentId' => (int)$torrentID, + 'groupID' => (int)$groupID, + 'artist' => $truncatedArtist, + 'groupName' => $groupName, + 'groupCategory' => (int)$groupCategoryID, + 'groupYear' => (int)$groupYear, + 'remasterTitle' => $remasterTitle, + 'format' => $format, + 'encoding' => $encoding, + 'hasLog' => $hasLog == 1, + 'hasCue' => $hasCue == 1, + 'hasLogDB' => $hasLogDB == 1, + 'logScore' => $logScore, + 'logChecksum' => $logChecksum, + 'media' => $media, + 'scene' => $scene == 1, + 'year' => (int)$year, + 'tags' => $tagsList, + 'snatched' => (int)$snatched, + 'seeders' => (int)$seeders, + 'leechers' => (int)$leechers, + 'data' => (int)$data, + 'size' => (int)$size, + 'wikiImage' => $wikiImage, + 'releaseType' => $releaseType, + ]; + } + + return [ + 'caption' => $caption, + 'tag' => $tag, + 'limit' => (int)$limit, + 'results' => $results + ]; } -?> diff --git a/sections/ajax/top10/users.php b/sections/ajax/top10/users.php index 52d399930..ddc791a1b 100644 --- a/sections/ajax/top10/users.php +++ b/sections/ajax/top10/users.php @@ -1,13 +1,13 @@ - 'failure')); - die(); - } + if (in_array($_GET['details'],array('ul','dl','numul','uls','dls'))) { + $Details = $_GET['details']; + } else { + print json_encode(array('status' => 'failure')); + die(); + } } else { - $Details = 'all'; + $Details = 'all'; } // defaults to 10 (duh) @@ -15,113 +15,114 @@ $Limit = in_array($Limit, array(10,100,250)) ? $Limit : 10; $BaseQuery = " - SELECT - u.ID, - u.Username, - ui.JoinDate, - u.Uploaded, - u.Downloaded, - ABS(u.Uploaded-".STARTING_UPLOAD.") / (".time()." - UNIX_TIMESTAMP(ui.JoinDate)) AS UpSpeed, - u.Downloaded / (".time()." - UNIX_TIMESTAMP(ui.JoinDate)) AS DownSpeed, - COUNT(t.ID) AS NumUploads - FROM users_main AS u - JOIN users_info AS ui ON ui.UserID = u.ID - LEFT JOIN torrents AS t ON t.UserID = u.ID - WHERE u.Enabled = '1' - AND Uploaded > '". 5 * 1024 * 1024 * 1024 ."' - AND Downloaded > '". 5 * 1024 * 1024 * 1024 ."' - AND (Paranoia IS NULL OR (Paranoia NOT LIKE '%\"uploaded\"%' AND Paranoia NOT LIKE '%\"downloaded\"%')) - GROUP BY u.ID"; + SELECT + um.ID, + um.Username, + ui.JoinDate, + uls.Uploaded, + uls.Downloaded, + ABS(uls.Uploaded-".STARTING_UPLOAD.") / (".time()." - UNIX_TIMESTAMP(ui.JoinDate)) AS UpSpeed, + um.Downloaded / (".time()." - UNIX_TIMESTAMP(ui.JoinDate)) AS DownSpeed, + COUNT(t.ID) AS NumUploads + FROM users_main AS um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + INNER JOIN users_info AS ui ON (ui.UserID = um.ID) + LEFT JOIN torrents AS t ON (t.UserID = um.ID) + WHERE um.Enabled = '1' + AND uls.Uploaded > 5 * 1024 * 1024 * 1024 + AND uls.Downloaded > 5 * 1024 * 1024 * 1024 + AND (um.Paranoia IS NULL OR (um.Paranoia NOT LIKE '%\"uploaded\"%' AND um.Paranoia NOT LIKE '%\"downloaded\"%')) + GROUP BY um.ID"; -$OuterResults = array(); +$OuterResults = []; if ($Details == 'all' || $Details == 'ul') { - if (!$TopUserUploads = $Cache->get_value("topuser_ul_$Limit")) { - $DB->query(" - $BaseQuery - ORDER BY u.Uploaded DESC - LIMIT $Limit;"); - $TopUserUploads = $DB->to_array(); - $Cache->cache_value("topuser_ul_$Limit", $TopUserUploads, 3600 * 12); - } - $OuterResults[] = generate_user_json('Uploaders', 'ul', $TopUserUploads, $Limit); + if (!$TopUserUploads = $Cache->get_value("topuser_ul_$Limit")) { + $DB->query(" + $BaseQuery + ORDER BY u.Uploaded DESC + LIMIT $Limit;"); + $TopUserUploads = $DB->to_array(); + $Cache->cache_value("topuser_ul_$Limit", $TopUserUploads, 3600 * 12); + } + $OuterResults[] = generate_user_json('Uploaders', 'ul', $TopUserUploads, $Limit); } if ($Details == 'all' || $Details == 'dl') { - if (!$TopUserDownloads = $Cache->get_value("topuser_dl_$Limit")) { - $DB->query(" - $BaseQuery - ORDER BY u.Downloaded DESC - LIMIT $Limit;"); - $TopUserDownloads = $DB->to_array(); - $Cache->cache_value("topuser_dl_$Limit", $TopUserDownloads, 3600 * 12); - } - $OuterResults[] = generate_user_json('Downloaders', 'dl', $TopUserDownloads, $Limit); + if (!$TopUserDownloads = $Cache->get_value("topuser_dl_$Limit")) { + $DB->query(" + $BaseQuery + ORDER BY u.Downloaded DESC + LIMIT $Limit;"); + $TopUserDownloads = $DB->to_array(); + $Cache->cache_value("topuser_dl_$Limit", $TopUserDownloads, 3600 * 12); + } + $OuterResults[] = generate_user_json('Downloaders', 'dl', $TopUserDownloads, $Limit); } if ($Details == 'all' || $Details == 'numul') { - if (!$TopUserNumUploads = $Cache->get_value("topuser_numul_$Limit")) { - $DB->query(" - $BaseQuery - ORDER BY NumUploads DESC - LIMIT $Limit;"); - $TopUserNumUploads = $DB->to_array(); - $Cache->cache_value("topuser_numul_$Limit", $TopUserNumUploads, 3600 * 12); - } - $OuterResults[] = generate_user_json('Torrents Uploaded', 'numul', $TopUserNumUploads, $Limit); + if (!$TopUserNumUploads = $Cache->get_value("topuser_numul_$Limit")) { + $DB->query(" + $BaseQuery + ORDER BY NumUploads DESC + LIMIT $Limit;"); + $TopUserNumUploads = $DB->to_array(); + $Cache->cache_value("topuser_numul_$Limit", $TopUserNumUploads, 3600 * 12); + } + $OuterResults[] = generate_user_json('Torrents Uploaded', 'numul', $TopUserNumUploads, $Limit); } if ($Details == 'all' || $Details == 'uls') { - if (!$TopUserUploadSpeed = $Cache->get_value("topuser_ulspeed_$Limit")) { - $DB->query(" - $BaseQuery - ORDER BY UpSpeed DESC - LIMIT $Limit;"); - $TopUserUploadSpeed = $DB->to_array(); - $Cache->cache_value("topuser_ulspeed_$Limit", $TopUserUploadSpeed, 3600 * 12); - } - $OuterResults[] = generate_user_json('Fastest Uploaders', 'uls', $TopUserUploadSpeed, $Limit); + if (!$TopUserUploadSpeed = $Cache->get_value("topuser_ulspeed_$Limit")) { + $DB->query(" + $BaseQuery + ORDER BY UpSpeed DESC + LIMIT $Limit;"); + $TopUserUploadSpeed = $DB->to_array(); + $Cache->cache_value("topuser_ulspeed_$Limit", $TopUserUploadSpeed, 3600 * 12); + } + $OuterResults[] = generate_user_json('Fastest Uploaders', 'uls', $TopUserUploadSpeed, $Limit); } if ($Details == 'all' || $Details == 'dls') { - if (!$TopUserDownloadSpeed = $Cache->get_value("topuser_dlspeed_$Limit")) { - $DB->query(" - $BaseQuery - ORDER BY DownSpeed DESC - LIMIT $Limit;"); - $TopUserDownloadSpeed = $DB->to_array(); - $Cache->cache_value("topuser_dlspeed_$Limit", $TopUserDownloadSpeed, 3600 * 12); - } - $OuterResults[] = generate_user_json('Fastest Downloaders', 'dls', $TopUserDownloadSpeed, $Limit); + if (!$TopUserDownloadSpeed = $Cache->get_value("topuser_dlspeed_$Limit")) { + $DB->query(" + $BaseQuery + ORDER BY DownSpeed DESC + LIMIT $Limit;"); + $TopUserDownloadSpeed = $DB->to_array(); + $Cache->cache_value("topuser_dlspeed_$Limit", $TopUserDownloadSpeed, 3600 * 12); + } + $OuterResults[] = generate_user_json('Fastest Downloaders', 'dls', $TopUserDownloadSpeed, $Limit); } print - json_encode( - array( - 'status' => 'success', - 'response' => $OuterResults - ) - ); + json_encode( + array( + 'status' => 'success', + 'response' => $OuterResults + ) + ); function generate_user_json($Caption, $Tag, $Details, $Limit) { - $results = array(); - foreach ($Details as $Detail) { - $results[] = array( - 'id' => (int)$Detail['ID'], - 'username' => $Detail['Username'], - 'uploaded' => (float)$Detail['Uploaded'], - 'upSpeed' => (float)$Detail['UpSpeed'], - 'downloaded' => (float)$Detail['Downloaded'], - 'downSpeed' => (float)$Detail['DownSpeed'], - 'numUploads' => (int)$Detail['NumUploads'], - 'joinDate' => $Detail['JoinDate'] - ); - } - return array( - 'caption' => $Caption, - 'tag' => $Tag, - 'limit' => (int)$Limit, - 'results' => $results - ); + $results = []; + foreach ($Details as $Detail) { + $results[] = array( + 'id' => (int)$Detail['ID'], + 'username' => $Detail['Username'], + 'uploaded' => (float)$Detail['Uploaded'], + 'upSpeed' => (float)$Detail['UpSpeed'], + 'downloaded' => (float)$Detail['Downloaded'], + 'downSpeed' => (float)$Detail['DownSpeed'], + 'numUploads' => (int)$Detail['NumUploads'], + 'joinDate' => $Detail['JoinDate'] + ); + } + return array( + 'caption' => $Caption, + 'tag' => $Tag, + 'limit' => (int)$Limit, + 'results' => $results + ); } ?> diff --git a/sections/ajax/torrent.php b/sections/ajax/torrent.php index dc97dc499..b0d823cd1 100644 --- a/sections/ajax/torrent.php +++ b/sections/ajax/torrent.php @@ -1,4 +1,4 @@ - $ArtistForm[4] == null ? array() : pullmediainfo($ArtistForm[4]), - 'dj' => $ArtistForm[6] == null ? array() : pullmediainfo($ArtistForm[6]), - 'artists' => $ArtistForm[1] == null ? array() : pullmediainfo($ArtistForm[1]), - 'with' => $ArtistForm[2] == null ? array() : pullmediainfo($ArtistForm[2]), - 'conductor' => $ArtistForm[5] == null ? array() : pullmediainfo($ArtistForm[5]), - 'remixedBy' => $ArtistForm[3] == null ? array() : pullmediainfo($ArtistForm[3]), - 'producer' => $ArtistForm[7] == null ? array() : pullmediainfo($ArtistForm[7]) - ); + $JsonMusicInfo = array( + 'composers' => $ArtistForm[4] == null ? [] : pullmediainfo($ArtistForm[4]), + 'dj' => $ArtistForm[6] == null ? [] : pullmediainfo($ArtistForm[6]), + 'artists' => $ArtistForm[1] == null ? [] : pullmediainfo($ArtistForm[1]), + 'with' => $ArtistForm[2] == null ? [] : pullmediainfo($ArtistForm[2]), + 'conductor' => $ArtistForm[5] == null ? [] : pullmediainfo($ArtistForm[5]), + 'remixedBy' => $ArtistForm[3] == null ? [] : pullmediainfo($ArtistForm[3]), + 'producer' => $ArtistForm[7] == null ? [] : pullmediainfo($ArtistForm[7]) + ); } else { - $JsonMusicInfo = null; + $JsonMusicInfo = null; } $TagList = explode('|', $TorrentDetails['GROUP_CONCAT(DISTINCT tags.Name SEPARATOR \'|\')']); $JsonTorrentDetails = array( - 'wikiBody' => Text::full_format($TorrentDetails['WikiBody']), - 'wikiImage' => $TorrentDetails['WikiImage'], - 'id' => (int)$TorrentDetails['ID'], - 'name' => $TorrentDetails['Name'], - 'year' => (int)$TorrentDetails['Year'], - 'recordLabel' => $TorrentDetails['RecordLabel'], - 'catalogueNumber' => $TorrentDetails['CatalogueNumber'], - 'releaseType' => (int)$TorrentDetails['ReleaseType'], - 'categoryId' => (int)$TorrentDetails['CategoryID'], - 'categoryName' => $CategoryName, - 'time' => $TorrentDetails['Time'], - 'vanityHouse' => $TorrentDetails['VanityHouse'] == 1, - 'isBookmarked' => Bookmarks::has_bookmarked('torrent', $GroupID), - 'musicInfo' => $JsonMusicInfo, - 'tags' => $TagList + 'wikiBody' => Text::full_format($TorrentDetails['WikiBody']), + 'wikiImage' => $TorrentDetails['WikiImage'], + 'id' => (int)$TorrentDetails['ID'], + 'name' => $TorrentDetails['Name'], + 'year' => (int)$TorrentDetails['Year'], + 'recordLabel' => $TorrentDetails['RecordLabel'], + 'catalogueNumber' => $TorrentDetails['CatalogueNumber'], + 'releaseType' => (int)$TorrentDetails['ReleaseType'], + 'categoryId' => (int)$TorrentDetails['CategoryID'], + 'categoryName' => $CategoryName, + 'time' => $TorrentDetails['Time'], + 'vanityHouse' => $TorrentDetails['VanityHouse'] == 1, + 'isBookmarked' => Bookmarks::has_bookmarked('torrent', $GroupID), + 'musicInfo' => $JsonMusicInfo, + 'tags' => $TagList ); $Torrent = $TorrentList[$TorrentID]; $Reports = Torrents::get_reports($TorrentID); if (count($Reports) > 0) { - $Torrent['Reported'] = true; + $Torrent['Reported'] = true; } else { - $Torrent['Reported'] = false; + $Torrent['Reported'] = false; } // Convert file list back to the old format $FileList = explode("\n", $Torrent['FileList']); foreach ($FileList as &$File) { - $File = Torrents::filelist_old_format($File); + $File = Torrents::filelist_old_format($File); } unset($File); $FileList = implode('|||', $FileList); $Userinfo = Users::user_info($Torrent['UserID']); $JsonTorrentList[] = array( - 'id' => (int)$Torrent['ID'], - 'infoHash' => $Torrent['InfoHash'], - 'media' => $Torrent['Media'], - 'format' => $Torrent['Format'], - 'encoding' => $Torrent['Encoding'], - 'remastered' => $Torrent['Remastered'] == 1, - 'remasterYear' => (int)$Torrent['RemasterYear'], - 'remasterTitle' => $Torrent['RemasterTitle'], - 'remasterRecordLabel' => $Torrent['RemasterRecordLabel'], - 'remasterCatalogueNumber' => $Torrent['RemasterCatalogueNumber'], - 'scene' => $Torrent['Scene'] == 1, - 'hasLog' => $Torrent['HasLog'] == 1, - 'hasCue' => $Torrent['HasCue'] == 1, - 'logScore' => (int)$Torrent['LogScore'], - 'logChecksum' => (int)$Torrent['LogChecksum'], - 'logCount' => (int)$Torrent['LogCount'], - 'fileCount' => (int)$Torrent['FileCount'], - 'size' => (int)$Torrent['Size'], - 'seeders' => (int)$Torrent['Seeders'], - 'leechers' => (int)$Torrent['Leechers'], - 'snatched' => (int)$Torrent['Snatched'], - 'freeTorrent' => $Torrent['FreeTorrent'] == 1, - 'reported' => $Torrent['Reported'], - 'time' => $Torrent['Time'], - 'description' => $Torrent['Description'], - 'fileList' => $FileList, - 'filePath' => $Torrent['FilePath'], - 'userId' => (int)$Torrent['UserID'], - 'username' => $Userinfo['Username'] + 'id' => (int)$Torrent['ID'], + 'infoHash' => $Torrent['InfoHash'], + 'media' => $Torrent['Media'], + 'format' => $Torrent['Format'], + 'encoding' => $Torrent['Encoding'], + 'remastered' => $Torrent['Remastered'] == 1, + 'remasterYear' => (int)$Torrent['RemasterYear'], + 'remasterTitle' => $Torrent['RemasterTitle'], + 'remasterRecordLabel' => $Torrent['RemasterRecordLabel'], + 'remasterCatalogueNumber' => $Torrent['RemasterCatalogueNumber'], + 'scene' => $Torrent['Scene'] == 1, + 'hasLog' => $Torrent['HasLog'] == 1, + 'hasCue' => $Torrent['HasCue'] == 1, + 'logScore' => (int)$Torrent['LogScore'], + 'logChecksum' => (int)$Torrent['LogChecksum'], + 'logCount' => (int)$Torrent['LogCount'], + 'fileCount' => (int)$Torrent['FileCount'], + 'size' => (int)$Torrent['Size'], + 'seeders' => (int)$Torrent['Seeders'], + 'leechers' => (int)$Torrent['Leechers'], + 'snatched' => (int)$Torrent['Snatched'], + 'freeTorrent' => $Torrent['FreeTorrent'] == 1, + 'reported' => $Torrent['Reported'], + 'time' => $Torrent['Time'], + 'description' => $Torrent['Description'], + 'fileList' => $FileList, + 'filePath' => $Torrent['FilePath'], + 'userId' => (int)$Torrent['UserID'], + 'username' => $Userinfo['Username'] ); json_print("success", array('group' => $JsonTorrentDetails, 'torrent' => array_pop($JsonTorrentList))); diff --git a/sections/ajax/torrentgroup.php b/sections/ajax/torrentgroup.php index 2f242b477..458c226de 100644 --- a/sections/ajax/torrentgroup.php +++ b/sections/ajax/torrentgroup.php @@ -9,115 +9,115 @@ $TorrentHash = (string)$_GET['hash']; if ($GroupID && $TorrentHash) { - json_die("failure", "bad parameters"); + json_die("failure", "bad parameters"); } if ($TorrentHash) { - if (!is_valid_torrenthash($TorrentHash)) { - json_die("failure", "bad hash parameter"); - } else { - $GroupID = (int)torrenthash_to_groupid($TorrentHash); - if (!$GroupID) { - json_die("failure", "bad hash parameter"); - } - } + if (!is_valid_torrenthash($TorrentHash)) { + json_die("failure", "bad hash parameter"); + } else { + $GroupID = (int)torrenthash_to_groupid($TorrentHash); + if (!$GroupID) { + json_die("failure", "bad hash parameter"); + } + } } if ($GroupID <= 0) { - json_die("failure", "bad id parameter"); + json_die("failure", "bad id parameter"); } $TorrentCache = get_group_info($GroupID, true, 0, true, true); if (!$TorrentCache) { - json_die("failure", "bad id parameter"); + json_die("failure", "bad id parameter"); } list($TorrentDetails, $TorrentList) = $TorrentCache; $ArtistForm = Artists::get_artist($GroupID); if ($TorrentDetails['CategoryID'] == 0) { - $CategoryName = 'Unknown'; + $CategoryName = 'Unknown'; } else { - $CategoryName = $Categories[$TorrentDetails['CategoryID'] - 1]; + $CategoryName = $Categories[$TorrentDetails['CategoryID'] - 1]; } -$JsonMusicInfo = array(); +$JsonMusicInfo = []; if ($CategoryName == 'Music') { - $JsonMusicInfo = array( - 'composers' => ($ArtistForm[4] == null) ? array() : pullmediainfo($ArtistForm[4]), - 'dj' => ($ArtistForm[6] == null) ? array() : pullmediainfo($ArtistForm[6]), - 'artists' => ($ArtistForm[1] == null) ? array() : pullmediainfo($ArtistForm[1]), - 'with' => ($ArtistForm[2] == null) ? array() : pullmediainfo($ArtistForm[2]), - 'conductor' => ($ArtistForm[5] == null) ? array() : pullmediainfo($ArtistForm[5]), - 'remixedBy' => ($ArtistForm[3] == null) ? array() : pullmediainfo($ArtistForm[3]), - 'producer' => ($ArtistForm[7] == null) ? array() : pullmediainfo($ArtistForm[7]) - ); + $JsonMusicInfo = array( + 'composers' => ($ArtistForm[4] == null) ? [] : pullmediainfo($ArtistForm[4]), + 'dj' => ($ArtistForm[6] == null) ? [] : pullmediainfo($ArtistForm[6]), + 'artists' => ($ArtistForm[1] == null) ? [] : pullmediainfo($ArtistForm[1]), + 'with' => ($ArtistForm[2] == null) ? [] : pullmediainfo($ArtistForm[2]), + 'conductor' => ($ArtistForm[5] == null) ? [] : pullmediainfo($ArtistForm[5]), + 'remixedBy' => ($ArtistForm[3] == null) ? [] : pullmediainfo($ArtistForm[3]), + 'producer' => ($ArtistForm[7] == null) ? [] : pullmediainfo($ArtistForm[7]) + ); } else { - $JsonMusicInfo = null; + $JsonMusicInfo = null; } $TagList = explode('|', $TorrentDetails['GROUP_CONCAT(DISTINCT tags.Name SEPARATOR \'|\')']); $JsonTorrentDetails = array( - 'wikiBody' => Text::full_format($TorrentDetails['WikiBody']), - 'wikiImage' => $TorrentDetails['WikiImage'], - 'id' => (int)$TorrentDetails['ID'], - 'name' => $TorrentDetails['Name'], - 'year' => (int)$TorrentDetails['Year'], - 'recordLabel' => $TorrentDetails['RecordLabel'], - 'catalogueNumber' => $TorrentDetails['CatalogueNumber'], - 'releaseType' => (int)$TorrentDetails['ReleaseType'], - 'categoryId' => (int)$TorrentDetails['CategoryID'], - 'categoryName' => $CategoryName, - 'time' => $TorrentDetails['Time'], - 'vanityHouse' => ($TorrentDetails['VanityHouse'] == 1), - 'isBookmarked' => Bookmarks::has_bookmarked('torrent', $GroupID), - 'musicInfo' => $JsonMusicInfo, - 'tags' => $TagList + 'wikiBody' => Text::full_format($TorrentDetails['WikiBody']), + 'wikiImage' => $TorrentDetails['WikiImage'], + 'id' => (int)$TorrentDetails['ID'], + 'name' => $TorrentDetails['Name'], + 'year' => (int)$TorrentDetails['Year'], + 'recordLabel' => $TorrentDetails['RecordLabel'], + 'catalogueNumber' => $TorrentDetails['CatalogueNumber'], + 'releaseType' => (int)$TorrentDetails['ReleaseType'], + 'categoryId' => (int)$TorrentDetails['CategoryID'], + 'categoryName' => $CategoryName, + 'time' => $TorrentDetails['Time'], + 'vanityHouse' => ($TorrentDetails['VanityHouse'] == 1), + 'isBookmarked' => Bookmarks::has_bookmarked('torrent', $GroupID), + 'musicInfo' => $JsonMusicInfo, + 'tags' => $TagList ); -$JsonTorrentList = array(); +$JsonTorrentList = []; foreach ($TorrentList as $Torrent) { - // Convert file list back to the old format - $FileList = explode("\n", $Torrent['FileList']); - foreach ($FileList as &$File) { - $File = Torrents::filelist_old_format($File); - } - unset($File); - $FileList = implode('|||', $FileList); - $Userinfo = Users::user_info($Torrent['UserID']); - $Reports = Torrents::get_reports($Torrent['ID']); - $Torrent['Reported'] = count($Reports) > 0; - $JsonTorrentList[] = array( - 'id' => (int)$Torrent['ID'], - 'media' => $Torrent['Media'], - 'format' => $Torrent['Format'], - 'encoding' => $Torrent['Encoding'], - 'remastered' => $Torrent['Remastered'] == 1, - 'remasterYear' => (int)$Torrent['RemasterYear'], - 'remasterTitle' => $Torrent['RemasterTitle'], - 'remasterRecordLabel' => $Torrent['RemasterRecordLabel'], - 'remasterCatalogueNumber' => $Torrent['RemasterCatalogueNumber'], - 'scene' => $Torrent['Scene'] == 1, - 'hasLog' => $Torrent['HasLog'] == 1, - 'hasCue' => $Torrent['HasCue'] == 1, - 'hasLogDB' => $Torrent['HasLogDB'] == 1, - 'logScore' => (int)$Torrent['LogScore'], - 'logChecksum' => $Torrent['LogChecksum'] == 1, - 'fileCount' => (int)$Torrent['FileCount'], - 'size' => (int)$Torrent['Size'], - 'seeders' => (int)$Torrent['Seeders'], - 'leechers' => (int)$Torrent['Leechers'], - 'snatched' => (int)$Torrent['Snatched'], - 'freeTorrent' => $Torrent['FreeTorrent'] == 1, - 'reported' => $Torrent['Reported'], - 'time' => $Torrent['Time'], - 'description' => $Torrent['Description'], - 'fileList' => $FileList, - 'filePath' => $Torrent['FilePath'], - 'userId' => (int)$Torrent['UserID'], - 'username' => $Userinfo['Username'] - ); + // Convert file list back to the old format + $FileList = explode("\n", $Torrent['FileList']); + foreach ($FileList as &$File) { + $File = Torrents::filelist_old_format($File); + } + unset($File); + $FileList = implode('|||', $FileList); + $Userinfo = Users::user_info($Torrent['UserID']); + $Reports = Torrents::get_reports($Torrent['ID']); + $Torrent['Reported'] = count($Reports) > 0; + $JsonTorrentList[] = array( + 'id' => (int)$Torrent['ID'], + 'media' => $Torrent['Media'], + 'format' => $Torrent['Format'], + 'encoding' => $Torrent['Encoding'], + 'remastered' => $Torrent['Remastered'] == 1, + 'remasterYear' => (int)$Torrent['RemasterYear'], + 'remasterTitle' => $Torrent['RemasterTitle'], + 'remasterRecordLabel' => $Torrent['RemasterRecordLabel'], + 'remasterCatalogueNumber' => $Torrent['RemasterCatalogueNumber'], + 'scene' => $Torrent['Scene'] == 1, + 'hasLog' => $Torrent['HasLog'] == 1, + 'hasCue' => $Torrent['HasCue'] == 1, + 'hasLogDB' => $Torrent['HasLogDB'] == 1, + 'logScore' => (int)$Torrent['LogScore'], + 'logChecksum' => $Torrent['LogChecksum'] == 1, + 'fileCount' => (int)$Torrent['FileCount'], + 'size' => (int)$Torrent['Size'], + 'seeders' => (int)$Torrent['Seeders'], + 'leechers' => (int)$Torrent['Leechers'], + 'snatched' => (int)$Torrent['Snatched'], + 'freeTorrent' => $Torrent['FreeTorrent'] == 1, + 'reported' => $Torrent['Reported'], + 'time' => $Torrent['Time'], + 'description' => $Torrent['Description'], + 'fileList' => $FileList, + 'filePath' => $Torrent['FilePath'], + 'userId' => (int)$Torrent['UserID'], + 'username' => $Userinfo['Username'] + ); } json_print("success", array('group' => $JsonTorrentDetails, 'torrents' => $JsonTorrentList)); diff --git a/sections/ajax/torrentgroupalbumart.php b/sections/ajax/torrentgroupalbumart.php index b4bd95ab8..5f4d299d6 100644 --- a/sections/ajax/torrentgroupalbumart.php +++ b/sections/ajax/torrentgroupalbumart.php @@ -3,16 +3,16 @@ $GroupID = (int)$_GET['id']; if ($GroupID === 0) { - error('bad id parameter', true); + error('bad id parameter', true); } $TorrentDetails = get_group_info($GroupID, true, 0, false); $TorrentDetails = $TorrentDetails[0]; $Image = $TorrentDetails['WikiImage']; if (!$Image) { // handle no artwork - $Image = STATIC_SERVER.'common/noartwork/'.$CategoryIcons[$TorrentDetails['CategoryID'] - 1]; + $Image = STATIC_SERVER.'common/noartwork/'.$CategoryIcons[$TorrentDetails['CategoryID'] - 1]; } json_die("success", array( - 'wikiImage' => $Image + 'wikiImage' => $Image )); diff --git a/sections/ajax/upload.php b/sections/ajax/upload.php index 8adbf2b4f..0ac46c0de 100644 --- a/sections/ajax/upload.php +++ b/sections/ajax/upload.php @@ -1,4 +1,4 @@ -get_value('genre_tags'); if (!$GenreTags) { - $DB->query(' - SELECT Name - FROM tags - WHERE TagType=\'genre\' - ORDER BY Name'); - $GenreTags = $DB->collect('Name'); - $Cache->cache_value('genre_tags', $GenreTags, 3600 * 24); + $DB->query(' + SELECT Name + FROM tags + WHERE TagType=\'genre\' + ORDER BY Name'); + $GenreTags = $DB->collect('Name'); + $Cache->cache_value('genre_tags', $GenreTags, 3600 * 24); } $UploadForm = $Categories[$_GET['categoryid']]; switch ($UploadForm) { - case 'Music': - $TorrentForm->music_form($GenreTags); - break; - - case 'Audiobooks': - case 'Comedy': - $TorrentForm->audiobook_form(); - break; - - case 'Applications': - case 'Comics': - case 'E-Books': - case 'E-Learning Videos': - $TorrentForm->simple_form($_GET['categoryid']); - break; - default: - echo 'Invalid action!'; + case 'Music': + $TorrentForm->music_form($GenreTags); + break; + + case 'Audiobooks': + case 'Comedy': + $TorrentForm->audiobook_form(); + break; + + case 'Applications': + case 'Comics': + case 'E-Books': + case 'E-Learning Videos': + $TorrentForm->simple_form($_GET['categoryid']); + break; + default: + echo 'Invalid action!'; } ?> diff --git a/sections/ajax/user.php b/sections/ajax/user.php index d15ecc361..870f0bdd6 100644 --- a/sections/ajax/user.php +++ b/sections/ajax/user.php @@ -1,66 +1,68 @@ query(" - SELECT - m.Username, - m.Email, - m.LastAccess, - m.IP, - p.Level AS Class, - m.Uploaded, - m.Downloaded, - m.RequiredRatio, - m.Enabled, - m.Paranoia, - m.Invites, - m.Title, - m.torrent_pass, - m.can_leech, - i.JoinDate, - i.Info, - i.Avatar, - i.Donor, - i.Warned, - COUNT(posts.id) AS ForumPosts, - i.Inviter, - i.DisableInvites, - inviter.username - FROM users_main AS m - JOIN users_info AS i ON i.UserID = m.ID - LEFT JOIN permissions AS p ON p.ID = m.PermissionID - LEFT JOIN users_main AS inviter ON i.Inviter = inviter.ID - LEFT JOIN forums_posts AS posts ON posts.AuthorID = m.ID - WHERE m.ID = $UserID - GROUP BY AuthorID"); +$DB->prepared_query(' + SELECT + m.Username, + m.Email, + m.LastAccess, + m.IP, + p.Level AS Class, + uls.Uploaded, + uls.Downloaded, + m.RequiredRatio, + m.Enabled, + m.Paranoia, + m.Invites, + m.Title, + m.torrent_pass, + m.can_leech, + i.JoinDate, + i.Info, + i.Avatar, + i.Donor, + i.Warned, + COUNT(posts.id) AS ForumPosts, + i.Inviter, + i.DisableInvites, + inviter.username + FROM users_main AS m + INNER JOIN users_leech_stats AS uls ON (uls.UserID = m.ID) + INNER JOIN users_info AS i ON (i.UserID = m.ID) + LEFT JOIN permissions AS p ON (p.ID = m.PermissionID) + LEFT JOIN users_main AS inviter ON (i.Inviter = inviter.ID) + LEFT JOIN forums_posts AS posts ON (posts.AuthorID = m.ID) + WHERE m.ID = ? + GROUP BY AuthorID + ', $UserID); if (!$DB->has_results()) { // If user doesn't exist - json_die("failure", "no such user"); + json_die("failure", "no such user"); } list($Username, $Email, $LastAccess, $IP, $Class, $Uploaded, $Downloaded, $RequiredRatio, $Enabled, $Paranoia, $Invites, $CustomTitle, $torrent_pass, $DisableLeech, $JoinDate, $Info, $Avatar, $Donor, $Warned, $ForumPosts, $InviterID, $DisableInvites, $InviterName, $RatioWatchEnds, $RatioWatchDownload) = $DB->next_record(MYSQLI_NUM, array(9, 11)); $Paranoia = unserialize($Paranoia); if (!is_array($Paranoia)) { - $Paranoia = array(); + $Paranoia = []; } $ParanoiaLevel = 0; foreach ($Paranoia as $P) { - $ParanoiaLevel++; - if (strpos($P, '+') !== false) { - $ParanoiaLevel++; - } + $ParanoiaLevel++; + if (strpos($P, '+') !== false) { + $ParanoiaLevel++; + } } // Raw time is better for JSON. @@ -68,323 +70,323 @@ //$LastAccess = time_diff($LastAccess); function check_paranoia_here($Setting) { - global $Paranoia, $Class, $UserID; - return check_paranoia($Setting, $Paranoia, $Class, $UserID); + global $Paranoia, $Class, $UserID; + return check_paranoia($Setting, $Paranoia, $Class, $UserID); } $Friend = false; $DB->query(" - SELECT FriendID - FROM friends - WHERE UserID = '$LoggedUser[ID]' - AND FriendID = '$UserID'"); + SELECT FriendID + FROM friends + WHERE UserID = '$LoggedUser[ID]' + AND FriendID = '$UserID'"); if ($DB->has_results()) { - $Friend = true; + $Friend = true; } if (check_paranoia_here('requestsfilled_count') || check_paranoia_here('requestsfilled_bounty')) { - $DB->query(" - SELECT COUNT(DISTINCT r.ID), SUM(rv.Bounty) - FROM requests AS r - LEFT JOIN requests_votes AS rv ON r.ID = rv.RequestID - WHERE r.FillerID = $UserID"); - list($RequestsFilled, $TotalBounty) = $DB->next_record(); - $DB->query(" - SELECT COUNT(RequestID), SUM(Bounty) - FROM requests_votes - WHERE UserID = $UserID"); - list($RequestsVoted, $TotalSpent) = $DB->next_record(); + $DB->query(" + SELECT COUNT(DISTINCT r.ID), SUM(rv.Bounty) + FROM requests AS r + LEFT JOIN requests_votes AS rv ON r.ID = rv.RequestID + WHERE r.FillerID = $UserID"); + list($RequestsFilled, $TotalBounty) = $DB->next_record(); + $DB->query(" + SELECT COUNT(RequestID), SUM(Bounty) + FROM requests_votes + WHERE UserID = $UserID"); + list($RequestsVoted, $TotalSpent) = $DB->next_record(); - $DB->query(" - SELECT COUNT(ID) - FROM torrents - WHERE UserID = '$UserID'"); - list($Uploads) = $DB->next_record(); + $DB->query(" + SELECT COUNT(ID) + FROM torrents + WHERE UserID = '$UserID'"); + list($Uploads) = $DB->next_record(); } else { - $RequestsFilled = null; - $TotalBounty = null; - $RequestsVoted = null; - $TotalSpent = null; + $RequestsFilled = null; + $TotalBounty = null; + $RequestsVoted = null; + $TotalSpent = null; } if (check_paranoia_here('uploads+')) { - $DB->query(" - SELECT COUNT(ID) - FROM torrents - WHERE UserID = '$UserID'"); - list($Uploads) = $DB->next_record(); + $DB->query(" + SELECT COUNT(ID) + FROM torrents + WHERE UserID = '$UserID'"); + list($Uploads) = $DB->next_record(); } else { - $Uploads = null; + $Uploads = null; } if (check_paranoia_here('artistsadded')) { - $DB->query(" - SELECT COUNT(ArtistID) - FROM torrents_artists - WHERE UserID = $UserID"); - list($ArtistsAdded) = $DB->next_record(); + $DB->query(" + SELECT COUNT(ArtistID) + FROM torrents_artists + WHERE UserID = $UserID"); + list($ArtistsAdded) = $DB->next_record(); } else { - $ArtistsAdded = null; + $ArtistsAdded = null; } // Do the ranks. if (check_paranoia_here('uploaded')) { - $UploadedRank = UserRank::get_rank('uploaded', $Uploaded); + $UploadedRank = UserRank::get_rank('uploaded', $Uploaded); } else { - $UploadedRank = null; + $UploadedRank = null; } if (check_paranoia_here('downloaded')) { - $DownloadedRank = UserRank::get_rank('downloaded', $Downloaded); + $DownloadedRank = UserRank::get_rank('downloaded', $Downloaded); } else { - $DownloadedRank = null; + $DownloadedRank = null; } if (check_paranoia_here('uploads+')) { - $UploadsRank = UserRank::get_rank('uploads', $Uploads); + $UploadsRank = UserRank::get_rank('uploads', $Uploads); } else { - $UploadsRank = null; + $UploadsRank = null; } if (check_paranoia_here('requestsfilled_count')) { - $RequestRank = UserRank::get_rank('requests', $RequestsFilled); + $RequestRank = UserRank::get_rank('requests', $RequestsFilled); } else { - $RequestRank = null; + $RequestRank = null; } $PostRank = UserRank::get_rank('posts', $ForumPosts); if (check_paranoia_here('requestsvoted_bounty')) { - $BountyRank = UserRank::get_rank('bounty', $TotalSpent); + $BountyRank = UserRank::get_rank('bounty', $TotalSpent); } else { - $BountyRank = null; + $BountyRank = null; } if (check_paranoia_here('artistsadded')) { - $ArtistsRank = UserRank::get_rank('artists', $ArtistsAdded); + $ArtistsRank = UserRank::get_rank('artists', $ArtistsAdded); } else { - $ArtistsRank = null; + $ArtistsRank = null; } if ($Downloaded == 0) { - $Ratio = 1; + $Ratio = 1; } elseif ($Uploaded == 0) { - $Ratio = 0.5; + $Ratio = 0.5; } else { - $Ratio = round($Uploaded / $Downloaded, 2); + $Ratio = round($Uploaded / $Downloaded, 2); } if (check_paranoia_here(array('uploaded', 'downloaded', 'uploads+', 'requestsfilled_count', 'requestsvoted_bounty', 'artistsadded'))) { - $OverallRank = floor(UserRank::overall_score($UploadedRank, $DownloadedRank, $UploadsRank, $RequestRank, $PostRank, $BountyRank, $ArtistsRank, $Ratio)); + $OverallRank = floor(UserRank::overall_score($UploadedRank, $DownloadedRank, $UploadsRank, $RequestRank, $PostRank, $BountyRank, $ArtistsRank, $Ratio)); } else { - $OverallRank = null; + $OverallRank = null; } // Community section if (check_paranoia_here('snatched+')) { $DB->query(" - SELECT COUNT(x.uid), COUNT(DISTINCT x.fid) - FROM xbt_snatched AS x - INNER JOIN torrents AS t ON t.ID = x.fid - WHERE x.uid = '$UserID'"); + SELECT COUNT(x.uid), COUNT(DISTINCT x.fid) + FROM xbt_snatched AS x + INNER JOIN torrents AS t ON t.ID = x.fid + WHERE x.uid = '$UserID'"); list($Snatched, $UniqueSnatched) = $DB->next_record(); } if (check_paranoia_here('torrentcomments+')) { - $DB->query(" - SELECT COUNT(ID) - FROM comments - WHERE Page = 'torrents' - AND AuthorID = '$UserID'"); - list($NumComments) = $DB->next_record(); + $DB->query(" + SELECT COUNT(ID) + FROM comments + WHERE Page = 'torrents' + AND AuthorID = '$UserID'"); + list($NumComments) = $DB->next_record(); } if (check_paranoia_here('torrentcomments+')) { - $DB->query(" - SELECT COUNT(ID) - FROM comments - WHERE Page = 'artist' - AND AuthorID = '$UserID'"); - list($NumArtistComments) = $DB->next_record(); + $DB->query(" + SELECT COUNT(ID) + FROM comments + WHERE Page = 'artist' + AND AuthorID = '$UserID'"); + list($NumArtistComments) = $DB->next_record(); } if (check_paranoia_here('torrentcomments+')) { - $DB->query(" - SELECT COUNT(ID) - FROM comments - WHERE Page = 'collages' - AND AuthorID = '$UserID'"); - list($NumCollageComments) = $DB->next_record(); + $DB->query(" + SELECT COUNT(ID) + FROM comments + WHERE Page = 'collages' + AND AuthorID = '$UserID'"); + list($NumCollageComments) = $DB->next_record(); } if (check_paranoia_here('torrentcomments+')) { - $DB->query(" - SELECT COUNT(ID) - FROM comments - WHERE Page = 'requests' - AND AuthorID = '$UserID'"); - list($NumRequestComments) = $DB->next_record(); + $DB->query(" + SELECT COUNT(ID) + FROM comments + WHERE Page = 'requests' + AND AuthorID = '$UserID'"); + list($NumRequestComments) = $DB->next_record(); } if (check_paranoia_here('collages+')) { - $DB->query(" - SELECT COUNT(ID) - FROM collages - WHERE Deleted = '0' - AND UserID = '$UserID'"); - list($NumCollages) = $DB->next_record(); + $DB->query(" + SELECT COUNT(ID) + FROM collages + WHERE Deleted = '0' + AND UserID = '$UserID'"); + list($NumCollages) = $DB->next_record(); } if (check_paranoia_here('collagecontribs+')) { - $DB->query(" - SELECT COUNT(DISTINCT ct.CollageID) - FROM collages_torrents AS ct - JOIN collages AS c ON ct.CollageID = c.ID - WHERE c.Deleted = '0' - AND ct.UserID = '$UserID'"); - list($NumCollageContribs) = $DB->next_record(); + $DB->query(" + SELECT COUNT(DISTINCT ct.CollageID) + FROM collages_torrents AS ct + JOIN collages AS c ON ct.CollageID = c.ID + WHERE c.Deleted = '0' + AND ct.UserID = '$UserID'"); + list($NumCollageContribs) = $DB->next_record(); } if (check_paranoia_here('uniquegroups+')) { - $DB->query(" - SELECT COUNT(DISTINCT GroupID) - FROM torrents - WHERE UserID = '$UserID'"); - list($UniqueGroups) = $DB->next_record(); + $DB->query(" + SELECT COUNT(DISTINCT GroupID) + FROM torrents + WHERE UserID = '$UserID'"); + list($UniqueGroups) = $DB->next_record(); } if (check_paranoia_here('perfectflacs+')) { - $DB->query(" - SELECT COUNT(ID) - FROM torrents - WHERE ( - (LogScore = 100 AND Format = 'FLAC') - OR (Media = 'Vinyl' AND Format = 'FLAC') - OR (Media = 'WEB' AND Format = 'FLAC') - OR (Media = 'DVD' AND Format = 'FLAC') - OR (Media = 'Soundboard' AND Format = 'FLAC') - OR (Media = 'Cassette' AND Format = 'FLAC') - OR (Media = 'SACD' AND Format = 'FLAC') - OR (Media = 'BD' AND Format = 'FLAC') - OR (Media = 'DAT' AND Format = 'FLAC') - ) - AND UserID = '$UserID'"); - list($PerfectFLACs) = $DB->next_record(); + $DB->query(" + SELECT COUNT(ID) + FROM torrents + WHERE ( + (LogScore = 100 AND Format = 'FLAC') + OR (Media = 'Vinyl' AND Format = 'FLAC') + OR (Media = 'WEB' AND Format = 'FLAC') + OR (Media = 'DVD' AND Format = 'FLAC') + OR (Media = 'Soundboard' AND Format = 'FLAC') + OR (Media = 'Cassette' AND Format = 'FLAC') + OR (Media = 'SACD' AND Format = 'FLAC') + OR (Media = 'BD' AND Format = 'FLAC') + OR (Media = 'DAT' AND Format = 'FLAC') + ) + AND UserID = '$UserID'"); + list($PerfectFLACs) = $DB->next_record(); } if (check_paranoia_here('seeding+')) { - $DB->query(" - SELECT COUNT(x.uid) - FROM xbt_files_users AS x - INNER JOIN torrents AS t ON t.ID = x.fid - WHERE x.uid = '$UserID' - AND x.remaining = 0"); - list($Seeding) = $DB->next_record(); + $DB->query(" + SELECT COUNT(x.uid) + FROM xbt_files_users AS x + INNER JOIN torrents AS t ON t.ID = x.fid + WHERE x.uid = '$UserID' + AND x.remaining = 0"); + list($Seeding) = $DB->next_record(); } if (check_paranoia_here('leeching+')) { - $DB->query(" - SELECT COUNT(x.uid) - FROM xbt_files_users AS x - INNER JOIN torrents AS t ON t.ID = x.fid - WHERE x.uid = '$UserID' - AND x.remaining > 0"); - list($Leeching) = $DB->next_record(); + $DB->query(" + SELECT COUNT(x.uid) + FROM xbt_files_users AS x + INNER JOIN torrents AS t ON t.ID = x.fid + WHERE x.uid = '$UserID' + AND x.remaining > 0"); + list($Leeching) = $DB->next_record(); } if (check_paranoia_here('invitedcount')) { - $DB->query(" - SELECT COUNT(UserID) - FROM users_info - WHERE Inviter = '$UserID'"); - list($Invited) = $DB->next_record(); + $DB->query(" + SELECT COUNT(UserID) + FROM users_info + WHERE Inviter = '$UserID'"); + list($Invited) = $DB->next_record(); } if (!$OwnProfile) { - $torrent_pass = ''; + $torrent_pass = ''; } // Run through some paranoia stuff to decide what we can send out. if (!check_paranoia_here('lastseen')) { - $LastAccess = ''; + $LastAccess = ''; } if (check_paranoia_here('ratio')) { - $Ratio = Format::get_ratio($Uploaded, $Downloaded, 5); + $Ratio = Format::get_ratio($Uploaded, $Downloaded, 5); } else { - $Ratio = null; + $Ratio = null; } if (!check_paranoia_here('uploaded')) { - $Uploaded = null; + $Uploaded = null; } if (!check_paranoia_here('downloaded')) { - $Downloaded = null; + $Downloaded = null; } if (isset($RequiredRatio) && !check_paranoia_here('requiredratio')) { - $RequiredRatio = null; + $RequiredRatio = null; } if ($ParanoiaLevel == 0) { - $ParanoiaLevelText = 'Off'; + $ParanoiaLevelText = 'Off'; } elseif ($ParanoiaLevel == 1) { - $ParanoiaLevelText = 'Very Low'; + $ParanoiaLevelText = 'Very Low'; } elseif ($ParanoiaLevel <= 5) { - $ParanoiaLevelText = 'Low'; + $ParanoiaLevelText = 'Low'; } elseif ($ParanoiaLevel <= 20) { - $ParanoiaLevelText = 'High'; + $ParanoiaLevelText = 'High'; } else { - $ParanoiaLevelText = 'Very high'; + $ParanoiaLevelText = 'Very high'; } //Bugfix for no access time available if ($LastAccess == '0000-00-00 00:00:00') { - $LastAccess = ''; + $LastAccess = ''; } header('Content-Type: text/plain; charset=utf-8'); json_print("success", array( - 'username' => $Username, - 'avatar' => $Avatar, - 'isFriend' => $Friend, - 'profileText' => Text::full_format($Info), - 'stats' => array( - 'joinedDate' => $JoinDate, - 'lastAccess' => $LastAccess, - 'uploaded' => (($Uploaded == null) ? null : (int)$Uploaded), - 'downloaded' => (($Downloaded == null) ? null : (int)$Downloaded), - 'ratio' => $Ratio, - 'requiredRatio' => (($RequiredRatio == null) ? null : (float)$RequiredRatio) - ), - 'ranks' => array( - 'uploaded' => $UploadedRank, - 'downloaded' => $DownloadedRank, - 'uploads' => $UploadsRank, - 'requests' => $RequestRank, - 'bounty' => $BountyRank, - 'posts' => $PostRank, - 'artists' => $ArtistsRank, - 'overall' => (($OverallRank == null) ? 0 : $OverallRank) - ), - 'personal' => array( - 'class' => $ClassLevels[$Class]['Name'], - 'paranoia' => $ParanoiaLevel, - 'paranoiaText' => $ParanoiaLevelText, - 'donor' => ($Donor == 1), - 'warned' => ($Warned != '0000-00-00 00:00:00'), - 'enabled' => ($Enabled == '1' || $Enabled == '0' || !$Enabled), - 'passkey' => $torrent_pass - ), - 'community' => array( - 'posts' => (int)$ForumPosts, - 'torrentComments' => (($NumComments == null) ? null : (int)$NumComments), - 'artistComments' => (($NumArtistComments == null) ? null : (int)$NumArtistComments), - 'collageComments' => (($NumCollageComments == null) ? null : (int)$NumCollageComments), - 'requestComments' => (($NumRequestComments == null) ? null : (int)$NumRequestComments), - 'collagesStarted' => (($NumCollages == null) ? null : (int)$NumCollages), - 'collagesContrib' => (($NumCollageContribs == null) ? null : (int)$NumCollageContribs), - 'requestsFilled' => (($RequestsFilled == null) ? null : (int)$RequestsFilled), - 'bountyEarned' => (($TotalBounty == null) ? null : (int)$TotalBounty), - 'requestsVoted' => (($RequestsVoted == null) ? null : (int)$RequestsVoted), - 'bountySpent' => (($TotalSpent == null) ? null : (int)$TotalSpent), - 'perfectFlacs' => (($PerfectFLACs == null) ? null : (int)$PerfectFLACs), - 'uploaded' => (($Uploads == null) ? null : (int)$Uploads), - 'groups' => (($UniqueGroups == null) ? null : (int)$UniqueGroups), - 'seeding' => (($Seeding == null) ? null : (int)$Seeding), - 'leeching' => (($Leeching == null) ? null : (int)$Leeching), - 'snatched' => (($Snatched == null) ? null : (int)$Snatched), - 'invited' => (($Invited == null) ? null : (int)$Invited), - 'artistsAdded' => (($ArtistsAdded == null) ? null : (int)$ArtistsAdded) - ) + 'username' => $Username, + 'avatar' => $Avatar, + 'isFriend' => $Friend, + 'profileText' => Text::full_format($Info), + 'stats' => array( + 'joinedDate' => $JoinDate, + 'lastAccess' => $LastAccess, + 'uploaded' => (($Uploaded == null) ? null : (int)$Uploaded), + 'downloaded' => (($Downloaded == null) ? null : (int)$Downloaded), + 'ratio' => $Ratio, + 'requiredRatio' => (($RequiredRatio == null) ? null : (float)$RequiredRatio) + ), + 'ranks' => array( + 'uploaded' => $UploadedRank, + 'downloaded' => $DownloadedRank, + 'uploads' => $UploadsRank, + 'requests' => $RequestRank, + 'bounty' => $BountyRank, + 'posts' => $PostRank, + 'artists' => $ArtistsRank, + 'overall' => (($OverallRank == null) ? 0 : $OverallRank) + ), + 'personal' => array( + 'class' => $ClassLevels[$Class]['Name'], + 'paranoia' => $ParanoiaLevel, + 'paranoiaText' => $ParanoiaLevelText, + 'donor' => ($Donor == 1), + 'warned' => ($Warned != '0000-00-00 00:00:00'), + 'enabled' => ($Enabled == '1' || $Enabled == '0' || !$Enabled), + 'passkey' => $torrent_pass + ), + 'community' => array( + 'posts' => (int)$ForumPosts, + 'torrentComments' => (($NumComments == null) ? null : (int)$NumComments), + 'artistComments' => (($NumArtistComments == null) ? null : (int)$NumArtistComments), + 'collageComments' => (($NumCollageComments == null) ? null : (int)$NumCollageComments), + 'requestComments' => (($NumRequestComments == null) ? null : (int)$NumRequestComments), + 'collagesStarted' => (($NumCollages == null) ? null : (int)$NumCollages), + 'collagesContrib' => (($NumCollageContribs == null) ? null : (int)$NumCollageContribs), + 'requestsFilled' => (($RequestsFilled == null) ? null : (int)$RequestsFilled), + 'bountyEarned' => (($TotalBounty == null) ? null : (int)$TotalBounty), + 'requestsVoted' => (($RequestsVoted == null) ? null : (int)$RequestsVoted), + 'bountySpent' => (($TotalSpent == null) ? null : (int)$TotalSpent), + 'perfectFlacs' => (($PerfectFLACs == null) ? null : (int)$PerfectFLACs), + 'uploaded' => (($Uploads == null) ? null : (int)$Uploads), + 'groups' => (($UniqueGroups == null) ? null : (int)$UniqueGroups), + 'seeding' => (($Seeding == null) ? null : (int)$Seeding), + 'leeching' => (($Leeching == null) ? null : (int)$Leeching), + 'snatched' => (($Snatched == null) ? null : (int)$Snatched), + 'invited' => (($Invited == null) ? null : (int)$Invited), + 'artistsAdded' => (($ArtistsAdded == null) ? null : (int)$ArtistsAdded) + ) )); ?> diff --git a/sections/ajax/user_recents.php b/sections/ajax/user_recents.php index fc22b9798..15f266d5b 100644 --- a/sections/ajax/user_recents.php +++ b/sections/ajax/user_recents.php @@ -1,74 +1,74 @@ - 50) { - json_die("failure", "bad parameters"); + json_die("failure", "bad parameters"); } if (empty($Limit)) { - $Limit = 15; + $Limit = 15; } -$Results = array(); +$Results = []; if (check_paranoia_here('snatched')) { - $DB->query(" - SELECT - g.ID, - g.Name, - g.WikiImage - FROM xbt_snatched AS s - INNER JOIN torrents AS t ON t.ID = s.fid - INNER JOIN torrents_group AS g ON t.GroupID = g.ID - WHERE s.uid = '$UserID' - AND g.CategoryID = '1' - AND g.WikiImage != '' - GROUP BY g.ID - ORDER BY s.tstamp DESC - LIMIT $Limit"); - $RecentSnatches = $DB->to_array(false, MYSQLI_ASSOC); - $Artists = Artists::get_artists($DB->collect('ID')); - foreach ($RecentSnatches as $Key => $SnatchInfo) { - $RecentSnatches[$Key]['artists'][] = $Artists[$SnatchInfo['ID']]; - $RecentSnatches[$Key]['ID'] = (int)$RecentSnatches[$Key]['ID']; + $DB->query(" + SELECT + g.ID, + g.Name, + g.WikiImage + FROM xbt_snatched AS s + INNER JOIN torrents AS t ON t.ID = s.fid + INNER JOIN torrents_group AS g ON t.GroupID = g.ID + WHERE s.uid = '$UserID' + AND g.CategoryID = '1' + AND g.WikiImage != '' + GROUP BY g.ID + ORDER BY s.tstamp DESC + LIMIT $Limit"); + $RecentSnatches = $DB->to_array(false, MYSQLI_ASSOC); + $Artists = Artists::get_artists($DB->collect('ID')); + foreach ($RecentSnatches as $Key => $SnatchInfo) { + $RecentSnatches[$Key]['artists'][] = $Artists[$SnatchInfo['ID']]; + $RecentSnatches[$Key]['ID'] = (int)$RecentSnatches[$Key]['ID']; - } - $Results['snatches'] = $RecentSnatches; + } + $Results['snatches'] = $RecentSnatches; } else { - $Results['snatches'] = "hidden"; + $Results['snatches'] = "hidden"; } if (check_paranoia_here('uploads')) { - $DB->query(" - SELECT - g.ID, - g.Name, - g.WikiImage - FROM torrents_group AS g - INNER JOIN torrents AS t ON t.GroupID = g.ID - WHERE t.UserID = '$UserID' - AND g.CategoryID = '1' - AND g.WikiImage != '' - GROUP BY g.ID - ORDER BY t.Time DESC - LIMIT $Limit"); - $RecentUploads = $DB->to_array(false, MYSQLI_ASSOC); - $Artists = Artists::get_artists($DB->collect('ID')); - foreach ($RecentUploads as $Key => $UploadInfo) { - $RecentUploads[$Key]['artists'][] = $Artists[$UploadInfo['ID']]; - $RecentUploads[$Key]['ID'] = (int)$RecentUploads[$Key]['ID']; + $DB->query(" + SELECT + g.ID, + g.Name, + g.WikiImage + FROM torrents_group AS g + INNER JOIN torrents AS t ON t.GroupID = g.ID + WHERE t.UserID = '$UserID' + AND g.CategoryID = '1' + AND g.WikiImage != '' + GROUP BY g.ID + ORDER BY t.Time DESC + LIMIT $Limit"); + $RecentUploads = $DB->to_array(false, MYSQLI_ASSOC); + $Artists = Artists::get_artists($DB->collect('ID')); + foreach ($RecentUploads as $Key => $UploadInfo) { + $RecentUploads[$Key]['artists'][] = $Artists[$UploadInfo['ID']]; + $RecentUploads[$Key]['ID'] = (int)$RecentUploads[$Key]['ID']; - } - $Results['uploads'] = $RecentUploads; + } + $Results['uploads'] = $RecentUploads; } else { - $Results['uploads'] = "hidden"; + $Results['uploads'] = "hidden"; } json_print("success", $Results); function check_paranoia_here($Setting) { - global $Paranoia, $Class, $UserID, $Preview; - if ($Preview == 1) { - return check_paranoia($Setting, $Paranoia, $Class); - } else { - return check_paranoia($Setting, $Paranoia, $Class, $UserID); - } + global $Paranoia, $Class, $UserID, $Preview; + if ($Preview == 1) { + return check_paranoia($Setting, $Paranoia, $Class); + } else { + return check_paranoia($Setting, $Paranoia, $Class, $UserID); + } } diff --git a/sections/ajax/userhistory/index.php b/sections/ajax/userhistory/index.php index 6d4e6a55f..68e0755b9 100644 --- a/sections/ajax/userhistory/index.php +++ b/sections/ajax/userhistory/index.php @@ -1,20 +1,20 @@ - 'failure') - ); - } + switch ($_GET['type']) { + case 'posts': + // Load post history page + include('post_history.php'); + break; + default: + print json_encode( + array('status' => 'failure') + ); + } } else { - print json_encode( - array('status' => 'failure') - ); + print json_encode( + array('status' => 'failure') + ); } ?> diff --git a/sections/ajax/userhistory/post_history.php b/sections/ajax/userhistory/post_history.php index 124cfb805..17c4cc198 100644 --- a/sections/ajax/userhistory/post_history.php +++ b/sections/ajax/userhistory/post_history.php @@ -4,26 +4,26 @@ */ function error_out($reason = '') { - $error = array('status' => 'failure'); - if ($reason !== '') - $error['reason'] = $reason; - print $error; - die(); + $error = array('status' => 'failure'); + if ($reason !== '') + $error['reason'] = $reason; + print $error; + die(); } if (!empty($LoggedUser['DisableForums'])) { - error_out('You do not have access to the forums!'); + error_out('You do not have access to the forums!'); } $UserID = empty($_GET['userid']) ? $LoggedUser['ID'] : $_GET['userid']; if (!is_number($UserID)) { - error_out('User does not exist!'); + error_out('User does not exist!'); } if (isset($LoggedUser['PostsPerPage'])) { - $PerPage = $LoggedUser['PostsPerPage']; + $PerPage = $LoggedUser['PostsPerPage']; } else { - $PerPage = POSTS_PER_PAGE; + $PerPage = POSTS_PER_PAGE; } list($Page, $Limit) = Format::page_limit($PerPage); @@ -35,151 +35,151 @@ function error_out($reason = '') { $ShowUnread = ($ViewingOwn && (!isset($_GET['showunread']) || !!$_GET['showunread'])); $ShowGrouped = ($ViewingOwn && (!isset($_GET['group']) || !!$_GET['group'])); if ($ShowGrouped) { - $SQL = ' - SELECT - SQL_CALC_FOUND_ROWS - MAX(p.ID) AS ID - FROM forums_posts AS p - LEFT JOIN forums_topics AS t ON t.ID = p.TopicID'; - if ($ShowUnread) { - $SQL .= ' - LEFT JOIN forums_last_read_topics AS l ON l.TopicID = t.ID AND l.UserID = '.$LoggedUser['ID']; - } - $SQL .= ' - LEFT JOIN forums AS f ON f.ID = t.ForumID - WHERE p.AuthorID = '.$UserID.' - AND ' . Forums::user_forums_sql(); - if ($ShowUnread) { - $SQL .= ' - AND ((t.IsLocked = \'0\' OR t.IsSticky = \'1\') - AND (l.PostID < t.LastPostID OR l.PostID IS NULL))'; - } - $SQL .= " - GROUP BY t.ID - ORDER BY p.ID DESC - LIMIT $Limit"; - $PostIDs = $DB->query($SQL); - $DB->query('SELECT FOUND_ROWS()'); - list($Results) = $DB->next_record(); - - if ($Results > $PerPage * ($Page - 1)) { - $DB->set_query_id($PostIDs); - $PostIDs = $DB->collect('ID'); - $SQL = " - SELECT - p.ID, - p.AddedTime, - p.Body, - p.EditedUserID, - p.EditedTime, - ed.Username, - p.TopicID, - t.Title, - t.LastPostID, - l.PostID AS LastRead, - t.IsLocked, - t.IsSticky - FROM forums_posts AS p - LEFT JOIN users_main AS um ON um.ID = p.AuthorID - LEFT JOIN users_info AS ui ON ui.UserID = p.AuthorID - LEFT JOIN users_main AS ed ON ed.ID = p.EditedUserID - JOIN forums_topics AS t ON t.ID = p.TopicID - JOIN forums AS f ON f.ID = t.ForumID - LEFT JOIN forums_last_read_topics AS l ON l.UserID = $UserID AND l.TopicID = t.ID - WHERE p.ID IN (".implode(',', $PostIDs).') - ORDER BY p.ID DESC'; - $Posts = $DB->query($SQL); - } + $SQL = ' + SELECT + SQL_CALC_FOUND_ROWS + MAX(p.ID) AS ID + FROM forums_posts AS p + LEFT JOIN forums_topics AS t ON t.ID = p.TopicID'; + if ($ShowUnread) { + $SQL .= ' + LEFT JOIN forums_last_read_topics AS l ON l.TopicID = t.ID AND l.UserID = '.$LoggedUser['ID']; + } + $SQL .= ' + LEFT JOIN forums AS f ON f.ID = t.ForumID + WHERE p.AuthorID = '.$UserID.' + AND ' . Forums::user_forums_sql(); + if ($ShowUnread) { + $SQL .= ' + AND ((t.IsLocked = \'0\' OR t.IsSticky = \'1\') + AND (l.PostID < t.LastPostID OR l.PostID IS NULL))'; + } + $SQL .= " + GROUP BY t.ID + ORDER BY p.ID DESC + LIMIT $Limit"; + $PostIDs = $DB->query($SQL); + $DB->query('SELECT FOUND_ROWS()'); + list($Results) = $DB->next_record(); + + if ($Results > $PerPage * ($Page - 1)) { + $DB->set_query_id($PostIDs); + $PostIDs = $DB->collect('ID'); + $SQL = " + SELECT + p.ID, + p.AddedTime, + p.Body, + p.EditedUserID, + p.EditedTime, + ed.Username, + p.TopicID, + t.Title, + t.LastPostID, + l.PostID AS LastRead, + t.IsLocked, + t.IsSticky + FROM forums_posts AS p + LEFT JOIN users_main AS um ON um.ID = p.AuthorID + LEFT JOIN users_info AS ui ON ui.UserID = p.AuthorID + LEFT JOIN users_main AS ed ON ed.ID = p.EditedUserID + JOIN forums_topics AS t ON t.ID = p.TopicID + JOIN forums AS f ON f.ID = t.ForumID + LEFT JOIN forums_last_read_topics AS l ON l.UserID = $UserID AND l.TopicID = t.ID + WHERE p.ID IN (".implode(',', $PostIDs).') + ORDER BY p.ID DESC'; + $Posts = $DB->query($SQL); + } } else { - $SQL = ' - SELECT - SQL_CALC_FOUND_ROWS'; - if ($ShowGrouped) { - $SQL .= ' - * - FROM ( - SELECT'; - } - $SQL .= ' - p.ID, - p.AddedTime, - p.Body, - p.EditedUserID, - p.EditedTime, - ed.Username, - p.TopicID, - t.Title, - t.LastPostID,'; - if ($UserID === $LoggedUser['ID']) { - $SQL .= ' - l.PostID AS LastRead,'; - } - $SQL .= " - t.IsLocked, - t.IsSticky - FROM forums_posts AS p - LEFT JOIN users_main AS um ON um.ID = p.AuthorID - LEFT JOIN users_info AS ui ON ui.UserID = p.AuthorID - LEFT JOIN users_main AS ed ON ed.ID = p.EditedUserID - JOIN forums_topics AS t ON t.ID = p.TopicID - JOIN forums AS f ON f.ID = t.ForumID - LEFT JOIN forums_last_read_topics AS l ON l.UserID = $UserID AND l.TopicID = t.ID - WHERE p.AuthorID = $UserID - AND " . Forums::user_forums_sql(); - - if ($ShowUnread) { - $SQL .= ' - AND ((t.IsLocked = \'0\' OR t.IsSticky = \'1\') - AND (l.PostID < t.LastPostID OR l.PostID IS NULL) - ) '; - } - - $SQL .= ' - ORDER BY p.ID DESC'; - - if ($ShowGrouped) { - $SQL .= ' - ) AS sub - GROUP BY TopicID - ORDER BY ID DESC'; - } - - $SQL .= " - LIMIT $Limit"; - $Posts = $DB->query($SQL); - - $DB->query('SELECT FOUND_ROWS()'); - list($Results) = $DB->next_record(); - - $DB->set_query_id($Posts); + $SQL = ' + SELECT + SQL_CALC_FOUND_ROWS'; + if ($ShowGrouped) { + $SQL .= ' + * + FROM ( + SELECT'; + } + $SQL .= ' + p.ID, + p.AddedTime, + p.Body, + p.EditedUserID, + p.EditedTime, + ed.Username, + p.TopicID, + t.Title, + t.LastPostID,'; + if ($UserID === $LoggedUser['ID']) { + $SQL .= ' + l.PostID AS LastRead,'; + } + $SQL .= " + t.IsLocked, + t.IsSticky + FROM forums_posts AS p + LEFT JOIN users_main AS um ON um.ID = p.AuthorID + LEFT JOIN users_info AS ui ON ui.UserID = p.AuthorID + LEFT JOIN users_main AS ed ON ed.ID = p.EditedUserID + JOIN forums_topics AS t ON t.ID = p.TopicID + JOIN forums AS f ON f.ID = t.ForumID + LEFT JOIN forums_last_read_topics AS l ON l.UserID = $UserID AND l.TopicID = t.ID + WHERE p.AuthorID = $UserID + AND " . Forums::user_forums_sql(); + + if ($ShowUnread) { + $SQL .= ' + AND ((t.IsLocked = \'0\' OR t.IsSticky = \'1\') + AND (l.PostID < t.LastPostID OR l.PostID IS NULL) + ) '; + } + + $SQL .= ' + ORDER BY p.ID DESC'; + + if ($ShowGrouped) { + $SQL .= ' + ) AS sub + GROUP BY TopicID + ORDER BY ID DESC'; + } + + $SQL .= " + LIMIT $Limit"; + $Posts = $DB->query($SQL); + + $DB->query('SELECT FOUND_ROWS()'); + list($Results) = $DB->next_record(); + + $DB->set_query_id($Posts); } -$JsonResults = array(); +$JsonResults = []; while (list($PostID, $AddedTime, $Body, $EditedUserID, $EditedTime, $EditedUsername, $TopicID, $ThreadTitle, $LastPostID, $LastRead, $Locked, $Sticky) = $DB->next_record()) { - $JsonResults[] = array( - 'postId' => (int)$PostID, - 'topicId' => (int)$TopicID, - 'threadTitle' => $ThreadTitle, - 'lastPostId' => (int)$LastPostID, - 'lastRead' => (int)$LastRead, - 'locked' => $Locked === '1', - 'sticky' => $Sticky === '1', - 'addedTime' => $AddedTime, - 'body' => Text::full_format($Body), - 'bbbody' => $Body, - 'editedUserId' => (int)$EditedUserID, - 'editedTime' => $EditedTime, - 'editedUsername' => $EditedUsername - ); + $JsonResults[] = array( + 'postId' => (int)$PostID, + 'topicId' => (int)$TopicID, + 'threadTitle' => $ThreadTitle, + 'lastPostId' => (int)$LastPostID, + 'lastRead' => (int)$LastRead, + 'locked' => $Locked === '1', + 'sticky' => $Sticky === '1', + 'addedTime' => $AddedTime, + 'body' => Text::full_format($Body), + 'bbbody' => $Body, + 'editedUserId' => (int)$EditedUserID, + 'editedTime' => $EditedTime, + 'editedUsername' => $EditedUsername + ); } print json_encode( - array( - 'status' => 'success', - 'response' => array( - 'currentPage' => (int)$Page, - 'pages' => ceil($Results / $PerPage), - 'threads' => $JsonResults - ) - ) - ); + array( + 'status' => 'success', + 'response' => array( + 'currentPage' => (int)$Page, + 'pages' => ceil($Results / $PerPage), + 'threads' => $JsonResults + ) + ) + ); diff --git a/sections/ajax/usersearch.php b/sections/ajax/usersearch.php index 249f992fe..c92677aa8 100644 --- a/sections/ajax/usersearch.php +++ b/sections/ajax/usersearch.php @@ -4,55 +4,55 @@ **********************************************************************/ if (empty($_GET['search'])) { - json_die("failure", "no search terms"); + json_die("failure", "no search terms"); } else { - $_GET['username'] = $_GET['search']; + $_GET['username'] = $_GET['search']; } define('USERS_PER_PAGE', 30); if (isset($_GET['username'])) { - $_GET['username'] = trim($_GET['username']); - - list($Page, $Limit) = Format::page_limit(USERS_PER_PAGE); - $DB->query(" - SELECT - SQL_CALC_FOUND_ROWS - ID, - Username, - Enabled, - PermissionID, - Donor, - Warned, - Avatar - FROM users_main AS um - JOIN users_info AS ui ON ui.UserID = um.ID - WHERE Username LIKE '%".db_string($_GET['username'])."%' - ORDER BY Username - LIMIT $Limit"); - $Results = $DB->to_array(); - $DB->query('SELECT FOUND_ROWS();'); - list($NumResults) = $DB->next_record(); + $_GET['username'] = trim($_GET['username']); + + list($Page, $Limit) = Format::page_limit(USERS_PER_PAGE); + $DB->query(" + SELECT + SQL_CALC_FOUND_ROWS + ID, + Username, + Enabled, + PermissionID, + Donor, + Warned, + Avatar + FROM users_main AS um + JOIN users_info AS ui ON ui.UserID = um.ID + WHERE Username LIKE '%".db_string($_GET['username'])."%' + ORDER BY Username + LIMIT $Limit"); + $Results = $DB->to_array(); + $DB->query('SELECT FOUND_ROWS();'); + list($NumResults) = $DB->next_record(); } -$JsonUsers = array(); +$JsonUsers = []; foreach ($Results as $Result) { - list($UserID, $Username, $Enabled, $PermissionID, $Donor, $Warned, $Avatar) = $Result; - - $JsonUsers[] = array( - 'userId' => (int)$UserID, - 'username' => $Username, - 'donor' => $Donor == 1, - 'warned' => ($Warned != '0000-00-00 00:00:00'), - 'enabled' => ($Enabled == 2 ? false : true), - 'class' => Users::make_class_string($PermissionID), - 'avatar' => $Avatar - ); + list($UserID, $Username, $Enabled, $PermissionID, $Donor, $Warned, $Avatar) = $Result; + + $JsonUsers[] = array( + 'userId' => (int)$UserID, + 'username' => $Username, + 'donor' => $Donor == 1, + 'warned' => ($Warned != '0000-00-00 00:00:00'), + 'enabled' => ($Enabled == 2 ? false : true), + 'class' => Users::make_class_string($PermissionID), + 'avatar' => $Avatar + ); } json_print("success", array( - 'currentPage' => (int)$Page, - 'pages' => ceil($NumResults / USERS_PER_PAGE), - 'results' => $JsonUsers + 'currentPage' => (int)$Page, + 'pages' => ceil($NumResults / USERS_PER_PAGE), + 'results' => $JsonUsers )); diff --git a/sections/ajax/wiki.php b/sections/ajax/wiki.php index 9c3b1d5e6..99d055b26 100644 --- a/sections/ajax/wiki.php +++ b/sections/ajax/wiki.php @@ -1,35 +1,35 @@ - $LoggedUser['EffectiveClass']) { - json_die("failure", "higher user class required to view article"); + json_die("failure", "higher user class required to view article"); } Text::$TOC = true; $TextBody = Text::full_format($Body, false); json_print("success", array( - 'title' => $Title, - 'bbBody' => $Body, - 'body' => $TextBody, - 'aliases' => $Aliases, - 'authorID' => (int)$AuthorID, - 'authorName' => $AuthorName, - 'date' => $Date, - 'revision' => (int)$Revision + 'title' => $Title, + 'bbBody' => $Body, + 'body' => $TextBody, + 'aliases' => $Aliases, + 'authorID' => (int)$AuthorID, + 'authorName' => $AuthorName, + 'date' => $Date, + 'revision' => (int)$Revision )); diff --git a/sections/api/index.php b/sections/api/index.php new file mode 100644 index 000000000..f9e3d7303 --- /dev/null +++ b/sections/api/index.php @@ -0,0 +1,55 @@ + $Categories, 'CollageCats' => $CollageCats, + 'ReleaseTypes' => $ReleaseTypes, 'Debug' => $Debug); + $class = getClassObject($_GET['action'], $DB, $Cache, $Twig, $config); +} else { + json_error('invalid action'); +} + +if (empty($_GET['aid']) || empty($_GET['token'])) { + json_error('invalid parameters'); +} + +$app_id = intval($_GET['aid']); +$token = $_GET['token']; + +$app = $Cache->get_value("api_apps_{$app_id}"); +if (!is_array($app)) { + $DB->prepared_query(" + SELECT Token, Name + FROM api_applications + WHERE ID = ? + LIMIT 1", $app_id); + if ($DB->record_count() === 0) { + json_error('invalid app'); + } + $app = $DB->to_array(false, MYSQLI_ASSOC); + $Cache->cache_value("api_apps_{$app_id}", $app, 0); +} +$app = $app[0]; + +if ($app['Token'] !== $token) { + json_error('invalid token'); +} + +$response = $class->run(); +print(json_encode(array('status' => 200, 'response' => $response), JSON_UNESCAPED_SLASHES)); +//$Debug->profile(); diff --git a/sections/apply/admin.php b/sections/apply/admin.php index cc9f9bbee..50c4eb504 100644 --- a/sections/apply/admin.php +++ b/sections/apply/admin.php @@ -1,51 +1,51 @@ -update( - $_POST['title'], - $_POST['description'], - (isset($_POST['status']) && is_numeric($_POST['status']) && $_POST['status'] == 1) - ); - } - $EDIT_ID = 0; /* return to list */ - $Saved = 'updated'; - } - } - else { - $AppRole = new ApplicantRole( - $_POST['title'], - $_POST['description'], - (isset($_POST['status']) && is_numeric($_POST['status']) && $_POST['status'] == 1), - $LoggedUser['ID'] - ); - $Saved = 'saved'; - } + authorize(); + $edit = array_filter($_POST, function ($x) { return preg_match('/^edit-\d+$/', $x);}, ARRAY_FILTER_USE_KEY); + if (is_array($edit) && count($edit) == 1) { + $EDIT_ID = trim(array_keys($edit)[0], 'edit-'); + $AppRole = ApplicantRole::factory($EDIT_ID); + } + elseif (isset($_POST['edit']) && is_numeric($_POST['edit'])) { + $EDIT_ID = intval($_POST['edit']); + $AppRole = ApplicantRole::factory($EDIT_ID); + if (isset($_POST['user_id']) && is_numeric($_POST['user_id'])) { + $user_id = intval($_POST['user_id']); + if ($user_id == $LoggedUser['ID']) { + $AppRole->update( + $_POST['title'], + $_POST['description'], + (isset($_POST['status']) && is_numeric($_POST['status']) && $_POST['status'] == 1) + ); + } + $EDIT_ID = 0; /* return to list */ + $Saved = 'updated'; + } + } + else { + $AppRole = new ApplicantRole( + $_POST['title'], + $_POST['description'], + (isset($_POST['status']) && is_numeric($_POST['status']) && $_POST['status'] == 1), + $LoggedUser['ID'] + ); + $Saved = 'saved'; + } } ?>

    Manage roles at

    @@ -53,94 +53,101 @@
    -
    Current Roles
    -
    - -

    The role title() ?> was .

    - +
    Current Roles
    +
    + +

    The role title() ?> was .

    + - - - $info) { ?> - - - - - - - -
    -
    -
    - -
    - () -
    Role created by - . -
    -
    - -

    There are no current roles. Create one using the form below.

    - + $info) { ?> +
    +
    +
    + +
    + () +
    Role created by + . +
    +
    + +

    There are no current roles. Create one using the form below.

    + -
    +
    -
    Role
    -
    +
    Role
    +
    -is_published() ? ' checked' : ''; - $checked_archived = $AppRole->is_published() ? '' : ' checked'; + $checked_published = $AppRole->is_published() ? ' checked' : ''; + $checked_archived = $AppRole->is_published() ? '' : ' checked'; } else { - $checked_published = ''; - $checked_archived = ' checked'; + $checked_published = ''; + $checked_archived = ' checked'; } ?> - - - - - - - - - - - - + +
    Title
    Visibility - />
    - /> -
    Description -description() : '', 60, 8, true, false); - $id = $text->getID(); - echo $text->preview(); + + + + + + + + + + + + - -
    Title
    Visibility + />
    + /> +
    Description +description() : '', 60, 8, true, false); + $id = $text->getID(); + echo $text->preview(); ?> - - - - - - - -
    - + + + + + + + +
    +
    - 80) { - $Applicant = new Applicant($LoggedUser['ID'], $Role, $Body); - header('Location: /apply.php?action=view&id=' . $Applicant->id()); - exit; - } - else { - $Error = "You need to explain things a bit more."; - } - } - else { - $Error = "You need to choose which role interests you."; - } + if (strlen($Role)) { + if (strlen($Body) > 80) { + $Applicant = new Applicant($LoggedUser['ID'], $Role, $Body); + header('Location: /apply.php?action=view&id=' . $Applicant->id()); + exit; + } + else { + $Error = "You need to explain things a bit more."; + } + } + else { + $Error = "You need to choose which role interests you."; + } } else { - $Role = ''; - $Body = ''; + $Role = ''; + $Body = ''; } View::show_header('Apply', 'apply'); ?>
    -
    -

    Apply for a role at

    - - - -
    +
    +

    Apply for a role at

    + + + +
    -
    -
    Open Roles
    -
    - - $info) { ?> - - - - - - - -
    -
    -
    - +
    +
    Open Roles
    +
    + + $info) { ?> + + + + + + + +
    +
    +
    + - -
    -

    Thanks for your interest in helping Orpheus! There are - no openings at the moment. Keep an eye on the front page - or the Orpheus forum for announcements in the future.

    -
    - +
    +

    Thanks for your interest in helping Orpheus! There are + no openings at the moment. Keep an eye on the front page + or the Orpheus forum for announcements in the future.

    +
    + -
    -
    + -
    -
    -
    -
    Your Role at
    -
    -
    Choose a role from the following list:
    - -
    -
    Your cover letter
    -
    At least 80 characters, now convince us! -preview(); + +
    +
    +
    Your Role at
    +
    +
    Choose a role from the following list:
    + +
    +
    Your cover letter
    +
    At least 80 characters, now convince us! +preview(); ?> -
    -
    +
    +
    -
    - - - -
    -
    - - +
    + + + +
    +
    + + -user_id() != $LoggedUser['ID']) { - error(403); - } - $note_delete = array_filter($_POST, function ($x) { return preg_match('/^note-delete-\d+$/', $x);}, ARRAY_FILTER_USE_KEY); - if (is_array($note_delete) && count($note_delete) == 1) { - $App->delete_note( - trim(array_keys($note_delete)[0], 'note-delete-') - ); - } - elseif (isset($_POST['resolve'])) { - if ($_POST['resolve'] === 'Resolve') { - $App->resolve(true); - } - elseif ($_POST['resolve'] === 'Reopen') { - $App->resolve(false); - } - } - elseif (isset($_POST['note_reply'])) { - $App->save_note( - $LoggedUser['ID'], - $_POST['note_reply'], - $IS_STAFF && $_POST['visibility'] == 'staff' ? 'staff' : 'public' - ); - } + authorize(); + $ID = intval($_POST['id']); + $App = Applicant::factory($ID); + if (!$IS_STAFF && $App->user_id() != $LoggedUser['ID']) { + error(403); + } + $note_delete = array_filter($_POST, function ($x) { return preg_match('/^note-delete-\d+$/', $x);}, ARRAY_FILTER_USE_KEY); + if (is_array($note_delete) && count($note_delete) == 1) { + $App->delete_note( + trim(array_keys($note_delete)[0], 'note-delete-') + ); + } + elseif (isset($_POST['resolve'])) { + if ($_POST['resolve'] === 'Resolve') { + $App->resolve(true); + } + elseif ($_POST['resolve'] === 'Reopen') { + $App->resolve(false); + } + } + elseif (isset($_POST['note_reply'])) { + $App->save_note( + $LoggedUser['ID'], + $_POST['note_reply'], + $IS_STAFF && $_POST['visibility'] == 'staff' ? 'staff' : 'public' + ); + } } elseif (isset($_GET['id']) && is_number($_GET['id'])) { - $ID = intval($_GET['id']); - $App = Applicant::factory($ID); - if (!$IS_STAFF && $App->user_id() != $LoggedUser['ID']) { - error(403); - } + $ID = intval($_GET['id']); + $App = Applicant::factory($ID); + if (!$IS_STAFF && $App->user_id() != $LoggedUser['ID']) { + error(403); + } } $Resolved = (isset($_GET['status']) && $_GET['status'] === 'resolved'); ?> @@ -43,154 +43,163 @@
    - +
    -
    is_resolved() ? ' style="font-style: italic;"' : '' ?>>role_title() ?> - -
    -
    - - - -
    -
    -
    Application received from user_id(), true, true, true, true, true, false) ?> received created(), 2) ?>. - -
    +
    is_resolved() ? ' style="font-style: italic;"' : '' ?>>role_title() ?> + +
    +
    + + + +
    +
    +
    Application received from user_id(), true, true, true, true, true, false) ?> received created(), 2) ?>. + +
    -
    -

    body()) ?>

    -is_resolved()) { ?> -
    - - -get_story() as $note) { - if (!$IS_STAFF && $note['visibility'] == 'staff') { - continue; - } +
    +

    body()) ?>

    +is_resolved()) { ?> + + +
    +get_story() as $note) { + if (!$IS_STAFF && $note['visibility'] == 'staff') { + continue; + } ?> - - - - - - -is_resolved()) { - if ($IS_STAFF) { + + + + + + +is_resolved()) { + if ($IS_STAFF) { ?> - - - - - - - + + + + + + - - - - -is_resolved() */ ?> -
    -
    - -
    -
    -
    - -
    -is_resolved()) { ?> -
    - -
    - -
    +
    + -
    +
    +
    + +
    +is_resolved()) { ?> +
    + +
    + +
    Visibility -
    -
    -
    -
    -
    -
    Reply -preview(); +
    Visibility +
    +
    +
    +
    +
    +
    Reply +preview(); ?> -
    -
    - - - - -
    -
    -
    -
    + + + + +
    + + + + +
    + + +is_resolved() */ ?> + + +
    - -

    Applications

    - - - - - - - - - - - - - - - - - - - - - - - - -
    RoleApplicantDate CreatedCommentsLast comment fromLast comment added
    - 0 */ - else { +

    Applications

    + + + + + + + + + + + + + + + + + + + + + + + + +
    RoleApplicantDate CreatedCommentsLast comment fromLast comment added
    + 0 */ + else { ?>
    The cupboard is empty. There are no applications to show.
    - -query(" - SELECT AliasID, ArtistID, Name, Redirect - FROM artists_alias - WHERE Name = '$DBAliasName'"); + SELECT AliasID, ArtistID, Name, Redirect + FROM artists_alias + WHERE Name = '$DBAliasName'"); if ($DB->has_results()) { - while (list($CloneAliasID, $CloneArtistID, $CloneAliasName, $CloneRedirect) = $DB->next_record(MYSQLI_NUM, false)) { - if (!strcasecmp($CloneAliasName, $AliasName)) { - break; - } - } - if ($CloneAliasID) { - if ($ArtistID == $CloneArtistID && $Redirect == 0) { - if ($CloneRedirect != 0) { - $DB->query(" - UPDATE artists_alias - SET ArtistID = '$ArtistID', Redirect = 0 - WHERE AliasID = '$CloneAliasID'"); - Misc::write_log("Redirection for the alias $CloneAliasID ($DBAliasName) for the artist $ArtistID was removed by user $LoggedUser[ID] ($LoggedUser[Username])"); - } else { - error('No changes were made as the target alias did not redirect anywhere.'); - } - } else { - error('An alias by that name already exists here. You can try renaming that artist to this one.'); - } - } + while (list($CloneAliasID, $CloneArtistID, $CloneAliasName, $CloneRedirect) = $DB->next_record(MYSQLI_NUM, false)) { + if (!strcasecmp($CloneAliasName, $AliasName)) { + break; + } + } + if ($CloneAliasID) { + if ($ArtistID == $CloneArtistID && $Redirect == 0) { + if ($CloneRedirect != 0) { + $DB->query(" + UPDATE artists_alias + SET ArtistID = '$ArtistID', Redirect = 0 + WHERE AliasID = '$CloneAliasID'"); + Misc::write_log("Redirection for the alias $CloneAliasID ($DBAliasName) for the artist $ArtistID was removed by user $LoggedUser[ID] ($LoggedUser[Username])"); + } else { + error('No changes were made as the target alias did not redirect anywhere.'); + } + } else { + error('An alias by that name already exists here. You can try renaming that artist to this one.'); + } + } } if (!$CloneAliasID) { - if ($Redirect) { - $DB->query(" - SELECT ArtistID, Redirect - FROM artists_alias - WHERE AliasID = $Redirect"); - if (!$DB->has_results()) { - error('Cannot redirect to a nonexistent artist alias.'); - } - list($FoundArtistID, $FoundRedirect) = $DB->next_record(); - if ($ArtistID != $FoundArtistID) { - error('Redirection must target an alias for the current artist.'); - } - if ($FoundRedirect != 0) { - $Redirect = $FoundRedirect; - } - } - $DB->query(" - INSERT INTO artists_alias - (ArtistID, Name, Redirect, UserID) - VALUES - ($ArtistID, '$DBAliasName', $Redirect, ".$LoggedUser['ID'].')'); - $AliasID = $DB->inserted_id(); + if ($Redirect) { + $DB->query(" + SELECT ArtistID, Redirect + FROM artists_alias + WHERE AliasID = $Redirect"); + if (!$DB->has_results()) { + error('Cannot redirect to a nonexistent artist alias.'); + } + list($FoundArtistID, $FoundRedirect) = $DB->next_record(); + if ($ArtistID != $FoundArtistID) { + error('Redirection must target an alias for the current artist.'); + } + if ($FoundRedirect != 0) { + $Redirect = $FoundRedirect; + } + } + $DB->query(" + INSERT INTO artists_alias + (ArtistID, Name, Redirect, UserID) + VALUES + ($ArtistID, '$DBAliasName', $Redirect, ".$LoggedUser['ID'].')'); + $AliasID = $DB->inserted_id(); - $DB->query(" - SELECT Name - FROM artists_group - WHERE ArtistID = $ArtistID"); - list($ArtistName) = $DB->next_record(MYSQLI_NUM, false); + $DB->query(" + SELECT Name + FROM artists_group + WHERE ArtistID = $ArtistID"); + list($ArtistName) = $DB->next_record(MYSQLI_NUM, false); - Misc::write_log("The alias $AliasID ($DBAliasName) was added to the artist $ArtistID (".db_string($ArtistName).') by user '.$LoggedUser['ID'].' ('.$LoggedUser['Username'].')'); + Misc::write_log("The alias $AliasID ($DBAliasName) was added to the artist $ArtistID (".db_string($ArtistName).') by user '.$LoggedUser['ID'].' ('.$LoggedUser['Username'].')'); } $Location = (empty($_SERVER['HTTP_REFERER'])) ? "artist.php?action=edit&artistid={$ArtistID}" : $_SERVER['HTTP_REFERER']; diff --git a/sections/artist/add_similar.php b/sections/artist/add_similar.php index cc0341928..70e631ded 100644 --- a/sections/artist/add_similar.php +++ b/sections/artist/add_similar.php @@ -6,64 +6,64 @@ $Artist2Name = db_string($_POST['artistname']); if (!is_number($Artist1ID)) { - error(0); + error(0); } if (empty($Artist2Name)) { - error('Blank artist name.'); + error('Blank artist name.'); } $DB->query(" - SELECT ArtistID - FROM artists_group - WHERE Name LIKE '$Artist2Name'"); + SELECT ArtistID + FROM artists_group + WHERE Name LIKE '$Artist2Name'"); list($Artist2ID) = $DB->next_record(); if (!empty($Artist2ID)) { // artist was found in the database - // Let's see if there's already a similar artists field for these two - $DB->query(" - SELECT s1.SimilarID - FROM artists_similar AS s1 - JOIN artists_similar AS s2 ON s1.SimilarID = s2.SimilarID - WHERE s1.ArtistID = '$Artist1ID' - AND s2.ArtistID = '$Artist2ID'"); - list($SimilarID) = $DB->next_record(); + // Let's see if there's already a similar artists field for these two + $DB->query(" + SELECT s1.SimilarID + FROM artists_similar AS s1 + JOIN artists_similar AS s2 ON s1.SimilarID = s2.SimilarID + WHERE s1.ArtistID = '$Artist1ID' + AND s2.ArtistID = '$Artist2ID'"); + list($SimilarID) = $DB->next_record(); - if ($SimilarID) { // The similar artists field already exists, just update the score - $DB->query(" - UPDATE artists_similar_scores - SET Score = Score + 200 - WHERE SimilarID = '$SimilarID'"); - } else { // No, it doesn't exist - create it - $DB->query(" - INSERT INTO artists_similar_scores (Score) - VALUES ('200')"); - $SimilarID = $DB->inserted_id(); - $DB->query(" - INSERT INTO artists_similar (ArtistID, SimilarID) - VALUES ('$Artist1ID', '$SimilarID')"); - $DB->query(" - INSERT INTO artists_similar (ArtistID, SimilarID) - VALUES ('$Artist2ID', '$SimilarID')"); - } + if ($SimilarID) { // The similar artists field already exists, just update the score + $DB->query(" + UPDATE artists_similar_scores + SET Score = Score + 200 + WHERE SimilarID = '$SimilarID'"); + } else { // No, it doesn't exist - create it + $DB->query(" + INSERT INTO artists_similar_scores (Score) + VALUES ('200')"); + $SimilarID = $DB->inserted_id(); + $DB->query(" + INSERT INTO artists_similar (ArtistID, SimilarID) + VALUES ('$Artist1ID', '$SimilarID')"); + $DB->query(" + INSERT INTO artists_similar (ArtistID, SimilarID) + VALUES ('$Artist2ID', '$SimilarID')"); + } - $DB->query(" - SELECT SimilarID - FROM artists_similar_votes - WHERE SimilarID = '$SimilarID' - AND UserID = '$UserID' - AND Way = 'up'"); - if (!$DB->has_results()) { - $DB->query(" - INSERT INTO artists_similar_votes (SimilarID, UserID, way) - VALUES ('$SimilarID', '$UserID', 'up')"); - } + $DB->query(" + SELECT SimilarID + FROM artists_similar_votes + WHERE SimilarID = '$SimilarID' + AND UserID = '$UserID' + AND Way = 'up'"); + if (!$DB->has_results()) { + $DB->query(" + INSERT INTO artists_similar_votes (SimilarID, UserID, way) + VALUES ('$SimilarID', '$UserID', 'up')"); + } - $Cache->delete_value("artist_$Artist1ID"); // Delete artist cache - $Cache->delete_value("artist_$Artist2ID"); // Delete artist cache - $Cache->delete_value("similar_positions_$Artist1ID"); // Delete artist's similar map cache - $Cache->delete_value("similar_positions_$Artist2ID"); // Delete artist's similar map cache + $Cache->delete_value("artist_$Artist1ID"); // Delete artist cache + $Cache->delete_value("artist_$Artist2ID"); // Delete artist cache + $Cache->delete_value("similar_positions_$Artist1ID"); // Delete artist's similar map cache + $Cache->delete_value("similar_positions_$Artist2ID"); // Delete artist's similar map cache } $Location = (empty($_SERVER['HTTP_REFERER'])) ? "artist.php?id={$Artist1ID}" : $_SERVER['HTTP_REFERER']; diff --git a/sections/artist/artist.php b/sections/artist/artist.php index a548f9516..df084049a 100644 --- a/sections/artist/artist.php +++ b/sections/artist/artist.php @@ -1,9 +1,9 @@ -get_value("artist_{$ArtistID}_revision_$RevisionID", true); + $RevisionID = $_GET['revisionid']; + if (!is_number($RevisionID)) { + error(0); + } + $Data = $Cache->get_value("artist_{$ArtistID}_revision_$RevisionID", true); } else { // viewing the live version - $Data = $Cache->get_value("artist_$ArtistID", true); - $RevisionID = false; + $Data = $Cache->get_value("artist_$ArtistID", true); + $RevisionID = false; } if ($Data) { - list($K, list($Name, $Image, $Body, $NumSimilar, $SimilarArray, , , $VanityHouseArtist)) = each($Data); + list($K, list($Name, $Image, $Body, $NumSimilar, $SimilarArray, , , $VanityHouseArtist)) = each($Data); } else { - if ($RevisionID) { - $sql = " - SELECT - a.Name, - wiki.Image, - wiki.body, - a.VanityHouse - FROM wiki_artists AS wiki - LEFT JOIN artists_group AS a ON wiki.RevisionID = a.RevisionID - WHERE wiki.RevisionID = '$RevisionID' "; - } else { - $sql = " - SELECT - a.Name, - wiki.Image, - wiki.body, - a.VanityHouse - FROM artists_group AS a - LEFT JOIN wiki_artists AS wiki ON wiki.RevisionID = a.RevisionID - WHERE a.ArtistID = '$ArtistID' "; - } - $sql .= " - GROUP BY a.ArtistID"; - $DB->query($sql); - - if (!$DB->has_results()) { - error(404); - } - - list($Name, $Image, $Body, $VanityHouseArtist) = $DB->next_record(MYSQLI_NUM, array(0)); + if ($RevisionID) { + $sql = " + SELECT + a.Name, + wiki.Image, + wiki.body, + a.VanityHouse + FROM wiki_artists AS wiki + LEFT JOIN artists_group AS a ON wiki.RevisionID = a.RevisionID + WHERE wiki.RevisionID = '$RevisionID' "; + } else { + $sql = " + SELECT + a.Name, + wiki.Image, + wiki.body, + a.VanityHouse + FROM artists_group AS a + LEFT JOIN wiki_artists AS wiki ON wiki.RevisionID = a.RevisionID + WHERE a.ArtistID = '$ArtistID' "; + } + $sql .= " + GROUP BY a.ArtistID"; + $DB->query($sql); + + if (!$DB->has_results()) { + error(404); + } + + list($Name, $Image, $Body, $VanityHouseArtist) = $DB->next_record(MYSQLI_NUM, array(0)); } @@ -69,184 +69,186 @@ function compare($X, $Y) { ob_start(); // Requests -$Requests = array(); +$Requests = []; if (empty($LoggedUser['DisableRequests'])) { - $Requests = $Cache->get_value("artists_requests_$ArtistID"); - if (!is_array($Requests)) { - $DB->query(" - SELECT - r.ID, - r.CategoryID, - r.Title, - r.Year, - r.TimeAdded, - COUNT(rv.UserID) AS Votes, - SUM(rv.Bounty) AS Bounty - FROM requests AS r - LEFT JOIN requests_votes AS rv ON rv.RequestID = r.ID - LEFT JOIN requests_artists AS ra ON r.ID = ra.RequestID - WHERE ra.ArtistID = $ArtistID - AND r.TorrentID = 0 - GROUP BY r.ID - ORDER BY Votes DESC"); - - if ($DB->has_results()) { - $Requests = $DB->to_array('ID', MYSQLI_ASSOC, false); - } else { - $Requests = array(); - } - $Cache->cache_value("artists_requests_$ArtistID", $Requests); - } + $Requests = $Cache->get_value("artists_requests_$ArtistID"); + if (!is_array($Requests)) { + $DB->query(" + SELECT + r.ID, + r.CategoryID, + r.Title, + r.Year, + r.TimeAdded, + COUNT(rv.UserID) AS Votes, + SUM(rv.Bounty) AS Bounty + FROM requests AS r + LEFT JOIN requests_votes AS rv ON rv.RequestID = r.ID + LEFT JOIN requests_artists AS ra ON r.ID = ra.RequestID + WHERE ra.ArtistID = $ArtistID + AND r.TorrentID = 0 + GROUP BY r.ID + ORDER BY Votes DESC"); + + if ($DB->has_results()) { + $Requests = $DB->to_array('ID', MYSQLI_ASSOC, false); + } else { + $Requests = []; + } + $Cache->cache_value("artists_requests_$ArtistID", $Requests); + } } $NumRequests = count($Requests); if (($Importances = $Cache->get_value("artist_groups_$ArtistID")) === false) { - $DB->query(" - SELECT - DISTINCTROW ta.GroupID, ta.Importance, tg.VanityHouse, tg.Year - FROM torrents_artists AS ta - JOIN torrents_group AS tg ON tg.ID = ta.GroupID - WHERE ta.ArtistID = '$ArtistID' - ORDER BY tg.Year DESC, tg.Name DESC"); - $GroupIDs = $DB->collect('GroupID'); - $Importances = $DB->to_array(false, MYSQLI_BOTH, false); - $Cache->cache_value("artist_groups_$ArtistID", $Importances, 0); + $DB->query(" + SELECT + DISTINCTROW ta.GroupID, ta.Importance, tg.VanityHouse, tg.Year + FROM torrents_artists AS ta + JOIN torrents_group AS tg ON tg.ID = ta.GroupID + WHERE ta.ArtistID = '$ArtistID' + ORDER BY tg.Year DESC, tg.Name DESC"); + $GroupIDs = $DB->collect('GroupID'); + $Importances = $DB->to_array(false, MYSQLI_BOTH, false); + $Cache->cache_value("artist_groups_$ArtistID", $Importances, 0); } else { - $GroupIDs = array(); - foreach ($Importances as $Group) { - $GroupIDs[] = $Group['GroupID']; - } + $GroupIDs = []; + foreach ($Importances as $Group) { + $GroupIDs[] = $Group['GroupID']; + } } if (count($GroupIDs) > 0) { - $TorrentList = Torrents::get_groups($GroupIDs, true, true); + $TorrentList = Torrents::get_groups($GroupIDs, true, true); } else { - $TorrentList = array(); + $TorrentList = []; } $NumGroups = count($TorrentList); if (!empty($TorrentList)) { ?>
    - $Group) { - switch ($Importances[$ID]['Importance']) { - case '2': - $Importances[$ID]['ReleaseType'] = 1024; - $GuestAlbums = true; - break; - - case '3': - $Importances[$ID]['ReleaseType'] = 1023; - $RemixerAlbums = true; - break; - - case '4': - $Importances[$ID]['ReleaseType'] = 1022; - $ComposerAlbums = true; - break; - - case '7': - $Importances[$ID]['ReleaseType'] = 1021; - $ProducerAlbums = true; - break; - - default: - if (!isset($ReleaseTypes[$TorrentList[$Group['GroupID']]['ReleaseType']])) { - $TorrentList[$Group['GroupID']]['ReleaseType'] = $UnknownRT; - } - $Importances[$ID]['ReleaseType'] = $TorrentList[$Group['GroupID']]['ReleaseType']; - } - - if (!isset($UsedReleases[$Importances[$ID]['ReleaseType']])) { - $UsedReleases[$Importances[$ID]['ReleaseType']] = true; - } - $Importances[$ID]['Sort'] = $ID; + switch ($Importances[$ID]['Importance']) { + case '2': + $Importances[$ID]['ReleaseType'] = 1024; + $GuestAlbums = true; + break; + + case '3': + $Importances[$ID]['ReleaseType'] = 1023; + $RemixerAlbums = true; + break; + + case '4': + $Importances[$ID]['ReleaseType'] = 1022; + $ComposerAlbums = true; + break; + + case '7': + $Importances[$ID]['ReleaseType'] = 1021; + $ProducerAlbums = true; + break; + + default: + if (!isset($ReleaseTypes[$TorrentList[$Group['GroupID']]['ReleaseType']])) { + $TorrentList[$Group['GroupID']]['ReleaseType'] = $UnknownRT; + } + $Importances[$ID]['ReleaseType'] = $TorrentList[$Group['GroupID']]['ReleaseType']; + } + + if (!isset($UsedReleases[$Importances[$ID]['ReleaseType']])) { + $UsedReleases[$Importances[$ID]['ReleaseType']] = true; + } + $Importances[$ID]['Sort'] = $ID; } if (!empty($GuestAlbums)) { - $ReleaseTypes[1024] = 'Guest Appearance'; + $ReleaseTypes[1024] = 'Guest Appearance'; } if (!empty($RemixerAlbums)) { - $ReleaseTypes[1023] = 'Remixed By'; + $ReleaseTypes[1023] = 'Remixed By'; } if (!empty($ComposerAlbums)) { - $ReleaseTypes[1022] = 'Composition'; + $ReleaseTypes[1022] = 'Composition'; } if (!empty($ProducerAlbums)) { - $ReleaseTypes[1021] = 'Produced By'; + $ReleaseTypes[1021] = 'Produced By'; } //Custom sorting for releases if (!empty($LoggedUser['SortHide'])) { - $SortOrder = array_flip(array_keys($LoggedUser['SortHide'])); + $SortOrder = array_flip(array_keys($LoggedUser['SortHide'])); } else { - $SortOrder = $ReleaseTypes; + $SortOrder = $ReleaseTypes; } // If the $SortOrder array doesn't have all release types, put the missing ones at the end $MissingTypes = array_diff_key($ReleaseTypes, $SortOrder); if (!empty($MissingTypes)) { - $MaxOrder = max($SortOrder); - foreach (array_keys($MissingTypes) as $Missing) { - $SortOrder[$Missing] = ++$MaxOrder; - } + $MaxOrder = max($SortOrder); + foreach (array_keys($MissingTypes) as $Missing) { + $SortOrder[$Missing] = ++$MaxOrder; + } } uasort($Importances, function ($A, $B) use ($SortOrder) { - if ($SortOrder[$A['ReleaseType']] == $SortOrder[$B['ReleaseType']]) { - return (($A['Sort'] < $B['Sort']) ? -1 : 1); - } - return (($SortOrder[$A['ReleaseType']] < $SortOrder[$B['ReleaseType']]) ? -1 : 1); + if ($SortOrder[$A['ReleaseType']] == $SortOrder[$B['ReleaseType']]) { + return (($A['Sort'] < $B['Sort']) ? -1 : 1); + } + return (($SortOrder[$A['ReleaseType']] < $SortOrder[$B['ReleaseType']]) ? -1 : 1); }); // Sort the anchors at the top of the page the same way as release types $UsedReleases = array_flip(array_intersect_key($SortOrder, $UsedReleases)); reset($TorrentList); if (!empty($UsedReleases)) { ?> -
    - + - > - 0) { + > + 0) { ?> - Requests - -
    -Requests + +
    + $Group) { - // $Tags array is for the sidebar on the right. - // Skip compilations and soundtracks. - $Merge = $Group['ReleaseType'] != 7 && $Group['ReleaseType'] != 3; + // $Tags array is for the sidebar on the right. + // Skip compilations and soundtracks. + $Merge = $Group['ReleaseType'] != 7 && $Group['ReleaseType'] != 3; - $TorrentTags = new Tags($Group['TagList'], $Merge); + $TorrentTags = new Tags($Group['TagList'], $Merge); - foreach ($Group['Torrents'] as $TorrentID => $Torrent) { - $NumTorrents++; + foreach ($Group['Torrents'] as $TorrentID => $Torrent) { + $NumTorrents++; - $Torrent['Seeders'] = (int)$Torrent['Seeders']; - $Torrent['Leechers'] = (int)$Torrent['Leechers']; - $Torrent['Snatched'] = (int)$Torrent['Snatched']; + $Torrent['Seeders'] = (int)$Torrent['Seeders']; + $Torrent['Leechers'] = (int)$Torrent['Leechers']; + $Torrent['Snatched'] = (int)$Torrent['Snatched']; - $NumSeeders += $Torrent['Seeders']; - $NumLeechers += $Torrent['Leechers']; - $NumSnatches += $Torrent['Snatched']; - } + $NumSeeders += $Torrent['Seeders']; + $NumLeechers += $Torrent['Leechers']; + $NumSnatches += $Torrent['Snatched']; + } } @@ -282,199 +284,204 @@ function compare($X, $Y) { $LastReleaseType = 0; foreach ($Importances as $Group) { - extract(Torrents::array_group($TorrentList[$Group['GroupID']]), EXTR_OVERWRITE); - $ReleaseType = $Group['ReleaseType']; - - if ($GroupID == $OldGroupID && $ReleaseType == $OldReleaseType) { - continue; - } else { - $OldGroupID = $GroupID; - $OldReleaseType = $ReleaseType; - } - -/* if (!empty($LoggedUser['DiscogView']) || (isset($LoggedUser['HideTypes']) && in_array($ReleaseType, $LoggedUser['HideTypes']))) { - $HideDiscog = ' hidden'; - } else { - $HideDiscog = ''; - } */ - if (!empty($LoggedUser['DiscogView']) || (isset($LoggedUser['SortHide']) && array_key_exists($ReleaseType, $LoggedUser['SortHide']) && $LoggedUser['SortHide'][$ReleaseType] == 1)) { - $HideDiscog = ' hidden'; - } else { - $HideDiscog = ''; - } - - $TorrentTags = new Tags($TagList, false); - - if ($ReleaseType != $LastReleaseType) { - switch ($ReleaseTypes[$ReleaseType]) { - case 'Remix': - $DisplayName = 'Remixes'; - break; - case 'Anthology': - $DisplayName = 'Anthologies'; - break; - case 'DJ Mix': - $DisplayName = 'DJ Mixes'; - break; - default: - $DisplayName = $ReleaseTypes[$ReleaseType].'s'; - break; - } - - $ReleaseTypeLabel = strtolower(str_replace(' ', '_', $ReleaseTypes[$ReleaseType])); - if ($OpenTable) { ?> - - - - - - - - - - - -$GroupName"; - if (check_perms('users_mod') || check_perms('torrents_fix_ghosts')) { - $DisplayName .= ' Fix'; - } - - - switch ($ReleaseType) { - case 1023: // Remixes, DJ Mixes, Guest artists, and Producers need the artist name - case 1024: - case 1021: - case 8: - if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5]) || !empty($ExtendedArtists[6])) { - unset($ExtendedArtists[2]); - unset($ExtendedArtists[3]); - $DisplayName = Artists::display_artists($ExtendedArtists).$DisplayName; - } elseif (count($GroupArtists) > 0) { - $DisplayName = Artists::display_artists(array(1 => $Artists), true, true).$DisplayName; - } - break; - case 1022: // Show performers on composer pages - if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5])) { - unset($ExtendedArtists[4]); - unset($ExtendedArtists[3]); - unset($ExtendedArtists[6]); - $DisplayName = Artists::display_artists($ExtendedArtists).$DisplayName; - } elseif (count($GroupArtists) > 0) { - $DisplayName = Artists::display_artists(array(1 => $Artists), true, true).$DisplayName; - } - break; - default: // Show composers otherwise - if (!empty($ExtendedArtists[4])) { - $DisplayName = Artists::display_artists(array(4 => $ExtendedArtists[4]), true, true).$DisplayName; - } - } - - if ($GroupYear > 0) { - $DisplayName = "$GroupYear - $DisplayName"; - } - - if ($GroupVanityHouse) { - $DisplayName .= ' [VH]'; - } - - $SnatchedGroupClass = ($GroupFlags['IsSnatched'] ? ' snatched_group' : ''); + extract(Torrents::array_group($TorrentList[$Group['GroupID']]), EXTR_OVERWRITE); + $ReleaseType = $Group['ReleaseType']; + + if ($GroupID == $OldGroupID && $ReleaseType == $OldReleaseType) { + continue; + } else { + $OldGroupID = $GroupID; + $OldReleaseType = $ReleaseType; + } + +/* if (!empty($LoggedUser['DiscogView']) || (isset($LoggedUser['HideTypes']) && in_array($ReleaseType, $LoggedUser['HideTypes']))) { + $HideDiscog = ' hidden'; + } else { + $HideDiscog = ''; + } */ + if (!empty($LoggedUser['DiscogView']) || (isset($LoggedUser['SortHide']) && array_key_exists($ReleaseType, $LoggedUser['SortHide']) && $LoggedUser['SortHide'][$ReleaseType] == 1)) { + $HideDiscog = ' hidden'; + } else { + $HideDiscog = ''; + } + + $TorrentTags = new Tags($TagList, false); + + if ($ReleaseType != $LastReleaseType) { + switch ($ReleaseTypes[$ReleaseType]) { + case 'Remix': + $DisplayName = 'Remixes'; + break; + case 'Anthology': + $DisplayName = 'Anthologies'; + break; + case 'DJ Mix': + $DisplayName = 'DJ Mixes'; + break; + default: + $DisplayName = $ReleaseTypes[$ReleaseType].'s'; + break; + } + + $ReleaseTypeLabel = strtolower(str_replace(' ', '_', $ReleaseTypes[$ReleaseType])); + if ($OpenTable) { ?> +
      (View)SizeSnatchesSeedersLeechers
    + + + + + + + + + + +$GroupName"; + if (check_perms('users_mod') || check_perms('torrents_fix_ghosts')) { + $DisplayName .= ' Fix'; + } + + + switch ($ReleaseType) { + case 1023: // Remixes, DJ Mixes, Guest artists, and Producers need the artist name + case 1024: + case 1021: + case 8: + if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5]) || !empty($ExtendedArtists[6])) { + unset($ExtendedArtists[2]); + unset($ExtendedArtists[3]); + $DisplayName = Artists::display_artists($ExtendedArtists).$DisplayName; + } elseif (count($GroupArtists) > 0) { + $DisplayName = Artists::display_artists(array(1 => $Artists), true, true).$DisplayName; + } + break; + case 1022: // Show performers on composer pages + if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5])) { + unset($ExtendedArtists[4]); + unset($ExtendedArtists[3]); + unset($ExtendedArtists[6]); + $DisplayName = Artists::display_artists($ExtendedArtists).$DisplayName; + } elseif (count($GroupArtists) > 0) { + $DisplayName = Artists::display_artists(array(1 => $Artists), true, true).$DisplayName; + } + break; + default: // Show composers otherwise + if (!empty($ExtendedArtists[4])) { + $DisplayName = Artists::display_artists(array(4 => $ExtendedArtists[4]), true, true).$DisplayName; + } + } + + if ($GroupYear > 0) { + $DisplayName = "$GroupYear - $DisplayName"; + } + + if ($GroupVanityHouse) { + $DisplayName .= ' [VH]'; + } + + $SnatchedGroupClass = ($GroupFlags['IsSnatched'] ? ' snatched_group' : ''); ?> - - - + - - $Torrent) { - if ($Torrent['Remastered'] && !$Torrent['RemasterYear']) { - $FirstUnknown = !isset($FirstUnknown); - } - $SnatchedTorrentClass = ($Torrent['IsSnatched'] ? ' snatched_torrent' : ''); - - if ($Torrent['RemasterTitle'] != $LastRemasterTitle - || $Torrent['RemasterYear'] != $LastRemasterYear - || $Torrent['RemasterRecordLabel'] != $LastRemasterRecordLabel - || $Torrent['RemasterCatalogueNumber'] != $LastRemasterCatalogueNumber - || isset($FirstUnknown) && $FirstUnknown - || $Torrent['Media'] != $LastMedia - ) { - - $EditionID++; +
    format('torrents.php?taglist=', $Name)?>
    + + + + $Torrent) { + if ($Torrent['Remastered'] && !$Torrent['RemasterYear']) { + $FirstUnknown = !isset($FirstUnknown); + } + $SnatchedTorrentClass = ($Torrent['IsSnatched'] ? ' snatched_torrent' : ''); + + if ($Torrent['RemasterTitle'] != $LastRemasterTitle + || $Torrent['RemasterYear'] != $LastRemasterYear + || $Torrent['RemasterRecordLabel'] != $LastRemasterRecordLabel + || $Torrent['RemasterCatalogueNumber'] != $LastRemasterCatalogueNumber + || isset($FirstUnknown) && $FirstUnknown + || $Torrent['Media'] != $LastMedia + ) { + + $EditionID++; ?> - - - - groupid_ edition group_torrent discog"> + + + - - - - - - - - torrent_row groupid_ edition_ group_torrent discog"> + + + + + + + -
      (View)SizeSnatchesSeedersLeechers
    -
    - -
    -
    - -
    - -
    - -
    - - - - Remove bookmark - - - - Bookmark - - group discog"> +
    +
    + +
    +
    + +
    + +
    + +
    + + + + Remove bookmark + + + + Bookmark + + -
    format('torrents.php?taglist=', $Name)?>
    -
    -
    - - [ - - | FL - - | [orpheus.network].json" class="tooltip" title="Download JSON">JS - ] - -   »  -
    + + [ + + | FL + + | [] [orpheus.network].json" class="tooltip" title="Download JSON">JS + ] + +   »  +
    - - + +
    -
    -

    (Revision #) [Vanity House]

    - - - +
    + + +
    + +
    +
    Tags
    +
      + +
    +
    + -
    -
    Statistics
    -
      -
    • Number of groups:
    • -
    • Number of torrents:
    • -
    • Number of seeders:
    • -
    • Number of leechers:
    • -
    • Number of snatches:
    • -
    -
    - +
    Statistics
    +
      +
    • Number of groups:
    • +
    • Number of torrents:
    • +
    • Number of seeders:
    • +
    • Number of leechers:
    • +
    • Number of snatches:
    • +
    + +query(" - SELECT - s2.ArtistID, - a.Name, - ass.Score, - ass.SimilarID - FROM artists_similar AS s1 - JOIN artists_similar AS s2 ON s1.SimilarID = s2.SimilarID AND s1.ArtistID != s2.ArtistID - JOIN artists_similar_scores AS ass ON ass.SimilarID = s1.SimilarID - JOIN artists_group AS a ON a.ArtistID = s2.ArtistID - WHERE s1.ArtistID = '$ArtistID' - ORDER BY ass.Score DESC - LIMIT 30 - "); - $SimilarArray = $DB->to_array(); - $NumSimilar = count($SimilarArray); + $DB->query(" + SELECT + s2.ArtistID, + a.Name, + ass.Score, + ass.SimilarID + FROM artists_similar AS s1 + JOIN artists_similar AS s2 ON s1.SimilarID = s2.SimilarID AND s1.ArtistID != s2.ArtistID + JOIN artists_similar_scores AS ass ON ass.SimilarID = s1.SimilarID + JOIN artists_group AS a ON a.ArtistID = s2.ArtistID + WHERE s1.ArtistID = '$ArtistID' + ORDER BY ass.Score DESC + LIMIT 30 + "); + $SimilarArray = $DB->to_array(); + $NumSimilar = count($SimilarArray); } ?> -
    -
    Similar Artists
    -
      - -
    • None found
    • - +
      Similar Artists
      +
        + +
      • None found
      • + -
      • - -
        - - - - X - -
        -
        -
      • - -
      -
    -
    -
    Add similar artist
    -
      -
    • -
      - - - - /> - -
      -
    • -
    -
    - -
    - + +
    + + + + X + +
    +
    + + + +
    +
    +
    Add similar artist
    +
      +
    • +
      + + + + /> + +
      +
    • +
    +
    + +
    +get_value("artists_collages_$ArtistID"); if (!is_array($Collages)) { - $DB->query(" - SELECT c.Name, c.NumTorrents, c.ID - FROM collages AS c - JOIN collages_artists AS ca ON ca.CollageID = c.ID - WHERE ca.ArtistID = '$ArtistID' - AND Deleted = '0' - AND CategoryID = '7'"); - $Collages = $DB->to_array(); - $Cache->cache_value("artists_collages_$ArtistID", $Collages, 3600 * 6); + $DB->query(" + SELECT c.Name, c.NumTorrents, c.ID + FROM collages AS c + JOIN collages_artists AS ca ON ca.CollageID = c.ID + WHERE ca.ArtistID = '$ArtistID' + AND Deleted = '0' + AND CategoryID = '7'"); + $Collages = $DB->to_array(); + $Cache->cache_value("artists_collages_$ArtistID", $Collages, 3600 * 6); } if (count($Collages) > 0) { - if (count($Collages) > MAX_COLLAGES) { - // Pick some at random - $Range = range(0,count($Collages) - 1); - shuffle($Range); - $Indices = array_slice($Range, 0, MAX_COLLAGES); - $SeeAll = ' (See all)'; - } else { - $Indices = range(0, count($Collages)-1); - $SeeAll = ''; - } + if (count($Collages) > MAX_COLLAGES) { + // Pick some at random + $Range = range(0,count($Collages) - 1); + shuffle($Range); + $Indices = array_slice($Range, 0, MAX_COLLAGES); + $SeeAll = ' (See all)'; + } else { + $Indices = range(0, count($Collages)-1); + $SeeAll = ''; + } ?> - - - - - - + + + + + - - - - - + + + + - - - - - -
     This artist is in collage 1) ? 's' : '')?># artists
     This artist is in collage 1) ? 's' : '')?># artists
    - + + + + + + 0) { ?> - - - - - - - - $Request) { - $CategoryName = $Categories[$Request['CategoryID'] - 1]; - $Title = display_str($Request['Title']); - if ($CategoryName == 'Music') { - $ArtistForm = Requests::get_artists($RequestID); - $ArtistLink = Artists::display_artists($ArtistForm, true, true); - $FullName = $ArtistLink."$Title [$Request[Year]]"; - } elseif ($CategoryName == 'Audiobooks' || $CategoryName == 'Comedy') { - $FullName = "$Title [$Request[Year]]"; - } else { - $FullName = "$Title"; - } - - if (!empty($Tags[$RequestID])) { - $ReqTagList = array(); - foreach ($Tags[$RequestID] as $TagID => $TagName) { - $ReqTagList[] = "".display_str($TagName).''; - } - $ReqTagList = implode(', ', $ReqTagList); - } else { - $ReqTagList = ''; - } +
    -   - Request Name - - Vote - - Bounty - - Added -
    + + + + + + + $Request) { + $CategoryName = $Categories[$Request['CategoryID'] - 1]; + $Title = display_str($Request['Title']); + if ($CategoryName == 'Music') { + $ArtistForm = Requests::get_artists($RequestID); + $ArtistLink = Artists::display_artists($ArtistForm, true, true); + $FullName = $ArtistLink."$Title [$Request[Year]]"; + } elseif ($CategoryName == 'Audiobooks' || $CategoryName == 'Comedy') { + $FullName = "$Title [$Request[Year]]"; + } else { + $FullName = "$Title"; + } + + if (!empty($Tags[$RequestID])) { + $ReqTagList = []; + foreach ($Tags[$RequestID] as $TagID => $TagName) { + $ReqTagList[] = "".display_str($TagName).''; + } + $ReqTagList = implode(', ', $ReqTagList); + } else { + $ReqTagList = ''; + } ?> - - - - - - - -
    +   + Request Name + + Vote + + Bounty + + Added +
    - -
    -
    - - - -    + - - - - - -
    -"> + + +
    + + + + + +    + + + + + + + + + + + + + 0) { - if ($SimilarData = $Cache->get_value("similar_positions_$ArtistID")) { - $Similar = new ARTISTS_SIMILAR($ArtistID, $Name); - $Similar->load_data($SimilarData); - if (!(current($Similar->Artists)->NameLength)) { - unset($Similar); - } - } - if (empty($Similar) || empty($Similar->Artists)) { - include(SERVER_ROOT.'/classes/image.class.php'); - $Img = new IMAGE; - $Img->create(WIDTH, HEIGHT); - $Img->color(255, 255, 255, 127); - - $Similar = new ARTISTS_SIMILAR($ArtistID, $Name); - $Similar->set_up(); - $Similar->set_positions(); - $Similar->background_image(); - - $SimilarData = $Similar->dump_data(); - - $Cache->cache_value("similar_positions_$ArtistID", $SimilarData, 3600 * 24); - } + if ($SimilarData = $Cache->get_value("similar_positions_$ArtistID")) { + $Similar = new ARTISTS_SIMILAR($ArtistID, $Name); + $Similar->load_data($SimilarData); + if (!(current($Similar->Artists)->NameLength)) { + unset($Similar); + } + } + if (empty($Similar) || empty($Similar->Artists)) { + include(SERVER_ROOT.'/classes/image.class.php'); + $Img = new IMAGE; + $Img->create(WIDTH, HEIGHT); + $Img->color(255, 255, 255, 127); + + $Similar = new ARTISTS_SIMILAR($ArtistID, $Name); + $Similar->set_up(); + $Similar->set_positions(); + $Similar->background_image(); + + $SimilarData = $Similar->dump_data(); + + $Cache->cache_value("similar_positions_$ArtistID", $SimilarData, 3600 * 24); + } ?> -
    -
    -   - Similar Artist Map - Switch to cloud -
    -
    -write_artists(); +
    +
    +   + Similar Artist Map + Switch to cloud +
    +
    +write_artists(); ?> -
    - -
    +
    + +
    - 0 */ ?> -
    -
    -   - Artist Information - Toggle -
    -
    -
    - 0 */ ?> +
    +
    +   + Artist Information + Toggle +
    +
    +
    + -
    - - + + - - 'pageid', - 'InputID' => $ArtistID, - 'Action' => 'comments.php?page=artist', - 'InputAction' => 'take_post', - 'SubscribeBox' => true - )); + + 'pageid', + 'InputID' => $ArtistID, + 'Action' => 'comments.php?page=artist', + 'InputAction' => 'take_post', + 'SubscribeBox' => true + )); ?> -
    -
    + + -cache_value($Key, $Data, 3600); diff --git a/sections/artist/autocomplete.php b/sections/artist/autocomplete.php index bec92ce4a..59cc19ea7 100644 --- a/sections/artist/autocomplete.php +++ b/sections/artist/autocomplete.php @@ -1,6 +1,6 @@ -get('autocomplete_artist_'.$KeySize.'_'.$Letters); if (!$AutoSuggest) { - $Limit = (($KeySize === $MaxKeySize) ? 250 : 10); - $DB->query(" - SELECT - a.ArtistID, - a.Name - FROM artists_group AS a - INNER JOIN torrents_artists AS ta ON ta.ArtistID=a.ArtistID - INNER JOIN torrents AS t ON t.GroupID=ta.GroupID - WHERE a.Name LIKE '".db_string(str_replace('\\','\\\\',$Letters),true)."%' - GROUP BY ta.ArtistID - ORDER BY t.Snatched DESC - LIMIT $Limit"); - $AutoSuggest = $DB->to_array(false,MYSQLI_NUM,false); - $Cache->cache_value('autocomplete_artist_'.$KeySize.'_'.$Letters,$AutoSuggest,1800 + 7200 * ($MaxKeySize - $KeySize)); // Can't cache things for too long in case names are edited + $Limit = (($KeySize === $MaxKeySize) ? 250 : 10); + $DB->query(" + SELECT + a.ArtistID, + a.Name + FROM artists_group AS a + INNER JOIN torrents_artists AS ta ON (ta.ArtistID=a.ArtistID) + INNER JOIN torrents AS t ON (t.GroupID=ta.GroupID) + INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID) + WHERE a.Name LIKE '".db_string(str_replace('\\','\\\\',$Letters),true)."%' + GROUP BY ta.ArtistID + ORDER BY tls.Snatched DESC + LIMIT $Limit"); + $AutoSuggest = $DB->to_array(false,MYSQLI_NUM,false); + $Cache->cache_value('autocomplete_artist_'.$KeySize.'_'.$Letters,$AutoSuggest,1800 + 7200 * ($MaxKeySize - $KeySize)); // Can't cache things for too long in case names are edited } $Matched = 0; -$ArtistIDs = array(); -$Response = array( - 'query' => $FullName, - 'suggestions' => array() -); +$Response = [ + 'query' => $FullName, + 'suggestions' => [] +]; foreach ($AutoSuggest as $Suggestion) { - list($ID, $Name) = $Suggestion; - if (stripos($Name, $FullName) === 0) { - $Response['suggestions'][] = array('value' => $Name, 'data' => $ID); - if (++$Matched > 9) { - break; - } - } + list($ID, $Name) = $Suggestion; + if (stripos($Name, $FullName) === 0) { + $Response['suggestions'][] = ['value' => $Name, 'data' => $ID]; + if (++$Matched > 9) { + break; + } + } } echo json_encode($Response); diff --git a/sections/artist/change_artistid.php b/sections/artist/change_artistid.php index e9705ea03..2b9e5b682 100644 --- a/sections/artist/change_artistid.php +++ b/sections/artist/change_artistid.php @@ -1,11 +1,11 @@ -query(" - SELECT Name - FROM artists_group - WHERE ArtistID = $ArtistID - LIMIT 1"); + SELECT Name + FROM artists_group + WHERE ArtistID = $ArtistID + LIMIT 1"); if (!(list($ArtistName) = $DB->next_record(MYSQLI_NUM, false))) { - error('An error has occurred.'); + error('An error has occurred.'); } if ($NewArtistID > 0) { - // Make sure that's a real artist ID number, and grab the name - $DB->query(" - SELECT Name - FROM artists_group - WHERE ArtistID = $NewArtistID - LIMIT 1"); - if (!(list($NewArtistName) = $DB->next_record())) { - error('Please enter a valid artist ID number.'); - } + // Make sure that's a real artist ID number, and grab the name + $DB->query(" + SELECT Name + FROM artists_group + WHERE ArtistID = $NewArtistID + LIMIT 1"); + if (!(list($NewArtistName) = $DB->next_record())) { + error('Please enter a valid artist ID number.'); + } } else { - // Didn't give an ID, so try to grab based on the name - $DB->query(" - SELECT ArtistID - FROM artists_alias - WHERE Name = '".db_string($NewArtistName)."' - LIMIT 1"); - if (!(list($NewArtistID) = $DB->next_record())) { - error('No artist by that name was found.'); - } + // Didn't give an ID, so try to grab based on the name + $DB->query(" + SELECT ArtistID + FROM artists_alias + WHERE Name = '".db_string($NewArtistName)."' + LIMIT 1"); + if (!(list($NewArtistID) = $DB->next_record())) { + error('No artist by that name was found.'); + } } if ($ArtistID == $NewArtistID) { - error('You cannot merge an artist with itself.'); + error('You cannot merge an artist with itself.'); } if (isset($_POST['confirm'])) { - // Get the information for the cache update - $DB->query(" - SELECT DISTINCT GroupID - FROM torrents_artists - WHERE ArtistID = $ArtistID"); - $Groups = $DB->collect('GroupID'); - $DB->query(" - SELECT DISTINCT RequestID - FROM requests_artists - WHERE ArtistID = $ArtistID"); - $Requests = $DB->collect('RequestID'); - $DB->query(" - SELECT DISTINCT UserID - FROM bookmarks_artists - WHERE ArtistID = $ArtistID"); - $BookmarkUsers = $DB->collect('UserID'); - $DB->query(" - SELECT DISTINCT ct.CollageID - FROM collages_torrents AS ct - JOIN torrents_artists AS ta ON ta.GroupID = ct.GroupID - WHERE ta.ArtistID = $ArtistID"); - $Collages = $DB->collect('CollageID'); - - // And the info to avoid double-listing an artist if it and the target are on the same group - $DB->query(" - SELECT DISTINCT GroupID - FROM torrents_artists - WHERE ArtistID = $NewArtistID"); - $NewArtistGroups = $DB->collect('GroupID'); - $NewArtistGroups[] = '0'; - $NewArtistGroups = implode(',', $NewArtistGroups); - - $DB->query(" - SELECT DISTINCT RequestID - FROM requests_artists - WHERE ArtistID = $NewArtistID"); - $NewArtistRequests = $DB->collect('RequestID'); - $NewArtistRequests[] = '0'; - $NewArtistRequests = implode(',', $NewArtistRequests); - - $DB->query(" - SELECT DISTINCT UserID - FROM bookmarks_artists - WHERE ArtistID = $NewArtistID"); - $NewArtistBookmarks = $DB->collect('UserID'); - $NewArtistBookmarks[] = '0'; - $NewArtistBookmarks = implode(',', $NewArtistBookmarks); - - // Merge all of this artist's aliases onto the new artist - $DB->query(" - UPDATE artists_alias - SET ArtistID = $NewArtistID - WHERE ArtistID = $ArtistID"); - - // Update the torrent groups, requests, and bookmarks - $DB->query(" - UPDATE IGNORE torrents_artists - SET ArtistID = $NewArtistID - WHERE ArtistID = $ArtistID - AND GroupID NOT IN ($NewArtistGroups)"); - $DB->query(" - DELETE FROM torrents_artists - WHERE ArtistID = $ArtistID"); - $DB->query(" - UPDATE IGNORE requests_artists - SET ArtistID = $NewArtistID - WHERE ArtistID = $ArtistID - AND RequestID NOT IN ($NewArtistRequests)"); - $DB->query(" - DELETE FROM requests_artists - WHERE ArtistID = $ArtistID"); - $DB->query(" - UPDATE IGNORE bookmarks_artists - SET ArtistID = $NewArtistID - WHERE ArtistID = $ArtistID - AND UserID NOT IN ($NewArtistBookmarks)"); - $DB->query(" - DELETE FROM bookmarks_artists - WHERE ArtistID = $ArtistID"); - - // Cache clearing - if (!empty($Groups)) { - foreach ($Groups as $GroupID) { - $Cache->delete_value("groups_artists_$GroupID"); - Torrents::update_hash($GroupID); - } - } - if (!empty($Requests)) { - foreach ($Requests as $RequestID) { - $Cache->delete_value("request_artists_$RequestID"); - Requests::update_sphinx_requests($RequestID); - } - } - if (!empty($BookmarkUsers)) { - foreach ($BookmarkUsers as $UserID) { - $Cache->delete_value("notify_artists_$UserID"); - } - } - if (!empty($Collages)) { - foreach ($Collages as $CollageID) { - $Cache->delete_value("collage_$CollageID"); - } - } - - $Cache->delete_value("artist_$ArtistID"); - $Cache->delete_value("artist_$NewArtistID"); - $Cache->delete_value("artist_groups_$ArtistID"); - $Cache->delete_value("artist_groups_$NewArtistID"); - - // Delete the old artist - $DB->query(" - DELETE FROM artists_group - WHERE ArtistID = $ArtistID"); - - Misc::write_log("The artist $ArtistID ($ArtistName) was made into a non-redirecting alias of artist $NewArtistID ($NewArtistName) by user ".$LoggedUser['ID']." (".$LoggedUser['Username'].')'); - - header("Location: artist.php?action=edit&artistid=$NewArtistID"); + // Get the information for the cache update + $DB->query(" + SELECT DISTINCT GroupID + FROM torrents_artists + WHERE ArtistID = $ArtistID"); + $Groups = $DB->collect('GroupID'); + $DB->query(" + SELECT DISTINCT RequestID + FROM requests_artists + WHERE ArtistID = $ArtistID"); + $Requests = $DB->collect('RequestID'); + $DB->query(" + SELECT DISTINCT UserID + FROM bookmarks_artists + WHERE ArtistID = $ArtistID"); + $BookmarkUsers = $DB->collect('UserID'); + $DB->query(" + SELECT DISTINCT ct.CollageID + FROM collages_torrents AS ct + JOIN torrents_artists AS ta ON ta.GroupID = ct.GroupID + WHERE ta.ArtistID = $ArtistID"); + $Collages = $DB->collect('CollageID'); + + // And the info to avoid double-listing an artist if it and the target are on the same group + $DB->query(" + SELECT DISTINCT GroupID + FROM torrents_artists + WHERE ArtistID = $NewArtistID"); + $NewArtistGroups = $DB->collect('GroupID'); + $NewArtistGroups[] = '0'; + $NewArtistGroups = implode(',', $NewArtistGroups); + + $DB->query(" + SELECT DISTINCT RequestID + FROM requests_artists + WHERE ArtistID = $NewArtistID"); + $NewArtistRequests = $DB->collect('RequestID'); + $NewArtistRequests[] = '0'; + $NewArtistRequests = implode(',', $NewArtistRequests); + + $DB->query(" + SELECT DISTINCT UserID + FROM bookmarks_artists + WHERE ArtistID = $NewArtistID"); + $NewArtistBookmarks = $DB->collect('UserID'); + $NewArtistBookmarks[] = '0'; + $NewArtistBookmarks = implode(',', $NewArtistBookmarks); + + // Merge all of this artist's aliases onto the new artist + $DB->query(" + UPDATE artists_alias + SET ArtistID = $NewArtistID + WHERE ArtistID = $ArtistID"); + + // Update the torrent groups, requests, and bookmarks + $DB->query(" + UPDATE IGNORE torrents_artists + SET ArtistID = $NewArtistID + WHERE ArtistID = $ArtistID + AND GroupID NOT IN ($NewArtistGroups)"); + $DB->query(" + DELETE FROM torrents_artists + WHERE ArtistID = $ArtistID"); + $DB->query(" + UPDATE IGNORE requests_artists + SET ArtistID = $NewArtistID + WHERE ArtistID = $ArtistID + AND RequestID NOT IN ($NewArtistRequests)"); + $DB->query(" + DELETE FROM requests_artists + WHERE ArtistID = $ArtistID"); + $DB->query(" + UPDATE IGNORE bookmarks_artists + SET ArtistID = $NewArtistID + WHERE ArtistID = $ArtistID + AND UserID NOT IN ($NewArtistBookmarks)"); + $DB->query(" + DELETE FROM bookmarks_artists + WHERE ArtistID = $ArtistID"); + + // Cache clearing + if (!empty($Groups)) { + foreach ($Groups as $GroupID) { + $Cache->delete_value("groups_artists_$GroupID"); + Torrents::update_hash($GroupID); + } + } + if (!empty($Requests)) { + foreach ($Requests as $RequestID) { + $Cache->delete_value("request_artists_$RequestID"); + Requests::update_sphinx_requests($RequestID); + } + } + if (!empty($BookmarkUsers)) { + foreach ($BookmarkUsers as $UserID) { + $Cache->delete_value("notify_artists_$UserID"); + } + } + if (!empty($Collages)) { + foreach ($Collages as $CollageID) { + $Cache->delete_value("collage_$CollageID"); + } + } + + $Cache->delete_value("artist_$ArtistID"); + $Cache->delete_value("artist_$NewArtistID"); + $Cache->delete_value("artist_groups_$ArtistID"); + $Cache->delete_value("artist_groups_$NewArtistID"); + + // Delete the old artist + $DB->query(" + DELETE FROM artists_group + WHERE ArtistID = $ArtistID"); + + Misc::write_log("The artist $ArtistID ($ArtistName) was made into a non-redirecting alias of artist $NewArtistID ($NewArtistName) by user ".$LoggedUser['ID']." (".$LoggedUser['Username'].')'); + + header("Location: artist.php?action=edit&artistid=$NewArtistID"); } else { - View::show_header('Merging Artists'); + View::show_header('Merging Artists'); ?> -
    -

    Confirm merge

    -
    -
    - - - - - -
    -

    Please confirm that you wish to make () into a non-redirecting alias of ().

    -
    - -
    -
    - +

    Confirm merge

    + +
    + + + + + +
    +

    Please confirm that you wish to make () into a non-redirecting alias of ().

    +
    + +
    +
    + diff --git a/sections/artist/concerts.php b/sections/artist/concerts.php index 8bd75c7e3..0ca439f70 100644 --- a/sections/artist/concerts.php +++ b/sections/artist/concerts.php @@ -1,102 +1,102 @@ -An error occurred when retrieving concert info.
    '; + echo '
    An error occurred when retrieving concert info.
    '; } elseif (!isset($ArtistEvents['events']['event'])) { // No upcoming events - echo '
    This artist has no upcoming concerts.
    '; - $Hidden = true; + echo '
    This artist has no upcoming concerts.
    '; + $Hidden = true; } else { - echo '
      '; - if (isset($ArtistEvents['events']['event'][0])) { // Multiple events - foreach ($ArtistEvents['events']['event'] as $Event) { - make_concert_link($Event); - } - } else { // Single event - make_concert_link($ArtistEvents['events']['event'], $Name); - } - echo '
    '; + echo '
      '; + if (isset($ArtistEvents['events']['event'][0])) { // Multiple events + foreach ($ArtistEvents['events']['event'] as $Event) { + make_concert_link($Event); + } + } else { // Single event + make_concert_link($ArtistEvents['events']['event'], $Name); + } + echo '
    '; } $Concerts .= ob_get_clean(); ?>
    -
    -  Upcoming concerts - Toggle -
    - +
    +  Upcoming concerts + Toggle +
    +
    -$ConcertTitle"; + // The event doesn't have a start date (this should never happen) + if ($Event['startDate'] == '') { + return; + } + $Date = get_date_title($Event['startDate']); + $ConcertTitle = $Date . ' - ' . $Event['venue']['name'] . ' at ' . + $Event['venue']['location']['city'] . ', ' . $Event['venue']['location']['country']; + $Concert = "$ConcertTitle"; ?> - -
  • - Go to thread
  • -" method="post"> + + " /> + + + +
  • - Go to thread
  • + diff --git a/sections/artist/delete.php b/sections/artist/delete.php index 1fd720761..7d2812d52 100644 --- a/sections/artist/delete.php +++ b/sections/artist/delete.php @@ -1,4 +1,4 @@ -query(" - SELECT Name - FROM artists_group - WHERE ArtistID = $ArtistID"); + SELECT Name + FROM artists_group + WHERE ArtistID = $ArtistID"); list($Name) = $DB->next_record(); $DB->query(" - SELECT tg.Name, tg.ID - FROM torrents_group AS tg - LEFT JOIN torrents_artists AS ta ON ta.GroupID = tg.ID - WHERE ta.ArtistID = $ArtistID"); + SELECT tg.Name, tg.ID + FROM torrents_group AS tg + LEFT JOIN torrents_artists AS ta ON ta.GroupID = tg.ID + WHERE ta.ArtistID = $ArtistID"); $Count = $DB->record_count(); if ($DB->has_results()) { ?> -
    - There are still torrents that have as an artist.
    - Please remove the artist from these torrents manually before attempting to delete.
    -
    -
      -next_record(MYSQLI_NUM, true)) { +
      + There are still torrents that have as an artist.
      + Please remove the artist from these torrents manually before attempting to delete.
      +
      +
        +next_record(MYSQLI_NUM, true)) { ?> -
      • - -
      • - + + + -
      -
      -
      - +
    +
    +query(" - SELECT r.Title, r.ID - FROM requests AS r - LEFT JOIN requests_artists AS ra ON ra.RequestID = r.ID - WHERE ra.ArtistID = $ArtistID"); + SELECT r.Title, r.ID + FROM requests AS r + LEFT JOIN requests_artists AS ra ON ra.RequestID = r.ID + WHERE ra.ArtistID = $ArtistID"); $Count += $DB->record_count(); if ($DB->has_results()) { ?> -
    - There are still requests that have as an artist.
    - Please remove the artist from these requests manually before attempting to delete.
    -
    -
      -next_record(MYSQLI_NUM, true)) { +
      + There are still requests that have as an artist.
      + Please remove the artist from these requests manually before attempting to delete.
      +
      +
        +next_record(MYSQLI_NUM, true)) { ?> -
      • - -
      • - + + + -
      -
      -
      - +
    +
    + -
    - Artist "" deleted! -
    - + Artist "" deleted! + + diff --git a/sections/artist/delete_alias.php b/sections/artist/delete_alias.php index 8a28fada7..5266f4baa 100644 --- a/sections/artist/delete_alias.php +++ b/sections/artist/delete_alias.php @@ -1,51 +1,51 @@ query(" - SELECT aa.AliasID - FROM artists_alias AS aa - JOIN artists_alias AS aa2 ON aa.ArtistID=aa2.ArtistID - WHERE aa.AliasID=".$AliasID); + SELECT aa.AliasID + FROM artists_alias AS aa + JOIN artists_alias AS aa2 ON aa.ArtistID=aa2.ArtistID + WHERE aa.AliasID=".$AliasID); if ($DB->record_count() === 1) { - //This is the last alias on the artist - error("That alias is the last alias for that artist; removing it would cause bad things to happen."); + //This is the last alias on the artist + error("That alias is the last alias for that artist; removing it would cause bad things to happen."); } $DB->query(" - SELECT GroupID - FROM torrents_artists - WHERE AliasID='$AliasID'"); + SELECT GroupID + FROM torrents_artists + WHERE AliasID='$AliasID'"); if ($DB->has_results()) { - list($GroupID) = $DB->next_record(); - if ($GroupID != 0) { - error("That alias still has the group ($GroupID) attached. Fix that first."); - } + list($GroupID) = $DB->next_record(); + if ($GroupID != 0) { + error("That alias still has the group ($GroupID) attached. Fix that first."); + } } $DB->query(" - SELECT aa.ArtistID, ag.Name, aa.Name - FROM artists_alias AS aa - JOIN artists_group AS ag ON aa.ArtistID=ag.ArtistID - WHERE aa.AliasID=$AliasID"); + SELECT aa.ArtistID, ag.Name, aa.Name + FROM artists_alias AS aa + JOIN artists_group AS ag ON aa.ArtistID=ag.ArtistID + WHERE aa.AliasID=$AliasID"); list($ArtistID, $ArtistName, $AliasName) = $DB->next_record(MYSQLI_NUM, false); $DB->query(" - DELETE FROM artists_alias - WHERE AliasID='$AliasID'"); + DELETE FROM artists_alias + WHERE AliasID='$AliasID'"); $DB->query(" - UPDATE artists_alias - SET Redirect='0' - WHERE Redirect='$AliasID'"); + UPDATE artists_alias + SET Redirect='0' + WHERE Redirect='$AliasID'"); Misc::write_log("The alias $AliasID ($AliasName) was removed from the artist $ArtistID ($ArtistName) by user $LoggedUser[ID] ($LoggedUser[Username])"); diff --git a/sections/artist/delete_similar.php b/sections/artist/delete_similar.php index 5e764fe60..a633d587f 100644 --- a/sections/artist/delete_similar.php +++ b/sections/artist/delete_similar.php @@ -4,30 +4,30 @@ $PrimaryArtistID = intval($_GET['artistid']); if (!is_number($SimilarID) || !$SimilarID) { - error(404); + error(404); } if (!check_perms('site_delete_tag')) { - error(403); + error(403); } $DB->query(" - SELECT ArtistID - FROM artists_similar - WHERE SimilarID = '$SimilarID'"); + SELECT ArtistID + FROM artists_similar + WHERE SimilarID = '$SimilarID'"); $ArtistIDs = $DB->to_array(); $DB->query(" - DELETE FROM artists_similar - WHERE SimilarID = '$SimilarID'"); + DELETE FROM artists_similar + WHERE SimilarID = '$SimilarID'"); $DB->query(" - DELETE FROM artists_similar_scores - WHERE SimilarID = '$SimilarID'"); + DELETE FROM artists_similar_scores + WHERE SimilarID = '$SimilarID'"); $DB->query(" - DELETE FROM artists_similar_votes - WHERE SimilarID = '$SimilarID'"); + DELETE FROM artists_similar_votes + WHERE SimilarID = '$SimilarID'"); foreach ($ArtistIDs as $ArtistID) { - list($ArtistID) = $ArtistID; - $Cache->delete_value("artist_$ArtistID"); // Delete artist cache - $Cache->delete_value("similar_positions_$ArtistID"); + list($ArtistID) = $ArtistID; + $Cache->delete_value("artist_$ArtistID"); // Delete artist cache + $Cache->delete_value("similar_positions_$ArtistID"); } $Location = (empty($_SERVER['HTTP_REFERER'])) ? "artist.php?id={$PrimaryArtistID}" : $_SERVER['HTTP_REFERER']; diff --git a/sections/artist/download.php b/sections/artist/download.php index 7ec57041d..24e523e9d 100644 --- a/sections/artist/download.php +++ b/sections/artist/download.php @@ -1,4 +1,4 @@ - 2 - || count($_REQUEST['list']) === 0 + !isset($_REQUEST['artistid']) + || !isset($_REQUEST['preference']) + || !is_number($_REQUEST['preference']) + || !is_number($_REQUEST['artistid']) + || $_REQUEST['preference'] > 2 + || count($_REQUEST['list']) === 0 ) { - error(0); + error(0); } if (!check_perms('zip_downloader')) { - error(403); + error(403); } -$Preferences = array('RemasterTitle DESC', 'Seeders ASC', 'Size ASC'); +$Preferences = array('t.RemasterTitle DESC', 'tls.Seeders ASC', 't.Size ASC'); $ArtistID = $_REQUEST['artistid']; $Preference = $Preferences[$_REQUEST['preference']]; -$DB->query(" - SELECT Name - FROM artists_group - WHERE ArtistID = '$ArtistID'"); +$DB->prepared_query(' + SELECT Name + FROM artists_group + WHERE ArtistID = ? + ', $ArtistID +); list($ArtistName) = $DB->next_record(MYSQLI_NUM, false); -$DB->query(" - SELECT GroupID, Importance - FROM torrents_artists - WHERE ArtistID = '$ArtistID'"); +$DB->prepared_query(' + SELECT GroupID, Importance + FROM torrents_artists + WHERE ArtistID = ? + ', $ArtistID +); if (!$DB->has_results()) { - error(404); + error(404); } -$ArtistRoles = array(); +$ArtistRoles = []; while (list($GroupID, $Importance) = $DB->next_record(MYSQLI_NUM, false)) { - // Get the highest importances to place the .torrents in the most relevant folders - if (!isset($ArtistRoles[$GroupID]) || $Importance < $ArtistRoles[$GroupID]) { - $ArtistRoles[$GroupID] = $Importance; - } + // Get the highest importances to place the .torrents in the most relevant folders + if (!isset($ArtistRoles[$GroupID]) || $Importance < $ArtistRoles[$GroupID]) { + $ArtistRoles[$GroupID] = $Importance; + } } $GroupIDs = array_keys($ArtistRoles); $SQL = 'SELECT CASE '; foreach ($_REQUEST['list'] as $Priority => $Selection) { - if (!is_number($Priority)) { - continue; - } - $SQL .= 'WHEN '; - switch ($Selection) { - case '00': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'V0 (VBR)'"; break; - case '01': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'APX (VBR)'"; break; - case '02': $SQL .= "t.Format = 'MP3' AND t.Encoding = '256 (VBR)'"; break; - case '03': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'V1 (VBR)'"; break; - case '10': $SQL .= "t.Format = 'MP3' AND t.Encoding = '224 (VBR)'"; break; - case '11': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'V2 (VBR)'"; break; - case '12': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'APS (VBR)'"; break; - case '13': $SQL .= "t.Format = 'MP3' AND t.Encoding = '192 (VBR)'"; break; - case '20': $SQL .= "t.Format = 'MP3' AND t.Encoding = '320'"; break; - case '21': $SQL .= "t.Format = 'MP3' AND t.Encoding = '256'"; break; - case '22': $SQL .= "t.Format = 'MP3' AND t.Encoding = '224'"; break; - case '23': $SQL .= "t.Format = 'MP3' AND t.Encoding = '192'"; break; - case '30': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'Vinyl'"; break; - case '31': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'DVD'"; break; - case '32': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'SACD'"; break; - case '33': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'WEB'"; break; - case '34': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless' AND HasLog = '1' AND LogScore = '100' AND HasCue = '1'"; break; - case '35': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless' AND HasLog = '1' AND LogScore = '100'"; break; - case '36': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless' AND HasLog = '1'"; break; - case '37': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless'"; break; - case '40': $SQL .= "t.Format = 'DTS'"; break; - case '42': $SQL .= "t.Format = 'AAC' AND t.Encoding = '320'"; break; - case '43': $SQL .= "t.Format = 'AAC' AND t.Encoding = '256'"; break; - case '44': $SQL .= "t.Format = 'AAC' AND t.Encoding = 'q5.5'"; break; - case '45': $SQL .= "t.Format = 'AAC' AND t.Encoding = 'q5'"; break; - case '46': $SQL .= "t.Format = 'AAC' AND t.Encoding = '192'"; break; - default: error(0); - } - $SQL .= " THEN $Priority "; + if (!is_number($Priority)) { + continue; + } + $SQL .= 'WHEN '; + switch ($Selection) { + case '00': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'V0 (VBR)'"; break; + case '01': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'APX (VBR)'"; break; + case '02': $SQL .= "t.Format = 'MP3' AND t.Encoding = '256 (VBR)'"; break; + case '03': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'V1 (VBR)'"; break; + case '10': $SQL .= "t.Format = 'MP3' AND t.Encoding = '224 (VBR)'"; break; + case '11': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'V2 (VBR)'"; break; + case '12': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'APS (VBR)'"; break; + case '13': $SQL .= "t.Format = 'MP3' AND t.Encoding = '192 (VBR)'"; break; + case '20': $SQL .= "t.Format = 'MP3' AND t.Encoding = '320'"; break; + case '21': $SQL .= "t.Format = 'MP3' AND t.Encoding = '256'"; break; + case '22': $SQL .= "t.Format = 'MP3' AND t.Encoding = '224'"; break; + case '23': $SQL .= "t.Format = 'MP3' AND t.Encoding = '192'"; break; + case '30': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'Vinyl'"; break; + case '31': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'DVD'"; break; + case '32': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'SACD'"; break; + case '33': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'WEB'"; break; + case '34': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless' AND HasLog = '1' AND LogScore = '100' AND HasCue = '1'"; break; + case '35': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless' AND HasLog = '1' AND LogScore = '100'"; break; + case '36': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless' AND HasLog = '1'"; break; + case '37': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless'"; break; + case '40': $SQL .= "t.Format = 'DTS'"; break; + case '42': $SQL .= "t.Format = 'AAC' AND t.Encoding = '320'"; break; + case '43': $SQL .= "t.Format = 'AAC' AND t.Encoding = '256'"; break; + case '44': $SQL .= "t.Format = 'AAC' AND t.Encoding = 'q5.5'"; break; + case '45': $SQL .= "t.Format = 'AAC' AND t.Encoding = 'q5'"; break; + case '46': $SQL .= "t.Format = 'AAC' AND t.Encoding = '192'"; break; + default: error(0); + } + $SQL .= " THEN $Priority "; } $SQL .= " - ELSE 100 - END AS Rank, - t.GroupID, - t.ID AS TorrentID, - t.Media, - t.Format, - t.Encoding, - tg.ReleaseType, - IF(t.RemasterYear = 0, tg.Year, t.RemasterYear) AS Year, - tg.Name, - t.Size + ELSE 100 + END AS Rank, + t.GroupID, + t.ID AS TorrentID, + t.Media, + t.Format, + t.Encoding, + tg.ReleaseType, + IF(t.RemasterYear = 0, tg.Year, t.RemasterYear) AS Year, + tg.Name, + t.Size FROM torrents AS t - JOIN torrents_group AS tg ON tg.ID = t.GroupID AND tg.CategoryID = '1' AND tg.ID IN (".implode(',', $GroupIDs).") -ORDER BY t.GroupID ASC, Rank DESC, t.$Preference"; +INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID) /* FIXME: only needed if sorting by Seeders */ +INNER JOIN torrents_group AS tg ON tg.ID = t.GroupID AND tg.CategoryID = '1' AND tg.ID IN (".implode(',', $GroupIDs).") +ORDER BY t.GroupID ASC, Rank DESC, $Preference"; $DownloadsQ = $DB->query($SQL); $Collector = new TorrentsDL($DownloadsQ, $ArtistName); while (list($Downloads, $GroupIDs) = $Collector->get_downloads('GroupID')) { - $Artists = Artists::get_artists($GroupIDs); - $TorrentFilesQ = $DB->query(" - SELECT TorrentID, File - FROM torrents_files - WHERE TorrentID IN (".implode(',', array_keys($GroupIDs)).')', false); - if (is_int($TorrentFilesQ)) { - // Query failed. Let's not create a broken zip archive - foreach ($GroupIDs as $GroupID) { - $Download =& $Downloads[$GroupID]; - $Download['Artist'] = Artists::display_artists($Artists[$GroupID], false, true, false); - $Collector->fail_file($Download); - } - continue; - } - while (list($TorrentID, $TorrentFile) = $DB->next_record(MYSQLI_NUM, false)) { - $GroupID = $GroupIDs[$TorrentID]; - $Download =& $Downloads[$GroupID]; - $Download['Artist'] = Artists::display_artists($Artists[$Download['GroupID']], false, true, false); - if ($Download['Rank'] == 100) { - $Collector->skip_file($Download); - continue; - } - switch ($ArtistRoles[$GroupID]) { - /** - * 1 => Main artist - * 2 => Guest artist - * 3 => Remixer - * 4 => Composer - * 5 => Conductor - * 6 => DJ / Compiler - * 7 => Producer - */ - case '1': - $ReleaseTypeName = $ReleaseTypes[$Download['ReleaseType']]; - break; - case '2': - $ReleaseTypeName = 'Guest Appearance'; - break; - case '3': - $ReleaseTypeName = 'Remixed By'; - break; - case '4': - $ReleaseTypeName = 'Composition'; - break; - case '5': - $ReleaseTypeName = 'Conducted By'; - break; - case '6': - $ReleaseTypeName = 'DJ Mix'; - break; - case '7': - $ReleaseTypeName = 'Produced By'; - break; - default: - $ReleaseTypeName = 'Other'; - break; - } - $Collector->add_file($TorrentFile, $Download, $ReleaseTypeName); - unset($Download); - } + $Artists = Artists::get_artists($GroupIDs); + $TorrentFilesQ = $DB->query(" + SELECT TorrentID, File + FROM torrents_files + WHERE TorrentID IN (".implode(',', array_keys($GroupIDs)).')', false); + if (is_int($TorrentFilesQ)) { + // Query failed. Let's not create a broken zip archive + foreach ($GroupIDs as $GroupID) { + $Download =& $Downloads[$GroupID]; + $Download['Artist'] = Artists::display_artists($Artists[$GroupID], false, true, false); + $Collector->fail_file($Download); + } + continue; + } + while (list($TorrentID, $TorrentFile) = $DB->next_record(MYSQLI_NUM, false)) { + $GroupID = $GroupIDs[$TorrentID]; + $Download =& $Downloads[$GroupID]; + $Download['Artist'] = Artists::display_artists($Artists[$Download['GroupID']], false, true, false); + if ($Download['Rank'] == 100) { + $Collector->skip_file($Download); + continue; + } + switch ($ArtistRoles[$GroupID]) { + /** + * 1 => Main artist + * 2 => Guest artist + * 3 => Remixer + * 4 => Composer + * 5 => Conductor + * 6 => DJ / Compiler + * 7 => Producer + */ + case '1': + $ReleaseTypeName = $ReleaseTypes[$Download['ReleaseType']]; + break; + case '2': + $ReleaseTypeName = 'Guest Appearance'; + break; + case '3': + $ReleaseTypeName = 'Remixed By'; + break; + case '4': + $ReleaseTypeName = 'Composition'; + break; + case '5': + $ReleaseTypeName = 'Conducted By'; + break; + case '6': + $ReleaseTypeName = 'DJ Mix'; + break; + case '7': + $ReleaseTypeName = 'Produced By'; + break; + default: + $ReleaseTypeName = 'Other'; + break; + } + $Collector->add_file($TorrentFile, $Download, $ReleaseTypeName); + unset($Download); + } } $Collector->finalize(); $Settings = array(implode(':', $_REQUEST['list']), $_REQUEST['preference']); if (!isset($LoggedUser['Collector']) || $LoggedUser['Collector'] != $Settings) { - Users::update_site_options($LoggedUser['ID'], array('Collector' => $Settings)); + Users::update_site_options($LoggedUser['ID'], array('Collector' => $Settings)); } define('SKIP_NO_CACHE_HEADERS', 1); -?> diff --git a/sections/artist/edit.php b/sections/artist/edit.php index 241eec982..7ee7cc912 100644 --- a/sections/artist/edit.php +++ b/sections/artist/edit.php @@ -1,4 +1,4 @@ -query(" - SELECT - Name, - Image, - Body, - VanityHouse - FROM artists_group AS a - LEFT JOIN wiki_artists ON wiki_artists.RevisionID = a.RevisionID - WHERE a.ArtistID = '$ArtistID'"); + SELECT + Name, + Image, + Body, + VanityHouse + FROM artists_group AS a + LEFT JOIN wiki_artists ON wiki_artists.RevisionID = a.RevisionID + WHERE a.ArtistID = '$ArtistID'"); if (!$DB->has_results()) { - error("Cannot find an artist with the ID {$ArtistID}: See the site log."); + error("Cannot find an artist with the ID {$ArtistID}: See the site log."); } list($Name, $Image, $Body, $VanityHouse) = $DB->next_record(MYSQLI_NUM, true); @@ -36,129 +36,133 @@ View::show_header('Edit artist'); ?>
    -
    -

    Edit

    -
    -
    -
    - - - -
    -

    Image:

    -
    -

    Artist information:

    -
    -

    - -

    -

    Edit summary:

    -
    -
    - -
    -
    -
    -
    - -

    Rename this artist

    -
    -
    - - - -
    - -
    - -
    -
    -
    -
    +
    +

    Edit

    +
    +
    +
    + + + +
    +

    Image:

    +
    +

    Artist information:

    +
    +

    + +

    +

    Edit summary:

    +
    +
    + +
    +
    +
    +
    + +

    Rename this artist

    +
    +
    + + + +
    + +
    + +
    +
    +
    +
    -

    Make into non-redirecting alias

    -
    -
    - - - -
    -

    Merges this artist ("") into the artist specified below (without redirection), so that ("") and its aliases will appear as a non-redirecting alias of the artist entered in the text box below.


    -
    -  
    - OR
    -   -

    - -
    -
    -
    -
    +

    Make into non-redirecting alias

    +
    +
    + + + +
    +

    Merges this artist ("") into the artist specified below (without redirection), so that ("") and its aliases will appear as a non-redirecting alias of the artist entered in the text box below.


    +
    +  
    + OR
    +   +

    + +
    +
    +
    +
    -

    Artist aliases

    -
    -

    List of existing artist aliases

    -
    -
      +

      Artist aliases

      +
      +

      List of existing artist aliases

      +
      +
        -query(" - SELECT AliasID, Name, UserID, Redirect - FROM artists_alias - WHERE ArtistID = '$ArtistID'"); - while (list($AliasID, $AliasName, $User, $Redirect) = $DB->next_record(MYSQLI_NUM, true)) { - if ($AliasName == $Name) { - $DefaultRedirectID = $AliasID; - } +query(" + SELECT AliasID, Name, UserID, Redirect + FROM artists_alias + WHERE ArtistID = '$ArtistID'"); + while (list($AliasID, $AliasName, $User, $Redirect) = $DB->next_record(MYSQLI_NUM, true)) { + if ($AliasName == $Name) { + $DefaultRedirectID = $AliasID; + } ?> -
      • - . - - User - - (writes redirect to ) - + . + + User + + (writes redirect to ) + - X -
      • -&auth=" title="Delete this alias" class="brackets tooltip">X + + -
      -
      -
      -

      Add a new artist alias

      -
      -

      This redirects artist names as they are written (e.g. when new torrents are uploaded or artists added). All uses of this new alias will be redirected to the alias ID you enter here. Use for common misspellings, inclusion of diacritical marks, etc.

      -
      - - - -
      - Name: -
      - -
      -
      - Writes redirect to: -
      -
      -
      - -
      -
      -
      -
      - +
    +
    +
    +

    Add a new artist alias

    +
    +

    This redirects artist names as they are written (e.g. when new torrents are uploaded or artists added). All uses of this new alias will be redirected to the alias ID you enter here. Use for common misspellings, inclusion of diacritical marks, etc.

    +
    + + + +
    + Name: +
    + +
    +
    + Writes redirect to: +
    +
    +
    + +
    +
    +
    +
    +
    - + diff --git a/sections/artist/editrequest.php b/sections/artist/editrequest.php index 3a1a80ceb..f2bf20fd4 100644 --- a/sections/artist/editrequest.php +++ b/sections/artist/editrequest.php @@ -1,69 +1,69 @@ prepared_query("SELECT - Name, - VanityHouse - FROM artists_group - WHERE ArtistID = ?", $ArtistID); + Name, + VanityHouse + FROM artists_group + WHERE ArtistID = ?", $ArtistID); if (!$DB->has_results()) { - error(404); + error(404); } list($Name, $VanityHouseArtist) = $DB->fetch_record(); if ($VanityHouseArtist) { - $Name .= ' [Vanity House]'; + $Name .= ' [Vanity House]'; } View::show_header("Request an Edit: " . $Name); ?>
    -
    -

    Request an Edit:

    -
    -
    -
    -

    You are requesting an edit for...

    -

    -
    -
    -

    - Please detail all information that needs to be edited for the artist. Include all relevant links (discogs, musicbrainz, etc.).

    - This will not generate a report, but will create a thread in the Editing forum.

    +

    +

    Request an Edit:

    +
    +
    +
    +

    You are requesting an edit for...

    +

    +
    +
    +

    + Please detail all information that needs to be edited for the artist. Include all relevant links (discogs, musicbrainz, etc.).

    + This will not generate a report, but will create a thread in the Editing forum.

    - What this form can be used for: -

    -
      -
    • Renaming the artist
    • -
    • Non-redirecting or redirecting aliases
    • -
    • Adding/Deleting aliases
    • -
    • etc...
    • -
    -

    Do NOT use this form for individual torrents or torrent groups. For individual - torrents, use the torrent report feature. For torrent groups, go to their respective - pages and use the edit request feature.

    -
    -
    -

    Edit Details

    + What this form can be used for: +

    +
      +
    • Renaming the artist
    • +
    • Non-redirecting or redirecting aliases
    • +
    • Adding/Deleting aliases
    • +
    • etc...
    • +
    +

    Do NOT use this form for individual torrents or torrent groups. For individual + torrents, use the torrent report feature. For torrent groups, go to their respective + pages and use the edit request feature.

    +
    +
    +

    Edit Details

    -
    -
    - - - -

    - -
    -
    -
    -
    +
    +
    + + + +

    + +
    +
    +
    +
    query(" - SELECT Name - FROM artists_group - WHERE ArtistID = $ArtistID"); + SELECT Name + FROM artists_group + WHERE ArtistID = $ArtistID"); if (!$DB->has_results()) { - error(404); + error(404); } list($Name) = $DB->next_record(); View::show_header("Revision history for $Name"); ?>
    -
    -

    Revision history for

    -
    - +

    Revision history for

    +
    + -query(" - SELECT ArtistID, Name - FROM artists_alias - WHERE Name LIKE '" . db_string($NameSearch) . "'"); - if (!$DB->has_results()) { - if (isset($LoggedUser['SearchType']) && $LoggedUser['SearchType']) { - header('Location: torrents.php?action=advanced&artistname=' . urlencode($_GET['artistname'])); - } else { - header('Location: torrents.php?searchstr=' . urlencode($_GET['artistname'])); - } - die(); - } - list($FirstID, $Name) = $DB->next_record(MYSQLI_NUM, false); - if ($DB->record_count() === 1 || !strcasecmp($Name, $NameSearch)) { - header("Location: artist.php?id=$FirstID"); - die(); - } - while (list($ID, $Name) = $DB->next_record(MYSQLI_NUM, false)) { - if (!strcasecmp($Name, $NameSearch)) { - header("Location: artist.php?id=$ID"); - die(); - } - } - header("Location: artist.php?id=$FirstID"); - die(); - } else { - header('Location: torrents.php'); - } + $NameSearch = str_replace('\\', '\\\\', trim($_GET['artistname'])); + $DB->query(" + SELECT ArtistID, Name + FROM artists_alias + WHERE Name LIKE '" . db_string($NameSearch) . "'"); + if (!$DB->has_results()) { + if (isset($LoggedUser['SearchType']) && $LoggedUser['SearchType']) { + header('Location: torrents.php?action=advanced&artistname=' . urlencode($_GET['artistname'])); + } else { + header('Location: torrents.php?searchstr=' . urlencode($_GET['artistname'])); + } + die(); + } + list($FirstID, $Name) = $DB->next_record(MYSQLI_NUM, false); + if ($DB->record_count() === 1 || !strcasecmp($Name, $NameSearch)) { + header("Location: artist.php?id=$FirstID"); + die(); + } + while (list($ID, $Name) = $DB->next_record(MYSQLI_NUM, false)) { + if (!strcasecmp($Name, $NameSearch)) { + header("Location: artist.php?id=$ID"); + die(); + } + } + header("Location: artist.php?id=$FirstID"); + die(); + } else { + header('Location: torrents.php'); + } } ?> diff --git a/sections/artist/notify.php b/sections/artist/notify.php index 752de6e42..a6b0f8003 100644 --- a/sections/artist/notify.php +++ b/sections/artist/notify.php @@ -1,56 +1,56 @@ query(" - SELECT GROUP_CONCAT(Name SEPARATOR '|') - FROM artists_alias - WHERE ArtistID = '$ArtistID' - AND Redirect = 0 - GROUP BY ArtistID"); + SELECT GROUP_CONCAT(Name SEPARATOR '|') + FROM artists_alias + WHERE ArtistID = '$ArtistID' + AND Redirect = 0 + GROUP BY ArtistID"); list($ArtistAliases) = $DB->next_record(MYSQLI_NUM, FALSE); $Notify = $Cache->get_value('notify_artists_'.$LoggedUser['ID']); if (empty($Notify)) { - $DB->query(" - SELECT ID, Artists - FROM users_notify_filters - WHERE Label = 'Artist notifications' - AND UserID = '$LoggedUser[ID]' - ORDER BY ID - LIMIT 1"); + $DB->query(" + SELECT ID, Artists + FROM users_notify_filters + WHERE Label = 'Artist notifications' + AND UserID = '$LoggedUser[ID]' + ORDER BY ID + LIMIT 1"); } else { - $DB->query(" - SELECT ID, Artists - FROM users_notify_filters - WHERE ID = '$Notify[ID]'"); + $DB->query(" + SELECT ID, Artists + FROM users_notify_filters + WHERE ID = '$Notify[ID]'"); } if (empty($Notify) && !$DB->has_results()) { - $DB->query(" - INSERT INTO users_notify_filters - (UserID, Label, Artists) - VALUES - ('$LoggedUser[ID]', 'Artist notifications', '|".db_string($ArtistAliases)."|')"); - $FilterID = $DB->inserted_id(); - $Cache->delete_value('notify_filters_'.$LoggedUser['ID']); - $Cache->delete_value('notify_artists_'.$LoggedUser['ID']); + $DB->query(" + INSERT INTO users_notify_filters + (UserID, Label, Artists) + VALUES + ('$LoggedUser[ID]', 'Artist notifications', '|".db_string($ArtistAliases)."|')"); + $FilterID = $DB->inserted_id(); + $Cache->delete_value('notify_filters_'.$LoggedUser['ID']); + $Cache->delete_value('notify_artists_'.$LoggedUser['ID']); } else { - list($ID, $ArtistNames) = $DB->next_record(MYSQLI_NUM, FALSE); - if (stripos($ArtistNames, "|$ArtistAliases|") === false) { - $ArtistNames .= "$ArtistAliases|"; - $DB->query(" - UPDATE users_notify_filters - SET Artists = '".db_string($ArtistNames)."' - WHERE ID = '$ID'"); - $Cache->delete_value('notify_filters_'.$LoggedUser['ID']); - $Cache->delete_value('notify_artists_'.$LoggedUser['ID']); - } + list($ID, $ArtistNames) = $DB->next_record(MYSQLI_NUM, FALSE); + if (stripos($ArtistNames, "|$ArtistAliases|") === false) { + $ArtistNames .= "$ArtistAliases|"; + $DB->query(" + UPDATE users_notify_filters + SET Artists = '".db_string($ArtistNames)."' + WHERE ID = '$ID'"); + $Cache->delete_value('notify_filters_'.$LoggedUser['ID']); + $Cache->delete_value('notify_artists_'.$LoggedUser['ID']); + } } $Location = (empty($_SERVER['HTTP_REFERER'])) ? "artist.php?id={$ArtistID}" : $_SERVER['HTTP_REFERER']; diff --git a/sections/artist/notifyremove.php b/sections/artist/notifyremove.php index 8fd0aa624..e4cecb56a 100644 --- a/sections/artist/notifyremove.php +++ b/sections/artist/notifyremove.php @@ -1,47 +1,47 @@ -get_value('notify_artists_'.$LoggedUser['ID'])) === false) { - $DB->query(" - SELECT ID, Artists - FROM users_notify_filters - WHERE Label = 'Artist notifications' - AND UserID = '$LoggedUser[ID]' - ORDER BY ID - LIMIT 1"); + $DB->query(" + SELECT ID, Artists + FROM users_notify_filters + WHERE Label = 'Artist notifications' + AND UserID = '$LoggedUser[ID]' + ORDER BY ID + LIMIT 1"); } else { - $DB->query(" - SELECT ID, Artists - FROM users_notify_filters - WHERE ID = '$Notify[ID]'"); + $DB->query(" + SELECT ID, Artists + FROM users_notify_filters + WHERE ID = '$Notify[ID]'"); } list($ID, $Artists) = $DB->next_record(MYSQLI_NUM, false); $DB->query(" - SELECT Name - FROM artists_alias - WHERE ArtistID = '$ArtistID' - AND Redirect = 0"); + SELECT Name + FROM artists_alias + WHERE ArtistID = '$ArtistID' + AND Redirect = 0"); while (list($Alias) = $DB->next_record(MYSQLI_NUM, false)) { - while (stripos($Artists, "|$Alias|") !== false) { - $Artists = str_ireplace("|$Alias|", '|', $Artists); - } + while (stripos($Artists, "|$Alias|") !== false) { + $Artists = str_ireplace("|$Alias|", '|', $Artists); + } } if ($Artists == '|') { - $DB->query(" - DELETE FROM users_notify_filters - WHERE ID = $ID"); + $DB->query(" + DELETE FROM users_notify_filters + WHERE ID = $ID"); } else { - $DB->query(" - UPDATE users_notify_filters - SET Artists = '".db_string($Artists)."' - WHERE ID = '$ID'"); + $DB->query(" + UPDATE users_notify_filters + SET Artists = '".db_string($Artists)."' + WHERE ID = '$ID'"); } $Cache->delete_value('notify_filters_'.$LoggedUser['ID']); $Cache->delete_value('notify_artists_'.$LoggedUser['ID']); diff --git a/sections/artist/rename.php b/sections/artist/rename.php index 453c90dd5..6ab0c0a52 100644 --- a/sections/artist/rename.php +++ b/sections/artist/rename.php @@ -26,210 +26,210 @@ $NewName = Artists::normalise_artist_name($_POST['name']); if (!$ArtistID || !is_number($ArtistID)) { - error(404); + error(404); } if (!check_perms('torrents_edit')) { - error(403); + error(403); } $DB->query(" - SELECT Name - FROM artists_group - WHERE ArtistID = '$ArtistID'"); + SELECT Name + FROM artists_group + WHERE ArtistID = '$ArtistID'"); if (!$DB->has_results()) { - error(404); + error(404); } list($OldName) = $DB->next_record(MYSQLI_NUM, false); if ($OldName == $NewName) { - error('The new name is identical to the old name.'); + error('The new name is identical to the old name.'); } $DB->query(" - SELECT AliasID - FROM artists_alias - WHERE Name = '".db_string($OldName)."' - AND ArtistID = '$ArtistID'"); + SELECT AliasID + FROM artists_alias + WHERE Name = '".db_string($OldName)."' + AND ArtistID = '$ArtistID'"); list($OldAliasID) = $DB->next_record(MYSQLI_NUM, false); if (!$OldAliasID) { - error('Could not find old alias ID'); + error('Could not find old alias ID'); } $DB->query(" - SELECT AliasID, ArtistID - FROM artists_alias - WHERE name LIKE '".db_string($NewName, true)."'"); + SELECT AliasID, ArtistID + FROM artists_alias + WHERE name LIKE '".db_string($NewName, true)."'"); list($TargetAliasID, $TargetArtistID) = $DB->next_record(MYSQLI_NUM, false); if (!$TargetAliasID || $TargetAliasID == $OldAliasID) { - // no merge, just rename - $DB->query(" - INSERT INTO artists_alias - (ArtistID, Name, Redirect, UserID) - VALUES - ($ArtistID, '".db_string($NewName)."', '0', '$LoggedUser[ID]')"); - $TargetAliasID = $DB->inserted_id(); - - $DB->query(" - UPDATE artists_alias - SET Redirect = '$TargetAliasID' - WHERE AliasID = '$OldAliasID'"); - $DB->query(" - UPDATE artists_group - SET Name = '".db_string($NewName)."' - WHERE ArtistID = '$ArtistID'"); - - $DB->query(" - SELECT GroupID - FROM torrents_artists - WHERE AliasID = '$OldAliasID'"); - $Groups = $DB->collect('GroupID'); - $DB->query(" - UPDATE IGNORE torrents_artists - SET AliasID = '$TargetAliasID' - WHERE AliasID = '$OldAliasID'"); - $DB->query(" - DELETE FROM torrents_artists - WHERE AliasID = '$OldAliasID'"); - if (!empty($Groups)) { - foreach ($Groups as $GroupID) { - $Cache->delete_value("groups_artists_$GroupID"); // Delete group artist cache - Torrents::update_hash($GroupID); - } - } - - $DB->query(" - SELECT RequestID - FROM requests_artists - WHERE AliasID = '$OldAliasID'"); - $Requests = $DB->collect('RequestID'); - $DB->query(" - UPDATE IGNORE requests_artists - SET AliasID = '$TargetAliasID' - WHERE AliasID = '$OldAliasID'"); - $DB->query(" - DELETE FROM requests_artists - WHERE AliasID = '$OldAliasID'"); - if (!empty($Requests)) { - foreach ($Requests as $RequestID) { - $Cache->delete_value("request_artists_$RequestID"); // Delete request artist cache - Requests::update_sphinx_requests($RequestID); - } - } - $TargetArtistID = $ArtistID; - -} else { // Merge stuff - $DB->query(" - UPDATE artists_alias - SET Redirect = '$TargetAliasID', ArtistID = '$TargetArtistID' - WHERE AliasID = '$OldAliasID'"); - $DB->query(" - UPDATE artists_alias - SET Redirect = '0' - WHERE AliasID = '$TargetAliasID'"); - if ($ArtistID != $TargetArtistID) { - $DB->query(" - UPDATE artists_alias - SET ArtistID = '$TargetArtistID' - WHERE ArtistID = '$ArtistID'"); - $DB->query(" - DELETE FROM artists_group - WHERE ArtistID = '$ArtistID'"); - } else { - $DB->query(" - UPDATE artists_group - SET Name = '".db_string($NewName)."' - WHERE ArtistID = '$ArtistID'"); - } - - $DB->query(" - SELECT GroupID - FROM torrents_artists - WHERE AliasID = '$OldAliasID'"); - $Groups = $DB->collect('GroupID'); - $DB->query(" - UPDATE IGNORE torrents_artists - SET AliasID = '$TargetAliasID', ArtistID = '$TargetArtistID' - WHERE AliasID = '$OldAliasID'"); - $DB->query(" - DELETE FROM torrents_artists - WHERE AliasID = '$OldAliasID'"); - if (!empty($Groups)) { - foreach ($Groups as $GroupID) { - $Cache->delete_value("groups_artists_$GroupID"); - Torrents::update_hash($GroupID); - } - } - - $DB->query(" - SELECT RequestID - FROM requests_artists - WHERE AliasID = '$OldAliasID'"); - $Requests = $DB->collect('RequestID'); - $DB->query(" - UPDATE IGNORE requests_artists - SET AliasID = '$TargetAliasID', ArtistID = '$TargetArtistID' - WHERE AliasID = '$OldAliasID'"); - $DB->query(" - DELETE FROM requests_artists - WHERE AliasID = '$OldAliasID'"); - if (!empty($Requests)) { - foreach ($Requests as $RequestID) { - $Cache->delete_value("request_artists_$RequestID"); - Requests::update_sphinx_requests($RequestID); - } - } - - if ($ArtistID != $TargetArtistID) { - $DB->query(" - SELECT GroupID - FROM torrents_artists - WHERE ArtistID = '$ArtistID'"); - $Groups = $DB->collect('GroupID'); - $DB->query(" - UPDATE IGNORE torrents_artists - SET ArtistID = '$TargetArtistID' - WHERE ArtistID = '$ArtistID'"); - $DB->query(" - DELETE FROM torrents_artists - WHERE ArtistID = '$ArtistID'"); - if (!empty($Groups)) { - foreach ($Groups as $GroupID) { - $Cache->delete_value("groups_artists_$GroupID"); - Torrents::update_hash($GroupID); - } - } - - $DB->query(" - SELECT RequestID - FROM requests_artists - WHERE ArtistID = '$ArtistID'"); - $Requests = $DB->collect('RequestID'); - $DB->query(" - UPDATE IGNORE requests_artists - SET ArtistID = '$TargetArtistID' - WHERE ArtistID = '$ArtistID'"); - $DB->query(" - DELETE FROM requests_artists - WHERE ArtistID = '$ArtistID'"); - if (!empty($Requests)) { - foreach ($Requests as $RequestID) { - $Cache->delete_value("request_artists_$RequestID"); - Requests::update_sphinx_requests($RequestID); - } - } - - Comments::merge('artist', $ArtistID, $TargetArtistID); - } + // no merge, just rename + $DB->query(" + INSERT INTO artists_alias + (ArtistID, Name, Redirect, UserID) + VALUES + ($ArtistID, '".db_string($NewName)."', '0', '$LoggedUser[ID]')"); + $TargetAliasID = $DB->inserted_id(); + + $DB->query(" + UPDATE artists_alias + SET Redirect = '$TargetAliasID' + WHERE AliasID = '$OldAliasID'"); + $DB->query(" + UPDATE artists_group + SET Name = '".db_string($NewName)."' + WHERE ArtistID = '$ArtistID'"); + + $DB->query(" + SELECT GroupID + FROM torrents_artists + WHERE AliasID = '$OldAliasID'"); + $Groups = $DB->collect('GroupID'); + $DB->query(" + UPDATE IGNORE torrents_artists + SET AliasID = '$TargetAliasID' + WHERE AliasID = '$OldAliasID'"); + $DB->query(" + DELETE FROM torrents_artists + WHERE AliasID = '$OldAliasID'"); + if (!empty($Groups)) { + foreach ($Groups as $GroupID) { + $Cache->delete_value("groups_artists_$GroupID"); // Delete group artist cache + Torrents::update_hash($GroupID); + } + } + + $DB->query(" + SELECT RequestID + FROM requests_artists + WHERE AliasID = '$OldAliasID'"); + $Requests = $DB->collect('RequestID'); + $DB->query(" + UPDATE IGNORE requests_artists + SET AliasID = '$TargetAliasID' + WHERE AliasID = '$OldAliasID'"); + $DB->query(" + DELETE FROM requests_artists + WHERE AliasID = '$OldAliasID'"); + if (!empty($Requests)) { + foreach ($Requests as $RequestID) { + $Cache->delete_value("request_artists_$RequestID"); // Delete request artist cache + Requests::update_sphinx_requests($RequestID); + } + } + $TargetArtistID = $ArtistID; + +} else { // Merge stuff + $DB->query(" + UPDATE artists_alias + SET Redirect = '$TargetAliasID', ArtistID = '$TargetArtistID' + WHERE AliasID = '$OldAliasID'"); + $DB->query(" + UPDATE artists_alias + SET Redirect = '0' + WHERE AliasID = '$TargetAliasID'"); + if ($ArtistID != $TargetArtistID) { + $DB->query(" + UPDATE artists_alias + SET ArtistID = '$TargetArtistID' + WHERE ArtistID = '$ArtistID'"); + $DB->query(" + DELETE FROM artists_group + WHERE ArtistID = '$ArtistID'"); + } else { + $DB->query(" + UPDATE artists_group + SET Name = '".db_string($NewName)."' + WHERE ArtistID = '$ArtistID'"); + } + + $DB->query(" + SELECT GroupID + FROM torrents_artists + WHERE AliasID = '$OldAliasID'"); + $Groups = $DB->collect('GroupID'); + $DB->query(" + UPDATE IGNORE torrents_artists + SET AliasID = '$TargetAliasID', ArtistID = '$TargetArtistID' + WHERE AliasID = '$OldAliasID'"); + $DB->query(" + DELETE FROM torrents_artists + WHERE AliasID = '$OldAliasID'"); + if (!empty($Groups)) { + foreach ($Groups as $GroupID) { + $Cache->delete_value("groups_artists_$GroupID"); + Torrents::update_hash($GroupID); + } + } + + $DB->query(" + SELECT RequestID + FROM requests_artists + WHERE AliasID = '$OldAliasID'"); + $Requests = $DB->collect('RequestID'); + $DB->query(" + UPDATE IGNORE requests_artists + SET AliasID = '$TargetAliasID', ArtistID = '$TargetArtistID' + WHERE AliasID = '$OldAliasID'"); + $DB->query(" + DELETE FROM requests_artists + WHERE AliasID = '$OldAliasID'"); + if (!empty($Requests)) { + foreach ($Requests as $RequestID) { + $Cache->delete_value("request_artists_$RequestID"); + Requests::update_sphinx_requests($RequestID); + } + } + + if ($ArtistID != $TargetArtistID) { + $DB->query(" + SELECT GroupID + FROM torrents_artists + WHERE ArtistID = '$ArtistID'"); + $Groups = $DB->collect('GroupID'); + $DB->query(" + UPDATE IGNORE torrents_artists + SET ArtistID = '$TargetArtistID' + WHERE ArtistID = '$ArtistID'"); + $DB->query(" + DELETE FROM torrents_artists + WHERE ArtistID = '$ArtistID'"); + if (!empty($Groups)) { + foreach ($Groups as $GroupID) { + $Cache->delete_value("groups_artists_$GroupID"); + Torrents::update_hash($GroupID); + } + } + + $DB->query(" + SELECT RequestID + FROM requests_artists + WHERE ArtistID = '$ArtistID'"); + $Requests = $DB->collect('RequestID'); + $DB->query(" + UPDATE IGNORE requests_artists + SET ArtistID = '$TargetArtistID' + WHERE ArtistID = '$ArtistID'"); + $DB->query(" + DELETE FROM requests_artists + WHERE ArtistID = '$ArtistID'"); + if (!empty($Requests)) { + foreach ($Requests as $RequestID) { + $Cache->delete_value("request_artists_$RequestID"); + Requests::update_sphinx_requests($RequestID); + } + } + + Comments::merge('artist', $ArtistID, $TargetArtistID); + } } // Clear torrent caches $DB->query(" - SELECT GroupID - FROM torrents_artists - WHERE ArtistID = '$ArtistID'"); + SELECT GroupID + FROM torrents_artists + WHERE ArtistID = '$ArtistID'"); while (list($GroupID) = $DB->next_record()) { - $Cache->delete_value("torrents_details_$GroupID"); + $Cache->delete_value("torrents_details_$GroupID"); } $Cache->delete_value("artist_$ArtistID"); diff --git a/sections/artist/takeedit.php b/sections/artist/takeedit.php index 43b0496d6..98896f933 100644 --- a/sections/artist/takeedit.php +++ b/sections/artist/takeedit.php @@ -1,4 +1,4 @@ -query(" - INSERT INTO wiki_artists - (PageID, Body, Image, UserID, Summary, Time) - VALUES - ('$ArtistID', '$Body', '$Image', '$UserID', '$Summary', '".sqltime()."')"); + $DB->query(" + INSERT INTO wiki_artists + (PageID, Body, Image, UserID, Summary, Time) + VALUES + ('$ArtistID', '$Body', '$Image', '$UserID', '$Summary', '".sqltime()."')"); } else { // revert - $DB->query(" - INSERT INTO wiki_artists (PageID, Body, Image, UserID, Summary, Time) - SELECT '$ArtistID', Body, Image, '$UserID', 'Reverted to revision $RevisionID', '".sqltime()."' - FROM wiki_artists - WHERE RevisionID = '$RevisionID'"); + $DB->query(" + INSERT INTO wiki_artists (PageID, Body, Image, UserID, Summary, Time) + SELECT '$ArtistID', Body, Image, '$UserID', 'Reverted to revision $RevisionID', '".sqltime()."' + FROM wiki_artists + WHERE RevisionID = '$RevisionID'"); } $RevisionID = $DB->inserted_id(); // Update artists table (technically, we don't need the RevisionID column, but we can use it for a join which is nice and fast) $DB->query(" - UPDATE artists_group - SET - ". (isset($VanityHouse) ? "VanityHouse = '$VanityHouse'," : '') ." - RevisionID = '$RevisionID' - WHERE ArtistID = '$ArtistID'"); + UPDATE artists_group + SET + ". (isset($VanityHouse) ? "VanityHouse = '$VanityHouse'," : '') ." + RevisionID = '$RevisionID' + WHERE ArtistID = '$ArtistID'"); // There we go, all done! $Cache->delete_value("artist_$ArtistID"); // Delete artist cache diff --git a/sections/artist/takeeditrequest.php b/sections/artist/takeeditrequest.php index 5acc6eb93..7168f2e34 100644 --- a/sections/artist/takeeditrequest.php +++ b/sections/artist/takeeditrequest.php @@ -3,7 +3,7 @@ authorize(); if (empty($_POST['artistid']) || !is_numeric($_POST['artistid'])) { - error(403); + error(403); } $EditForumID = EDITING_FORUM_ID; @@ -12,19 +12,19 @@ $ArtistID = intval($_POST['artistid']); $DB->prepared_query("SELECT - Name, - VanityHouse - FROM artists_group - WHERE ArtistID = ?", $ArtistID); + Name, + VanityHouse + FROM artists_group + WHERE ArtistID = ?", $ArtistID); if (!$DB->has_results()) { - error(404); + error(404); } list($Name, $VanityHouseArtist) = $DB->fetch_record(); if ($VanityHouseArtist) { - $Name .= ' [Vanity House]'; + $Name .= ' [Vanity House]'; } $sqltime = sqltime(); @@ -39,109 +39,109 @@ POST; $DB->prepared_query(" - INSERT INTO forums_topics - (Title, AuthorID, ForumID, LastPostTime, LastPostAuthorID, CreatedTime) - Values - (?, ?, ?, ?, ?, ?)", $Title, $BotID, $EditForumID, $sqltime, $BotID, $sqltime); + INSERT INTO forums_topics + (Title, AuthorID, ForumID, LastPostTime, LastPostAuthorID, CreatedTime) + Values + (?, ?, ?, ?, ?, ?)", $Title, $BotID, $EditForumID, $sqltime, $BotID, $sqltime); $TopicID = $DB->inserted_id(); $DB->prepared_query(" - INSERT INTO forums_posts - (TopicID, AuthorID, AddedTime, Body) - VALUES - (?, ?, ?, ?)", $TopicID, $BotID, $sqltime, $Body); + INSERT INTO forums_posts + (TopicID, AuthorID, AddedTime, Body) + VALUES + (?, ?, ?, ?)", $TopicID, $BotID, $sqltime, $Body); $PostID = $DB->inserted_id(); $DB->prepared_query(" - UPDATE forums - SET - NumPosts = NumPosts + 1, - NumTopics = NumTopics + 1, - LastPostID = ?, - LastPostAuthorID = ?, - LastPostTopicID = ?, - LastPostTime = ? - WHERE ID = ?", $PostID, $BotID, $TopicID, $sqltime, $EditForumID); + UPDATE forums + SET + NumPosts = NumPosts + 1, + NumTopics = NumTopics + 1, + LastPostID = ?, + LastPostAuthorID = ?, + LastPostTopicID = ?, + LastPostTime = ? + WHERE ID = ?", $PostID, $BotID, $TopicID, $sqltime, $EditForumID); $DB->prepared_query(" - UPDATE forums_topics - SET - NumPosts = NumPosts + 1, - LastPostID = ?, - LastPostAuthorID = ?, - LastPostTime = ? - WHERE ID = ?", $PostID, $BotID, $sqltime, $TopicID); + UPDATE forums_topics + SET + NumPosts = NumPosts + 1, + LastPostID = ?, + LastPostAuthorID = ?, + LastPostTime = ? + WHERE ID = ?", $PostID, $BotID, $sqltime, $TopicID); // if cache exists modify it, if not, then it will be correct when selected next, and we can skip this block if ($Forum = $Cache->get_value("forums_{$EditForumID}")) { - list($Forum,,,$Stickies) = $Forum; - - // Remove the last thread from the index - if (count($Forum) == TOPICS_PER_PAGE && $Stickies < TOPICS_PER_PAGE) { - array_pop($Forum); - } - - if ($Stickies > 0) { - $Part1 = array_slice($Forum, 0, $Stickies, true); // Stickies - $Part3 = array_slice($Forum, $Stickies, TOPICS_PER_PAGE - $Stickies - 1, true); // Rest of page - } else { - $Part1 = array(); - $Part3 = $Forum; - } - $Part2 = array($TopicID => array( - 'ID' => $TopicID, - 'Title' => $Title, - 'AuthorID' => $BotID, - 'IsLocked' => 0, - 'IsSticky' => 0, - 'NumPosts' => 1, - 'LastPostID' => $PostID, - 'LastPostTime' => $sqltime, - 'LastPostAuthorID' => $BotID, - 'NoPoll' => 1 - )); // Bumped - $Forum = $Part1 + $Part2 + $Part3; - - $Cache->cache_value("forums_{$EditForumID}", array($Forum, '', 0, $Stickies), 0); - - // Update the forum root - $Cache->begin_transaction('forums_list'); - $Cache->update_row($EditForumID, array( - 'NumPosts' => '+1', - 'NumTopics' => '+1', - 'LastPostID' => $PostID, - 'LastPostAuthorID' => $BotID, - 'LastPostTopicID' => $TopicID, - 'LastPostTime' => $sqltime, - 'Title' => $Title, - 'IsLocked' => 0, - 'IsSticky' => 0 - )); - $Cache->commit_transaction(0); + list($Forum,,,$Stickies) = $Forum; + + // Remove the last thread from the index + if (count($Forum) == TOPICS_PER_PAGE && $Stickies < TOPICS_PER_PAGE) { + array_pop($Forum); + } + + if ($Stickies > 0) { + $Part1 = array_slice($Forum, 0, $Stickies, true); // Stickies + $Part3 = array_slice($Forum, $Stickies, TOPICS_PER_PAGE - $Stickies - 1, true); // Rest of page + } else { + $Part1 = []; + $Part3 = $Forum; + } + $Part2 = array($TopicID => array( + 'ID' => $TopicID, + 'Title' => $Title, + 'AuthorID' => $BotID, + 'IsLocked' => 0, + 'IsSticky' => 0, + 'NumPosts' => 1, + 'LastPostID' => $PostID, + 'LastPostTime' => $sqltime, + 'LastPostAuthorID' => $BotID, + 'NoPoll' => 1 + )); // Bumped + $Forum = $Part1 + $Part2 + $Part3; + + $Cache->cache_value("forums_{$EditForumID}", array($Forum, '', 0, $Stickies), 0); + + // Update the forum root + $Cache->begin_transaction('forums_list'); + $Cache->update_row($EditForumID, array( + 'NumPosts' => '+1', + 'NumTopics' => '+1', + 'LastPostID' => $PostID, + 'LastPostAuthorID' => $BotID, + 'LastPostTopicID' => $TopicID, + 'LastPostTime' => $sqltime, + 'Title' => $Title, + 'IsLocked' => 0, + 'IsSticky' => 0 + )); + $Cache->commit_transaction(0); } else { - // If there's no cache, we have no data, and if there's no data - $Cache->delete_value('forums_list'); + // If there's no cache, we have no data, and if there's no data + $Cache->delete_value('forums_list'); } $Cache->begin_transaction("thread_{$TopicID}_catalogue_0"); $Post = [ - 'ID' => $PostID, - 'AuthorID' => $BotID, - 'AddedTime' => $sqltime, - 'Body' => $Body, - 'EditedUserID' => 0, - 'EditedTime' => '0000-00-00 00:00:00' + 'ID' => $PostID, + 'AuthorID' => $BotID, + 'AddedTime' => $sqltime, + 'Body' => $Body, + 'EditedUserID' => 0, + 'EditedTime' => '0000-00-00 00:00:00' ]; $Cache->insert('', $Post); $Cache->commit_transaction(0); $Cache->begin_transaction("thread_{$TopicID}_info"); $Cache->update_row(false, [ - 'Posts' => '+1', - 'LastPostAuthorID' => $BotID, - 'LastPostTime' => $sqltime + 'Posts' => '+1', + 'LastPostAuthorID' => $BotID, + 'LastPostTime' => $sqltime ]); $Cache->commit_transaction(0); diff --git a/sections/artist/vote_similar.php b/sections/artist/vote_similar.php index da2ec523c..104196b63 100644 --- a/sections/artist/vote_similar.php +++ b/sections/artist/vote_similar.php @@ -5,34 +5,34 @@ $Way = db_string($_GET['way']); if (!is_number($SimilarID) || !is_number($ArtistID)) { - error(404); + error(404); } if (!in_array($Way, array('up', 'down'))) { - error(404); + error(404); } $DB->query(" - SELECT SimilarID - FROM artists_similar_votes - WHERE SimilarID='$SimilarID' - AND UserID='$UserID' - AND Way='$Way'"); + SELECT SimilarID + FROM artists_similar_votes + WHERE SimilarID='$SimilarID' + AND UserID='$UserID' + AND Way='$Way'"); if (!$DB->has_results()) { - if ($Way == 'down') { - $Score = 'Score-100'; - } elseif ($Way == 'up') { - $Score = 'Score+100'; - } else { // Nothing is impossible! - $Score = 'Score'; - } - $DB->query(" - UPDATE artists_similar_scores - SET Score=$Score - WHERE SimilarID='$SimilarID'"); - $DB->query(" - INSERT INTO artists_similar_votes (SimilarID, UserID, Way) - VALUES ('$SimilarID', '$UserID', '$Way')"); - $Cache->delete_value('artist_'.$ArtistID); // Delete artist cache + if ($Way == 'down') { + $Score = 'Score-100'; + } elseif ($Way == 'up') { + $Score = 'Score+100'; + } else { // Nothing is impossible! + $Score = 'Score'; + } + $DB->query(" + UPDATE artists_similar_scores + SET Score=$Score + WHERE SimilarID='$SimilarID'"); + $DB->query(" + INSERT INTO artists_similar_votes (SimilarID, UserID, Way) + VALUES ('$SimilarID', '$UserID', '$Way')"); + $Cache->delete_value('artist_'.$ArtistID); // Delete artist cache } $Location = (empty($_SERVER['HTTP_REFERER'])) ? "artist.php?id={$ArtistID}" : $_SERVER['HTTP_REFERER']; diff --git a/sections/better/index.php b/sections/better/index.php index b2bc799bb..8dce26c5b 100644 --- a/sections/better/index.php +++ b/sections/better/index.php @@ -1,54 +1,21 @@ - diff --git a/sections/better/missing.php b/sections/better/missing.php new file mode 100644 index 000000000..b89988390 --- /dev/null +++ b/sections/better/missing.php @@ -0,0 +1,363 @@ + 'torrents_bad_tags', + 'folders' => 'torrents_bad_folders', + 'files' => 'torrents_bad_files', + 'lineage' => 'torrents_missing_lineage' +]; + +if (!empty($_GET['userid']) && is_number($_GET['userid'])) { + if (check_perms('users_override_paranoia')) { + $userId = $_GET['userid']; + } else { + error(403); + } +} else { + $userId = $LoggedUser['ID']; +} + +if (empty($_GET['type']) || !in_array($_GET['type'], ['checksum', 'tags', 'folders', 'files', 'lineage', 'artwork', 'artistimg', 'artistdesc'])) { + $_GET['type'] = 'checksum'; +} +$type = $_GET['type']; + +if (empty($_GET['filter']) || !in_array($_GET['filter'], ['snatched', 'uploaded'])) { + $_GET['filter'] = 'all'; +} +$filter = $_GET['filter']; + +if (empty($_GET['search'])) { + $_GET['search'] = ''; +} +$search = $_GET['search']; + +if (check_perms('admin_reports') && in_array($type, array_keys($badMap)) && !empty($_GET['remove']) && is_number($_GET['remove'])) { + $remove = $_GET['remove']; + $DB->prepared_query(sprintf(' + DELETE FROM %s + WHERE TorrentID = ?', $badMap[$type]), $remove); + $DB->prepared_query(' + SELECT GroupID + FROM torrents + WHERE ID = ?', $remove); + list($groupId) = $DB->next_record(); + $Cache->delete_value('torrents_details_'.$groupId); +} + +$query = ''; +$joins = []; +$where = []; +$order = ''; +$params = []; +$joinparams = []; + +switch ($type) { + case 'checksum': + $query = ' + SELECT SQL_CALC_FOUND_ROWS t.ID AS TorrentID, t.GroupID + FROM torrents t + INNER JOIN torrents_group tg ON tg.ID = t.GroupID'; + $order = 'ORDER BY t.Time ASC'; + $where[] = "t.HasLogDB = '1' AND t.LogChecksum = '0'"; + $mode = 'torrents'; + switch ($filter) { + case 'snatched': + $joins[] = 'INNER JOIN xbt_snatched as x ON x.fid = t.TorrentID AND x.uid = ?'; + $joinparams[] = $userId; + break; + case 'uploaded': + $where[] = 't.UserID = ?'; + $params[] = $userId; + break; + } + break; + case 'tags': + case 'folders': + case 'files': + case 'lineage': + $query = sprintf(' + SELECT SQL_CALC_FOUND_ROWS bad.TorrentID, t.GroupID + FROM %s AS bad + INNER JOIN torrents t ON t.ID = bad.TorrentID + INNER JOIN torrents_group tg ON tg.ID = t.GroupID', $badMap[$type]); + $order = 'ORDER BY bad.TimeAdded ASC'; + $mode = 'torrents'; + switch ($filter) { + case 'snatched': + $joins[] = 'INNER JOIN xbt_snatched as x ON x.fid = bad.TorrentID AND x.uid = ?'; + $joinparams[] = $userId; + break; + case 'uploaded': + $where[] = 't.UserID = ?'; + $params[] = $userId; + break; + } + break; + case 'artwork': + $query = ' + SELECT SQL_CALC_FOUND_ROWS tg.ID, tg.Name + FROM torrents_group tg'; + $where[] = "tg.CategoryID = 1 AND coalesce(wt.Image, tg.WikiImage) = ''"; + $order = 'ORDER BY tg.Name'; + $joins[] = 'LEFT JOIN wiki_torrents wt ON wt.RevisionID = tg.RevisionID'; + $mode = 'groups'; + break; + case 'artistimg': + $query = ' + SELECT SQL_CALC_FOUND_ROWS a.ArtistID, a.Name + FROM artists_group a'; + $where[] = "(wa.Image IS NULL OR wa.Image = '')"; + $order = 'ORDER BY a.Name'; + $joins[] = 'LEFT JOIN wiki_artists wa ON wa.RevisionID = a.RevisionID'; + $mode = 'artists'; + break; + case 'artistdesc': + $query = ' + SELECT SQL_CALC_FOUND_ROWS a.ArtistID, a.Name + FROM artists_group a'; + $where[] = "(wa.Body IS NULL OR wa.Body = '')"; + $order = 'ORDER BY a.Name'; + $joins[] = 'LEFT JOIN wiki_artists wa ON wa.RevisionID = a.RevisionID'; + $mode = 'artists'; + break; +} + +if ($search !== '') { + switch ($mode) { + case 'torrents': + $where[] = '( + tg.Name LIKE ? + OR t.Description LIKE ? + OR coalesce(wt.Body, tg.WikiBody) LIKE ? + OR tg.TagList LIKE ? + )'; + $searchString = "%$search%"; + $params = array_merge($params, array_fill(0, 4, $searchString)); + $joins[] = 'LEFT JOIN wiki_torrents wt ON wt.RevisionID = tg.RevisionID'; + break; + case 'groups': + $where[] = '( + tg.Name LIKE ? + OR coalesce(wt.Body, tg.WikiBody) LIKE ? + OR tg.TagList LIKE ? + )'; + $searchString = "%$search%"; + $params = array_merge($params, array_fill(0, 3, $searchString)); + break; + case 'artists': + $where[] = 'a.Name LIKE ?'; + $searchString = "%$search%"; + $params = array_merge($params, [$searchString]); + break; + } +} + +if (count($where) > 0) { + $where = 'WHERE '.implode(' AND ', $where); +} else { + $where = ''; +} + +$page = !empty($_GET['page']) ? intval($_GET['page']) : 1; +$page = max(1, $page); +$limit = TORRENTS_PER_PAGE; +$offset = TORRENTS_PER_PAGE * ($page - 1); + +$joins = implode("\n", $joins); +$params = array_merge($joinparams, $params); +$query = sprintf(' +%s +%s +%s +%s LIMIT %s OFFSET %s', $query, $joins, $where, $order, $limit, $offset); + +$qId = $DB->prepared_query($query, ...$params); +$DB->prepared_query('SELECT FOUND_ROWS()'); +list($resultCount) = $DB->next_record(); + +$pages = Format::get_pages($page, $resultCount, TORRENTS_PER_PAGE); + +switch ($mode) { + case 'torrents': + if ($resultCount > 0) { + $DB->set_query_id($qId); + $torrents = $DB->to_array('TorrentID', MYSQLI_ASSOC); + } else { + $torrents = []; + } + $groups = array_map(function ($t) { return $t['GroupID']; }, $torrents); + $results = Torrents::get_groups($groups); + break; + case 'groups': + if ($resultCount > 0) { + $DB->set_query_id($qId); + $groups = $DB->to_array('ID', MYSQLI_ASSOC); + foreach (Artists::get_artists(array_keys($groups)) as $groupId => $data) { + $groups[$groupId]['Artists'] = []; + $groups[$groupId]['ExtendedArtists'] = []; + foreach ([1, 4, 6] as $importance) { + if (isset($data[$importance])) { + $groups[$groupId]['Artists'] = array_merge($groups[$groupId]['Artists'], $data[$importance]); + } + } + } + } else { + $groups = []; + } + break; + case 'artists': + if ($resultCount > 0) { + $DB->set_query_id($qId); + $artists = $DB->to_array('ArtistID', MYSQLI_ASSOC); + } else { + $artists = []; + } + break; +} + +View::show_header('Missing Search'); + +function selected($val) { + return $val ? ' selected="selected"' : ''; +} +?> + +
    +
    +

    Missing

    + +
    + + + + + + + + + + + +
    Filter + + +
    Search + +
     
    +
    + +
    +
    +

    There are remaining 1 && check_perms('zip_downloader')) { + $idList = implode(',', array_map(function ($t) { return $t['TorrentID']; }, $torrents)); +?> + + Download All + +

    +
    + + + $info) { + $group = $results[$info['GroupID']]; + $groupId = $group['ID']; + $groupYear = $group['Year']; + $groupName = $group['Name']; + $groupFlags = isset($group['Flags']) ? $group['Flags'] : ['IsSnatched' => false]; + $groupTorrents = isset($group['Torrents']) ? $group['Torrents'] : []; + $releaseType = $group['ReleaseType']; + $tags = new Tags($group['TagList']); + $extendedArtists = $group['ExtendedArtists']; + + if (!empty($extendedArtists[1]) || !empty($extendedArtists[4]) || !empty($extendedArtists[5]) || !empty($extendedArtists[6])) { + unset($extendedArtists[2]); + unset($extendedArtists[3]); + $displayName = Artists::display_artists($extendedArtists); + } else { + $displayName = ''; + } + $displayName .= "$groupName"; + if ($groupYear > 0) { + $displayName .= " [$groupYear]"; + } + if ($releaseType > 0) { + $displayName .= ' ['.$ReleaseTypes[$releaseType].']'; + } + + $extraInfo = Torrents::torrent_info($groupTorrents[$torrent]); + if ($extraInfo) { + $displayName .= " - $extraInfo"; + } +?> + "> + + + $group) { + if (count($group['Artists']) > 1) { + $artist = 'Various Artists'; + } else { + $artist = sprintf('%s', $group['Artists'][0]['id'], $group['Artists'][0]['name']); + } +?> + + + + $artist) { +?> + + + + +
    + + DL + + + + X + +
    format()?>
    +
    -
    +
    +
    + + diff --git a/sections/better/single.php b/sections/better/single.php index fa95e06b7..4ac7a0522 100644 --- a/sections/better/single.php +++ b/sections/better/single.php @@ -1,75 +1,82 @@ -get_value('better_single_groupids')) === false) { - $DB->query(" - SELECT - t.ID AS TorrentID, - t.GroupID AS GroupID - FROM xbt_files_users AS x - JOIN torrents AS t ON t.ID=x.fid - WHERE t.Format='FLAC' - GROUP BY x.fid - HAVING COUNT(x.uid) = 1 - ORDER BY t.LogScore DESC, t.Time ASC - LIMIT 30"); + $DB->query(" + SELECT + t.ID AS TorrentID, + t.GroupID AS GroupID + FROM xbt_files_users AS x + JOIN torrents AS t ON t.ID=x.fid + WHERE t.Format='FLAC' + GROUP BY x.fid + HAVING COUNT(x.uid) = 1 + ORDER BY t.LogScore DESC, t.Time ASC + LIMIT 30"); - $Results = $DB->to_pair('GroupID', 'TorrentID', false); - $Cache->cache_value('better_single_groupids', $Results, 30 * 60); + $Results = $DB->to_pair('GroupID', 'TorrentID', false); + $Cache->cache_value('better_single_groupids', $Results, 30 * 60); } $Groups = Torrents::get_groups(array_keys($Results)); View::show_header('Single seeder FLACs'); ?> - +
    - - - - -Single Seeded + +
    +
    Torrent
    + + + + $FlacID) { - if (!isset($Groups[$GroupID])) { - continue; - } - $Group = $Groups[$GroupID]; - extract(Torrents::array_group($Group)); - $TorrentTags = new Tags($TagList); + if (!isset($Groups[$GroupID])) { + continue; + } + $Group = $Groups[$GroupID]; + extract(Torrents::array_group($Group)); + $TorrentTags = new Tags($TagList); - if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5]) || !empty($ExtendedArtists[6])) { - unset($ExtendedArtists[2]); - unset($ExtendedArtists[3]); - $DisplayName = Artists::display_artists($ExtendedArtists); - } else { - $DisplayName = ''; - } + if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5]) || !empty($ExtendedArtists[6])) { + unset($ExtendedArtists[2]); + unset($ExtendedArtists[3]); + $DisplayName = Artists::display_artists($ExtendedArtists); + } else { + $DisplayName = ''; + } - $DisplayName .= "$GroupName"; - if ($GroupYear > 0) { - $DisplayName .= " [$GroupYear]"; - } - if ($ReleaseType > 0) { - $DisplayName .= " [".$ReleaseTypes[$ReleaseType]."]"; - } + $DisplayName .= "$GroupName"; + if ($GroupYear > 0) { + $DisplayName .= " [$GroupYear]"; + } + if ($ReleaseType > 0) { + $DisplayName .= " [".$ReleaseTypes[$ReleaseType]."]"; + } - $ExtraInfo = Torrents::torrent_info($Torrents[$FlacID]); - if ($ExtraInfo) { - $DisplayName .= ' - '.$ExtraInfo; - } + $ExtraInfo = Torrents::torrent_info($Torrents[$FlacID]); + if ($ExtraInfo) { + $DisplayName .= ' - '.$ExtraInfo; + } ?> - - - - -
    Torrent
    - - DL - - -
    format()?>
    -
    + + + + DL + + +
    format()?>
    + + + + +
    - diff --git a/sections/better/transcode.php b/sections/better/transcode.php index 0b542ce30..cc2f88186 100644 --- a/sections/better/transcode.php +++ b/sections/better/transcode.php @@ -1,186 +1,359 @@ - 3) { - error(0); + 'V0 (VBR)', 'v2' => 'V2 (VBR)', '320' => '320']; -if ($_GET['type'] === '3') { - $List = "!(v0 | v2 | 320)"; -} else { - $List = '!'.$Options[$_GET['type']]; - if ($_GET['type'] !== '0') { - $_GET['type'] = display_str($_GET['type']); - } +function transcode_init_sphql() { + // Initializes a basic SphinxqlQuery object + $sqlQL = new SphinxqlQuery(); + $sqlQL->select('groupid') + ->from('better_transcode') + ->where('logscore', 100) + ->where_match('FLAC', 'format') + ->order_by('RAND()') + ->limit(0, TORRENTS_PER_PAGE, TORRENTS_PER_PAGE); + if (in_array($_GET['target'], ['v0', 'v2', '320'])) { + // V0/V2/320 is missing + $sqlQL->where_match('!'.$_GET['target'], 'encoding', false); + } elseif ($_GET['target'] === 'all') { + // all transcodes are missing + $sqlQL->where_match('!(v0 | v2 | 320)', 'encoding', false); + } else { + // any transcode is missing + $sqlQL->where_match('!(v0 v2 320)', 'encoding', false); + } + if (!empty($_GET['search'])) { + $sqlQL->where_match($_GET['search'], '(groupname,artistname,year,taglist)'); + } + return $sqlQL; } -$SphQL = new SphinxqlQuery(); -$SphQL->select('id, groupid') - ->from('better_transcode') - ->where('logscore', 100) - ->where_match('FLAC', 'format') - ->where_match($List, 'encoding', false) - ->order_by('RAND()') - ->limit(0, TORRENTS_PER_PAGE, TORRENTS_PER_PAGE); -if (!empty($_GET['search'])) { - $SphQL->where_match($_GET['search'], '(groupname,artistname,year,taglist)'); + +function transcode_parse_groups($groups) { + $torrentGroups = []; + foreach ($groups as $groupID => $group) { + if (empty($group['Torrents'])) { + continue; + } + foreach ($group['Torrents'] as $torrentID => $torrent) { + $remIdent = "$torrent[Media] $torrent[RemasterYear] $torrent[RemasterTitle] $torrent[RemasterRecordLabel] $torrent[RemasterCatalogueNumber]"; + if (!isset($torrentGroups[$groupID])) { + $torrentGroups[$groupID] = [ + 'Year' => $group['Year'], + 'ExtendedArtists' => $group['ExtendedArtists'], + 'Name' => $group['Name'], + 'ReleaseType' => $group['ReleaseType'], + 'TagList' => $group['TagList'], + 'Editions' => [] + ]; + } + if (!isset($torrentGroups[$groupID]['Editions'][$remIdent])) { + if ($torrent['Remastered'] && $torrent['RemasterYear'] != 0) { + $editionName = $torrent['RemasterYear']; + $addExtra = ' - '; + if ($torrent['RemasterRecordLabel']) { + $editionName .= $addExtra.display_str($torrent['RemasterRecordLabel']); + $addExtra = ' / '; + } + if ($torrent['RemasterCatalogueNumber']) { + $editionName .= $addExtra.display_str($torrent['RemasterCatalogueNumber']); + $addExtra = ' / '; + } + if ($torrent['RemasterTitle']) { + $editionName .= $addExtra.display_str($torrent['RemasterTitle']); + $addExtra = ' / '; + } + $editionName .= $addExtra.display_str($torrent['Media']); + } else { + $addExtra = ' / '; + if (!$torrent['Remastered']) { + $editionName = 'Original Release'; + if ($group['RecordLabel']) { + $editionName .= $addExtra.$group['RecordLabel']; + $addExtra = ' / '; + } + if ($group['CatalogueNumber']) { + $editionName .= $addExtra.$group['CatalogueNumber']; + $addExtra = ' / '; + } + } else { + $editionName = 'Unknown Release(s)'; + } + $editionName .= $addExtra.display_str($torrent['Media']); + } + $torrentGroups[$groupID]['Editions'][$remIdent] = [ + 'FlacIDs' => [], + 'MP3s' => [], + 'Media' => $torrent['Media'], + 'EditionName' => $editionName, + 'FLACIsSnatched' => false + ]; + } + + if ($torrent['Format'] == 'MP3') { + $torrentGroups[$groupID]['Editions'][$remIdent]['MP3s'][$torrent['Encoding']] = true; + } elseif ($torrent['Format'] == 'FLAC' && ($torrent['LogScore'] == 100 || $torrent['Media'] != 'CD') + && !isset($torrentGroups[$groupID]['Editions'][$remIdent]['FlacIDs'][$torrentID])) { + $torrentGroups[$groupID]['Editions'][$remIdent]['FlacIDs'][$torrentID] = true; + $torrentGroups[$groupID]['Editions'][$remIdent]['FLACIsSnatched'] = $torrentGroups[$groupID]['Editions'][$remIdent]['FLACIsSnatched'] || $torrent['IsSnatched']; + } + } + } + return $torrentGroups; } -$SphQLResult = $SphQL->query(); -$TorrentCount = $SphQLResult->get_meta('total'); +$groups = []; +$resultCount = 0; +if (in_array($_GET['filter'], ['all', 'uploaded'])) { + $sqlQL = transcode_init_sphql(); + if ($_GET['filter'] === 'uploaded') { + $sqlQL->where('uploader', $userId); + } + + $sqlQLResult = $sqlQL->query(); + $resultCount = $sqlQLResult->get_meta('total'); + if ($resultCount != 0) { + $results = $sqlQLResult->collect('groupid'); + $groups = Torrents::get_groups(array_values($results)); + $groups = transcode_parse_groups($groups); + } + unset($sqlQL, $sqlQLResult, $results); +} elseif (in_array($_GET['filter'], ['snatched', 'seeding'])) { + // Read all snatched/seeding torrents + $table = $_GET['filter'] === 'seeding' ? 'xbt_files_users' : 'xbt_snatched'; + $extraFilter = $_GET['filter'] === 'seeding' ? 'AND x.active = 1 AND x.Remaining = 0' : ''; + $DB->prepared_query(sprintf(" + SELECT t.GroupID, x.fid + FROM %s AS x + JOIN torrents AS t ON t.ID=x.fid + JOIN torrents_group AS tg ON tg.ID = t.GroupID + WHERE t.Format = 'FLAC' + AND (t.LogScore = '100' OR t.Media != 'CD') + AND tg.CategoryID = 1 + AND x.uid = ? + %s", $table, $extraFilter), $userId); + $snatched = $DB->to_array(); + shuffle($snatched); // randomize results + while ($resultCount < TORRENTS_PER_PAGE && count($snatched) > 0) { + // we throw TORRENTS_PER_PAGE results into Sphinx until we have at least TORRENTS_PER_PAGE results (or no snatches left) + $snatchedTmp = array_slice($snatched, 0, TORRENTS_PER_PAGE); + $snatched = array_slice($snatched, TORRENTS_PER_PAGE); -if ($TorrentCount == 0) { - error('No results found!'); + $sqlQL = transcode_init_sphql(); + $sqlQL->where('groupid', array_map(function ($row) { return $row['GroupID']; }, $snatchedTmp)); + + $sqlQLResult = $sqlQL->query(); + $resultsTmp = $sqlQLResult->collect('groupid'); + $groupsTmp = Torrents::get_groups(array_values($resultsTmp)); + $groupsTmp = transcode_parse_groups($groupsTmp); + // Since we're asking Sphinxql about groups and remidents, the result can/will contain different editions that are transcodable but weren't snatched, so let's filter them out + foreach ($groupsTmp as $groupID => $group) { + foreach ($group['Editions'] as $remIdent => $edition) { + $editionSnatched = false; + foreach ($snatchedTmp as $snatchedTmpE) { + if (isset($edition['FlacIDs'][$snatchedTmpE['fid']])) { + $editionSnatched = true; + break; + } + } + if (!$editionSnatched || count($edition['MP3s']) === 3) { + unset($groupsTmp[$groupID]['Editions'][$remIdent]); + } + } + $resultCount += count($groupsTmp[$groupID]['Editions']); + if (count($groupsTmp[$groupID]['Editions']) === 0) { + unset($groupsTmp[$groupID]); + } + } + $groups = $groupsTmp + $groups; + unset($snatchedTmp, $sqlQL, $sqlQLResult, $resultsTmp, $groupsTmp); + } } -$Results = $SphQLResult->to_array('groupid'); -$Groups = Torrents::get_groups(array_keys($Results)); -$TorrentGroups = array(); -foreach ($Groups as $GroupID => $Group) { - if (empty($Group['Torrents'])) { - unset($Groups[$GroupID]); - continue; - } - foreach ($Group['Torrents'] as $Torrent) { - $TorRemIdent = "$Torrent[Media] $Torrent[RemasterYear] $Torrent[RemasterTitle] $Torrent[RemasterRecordLabel] $Torrent[RemasterCatalogueNumber]"; - if (!isset($TorrentGroups[$Group['ID']])) { - $TorrentGroups[$Group['ID']] = array( - $TorRemIdent => array( - 'FlacID' => 0, - 'Formats' => array(), - 'RemasterTitle' => $Torrent['RemasterTitle'], - 'RemasterYear' => $Torrent['RemasterYear'], - 'RemasterRecordLabel' => $Torrent['RemasterRecordLabel'], - 'RemasterCatalogueNumber' => $Torrent['RemasterCatalogueNumber'], - 'IsSnatched' => false - ) - ); - } elseif (!isset($TorrentGroups[$Group['ID']][$TorRemIdent])) { - $TorrentGroups[$Group['ID']][$TorRemIdent] = array( - 'FlacID' => 0, - 'Formats' => array(), - 'RemasterTitle' => $Torrent['RemasterTitle'], - 'RemasterYear' => $Torrent['RemasterYear'], - 'RemasterRecordLabel' => $Torrent['RemasterRecordLabel'], - 'RemasterCatalogueNumber' => $Torrent['RemasterCatalogueNumber'], - 'IsSnatched' => false - ); - } - if ($Torrent['Format'] == 'MP3' && isset($EncodingKeys[$Torrent['Encoding']])) { - $TorrentGroups[$Group['ID']][$TorRemIdent]['Formats'][$Torrent['Encoding']] = true; - } elseif ($TorrentGroups[$Group['ID']][$TorRemIdent]['FlacID'] == 0 && $Torrent['Format'] == 'FLAC' && ($Torrent['LogScore'] == 100 || $Torrent['Media'] != 'CD')) { - $TorrentGroups[$Group['ID']][$TorRemIdent]['FlacID'] = $Torrent['ID']; - $TorrentGroups[$Group['ID']][$TorRemIdent]['IsSnatched'] = $Torrent['IsSnatched']; - } - } +$counter = [ + 'total' => 0, //how many FLAC torrents can be transcoded? + 'miss_total' => 0, //how many transcodes are missing? + 'miss_V0 (VBR)' => 0, //how many V0 transcodes are missing? + 'miss_V2 (VBR)' => 0, //how many V2 transcodes are missing? + 'miss_320' => 0, //how many 320 transcodes are missing? + 'ids' => [] +]; +foreach ($groups as $groupID => $group) { + foreach ($group['Editions'] as $remIdent => $edition) { + if (count($edition['FlacIDs']) === 0 //no FLAC in this group + || (!empty($edition['MP3s']) && $_GET['target'] === 'all') //at least one transcode present when we only wanted groups containing no transcodes at all + || (isset($encodings[$_GET['target']]) && isset($edition['MP3s'][$encodings[$_GET['target']]])) //the transcode we asked for is already there + || count($edition['MP3s']) === 3) //all 3 transcodes are there already (this can happen due to the caching of Sphinx's better_transcode table) + { + unset($groups[$groupID]['Editions'][$remIdent]); + continue; + } + $edition_miss = 0; //number of transcodes missing in this edition + foreach ($encodings as $Encoding) { + if (!isset($edition['MP3s'][$Encoding])) { + ++$edition_miss; + ++$counter['miss_'.$Encoding]; + } + } + $counter['miss_total'] += $edition_miss; + $counter['total'] += (bool)$edition_miss; + if ($edition_miss) { + foreach ($edition['FlacIDs'] as $id => $_) { + $counter['ids'][] = $id; + } + } + } } View::show_header('Transcode Search'); + +function selected($val) { + return $val ? ' selected="selected"' : ''; +} ?>
    -
    -
    - - - - - -
    Search: - - - -   - -
    -
    -
    - - - - - - - - $Editions) { - $GroupInfo = $Groups[$GroupID]; - $GroupYear = $GroupInfo['Year']; - $ExtendedArtists = $GroupInfo['ExtendedArtists']; - $GroupCatalogueNumber = $GroupInfo['CatalogueNumber']; - $GroupName = $GroupInfo['Name']; - $GroupRecordLabel = $GroupInfo['RecordLabel']; - $ReleaseType = $GroupInfo['ReleaseType']; - - if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5]) || !empty($ExtendedArtists[6])) { - unset($ExtendedArtists[2]); - unset($ExtendedArtists[3]); - $ArtistNames = Artists::display_artists($ExtendedArtists); - } else { - $ArtistNames = ''; - } - $TorrentTags = new Tags($GroupInfo['TagList']); - foreach ($Editions as $RemIdent => $Edition) { - if (!$Edition['FlacID'] //no FLAC in this group - || !empty($Edition['Formats']) && $_GET['type'] === '3' //at least one transcode present when we only wanted groups containing no transcodes at all (type 3) - || $Edition['Formats'][$Encodings[$_GET['type']]] == true //the transcode we asked for is already there - || count($Edition['Formats']) === 3) //all 3 transcodes are there already (this can happen due to the caching of Sphinx's better_transcode table) - { - continue; - } - $DisplayName = $ArtistNames . ''.$GroupName.''; - if ($GroupYear > 0) { - $DisplayName .= " [$GroupYear]"; - } - if ($ReleaseType > 0) { - $DisplayName .= ' ['.$ReleaseTypes[$ReleaseType].']'; - } - if ($Edition['IsSnatched']) { - $DisplayName .= ' ' . Format::torrent_label('Snatched!'); - } - - $EditionInfo = array(); - if (!empty($Edition['RemasterYear'])) { - $ExtraInfo = $Edition['RemasterYear']; - } else { - $ExtraInfo = ''; - } - if (!empty($Edition['RemasterRecordLabel'])) { - $EditionInfo[] = $Edition['RemasterRecordLabel']; - } - if (!empty($Edition['RemasterTitle'])) { - $EditionInfo[] = $Edition['RemasterTitle']; - } - if (!empty($Edition['RemasterCatalogueNumber'])) { - $EditionInfo[] = $Edition['RemasterCatalogueNumber']; - } - if (!empty($Edition['RemasterYear'])) { - $ExtraInfo .= ' - '; - } - $ExtraInfo .= implode(' / ', $EditionInfo); +

    Transcodes

    + + + +
    TorrentV2V0320
    + + + + + + + + + +
    Filter + + +
    Search + +
     
    + +

    About

    +
    +

    + This page aims at listing random transcodable perfect FLACs matching the options you selected above, but there can be more or less matches on this page. The following numbers tell you something about the torrents currently listed below and can change if you reload.

    + + Number of perfect FLACs you can transcode:
    + Number of missing transcodes:
    + Number of missing V2 / V0 / 320 transcodes: / / + 1) { + $idList = implode(',', $counter['ids']); ?> - > - - - DL - - -

    -
    format()?>
    - - YES
    ' : 'NO'?> - YES
    ' : 'NO'?> - YES
    ' : 'NO'?> - - +
    + Download All + +

    +
    +

    List

    + + + + + + + + + + $group) { + $groupYear = $group['Year']; + $extendedArtists = $group['ExtendedArtists']; + $groupName = $group['Name']; + $releaseType = $group['ReleaseType']; + + if (!empty($extendedArtists[1]) || !empty($extendedArtists[4]) || !empty($extendedArtists[5]) || !empty($extendedArtists[6])) { + unset($extendedArtists[2]); + unset($extendedArtists[3]); + $artistNames = Artists::display_artists($extendedArtists); + } else { + $artistNames = ''; + } + + $torrentTags = new Tags($group['TagList']); + + foreach ($group['Editions'] as $remIdent => $edition) { + // TODO: point to the correct FLAC (?) + $flacID = array_search(true, $edition['FlacIDs']); + $displayName = $artistNames . "$groupName"; + if ($groupYear > 0) { + $displayName .= " [$groupYear]"; + } + if ($releaseType > 0) { + $displayName .= ' ['.$ReleaseTypes[$releaseType].']'; + } + if ($edition['FLACIsSnatched']) { + $displayName .= ' ' . Format::torrent_label('Snatched!'); + } +?> + > + + + + + + -
    TorrentV2V0320
    No results found!
    + + DL + + +
    +
    format('better.php?action=transcode&tags=')?>
    +
    YES' : 'NO')?>YES' : 'NO')?>YES' : 'NO')?>
    +
    - diff --git a/sections/blog/blog_page.php b/sections/blog/blog_page.php index 113a108c5..812f94ae1 100644 --- a/sections/blog/blog_page.php +++ b/sections/blog/blog_page.php @@ -3,125 +3,125 @@ View::show_header('Blog','bbcode'); if (check_perms('admin_manage_blog')) { - $BlogID = 0; - $Title = ''; - $Body = ''; - $ThreadID = null; - if (!empty($_GET['action']) && $_GET['action'] === 'editblog' && !empty($_GET['id'])) { - $BlogID = intval($_GET['id']); - $DB->prepared_query(" + $BlogID = 0; + $Title = ''; + $Body = ''; + $ThreadID = null; + if (!empty($_GET['action']) && $_GET['action'] === 'editblog' && !empty($_GET['id'])) { + $BlogID = intval($_GET['id']); + $DB->prepared_query(" SELECT Title, Body, ThreadID FROM blog WHERE ID = ?", $BlogID); - list($Title, $Body, $ThreadID) = $DB->fetch_record(0, 1); - $ThreadID = $ThreadID ?? 0; - } - ?> -
    -
    - -
    -
    -
    - - - - - -

    Title

    - />
    -

    Body

    - -
    -
    -

    Thread ID

    - /> - (Leave blank to create thread automatically, set to 0 to not use thread) -

    - - tabindex="2"/> - + list($Title, $Body, $ThreadID) = $DB->fetch_record(0, 1); + $ThreadID = $ThreadID ?? 0; + } + ?> +
    +
    + +
    + +
    + + + + + +

    Title

    + />
    +

    Body

    + +
    +
    +

    Thread ID

    + /> + (Leave blank to create thread automatically, set to 0 to not use thread) +

    + + tabindex="2"/> + -
    - -
    -
    - -
    -
    - + +
    +
    + + +
    + -
    - get_value('blog')) { - $DB->prepared_query(" - SELECT - b.ID, - um.Username, - b.UserID, - b.Title, - b.Body, - b.Time, - b.ThreadID - FROM blog AS b - LEFT JOIN users_main AS um ON b.UserID = um.ID - ORDER BY Time DESC - LIMIT 20"); - $Blog = $DB->to_array(); - $Cache->cache_value('blog', $Blog, 1209600); - } + ?> +
    + get_value('blog')) { + $DB->prepared_query(" + SELECT + b.ID, + um.Username, + b.UserID, + b.Title, + b.Body, + b.Time, + b.ThreadID + FROM blog AS b + LEFT JOIN users_main AS um ON b.UserID = um.ID + ORDER BY Time DESC + LIMIT 20"); + $Blog = $DB->to_array(); + $Cache->cache_value('blog', $Blog, 1209600); + } - if (count($Blog) > 0 && G::$LoggedUser['LastReadBlog'] < $Blog[0][0]) { - $Cache->begin_transaction('user_info_heavy_'.G::$LoggedUser['ID']); - $Cache->update_row(false, array('LastReadBlog' => $Blog[0][0])); - $Cache->commit_transaction(0); - $DB->prepared_query(" - UPDATE users_info - SET LastReadBlog = ? - WHERE UserID = ?", $Blog[0][0], G::$LoggedUser['ID']); - G::$LoggedUser['LastReadBlog'] = $Blog[0][0]; - } + if (count($Blog) > 0 && G::$LoggedUser['LastReadBlog'] < $Blog[0][0]) { + $Cache->begin_transaction('user_info_heavy_'.G::$LoggedUser['ID']); + $Cache->update_row(false, array('LastReadBlog' => $Blog[0][0])); + $Cache->commit_transaction(0); + $DB->prepared_query(" + UPDATE users_info + SET LastReadBlog = ? + WHERE UserID = ?", $Blog[0][0], G::$LoggedUser['ID']); + G::$LoggedUser['LastReadBlog'] = $Blog[0][0]; + } - foreach ($Blog as $BlogItem) { - list($BlogID, $Author, $AuthorID, $Title, $Body, $BlogTime, $ThreadID) = $BlogItem; - ?> -
    -
    - - posted by - - - Edit - Delete - -
    - -
    -
    - +
    +
    + - posted by + + - Edit + Delete + +
    + +
    +
    + -
    +
    \ No newline at end of file +?> diff --git a/sections/blog/dead_thread.php b/sections/blog/dead_thread.php index 894475973..853c40e74 100644 --- a/sections/blog/dead_thread.php +++ b/sections/blog/dead_thread.php @@ -3,15 +3,15 @@ authorize(); if (!isset($_GET['id'])) { - error('Please provide an ID for a blog post to remove the thread link from.'); + error('Please provide an ID for a blog post to remove the thread link from.'); } $ID = intval($_GET['id']); G::$DB->prepared_query('UPDATE blog SET ThreadID = NULL WHERE ID = ? ', $ID); if (G::$DB->affected_rows() > 0) { - $Cache->delete_value('blog'); - $Cache->delete_value('feed_blog'); + $Cache->delete_value('blog'); + $Cache->delete_value('feed_blog'); } -header('Location: blog.php'); \ No newline at end of file +header('Location: blog.php'); diff --git a/sections/blog/delete_blog.php b/sections/blog/delete_blog.php index e9deb92b7..967e1de2b 100644 --- a/sections/blog/delete_blog.php +++ b/sections/blog/delete_blog.php @@ -2,13 +2,13 @@ authorize(); if (empty($_GET['id'])) { - error('You must provide an ID of a blog to delete'); + error('You must provide an ID of a blog to delete'); } $BlogID = intval($_GET['id']); if ($BlogID > 0) { - $DB->prepared_query("DELETE FROM blog WHERE ID = ?", $BlogID); - $Cache->delete_value('blog'); - $Cache->delete_value('feed_blog'); + $DB->prepared_query("DELETE FROM blog WHERE ID = ?", $BlogID); + $Cache->delete_value('blog'); + $Cache->delete_value('feed_blog'); } header('Location: blog.php'); diff --git a/sections/blog/index.php b/sections/blog/index.php index 9003e05c2..7c5df7138 100644 --- a/sections/blog/index.php +++ b/sections/blog/index.php @@ -1,29 +1,29 @@ - 0) { - $DB->prepared_query(" - SELECT ForumID - FROM forums_topics - WHERE ID = ?", $ThreadID); - if (!$DB->has_results()) { - error('No such thread exists!'); - } + $DB->prepared_query(" + SELECT ForumID + FROM forums_topics + WHERE ID = ?", $ThreadID); + if (!$DB->has_results()) { + error('No such thread exists!'); + } } elseif ($ThreadID === '') { - $ThreadID = Misc::create_thread(ANNOUNCEMENT_FORUM_ID, G::$LoggedUser['ID'], $_POST['title'], $_POST['body']); - if ($ThreadID < 1) { - error(0); - } + $ThreadID = Misc::create_thread(ANNOUNCEMENT_FORUM_ID, G::$LoggedUser['ID'], $_POST['title'], $_POST['body']); + if ($ThreadID < 1) { + error(0); + } } else { - $ThreadID = null; + $ThreadID = null; } $Important = isset($_POST['important']) ? '1' : '0'; if ($BlogID > 0) { - $DB->prepared_query(" - UPDATE blog - SET - Title = ?, - Body = ?, - ThreadID = ?, - Important = ? - WHERE ID = ?", $_POST['title'], $_POST['body'], $ThreadID, $Important, $BlogID); - $Cache->delete_value('blog'); - $Cache->delete_value('feed_blog'); - if ($Important == '1') { - $Cache->delete_value('blog_latest_id'); - } - if (isset($_POST['subscribe']) && $ThreadID !== null && $ThreadID > 0) { - $DB->prepared_query(" - INSERT IGNORE INTO users_subscriptions - VALUES (?, ?)", G::$LoggedUser['ID'], $ThreadID); - $Cache->delete_value('subscriptions_user_'.G::$LoggedUser['ID']); - } + $DB->prepared_query(" + UPDATE blog + SET + Title = ?, + Body = ?, + ThreadID = ?, + Important = ? + WHERE ID = ?", $_POST['title'], $_POST['body'], $ThreadID, $Important, $BlogID); + $Cache->delete_value('blog'); + $Cache->delete_value('feed_blog'); + if ($Important == '1') { + $Cache->delete_value('blog_latest_id'); + } + if (isset($_POST['subscribe']) && $ThreadID !== null && $ThreadID > 0) { + $DB->prepared_query(" + INSERT IGNORE INTO users_subscriptions + VALUES (?, ?)", G::$LoggedUser['ID'], $ThreadID); + $Cache->delete_value('subscriptions_user_'.G::$LoggedUser['ID']); + } } -header('Location: blog.php'); \ No newline at end of file +header('Location: blog.php'); diff --git a/sections/blog/take_new_blog.php b/sections/blog/take_new_blog.php index 0b5c3960b..5163e8db2 100644 --- a/sections/blog/take_new_blog.php +++ b/sections/blog/take_new_blog.php @@ -2,47 +2,47 @@ authorize(); if (empty($_POST['title']) || empty($_POST['body'])) { - error('You must have a title and body for the blog post.'); + error('You must have a title and body for the blog post.'); } $ThreadID = !isset($_POST['thread']) || $_POST['thread'] === '' ? '' : max(0, intval($_POST['thread'])); if ($ThreadID > 0) { - $DB->prepared_query(" - SELECT ForumID - FROM forums_topics - WHERE ID = ?", $ThreadID); - if (!$DB->has_results()) { - error('No such thread exists!'); - } + $DB->prepared_query(" + SELECT ForumID + FROM forums_topics + WHERE ID = ?", $ThreadID); + if (!$DB->has_results()) { + error('No such thread exists!'); + } } elseif ($ThreadID === '') { - $ThreadID = Misc::create_thread(ANNOUNCEMENT_FORUM_ID, G::$LoggedUser['ID'], $_POST['title'], $_POST['body']); - if ($ThreadID < 1) { - error(0); - } + $ThreadID = Misc::create_thread(ANNOUNCEMENT_FORUM_ID, G::$LoggedUser['ID'], $_POST['title'], $_POST['body']); + if ($ThreadID < 1) { + error(0); + } } else { - $ThreadID = null; + $ThreadID = null; } $Important = isset($_POST['important']) ? '1' : '0'; $DB->prepared_query(" - INSERT INTO blog - (UserID, Title, Body, Time, ThreadID, Important) - VALUES - (?, ?, ?, ?, ?, ?)", G::$LoggedUser['ID'], $_POST['title'], $_POST['body'], sqltime(), $ThreadID, $Important); + INSERT INTO blog + (UserID, Title, Body, Time, ThreadID, Important) + VALUES + (?, ?, ?, ?, ?, ?)", G::$LoggedUser['ID'], $_POST['title'], $_POST['body'], sqltime(), $ThreadID, $Important); $Cache->delete_value('blog'); if ($Important == '1') { - $Cache->delete_value('blog_latest_id'); + $Cache->delete_value('blog_latest_id'); } if (isset($_POST['subscribe']) && $ThreadID !== null && $ThreadID > 0) { - $DB->prepared_query(" - INSERT IGNORE INTO users_subscriptions - VALUES (?, ?)", G::$LoggedUser['ID'], $ThreadID); - $Cache->delete_value('subscriptions_user_'.G::$LoggedUser['ID']); + $DB->prepared_query(" + INSERT IGNORE INTO users_subscriptions + VALUES (?, ?)", G::$LoggedUser['ID'], $ThreadID); + $Cache->delete_value('subscriptions_user_'.G::$LoggedUser['ID']); } NotificationsManager::send_push(NotificationsManager::get_push_enabled_users(), $_POST['title'], $_POST['body'], site_url() . 'index.php', NotificationsManager::BLOG); -header('Location: blog.php'); \ No newline at end of file +header('Location: blog.php'); diff --git a/sections/bonus/bprates.php b/sections/bonus/bprates.php index 99a882800..d16b11cb0 100644 --- a/sections/bonus/bprates.php +++ b/sections/bonus/bprates.php @@ -5,16 +5,19 @@ $Limit = TORRENTS_PER_PAGE; $Offset = TORRENTS_PER_PAGE * ($Page-1); -if (!empty($_GET['userid']) && check_perms('users_mod')) { - $UserID = intval($_GET['userid']); - $User = array_merge(Users::user_stats($_GET['userid']), Users::user_info($_GET['userid']), Users::user_heavy_info($_GET['userid'])); - if (empty($User)) { - error(404); - } +if (!empty($_GET['userid'])) { + if (!check_perms('admin_bp_history')) { + error(403); + } + $UserID = intval($_GET['userid']); + $User = array_merge(Users::user_stats($_GET['userid']), Users::user_info($_GET['userid']), Users::user_heavy_info($_GET['userid'])); + if (empty($User)) { + error(404); + } } else { - $UserID = $LoggedUser['ID']; - $User = $LoggedUser; + $UserID = $LoggedUser['ID']; + $User = $LoggedUser; } $Title = ($UserID === $LoggedUser['ID']) ? 'Your Bonus Points Rate' : "{$User['Username']}'s Bonus Point Rate"; @@ -22,19 +25,21 @@ $DB->prepared_query(" SELECT - COUNT(xfu.uid) as TotalTorrents, - SUM(t.Size) as TotalSize, - SUM(IFNULL((t.Size / (1024 * 1024 * 1024)) * ( - 0.0433 + ( - (0.07 * LN(1 + (xfh.seedtime / (24)))) / (POW(GREATEST(t.Seeders, 1), 0.35)) - ) - ), 0)) AS TotalHourlyPoints -FROM - (SELECT DISTINCT uid,fid FROM xbt_files_users WHERE active=1 AND remaining=0 AND mtime > unix_timestamp(NOW() - INTERVAL 1 HOUR) AND uid = ?) AS xfu - JOIN xbt_files_history AS xfh ON xfh.uid = xfu.uid AND xfh.fid = xfu.fid - JOIN torrents AS t ON t.ID = xfu.fid + COUNT(xfu.uid) as TotalTorrents, + SUM(t.Size) as TotalSize, + SUM(IFNULL((t.Size / (1024 * 1024 * 1024)) * ( + 0.0433 + ( + (0.07 * LN(1 + (xfh.seedtime / (24)))) / (POW(GREATEST(tls.Seeders, 1), 0.35)) + ) + ), 0)) AS TotalHourlyPoints +FROM ( + SELECT DISTINCT uid,fid FROM xbt_files_users WHERE active=1 AND remaining=0 AND mtime > unix_timestamp(NOW() - INTERVAL 1 HOUR) AND uid = ? +) AS xfu +INNER JOIN xbt_files_history AS xfh ON (xfh.uid = xfu.uid AND xfh.fid = xfu.fid) +INNER JOIN torrents AS t ON (t.ID = xfu.fid) +INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID) WHERE - xfu.uid = ?", $UserID, $UserID); + xfu.uid = ?", $UserID, $UserID); list($TotalTorrents, $TotalSize, $TotalHourlyPoints) = $DB->next_record(); $TotalTorrents = intval($TotalTorrents); @@ -51,157 +56,160 @@ ?>
    -

    -

    Points:

    +

    +

    Points:

    - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + +
    Total TorrentsSizeBP/hourBP/dayBP/weekBP/monthBP/year
    Total TorrentsSizeBP/hourBP/dayBP/weekBP/monthBP/year

    + - - - - - - - - - - - - - - + + + + + + + + + + + + + + 0) { - $DB->prepared_query(" - SELECT - t.ID, - t.GroupID, - t.Size, - t.Format, - t.Encoding, - t.HasLog, - t.HasLogDB, - t.HasCue, - t.LogScore, - t.LogChecksum, - t.Media, - t.Scene, - t.RemasterYear, - t.RemasterTitle, - GREATEST(t.Seeders, 1) AS Seeders, - xfh.seedtime AS Seedtime, - ((t.Size / (1024 * 1024 * 1024)) * ( - 0.0433 + ( - (0.07 * LN(1 + (xfh.seedtime / (24)))) / (POW(GREATEST(t.Seeders, 1), 0.35)) - ) - )) AS HourlyPoints - FROM - (SELECT DISTINCT uid,fid FROM xbt_files_users WHERE active=1 AND remaining=0 AND mtime > unix_timestamp(NOW() - INTERVAL 1 HOUR) AND uid = ?) AS xfu - JOIN xbt_files_history AS xfh ON xfh.uid = xfu.uid AND xfh.fid = xfu.fid - JOIN torrents AS t ON t.ID = xfu.fid - WHERE - xfu.uid = ? - LIMIT ? - OFFSET ?", $UserID, $UserID, $Limit, $Offset); - - $GroupIDs = $DB->collect('GroupID'); - $Groups = Torrents::get_groups($GroupIDs, true, true, false); - while ($Torrent = $DB->next_record(MYSQLI_ASSOC)) { - // list($TorrentID, $GroupID, $Size, $Format, $Encoding, $HasLog, $HasLogDB, $HasCue, $LogScore, $LogChecksum, $Media, $Scene, $Seeders, $Seedtime, $HourlyPoints) - $Size = intval($Torrent['Size']); - $Seeders = intval($Torrent['Seeders']); - $HourlyPoints = floatval($Torrent['HourlyPoints']); - $DailyPoints = $HourlyPoints * 24; - $WeeklyPoints = $DailyPoints * 7; - $MonthlyPoints = $DailyPoints * 30.436875; - $YearlyPoints = $DailyPoints * 365.2425; - - extract(Torrents::array_group($Groups[$Torrent['GroupID']])); - - $TorrentTags = new Tags($TagList); - - if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5])) { - unset($ExtendedArtists[2]); - unset($ExtendedArtists[3]); - $DisplayName = Artists::display_artists($ExtendedArtists); - } elseif (!empty($Artists)) { - $DisplayName = Artists::display_artists(array(1 => $Artists)); - } else { - $DisplayName = ''; - } - $DisplayName .= '' . $GroupName . ''; - if ($GroupYear > 0) { - $DisplayName .= " [$GroupYear]"; - } - if ($GroupVanityHouse) { - $DisplayName .= ' [VH]'; - } - - $ExtraInfo = Torrents::torrent_info($Torrent); - if ($ExtraInfo) { - $DisplayName .= " - $ExtraInfo"; - } + $DB->prepared_query(" + SELECT + t.ID, + t.GroupID, + t.Size, + t.Format, + t.Encoding, + t.HasLog, + t.HasLogDB, + t.HasCue, + t.LogScore, + t.LogChecksum, + t.Media, + t.Scene, + t.RemasterYear, + t.RemasterTitle, + GREATEST(tls.Seeders, 1) AS Seeders, + xfh.seedtime AS Seedtime, + ((t.Size / (1024 * 1024 * 1024)) * ( + 0.0433 + ( + (0.07 * LN(1 + (xfh.seedtime / (24)))) / (POW(GREATEST(tls.Seeders, 1), 0.35)) + ) + )) AS HourlyPoints + FROM ( + SELECT DISTINCT uid,fid FROM xbt_files_users WHERE active=1 AND remaining=0 AND mtime > unix_timestamp(NOW() - INTERVAL 1 HOUR) AND uid = ? + ) AS xfu + INNER JOIN xbt_files_history AS xfh ON xfh.uid = xfu.uid AND xfh.fid = xfu.fid + INNER JOIN torrents AS t ON t.ID = xfu.fid + INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID) + WHERE + xfu.uid = ? + LIMIT ? + OFFSET ?", $UserID, $UserID, $Limit, $Offset); + + $GroupIDs = $DB->collect('GroupID'); + $Groups = Torrents::get_groups($GroupIDs, true, true, false); + while ($Torrent = $DB->next_record(MYSQLI_ASSOC)) { + // list($TorrentID, $GroupID, $Size, $Format, $Encoding, $HasLog, $HasLogDB, $HasCue, $LogScore, $LogChecksum, $Media, $Scene, $Seeders, $Seedtime, $HourlyPoints) + $Size = intval($Torrent['Size']); + $Seeders = intval($Torrent['Seeders']); + $HourlyPoints = floatval($Torrent['HourlyPoints']); + $DailyPoints = $HourlyPoints * 24; + $WeeklyPoints = $DailyPoints * 7; + $MonthlyPoints = $DailyPoints * 30.436875; + $YearlyPoints = $DailyPoints * 365.2425; + + extract(Torrents::array_group($Groups[$Torrent['GroupID']])); + + $TorrentTags = new Tags($TagList); + + if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5])) { + unset($ExtendedArtists[2]); + unset($ExtendedArtists[3]); + $DisplayName = Artists::display_artists($ExtendedArtists); + } elseif (!empty($Artists)) { + $DisplayName = Artists::display_artists(array(1 => $Artists)); + } else { + $DisplayName = ''; + } + $DisplayName .= '' . $GroupName . ''; + if ($GroupYear > 0) { + $DisplayName .= " [$GroupYear]"; + } + if ($GroupVanityHouse) { + $DisplayName .= ' [VH]'; + } + + $ExtraInfo = Torrents::torrent_info($Torrent); + if ($ExtraInfo) { + $DisplayName .= " - $ExtraInfo"; + } ?> - - - - - - - - - - - + + + + + + + + + + + - - - + + + - +
    TorrentSizeSeedersSeedtimeBP/hourBP/dayBP/weekBP/monthBP/year
    TorrentSizeSeedersSeedtimeBP/hourBP/dayBP/weekBP/monthBP/year
    No torrents being seeded currently
    No torrents being seeded currently
    + diff --git a/sections/bonus/history.php b/sections/bonus/history.php index c699ca5a7..6c2009b3d 100644 --- a/sections/bonus/history.php +++ b/sections/bonus/history.php @@ -2,15 +2,18 @@ View::show_header('Bonus Points Purchase History', 'bonus'); -if (check_perms('admin_bp_history') && isset($_GET['id']) && is_number($_GET['id'])) { - $ID = (int)$_GET['id']; - $Header = 'Bonus Points Spending History for ' . Users::format_username($ID); - $WhoSpent = Users::format_username($ID) . ' has spent'; +if (isset($_GET['userid']) && is_number($_GET['userid'])) { + if (!check_perms('admin_bp_history')) { + error(403); + } + $ID = (int)$_GET['userid']; + $Header = 'Bonus Points Spending History for ' . Users::format_username($ID); + $WhoSpent = Users::format_username($ID) . ' has spent'; } else { - $ID = G::$LoggedUser['ID']; - $Header = 'Bonus Points Spending History'; - $WhoSpent = 'You have spent'; + $ID = G::$LoggedUser['ID']; + $Header = 'Bonus Points Spending History'; + $WhoSpent = 'You have spent'; } $Summary = $Bonus->getUserSummary($ID); @@ -19,56 +22,91 @@ $Pages = Format::get_pages($Page, $Summary['nr'], TORRENTS_PER_PAGE); if ($Summary['nr'] > 0) { - $History = $Bonus->getUserHistory($ID, $Page, TORRENTS_PER_PAGE); + $History = $Bonus->getUserHistory($ID, $Page, TORRENTS_PER_PAGE); } ?>
    -

    +

    - -

    bonus points to purchase .

    - -

    No purchase history

    -getUserPoolHistory($ID); +$pool_total = 0; +if (!is_null($PoolSummary)) { + $has_spent++; + foreach ($PoolSummary as $p) { + $when = (time() < strtotime($p['UntilDate'])) + ? " ending in " . time_diff($p['UntilDate']) + : " ended " . time_diff($p['UntilDate']) . ' ago'; + $pool_total += $p['Total']; +?> +

    bonus points to donate to the .

    + +

    bonus points to purchase .

    + 500000) { $adj = 'very '; } + elseif ($total > 1000000) { $adj = 'very, very '; } + elseif ($total > 5000000) { $adj = 'extremely '; } + elseif ($total > 10000000) { $adj = 'exceptionally '; } + else { $adj = ''; } +?> +

    That makes a grand total of points, well done!

    + +

    No purchase history.

    + - - - - - - - - - - - - - - - - - - - - -
    ItemPricePurchase DateFor
    - - + + + + + + + + + + + + + + + + + + + + +
    ItemPricePurchase DateFor
    + +
    -getItem($Label); - if ($Item) { - $Price = $Bonus->getEffectivePrice($Label, G::$LoggedUser['EffectiveClass']); - if ($Price > G::$LoggedUser['BonusPoints']) { - error('You cannot afford this item.'); - } - switch($Label) { - case 'token-1': case 'token-2': case 'token-3': - case 'other-1': case 'other-2': case 'other-3': - require_once(SERVER_ROOT . '/sections/bonus/tokens.php'); - break; - case 'invite': - require_once(SERVER_ROOT . '/sections/bonus/invite.php'); - break; - case 'title-bb-y': - case 'title-bb-n': - case 'title-off': - require_once(SERVER_ROOT . '/sections/bonus/title.php'); - break; - default: - require_once(SERVER_ROOT . DEFAULT_PAGE); - break; - } - } - else { - require_once(SERVER_ROOT . DEFAULT_PAGE); - break; - } - } - break; - case 'bprates': - require_once(SERVER_ROOT . '/sections/bonus/bprates.php'); - break; - case 'title': - require_once(SERVER_ROOT . '/sections/bonus/title.php'); - break; - case 'history': - require_once(SERVER_ROOT . '/sections/bonus/history.php'); - break; - default: - require_once(SERVER_ROOT . DEFAULT_PAGE); - break; - } + switch ($_GET['action']) { + case 'purchase': + /* handle validity and cost as early as possible */ + if (isset($_REQUEST['label']) && preg_match('/^[a-z]{1,15}(-\w{1,15}){0,4}/', $_REQUEST['label'])) { + $Label = $_REQUEST['label']; + $Item = $Bonus->getItem($Label); + if ($Item) { + $Price = $Bonus->getEffectivePrice($Label, G::$LoggedUser['EffectiveClass']); + if ($Price > G::$LoggedUser['BonusPoints']) { + error('You cannot afford this item.'); + } + switch($Label) { + case 'token-1': case 'token-2': case 'token-3': + case 'other-1': case 'other-2': case 'other-3': + require_once(SERVER_ROOT . '/sections/bonus/tokens.php'); + break; + case 'invite': + require_once(SERVER_ROOT . '/sections/bonus/invite.php'); + break; + case 'title-bb-y': + case 'title-bb-n': + case 'title-off': + require_once(SERVER_ROOT . '/sections/bonus/title.php'); + break; + default: + require_once(SERVER_ROOT . DEFAULT_PAGE); + break; + } + } + else { + require_once(SERVER_ROOT . DEFAULT_PAGE); + break; + } + } + break; + case 'bprates': + require_once(SERVER_ROOT . '/sections/bonus/bprates.php'); + break; + case 'title': + require_once(SERVER_ROOT . '/sections/bonus/title.php'); + break; + case 'history': + require_once(SERVER_ROOT . '/sections/bonus/history.php'); + break; + case 'donate': + default: + require_once(SERVER_ROOT . DEFAULT_PAGE); + break; + } } else { - require_once(SERVER_ROOT . DEFAULT_PAGE); + require_once(SERVER_ROOT . DEFAULT_PAGE); } diff --git a/sections/bonus/invite.php b/sections/bonus/invite.php index cc6da6f4c..a57594b76 100644 --- a/sections/bonus/invite.php +++ b/sections/bonus/invite.php @@ -2,8 +2,8 @@ authorize(); if ($Bonus->purchaseInvite(G::$LoggedUser['ID'])) { - header('Location: bonus.php?complete=invite'); + header('Location: bonus.php?complete=invite'); } else { - error(403); + error(403); } diff --git a/sections/bonus/store.php b/sections/bonus/store.php index b158bd925..47153c043 100644 --- a/sections/bonus/store.php +++ b/sections/bonus/store.php @@ -5,76 +5,141 @@ if (isset($_GET['complete'])) { $label = $_GET['complete']; $item = $Bonus->getItem($label); - print << - {$item['Title']} purchased! + {$item['Title']} purchased! HTML; } ?>
    -

    Bonus Points Shop

    +

    Bonus Points Shop

    + +
    User error, no bonus points donated.
    + +
    Warning! You cannot donate if you only have points.
    +donate($_POST['poolid'], $value, G::$LoggedUser['ID'], G::$LoggedUser['EffectiveClass'])) { +?> +
    Success! Your donation to the Bonus Point pool has been recorded.
    + +
    No bonus points donated, insufficient funds.
    +getOpenPool(); +if (count($pool) > 0) { +?> +
    +
    +

    The pool is open for business!

    +
    Donate points for greater good! The points you give here will be distributed out to everyone + who participates in the contest. You can give as many times as you want until the end.
    +
    +

    The grand total currently stands at points

    +
    + + + + + + + + + + + + + + +
    Current BPDonated BP
    + + + +
    +
    The fine print: obviously, you cannot donate more BP than you currently have. There are no refunds. Some handling fees apply.
    +
    +
    +
    + +
    - - - - - - - - - - +
    OptionDescriptionPointsCheckout
    + + + + + + + + + getList(); foreach ($Items as $Label => $Item) { - if ($Item['MinClass'] > G::$LoggedUser['EffectiveClass']) { - continue; - } - $Cnt++; - $RowClass = ($Cnt % 2 === 0) ? 'rowb' : 'rowa'; - $Price = $Bonus->getEffectivePrice($Label, G::$LoggedUser['EffectiveClass']); - $FormattedPrice = number_format($Price); - print << - - - - + + + + } + print << + HTML; } ?> - -
    OptionDescriptionPointsCheckout
    {$Cnt}{$Item['Title']}{$FormattedPrice} + if ($Item['MinClass'] > G::$LoggedUser['EffectiveClass']) { + continue; + } + $Cnt++; + $RowClass = ($Cnt % 2 === 0) ? 'rowb' : 'rowa'; + $Price = $Bonus->getEffectivePrice($Label, G::$LoggedUser['EffectiveClass']); + $FormattedPrice = number_format($Price); + print << + {$Cnt}{$Item['Title']}{$FormattedPrice} HTML; - if (G::$LoggedUser['BonusPoints'] >= $Price) { + if (G::$LoggedUser['BonusPoints'] >= $Price) { $NextFunction = preg_match('/^other-\d$/', $Label) ? 'ConfirmOther' : 'null'; $OnClick = preg_match('/^title-bbcode-[yn]$/', $Label) ? "NoOp" : "ConfirmPurchase"; - print <<Purchase + print <<Purchase HTML; - } - else { - print <<Too Expensive + } + else { + print <<Too Expensive HTML; - } - print << -
    + + +
    purchaseTitle($ID, $Label, $_POST['title'], G::$LoggedUser['EffectiveClass'])) { - header('Location: bonus.php?complete=' . urlencode($Label)); - } - else { - error('You cannot afford this item.'); - } + authorize(); + if (!isset($_POST['title'])) { + error(403); + } + if ($Bonus->purchaseTitle($ID, $Label, $_POST['title'], G::$LoggedUser['EffectiveClass'])) { + header('Location: bonus.php?complete=' . urlencode($Label)); + } + else { + error('You cannot afford this item.'); + } } View::show_header('Bonus Points - Title', 'bonus'); ?>
    - - - - - - - - - - - -
    Custom Title, - Points
    -
    - - -
    -  

    -
    -
    -
    + + + + + + + + + + + +
    Custom Title, - Points
    +
    + + +
    +  

    +
    +
    +
    -purchaseToken(G::$LoggedUser['ID'], $Label, G::$LoggedUser)) { - error('Purchase not concluded.'); + error('Purchase not concluded.'); } } else { - if (empty($_GET['user'])) { - error('You have to enter a username to give tokens to.'); - } - $ID = Users::ID_from_username(urldecode($_GET['user'])); - if (is_null($ID)) { - error('Invalid username. Please select a valid user'); - } - elseif ($ID == G::$LoggedUser['ID']) { - error('You cannot give yourself tokens.'); - } + if (empty($_GET['user'])) { + error('You have to enter a username to give tokens to.'); + } + $ID = Users::ID_from_username(urldecode($_GET['user'])); + if (is_null($ID)) { + error('Invalid username. Please select a valid user'); + } + elseif ($ID == G::$LoggedUser['ID']) { + error('You cannot give yourself tokens.'); + } if (!$Bonus->purchaseTokenOther(G::$LoggedUser['ID'], $ID, $Label, G::$LoggedUser)) { - error('Purchase for other not concluded.'); + error('Purchase for other not concluded.'); } } diff --git a/sections/bookmarks/add.php b/sections/bookmarks/add.php index 82b7617b0..7b2ae43a6 100644 --- a/sections/bookmarks/add.php +++ b/sections/bookmarks/add.php @@ -1,10 +1,10 @@ -query(" - SELECT UserID - FROM $Table - WHERE UserID = '$LoggedUser[ID]' - AND $Col = $PageID"); + SELECT UserID + FROM $Table + WHERE UserID = '$LoggedUser[ID]' + AND $Col = $PageID"); if (!$DB->has_results()) { - if ($Type === 'torrent') { - $DB->query(" - SELECT MAX(Sort) - FROM `bookmarks_torrents` - WHERE UserID = $LoggedUser[ID]"); - list($Sort) = $DB->next_record(); - if (!$Sort) { - $Sort = 0; - } - $Sort += 1; - $DB->query(" - INSERT IGNORE INTO $Table (UserID, $Col, Time, Sort) - VALUES ('$LoggedUser[ID]', $PageID, '".sqltime()."', $Sort)"); - } else { - $DB->query(" - INSERT IGNORE INTO $Table (UserID, $Col, Time) - VALUES ('$LoggedUser[ID]', $PageID, '".sqltime()."')"); - } - $Cache->delete_value('bookmarks_'.$Type.'_'.$LoggedUser['ID']); - if ($Type == 'torrent') { - $Cache->delete_value("bookmarks_group_ids_$UserID"); + if ($Type === 'torrent') { + $DB->query(" + SELECT MAX(Sort) + FROM `bookmarks_torrents` + WHERE UserID = $LoggedUser[ID]"); + list($Sort) = $DB->next_record(); + if (!$Sort) { + $Sort = 0; + } + $Sort += 1; + $DB->query(" + INSERT IGNORE INTO $Table (UserID, $Col, Time, Sort) + VALUES ('$LoggedUser[ID]', $PageID, '".sqltime()."', $Sort)"); + } else { + $DB->query(" + INSERT IGNORE INTO $Table (UserID, $Col, Time) + VALUES ('$LoggedUser[ID]', $PageID, '".sqltime()."')"); + } + $Cache->delete_value('bookmarks_'.$Type.'_'.$LoggedUser['ID']); + if ($Type == 'torrent') { + $Cache->delete_value("bookmarks_group_ids_$UserID"); - $DB->query(" - SELECT Name, Year, WikiBody, TagList - FROM torrents_group - WHERE ID = $PageID"); - list($GroupTitle, $Year, $Body, $TagList) = $DB->next_record(); - $TagList = str_replace('_', '.', $TagList); + $DB->query(" + SELECT Name, Year, WikiBody, TagList + FROM torrents_group + WHERE ID = $PageID"); + list($GroupTitle, $Year, $Body, $TagList) = $DB->next_record(); + $TagList = str_replace('_', '.', $TagList); - $DB->query(" - SELECT ID, Format, Encoding, HasLog, HasCue, HasLogDB, LogScore, LogChecksum, Media, Scene, FreeTorrent, UserID - FROM torrents - WHERE GroupID = $PageID"); - // RSS feed stuff - while ($Torrent = $DB->next_record()) { - $Title = $GroupTitle; - list($TorrentID, $Format, $Bitrate, $HasLog, $HasCue, $HasLogDB, $LogScore, $LogChecksum, $Media, $Scene, $Freeleech, $UploaderID) = $Torrent; - $Title .= " [$Year] - "; - $Title .= "$Format / $Bitrate"; - if ($HasLog == "'1'") { - $Title .= ' / Log'; - } - if ($HasLogDB) { - $Title .= " / $LogScore%"; - } - if ($HasCue == "'1'") { - $Title .= ' / Cue'; - } - $Title .= ' / '.trim($Media); - if ($Scene == '1') { - $Title .= ' / Scene'; - } - if ($Freeleech == '1') { - $Title .= ' / Freeleech!'; - } - if ($Freeleech == '2') { - $Title .= ' / Neutral leech!'; - } + $DB->query(" + SELECT ID, Format, Encoding, HasLog, HasCue, HasLogDB, LogScore, LogChecksum, Media, Scene, FreeTorrent, UserID + FROM torrents + WHERE GroupID = $PageID"); + // RSS feed stuff + while ($Torrent = $DB->next_record()) { + $Title = $GroupTitle; + list($TorrentID, $Format, $Bitrate, $HasLog, $HasCue, $HasLogDB, $LogScore, $LogChecksum, $Media, $Scene, $Freeleech, $UploaderID) = $Torrent; + $Title .= " [$Year] - "; + $Title .= "$Format / $Bitrate"; + if ($HasLog == "'1'") { + $Title .= ' / Log'; + } + if ($HasLogDB) { + $Title .= " / $LogScore%"; + } + if ($HasCue == "'1'") { + $Title .= ' / Cue'; + } + $Title .= ' / '.trim($Media); + if ($Scene == '1') { + $Title .= ' / Scene'; + } + if ($Freeleech == '1') { + $Title .= ' / Freeleech!'; + } + if ($Freeleech == '2') { + $Title .= ' / Neutral leech!'; + } - $UploaderInfo = Users::user_info($UploaderID); - $Item = $Feed->item($Title, - Text::strip_bbcode($Body), - 'torrents.php?action=download&authkey=[[AUTHKEY]]&torrent_pass=[[PASSKEY]]&id='.$TorrentID, - $UploaderInfo['Username'], - "torrents.php?id=$PageID", - trim($TagList)); - $Feed->populate('torrents_bookmarks_t_'.$LoggedUser['torrent_pass'], $Item); - } - } elseif ($Type == 'request') { - $DB->query(" - SELECT UserID - FROM $Table - WHERE $Col = '".db_string($PageID)."'"); - if ($DB->record_count() < 100) { - // Sphinx doesn't like huge MVA updates. Update sphinx_requests_delta - // and live with the <= 1 minute delay if we have more than 100 bookmarkers - $Bookmarkers = implode(',', $DB->collect('UserID')); - $SphQL = new SphinxqlQuery(); - $SphQL->raw_query("UPDATE requests, requests_delta SET bookmarker = ($Bookmarkers) WHERE id = $PageID"); - } else { - Requests::update_sphinx_requests($PageID); - } - } + $UploaderInfo = Users::user_info($UploaderID); + $Item = $Feed->item($Title, + Text::strip_bbcode($Body), + 'torrents.php?action=download&authkey=[[AUTHKEY]]&torrent_pass=[[PASSKEY]]&id='.$TorrentID, + $UploaderInfo['Username'], + "torrents.php?id=$PageID", + trim($TagList)); + $Feed->populate('torrents_bookmarks_t_'.$LoggedUser['torrent_pass'], $Item); + } + } elseif ($Type == 'request') { + $DB->query(" + SELECT UserID + FROM $Table + WHERE $Col = '".db_string($PageID)."'"); + if ($DB->record_count() < 100) { + // Sphinx doesn't like huge MVA updates. Update sphinx_requests_delta + // and live with the <= 1 minute delay if we have more than 100 bookmarkers + $Bookmarkers = implode(',', $DB->collect('UserID')); + $SphQL = new SphinxqlQuery(); + $SphQL->raw_query("UPDATE requests, requests_delta SET bookmarker = ($Bookmarkers) WHERE id = $PageID"); + } else { + Requests::update_sphinx_requests($PageID); + } + } } diff --git a/sections/bookmarks/artists.php b/sections/bookmarks/artists.php index b762e532d..1ac65f1f2 100644 --- a/sections/bookmarks/artists.php +++ b/sections/bookmarks/artists.php @@ -1,21 +1,21 @@ -query(" - SELECT Username - FROM users_main - WHERE ID = '$UserID'"); - list($Username) = $DB->next_record(); + if (!check_perms('users_override_paranoia')) { + error(403); + } + $UserID = $_GET['userid']; + $Sneaky = $UserID !== $LoggedUser['ID']; + if (!is_number($UserID)) { + error(404); + } + $DB->query(" + SELECT Username + FROM users_main + WHERE ID = '$UserID'"); + list($Username) = $DB->next_record(); } else { - $UserID = $LoggedUser['ID']; + $UserID = $LoggedUser['ID']; } $Sneaky = $UserID !== $LoggedUser['ID']; @@ -23,11 +23,11 @@ //$ArtistList = Bookmarks::all_bookmarks('artist', $UserID); $DB->query(" - SELECT ag.ArtistID, ag.Name - FROM bookmarks_artists AS ba - INNER JOIN artists_group AS ag ON ba.ArtistID = ag.ArtistID - WHERE ba.UserID = $UserID - ORDER BY ag.Name"); + SELECT ag.ArtistID, ag.Name + FROM bookmarks_artists AS ba + INNER JOIN artists_group AS ag ON ba.ArtistID = ag.ArtistID + WHERE ba.UserID = $UserID + ORDER BY ag.Name"); $ArtistList = $DB->to_array(); @@ -37,72 +37,73 @@ ?>
    -
    -

    - -
    -
    - -

    You have not bookmarked any artists.

    -
    +
    +

    + +
    +
    + +

    You have not bookmarked any artists.

    +
    - - - - - - + + + + - - + - -" onclick="Unbookmark('artist', , 'Bookmark'); return false;" class="brackets">Remove bookmark + + + + -
    Artist
    Artist
    - - -get_value('notify_artists_'.$LoggedUser['ID'])) === false) { - $DB->query(" - SELECT ID, Artists - FROM users_notify_filters - WHERE UserID = '$LoggedUser[ID]' - AND Label = 'Artist notifications' - LIMIT 1"); - $Notify = $DB->next_record(MYSQLI_ASSOC); - $Cache->cache_value('notify_artists_'.$LoggedUser['ID'], $Notify, 0); - } - if (stripos($Notify['Artists'], "|$Name|") === false) { +
    + + +get_value('notify_artists_'.$LoggedUser['ID'])) === false) { + $DB->query(" + SELECT ID, Artists + FROM users_notify_filters + WHERE UserID = '$LoggedUser[ID]' + AND Label = 'Artist notifications' + LIMIT 1"); + $Notify = $DB->next_record(MYSQLI_ASSOC); + $Cache->cache_value('notify_artists_'.$LoggedUser['ID'], $Notify, 0); + } + if (stripos($Notify['Artists'], "|$Name|") === false) { ?> - Notify of new uploads -&auth=" class="brackets">Notify of new uploads + - Do not notify of new uploads -&auth=" class="brackets">Do not notify of new uploads + - Remove bookmark - -
    - + + -cache_value('bookmarks_'.$UserID, serialize(array(array($Username, $TorrentList, $CollageDataList))), 3600); ?> diff --git a/sections/bookmarks/edit_torrents.php b/sections/bookmarks/edit_torrents.php index d3eb55616..4bd23af94 100644 --- a/sections/bookmarks/edit_torrents.php +++ b/sections/bookmarks/edit_torrents.php @@ -1,20 +1,20 @@ query(" - SELECT Username - FROM users_main - WHERE ID = '$UserID'"); - list($Username) = $DB->next_record(); + if (!check_perms('users_override_paranoia')) { + error(403); + } + $UserID = $_GET['userid']; + if (!is_number($UserID)) { + error(404); + } + $DB->query(" + SELECT Username + FROM users_main + WHERE ID = '$UserID'"); + list($Username) = $DB->next_record(); } else { - $UserID = $LoggedUser['ID']; + $UserID = $LoggedUser['ID']; } // Finally we start diff --git a/sections/bookmarks/index.php b/sections/bookmarks/index.php index 074f55262..913e87e3e 100644 --- a/sections/bookmarks/index.php +++ b/sections/bookmarks/index.php @@ -1,4 +1,4 @@ -query(" - CREATE TEMPORARY TABLE snatched_groups_temp - (GroupID int PRIMARY KEY)"); - $DB->query(" - INSERT INTO snatched_groups_temp - SELECT DISTINCT GroupID - FROM torrents AS t - JOIN xbt_snatched AS s ON s.fid = t.ID - WHERE s.uid = '$LoggedUser[ID]'"); - $DB->query(" - DELETE b - FROM bookmarks_torrents AS b - JOIN snatched_groups_temp AS s - USING(GroupID) - WHERE b.UserID = '$LoggedUser[ID]'"); - $Cache->delete_value("bookmarks_group_ids_$UserID"); - header('Location: bookmarks.php'); - die(); - break; + case 'remove_snatched': + authorize(); + $DB->query(" + CREATE TEMPORARY TABLE snatched_groups_temp + (GroupID int PRIMARY KEY)"); + $DB->query(" + INSERT INTO snatched_groups_temp + SELECT DISTINCT GroupID + FROM torrents AS t + JOIN xbt_snatched AS s ON s.fid = t.ID + WHERE s.uid = '$LoggedUser[ID]'"); + $DB->query(" + DELETE b + FROM bookmarks_torrents AS b + JOIN snatched_groups_temp AS s + USING(GroupID) + WHERE b.UserID = '$LoggedUser[ID]'"); + $Cache->delete_value("bookmarks_group_ids_$UserID"); + header('Location: bookmarks.php'); + die(); + break; - case 'edit': - if (empty($_REQUEST['type'])) { - $_REQUEST['type'] = false; - } - switch ($_REQUEST['type']) { - case 'torrents': - require(SERVER_ROOT.'/sections/bookmarks/edit_torrents.php'); - break; - default: - error(404); - } - break; + case 'edit': + if (empty($_REQUEST['type'])) { + $_REQUEST['type'] = false; + } + switch ($_REQUEST['type']) { + case 'torrents': + require(SERVER_ROOT.'/sections/bookmarks/edit_torrents.php'); + break; + default: + error(404); + } + break; - case 'view': - if (empty($_REQUEST['type'])) { - $_REQUEST['type'] = 'torrents'; - } - switch ($_REQUEST['type']) { - case 'torrents': - require(SERVER_ROOT.'/sections/bookmarks/torrents.php'); - break; - case 'artists': - require(SERVER_ROOT.'/sections/bookmarks/artists.php'); - break; - case 'collages': - $_GET['bookmarks'] = '1'; - require(SERVER_ROOT.'/sections/collages/browse.php'); - break; - case 'requests': - $_GET['type'] = 'bookmarks'; - require(SERVER_ROOT.'/sections/requests/requests.php'); - break; - default: - error(404); - } - break; + case 'view': + if (empty($_REQUEST['type'])) { + $_REQUEST['type'] = 'torrents'; + } + switch ($_REQUEST['type']) { + case 'torrents': + require(SERVER_ROOT.'/sections/bookmarks/torrents.php'); + break; + case 'artists': + require(SERVER_ROOT.'/sections/bookmarks/artists.php'); + break; + case 'collages': + $_GET['bookmarks'] = '1'; + require(SERVER_ROOT.'/sections/collages/browse.php'); + break; + case 'requests': + $_GET['type'] = 'bookmarks'; + require(SERVER_ROOT.'/sections/requests/requests.php'); + break; + default: + error(404); + } + break; - default: - error(404); + default: + error(404); } diff --git a/sections/bookmarks/mass_edit.php b/sections/bookmarks/mass_edit.php index 4d03be8a9..6b8631db4 100644 --- a/sections/bookmarks/mass_edit.php +++ b/sections/bookmarks/mass_edit.php @@ -3,17 +3,17 @@ authorize(); if ($UserID != $LoggedUser['ID'] || !Bookmarks::can_bookmark('torrent')) { - error(403); + error(403); } if ($_POST['type'] === 'torrents') { - // require_once SERVER_ROOT.'/classes/mass_user_bookmarks_editor.class.php'; //Bookmark Updater Class - $BU = new MASS_USER_BOOKMARKS_EDITOR; - if ($_POST['delete']) { - $BU->mass_remove(); - } elseif ($_POST['update']) { - $BU->mass_update(); - } + // require_once SERVER_ROOT.'/classes/mass_user_bookmarks_editor.class.php'; //Bookmark Updater Class + $BU = new MASS_USER_BOOKMARKS_EDITOR; + if ($_POST['delete']) { + $BU->mass_remove(); + } elseif ($_POST['update']) { + $BU->mass_update(); + } } header('Location: bookmarks.php?type=torrents'); diff --git a/sections/bookmarks/remove.php b/sections/bookmarks/remove.php index e7faa5157..5b0873724 100644 --- a/sections/bookmarks/remove.php +++ b/sections/bookmarks/remove.php @@ -1,8 +1,8 @@ -query(" - DELETE FROM $Table - WHERE UserID = $LoggedUser[ID] - AND $Col = $PageID"); + DELETE FROM $Table + WHERE UserID = $LoggedUser[ID] + AND $Col = $PageID"); $Cache->delete_value("bookmarks_{$Type}_$UserID"); if ($DB->affected_rows()) { - if ($Type === 'torrent') { - $Cache->delete_value("bookmarks_group_ids_$UserID"); - } elseif ($Type === 'request') { - $DB->query(" - SELECT UserID - FROM $Table - WHERE $Col = $PageID"); - if ($DB->record_count() < 100) { - // Sphinx doesn't like huge MVA updates. Update sphinx_requests_delta - // and live with the <= 1 minute delay if we have more than 100 bookmarkers - $Bookmarkers = implode(',', $DB->collect('UserID')); - $SphQL = new SphinxqlQuery(); - $SphQL->raw_query("UPDATE requests, requests_delta SET bookmarker = ($Bookmarkers) WHERE id = $PageID"); - } else { - Requests::update_sphinx_requests($PageID); - } - } + if ($Type === 'torrent') { + $Cache->delete_value("bookmarks_group_ids_$UserID"); + } elseif ($Type === 'request') { + $DB->query(" + SELECT UserID + FROM $Table + WHERE $Col = $PageID"); + if ($DB->record_count() < 100) { + // Sphinx doesn't like huge MVA updates. Update sphinx_requests_delta + // and live with the <= 1 minute delay if we have more than 100 bookmarkers + $Bookmarkers = implode(',', $DB->collect('UserID')); + $SphQL = new SphinxqlQuery(); + $SphQL->raw_query("UPDATE requests, requests_delta SET bookmarker = ($Bookmarkers) WHERE id = $PageID"); + } else { + Requests::update_sphinx_requests($PageID); + } + } } diff --git a/sections/bookmarks/torrents.php b/sections/bookmarks/torrents.php index 0d2971862..d32b315d3 100644 --- a/sections/bookmarks/torrents.php +++ b/sections/bookmarks/torrents.php @@ -5,52 +5,52 @@ //~~~~~~~~~~~ Main bookmarks page ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// function compare($X, $Y) { - return($Y['count'] - $X['count']); + return($Y['count'] - $X['count']); } if (!empty($_GET['userid'])) { - if (!check_perms('users_override_paranoia')) { - error(403); - } - $UserID = $_GET['userid']; - if (!is_number($UserID)) { - error(404); - } - $DB->query(" - SELECT Username - FROM users_main - WHERE ID = '$UserID'"); - list($Username) = $DB->next_record(); + if (!check_perms('users_override_paranoia')) { + error(403); + } + $UserID = $_GET['userid']; + if (!is_number($UserID)) { + error(404); + } + $DB->query(" + SELECT Username + FROM users_main + WHERE ID = '$UserID'"); + list($Username) = $DB->next_record(); } else { - $UserID = $LoggedUser['ID']; + $UserID = $LoggedUser['ID']; } $Sneaky = $UserID !== $LoggedUser['ID']; $Title = $Sneaky ? "$Username's bookmarked torrent groups" : 'Your bookmarked torrent groups'; $NumGroups = 0; -$ArtistCount = array(); +$ArtistCount = []; list($GroupIDs, $CollageDataList, $TorrentList) = Users::get_bookmarks($UserID); foreach ($GroupIDs as $Idx => $GroupID) { - if (!isset($TorrentList[$GroupID])) { - unset($GroupIDs[$Idx]); - continue; - } - // Handle stats and stuff - $NumGroups++; - extract(Torrents::array_group($TorrentList[$GroupID])); - if ($Artists) { - foreach ($Artists as $Artist) { - if (!isset($ArtistCount[$Artist['id']])) { - $ArtistCount[$Artist['id']] = array('name' => $Artist['name'], 'count' => 1); - } else { - $ArtistCount[$Artist['id']]['count']++; - } - } - } - // We need to append the tag list to the Tags::$all array - new Tags($TagList); + if (!isset($TorrentList[$GroupID])) { + unset($GroupIDs[$Idx]); + continue; + } + // Handle stats and stuff + $NumGroups++; + extract(Torrents::array_group($TorrentList[$GroupID])); + if ($Artists) { + foreach ($Artists as $Artist) { + if (!isset($ArtistCount[$Artist['id']])) { + $ArtistCount[$Artist['id']] = array('name' => $Artist['name'], 'count' => 1); + } else { + $ArtistCount[$Artist['id']]['count']++; + } + } + } + // We need to append the tag list to the Tags::$all array + new Tags($TagList); } $GroupIDs = array_values($GroupIDs); @@ -60,343 +60,347 @@ function compare($X, $Y) { View::show_header($Title, 'browse,collage'); ?>
    -
    -

    RSS feed 

    - -
    - -
    -

    You have not bookmarked any torrents.

    -
    +
    +

    RSS feed 

    + +
    + +
    +

    You have not bookmarked any torrents.

    +
    - - query(" - SELECT IRCKey - FROM users_main - WHERE ID = $LoggedUser[ID]"); + SELECT IRCKey + FROM users_main + WHERE ID = $LoggedUser[ID]"); list($IRCKey) = $DB->next_record(); if (empty($IRCKey)) { ?>
    -
    -

    IRC Rules - Please read these carefully!

    -
    -
    -

    - Please set your IRC Key on your profile first! For more information on IRC, please read the wiki article. -

    -
    +
    +

    IRC Rules - Please read these carefully!

    +
    +
    +

    + Please set your IRC Key on your profile first! For more information on IRC, please read the wiki article. +

    +
    -
    -
    -

    IRC Rules - Please read these carefully!

    -
    -
    - -
    - - -
    -
    +
    +

    IRC Rules - Please read these carefully!

    +
    +
    + +
    + + +
    +
    -
    -
    -

    IRC

    -
    -
    -
    -

    If you have an IRC client, refer to this wiki article for information on how to connect. (IRC applet users are automatically identified with Drone.)

    -
    - - - - - - - +
    +

    IRC

    +
    +
    +
    +

    If you have an IRC client, refer to this wiki article for information on how to connect. (IRC applet users are automatically identified with Drone.)

    +
    + + + + + + + - - - - - - -
    + + + + + +
    +
    -query(" - SELECT MAX(Sort) - FROM collages_artists - WHERE CollageID = '$CollageID'"); - list($Sort) = $DB->next_record(); - $Sort += 10; - - $DB->query(" - SELECT ArtistID - FROM collages_artists - WHERE CollageID = '$CollageID' - AND ArtistID = '$ArtistID'"); - if (!$DB->has_results()) { - $DB->query(" - INSERT IGNORE INTO collages_artists - (CollageID, ArtistID, UserID, Sort, AddedOn) - VALUES - ('$CollageID', '$ArtistID', '$LoggedUser[ID]', '$Sort', '" . sqltime() . "')"); - - $DB->query(" - UPDATE collages - SET NumTorrents = NumTorrents + 1, Updated = '" . sqltime() . "' - WHERE ID = '$CollageID'"); - - $Cache->delete_value("collage_$CollageID"); - $Cache->delete_value("artists_collages_$ArtistID"); - $Cache->delete_value("artists_collages_personal_$ArtistID"); - - $DB->query(" - SELECT UserID - FROM users_collage_subs - WHERE CollageID = $CollageID"); - while (list($CacheUserID) = $DB->next_record()) { - $Cache->delete_value("collage_subs_user_new_$CacheUserID"); - } - } + global $Cache, $LoggedUser, $DB; + + $DB->query(" + SELECT MAX(Sort) + FROM collages_artists + WHERE CollageID = '$CollageID'"); + list($Sort) = $DB->next_record(); + $Sort += 10; + + $DB->query(" + SELECT ArtistID + FROM collages_artists + WHERE CollageID = '$CollageID' + AND ArtistID = '$ArtistID'"); + if (!$DB->has_results()) { + $DB->query(" + INSERT IGNORE INTO collages_artists + (CollageID, ArtistID, UserID, Sort, AddedOn) + VALUES + ('$CollageID', '$ArtistID', '$LoggedUser[ID]', '$Sort', '" . sqltime() . "')"); + + $DB->query(" + UPDATE collages + SET NumTorrents = NumTorrents + 1, Updated = '" . sqltime() . "' + WHERE ID = '$CollageID'"); + + $Cache->delete_value("collage_$CollageID"); + $Cache->delete_value("artists_collages_$ArtistID"); + $Cache->delete_value("artists_collages_personal_$ArtistID"); + + $DB->query(" + SELECT UserID + FROM users_collage_subs + WHERE CollageID = $CollageID"); + while (list($CacheUserID) = $DB->next_record()) { + $Cache->delete_value("collage_subs_user_new_$CacheUserID"); + } + } } $CollageID = $_POST['collageid']; if (!is_number($CollageID)) { - error(404); + error(404); } $DB->query(" - SELECT UserID, CategoryID, Locked, NumTorrents, MaxGroups, MaxGroupsPerUser - FROM collages - WHERE ID = '$CollageID'"); + SELECT UserID, CategoryID, Locked, NumTorrents, MaxGroups, MaxGroupsPerUser + FROM collages + WHERE ID = '$CollageID'"); list($UserID, $CategoryID, $Locked, $NumTorrents, $MaxGroups, $MaxGroupsPerUser) = $DB->next_record(); if (!check_perms('site_collages_delete')) { - if ($Locked) { - $Err = 'This collage is locked'; - } - if ($CategoryID == 0 && $UserID != $LoggedUser['ID']) { - $Err = 'You cannot edit someone else\'s personal collage.'; - } - if ($MaxGroups > 0 && $NumTorrents >= $MaxGroups) { - $Err = 'This collage already holds its maximum allowed number of artists.'; - } - - if (isset($Err)) { - error($Err); - } + if ($Locked) { + $Err = 'This collage is locked'; + } + if ($CategoryID == 0 && $UserID != $LoggedUser['ID']) { + $Err = 'You cannot edit someone else\'s personal collage.'; + } + if ($MaxGroups > 0 && $NumTorrents >= $MaxGroups) { + $Err = 'This collage already holds its maximum allowed number of artists.'; + } + + if (isset($Err)) { + error($Err); + } } if ($MaxGroupsPerUser > 0) { - $DB->query(" - SELECT COUNT(*) - FROM collages_artists - WHERE CollageID = '$CollageID' - AND UserID = '$LoggedUser[ID]'"); - list($GroupsForUser) = $DB->next_record(); - if (!check_perms('site_collages_delete') && $GroupsForUser >= $MaxGroupsPerUser) { - error(403); - } + $DB->query(" + SELECT COUNT(*) + FROM collages_artists + WHERE CollageID = '$CollageID' + AND UserID = '$LoggedUser[ID]'"); + list($GroupsForUser) = $DB->next_record(); + if (!check_perms('site_collages_delete') && $GroupsForUser >= $MaxGroupsPerUser) { + error(403); + } } if ($_REQUEST['action'] == 'add_artist') { - $Val->SetFields('url', '1', 'regex', 'The URL must be a link to a artist on the site.', array('regex' => '/^'.ARTIST_REGEX.'/i')); - $Err = $Val->ValidateForm($_POST); - - if ($Err) { - error($Err); - } - - $URL = $_POST['url']; - - // Get artist ID - preg_match('/^'.ARTIST_REGEX.'/i', $URL, $Matches); - $ArtistID = $Matches[4]; - if (!$ArtistID || (int)$ArtistID === 0) { - error(404); - } - - $DB->query(" - SELECT ArtistID - FROM artists_group - WHERE ArtistID = '$ArtistID'"); - list($ArtistID) = $DB->next_record(); - if (!$ArtistID) { - error('The artist was not found in the database.'); - } - - add_artist($CollageID, $ArtistID); + $Val->SetFields('url', '1', 'regex', 'The URL must be a link to a artist on the site.', array('regex' => '/^'.ARTIST_REGEX.'/i')); + $Err = $Val->ValidateForm($_POST); + + if ($Err) { + error($Err); + } + + $URL = $_POST['url']; + + // Get artist ID + preg_match('/^'.ARTIST_REGEX.'/i', $URL, $Matches); + $ArtistID = $Matches[4]; + if (!$ArtistID || (int)$ArtistID === 0) { + error(404); + } + + $DB->query(" + SELECT ArtistID + FROM artists_group + WHERE ArtistID = '$ArtistID'"); + list($ArtistID) = $DB->next_record(); + if (!$ArtistID) { + error('The artist was not found in the database.'); + } + + add_artist($CollageID, $ArtistID); } else { - $URLs = explode("\n", $_REQUEST['urls']); - $ArtistIDs = array(); - $Err = ''; - foreach ($URLs as $Key => &$URL) { - $URL = trim($URL); - if ($URL == '') { - unset($URLs[$Key]); - } - } - unset($URL); - - if (!check_perms('site_collages_delete')) { - if ($MaxGroups > 0 && ($NumTorrents + count($URLs) > $MaxGroups)) { - $Err = "This collage can only hold $MaxGroups artists."; - } - if ($MaxGroupsPerUser > 0 && ($GroupsForUser + count($URLs) > $MaxGroupsPerUser)) { - $Err = "You may only have $MaxGroupsPerUser artists in this collage."; - } - } - - foreach ($URLs as $URL) { - $Matches = array(); - if (preg_match('/^'.ARTIST_REGEX.'/i', $URL, $Matches)) { - $ArtistIDs[] = $Matches[4]; - $ArtistID = $Matches[4]; - } else { - $Err = "One of the entered URLs ($URL) does not correspond to an artist on the site."; - break; - } - - $DB->query(" - SELECT ArtistID - FROM artists_group - WHERE ArtistID = '$ArtistID'"); - if (!$DB->has_results()) { - $Err = "One of the entered URLs ($URL) does not correspond to an artist on the site."; - break; - } - } - - if ($Err) { - error($Err); - } - - foreach ($ArtistIDs as $ArtistID) { - add_artist($CollageID, $ArtistID); - } + $URLs = explode("\n", $_REQUEST['urls']); + $ArtistIDs = []; + $Err = ''; + foreach ($URLs as $Key => &$URL) { + $URL = trim($URL); + if ($URL == '') { + unset($URLs[$Key]); + } + } + unset($URL); + + if (!check_perms('site_collages_delete')) { + if ($MaxGroups > 0 && ($NumTorrents + count($URLs) > $MaxGroups)) { + $Err = "This collage can only hold $MaxGroups artists."; + } + if ($MaxGroupsPerUser > 0 && ($GroupsForUser + count($URLs) > $MaxGroupsPerUser)) { + $Err = "You may only have $MaxGroupsPerUser artists in this collage."; + } + } + + foreach ($URLs as $URL) { + $Matches = []; + if (preg_match('/^'.ARTIST_REGEX.'/i', $URL, $Matches)) { + $ArtistIDs[] = $Matches[4]; + $ArtistID = $Matches[4]; + } else { + $Err = "One of the entered URLs ($URL) does not correspond to an artist on the site."; + break; + } + + $DB->query(" + SELECT ArtistID + FROM artists_group + WHERE ArtistID = '$ArtistID'"); + if (!$DB->has_results()) { + $Err = "One of the entered URLs ($URL) does not correspond to an artist on the site."; + break; + } + } + + if ($Err) { + error($Err); + } + + foreach ($ArtistIDs as $ArtistID) { + add_artist($CollageID, $ArtistID); + } } header("Location: collages.php?id=$CollageID"); diff --git a/sections/collages/add_torrent.php b/sections/collages/add_torrent.php index c5e7228af..3c89c6b82 100644 --- a/sections/collages/add_torrent.php +++ b/sections/collages/add_torrent.php @@ -1,4 +1,4 @@ -query(" - SELECT MAX(Sort) - FROM collages_torrents - WHERE CollageID = '$CollageID'"); - list($Sort) = $DB->next_record(); - $Sort += 10; - - $DB->query(" - SELECT GroupID - FROM collages_torrents - WHERE CollageID = '$CollageID' - AND GroupID = '$GroupID'"); - if (!$DB->has_results()) { - $DB->query(" - INSERT IGNORE INTO collages_torrents - (CollageID, GroupID, UserID, Sort, AddedOn) - VALUES - ('$CollageID', '$GroupID', '$LoggedUser[ID]', '$Sort', '" . sqltime() . "')"); - - $DB->query(" - UPDATE collages - SET NumTorrents = NumTorrents + 1, Updated = '" . sqltime() . "' - WHERE ID = '$CollageID'"); - - $Cache->delete_value("collage_$CollageID"); - $Cache->delete_value("torrents_details_$GroupID"); - $Cache->delete_value("torrent_collages_$GroupID"); - $Cache->delete_value("torrent_collages_personal_$GroupID"); - - $DB->query(" - SELECT UserID - FROM users_collage_subs - WHERE CollageID = $CollageID"); - while (list($CacheUserID) = $DB->next_record()) { - $Cache->delete_value("collage_subs_user_new_$CacheUserID"); - } - } + global $Cache, $LoggedUser, $DB; + + $DB->query(" + SELECT MAX(Sort) + FROM collages_torrents + WHERE CollageID = '$CollageID'"); + list($Sort) = $DB->next_record(); + $Sort += 10; + + $DB->query(" + SELECT GroupID + FROM collages_torrents + WHERE CollageID = '$CollageID' + AND GroupID = '$GroupID'"); + if (!$DB->has_results()) { + $DB->query(" + INSERT IGNORE INTO collages_torrents + (CollageID, GroupID, UserID, Sort, AddedOn) + VALUES + ('$CollageID', '$GroupID', '$LoggedUser[ID]', '$Sort', '" . sqltime() . "')"); + + $DB->query(" + UPDATE collages + SET NumTorrents = NumTorrents + 1, Updated = '" . sqltime() . "' + WHERE ID = '$CollageID'"); + + $Cache->delete_value("collage_$CollageID"); + $Cache->delete_value("torrents_details_$GroupID"); + $Cache->delete_value("torrent_collages_$GroupID"); + $Cache->delete_value("torrent_collages_personal_$GroupID"); + + $DB->query(" + SELECT UserID + FROM users_collage_subs + WHERE CollageID = $CollageID"); + while (list($CacheUserID) = $DB->next_record()) { + $Cache->delete_value("collage_subs_user_new_$CacheUserID"); + } + } } $CollageID = $_POST['collageid']; if (!is_number($CollageID)) { - error(404); + error(404); } $DB->query(" - SELECT UserID, CategoryID, Locked, NumTorrents, MaxGroups, MaxGroupsPerUser - FROM collages - WHERE ID = '$CollageID'"); + SELECT UserID, CategoryID, Locked, NumTorrents, MaxGroups, MaxGroupsPerUser + FROM collages + WHERE ID = '$CollageID'"); list($UserID, $CategoryID, $Locked, $NumTorrents, $MaxGroups, $MaxGroupsPerUser) = $DB->next_record(); if (!check_perms('site_collages_delete')) { - if ($Locked) { - $Err = 'This collage is locked'; - } - if ($CategoryID == 0 && $UserID != $LoggedUser['ID']) { - $Err = 'You cannot edit someone else\'s personal collage.'; - } - if ($MaxGroups > 0 && $NumTorrents >= $MaxGroups) { - $Err = 'This collage already holds its maximum allowed number of torrents.'; - } - - if (isset($Err)) { - error($Err); - } + if ($Locked) { + $Err = 'This collage is locked'; + } + if ($CategoryID == 0 && $UserID != $LoggedUser['ID']) { + $Err = 'You cannot edit someone else\'s personal collage.'; + } + if ($MaxGroups > 0 && $NumTorrents >= $MaxGroups) { + $Err = 'This collage already holds its maximum allowed number of torrents.'; + } + + if (isset($Err)) { + error($Err); + } } if ($MaxGroupsPerUser > 0) { - $DB->query(" - SELECT COUNT(*) - FROM collages_torrents - WHERE CollageID = '$CollageID' - AND UserID = '$LoggedUser[ID]'"); - list($GroupsForUser) = $DB->next_record(); - if (!check_perms('site_collages_delete') && $GroupsForUser >= $MaxGroupsPerUser) { - error(403); - } + $DB->query(" + SELECT COUNT(*) + FROM collages_torrents + WHERE CollageID = '$CollageID' + AND UserID = '$LoggedUser[ID]'"); + list($GroupsForUser) = $DB->next_record(); + if (!check_perms('site_collages_delete') && $GroupsForUser >= $MaxGroupsPerUser) { + error(403); + } } if ($_REQUEST['action'] == 'add_torrent') { - $Val->SetFields('url', '1', 'regex', 'The URL must be a link to a torrent on the site.', array('regex' => '/^'.TORRENT_GROUP_REGEX.'/i')); - $Err = $Val->ValidateForm($_POST); - - if ($Err) { - error($Err); - } - - $URL = $_POST['url']; - - // Get torrent ID - preg_match('/^'.TORRENT_GROUP_REGEX.'/i', $URL, $Matches); - $TorrentID = $Matches[4]; - if (!$TorrentID || (int)$TorrentID == 0) { - error(404); - } - - $DB->query(" - SELECT ID - FROM torrents_group - WHERE ID = '$TorrentID'"); - list($GroupID) = $DB->next_record(); - if (!$GroupID) { - error('The torrent was not found in the database.'); - } - - add_torrent($CollageID, $GroupID); + $Val->SetFields('url', '1', 'regex', 'The URL must be a link to a torrent on the site.', array('regex' => '/^'.TORRENT_GROUP_REGEX.'/i')); + $Err = $Val->ValidateForm($_POST); + + if ($Err) { + error($Err); + } + + $URL = $_POST['url']; + + // Get torrent ID + preg_match('/^'.TORRENT_GROUP_REGEX.'/i', $URL, $Matches); + $TorrentID = $Matches[4]; + if (!$TorrentID || (int)$TorrentID == 0) { + error(404); + } + + $DB->query(" + SELECT ID + FROM torrents_group + WHERE ID = '$TorrentID'"); + list($GroupID) = $DB->next_record(); + if (!$GroupID) { + error('The torrent was not found in the database.'); + } + + add_torrent($CollageID, $GroupID); } else { - $URLs = explode("\n", $_REQUEST['urls']); - $GroupIDs = array(); - $Err = ''; - foreach ($URLs as $Key => &$URL) { - $URL = trim($URL); - if ($URL == '') { - unset($URLs[$Key]); - } - } - unset($URL); - - if (!check_perms('site_collages_delete')) { - if ($MaxGroups > 0 && ($NumTorrents + count($URLs) > $MaxGroups)) { - $Err = "This collage can only hold $MaxGroups torrents."; - } - if ($MaxGroupsPerUser > 0 && ($GroupsForUser + count($URLs) > $MaxGroupsPerUser)) { - $Err = "You may only have $MaxGroupsPerUser torrents in this collage."; - } - } - - foreach ($URLs as $URL) { - $Matches = array(); - if (preg_match('/^'.TORRENT_GROUP_REGEX.'/i', $URL, $Matches)) { - $GroupIDs[] = $Matches[4]; - $GroupID = $Matches[4]; - } else { - $Err = "One of the entered URLs ($URL) does not correspond to a torrent group on the site."; - break; - } - - $DB->query(" - SELECT ID - FROM torrents_group - WHERE ID = '$GroupID'"); - if (!$DB->has_results()) { - $Err = "One of the entered URLs ($URL) does not correspond to a torrent group on the site."; - break; - } - } - - if ($Err) { - error($Err); - } - - foreach ($GroupIDs as $GroupID) { - add_torrent($CollageID, $GroupID); - } + $URLs = explode("\n", $_REQUEST['urls']); + $GroupIDs = []; + $Err = ''; + foreach ($URLs as $Key => &$URL) { + $URL = trim($URL); + if ($URL == '') { + unset($URLs[$Key]); + } + } + unset($URL); + + if (!check_perms('site_collages_delete')) { + if ($MaxGroups > 0 && ($NumTorrents + count($URLs) > $MaxGroups)) { + $Err = "This collage can only hold $MaxGroups torrents."; + } + if ($MaxGroupsPerUser > 0 && ($GroupsForUser + count($URLs) > $MaxGroupsPerUser)) { + $Err = "You may only have $MaxGroupsPerUser torrents in this collage."; + } + } + + foreach ($URLs as $URL) { + $Matches = []; + if (preg_match('/^'.TORRENT_GROUP_REGEX.'/i', $URL, $Matches)) { + $GroupIDs[] = $Matches[4]; + $GroupID = $Matches[4]; + } else { + $Err = "One of the entered URLs ($URL) does not correspond to a torrent group on the site."; + break; + } + + $DB->query(" + SELECT ID + FROM torrents_group + WHERE ID = '$GroupID'"); + if (!$DB->has_results()) { + $Err = "One of the entered URLs ($URL) does not correspond to a torrent group on the site."; + break; + } + } + + if ($Err) { + error($Err); + } + + foreach ($GroupIDs as $GroupID) { + add_torrent($CollageID, $GroupID); + } } header('Location: collages.php?id='.$CollageID); diff --git a/sections/collages/all_comments.php b/sections/collages/all_comments.php index e237d6184..5be166ab9 100644 --- a/sections/collages/all_comments.php +++ b/sections/collages/all_comments.php @@ -1,10 +1,10 @@ -query(" - SELECT Name - FROM collages - WHERE ID = '$CollageID'"); + SELECT Name + FROM collages + WHERE ID = '$CollageID'"); list($Name) = $DB->next_record(); // Start printing View::show_header("Comments for collage $Name", 'comments,bbcode,subscriptions'); ?>
    -
    -

    - Collages > - -

    - - +
    + 'pageid', - 'InputID' => $CollageID, - 'Action' => 'comments.php?page=collages', - 'InputAction' => 'take_post', - 'TextareaCols' => 90, - 'SubscribeBox' => true - )); - } + if ($ThreadInfo['MinClassWrite'] <= $LoggedUser['Class'] && !$LoggedUser['DisablePosting']) { + View::parse('generic/reply/quickreply.php', array( + 'InputName' => 'pageid', + 'InputID' => $CollageID, + 'Action' => 'comments.php?page=collages', + 'InputAction' => 'take_post', + 'TextareaCols' => 90, + 'SubscribeBox' => true + )); + } } ?> - +
    - + diff --git a/sections/collages/artist_collage.php b/sections/collages/artist_collage.php index 372b0eb0d..2ec746249 100644 --- a/sections/collages/artist_collage.php +++ b/sections/collages/artist_collage.php @@ -1,298 +1,314 @@ -query(" - SELECT - ca.ArtistID, - ag.Name, - aw.Image, - ca.UserID - FROM collages_artists AS ca - JOIN artists_group AS ag ON ag.ArtistID = ca.ArtistID - LEFT JOIN wiki_artists AS aw ON aw.RevisionID = ag.RevisionID - WHERE ca.CollageID = '$CollageID' - ORDER BY ca.Sort"); + SELECT + ca.ArtistID, + ag.Name, + aw.Image, + ca.UserID + FROM collages_artists AS ca + JOIN artists_group AS ag ON ag.ArtistID = ca.ArtistID + LEFT JOIN wiki_artists AS aw ON aw.RevisionID = ag.RevisionID + WHERE ca.CollageID = '$CollageID' + ORDER BY ca.Sort"); $Artists = $DB->to_array('ArtistID', MYSQLI_ASSOC); // Loop through the result set, building up $Collage and $TorrentTable // Then we print them. -$Collage = array(); +$Collage = []; $ArtistTable = ''; $NumGroups = count($Artists); $NumGroupsByUser = 0; -$UserAdditions = array(); +$UserAdditions = []; foreach ($Artists as $Artist) { - $UserID = $Artist['UserID']; - if ($UserID == $LoggedUser['ID']) { - $NumGroupsByUser++; - } + $UserID = $Artist['UserID']; + if ($UserID == $LoggedUser['ID']) { + $NumGroupsByUser++; + } - if (!isset($UserAdditions[$UserID])) { - $UserAdditions[$UserID] = 0; - } - $UserAdditions[$UserID]++; + if (!isset($UserAdditions[$UserID])) { + $UserAdditions[$UserID] = 0; + } + $UserAdditions[$UserID]++; - ob_start(); + ob_start(); ?> - - - - + + + -
  • - - - <?=$Artist['Name']?> - - - - -
  • - +
  • + + + <?=$Artist['Name']?> + + + + +
  • + 0 && $NumGroups >= $MaxGroups) || ($MaxGroupsPerUser > 0 && $NumGroupsByUser >= $MaxGroupsPerUser))) { - $PreventAdditions = true; + $PreventAdditions = true; } // Silly hack for people who are on the old setting $CollageCovers = (isset($LoggedUser['CollageCovers']) ? $LoggedUser['CollageCovers'] : 25 * (abs($LoggedUser['HideCollage'] - 1))); -$CollagePages = array(); +$CollagePages = []; // Pad it out if ($NumGroups > $CollageCovers) { - for ($i = $NumGroups + 1; $i <= ceil($NumGroups / $CollageCovers) * $CollageCovers; $i++) { - $Collage[] = '
  • '; - } + for ($i = $NumGroups + 1; $i <= ceil($NumGroups / $CollageCovers) * $CollageCovers; $i++) { + $Collage[] = '
  • '; + } } for ($i = 0; $i < $NumGroups / $CollageCovers; $i++) { - $Groups = array_slice($Collage, $i * $CollageCovers, $CollageCovers); - $CollagePage = ''; - foreach ($Groups as $Group) { - $CollagePage .= $Group; - } - $CollagePages[] = $CollagePage; + $Groups = array_slice($Collage, $i * $CollageCovers, $CollageCovers); + $CollagePage = ''; + foreach ($Groups as $Group) { + $CollagePage .= $Group; + } + $CollagePages[] = $CollagePage; } View::show_header($Name, 'browse,collage,bbcode,voting,recommend'); ?>
    -
    -

    - - + +
    +
    Add ArtistsBatch add
    +
    +
    + + + +
    + /> +
    +
    + +
    + Enter the URL of an artist on the site. +
    +
    + +
    + +

    Comments

    +query(" - SELECT - c.ID, - c.Body, - c.AuthorID, - um.Username, - c.AddedTime - FROM comments AS c - LEFT JOIN users_main AS um ON um.ID = c.AuthorID - WHERE c.Page = 'collages' - AND c.PageID = $CollageID - ORDER BY c.ID DESC - LIMIT 15"); - $CommentList = $DB->to_array(false, MYSQLI_NUM); + $DB->query(" + SELECT + c.ID, + c.Body, + c.AuthorID, + um.Username, + c.AddedTime + FROM comments AS c + LEFT JOIN users_main AS um ON um.ID = c.AuthorID + WHERE c.Page = 'collages' + AND c.PageID = $CollageID + ORDER BY c.ID DESC + LIMIT 15"); + $CommentList = $DB->to_array(false, MYSQLI_NUM); } foreach ($CommentList as $Comment) { - list($CommentID, $Body, $UserID, $Username, $CommentTime) = $Comment; + list($CommentID, $Body, $UserID, $Username, $CommentTime) = $Comment; ?> -
    -
    - -
    - Report -
    -
    -
    - +
    + +
    + Report +
    +
    +
    + - - + View all comments +
    + -
    -
    Add comment
    -
    - - - - -
    -
    - -
    -
    - -
    -
    -
    -
    - +
    Add comment
    +
    + + + + +
    +
    + +
    +
    + +
    +
    +
    + + - -
    - +
    + -
    -
    Cover Art
    -
      - +
      Cover Art
      +
        + -
      -
    - $CollageCovers) { ?> - - - +
    + $CollageCovers) { ?> + + + - - - - +
    Artists
    + + + -
    Artists
    -
    + + - diff --git a/sections/collages/browse.php b/sections/collages/browse.php index ab3c4328f..29b7ccbb4 100644 --- a/sections/collages/browse.php +++ b/sections/collages/browse.php @@ -11,150 +11,150 @@ // Are we searching in bodies, or just names? if (!empty($_GET['type'])) { - $Type = $_GET['type']; - if (!in_array($Type, array('c.name', 'description'))) { - $Type = 'c.name'; - } + $Type = $_GET['type']; + if (!in_array($Type, array('c.name', 'description'))) { + $Type = 'c.name'; + } } else { - $Type = 'c.name'; + $Type = 'c.name'; } if (!empty($_GET['search'])) { - // What are we looking for? Let's make sure it isn't dangerous. - $Search = db_string(trim($_GET['search'])); - // Break search string down into individual words - $Words = explode(' ', $Search); + // What are we looking for? Let's make sure it isn't dangerous. + $Search = db_string(trim($_GET['search'])); + // Break search string down into individual words + $Words = explode(' ', $Search); } if (!empty($_GET['tags'])) { - $Tags = explode(',', db_string(trim($_GET['tags']))); - foreach ($Tags as $ID => $Tag) { - $Tags[$ID] = Misc::sanitize_tag($Tag); - } + $Tags = explode(',', db_string(trim($_GET['tags']))); + foreach ($Tags as $ID => $Tag) { + $Tags[$ID] = Misc::sanitize_tag($Tag); + } } if (!empty($_GET['cats'])) { - $Categories = $_GET['cats']; - foreach ($Categories as $Cat => $Accept) { - if (empty($CollageCats[$Cat]) || !$Accept) { - unset($Categories[$Cat]); - } - } - $Categories = array_keys($Categories); + $Categories = $_GET['cats']; + foreach ($Categories as $Cat => $Accept) { + if (empty($CollageCats[$Cat]) || !$Accept) { + unset($Categories[$Cat]); + } + } + $Categories = array_keys($Categories); } else { - $Categories = array(1, 2, 3, 4, 5, 6, 7); + $Categories = array(1, 2, 3, 4, 5, 6, 7); } // Ordering if (!empty($_GET['order_by']) && !empty($OrderTable[$_GET['order_by']])) { - $Order = $OrderTable[$_GET['order_by']]; + $Order = $OrderTable[$_GET['order_by']]; } else { - $Order = 'ID'; + $Order = 'ID'; } if (!empty($_GET['order_way']) && !empty($WayTable[$_GET['order_way']])) { - $Way = $WayTable[$_GET['order_way']]; + $Way = $WayTable[$_GET['order_way']]; } else { - $Way = 'DESC'; + $Way = 'DESC'; } $BookmarkView = !empty($_GET['bookmarks']); if ($BookmarkView) { - $Categories[] = 0; - $BookmarkJoin = 'INNER JOIN bookmarks_collages AS bc ON c.ID = bc.CollageID'; + $Categories[] = 0; + $BookmarkJoin = 'INNER JOIN bookmarks_collages AS bc ON c.ID = bc.CollageID'; } else { - $BookmarkJoin = ''; + $BookmarkJoin = ''; } $BaseSQL = $SQL = " - SELECT - SQL_CALC_FOUND_ROWS - c.ID, - c.Name, - c.NumTorrents, - c.TagList, - c.CategoryID, - c.UserID, - c.Subscribers, - c.Updated - FROM collages AS c - $BookmarkJoin - WHERE Deleted = '0'"; + SELECT + SQL_CALC_FOUND_ROWS + c.ID, + c.Name, + c.NumTorrents, + c.TagList, + c.CategoryID, + c.UserID, + c.Subscribers, + c.Updated + FROM collages AS c + $BookmarkJoin + WHERE Deleted = '0'"; if ($BookmarkView) { - $SQL .= " AND bc.UserID = '" . $LoggedUser['ID'] . "'"; + $SQL .= " AND bc.UserID = '" . $LoggedUser['ID'] . "'"; } if (!empty($Search)) { - $SQL .= " AND $Type LIKE '%"; - $SQL .= implode("%' AND $Type LIKE '%", $Words); - $SQL .= "%'"; + $SQL .= " AND $Type LIKE '%"; + $SQL .= implode("%' AND $Type LIKE '%", $Words); + $SQL .= "%'"; } if (isset($_GET['tags_type']) && $_GET['tags_type'] === '0') { // Any - $_GET['tags_type'] = '0'; + $_GET['tags_type'] = '0'; } else { // All - $_GET['tags_type'] = '1'; + $_GET['tags_type'] = '1'; } if (!empty($Tags)) { - $SQL.= " AND (TagList LIKE '%"; - if ($_GET['tags_type'] === '0') { - $SQL .= implode("%' OR TagList LIKE '%", $Tags); - } else { - $SQL .= implode("%' AND TagList LIKE '%", $Tags); - } - $SQL .= "%')"; + $SQL.= " AND (TagList LIKE '%"; + if ($_GET['tags_type'] === '0') { + $SQL .= implode("%' OR TagList LIKE '%", $Tags); + } else { + $SQL .= implode("%' AND TagList LIKE '%", $Tags); + } + $SQL .= "%')"; } if (!empty($_GET['userid'])) { - $UserID = $_GET['userid']; - if (!is_number($UserID)) { - error(404); - } - $User = Users::user_info($UserID); - $Perms = Permissions::get_permissions($User['PermissionID']); - $UserClass = $Perms['Class']; + $UserID = $_GET['userid']; + if (!is_number($UserID)) { + error(404); + } + $User = Users::user_info($UserID); + $Perms = Permissions::get_permissions($User['PermissionID']); + $UserClass = $Perms['Class']; - $UserLink = ''.$User['Username'].''; - if (!empty($_GET['contrib'])) { - if (!check_paranoia('collagecontribs', $User['Paranoia'], $UserClass, $UserID)) { - error(403); - } - $DB->query(" - SELECT DISTINCT CollageID - FROM collages_torrents - WHERE UserID = $UserID"); - $CollageIDs = $DB->collect('CollageID'); - if (empty($CollageIDs)) { - $SQL .= " AND 0"; - } else { - $SQL .= " AND c.ID IN(".db_string(implode(',', $CollageIDs)).')'; - } - } else { - if (!check_paranoia('collages', $User['Paranoia'], $UserClass, $UserID)) { - error(403); - } - $SQL .= " AND UserID = '".$_GET['userid']."'"; - } - $Categories[] = 0; + $UserLink = ''.$User['Username'].''; + if (!empty($_GET['contrib'])) { + if (!check_paranoia('collagecontribs', $User['Paranoia'], $UserClass, $UserID)) { + error(403); + } + $DB->query(" + SELECT DISTINCT CollageID + FROM collages_torrents + WHERE UserID = $UserID"); + $CollageIDs = $DB->collect('CollageID'); + if (empty($CollageIDs)) { + $SQL .= " AND 0"; + } else { + $SQL .= " AND c.ID IN(".db_string(implode(',', $CollageIDs)).')'; + } + } else { + if (!check_paranoia('collages', $User['Paranoia'], $UserClass, $UserID)) { + error(403); + } + $SQL .= " AND UserID = '".$_GET['userid']."'"; + } + $Categories[] = 0; } if (!empty($Categories)) { - $SQL .= " AND CategoryID IN(".db_string(implode(',', $Categories)).')'; + $SQL .= " AND CategoryID IN(".db_string(implode(',', $Categories)).')'; } if (isset($_GET['action']) && $_GET['action'] === 'mine') { - $SQL = $BaseSQL; - $SQL .= " - AND c.UserID = '".$LoggedUser['ID']."' - AND c.CategoryID = 0"; + $SQL = $BaseSQL; + $SQL .= " + AND c.UserID = '".$LoggedUser['ID']."' + AND c.CategoryID = 0"; } $SQL .= " - ORDER BY $Order $Way - LIMIT $Limit"; + ORDER BY $Order $Way + LIMIT $Limit"; $DB->query($SQL); $Collages = $DB->to_array(); $DB->query('SELECT FOUND_ROWS()'); @@ -163,182 +163,189 @@ View::show_header(($BookmarkView) ? 'Your bookmarked collages' : 'Browse collages'); ?>
    -
    - -

    Your bookmarked collages

    - -

    Browse collages

    - -
    - -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    Search terms: - -
    Tags (comma-separated): - />  - />   - /> -
    Categories: - $Cat) { ?> - /> -    - -
    Search in: - /> Names   - /> Descriptions -
    Order by: - - -
    - -
    -
    -
    - - + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    Search terms: + +
    Tags (comma-separated): + />  + />   + /> +
    Categories: + $Cat) { ?> + /> +    + +
    Search in: + /> Names   + /> Descriptions +
    Order by: + + +
    + +
    +
    +
    + + - +
    +
    - -

    You have not bookmarked any collages.

    - -

    Your search did not match anything.

    -

    Make sure all names are spelled correctly, or try making your search less specific.

    - + +

    You have not bookmarked any collages.

    + +

    Your search did not match anything.

    +

    Make sure all names are spelled correctly, or try making your search less specific.

    +
    - - - - - - - - - - + + + + + + + + - "> - - - - - - - -"> + + + + + + + +
    CategoryCollageTorrentsSubscribersUpdatedAuthor
    CategoryCollageTorrentsSubscribersUpdatedAuthor
    - - - - - - Remove bookmark - - -
    format('collages.php?action=search&tags=')?>
    -
    + + + + + + Remove bookmark + + +
    format('collages.php?action=search&tags=')?>
    +
    - + - + diff --git a/sections/collages/collage.php b/sections/collages/collage.php index d953240a6..8c6c53897 100644 --- a/sections/collages/collage.php +++ b/sections/collages/collage.php @@ -1,82 +1,82 @@ -get_value("collage_$CollageID"); if ($CollageData) { - list($Name, $Description, $CommentList, $Deleted, $CollageCategoryID, $CreatorID, $Locked, $MaxGroups, $MaxGroupsPerUser, $Updated, $Subscribers) = $CollageData; + list($Name, $Description, $CommentList, $Deleted, $CollageCategoryID, $CreatorID, $Locked, $MaxGroups, $MaxGroupsPerUser, $Updated, $Subscribers) = $CollageData; } else { - $DB->query(" - SELECT - Name, - Description, - UserID, - Deleted, - CategoryID, - Locked, - MaxGroups, - MaxGroupsPerUser, - Updated, - Subscribers - FROM collages - WHERE ID = '$CollageID'"); - if ($DB->has_results()) { - list($Name, $Description, $CreatorID, $Deleted, $CollageCategoryID, $Locked, $MaxGroups, $MaxGroupsPerUser, $Updated, $Subscribers) = $DB->next_record(MYSQLI_NUM); - $CommentList = null; - } else { - $Deleted = '1'; - } - $SetCache = true; + $DB->query(" + SELECT + Name, + Description, + UserID, + Deleted, + CategoryID, + Locked, + MaxGroups, + MaxGroupsPerUser, + Updated, + Subscribers + FROM collages + WHERE ID = '$CollageID'"); + if ($DB->has_results()) { + list($Name, $Description, $CreatorID, $Deleted, $CollageCategoryID, $Locked, $MaxGroups, $MaxGroupsPerUser, $Updated, $Subscribers) = $DB->next_record(MYSQLI_NUM); + $CommentList = null; + } else { + $Deleted = '1'; + } + $SetCache = true; } if ($Deleted === '1') { - header("Location: log.php?search=Collage+$CollageID"); - die(); + header("Location: log.php?search=Collage+$CollageID"); + die(); } // Handle subscriptions if (($CollageSubscriptions = $Cache->get_value('collage_subs_user_'.$LoggedUser['ID'])) === false) { - $DB->query(" - SELECT CollageID - FROM users_collage_subs - WHERE UserID = '$LoggedUser[ID]'"); - $CollageSubscriptions = $DB->collect(0); - $Cache->cache_value('collage_subs_user_'.$LoggedUser['ID'], $CollageSubscriptions, 0); + $DB->query(" + SELECT CollageID + FROM users_collage_subs + WHERE UserID = '$LoggedUser[ID]'"); + $CollageSubscriptions = $DB->collect(0); + $Cache->cache_value('collage_subs_user_'.$LoggedUser['ID'], $CollageSubscriptions, 0); } if (!empty($CollageSubscriptions) && in_array($CollageID, $CollageSubscriptions)) { - $DB->query(" - UPDATE users_collage_subs - SET LastVisit = NOW() - WHERE UserID = ".$LoggedUser['ID']." - AND CollageID = $CollageID"); - $Cache->delete_value('collage_subs_user_new_'.$LoggedUser['ID']); + $DB->query(" + UPDATE users_collage_subs + SET LastVisit = NOW() + WHERE UserID = ".$LoggedUser['ID']." + AND CollageID = $CollageID"); + $Cache->delete_value('collage_subs_user_new_'.$LoggedUser['ID']); } if ($CollageCategoryID == array_search(ARTIST_COLLAGE, $CollageCats)) { - include(SERVER_ROOT.'/sections/collages/artist_collage.php'); + include(SERVER_ROOT.'/sections/collages/artist_collage.php'); } else { - include(SERVER_ROOT . '/sections/collages/torrent_collage.php'); + include(SERVER_ROOT . '/sections/collages/torrent_collage.php'); } if (isset($SetCache)) { - $CollageData = array( - $Name, - $Description, - $CommentList, - (bool)$Deleted, - (int)$CollageCategoryID, - (int)$CreatorID, - (bool)$Locked, - (int)$MaxGroups, - (int)$MaxGroupsPerUser, - $Updated, - (int)$Subscribers); - $Cache->cache_value("collage_$CollageID", $CollageData, 3600); + $CollageData = array( + $Name, + $Description, + $CommentList, + (bool)$Deleted, + (int)$CollageCategoryID, + (int)$CreatorID, + (bool)$Locked, + (int)$MaxGroups, + (int)$MaxGroupsPerUser, + $Updated, + (int)$Subscribers); + $Cache->cache_value("collage_$CollageID", $CollageData, 3600); } diff --git a/sections/collages/delete.php b/sections/collages/delete.php index d03c48aae..9d4130f03 100644 --- a/sections/collages/delete.php +++ b/sections/collages/delete.php @@ -1,52 +1,52 @@ -query(" - SELECT Name, CategoryID, UserID - FROM collages - WHERE ID = '$CollageID'"); + SELECT Name, CategoryID, UserID + FROM collages + WHERE ID = '$CollageID'"); list($Name, $CategoryID, $UserID) = $DB->next_record(); if (!check_perms('site_collages_delete') && $UserID != $LoggedUser['ID']) { - error(403); + error(403); } View::show_header('Delete collage'); ?>
    -
    -
    - Delete collage -
    -
    -
    - - - - +
    + Delete collage +
    +
    + + + + + -
    - Warning: This is a personal collage. If you delete this collage, it cannot be recovered! -
    - + Warning: This is a personal collage. If you delete this collage, it cannot be recovered! +
    + -
    - Reason: - -
    -
    - -
    -
    -
    -
    +
    + Reason: + +
    +
    + +
    + +
    + - diff --git a/sections/collages/download.php b/sections/collages/download.php index 2cb392ffb..384cf9388 100644 --- a/sections/collages/download.php +++ b/sections/collages/download.php @@ -1,4 +1,4 @@ - 2 - || count($_REQUEST['list']) === 0 + !isset($_REQUEST['collageid']) + || !isset($_REQUEST['preference']) + || !is_number($_REQUEST['preference']) + || !is_number($_REQUEST['collageid']) + || $_REQUEST['preference'] > 2 + || count($_REQUEST['list']) === 0 ) { - error(0); + error(0); } if (!check_perms('zip_downloader')) { - error(403); + error(403); } -$Preferences = array('RemasterTitle DESC', 'Seeders ASC', 'Size ASC'); +$Preferences = array('t.RemasterTitle DESC', 'tls.Seeders ASC', 't.Size ASC'); $CollageID = $_REQUEST['collageid']; $Preference = $Preferences[$_REQUEST['preference']]; -$DB->query(" - SELECT Name - FROM collages - WHERE ID = '$CollageID'"); +$DB->prepared_query(' + SELECT Name + FROM collages + WHERE ID = ? + ', $CollageID +); list($CollageName) = $DB->next_record(MYSQLI_NUM, false); $SQL = 'SELECT CASE '; foreach ($_REQUEST['list'] as $Priority => $Selection) { - if (!is_number($Priority)) { - continue; - } - $SQL .= 'WHEN '; - switch ($Selection) { - case '00': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'V0 (VBR)'"; break; - case '01': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'APX (VBR)'"; break; - case '02': $SQL .= "t.Format = 'MP3' AND t.Encoding = '256 (VBR)'"; break; - case '03': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'V1 (VBR)'"; break; - case '10': $SQL .= "t.Format = 'MP3' AND t.Encoding = '224 (VBR)'"; break; - case '11': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'V2 (VBR)'"; break; - case '12': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'APS (VBR)'"; break; - case '13': $SQL .= "t.Format = 'MP3' AND t.Encoding = '192 (VBR)'"; break; - case '20': $SQL .= "t.Format = 'MP3' AND t.Encoding = '320'"; break; - case '21': $SQL .= "t.Format = 'MP3' AND t.Encoding = '256'"; break; - case '22': $SQL .= "t.Format = 'MP3' AND t.Encoding = '224'"; break; - case '23': $SQL .= "t.Format = 'MP3' AND t.Encoding = '192'"; break; - case '30': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'Vinyl'"; break; - case '31': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'DVD'"; break; - case '32': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'SACD'"; break; - case '33': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'WEB'"; break; - case '34': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless' AND HasLog = '1' AND LogScore = '100' AND HasCue = '1'"; break; - case '35': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless' AND HasLog = '1' AND LogScore = '100'"; break; - case '36': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless' AND HasLog = '1'"; break; - case '37': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless'"; break; - case '40': $SQL .= "t.Format = 'DTS'"; break; - case '42': $SQL .= "t.Format = 'AAC' AND t.Encoding = '320'"; break; - case '43': $SQL .= "t.Format = 'AAC' AND t.Encoding = '256'"; break; - case '44': $SQL .= "t.Format = 'AAC' AND t.Encoding = 'q5.5'"; break; - case '45': $SQL .= "t.Format = 'AAC' AND t.Encoding = 'q5'"; break; - case '46': $SQL .= "t.Format = 'AAC' AND t.Encoding = '192'"; break; - default: error(0); - } - $SQL .= " THEN $Priority "; + if (!is_number($Priority)) { + continue; + } + $SQL .= 'WHEN '; + switch ($Selection) { + case '00': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'V0 (VBR)'"; break; + case '01': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'APX (VBR)'"; break; + case '02': $SQL .= "t.Format = 'MP3' AND t.Encoding = '256 (VBR)'"; break; + case '03': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'V1 (VBR)'"; break; + case '10': $SQL .= "t.Format = 'MP3' AND t.Encoding = '224 (VBR)'"; break; + case '11': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'V2 (VBR)'"; break; + case '12': $SQL .= "t.Format = 'MP3' AND t.Encoding = 'APS (VBR)'"; break; + case '13': $SQL .= "t.Format = 'MP3' AND t.Encoding = '192 (VBR)'"; break; + case '20': $SQL .= "t.Format = 'MP3' AND t.Encoding = '320'"; break; + case '21': $SQL .= "t.Format = 'MP3' AND t.Encoding = '256'"; break; + case '22': $SQL .= "t.Format = 'MP3' AND t.Encoding = '224'"; break; + case '23': $SQL .= "t.Format = 'MP3' AND t.Encoding = '192'"; break; + case '30': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'Vinyl'"; break; + case '31': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'DVD'"; break; + case '32': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'SACD'"; break; + case '33': $SQL .= "t.Format = 'FLAC' AND t.Encoding = '24bit Lossless' AND t.Media = 'WEB'"; break; + case '34': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless' AND HasLog = '1' AND LogScore = '100' AND HasCue = '1'"; break; + case '35': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless' AND HasLog = '1' AND LogScore = '100'"; break; + case '36': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless' AND HasLog = '1'"; break; + case '37': $SQL .= "t.Format = 'FLAC' AND t.Encoding = 'Lossless'"; break; + case '40': $SQL .= "t.Format = 'DTS'"; break; + case '42': $SQL .= "t.Format = 'AAC' AND t.Encoding = '320'"; break; + case '43': $SQL .= "t.Format = 'AAC' AND t.Encoding = '256'"; break; + case '44': $SQL .= "t.Format = 'AAC' AND t.Encoding = 'q5.5'"; break; + case '45': $SQL .= "t.Format = 'AAC' AND t.Encoding = 'q5'"; break; + case '46': $SQL .= "t.Format = 'AAC' AND t.Encoding = '192'"; break; + default: error(0); + } + $SQL .= " THEN $Priority "; } $SQL .= " - ELSE 100 - END AS Rank, - t.GroupID, - t.ID AS TorrentID, - t.Media, - t.Format, - t.Encoding, - IF(t.RemasterYear = 0, tg.Year, t.RemasterYear) AS Year, - tg.Name, - t.Size + ELSE 100 + END AS Rank, + t.GroupID, + t.ID AS TorrentID, + t.Media, + t.Format, + t.Encoding, + IF(t.RemasterYear = 0, tg.Year, t.RemasterYear) AS Year, + tg.Name, + t.Size FROM torrents AS t - INNER JOIN collages_torrents AS c ON t.GroupID = c.GroupID AND c.CollageID = '$CollageID' - INNER JOIN torrents_group AS tg ON tg.ID = t.GroupID AND tg.CategoryID = '1' -ORDER BY t.GroupID ASC, Rank DESC, t.$Preference"; +INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID) /* FIXME: only needed if sorting by Seeders */ +INNER JOIN collages_torrents AS c ON (t.GroupID = c.GroupID AND c.CollageID = '$CollageID') +INNER JOIN torrents_group AS tg ON (tg.ID = t.GroupID AND tg.CategoryID = '1') +ORDER BY t.GroupID ASC, Rank DESC, $Preference"; $DownloadsQ = $DB->query($SQL); $Collector = new TorrentsDL($DownloadsQ, $CollageName); while (list($Downloads, $GroupIDs) = $Collector->get_downloads('GroupID')) { - $Artists = Artists::get_artists($GroupIDs); - $TorrentFilesQ = $DB->query(" - SELECT TorrentID, File - FROM torrents_files - WHERE TorrentID IN (".implode(',', array_keys($GroupIDs)).')', false); - if (is_int($TorrentFilesQ)) { - // Query failed. Let's not create a broken zip archive - foreach ($GroupIDs as $GroupID) { - $Download =& $Downloads[$GroupID]; - $Download['Artist'] = Artists::display_artists($Artists[$GroupID], false, true, false); - $Collector->fail_file($Download); - } - continue; - } - while (list($TorrentID, $TorrentFile) = $DB->next_record(MYSQLI_NUM, false)) { - $GroupID = $GroupIDs[$TorrentID]; - $Download =& $Downloads[$GroupID]; - $Download['Artist'] = Artists::display_artists($Artists[$Download['GroupID']], false, true, false); - if ($Download['Rank'] == 100) { - $Collector->skip_file($Download); - continue; - } - $Collector->add_file($TorrentFile, $Download); - unset($Download); - } + $Artists = Artists::get_artists($GroupIDs); + $TorrentFilesQ = $DB->query(" + SELECT TorrentID, File + FROM torrents_files + WHERE TorrentID IN (".implode(',', array_keys($GroupIDs)).')', false); + if (is_int($TorrentFilesQ)) { + // Query failed. Let's not create a broken zip archive + foreach ($GroupIDs as $GroupID) { + $Download =& $Downloads[$GroupID]; + $Download['Artist'] = Artists::display_artists($Artists[$GroupID], false, true, false); + $Collector->fail_file($Download); + } + continue; + } + while (list($TorrentID, $TorrentFile) = $DB->next_record(MYSQLI_NUM, false)) { + $GroupID = $GroupIDs[$TorrentID]; + $Download =& $Downloads[$GroupID]; + $Download['Artist'] = Artists::display_artists($Artists[$Download['GroupID']], false, true, false); + if ($Download['Rank'] == 100) { + $Collector->skip_file($Download); + continue; + } + $Collector->add_file($TorrentFile, $Download); + unset($Download); + } } $Collector->finalize(); $Settings = array(implode(':', $_REQUEST['list']), $_REQUEST['preference']); if (!isset($LoggedUser['Collector']) || $LoggedUser['Collector'] != $Settings) { - Users::update_site_options($LoggedUser['ID'], array('Collector' => $Settings)); + Users::update_site_options($LoggedUser['ID'], array('Collector' => $Settings)); } define('SKIP_NO_CACHE_HEADERS', 1); -?> diff --git a/sections/collages/edit.php b/sections/collages/edit.php index c8efb33e6..07a098e07 100644 --- a/sections/collages/edit.php +++ b/sections/collages/edit.php @@ -1,102 +1,107 @@ -query(" - SELECT Name, Description, TagList, UserID, CategoryID, Locked, MaxGroups, MaxGroupsPerUser, Featured - FROM collages - WHERE ID = '$CollageID'"); + SELECT Name, Description, TagList, UserID, CategoryID, Locked, MaxGroups, MaxGroupsPerUser, Featured + FROM collages + WHERE ID = '$CollageID'"); list($Name, $Description, $TagList, $UserID, $CategoryID, $Locked, $MaxGroups, $MaxGroupsPerUser, $Featured) = $DB->next_record(); $TagList = implode(', ', explode(' ', $TagList)); if ($CategoryID == 0 && $UserID != $LoggedUser['ID'] && !check_perms('site_collages_delete')) { - error(403); + error(403); } View::show_header('Edit collage'); if (!empty($Err)) { - if (isset($ErrNoEscape)) { - echo '
    '.$Err.'
    '; - } else { - echo '
    '.display_str($Err).'
    '; - } + if (isset($ErrNoEscape)) { + echo '
    '.$Err.'
    '; + } else { + echo '
    '.display_str($Err).'
    '; + } } ?>
    -
    -

    Edit collage

    -
    -
    - - - - - - - - - - +

    Edit collage

    + + + + + +
    Name
    + + + + + + 0 || check_perms('site_collages_delete')) { ?> - - - + + - - - - - - - - - - - - - - - -"> + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - -
    Name
    Category -
    Category + -
    Description - -
    Tags
    Featured />
    Description + +
    Tags
    Featured />
    Locked/>
    Max groups
    Max groups per user
    Locked/>
    Max groups
    Max groups per user
    -
    + + + + + +
    - + diff --git a/sections/collages/edit_handle.php b/sections/collages/edit_handle.php index 98e995b32..106490cf6 100644 --- a/sections/collages/edit_handle.php +++ b/sections/collages/edit_handle.php @@ -1,88 +1,88 @@ -query(" - SELECT UserID, CategoryID, Locked, MaxGroups, MaxGroupsPerUser - FROM collages - WHERE ID = '$CollageID'"); + SELECT UserID, CategoryID, Locked, MaxGroups, MaxGroupsPerUser + FROM collages + WHERE ID = '$CollageID'"); list($UserID, $CategoryID, $Locked, $MaxGroups, $MaxGroupsPerUser) = $DB->next_record(); if ($CategoryID == 0 && $UserID != $LoggedUser['ID'] && !check_perms('site_collages_delete')) { - error(403); + error(403); } if (isset($_POST['name'])) { - $DB->query(" - SELECT ID, Deleted - FROM collages - WHERE Name = '".db_string($_POST['name'])."' - AND ID != '$CollageID' - LIMIT 1"); - if ($DB->has_results()) { - list($ID, $Deleted) = $DB->next_record(); - if ($Deleted) { - $Err = 'A collage with that name already exists but needs to be recovered, please contact the staff team!'; - } else { - $Err = "A collage with that name already exists: $_POST[name]."; - } - $ErrNoEscape = true; - include(SERVER_ROOT.'/sections/collages/edit.php'); - die(); - } + $DB->query(" + SELECT ID, Deleted + FROM collages + WHERE Name = '".db_string($_POST['name'])."' + AND ID != '$CollageID' + LIMIT 1"); + if ($DB->has_results()) { + list($ID, $Deleted) = $DB->next_record(); + if ($Deleted) { + $Err = 'A collage with that name already exists but needs to be recovered, please contact the staff team!'; + } else { + $Err = "A collage with that name already exists: $_POST[name]."; + } + $ErrNoEscape = true; + include(SERVER_ROOT.'/sections/collages/edit.php'); + die(); + } } $TagList = explode(',', $_POST['tags']); foreach ($TagList as $ID => $Tag) { - $TagList[$ID] = Misc::sanitize_tag($Tag); + $TagList[$ID] = Misc::sanitize_tag($Tag); } $TagList = implode(' ', $TagList); $Updates = array("Description='".db_string($_POST['description'])."', TagList='".db_string($TagList)."'"); if (!check_perms('site_collages_delete') && ($CategoryID == 0 && $UserID == $LoggedUser['ID'] && check_perms('site_collages_renamepersonal'))) { - if (!stristr($_POST['name'], $LoggedUser['Username'])) { - error("Your personal collage's title must include your username."); - } + if (!stristr($_POST['name'], $LoggedUser['Username'])) { + error("Your personal collage's title must include your username."); + } } if (isset($_POST['featured']) && $CategoryID == 0 && (($LoggedUser['ID'] == $UserID && check_perms('site_collages_personal')) || check_perms('site_collages_delete'))) { - $DB->query(" - UPDATE collages - SET Featured = 0 - WHERE CategoryID = 0 - AND UserID = $UserID"); - $Updates[] = 'Featured = 1'; + $DB->query(" + UPDATE collages + SET Featured = 0 + WHERE CategoryID = 0 + AND UserID = $UserID"); + $Updates[] = 'Featured = 1'; } if (check_perms('site_collages_delete') || ($CategoryID == 0 && $UserID == $LoggedUser['ID'] && check_perms('site_collages_renamepersonal'))) { - $Updates[] = "Name = '".db_string($_POST['name'])."'"; + $Updates[] = "Name = '".db_string($_POST['name'])."'"; } if (isset($_POST['category']) && !empty($CollageCats[$_POST['category']]) && $_POST['category'] != $CategoryID && ($_POST['category'] != 0 || check_perms('site_collages_delete'))) { - $Updates[] = 'CategoryID = '.$_POST['category']; + $Updates[] = 'CategoryID = '.$_POST['category']; } if (check_perms('site_collages_delete')) { - if (isset($_POST['locked']) != $Locked) { - $Updates[] = 'Locked = ' . ($Locked ? "'0'" : "'1'"); - } - if (isset($_POST['maxgroups']) && ($_POST['maxgroups'] == 0 || is_number($_POST['maxgroups'])) && $_POST['maxgroups'] != $MaxGroups) { - $Updates[] = 'MaxGroups = ' . $_POST['maxgroups']; - } - if (isset($_POST['maxgroups']) && ($_POST['maxgroupsperuser'] == 0 || is_number($_POST['maxgroupsperuser'])) && $_POST['maxgroupsperuser'] != $MaxGroupsPerUser) { - $Updates[] = 'MaxGroupsPerUser = ' . $_POST['maxgroupsperuser']; - } + if (isset($_POST['locked']) != $Locked) { + $Updates[] = 'Locked = ' . ($Locked ? "'0'" : "'1'"); + } + if (isset($_POST['maxgroups']) && ($_POST['maxgroups'] == 0 || is_number($_POST['maxgroups'])) && $_POST['maxgroups'] != $MaxGroups) { + $Updates[] = 'MaxGroups = ' . $_POST['maxgroups']; + } + if (isset($_POST['maxgroups']) && ($_POST['maxgroupsperuser'] == 0 || is_number($_POST['maxgroupsperuser'])) && $_POST['maxgroupsperuser'] != $MaxGroupsPerUser) { + $Updates[] = 'MaxGroupsPerUser = ' . $_POST['maxgroupsperuser']; + } } if (!empty($Updates)) { - $DB->query(' - UPDATE collages - SET '.implode(', ', $Updates)." - WHERE ID = $CollageID"); + $DB->query(' + UPDATE collages + SET '.implode(', ', $Updates)." + WHERE ID = $CollageID"); } $Cache->delete_value('collage_'.$CollageID); header('Location: collages.php?id='.$CollageID); diff --git a/sections/collages/index.php b/sections/collages/index.php index 6a0a45bcd..d21b78583 100644 --- a/sections/collages/index.php +++ b/sections/collages/index.php @@ -1,108 +1,108 @@ - diff --git a/sections/collages/manage.php b/sections/collages/manage.php index d6aa88c85..0f05fcebb 100644 --- a/sections/collages/manage.php +++ b/sections/collages/manage.php @@ -1,146 +1,147 @@ -query(" - SELECT Name, UserID, CategoryID - FROM collages - WHERE ID = '$CollageID'"); + SELECT Name, UserID, CategoryID + FROM collages + WHERE ID = '$CollageID'"); list($Name, $UserID, $CategoryID) = $DB->next_record(); if ($CategoryID == 0 && $UserID != $LoggedUser['ID'] && !check_perms('site_collages_delete')) { - error(403); + error(403); } if ($CategoryID == array_search(ARTIST_COLLAGE, $CollageCats)) { - error(404); + error(404); } $DB->query(" - SELECT - ct.GroupID, - um.ID, - um.Username, - ct.Sort, - tg.CatalogueNumber - FROM collages_torrents AS ct - JOIN torrents_group AS tg ON tg.ID = ct.GroupID - LEFT JOIN users_main AS um ON um.ID = ct.UserID - WHERE ct.CollageID = '$CollageID' - ORDER BY ct.Sort"); + SELECT + ct.GroupID, + um.ID, + um.Username, + ct.Sort, + tg.CatalogueNumber + FROM collages_torrents AS ct + JOIN torrents_group AS tg ON tg.ID = ct.GroupID + LEFT JOIN users_main AS um ON um.ID = ct.UserID + WHERE ct.CollageID = '$CollageID' + ORDER BY ct.Sort"); $GroupIDs = $DB->collect('GroupID'); $CollageDataList = $DB->to_array('GroupID', MYSQLI_ASSOC); if (count($GroupIDs) > 0) { - $TorrentList = Torrents::get_groups($GroupIDs); + $TorrentList = Torrents::get_groups($GroupIDs); } else { - $TorrentList = array(); + $TorrentList = []; } View::show_header("Manage collage: $Name", 'jquery-ui,jquery.tablesorter,sort'); ?>
    -
    -

    Manage collage

    -
    - - - - - -
    Sorting
    -
      -
    • Click on the headings to organize columns automatically.
    • -
    • Sort multiple columns simultaneously by holding down the shift key and clicking other column headers.
    • -
    • Click and drag any row to change its order.
    • -
    • Press "Save All Changes" when you are finished sorting.
    • -
    • Press "Edit" or "Remove" to simply modify one entry.
    • -
    -
    +
    +

    Manage collage

    +
    + + + + + +
    Sorting
    +
      +
    • Click on the headings to organize columns automatically.
    • +
    • Sort multiple columns simultaneously by holding down the shift key and clicking other column headers.
    • +
    • Click and drag any row to change its order.
    • +
    • Press "Save All Changes" when you are finished sorting.
    • +
    • Press "Edit" or "Remove" to simply modify one entry.
    • +
    +
    - - - - - - - - - - - - - - - - + + +
    Order#Cat. #YearArtistTorrentUserTweak
    + + + + + + + + + + + + + + 0) { - $DisplayName .= Artists::display_artists(array('1' => $Artists), true, false); - } - $TorrentLink = "$GroupName"; - $GroupYear = $GroupYear > 0 ? $GroupYear : ''; - if ($GroupVanityHouse) { - $DisplayName .= ' [VH]'; - } + $DisplayName = ''; + if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5]) || !empty($ExtendedArtists[6])) { + unset($ExtendedArtists[2]); + unset($ExtendedArtists[3]); + $DisplayName .= Artists::display_artists($ExtendedArtists, true, false); + } elseif (count($Artists) > 0) { + $DisplayName .= Artists::display_artists(array('1' => $Artists), true, false); + } + $TorrentLink = "$GroupName"; + $GroupYear = $GroupYear > 0 ? $GroupYear : ''; + if ($GroupVanityHouse) { + $DisplayName .= ' [VH]'; + } - $AltCSS = ($Number % 2 === 0) ? 'rowa' : 'rowb'; + $AltCSS = ($Number % 2 === 0) ? 'rowa' : 'rowb'; ?> - - - - - - - - - - - - - - -
    Order#Cat. #YearArtistTorrentUserTweak
    - - - - - - - - -
    - - + +
    + + + + + + + + + + + + + + + + + +
    + + + + + +
    - + diff --git a/sections/collages/manage_artists.php b/sections/collages/manage_artists.php index ee6380d2b..b306e1ba5 100644 --- a/sections/collages/manage_artists.php +++ b/sections/collages/manage_artists.php @@ -1,33 +1,33 @@ -query(" - SELECT Name, UserID, CategoryID - FROM collages - WHERE ID = '$CollageID'"); + SELECT Name, UserID, CategoryID + FROM collages + WHERE ID = '$CollageID'"); list($Name, $UserID, $CategoryID) = $DB->next_record(); if ($CategoryID === '0' && $UserID !== $LoggedUser['ID'] && !check_perms('site_collages_delete')) { - error(403); + error(403); } if ($CategoryID != array_search(ARTIST_COLLAGE, $CollageCats)) { - error(404); + error(404); } $DB->query(" - SELECT - ca.ArtistID, - ag.Name, - um.ID AS UserID, - um.Username, - ca.Sort - FROM collages_artists AS ca - JOIN artists_group AS ag ON ag.ArtistID = ca.ArtistID - LEFT JOIN users_main AS um ON um.ID = ca.UserID - WHERE ca.CollageID = '$CollageID' - ORDER BY ca.Sort"); + SELECT + ca.ArtistID, + ag.Name, + um.ID AS UserID, + um.Username, + ca.Sort + FROM collages_artists AS ca + JOIN artists_group AS ag ON ag.ArtistID = ca.ArtistID + LEFT JOIN users_main AS um ON um.ID = ca.UserID + WHERE ca.CollageID = '$CollageID' + ORDER BY ca.Sort"); $Artists = $DB->to_array('ArtistID', MYSQLI_ASSOC); @@ -37,76 +37,77 @@ ?>
    -
    -

    Manage collage

    -
    - - - - - -
    Sorting
    -
      -
    • Click on the headings to organize columns automatically.
    • -
    • Sort multiple columns simultaneously by holding down the shift key and clicking other column headers.
    • -
    • Click and drag any row to change its order.
    • -
    • Press "Save All Changes" when you are finished sorting.
    • -
    • Press "Edit" or "Remove" to simply modify one entry.
    • -
    -
    +
    +

    Manage collage

    +
    + + + + + +
    Sorting
    +
      +
    • Click on the headings to organize columns automatically.
    • +
    • Sort multiple columns simultaneously by holding down the shift key and clicking other column headers.
    • +
    • Click and drag any row to change its order.
    • +
    • Press "Save All Changes" when you are finished sorting.
    • +
    • Press "Edit" or "Remove" to simply modify one entry.
    • +
    +
    - - - - - - - - - - - - - - - - - - - - - - - - -
    Order#ArtistUserTweak
    - - - - - - - - -
    - - + + + + + + + + + + + + + + + + + + + + + + + + +
    Order#ArtistUserTweak
    + + + + + + + + +
    + +
    - + diff --git a/sections/collages/manage_artists_handle.php b/sections/collages/manage_artists_handle.php index 2801fa737..9d8d3f699 100644 --- a/sections/collages/manage_artists_handle.php +++ b/sections/collages/manage_artists_handle.php @@ -4,72 +4,72 @@ $CollageID = $_POST['collageid']; if (!is_number($CollageID)) { - error(404); + error(404); } $DB->query(" - SELECT UserID, CategoryID - FROM collages - WHERE ID = '$CollageID'"); + SELECT UserID, CategoryID + FROM collages + WHERE ID = '$CollageID'"); list($UserID, $CategoryID) = $DB->next_record(); if ($CategoryID === '0' && $UserID !== $LoggedUser['ID'] && !check_perms('site_collages_delete')) { - error(403); + error(403); } if ($CategoryID !== array_search(ARTIST_COLLAGE, $CollageCats)) { - error(403); + error(403); } $ArtistID = $_POST['artistid']; if (!is_number($ArtistID)) { - error(404); + error(404); } if ($_POST['submit'] === 'Remove') { - $DB->query(" - DELETE FROM collages_artists - WHERE CollageID = '$CollageID' - AND ArtistID = '$ArtistID'"); - $Rows = $DB->affected_rows(); - $DB->query(" - UPDATE collages - SET NumTorrents = NumTorrents - $Rows - WHERE ID = '$CollageID'"); - $Cache->delete_value("artists_collages_$ArtistID"); - $Cache->delete_value("artists_collages_personal_$ArtistID"); + $DB->query(" + DELETE FROM collages_artists + WHERE CollageID = '$CollageID' + AND ArtistID = '$ArtistID'"); + $Rows = $DB->affected_rows(); + $DB->query(" + UPDATE collages + SET NumTorrents = NumTorrents - $Rows + WHERE ID = '$CollageID'"); + $Cache->delete_value("artists_collages_$ArtistID"); + $Cache->delete_value("artists_collages_personal_$ArtistID"); } elseif (isset($_POST['drag_drop_collage_sort_order'])) { - @parse_str($_POST['drag_drop_collage_sort_order'], $Series); - $Series = @array_shift($Series); - if (is_array($Series)) { - $SQL = array(); - foreach ($Series as $Sort => $ArtistID) { - if (is_number($Sort) && is_number($ArtistID)) { - $Sort = ($Sort + 1) * 10; - $SQL[] = sprintf('(%d, %d, %d)', $ArtistID, $Sort, $CollageID); - } - } + @parse_str($_POST['drag_drop_collage_sort_order'], $Series); + $Series = @array_shift($Series); + if (is_array($Series)) { + $SQL = []; + foreach ($Series as $Sort => $ArtistID) { + if (is_number($Sort) && is_number($ArtistID)) { + $Sort = ($Sort + 1) * 10; + $SQL[] = sprintf('(%d, %d, %d)', $ArtistID, $Sort, $CollageID); + } + } - $SQL = ' - INSERT INTO collages_artists - (ArtistID, Sort, CollageID) - VALUES - ' . implode(', ', $SQL) . ' - ON DUPLICATE KEY UPDATE - Sort = VALUES (Sort)'; + $SQL = ' + INSERT INTO collages_artists + (ArtistID, Sort, CollageID) + VALUES + ' . implode(', ', $SQL) . ' + ON DUPLICATE KEY UPDATE + Sort = VALUES (Sort)'; - $DB->query($SQL); - } + $DB->query($SQL); + } } else { - $Sort = $_POST['sort']; - if (!is_number($Sort)) { - error(404); - } - $DB->query(" - UPDATE collages_artists - SET Sort = '$Sort' - WHERE CollageID = '$CollageID' - AND ArtistID = '$ArtistID'"); + $Sort = $_POST['sort']; + if (!is_number($Sort)) { + error(404); + } + $DB->query(" + UPDATE collages_artists + SET Sort = '$Sort' + WHERE CollageID = '$CollageID' + AND ArtistID = '$ArtistID'"); } $Cache->delete_value("collage_$CollageID"); diff --git a/sections/collages/manage_handle.php b/sections/collages/manage_handle.php index b32aa2027..563c64e0f 100644 --- a/sections/collages/manage_handle.php +++ b/sections/collages/manage_handle.php @@ -4,71 +4,71 @@ $CollageID = $_POST['collageid']; if (!is_number($CollageID)) { - error(404); + error(404); } $DB->query(" - SELECT UserID, CategoryID - FROM collages - WHERE ID = '$CollageID'"); + SELECT UserID, CategoryID + FROM collages + WHERE ID = '$CollageID'"); list($UserID, $CategoryID) = $DB->next_record(); if ($CategoryID === '0' && $UserID !== $LoggedUser['ID'] && !check_perms('site_collages_delete')) { - error(403); + error(403); } $GroupID = $_POST['groupid']; if (!is_number($GroupID)) { - error(404); + error(404); } if ($_POST['submit'] === 'Remove') { - $DB->query(" - DELETE FROM collages_torrents - WHERE CollageID = '$CollageID' - AND GroupID = '$GroupID'"); - $Rows = $DB->affected_rows(); - $DB->query(" - UPDATE collages - SET NumTorrents = NumTorrents - $Rows - WHERE ID = '$CollageID'"); - $Cache->delete_value("torrents_details_$GroupID"); - $Cache->delete_value("torrent_collages_$GroupID"); - $Cache->delete_value("torrent_collages_personal_$GroupID"); + $DB->query(" + DELETE FROM collages_torrents + WHERE CollageID = '$CollageID' + AND GroupID = '$GroupID'"); + $Rows = $DB->affected_rows(); + $DB->query(" + UPDATE collages + SET NumTorrents = NumTorrents - $Rows + WHERE ID = '$CollageID'"); + $Cache->delete_value("torrents_details_$GroupID"); + $Cache->delete_value("torrent_collages_$GroupID"); + $Cache->delete_value("torrent_collages_personal_$GroupID"); } elseif (isset($_POST['drag_drop_collage_sort_order'])) { - @parse_str($_POST['drag_drop_collage_sort_order'], $Series); - $Series = @array_shift($Series); - if (is_array($Series)) { - $SQL = array(); - foreach ($Series as $Sort => $GroupID) { - if (is_number($Sort) && is_number($GroupID)) { - $Sort = ($Sort + 1) * 10; - $SQL[] = sprintf('(%d, %d, %d)', $GroupID, $Sort, $CollageID); - } - } + @parse_str($_POST['drag_drop_collage_sort_order'], $Series); + $Series = @array_shift($Series); + if (is_array($Series)) { + $SQL = []; + foreach ($Series as $Sort => $GroupID) { + if (is_number($Sort) && is_number($GroupID)) { + $Sort = ($Sort + 1) * 10; + $SQL[] = sprintf('(%d, %d, %d)', $GroupID, $Sort, $CollageID); + } + } - $SQL = ' - INSERT INTO collages_torrents - (GroupID, Sort, CollageID) - VALUES - ' . implode(', ', $SQL) . ' - ON DUPLICATE KEY UPDATE - Sort = VALUES (Sort)'; + $SQL = ' + INSERT INTO collages_torrents + (GroupID, Sort, CollageID) + VALUES + ' . implode(', ', $SQL) . ' + ON DUPLICATE KEY UPDATE + Sort = VALUES (Sort)'; - $DB->query($SQL); - } + $DB->query($SQL); + } } else { - $Sort = $_POST['sort']; - if (!is_number($Sort)) { - error(404); - } - $DB->query(" - UPDATE collages_torrents - SET Sort = '$Sort' - WHERE CollageID = '$CollageID' - AND GroupID = '$GroupID'"); + $Sort = $_POST['sort']; + if (!is_number($Sort)) { + error(404); + } + $DB->query(" + UPDATE collages_torrents + SET Sort = '$Sort' + WHERE CollageID = '$CollageID' + AND GroupID = '$GroupID'"); } $Cache->delete_value("collage_$CollageID"); diff --git a/sections/collages/new.php b/sections/collages/new.php index 01989f841..294ac64c8 100644 --- a/sections/collages/new.php +++ b/sections/collages/new.php @@ -1,92 +1,93 @@ -
    - -
    -
    -
    +
    + -
    - - - - - - - - - - + + + + + + + + + + + + + + + +
    Name - name="name" size="60" id="namebox" value="" /> - -
    Category - + + + + + + + + + - - - - - - - - - - - - - - - -
    Name + name="name" size="60" id="namebox" value="" /> + +
    Category + -
    -
      -
    • Theme - A collage containing releases that all relate to a certain theme (e.g. "Searching for the Perfect Beat", "Concept Albums", "Funky Groove", etc.).
    • -
    • Genre introduction - A subjective introduction to a genre composed by our own users.
    • -
    • Discography - A collage containing all the releases of an artist, which can be useful for keeping track of side projects.
    • -
    • Label - A collage containing all the releases of a particular record label.
    • -
    • Staff picks - A listing of recommendations picked by the staff on special occasions.
    • -
    • Charts - Contains all the releases that comprise a certain type of chart (e.g. Billboard Top 100, Pitchfork Top 100, Top 10, etc.).
    • - -
    • Personal - You can put whatever you want here. It is your own personal collage.
    • - -
    -
    Description - -
    Tags (comma-separated) - -
    - Please ensure your collage will be allowed under the Collage Rules. -
    - + +
    +
      +
    • Theme - A collage containing releases that all relate to a certain theme (e.g. "Searching for the Perfect Beat", "Concept Albums", "Funky Groove", etc.).
    • +
    • Genre introduction - A subjective introduction to a genre composed by our own users.
    • +
    • Discography - A collage containing all the releases of an artist, which can be useful for keeping track of side projects.
    • +
    • Label - A collage containing all the releases of a particular record label.
    • +
    • Staff picks - A listing of recommendations picked by the staff on special occasions.
    • +
    • Charts - Contains all the releases that comprise a certain type of chart (e.g. Billboard Top 100, Pitchfork Top 100, Top 10, etc.).
    • + +
    • Personal - You can put whatever you want here. It is your own personal collage.
    • + +
    +
    Description + +
    Tags (comma-separated) + +
    + Please ensure your collage will be allowed under the Collage Rules. +
    + - + diff --git a/sections/collages/new_handle.php b/sections/collages/new_handle.php index 289ead34b..8fb0c065c 100644 --- a/sections/collages/new_handle.php +++ b/sections/collages/new_handle.php @@ -4,89 +4,89 @@ include(SERVER_ROOT.'/classes/validate.class.php'); $Val = new VALIDATE; -$P = array(); +$P = []; $P = db_array($_POST); if ($P['category'] > 0 || check_perms('site_collages_renamepersonal')) { - $Val->SetFields('name', '1', 'string', 'The name must be between 3 and 100 characters', array('maxlength' => 100, 'minlength' => 3)); + $Val->SetFields('name', '1', 'string', 'The name must be between 3 and 100 characters', array('maxlength' => 100, 'minlength' => 3)); } else { - // Get a collage name and make sure it's unique - $name = $LoggedUser['Username']."'s personal collage"; - $P['name'] = db_string($name); - $DB->query(" - SELECT ID - FROM collages - WHERE Name = '".$P['name']."'"); - $i = 2; - while ($DB->has_results()) { - $P['name'] = db_string("$name no. $i"); - $DB->query(" - SELECT ID - FROM collages - WHERE Name = '".$P['name']."'"); - $i++; - } + // Get a collage name and make sure it's unique + $name = $LoggedUser['Username']."'s personal collage"; + $P['name'] = db_string($name); + $DB->query(" + SELECT ID + FROM collages + WHERE Name = '".$P['name']."'"); + $i = 2; + while ($DB->has_results()) { + $P['name'] = db_string("$name no. $i"); + $DB->query(" + SELECT ID + FROM collages + WHERE Name = '".$P['name']."'"); + $i++; + } } $Val->SetFields('description', '1', 'string', 'The description must be between 10 and 65535 characters', array('maxlength' => 65535, 'minlength' => 10)); $Err = $Val->ValidateForm($_POST); if (!$Err && $P['category'] === '0') { - $DB->query(" - SELECT COUNT(ID) - FROM collages - WHERE UserID = '$LoggedUser[ID]' - AND CategoryID = '0' - AND Deleted = '0'"); - list($CollageCount) = $DB->next_record(); - if (($CollageCount >= $LoggedUser['Permissions']['MaxCollages']) || !check_perms('site_collages_personal')) { - $Err = 'You may not create a personal collage.'; - } elseif (check_perms('site_collages_renamepersonal') && !stristr($P['name'], $LoggedUser['Username'])) { - $Err = 'Your personal collage\'s title must include your username.'; - } + $DB->query(" + SELECT COUNT(ID) + FROM collages + WHERE UserID = '$LoggedUser[ID]' + AND CategoryID = '0' + AND Deleted = '0'"); + list($CollageCount) = $DB->next_record(); + if (($CollageCount >= $LoggedUser['Permissions']['MaxCollages']) || !check_perms('site_collages_personal')) { + $Err = 'You may not create a personal collage.'; + } elseif (check_perms('site_collages_renamepersonal') && !stristr($P['name'], $LoggedUser['Username'])) { + $Err = 'Your personal collage\'s title must include your username.'; + } } if (!$Err) { - $DB->query(" - SELECT ID, Deleted - FROM collages - WHERE Name = '$P[name]'"); - if ($DB->has_results()) { - list($ID, $Deleted) = $DB->next_record(); - if ($Deleted) { - $Err = 'That collection already exists but needs to be recovered; please contact the staff team!'; - } else { - $Err = "That collection already exists: $ID."; - } - } + $DB->query(" + SELECT ID, Deleted + FROM collages + WHERE Name = '$P[name]'"); + if ($DB->has_results()) { + list($ID, $Deleted) = $DB->next_record(); + if ($Deleted) { + $Err = 'That collection already exists but needs to be recovered; please contact the staff team!'; + } else { + $Err = "That collection already exists: $ID."; + } + } } if (!$Err) { - if (empty($CollageCats[$P['category']])) { - $Err = 'Please select a category'; - } + if (empty($CollageCats[$P['category']])) { + $Err = 'Please select a category'; + } } if ($Err) { - $Name = $_POST['name']; - $Category = $_POST['category']; - $Tags = $_POST['tags']; - $Description = $_POST['description']; - include(SERVER_ROOT.'/sections/collages/new.php'); - die(); + $Name = $_POST['name']; + $Category = $_POST['category']; + $Tags = $_POST['tags']; + $Description = $_POST['description']; + include(SERVER_ROOT.'/sections/collages/new.php'); + die(); } $TagList = explode(',', $_POST['tags']); foreach ($TagList as $ID => $Tag) { - $TagList[$ID] = Misc::sanitize_tag($Tag); + $TagList[$ID] = Misc::sanitize_tag($Tag); } $TagList = implode(' ', $TagList); $DB->query(" - INSERT INTO collages - (Name, Description, UserID, TagList, CategoryID) - VALUES - ('$P[name]', '$P[description]', $LoggedUser[ID], '$TagList', '$P[category]')"); + INSERT INTO collages + (Name, Description, UserID, TagList, CategoryID) + VALUES + ('$P[name]', '$P[description]', $LoggedUser[ID], '$TagList', '$P[category]')"); $CollageID = $DB->inserted_id(); $Cache->delete_value("collage_$CollageID"); diff --git a/sections/collages/recover.php b/sections/collages/recover.php index ad25db211..e05e67634 100644 --- a/sections/collages/recover.php +++ b/sections/collages/recover.php @@ -1,49 +1,49 @@ -query(" - SELECT Name - FROM collages - WHERE ID = $CollageID"); - if (!$DB->has_results()) { - error('Collage is completely deleted'); - } else { - $DB->query(" - UPDATE collages - SET Deleted = '0' - WHERE ID = $CollageID"); - $Cache->delete_value("collage_$CollageID"); - Misc::write_log("Collage $CollageID was recovered by ".$LoggedUser['Username']); - header("Location: collages.php?id=$CollageID"); - } + $DB->query(" + SELECT Name + FROM collages + WHERE ID = $CollageID"); + if (!$DB->has_results()) { + error('Collage is completely deleted'); + } else { + $DB->query(" + UPDATE collages + SET Deleted = '0' + WHERE ID = $CollageID"); + $Cache->delete_value("collage_$CollageID"); + Misc::write_log("Collage $CollageID was recovered by ".$LoggedUser['Username']); + header("Location: collages.php?id=$CollageID"); + } } View::show_header('Collage recovery!'); ?>
    -
    -
    - Recover deleted collage -
    -
    -
    - - -
    - Collage ID: - -
    -
    - -
    -
    -
    -
    +
    +
    + Recover deleted collage +
    +
    +
    + + +
    + Collage ID: + +
    +
    + +
    +
    +
    +
    -query(" - SELECT Name, CategoryID, UserID - FROM collages - WHERE ID = '$CollageID'"); + SELECT Name, CategoryID, UserID + FROM collages + WHERE ID = '$CollageID'"); list($Name, $CategoryID, $UserID) = $DB->next_record(MYSQLI_NUM, false); if (!check_perms('site_collages_delete') && $UserID !== $LoggedUser['ID']) { - error(403); + error(403); } $Reason = trim($_POST['reason']); if (!$Reason) { - error('You must enter a reason!'); + error('You must enter a reason!'); } $DB->query(" - SELECT GroupID - FROM collages_torrents - WHERE CollageID = '$CollageID'"); + SELECT GroupID + FROM collages_torrents + WHERE CollageID = '$CollageID'"); while (list($GroupID) = $DB->next_record()) { - $Cache->delete_value("torrents_details_$GroupID"); - $Cache->delete_value("torrent_collages_$GroupID"); - $Cache->delete_value("torrent_collages_personal_$GroupID"); + $Cache->delete_value("torrents_details_$GroupID"); + $Cache->delete_value("torrent_collages_$GroupID"); + $Cache->delete_value("torrent_collages_personal_$GroupID"); } //Personal collages have CategoryID 0 if ($CategoryID == 0) { - $DB->query(" - DELETE FROM collages - WHERE ID = '$CollageID'"); - $DB->query(" - DELETE FROM collages_torrents - WHERE CollageID = '$CollageID'"); - Comments::delete_page('collages', $CollageID); + $DB->query(" + DELETE FROM collages + WHERE ID = '$CollageID'"); + $DB->query(" + DELETE FROM collages_torrents + WHERE CollageID = '$CollageID'"); + Comments::delete_page('collages', $CollageID); } else { - $DB->query(" - UPDATE collages - SET Deleted = '1' - WHERE ID = '$CollageID'"); - Subscriptions::flush_subscriptions('collages', $CollageID); - Subscriptions::flush_quote_notifications('collages', $CollageID); + $DB->query(" + UPDATE collages + SET Deleted = '1' + WHERE ID = '$CollageID'"); + Subscriptions::flush_subscriptions('collages', $CollageID); + Subscriptions::flush_quote_notifications('collages', $CollageID); } Misc::write_log("Collage $CollageID ($Name) was deleted by ".$LoggedUser['Username'].": $Reason"); diff --git a/sections/collages/torrent_collage.php b/sections/collages/torrent_collage.php index be4a09d02..2e043160d 100644 --- a/sections/collages/torrent_collage.php +++ b/sections/collages/torrent_collage.php @@ -1,32 +1,32 @@ query(" - SELECT - ct.GroupID, - ct.UserID - FROM collages_torrents AS ct - JOIN torrents_group AS tg ON tg.ID = ct.GroupID - WHERE ct.CollageID = '$CollageID' - ORDER BY ct.Sort"); + SELECT + ct.GroupID, + ct.UserID + FROM collages_torrents AS ct + JOIN torrents_group AS tg ON tg.ID = ct.GroupID + WHERE ct.CollageID = '$CollageID' + ORDER BY ct.Sort"); $GroupIDs = $DB->collect('GroupID'); $Contributors = $DB->to_pair('GroupID', 'UserID', false); if (count($GroupIDs) > 0) { - $TorrentList = Torrents::get_groups($GroupIDs); - $UserVotes = Votes::get_user_votes($LoggedUser['ID']); + $TorrentList = Torrents::get_groups($GroupIDs); + $UserVotes = Votes::get_user_votes($LoggedUser['ID']); } else { - $TorrentList = array(); + $TorrentList = []; } $NumGroups = count($TorrentList); $NumGroupsByUser = 0; -$TopArtists = array(); -$UserAdditions = array(); +$TopArtists = []; +$UserAdditions = []; $Number = 0; // We loop through all groups building some basic statistics for them @@ -34,64 +34,64 @@ function compare($X, $Y) { // HTML inline instead of doing it all up here. Yeah, it's more complicated // but the memory savings are a lot foreach ($GroupIDs as $Idx => $GroupID) { - if (!isset($TorrentList[$GroupID])) { - unset($GroupIDs[$Idx]); - continue; - } - $Group = $TorrentList[$GroupID]; - extract(Torrents::array_group($Group)); - $UserID = $Contributors[$GroupID]; - new Tags($TagList); + if (!isset($TorrentList[$GroupID])) { + unset($GroupIDs[$Idx]); + continue; + } + $Group = $TorrentList[$GroupID]; + extract(Torrents::array_group($Group)); + $UserID = $Contributors[$GroupID]; + new Tags($TagList); - // Handle stats and stuff - $Number++; - if ($UserID == $LoggedUser['ID']) { - $NumGroupsByUser++; - } + // Handle stats and stuff + $Number++; + if ($UserID == $LoggedUser['ID']) { + $NumGroupsByUser++; + } - if (!empty($ExtendedArtists[1]) - || !empty($ExtendedArtists[4]) - || !empty($ExtendedArtists[5]) - || !empty($ExtendedArtists[6]) - ) { - $CountArtists = array_merge((array)$ExtendedArtists[1], (array)$ExtendedArtists[4], (array)$ExtendedArtists[5], (array)$ExtendedArtists[6]); - } else { - $CountArtists = $GroupArtists; - } + if (!empty($ExtendedArtists[1]) + || !empty($ExtendedArtists[4]) + || !empty($ExtendedArtists[5]) + || !empty($ExtendedArtists[6]) + ) { + $CountArtists = array_merge((array)$ExtendedArtists[1], (array)$ExtendedArtists[4], (array)$ExtendedArtists[5], (array)$ExtendedArtists[6]); + } else { + $CountArtists = $GroupArtists; + } - if ($CountArtists) { - foreach ($CountArtists as $Artist) { - if (!isset($TopArtists[$Artist['id']])) { - $TopArtists[$Artist['id']] = array('name' => $Artist['name'], 'count' => 1); - } else { - $TopArtists[$Artist['id']]['count']++; - } - } - } + if ($CountArtists) { + foreach ($CountArtists as $Artist) { + if (!isset($TopArtists[$Artist['id']])) { + $TopArtists[$Artist['id']] = array('name' => $Artist['name'], 'count' => 1); + } else { + $TopArtists[$Artist['id']]['count']++; + } + } + } - if (!isset($UserAdditions[$UserID])) { - $UserAdditions[$UserID] = 0; - } - $UserAdditions[$UserID]++; + if (!isset($UserAdditions[$UserID])) { + $UserAdditions[$UserID] = 0; + } + $UserAdditions[$UserID]++; } // Re-index the array so we can abuse that later to slice parts out of it $GroupIDs = array_values($GroupIDs); if ($CollageCategoryID === '0' && !check_perms('site_collages_delete')) { - if (!check_perms('site_collages_personal') || $CreatorID !== $LoggedUser['ID']) { - $PreventAdditions = true; - } + if (!check_perms('site_collages_personal') || $CreatorID !== $LoggedUser['ID']) { + $PreventAdditions = true; + } } if (!check_perms('site_collages_delete') - && ( - $Locked - || ($MaxGroups > 0 && $NumGroups >= $MaxGroups) - || ($MaxGroupsPerUser > 0 && $NumGroupsByUser >= $MaxGroupsPerUser) - ) + && ( + $Locked + || ($MaxGroups > 0 && $NumGroups >= $MaxGroups) + || ($MaxGroupsPerUser > 0 && $NumGroupsByUser >= $MaxGroupsPerUser) + ) ) { - $PreventAdditions = true; + $PreventAdditions = true; } // Silly hack for people who are on the old setting @@ -100,535 +100,550 @@ function compare($X, $Y) { View::show_header($Name, 'browse,collage,bbcode,voting,recommend'); ?>
    -
    -

    - - - +
    + + +
    + +
    +
    Add torrent groupBatch add
    +
    +
    + + + +
    + +
    +
    + +
    + Enter the URL of a torrent group on the site. +
    +
    + +
    + +

    Comments

    +query(" - SELECT - c.ID, - c.Body, - c.AuthorID, - um.Username, - c.AddedTime - FROM comments AS c - LEFT JOIN users_main AS um ON um.ID = c.AuthorID - WHERE c.Page = 'collages' - AND c.PageID = $CollageID - ORDER BY c.ID DESC - LIMIT 15"); - $CommentList = $DB->to_array(false, MYSQLI_NUM); + $DB->query(" + SELECT + c.ID, + c.Body, + c.AuthorID, + um.Username, + c.AddedTime + FROM comments AS c + LEFT JOIN users_main AS um ON um.ID = c.AuthorID + WHERE c.Page = 'collages' + AND c.PageID = $CollageID + ORDER BY c.ID DESC + LIMIT 15"); + $CommentList = $DB->to_array(false, MYSQLI_NUM); } foreach ($CommentList as $Comment) { - list($CommentID, $Body, $UserID, $Username, $CommentTime) = $Comment; + list($CommentID, $Body, $UserID, $Username, $CommentTime) = $Comment; ?> -
    -
    - -
    - Report -
    -
    -
    - +
    + +
    + Report +
    +
    + + - - + View all comments + + -
    -
    Add comment
    -
    - - - - -
    -
    - -
    -
    - -
    -
    -
    -
    - +
    Add comment
    +
    + + + + +
    +
    + +
    +
    + +
    +
    +
    + + - -
    - +
    + -
    -
    Cover Art
    -
      - +
      Cover Art
      +
        + -
      -
    - $CollageCovers) { ?> - - $CollageCovers) { - for ($i = $NumGroups + 1; $i <= ceil($NumGroups / $CollageCovers) * $CollageCovers; $i++) { - $CollagePages[count($CollagePages) - 1] .= '
  • '; - } -} + +
    + $CollageCovers) { ?> + + $CollageCovers) { + for ($i = $NumGroups + 1; $i <= ceil($NumGroups / $CollageCovers) * $CollageCovers; $i++) { + $CollagePages[count($CollagePages) - 1] .= '
  • '; + } + } ?> - -//); + //]]> + - - - - - - - - - - +
    TorrentsSizeSnatchesSeedersLeechers
    + + + + + + + + + $GroupID) { - $Group = $TorrentList[$GroupID]; - extract(Torrents::array_group($Group)); - /** - * @var int $GroupID - * @var string $GroupName - * @var string $GroupYear - * @var int $GroupCategoryID - * @var string $GroupRecordLabel - * @var bool $GroupVanityHouse - * @var array $GroupFlags - * @var array $Artists - * @var array $ExtendedArtists - * @var string $TagList - * @var string $WikiImage - */ + $Group = $TorrentList[$GroupID]; + extract(Torrents::array_group($Group)); + /** + * @var int $GroupID + * @var string $GroupName + * @var string $GroupYear + * @var int $GroupCategoryID + * @var string $GroupRecordLabel + * @var bool $GroupVanityHouse + * @var array $GroupFlags + * @var array $Artists + * @var array $ExtendedArtists + * @var string $TagList + * @var string $WikiImage + */ - $TorrentTags = new Tags($TagList); - $Number++; - $DisplayName = "$Number - "; + $TorrentTags = new Tags($TagList); + $Number++; + $DisplayName = "$Number - "; - if (!empty($ExtendedArtists[1]) - || !empty($ExtendedArtists[4]) - || !empty($ExtendedArtists[5]) - || !empty($ExtendedArtists[6]) - ) { - unset($ExtendedArtists[2]); - unset($ExtendedArtists[3]); - $DisplayName .= Artists::display_artists($ExtendedArtists); - } - elseif (count($GroupArtists) > 0) { - $DisplayName .= Artists::display_artists(array('1' => $GroupArtists)); - } + if (!empty($ExtendedArtists[1]) + || !empty($ExtendedArtists[4]) + || !empty($ExtendedArtists[5]) + || !empty($ExtendedArtists[6]) + ) { + unset($ExtendedArtists[2]); + unset($ExtendedArtists[3]); + $DisplayName .= Artists::display_artists($ExtendedArtists); + } + elseif (count($GroupArtists) > 0) { + $DisplayName .= Artists::display_artists(array('1' => $GroupArtists)); + } - $DisplayName .= "$GroupName"; - if ($GroupYear > 0) { - $DisplayName = "$DisplayName [$GroupYear]"; - } - if ($GroupVanityHouse) { - $DisplayName .= ' [VH]'; - } - $SnatchedGroupClass = ($GroupFlags['IsSnatched'] ? ' snatched_group' : ''); - $UserVote = isset($UserVotes[$GroupID]) ? $UserVotes[$GroupID]['Type'] : ''; + $DisplayName .= "$GroupName"; + if ($GroupYear > 0) { + $DisplayName = "$DisplayName [$GroupYear]"; + } + if ($GroupVanityHouse) { + $DisplayName .= ' [VH]'; + } + $SnatchedGroupClass = ($GroupFlags['IsSnatched'] ? ' snatched_group' : ''); + $UserVote = isset($UserVotes[$GroupID]) ? $UserVotes[$GroupID]['Type'] : ''; - if (count($Torrents) > 1 || $GroupCategoryID == 1) { - // Grouped torrents - $ShowGroups = !(!empty($LoggedUser['TorrentGrouping']) && $LoggedUser['TorrentGrouping'] == 1); - ?> - - - - - - 1 || $GroupCategoryID == 1) { + // Grouped torrents + $ShowGroups = !(!empty($LoggedUser['TorrentGrouping']) && $LoggedUser['TorrentGrouping'] == 1); + ?> + + + + + + $Torrent) { + foreach ($Torrents as $TorrentID => $Torrent) { - if ($Torrent['Remastered'] && !$Torrent['RemasterYear']) { - $FirstUnknown = !isset($FirstUnknown); - } - $SnatchedTorrentClass = $Torrent['IsSnatched'] ? ' snatched_torrent' : ''; + if ($Torrent['Remastered'] && !$Torrent['RemasterYear']) { + $FirstUnknown = !isset($FirstUnknown); + } + $SnatchedTorrentClass = $Torrent['IsSnatched'] ? ' snatched_torrent' : ''; - if ($Torrent['RemasterTitle'] != $LastRemasterTitle - || $Torrent['RemasterYear'] != $LastRemasterYear - || $Torrent['RemasterRecordLabel'] != $LastRemasterRecordLabel - || $Torrent['RemasterCatalogueNumber'] != $LastRemasterCatalogueNumber - || (isset($FirstUnknown) && $FirstUnknown) - || $Torrent['Media'] != $LastMedia - ) { - $EditionID++; - ?> - - - - - - - - - - - - + + + + + + + + + + + + $GroupName"; + $DisplayName = "$GroupName"; - if ($Torrent['IsSnatched']) { - $DisplayName .= ' ' . Format::torrent_label('Snatched!'); - } - if ($Torrent['FreeTorrent'] == '1') { - $DisplayName .= ' ' . Format::torrent_label('Freeleech!'); - } - elseif ($Torrent['FreeTorrent'] == '2') { - $DisplayName .= ' ' . Format::torrent_label('Neutral Leech!'); - } - elseif ($Torrent['PersonalFL']) { - $DisplayName .= ' ' . Format::torrent_label('Personal Freeleech!'); - } - $SnatchedTorrentClass = ($Torrent['IsSnatched'] ? ' snatched_torrent' : ''); - ?> - - - - - - - - - - + + + + + + + + + + -
    TorrentsSizeSnatchesSeedersLeechers
    -
    - -
    -
    -
    -
    - - - - Remove bookmark - - - - Bookmark - - -
    format() ?>
    -
    +
    + +
    +
    +
    +
    + + + + Remove bookmark + + + + Bookmark + + +
    format() ?>
    +
    -
    - - DL - - | FL - - | RP - -   »  -
    +
    + + DL + + | FL + + | RP + +   »  +
    -
    -
    -
    - - DL - - | FL - - | RP - - - -
    format() ?>
    -
    +
    +
    +
    + + DL + + | FL + + | RP + + + +
    format() ?>
    +
    -
    + + - torrents.Time'; - $Conditions[] = "comments.AuthorID != $UserID"; - $Title = 'Comments left on torrents ' . ($Self ? 'you\'ve' : $Username . ' has') . ' uploaded'; - $Header = 'Comments left on torrents ' . ($Self ? 'you\'ve' : Users::format_username($UserID, false, false, false) . ' has') . ' uploaded'; - } else { - $Type = 'default'; - $Conditions[] = "comments.AuthorID = $UserID"; - $Title = 'Torrent comments left by ' . ($Self ? 'you' : $Username); - $Header = 'Torrent comments left by ' . ($Self ? 'you' : Users::format_username($UserID, false, false, false)); - } - break; + case 'artist': + $Field1 = 'artists_group.ArtistID'; + $Field2 = 'artists_group.Name'; + $Table = 'artists_group'; + $Title = 'Artist comments left by ' . ($Self ? 'you' : $Username); + $Header = 'Artist comments left by ' . ($Self ? 'you' : Users::format_username($UserID, false, false, false)); + $Conditions[] = "comments.AuthorID = $UserID"; + break; + case 'collages': + $Field1 = 'collages.ID'; + $Field2 = 'collages.Name'; + $Table = 'collages'; + $Conditions[] = "collages.Deleted = '0'"; + if ($Type == 'created') { + $Conditions[] = "collages.UserID = $UserID"; + $Conditions[] = "comments.AuthorID != $UserID"; + $Title = 'Comments left on collages ' . ($Self ? 'you' : $Username) . ' created'; + $Header = 'Comments left on collages ' . ($Self ? 'you' : Users::format_username($UserID, false, false, false)) . ' created'; + } elseif ($Type == 'contributed') { + $Conditions[] = 'IF(collages.CategoryID = ' . array_search('Artists', $CollageCats) . ', collages_artists.ArtistID, collages_torrents.GroupID) IS NOT NULL'; + $Conditions[] = "comments.AuthorID != $UserID"; + $Join[] = "LEFT JOIN collages_torrents ON collages_torrents.CollageID = collages.ID AND collages_torrents.UserID = $UserID"; + $Join[] = "LEFT JOIN collages_artists ON collages_artists.CollageID = collages.ID AND collages_artists.UserID = $UserID"; + $Title = 'Comments left on collages ' . ($Self ? 'you\'ve' : $Username . ' has') . ' contributed to'; + $Header = 'Comments left on collages ' . ($Self ? 'you\'ve' : Users::format_username($UserID, false, false, false).' has') . ' contributed to'; + } else { + $Type = 'default'; + $Conditions[] = "comments.AuthorID = $UserID"; + $Title = 'Collage comments left by ' . ($Self ? 'you' : $Username); + $Header = 'Collage comments left by ' . ($Self ? 'you' : Users::format_username($UserID, false, false, false)); + } + break; + case 'requests': + $Field1 = 'requests.ID'; + $Field2 = 'requests.Title'; + $Table = 'requests'; + if ($Type == 'created') { + $Conditions[] = "requests.UserID = $UserID"; + $Conditions[] = "comments.AuthorID != $UserID"; + $Title = 'Comments left on requests ' . ($Self ? 'you' : $Username) . ' created'; + $Header = 'Comments left on requests ' . ($Self ? 'you' : Users::format_username($UserID, false, false, false)) . ' created'; + } elseif ($Type == 'voted') { + $Conditions[] = "requests_votes.UserID = $UserID"; + $Conditions[] = "comments.AuthorID != $UserID"; + $Join[] = 'JOIN requests_votes ON requests_votes.RequestID = requests.ID'; + $Title = 'Comments left on requests ' . ($Self ? 'you\'ve' : $Username . ' has') . ' voted on'; + $Header = 'Comments left on requests ' . ($Self ? 'you\'ve' : Users::format_username($UserID, false, false, false) . ' has') . ' voted on'; + } else { + $Type = 'default'; + $Conditions[] = "comments.AuthorID = $UserID"; + $Title = 'Request comments left by ' . ($Self ? 'you' : $Username); + $Header = 'Request comments left by ' . ($Self ? 'you' : Users::format_username($UserID, false, false, false)); + } + break; + case 'torrents': + default: + $Action = 'torrents'; + $Field1 = 'torrents.GroupID'; + $Field2 = 'torrents_group.Name'; + $Table = 'torrents'; + $Join[] = 'JOIN torrents_group ON torrents.GroupID = torrents_group.ID'; + if ($Type == 'uploaded') { + $Conditions[] = "torrents.UserID = $UserID"; + $Conditions[] = 'comments.AddedTime > torrents.Time'; + $Conditions[] = "comments.AuthorID != $UserID"; + $Title = 'Comments left on torrents ' . ($Self ? 'you\'ve' : $Username . ' has') . ' uploaded'; + $Header = 'Comments left on torrents ' . ($Self ? 'you\'ve' : Users::format_username($UserID, false, false, false) . ' has') . ' uploaded'; + } else { + $Type = 'default'; + $Conditions[] = "comments.AuthorID = $UserID"; + $Title = 'Torrent comments left by ' . ($Self ? 'you' : $Username); + $Header = 'Torrent comments left by ' . ($Self ? 'you' : Users::format_username($UserID, false, false, false)); + } + break; } $Join[] = "JOIN comments ON comments.Page = '$Action' AND comments.PageID = $Field1"; $Join = implode("\n\t\t", $Join); @@ -139,23 +139,23 @@ $Conditions = ($Conditions ? 'WHERE ' . $Conditions : ''); $SQL = " - SELECT - SQL_CALC_FOUND_ROWS - comments.AuthorID, - comments.Page, - comments.PageID, - $Field2, - comments.ID, - comments.Body, - comments.AddedTime, - comments.EditedTime, - comments.EditedUserID - FROM $Table - $Join - $Conditions - GROUP BY comments.ID - ORDER BY comments.ID DESC - LIMIT $Limit"; + SELECT + SQL_CALC_FOUND_ROWS + comments.AuthorID, + comments.Page, + comments.PageID, + $Field2, + comments.ID, + comments.Body, + comments.AddedTime, + comments.EditedTime, + comments.EditedUserID + FROM $Table + $Join + $Conditions + GROUP BY comments.ID + ORDER BY comments.ID DESC + LIMIT $Limit"; $Comments = $DB->query($SQL); $Count = $DB->record_count(); @@ -166,110 +166,113 @@ $DB->set_query_id($Comments); if ($Action == 'requests') { - $RequestIDs = array_flip(array_flip($DB->collect('PageID'))); - $Artists = array(); - foreach ($RequestIDs as $RequestID) { - $Artists[$RequestID] = Requests::get_artists($RequestID); - } - $DB->set_query_id($Comments); + $RequestIDs = array_flip(array_flip($DB->collect('PageID'))); + $Artists = []; + foreach ($RequestIDs as $RequestID) { + $Artists[$RequestID] = Requests::get_artists($RequestID); + } + $DB->set_query_id($Comments); } elseif ($Action == 'torrents') { - $GroupIDs = array_flip(array_flip($DB->collect('PageID'))); - $Artists = Artists::get_artists($GroupIDs); - $DB->set_query_id($Comments); + $GroupIDs = array_flip(array_flip($DB->collect('PageID'))); + $Artists = Artists::get_artists($GroupIDs); + $DB->set_query_id($Comments); } $LinkID = (!$Self ? '&id=' . $UserID : ''); -$ActionLinks = $TypeLinks = array(); +$ActionLinks = $TypeLinks = []; if ($Action != 'artist') { - $ActionLinks[] = 'Artist comments'; + $ActionLinks[] = 'Artist comments'; } if ($Action != 'collages') { - $ActionLinks[] = 'Collage comments'; + $ActionLinks[] = 'Collage comments'; } if ($Action != 'requests') { - $ActionLinks[] = 'Request comments'; + $ActionLinks[] = 'Request comments'; } if ($Action != 'torrents') { - $ActionLinks[] = 'Torrent comments'; + $ActionLinks[] = 'Torrent comments'; } switch ($Action) { - case 'collages': - $BaseLink = 'comments.php?action=collages' . $LinkID; - if ($Type != 'default') { - $TypeLinks[] = 'Display comments left on collages ' . ($Self ? 'you\'ve' : $Username . ' has') . ' made'; - } - if ($Type != 'created') { - $TypeLinks[] = 'Display comments left on ' . ($Self ? 'your collages' : 'collages created by ' .$Username) . ''; - } - if ($Type != 'contributed') { - $TypeLinks[] = 'Display comments left on collages ' . ($Self ? 'you\'ve' : $Username . ' has') . ' contributed to'; - } - break; - case 'requests': - $BaseLink = 'comments.php?action=requests' . $LinkID; - if ($Type != 'default') { - $TypeLinks[] = 'Display comments left on requests ' . ($Self ? 'you\'ve' : $Username . 'has') . ' made'; - } - if ($Type != 'created') { - // - //your requests - $TypeLinks[] = 'Display comments left on requests ' . ($Self ? 'you' : $Username) . ' created'; - } - if ($Type != 'voted') { - $TypeLinks[] = 'Display comments left on requests ' . ($Self ? 'you\'ve' : $Username . ' has') . ' voted on'; - } - break; - case 'torrents': - if ($Type != 'default') { - $TypeLinks[] = 'Display comments left on torrents ' . ($Self ? 'you\'ve' : $Username . ' has' ) . ' made'; - } - if ($Type != 'uploaded') { - $TypeLinks[] = 'Display comments left on torrents ' . ($Self ? 'you\'ve' : $Username . ' has') . ' uploaded'; - } - break; + case 'collages': + $BaseLink = 'comments.php?action=collages' . $LinkID; + if ($Type != 'default') { + $TypeLinks[] = 'Display comments left on collages ' . ($Self ? 'you\'ve' : $Username . ' has') . ' made'; + } + if ($Type != 'created') { + $TypeLinks[] = 'Display comments left on ' . ($Self ? 'your collages' : 'collages created by ' .$Username) . ''; + } + if ($Type != 'contributed') { + $TypeLinks[] = 'Display comments left on collages ' . ($Self ? 'you\'ve' : $Username . ' has') . ' contributed to'; + } + break; + case 'requests': + $BaseLink = 'comments.php?action=requests' . $LinkID; + if ($Type != 'default') { + $TypeLinks[] = 'Display comments left on requests ' . ($Self ? 'you\'ve' : $Username . 'has') . ' made'; + } + if ($Type != 'created') { + // + //your requests + $TypeLinks[] = 'Display comments left on requests ' . ($Self ? 'you' : $Username) . ' created'; + } + if ($Type != 'voted') { + $TypeLinks[] = 'Display comments left on requests ' . ($Self ? 'you\'ve' : $Username . ' has') . ' voted on'; + } + break; + case 'torrents': + if ($Type != 'default') { + $TypeLinks[] = 'Display comments left on torrents ' . ($Self ? 'you\'ve' : $Username . ' has' ) . ' made'; + } + if ($Type != 'uploaded') { + $TypeLinks[] = 'Display comments left on torrents ' . ($Self ? 'you\'ve' : $Username . ' has') . ' uploaded'; + } + break; } $Links = implode(' ', $ActionLinks) . (count($TypeLinks) ? '
    ' . implode(' ', $TypeLinks) : ''); View::show_header($Title, 'bbcode,comments'); ?>
    -
    -

    - - - -
    - - +

    + + + +
    + + 0) { - $DB->set_query_id($Comments); - while (list($AuthorID, $Page, $PageID, $Name, $PostID, $Body, $AddedTime, $EditedTime, $EditedUserID) = $DB->next_record()) { - $Link = Comments::get_url($Page, $PageID, $PostID); - switch ($Page) { - case 'artist': - $Header = " on $Name"; - break; - case 'collages': - $Header = " on $Name"; - break; - case 'requests': - $Header = ' on ' . Artists::display_artists($Artists[$PageID]) . " $Name"; - break; - case 'torrents': - $Header = ' on ' . Artists::display_artists($Artists[$PageID]) . " $Name"; - break; - } - CommentsView::render_comment($AuthorID, $PostID, $Body, $AddedTime, $EditedUserID, $EditedTime, $Link, false, $Header, false); - } + $DB->set_query_id($Comments); + while (list($AuthorID, $Page, $PageID, $Name, $PostID, $Body, $AddedTime, $EditedTime, $EditedUserID) = $DB->next_record()) { + $Link = Comments::get_url($Page, $PageID, $PostID); + switch ($Page) { + case 'artist': + $Header = " on $Name"; + break; + case 'collages': + $Header = " on $Name"; + break; + case 'requests': + $Header = ' on ' . Artists::display_artists($Artists[$PageID]) . " $Name"; + break; + case 'torrents': + $Header = ' on ' . Artists::display_artists($Artists[$PageID]) . " $Name"; + break; + } + CommentsView::render_comment($AuthorID, $PostID, $Body, $AddedTime, $EditedUserID, $EditedTime, $Link, false, $Header, false); + } } else { ?> -
    No results.
    - - +
    No results.
    + + -query(" - SELECT Body - FROM comments - WHERE ID = $PostID"); + SELECT Body + FROM comments + WHERE ID = $PostID"); list($Body) = $DB->next_record(MYSQLI_NUM); -echo trim($Body); \ No newline at end of file +echo trim($Body); diff --git a/sections/comments/index.php b/sections/comments/index.php index 60f911609..f97a3e1df 100644 --- a/sections/comments/index.php +++ b/sections/comments/index.php @@ -1,46 +1,46 @@ -query(" - SELECT AuthorID - FROM comments - WHERE ID = $PostID"); + SELECT AuthorID + FROM comments + WHERE ID = $PostID"); if (!$DB->has_results()) { - error(404); + error(404); } list($AuthorID) = $DB->next_record(); $UserInfo = Users::user_info($AuthorID); if ($UserInfo['Class'] > $LoggedUser['Class']) { - error(403); + error(403); } $URL = site_url() . Comments::get_url_query($PostID); if ($Length !== 'verbal') { - $Time = (int)$Length * (7 * 24 * 60 * 60); - Tools::warn_user($AuthorID, $Time, "$URL - $Reason"); - $Subject = 'You have received a warning'; - $PrivateMessage = "You have received a $Length week warning for [url=$URL]this comment[/url].\n\n[quote]{$PrivateMessage}[/quote]"; - $WarnTime = time_plus($Time); - $AdminComment = date('Y-m-d') . " - Warned until $WarnTime by " . $LoggedUser['Username'] . "\nReason: $URL - $Reason\n\n"; + $Time = (int)$Length * (7 * 24 * 60 * 60); + Tools::warn_user($AuthorID, $Time, "$URL - $Reason"); + $Subject = 'You have received a warning'; + $PrivateMessage = "You have received a $Length week warning for [url=$URL]this comment[/url].\n\n[quote]{$PrivateMessage}[/quote]"; + $WarnTime = time_plus($Time); + $AdminComment = date('Y-m-d') . " - Warned until $WarnTime by " . $LoggedUser['Username'] . "\nReason: $URL - $Reason\n\n"; } else { - $Subject = 'You have received a verbal warning'; - $PrivateMessage = "You have received a verbal warning for [url=$URL]this comment[/url].\n\n[quote]{$PrivateMessage}[/quote]"; - $AdminComment = date('Y-m-d') . ' - Verbally warned by ' . $LoggedUser['Username'] . " for $URL\nReason: $Reason\n\n"; - Tools::update_user_notes($AuthorID, $AdminComment); + $Subject = 'You have received a verbal warning'; + $PrivateMessage = "You have received a verbal warning for [url=$URL]this comment[/url].\n\n[quote]{$PrivateMessage}[/quote]"; + $AdminComment = date('Y-m-d') . ' - Verbally warned by ' . $LoggedUser['Username'] . " for $URL\nReason: $Reason\n\n"; + Tools::update_user_notes($AuthorID, $AdminComment); } $DB->query(" - INSERT INTO users_warnings_forums - (UserID, Comment) - VALUES - ('$AuthorID', '" . db_string($AdminComment) . "') - ON DUPLICATE KEY UPDATE - Comment = CONCAT('" . db_string($AdminComment) . "', Comment)"); + INSERT INTO users_warnings_forums + (UserID, Comment) + VALUES + ('$AuthorID', '" . db_string($AdminComment) . "') + ON DUPLICATE KEY UPDATE + Comment = CONCAT('" . db_string($AdminComment) . "', Comment)"); Misc::send_pm($AuthorID, $LoggedUser['ID'], $Subject, $PrivateMessage); Comments::edit($PostID, $Body); diff --git a/sections/comments/warn.php b/sections/comments/warn.php index b69d4d27a..309e0705f 100644 --- a/sections/comments/warn.php +++ b/sections/comments/warn.php @@ -1,16 +1,16 @@ query(" - SELECT Body, AuthorID - FROM comments - WHERE ID = $PostID"); + SELECT Body, AuthorID + FROM comments + WHERE ID = $PostID"); if (!$DB->has_results()) { - error(404); + error(404); } list($PostBody, $AuthorID) = $DB->next_record(); $UserInfo = Users::user_info($AuthorID); @@ -19,49 +19,49 @@ ?>
    -
    -

    Warning

    -
    -
    -
    - - - - - - - - - - - - - - - - - - - -
    Reason: - -
    Length: -
    Private message: - -
    Edit post: - -
    - -
    -
    -
    +
    +

    Warning

    +
    +
    +
    + + + + + + + + + + + + + + + + + + + +
    Reason: + +
    Length: +
    Private message: + +
    Edit post: + +
    + +
    +
    +
    -get_contest($ContestMgr->save($_POST)); + $Saved = 1; } - -if (!empty($_POST['name'])) { - authorize(); - Contest::save($_POST); - $Contest = Contest::get_contest($_POST['cid']); - $Saved = 1; +elseif (isset($_GET['id']) && intval($_GET['id'])) { + $Contest = $ContestMgr->get_contest(intval($_GET['id'])); +} +elseif (!$Create) { + $Contest = $ContestMgr->get_current_contest(); } View::show_header('contest admin'); ?>
    -
    -

    Contest admin

    - -
    +
    +

    Contest admin

    + +
    Contest information saved.

    "; + echo "

    Contest information saved.

    "; } +$ContestType = $ContestMgr->get_type(); -Contest::init_admin(); - -G::$DB->query(" - SELECT c.ID, c.Name, c.DateBegin, c.DateEnd, t.ID as ContestType - FROM contest c - INNER JOIN contest_type t ON (t.ID = c.ContestTypeID) - ORDER BY c.DateBegin - "); -if (G::$DB->has_results()) { +if (!$Create) { + $ContestList = $ContestMgr->get_list(); + if (count($ContestList)) { ?> -
    - - - - - - - -next_record()) { +
    +
    NameContest TypeDate BeginsDate Ends
    + + + + + + + - - - - - - - + + + + + + -
    NameContest TypeBeginsEnds
    -
    + +
    -
    -

    Request pairs

    - +

    Request pairs

    +get_request_pairs(); + if (!count($Pairs)) { ?> -

    No members have filled out more than one request for the same member.

    -No members have filled out more than one request for the same member.

    + -

    The following members have filled out more than one request for the same member.

    - - - - - - -The following members have filled out more than one request for the same member.

    +
    Request fillerRequest creatorFilled
    + + + + + + - - - - - - + + + + + -
    Request fillerRequest creatorFilled
    - + -
    - + +
    +get_contest($Contest['ID']); + $total = $ContestMgr->calculate_pool_payout($Contest['ID']); + $bonus = $total['bonus']; ?> - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Contest name: -

    Edit the name of the contest

    - -
    Contest type: -

    Edit the type of the contest

    - + + + + + + + + + +
    PayoutValue
    Enabled users
    Enabled user bonus
    Contest participation
    Per entry added
    Status of payout
    Payout is ready
    + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Contest name: +

    Edit the name of the contest

    + +
    Contest type: +

    Edit the type of the contest

    + -
    Begin date: -

    Uploaded torrents/completed requests are counted from this date (yyyy/mm/dd hh:mm:ss)

    - -
    End date: -

    Uploaded torrents/completed requests are counted up until this date (yyyy/mm/dd hh:mm:ss)

    - -
    Displayed: -

    This many people will be displayed on the ladderboard

    - -
    Max tracked: -

    Even if a person is not on the displayed ladderboard, we can still tell them - where they are (this corresponds to an SQL LIMIT value).

    - -
    Banner: -

    This is the image displayed at the top of the page (optional). - May be a local asset, or a URL.

    - -
    Introduction: -

    This is the introduction / guide of the contest.

    - -
    - -
    -
    - - - - - + +
    Bonus Point pool: +

    Members can contribute their Bonus Points to an award pool

    + /> +
    Begin date: +

    Uploaded torrents/completed requests are counted from this date (yyyy/mm/dd hh:mm:ss)

    + +
    End date: +

    Uploaded torrents/completed requests are counted up until this date (yyyy/mm/dd hh:mm:ss)

    + +
    Displayed: +

    This many people will be displayed on the ladderboard

    + +
    Max tracked: +

    Even if a person is not on the displayed ladderboard, we can still tell them + where they are (this corresponds to an SQL LIMIT value).

    + +
    Banner: +

    This is the image displayed at the top of the page (optional). + May be a local asset, or a URL.

    + +
    Introduction: +

    This is the introduction / guide of the contest.

    + +
    + +
    +
    + + + + + + + + + + get_current_contest(); if ($Contest !== false and strlen($Contest['Banner'])) { ?>
    - <?=$Contest['Name'] ?> + <?=$Contest['Name'] ?>
    -
    -
    -

    There is no contest at the moment.

    -
    -
    +
    +

    There is no contest at the moment.

    +
    + diff --git a/sections/contest/leaderboard.php b/sections/contest/leaderboard.php index 282329eb5..a26c3c21b 100644 --- a/sections/contest/leaderboard.php +++ b/sections/contest/leaderboard.php @@ -1,196 +1,213 @@ get_contest(intval($_POST['leaderboard'])); } elseif (isset($_GET['id']) && is_number($_GET['id'])) { - $Contest = Contest::get_contest(intval($_GET['id'])); + $Contest = $ContestMgr->get_contest(intval($_GET['id'])); } else { - $Contest = Contest::get_current_contest(); + $Contest = $ContestMgr->get_current_contest(); } if (!empty($Contest)) { - $Leaderboard = Contest::get_leaderboard($Contest['ID']); - View::show_header($Contest['Name'].' Leaderboard'); + $Leaderboard = $ContestMgr->get_leaderboard($Contest['ID']); + View::show_header($Contest['Name'].' Leaderboard'); } else { - View::show_header('Leaderboard'); + View::show_header('Leaderboard'); } if ($Contest['Banner']) { ?>
    - <?= $Contest['Name'] ?> + <?= $Contest['Name'] ?>
    -

    Leaderboard

    -get_prior_contests(); +if (check_perms('admin_manage_contest') && count($Prior)) { ?> -
    - +get_contest($id[0]); + $selected = $prior_contest['ID'] == $Contest['ID'] ? ' selected' : ''; ?> - -"> + - - -
    - + + + + +
    + 0) { + $bp = new \Gazelle\BonusPool($DB, $Cache, $Contest['BonusPool']); +?> +

    The Bonus Point pool currently stands at getTotalSent()) ?> points.

    + + + +

    The scheduler has not run yet, there are no results to display.

    + +

    That's not supposed to happen. Looks like the contest hasn't begun yet!

    + -

    That's not supposed to happen. Looks like the contest hasn't begun yet!

    - -

    A grand total of get_value("contest_leaderboard_total_{$Contest['ID']}") ?: "many, many, many" ?> .

    - +
    - - - - - - - - + + + + + + + $Contest['Display'] || $nr_rows > $Contest['Display']) { // cut off at limit, even if we haven't reached last winning place because of too many ties break; } - ++$nr_rows; + ++$nr_rows; - if ($row[0] == $LoggedUser['ID']) { - $user_extra = " (that's you!)"; - $user_seen = 1; - } - else { - $user_extra = ''; - } + if ($row[0] == $LoggedUser['ID']) { + $user_extra = " (that's you!)"; + $user_seen = 1; + } + else { + $user_extra = ''; + } - $artist_markup = ''; - $artist_id = explode(',', $row[4]); - $artist_name = explode(chr(1), $row[5]); - if (count($artist_id) > 2) { - $artist_markup = 'Various Artists - '; - } - elseif (count($artist_id) == 2) { - $artist_markup = sprintf( - '%s & %s - ', - $artist_id[0], $artist_name[0], - $artist_id[1], $artist_name[1] - ); - } - //For non-music torrents, $artist_id[0] does exist but has no value. - else { - if ((string)$artist_id[0] == '') { - $artist_markup = ''; - } - else { - $artist_markup = sprintf( - '%s - ', - $artist_id[0], $artist_name[0] - ); - } - } + $artist_markup = ''; + $artist_id = explode(',', $row[4]); + $artist_name = explode(chr(1), $row[5]); + if (count($artist_id) > 2) { + $artist_markup = 'Various Artists - '; + } + elseif (count($artist_id) == 2) { + $artist_markup = sprintf( + '%s & %s - ', + $artist_id[0], $artist_name[0], + $artist_id[1], $artist_name[1] + ); + } + //For non-music torrents, $artist_id[0] does exist but has no value. + else { + if ((string)$artist_id[0] == '') { + $artist_markup = ''; + } + else { + $artist_markup = sprintf( + '%s - ', + $artist_id[0], $artist_name[0] + ); + } + } - $userinfo = Users::user_info($row[0]); - printf(<< - - - - - - + $userinfo = Users::user_info($row[0]); + printf(<< + + + + + + END_STR - , $rank, - $row[0], $userinfo['Username'], - $artist_markup, - $row[2], $row[3], // torrent - time_diff($row[6], 1), - $score - ); - } + , $rank, + $row[0], $userinfo['Username'], + $artist_markup, + $row[2], $row[3], // torrent + time_diff($row[6], 1), + $score + ); + } ?>
    RankWhoMost recent Most recent time
    RankWhoMost recent Most recent time
    %d%s$user_extra%s%s%s%d
    %d%s$user_extra%s%s%s%d
    - -

    With your upload, you are currently ranked number on the leaderboard. Keep going and see if you can make it!

    -With your upload, you are currently ranked number on the leaderboard. Keep going and see if you can make it!

    + -

    It doesn't look like you're on the leaderboard at all... for fame and glory!

    +

    It doesn't look like you're on the leaderboard at all... for fame and glory!

    - -
    - + - Why donate? -
    - {$SiteName} has no advertisements, is not sponsored, and provides its services free of charge. For these reasons, {$SiteName}'s financial obligations can only be met with the help of voluntary user donations. Supporting {$SiteName} is and will always remain voluntary. If you are financially able, help pay {$SiteName}'s bills by donating. {$SiteName}'s survival is up to you.
    -
    - {$SiteName} uses all voluntary donations to cover the costs of running the site, tracker, and IRC network. These costs represent the hardware the site runs on (e.g. servers, upgrades, fixes, etc.), and recurring operating expenses (e.g. hosting, bandwidth, power, etc.).
    -
    - Please note that {$SiteName} is a nonprofit organization. No staff member or other individual responsible for the site's operation personally profits from user donations. As a donor, your financial support is exclusively applied to operating costs. When you donate you aren't paying the {$SiteName} Staff, purchasing upload credit, or buying the ability to download. When you donate you are paying {$SiteName}'s bills.
    -
    - {$SiteName}'s Donor Rank system is currently available to all credited donors. This system provides donors with perks. Some of these perks are cosmetic (e.g. a donor icon added to your account), some are one-time benefits (e.g. additional invites), and others modify specific site options (e.g. additional profile information boxes, or personal collages). -
    + Why donate? +
    + {$SiteName} has no advertisements, is not sponsored, and provides its services free of charge. For these reasons, {$SiteName}'s financial obligations can only be met with the help of voluntary user donations. Supporting {$SiteName} is and will always remain voluntary. If you are financially able, help pay {$SiteName}'s bills by donating. {$SiteName}'s survival is up to you.
    +
    + {$SiteName} uses all voluntary donations to cover the costs of running the site, tracker, and IRC network. These costs represent the hardware the site runs on (e.g. servers, upgrades, fixes, etc.), and recurring operating expenses (e.g. hosting, bandwidth, power, etc.).
    +
    + Please note that {$SiteName} is a nonprofit organization. No staff member or other individual responsible for the site's operation personally profits from user donations. As a donor, your financial support is exclusively applied to operating costs. When you donate you aren't paying the {$SiteName} Staff, purchasing upload credit, or buying the ability to download. When you donate you are paying {$SiteName}'s bills.
    +
    + {$SiteName}'s Donor Rank system is currently available to all credited donors. This system provides donors with perks. Some of these perks are cosmetic (e.g. a donor icon added to your account), some are one-time benefits (e.g. additional invites), and others modify specific site options (e.g. additional profile information boxes, or personal collages). +
    - Donating -
    - If you wish to donate, please contact Athena, and they will handle your donation manually. -

    - The minimum amount required for donating is 10 USD, and must be paid in Bitcoin. -
    + Donating +
    + If you wish to donate, please contact Spine, and they will handle your donation manually. +

    + The minimum amount required for donating is 10 USD, and must be paid in Bitcoin. +
    - What you will receive for donating -
    - Any donation or contribution option listed above gives you the opportunity to receive Donor Points. After acquiring your first Donor Point, your account will unlock Donor Rank #1. This rank will last forever, and you'll receive the following perks upon unlocking it:
    -
    -
      -
    • Our eternal love, as represented by the red heart you get next to your name
    • -
    • Inactivity timer immunity
    • -
    • Access to the notifications system
    • -
    • Two invites
    • -
    • Collage creation privileges
    • -
    • Personal collage creation privileges
    • -
    • One additional personal collage
    • -
    • A warm, fuzzy feeling
    • -
    -
    - There are a number of additional perks waiting for you when you unlock additional Donor Ranks. -
    -
    - - Be reminded that when you make a donation, you aren't "purchasing" Donor Ranks, invites, or any {$SiteName}-specific benefit. When donating, you are helping {$SiteName} pay its bills, and your donation should be made in this spirit. The {$SiteName} Staff does its best to recognize {$SiteName}'s financial supporters in a fair and fun way, but all Donor Perks are subject to change or cancellation at any time, without notice. -
    + What you will receive for donating +
    + Any donation or contribution option listed above gives you the opportunity to receive Donor Points. After acquiring your first Donor Point, your account will unlock Donor Rank #1. This rank will last forever, and you'll receive the following perks upon unlocking it:
    +
    +
      +
    • Our eternal love, as represented by the red heart you get next to your name
    • +
    • Inactivity timer immunity
    • +
    • Access to the notifications system
    • +
    • Two invites
    • +
    • Collage creation privileges
    • +
    • Personal collage creation privileges
    • +
    • One additional personal collage
    • +
    • A warm, fuzzy feeling
    • +
    +
    + There are a number of additional perks waiting for you when you unlock additional Donor Ranks. +
    +
    + + Be reminded that when you make a donation, you aren't "purchasing" Donor Ranks, invites, or any {$SiteName}-specific benefit. When donating, you are helping {$SiteName} pay its bills, and your donation should be made in this spirit. The {$SiteName} Staff does its best to recognize {$SiteName}'s financial supporters in a fair and fun way, but all Donor Perks are subject to change or cancellation at any time, without notice. +
    - What you won't receive for donating -
    -
    • Immunity from the rules
    • Additional upload credit
    -
    + What you won't receive for donating +
    +
    • Immunity from the rules
    • Additional upload credit
    +
    HTML; diff --git a/sections/enable/index.php b/sections/enable/index.php index df5030712..08e1f7c84 100644 --- a/sections/enable/index.php +++ b/sections/enable/index.php @@ -1,4 +1,4 @@ -Error: 403 Forbidden. -Error: 404 Not Found. -Error: 413 Request is too large. -Error: 504 Gateway timeout. -Search Log'; - } + if (isset($Log) && $Log) { + $Description .= ' Search Log'; + } - if (empty($NoHTML) && $Error != -1) { - View::show_header($Title); + if (empty($NoHTML) && $Error != -1) { + View::show_header($Title); ?> -
    -
    -

    -
    -
    -

    -
    -
    - +
    +

    +
    +
    +

    +
    +
    +open_feed(); - $Feed->channel('Blocked', 'RSS feed.'); - $Feed->close_feed(); - die(); + $Feed->open_feed(); + $Feed->channel('Blocked', 'RSS feed.'); + $Feed->close_feed(); + die(); } $User = (int)$_GET['user']; if (!$Enabled = $Cache->get_value("enabled_$User")) { - require(SERVER_ROOT.'/classes/mysql.class.php'); - $DB = NEW DB_MYSQL; //Load the database wrapper - $DB->query(" - SELECT Enabled - FROM users_main - WHERE ID = '$User'"); - list($Enabled) = $DB->next_record(); - $Cache->cache_value("enabled_$User", $Enabled, 0); + require(SERVER_ROOT.'/classes/mysql.class.php'); + $DB = NEW DB_MYSQL; //Load the database wrapper + $DB->query(" + SELECT Enabled + FROM users_main + WHERE ID = '$User'"); + list($Enabled) = $DB->next_record(); + $Cache->cache_value("enabled_$User", $Enabled, 0); } if (md5($User.RSS_HASH.$_GET['passkey']) !== $_GET['auth'] || $Enabled != 1) { - $Feed->open_feed(); - $Feed->channel('Blocked', 'RSS feed.'); - $Feed->close_feed(); - die(); + $Feed->open_feed(); + $Feed->channel('Blocked', 'RSS feed.'); + $Feed->close_feed(); + die(); } require(SERVER_ROOT.'/classes/text.class.php'); $Feed->open_feed(); switch ($_GET['feed']) { - case 'feed_news': - $Feed->channel('News', 'RSS feed for site news.'); - if (!$News = $Cache->get_value('news')) { - require(SERVER_ROOT.'/classes/mysql.class.php'); //Require the database wrapper - $DB = NEW DB_MYSQL; //Load the database wrapper - $DB->query(" - SELECT - ID, - Title, - Body, - Time - FROM news - ORDER BY Time DESC - LIMIT 10"); - $News = $DB->to_array(false, MYSQLI_NUM, false); - $Cache->cache_value('news', $News, 1209600); - } - $Count = 0; - foreach ($News as $NewsItem) { - list($NewsID, $Title, $Body, $NewsTime) = $NewsItem; - if (strtotime($NewsTime) >= time()) { - continue; - } - echo $Feed->item($Title, Text::strip_bbcode($Body), "index.php#news$NewsID", SITE_NAME.' Staff', '', '', $NewsTime); - if (++$Count > 4) { - break; - } - } - break; - case 'feed_blog': - $Feed->channel('Blog', 'RSS feed for site blog.'); - if (!$Blog = $Cache->get_value('blog')) { - require(SERVER_ROOT.'/classes/mysql.class.php'); //Require the database wrapper - $DB = NEW DB_MYSQL; //Load the database wrapper - $DB->query(" - SELECT - b.ID, - um.Username, - b.UserID, - b.Title, - b.Body, - b.Time, - b.ThreadID - FROM blog AS b - LEFT JOIN users_main AS um ON b.UserID = um.ID - ORDER BY Time DESC - LIMIT 20"); - $Blog = $DB->to_array(); - $Cache->cache_value('blog', $Blog, 1209600); - } - foreach ($Blog as $BlogItem) { - list($BlogID, $Author, $AuthorID, $Title, $Body, $BlogTime, $ThreadID) = $BlogItem; - if ($ThreadID) { - echo $Feed->item($Title, Text::strip_bbcode($Body), "forums.php?action=viewthread&threadid=$ThreadID", SITE_NAME.' Staff', '', '', $BlogTime); - } else { - echo $Feed->item($Title, Text::strip_bbcode($Body), "blog.php#blog$BlogID", SITE_NAME.' Staff', '', '', $BlogTime); - } - } - break; - case 'feed_changelog': - $Feed->channel('Gazelle Change Log', 'RSS feed for Gazelle\'s changelog.'); - if (!$Changelog = $Cache->get_value('changelog')) { - require(SERVER_ROOT.'/classes/mysql.class.php'); - require(SERVER_ROOT.'/classes/misc.class.php'); + case 'feed_news': + $Feed->channel('News', 'RSS feed for site news.'); + if (!$News = $Cache->get_value('news')) { + require(SERVER_ROOT.'/classes/mysql.class.php'); //Require the database wrapper + $DB = NEW DB_MYSQL; //Load the database wrapper + $DB->query(" + SELECT + ID, + Title, + Body, + Time + FROM news + ORDER BY Time DESC + LIMIT 10"); + $News = $DB->to_array(false, MYSQLI_NUM, false); + $Cache->cache_value('news', $News, 1209600); + } + $Count = 0; + foreach ($News as $NewsItem) { + list($NewsID, $Title, $Body, $NewsTime) = $NewsItem; + if (strtotime($NewsTime) >= time()) { + continue; + } + echo $Feed->item($Title, Text::strip_bbcode($Body), "index.php#news$NewsID", SITE_NAME.' Staff', '', '', $NewsTime); + if (++$Count > 4) { + break; + } + } + break; + case 'feed_blog': + $Feed->channel('Blog', 'RSS feed for site blog.'); + if (!$Blog = $Cache->get_value('blog')) { + require(SERVER_ROOT.'/classes/mysql.class.php'); //Require the database wrapper + $DB = NEW DB_MYSQL; //Load the database wrapper + $DB->query(" + SELECT + b.ID, + um.Username, + b.UserID, + b.Title, + b.Body, + b.Time, + b.ThreadID + FROM blog AS b + LEFT JOIN users_main AS um ON b.UserID = um.ID + ORDER BY Time DESC + LIMIT 20"); + $Blog = $DB->to_array(); + $Cache->cache_value('blog', $Blog, 1209600); + } + foreach ($Blog as $BlogItem) { + list($BlogID, $Author, $AuthorID, $Title, $Body, $BlogTime, $ThreadID) = $BlogItem; + if ($ThreadID) { + echo $Feed->item($Title, Text::strip_bbcode($Body), "forums.php?action=viewthread&threadid=$ThreadID", SITE_NAME.' Staff', '', '', $BlogTime); + } else { + echo $Feed->item($Title, Text::strip_bbcode($Body), "blog.php#blog$BlogID", SITE_NAME.' Staff', '', '', $BlogTime); + } + } + break; + case 'feed_changelog': + $Feed->channel('Gazelle Change Log', 'RSS feed for Gazelle\'s changelog.'); + if (!$Changelog = $Cache->get_value('changelog')) { + require(SERVER_ROOT.'/classes/mysql.class.php'); + require(SERVER_ROOT.'/classes/misc.class.php'); - $DB = NEW DB_MYSQL; - $DB->query(" - SELECT Message, Author, Date(Time) - FROM changelog - ORDER BY Time DESC - LIMIT 20"); - $Changelog = $DB->to_array(); - $Cache->cache_value('changelog', $Changelog, 86400); - } - foreach ($Changelog as $Change) { - list($Message, $Author, $Date) = $Change; - echo $Feed->item("$Date by $Author", $Message, 'tools.php?action=change_log', SITE_NAME.' Staff', '', '', $Date); - } - break; - case 'torrents_all': - $Feed->channel('All Torrents', 'RSS feed for all new torrent uploads.'); - $Feed->retrieve('torrents_all', $_GET['authkey'], $_GET['passkey']); - break; - case 'torrents_music': - $Feed->channel('Music Torrents', 'RSS feed for all new music torrents.'); - $Feed->retrieve('torrents_music', $_GET['authkey'], $_GET['passkey']); - break; - case 'torrents_apps': - $Feed->channel('Application Torrents', 'RSS feed for all new application torrents.'); - $Feed->retrieve('torrents_apps', $_GET['authkey'], $_GET['passkey']); - break; - case 'torrents_ebooks': - $Feed->channel('E-Book Torrents', 'RSS feed for all new e-book torrents.'); - $Feed->retrieve('torrents_ebooks', $_GET['authkey'], $_GET['passkey']); - break; - case 'torrents_abooks': - $Feed->channel('Audiobook Torrents', 'RSS feed for all new audiobook torrents.'); - $Feed->retrieve('torrents_abooks', $_GET['authkey'], $_GET['passkey']); - break; - case 'torrents_evids': - $Feed->channel('E-Learning Video Torrents', 'RSS feed for all new e-learning video torrents.'); - $Feed->retrieve('torrents_evids', $_GET['authkey'], $_GET['passkey']); - break; - case 'torrents_comedy': - $Feed->channel('Comedy Torrents', 'RSS feed for all new comedy torrents.'); - $Feed->retrieve('torrents_comedy', $_GET['authkey'], $_GET['passkey']); - break; - case 'torrents_comics': - $Feed->channel('Comic Torrents', 'RSS feed for all new comic torrents.'); - $Feed->retrieve('torrents_comics', $_GET['authkey'], $_GET['passkey']); - break; - case 'torrents_mp3': - $Feed->channel('MP3 Torrents', 'RSS feed for all new mp3 torrents.'); - $Feed->retrieve('torrents_mp3', $_GET['authkey'], $_GET['passkey']); - break; - case 'torrents_flac': - $Feed->channel('FLAC Torrents', 'RSS feed for all new FLAC torrents.'); - $Feed->retrieve('torrents_flac', $_GET['authkey'], $_GET['passkey']); - break; - case 'torrents_vinyl': - $Feed->channel('Vinyl Sourced Torrents', 'RSS feed for all new vinyl sourced torrents.'); - $Feed->retrieve('torrents_vinyl', $_GET['authkey'], $_GET['passkey']); - break; - case 'torrents_lossless': - $Feed->channel('Lossless Torrents', 'RSS feed for all new lossless uploads.'); - $Feed->retrieve('torrents_lossless', $_GET['authkey'], $_GET['passkey']); - break; - case 'torrents_lossless24': - $Feed->channel('24bit Lossless Torrents', 'RSS feed for all new 24bit uploads.'); - $Feed->retrieve('torrents_lossless24', $_GET['authkey'], $_GET['passkey']); - break; - default: - // Personalized torrents - if (empty($_GET['name']) && substr($_GET['feed'], 0, 16) == 'torrents_notify_') { - // All personalized torrent notifications - $Feed->channel('Personalized torrent notifications', 'RSS feed for personalized torrent notifications.'); - $Feed->retrieve($_GET['feed'], $_GET['authkey'], $_GET['passkey']); - } elseif (!empty($_GET['name']) && substr($_GET['feed'], 0, 16) == 'torrents_notify_') { - // Specific personalized torrent notification channel - $Feed->channel(display_str($_GET['name']), 'Personal RSS feed: '.display_str($_GET['name'])); - $Feed->retrieve($_GET['feed'], $_GET['authkey'], $_GET['passkey']); - } elseif (!empty($_GET['name']) && substr($_GET['feed'], 0, 21) == 'torrents_bookmarks_t_') { - // Bookmarks - $Feed->channel('Bookmarked torrent notifications', 'RSS feed for bookmarked torrents.'); - $Feed->retrieve($_GET['feed'], $_GET['authkey'], $_GET['passkey']); - } else { - $Feed->channel('All Torrents', 'RSS feed for all new torrent uploads.'); - $Feed->retrieve('torrents_all', $_GET['authkey'], $_GET['passkey']); - } + $DB = NEW DB_MYSQL; + $DB->query(" + SELECT Message, Author, Date(Time) + FROM changelog + ORDER BY Time DESC + LIMIT 20"); + $Changelog = $DB->to_array(); + $Cache->cache_value('changelog', $Changelog, 86400); + } + foreach ($Changelog as $Change) { + list($Message, $Author, $Date) = $Change; + echo $Feed->item("$Date by $Author", $Message, 'tools.php?action=change_log', SITE_NAME.' Staff', '', '', $Date); + } + break; + case 'torrents_all': + $Feed->channel('All Torrents', 'RSS feed for all new torrent uploads.'); + $Feed->retrieve('torrents_all', $_GET['authkey'], $_GET['passkey']); + break; + case 'torrents_music': + $Feed->channel('Music Torrents', 'RSS feed for all new music torrents.'); + $Feed->retrieve('torrents_music', $_GET['authkey'], $_GET['passkey']); + break; + case 'torrents_apps': + $Feed->channel('Application Torrents', 'RSS feed for all new application torrents.'); + $Feed->retrieve('torrents_apps', $_GET['authkey'], $_GET['passkey']); + break; + case 'torrents_ebooks': + $Feed->channel('E-Book Torrents', 'RSS feed for all new e-book torrents.'); + $Feed->retrieve('torrents_ebooks', $_GET['authkey'], $_GET['passkey']); + break; + case 'torrents_abooks': + $Feed->channel('Audiobook Torrents', 'RSS feed for all new audiobook torrents.'); + $Feed->retrieve('torrents_abooks', $_GET['authkey'], $_GET['passkey']); + break; + case 'torrents_evids': + $Feed->channel('E-Learning Video Torrents', 'RSS feed for all new e-learning video torrents.'); + $Feed->retrieve('torrents_evids', $_GET['authkey'], $_GET['passkey']); + break; + case 'torrents_comedy': + $Feed->channel('Comedy Torrents', 'RSS feed for all new comedy torrents.'); + $Feed->retrieve('torrents_comedy', $_GET['authkey'], $_GET['passkey']); + break; + case 'torrents_comics': + $Feed->channel('Comic Torrents', 'RSS feed for all new comic torrents.'); + $Feed->retrieve('torrents_comics', $_GET['authkey'], $_GET['passkey']); + break; + case 'torrents_mp3': + $Feed->channel('MP3 Torrents', 'RSS feed for all new mp3 torrents.'); + $Feed->retrieve('torrents_mp3', $_GET['authkey'], $_GET['passkey']); + break; + case 'torrents_flac': + $Feed->channel('FLAC Torrents', 'RSS feed for all new FLAC torrents.'); + $Feed->retrieve('torrents_flac', $_GET['authkey'], $_GET['passkey']); + break; + case 'torrents_vinyl': + $Feed->channel('Vinyl Sourced Torrents', 'RSS feed for all new vinyl sourced torrents.'); + $Feed->retrieve('torrents_vinyl', $_GET['authkey'], $_GET['passkey']); + break; + case 'torrents_lossless': + $Feed->channel('Lossless Torrents', 'RSS feed for all new lossless uploads.'); + $Feed->retrieve('torrents_lossless', $_GET['authkey'], $_GET['passkey']); + break; + case 'torrents_lossless24': + $Feed->channel('24bit Lossless Torrents', 'RSS feed for all new 24bit uploads.'); + $Feed->retrieve('torrents_lossless24', $_GET['authkey'], $_GET['passkey']); + break; + default: + // Personalized torrents + if (empty($_GET['name']) && substr($_GET['feed'], 0, 16) == 'torrents_notify_') { + // All personalized torrent notifications + $Feed->channel('Personalized torrent notifications', 'RSS feed for personalized torrent notifications.'); + $Feed->retrieve($_GET['feed'], $_GET['authkey'], $_GET['passkey']); + } elseif (!empty($_GET['name']) && substr($_GET['feed'], 0, 16) == 'torrents_notify_') { + // Specific personalized torrent notification channel + $Feed->channel(display_str($_GET['name']), 'Personal RSS feed: '.display_str($_GET['name'])); + $Feed->retrieve($_GET['feed'], $_GET['authkey'], $_GET['passkey']); + } elseif (!empty($_GET['name']) && substr($_GET['feed'], 0, 21) == 'torrents_bookmarks_t_') { + // Bookmarks + $Feed->channel('Bookmarked torrent notifications', 'RSS feed for bookmarked torrents.'); + $Feed->retrieve($_GET['feed'], $_GET['authkey'], $_GET['passkey']); + } else { + $Feed->channel('All Torrents', 'RSS feed for all new torrent uploads.'); + $Feed->retrieve('torrents_all', $_GET['authkey'], $_GET['passkey']); + } } $Feed->close_feed(); ?> diff --git a/sections/forums/add_poll_option.php b/sections/forums/add_poll_option.php index ea442c71c..a05401543 100644 --- a/sections/forums/add_poll_option.php +++ b/sections/forums/add_poll_option.php @@ -1,28 +1,28 @@ -query(" - SELECT ForumID - FROM forums_topics - WHERE ID = $ThreadID"); - list($ForumID) = $DB->next_record(); - if (!in_array($ForumID, $ForumsRevealVoters)) { - error(403); - } + $DB->query(" + SELECT ForumID + FROM forums_topics + WHERE ID = $ThreadID"); + list($ForumID) = $DB->next_record(); + if (!in_array($ForumID, $ForumsRevealVoters)) { + error(403); + } } $DB->query(" - SELECT Answers - FROM forums_polls - WHERE TopicID = $ThreadID"); + SELECT Answers + FROM forums_polls + WHERE TopicID = $ThreadID"); if (!$DB->has_results()) { - error(404); + error(404); } list($Answers) = $DB->next_record(MYSQLI_NUM, false); @@ -31,9 +31,9 @@ $Answers = serialize($Answers); $DB->query(" - UPDATE forums_polls - SET Answers = '".db_string($Answers)."' - WHERE TopicID = $ThreadID"); + UPDATE forums_polls + SET Answers = '".db_string($Answers)."' + WHERE TopicID = $ThreadID"); $Cache->delete_value("polls_$ThreadID"); header("Location: forums.php?action=viewthread&threadid=$ThreadID"); diff --git a/sections/forums/ajax_get_edit.php b/sections/forums/ajax_get_edit.php index cddc65208..e60a5bdb7 100644 --- a/sections/forums/ajax_get_edit.php +++ b/sections/forums/ajax_get_edit.php @@ -1,78 +1,82 @@ -get_value($Type.'_edits_'.$PostID); if (!is_array($Edits)) { - $DB->query(" - SELECT EditUser, EditTime, Body - FROM comments_edits - WHERE Page = '$Type' AND PostID = $PostID - ORDER BY EditTime DESC"); - $Edits = $DB->to_array(); - $Cache->cache_value($Type.'_edits_'.$PostID, $Edits, 0); + $DB->query(" + SELECT EditUser, EditTime, Body + FROM comments_edits + WHERE Page = '$Type' AND PostID = $PostID + ORDER BY EditTime DESC"); + $Edits = $DB->to_array(); + $Cache->cache_value($Type.'_edits_'.$PostID, $Edits, 0); } list($UserID, $Time) = $Edits[$Depth]; if ($Depth != 0) { - list(,,$Body) = $Edits[$Depth - 1]; + list(,,$Body) = $Edits[$Depth - 1]; } else { - //Not an edit, have to get from the original - switch ($Type) { - case 'forums': - //Get from normal forum stuffs - $DB->query(" - SELECT Body - FROM forums_posts - WHERE ID = $PostID"); - list($Body) = $DB->next_record(); - break; - case 'collages': - case 'requests': - case 'artist': - case 'torrents': - $DB->query(" - SELECT Body - FROM comments - WHERE Page = '$Type' AND ID = $PostID"); - list($Body) = $DB->next_record(); - break; - } + //Not an edit, have to get from the original + switch ($Type) { + case 'forums': + //Get from normal forum stuffs + $DB->query(" + SELECT Body + FROM forums_posts + WHERE ID = $PostID"); + list($Body) = $DB->next_record(); + break; + case 'collages': + case 'requests': + case 'artist': + case 'torrents': + $DB->query(" + SELECT Body + FROM comments + WHERE Page = '$Type' AND ID = $PostID"); + list($Body) = $DB->next_record(); + break; + } } ?> - -
    -
    + +
    +
    - + « - - + + - - Original Post - + Original Post + 0) { ?> - » - + » + diff --git a/sections/forums/catchup.php b/sections/forums/catchup.php index 7f59af899..5d860dea9 100644 --- a/sections/forums/catchup.php +++ b/sections/forums/catchup.php @@ -1,28 +1,28 @@ -query(" - UPDATE users_info - SET CatchupTime = NOW() - WHERE UserID = $LoggedUser[ID]"); - $Cache->delete_value('user_info_'.$LoggedUser['ID']); - header('Location: forums.php'); + $DB->query(" + UPDATE users_info + SET CatchupTime = NOW() + WHERE UserID = $LoggedUser[ID]"); + $Cache->delete_value('user_info_'.$LoggedUser['ID']); + header('Location: forums.php'); } else { - // Insert a value for each topic - $DB->query(" - INSERT INTO forums_last_read_topics (UserID, TopicID, PostID) - SELECT '$LoggedUser[ID]', ID, LastPostID - FROM forums_topics - WHERE (LastPostTime > '".time_minus(3600 * 24 * 30)."' OR IsSticky = '1') - AND ForumID = ".$_GET['forumid']." - ON DUPLICATE KEY UPDATE - PostID = LastPostID"); + // Insert a value for each topic + $DB->query(" + INSERT INTO forums_last_read_topics (UserID, TopicID, PostID) + SELECT '$LoggedUser[ID]', ID, LastPostID + FROM forums_topics + WHERE (LastPostTime > '".time_minus(3600 * 24 * 30)."' OR IsSticky = '1') + AND ForumID = ".$_GET['forumid']." + ON DUPLICATE KEY UPDATE + PostID = LastPostID"); - header('Location: forums.php?action=viewforum&forumid='.$_GET['forumid']); + header('Location: forums.php?action=viewforum&forumid='.$_GET['forumid']); } ?> diff --git a/sections/forums/change_vote.php b/sections/forums/change_vote.php index 7dff1e442..0eeea20eb 100644 --- a/sections/forums/change_vote.php +++ b/sections/forums/change_vote.php @@ -1,28 +1,28 @@ -query(" - SELECT ForumID - FROM forums_topics - WHERE ID = $ThreadID"); - list($ForumID) = $DB->next_record(); - if (!in_array($ForumID, $ForumsRevealVoters)) { - error(403); - } - } + if (!check_perms('site_moderate_forums')) { + $DB->query(" + SELECT ForumID + FROM forums_topics + WHERE ID = $ThreadID"); + list($ForumID) = $DB->next_record(); + if (!in_array($ForumID, $ForumsRevealVoters)) { + error(403); + } + } - $DB->query(" - UPDATE forums_polls_votes - SET Vote = $NewVote - WHERE TopicID = $ThreadID - AND UserID = ".$LoggedUser['ID']); - $Cache->delete_value('polls_'.$ThreadID); - header("Location: forums.php?action=viewthread&threadid=".$ThreadID); + $DB->query(" + UPDATE forums_polls_votes + SET Vote = $NewVote + WHERE TopicID = $ThreadID + AND UserID = ".$LoggedUser['ID']); + $Cache->delete_value('polls_'.$ThreadID); + header("Location: forums.php?action=viewthread&threadid=".$ThreadID); } else { - error(404); + error(404); } diff --git a/sections/forums/delete.php b/sections/forums/delete.php index 6ddfbbda7..2b2868a27 100644 --- a/sections/forums/delete.php +++ b/sections/forums/delete.php @@ -1,36 +1,36 @@ -query(" - SELECT - TopicID, - ForumID, - CEIL(COUNT(p.ID) / ".POSTS_PER_PAGE.") AS Pages, - CEIL(SUM(IF(p.ID <= '$PostID', 1, 0)) / ".POSTS_PER_PAGE.") AS Page, - StickyPostID - FROM forums_posts AS p - JOIN forums_topics AS t ON t.ID = p.TopicID - WHERE p.TopicID = ( - SELECT TopicID - FROM forums_posts - WHERE ID = '$PostID' - ) - GROUP BY t.ID"); + SELECT + TopicID, + ForumID, + CEIL(COUNT(p.ID) / ".POSTS_PER_PAGE.") AS Pages, + CEIL(SUM(IF(p.ID <= '$PostID', 1, 0)) / ".POSTS_PER_PAGE.") AS Page, + StickyPostID + FROM forums_posts AS p + JOIN forums_topics AS t ON t.ID = p.TopicID + WHERE p.TopicID = ( + SELECT TopicID + FROM forums_posts + WHERE ID = '$PostID' + ) + GROUP BY t.ID"); list($TopicID, $ForumID, $Pages, $Page, $StickyPostID) = $DB->next_record(); if (!$TopicID) { - // Post is deleted or thread doesn't exist - error(0); // This is evil, but the ajax call doesn't check the response + // Post is deleted or thread doesn't exist + error(0); // This is evil, but the ajax call doesn't check the response } // $Pages = number of pages in the thread @@ -38,101 +38,101 @@ // These are set for cache clearing. $DB->query(" - DELETE FROM forums_posts - WHERE ID = '$PostID'"); + DELETE FROM forums_posts + WHERE ID = '$PostID'"); $DB->query(" - SELECT MAX(ID) - FROM forums_posts - WHERE TopicID = '$TopicID'"); + SELECT MAX(ID) + FROM forums_posts + WHERE TopicID = '$TopicID'"); list($LastID) = $DB->next_record(); $DB->query(" - UPDATE forums AS f, forums_topics AS t - SET f.NumPosts = f.NumPosts - 1, - t.NumPosts = t.NumPosts - 1 - WHERE f.ID = '$ForumID' - AND t.ID = '$TopicID'"); + UPDATE forums AS f, forums_topics AS t + SET f.NumPosts = f.NumPosts - 1, + t.NumPosts = t.NumPosts - 1 + WHERE f.ID = '$ForumID' + AND t.ID = '$TopicID'"); if ($LastID < $PostID) { // Last post in a topic was removed - $DB->query(" - SELECT p.AuthorID, u.Username, p.AddedTime - FROM forums_posts AS p - LEFT JOIN users_main AS u ON u.ID = p.AuthorID - WHERE p.ID = '$LastID'"); - list($LastAuthorID, $LastAuthorName, $LastTime) = $DB->next_record(); - $DB->query(" - UPDATE forums_topics - SET - LastPostID = '$LastID', - LastPostAuthorID = '$LastAuthorID', - LastPostTime = '$LastTime' - WHERE ID = '$TopicID'"); - $DB->query(" - SELECT - t.ID, - t.Title, - t.LastPostID, - t.LastPostTime, - t.LastPostAuthorID, - u.Username - FROM forums_topics AS t - LEFT JOIN users_main AS u ON u.ID = t.LastPostAuthorID - WHERE ForumID = '$ForumID' - AND t.ID != '$TopicID' - ORDER BY LastPostID DESC - LIMIT 1"); - list($LastTopicID, $LastTopicTitle, $LastTopicPostID, $LastTopicPostTime, $LastTopicAuthorID, $LastTopicAuthorName) = $DB->next_record(MYSQLI_BOTH, false); + $DB->query(" + SELECT p.AuthorID, u.Username, p.AddedTime + FROM forums_posts AS p + LEFT JOIN users_main AS u ON u.ID = p.AuthorID + WHERE p.ID = '$LastID'"); + list($LastAuthorID, $LastAuthorName, $LastTime) = $DB->next_record(); + $DB->query(" + UPDATE forums_topics + SET + LastPostID = '$LastID', + LastPostAuthorID = '$LastAuthorID', + LastPostTime = '$LastTime' + WHERE ID = '$TopicID'"); + $DB->query(" + SELECT + t.ID, + t.Title, + t.LastPostID, + t.LastPostTime, + t.LastPostAuthorID, + u.Username + FROM forums_topics AS t + LEFT JOIN users_main AS u ON u.ID = t.LastPostAuthorID + WHERE ForumID = '$ForumID' + AND t.ID != '$TopicID' + ORDER BY LastPostID DESC + LIMIT 1"); + list($LastTopicID, $LastTopicTitle, $LastTopicPostID, $LastTopicPostTime, $LastTopicAuthorID, $LastTopicAuthorName) = $DB->next_record(MYSQLI_BOTH, false); - if ($LastID < $LastTopicPostID) { // Topic is no longer the most recent in its forum - $DB->query(" - UPDATE forums - SET - LastPostTopicID = '$LastTopicID', - LastPostID = '$LastTopicPostID', - LastPostAuthorID = '$LastTopicAuthorID', - LastPostTime = '$LastTopicPostTime' - WHERE ID = '$ForumID' - AND LastPostTopicID = '$TopicID'"); - $UpdateArrayForums = array( - 'NumPosts' => '-1', - 'LastPostID' => $LastTopicPostID, - 'LastPostAuthorID' => $LastTopicAuthorID, - 'LastPostTime' => $LastTopicPostTime, - 'LastPostTopicID' => $LastTopicID, - 'Title' => $LastTopicTitle); - } else { // Topic is still the most recent in its forum - $DB->query(" - UPDATE forums - SET - LastPostID = '$LastID', - LastPostAuthorID = '$LastAuthorID', - LastPostTime = '$LastTime' - WHERE ID = '$ForumID' - AND LastPostTopicID = '$TopicID'"); - $UpdateArrayForums = array( - 'NumPosts' => '-1', - 'LastPostID' => $LastID, - 'LastPostAuthorID' => $LastAuthorID, - 'LastPostTime' => $LastTime); - } - $UpdateArrayThread = array('Posts' => '-1', 'LastPostAuthorID' => $LastAuthorID, 'LastPostTime' => $LastTime); + if ($LastID < $LastTopicPostID) { // Topic is no longer the most recent in its forum + $DB->query(" + UPDATE forums + SET + LastPostTopicID = '$LastTopicID', + LastPostID = '$LastTopicPostID', + LastPostAuthorID = '$LastTopicAuthorID', + LastPostTime = '$LastTopicPostTime' + WHERE ID = '$ForumID' + AND LastPostTopicID = '$TopicID'"); + $UpdateArrayForums = array( + 'NumPosts' => '-1', + 'LastPostID' => $LastTopicPostID, + 'LastPostAuthorID' => $LastTopicAuthorID, + 'LastPostTime' => $LastTopicPostTime, + 'LastPostTopicID' => $LastTopicID, + 'Title' => $LastTopicTitle); + } else { // Topic is still the most recent in its forum + $DB->query(" + UPDATE forums + SET + LastPostID = '$LastID', + LastPostAuthorID = '$LastAuthorID', + LastPostTime = '$LastTime' + WHERE ID = '$ForumID' + AND LastPostTopicID = '$TopicID'"); + $UpdateArrayForums = array( + 'NumPosts' => '-1', + 'LastPostID' => $LastID, + 'LastPostAuthorID' => $LastAuthorID, + 'LastPostTime' => $LastTime); + } + $UpdateArrayThread = array('Posts' => '-1', 'LastPostAuthorID' => $LastAuthorID, 'LastPostTime' => $LastTime); } else { - $UpdateArrayForums = array('NumPosts' => '-1'); - $UpdateArrayThread = array('Posts' => '-1'); + $UpdateArrayForums = array('NumPosts' => '-1'); + $UpdateArrayThread = array('Posts' => '-1'); } if ($StickyPostID == $PostID) { - $DB->query(" - UPDATE forums_topics - SET StickyPostID = 0 - WHERE ID = $TopicID"); + $DB->query(" + UPDATE forums_topics + SET StickyPostID = 0 + WHERE ID = $TopicID"); } //We need to clear all subsequential catalogues as they've all been bumped with the absence of this post $ThisCatalogue = floor((POSTS_PER_PAGE * $Page - POSTS_PER_PAGE) / THREAD_CATALOGUE); $LastCatalogue = floor((POSTS_PER_PAGE * $Pages - POSTS_PER_PAGE) / THREAD_CATALOGUE); for ($i = $ThisCatalogue; $i <= $LastCatalogue; $i++) { - $Cache->delete_value("thread_$TopicID"."_catalogue_$i"); + $Cache->delete_value("thread_$TopicID"."_catalogue_$i"); } $Cache->begin_transaction("thread_$TopicID".'_info'); @@ -150,6 +150,6 @@ // quote notifications Subscriptions::flush_quote_notifications('forums', $TopicID); $DB->query(" - DELETE FROM users_notify_quoted - WHERE Page = 'forums' - AND PostID = '$PostID'"); + DELETE FROM users_notify_quoted + WHERE Page = 'forums' + AND PostID = '$PostID'"); diff --git a/sections/forums/delete_poll_option.php b/sections/forums/delete_poll_option.php index 51259bd21..d7839d9cd 100644 --- a/sections/forums/delete_poll_option.php +++ b/sections/forums/delete_poll_option.php @@ -1,46 +1,46 @@ -query(" - SELECT ForumID - FROM forums_topics - WHERE ID = $ThreadID"); - list($ForumID) = $DB->next_record(); - if (!in_array($ForumID, $ForumsRevealVoters)) { - error(403); - } + $DB->query(" + SELECT ForumID + FROM forums_topics + WHERE ID = $ThreadID"); + list($ForumID) = $DB->next_record(); + if (!in_array($ForumID, $ForumsRevealVoters)) { + error(403); + } - $DB->query(" - SELECT Answers - FROM forums_polls - WHERE TopicID = $ThreadID"); - if (!$DB->has_results()) { - error(404); - } + $DB->query(" + SELECT Answers + FROM forums_polls + WHERE TopicID = $ThreadID"); + if (!$DB->has_results()) { + error(404); + } - list($Answers) = $DB->next_record(MYSQLI_NUM, false); - $Answers = unserialize($Answers); - unset($Answers[$PollOption]); - $Answers = serialize($Answers); + list($Answers) = $DB->next_record(MYSQLI_NUM, false); + $Answers = unserialize($Answers); + unset($Answers[$PollOption]); + $Answers = serialize($Answers); - $DB->query(" - UPDATE forums_polls - SET Answers = '".db_string($Answers)."' - WHERE TopicID = $ThreadID"); - $DB->query(" - DELETE FROM forums_polls_votes - WHERE Vote = $PollOption - AND TopicID = $ThreadID"); + $DB->query(" + UPDATE forums_polls + SET Answers = '".db_string($Answers)."' + WHERE TopicID = $ThreadID"); + $DB->query(" + DELETE FROM forums_polls_votes + WHERE Vote = $PollOption + AND TopicID = $ThreadID"); - $Cache->delete_value("polls_$ThreadID"); - header("Location: forums.php?action=viewthread&threadid=$ThreadID"); + $Cache->delete_value("polls_$ThreadID"); + header("Location: forums.php?action=viewthread&threadid=$ThreadID"); } else { - error(404); + error(404); } diff --git a/sections/forums/edit_rules.php b/sections/forums/edit_rules.php index ce796eace..911847864 100644 --- a/sections/forums/edit_rules.php +++ b/sections/forums/edit_rules.php @@ -1,82 +1,84 @@ -query(" - INSERT INTO forums_specific_rules (ForumID, ThreadID) - VALUES ($ForumID, ".$_POST['new_thread'].')'); - } - } - if (!empty($_POST['del'])) { - if (is_number($_POST['threadid'])) { - $DB->query(" - DELETE FROM forums_specific_rules - WHERE ForumID = $ForumID - AND ThreadID = ".$_POST['threadid']); - } - } - $Cache->delete_value('forums_list'); + if (!empty($_POST['add'])) { + if (is_number($_POST['new_thread'])) { + $DB->query(" + INSERT INTO forums_specific_rules (ForumID, ThreadID) + VALUES ($ForumID, ".$_POST['new_thread'].')'); + } + } + if (!empty($_POST['del'])) { + if (is_number($_POST['threadid'])) { + $DB->query(" + DELETE FROM forums_specific_rules + WHERE ForumID = $ForumID + AND ThreadID = ".$_POST['threadid']); + } + } + $Cache->delete_value('forums_list'); } $DB->query(" - SELECT ThreadID - FROM forums_specific_rules - WHERE ForumID = $ForumID"); + SELECT ThreadID + FROM forums_specific_rules + WHERE ForumID = $ForumID"); $ThreadIDs = $DB->collect('ThreadID'); View::show_header(); ?>
    -
    -

    - Forums - > - - > - Edit forum specific rules -

    -
    - - - - - - - - - - - - - - - - -
    Thread ID
    - - - -
    -
    - - -
    -
    +
    +

    + Forums + > + + > + Edit forum specific rules +

    +
    + + + + + + + + + + + + + + + + +
    Thread ID
    + + + +
    +
    + + +
    +
    - diff --git a/sections/forums/forum.php b/sections/forums/forum.php index 71fa339e2..9aa31d47a 100644 --- a/sections/forums/forum.php +++ b/sections/forums/forum.php @@ -2,9 +2,9 @@ /**********|| Page to show individual forums || ********************************\ Things to expect in $_GET: - ForumID: ID of the forum curently being browsed - page: The page the user's on. - page = 1 is the same as no page + ForumID: ID of the forum curently being browsed + page: The page the user's on. + page = 1 is the same as no page ********************************************************************************/ @@ -13,16 +13,16 @@ // Check for lame SQL injection attempts $ForumID = $_GET['forumid']; if (!is_number($ForumID)) { - error(0); + error(0); } $IsDonorForum = $ForumID == DONOR_FORUM ? true : false; $Tooltip = $ForumID == DONOR_FORUM ? "tooltip_gold" : "tooltip"; if (isset($LoggedUser['PostsPerPage'])) { - $PerPage = $LoggedUser['PostsPerPage']; + $PerPage = $LoggedUser['PostsPerPage']; } else { - $PerPage = POSTS_PER_PAGE; + $PerPage = POSTS_PER_PAGE; } list($Page, $Limit) = Format::page_limit(TOPICS_PER_PAGE); @@ -32,246 +32,246 @@ // Caching anything beyond the first page of any given forum is just wasting RAM. // Users are more likely to search than to browse to page 2. if ($Page == 1) { - list($Forum,,,$Stickies) = $Cache->get_value("forums_$ForumID"); + list($Forum,,,$Stickies) = $Cache->get_value("forums_$ForumID"); } if (!isset($Forum) || !is_array($Forum)) { - $DB->query(" - SELECT - ID, - Title, - AuthorID, - IsLocked, - IsSticky, - NumPosts, - LastPostID, - LastPostTime, - LastPostAuthorID - FROM forums_topics - WHERE ForumID = '$ForumID' - ORDER BY Ranking = 0, Ranking ASC, IsSticky DESC, LastPostTime DESC - LIMIT $Limit"); // Can be cached until someone makes a new post - $Forum = $DB->to_array('ID', MYSQLI_ASSOC, false); + $DB->query(" + SELECT + ID, + Title, + AuthorID, + IsLocked, + IsSticky, + NumPosts, + LastPostID, + LastPostTime, + LastPostAuthorID + FROM forums_topics + WHERE ForumID = '$ForumID' + ORDER BY Ranking = 0, Ranking ASC, IsSticky DESC, LastPostTime DESC + LIMIT $Limit"); // Can be cached until someone makes a new post + $Forum = $DB->to_array('ID', MYSQLI_ASSOC, false); - if ($Page == 1) { - $DB->query(" - SELECT COUNT(ID) - FROM forums_topics - WHERE ForumID = '$ForumID' - AND IsSticky = '1'"); - list($Stickies) = $DB->next_record(); - $Cache->cache_value("forums_$ForumID", array($Forum, '', 0, $Stickies), 0); - } + if ($Page == 1) { + $DB->query(" + SELECT COUNT(ID) + FROM forums_topics + WHERE ForumID = '$ForumID' + AND IsSticky = '1'"); + list($Stickies) = $DB->next_record(); + $Cache->cache_value("forums_$ForumID", array($Forum, '', 0, $Stickies), 0); + } } if (!isset($Forums[$ForumID])) { - error(404); + error(404); } // Make sure they're allowed to look at the page if (!check_perms('site_moderate_forums')) { - if (isset($LoggedUser['CustomForums'][$ForumID]) && $LoggedUser['CustomForums'][$ForumID] === 0) { - error(403); - } + if (isset($LoggedUser['CustomForums'][$ForumID]) && $LoggedUser['CustomForums'][$ForumID] === 0) { + error(403); + } } $ForumName = display_str($Forums[$ForumID]['Name']); if (!Forums::check_forumperm($ForumID)) { - error(403); + error(403); } // Start printing View::show_header('Forums > '. $Forums[$ForumID]['Name'], '', $IsDonorForum ? 'donor' : ''); ?>
    -

    Forums >

    - - - - - - - + diff --git a/sections/forums/get_post.php b/sections/forums/get_post.php index 1e90446b1..6cea017fe 100644 --- a/sections/forums/get_post.php +++ b/sections/forums/get_post.php @@ -1,4 +1,4 @@ -query(" - SELECT - p.Body, - t.ForumID - FROM forums_posts AS p - JOIN forums_topics AS t ON p.TopicID = t.ID - WHERE p.ID = '$PostID'"); + SELECT + p.Body, + t.ForumID + FROM forums_posts AS p + JOIN forums_topics AS t ON p.TopicID = t.ID + WHERE p.ID = '$PostID'"); list($Body, $ForumID) = $DB->next_record(MYSQLI_NUM); // Is the user allowed to view the post? if (!Forums::check_forumperm($ForumID)) { - error(0); + error(0); } // This gets sent to the browser, which echoes it wherever diff --git a/sections/forums/index.php b/sections/forums/index.php index 9a01febad..50b13c3e5 100644 --- a/sections/forums/index.php +++ b/sections/forums/index.php @@ -1,9 +1,9 @@ -
    -

    Forums

    -
    -Forums +
    + - - + if ($CategoryID != $LastCategoryID) { + $Row = 'b'; + $LastCategoryID = $CategoryID; + if ($OpenTable) { ?> + +

    - - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - -
    ForumLast PostTopicsPosts
    ForumLast PostTopicsPosts
    -

    - -

    -
    - There are no topics here.Create one!' : '')?> - 00 - - 50) ? "title='".display_str($LastTopic)."'" : "")?>> - - - - - - - by -
    -
    - + + + +

    + +

    + + + + There are no topics here.Create one!' : '')?> + + 0 + 0 + + + + 50) ? "title='".display_str($LastTopic)."'" : "")?>> + + + + + + + by + + + + + + + +
    +
    - + diff --git a/sections/forums/mod_thread.php b/sections/forums/mod_thread.php index 7f5d1325e..4cd46961a 100644 --- a/sections/forums/mod_thread.php +++ b/sections/forums/mod_thread.php @@ -1,4 +1,4 @@ - 0) { - $Ranking = 0; + $Ranking = 0; } elseif (0 > $Ranking) { - error('Ranking cannot be a negative value'); + error('Ranking cannot be a negative value'); } $Title = db_string($_POST['title']); $RawTitle = $_POST['title']; @@ -42,362 +42,372 @@ $Page = (int)$_POST['page']; $Action = ''; -$TrashForumID = ($ForumID === EDITING_FORUM_ID) ? EDITING_TRASH_FORUM_ID : TRASH_FORUM_ID; -$ResolveForumID = ($ForumID === HELP_FORUM_ID) ? HELP_RESOLVED_FORUM_ID : (($ForumID === BUGS_FORUM_ID) ? BUGS_RESOLVED_FORUM_ID : ''); - -if ($Locked == 1) { - $DB->query(" - DELETE FROM forums_last_read_topics - WHERE TopicID = '$TopicID'"); +if ($Locked == 1 && check_perms('site_moderate_forums')) { + $DB->query(" + DELETE FROM forums_last_read_topics + WHERE TopicID = '$TopicID'"); } $DB->query(" - SELECT - t.ForumID, - f.Name, - f.MinClassWrite, - COUNT(p.ID) AS Posts, - t.AuthorID, - t.Title, - t.IsLocked, - t.IsSticky, - t.Ranking - FROM forums_topics AS t - LEFT JOIN forums_posts AS p ON p.TopicID = t.ID - LEFT JOIN forums AS f ON f.ID = t.ForumID - WHERE t.ID = '$TopicID' - GROUP BY p.TopicID"); + SELECT + t.ForumID, + f.Name, + f.MinClassWrite, + COUNT(p.ID) AS Posts, + t.AuthorID, + t.Title, + t.IsLocked, + t.IsSticky, + t.Ranking + FROM forums_topics AS t + LEFT JOIN forums_posts AS p ON p.TopicID = t.ID + LEFT JOIN forums AS f ON f.ID = t.ForumID + WHERE t.ID = '$TopicID' + GROUP BY p.TopicID"); list($OldForumID, $OldForumName, $MinClassWrite, $Posts, $ThreadAuthorID, $OldTitle, $OldLocked, $OldSticky, $OldRanking) = $DB->next_record(MYSQLI_BOTH, false); if ($MinClassWrite > $LoggedUser['Class']) { - error(403); + error(403); +} + +if (isset($_POST['transition'])) { + if (is_number($_POST['transition'])) { + // Permissions are handled inside forums.class.php + $transitions = Forums::get_transitions(); + $Debug->log_var($transitions); + if (isset($transitions[$_POST['transition']])) { + $transition = $transitions[$_POST['transition']]; + if ($transition['source'] != $OldForumID) { + error(403); + } + $ForumID = $transition['destination']; + $Sticky = $OldSticky; + $Locked = $OldLocked; + $Ranking = $OldRanking; + $Title = db_string($OldTitle); + $RawTitle = $OldTitle; + $Action = 'transitioning'; + } else { + error(0); + } + } else { + error(0); + } } // If we're deleting a thread if (isset($_POST['delete'])) { - if (!check_perms('site_admin_forums')) { - error(403); - } - - $DB->query(" - DELETE FROM forums_posts - WHERE TopicID = '$TopicID'"); - $DB->query(" - DELETE FROM forums_topics - WHERE ID = '$TopicID'"); - - $DB->query(" - SELECT - t.ID, - t.LastPostID, - t.Title, - p.AuthorID, - um.Username, - p.AddedTime, - ( - SELECT COUNT(pp.ID) - FROM forums_posts AS pp - JOIN forums_topics AS tt ON pp.TopicID = tt.ID - WHERE tt.ForumID = '$ForumID' - ), - t.IsLocked, - t.IsSticky - FROM forums_topics AS t - JOIN forums_posts AS p ON p.ID = t.LastPostID - LEFT JOIN users_main AS um ON um.ID = p.AuthorID - WHERE t.ForumID = '$ForumID' - GROUP BY t.ID - ORDER BY t.LastPostID DESC - LIMIT 1"); - list($NewLastTopic, $NewLastPostID, $NewLastTitle, $NewLastAuthorID, $NewLastAuthorName, $NewLastAddedTime, $NumPosts, $NewLocked, $NewSticky) = $DB->next_record(MYSQLI_NUM, false); - - $DB->query(" - UPDATE forums - SET - NumTopics = NumTopics - 1, - NumPosts = NumPosts - '$Posts', - LastPostTopicID = '$NewLastTopic', - LastPostID = '$NewLastPostID', - LastPostAuthorID = '$NewLastAuthorID', - LastPostTime = '$NewLastAddedTime' - WHERE ID = '$ForumID'"); - $Cache->delete_value("forums_$ForumID"); - - $Cache->delete_value("thread_$TopicID"); - - $Cache->begin_transaction('forums_list'); - $UpdateArray = array( - 'NumPosts' => $NumPosts, - 'NumTopics' => '-1', - 'LastPostID' => $NewLastPostID, - 'LastPostAuthorID' => $NewLastAuthorID, - 'LastPostTopicID' => $NewLastTopic, - 'LastPostTime' => $NewLastAddedTime, - 'Title' => $NewLastTitle, - 'IsLocked' => $NewLocked, - 'IsSticky' => $NewSticky - ); - - $Cache->update_row($ForumID, $UpdateArray); - $Cache->commit_transaction(0); - $Cache->delete_value("thread_{$TopicID}_info"); - - // subscriptions - Subscriptions::move_subscriptions('forums', $TopicID, null); - - // quote notifications - Subscriptions::flush_quote_notifications('forums', $TopicID); - $DB->query(" - DELETE FROM users_notify_quoted - WHERE Page = 'forums' - AND PageID = '$TopicID'"); - - header("Location: forums.php?action=viewforum&forumid=$ForumID"); + if (!check_perms('site_admin_forums')) { + error(403); + } + + $DB->query(" + DELETE FROM forums_posts + WHERE TopicID = '$TopicID'"); + $DB->query(" + DELETE FROM forums_topics + WHERE ID = '$TopicID'"); + + $DB->query(" + SELECT + t.ID, + t.LastPostID, + t.Title, + p.AuthorID, + um.Username, + p.AddedTime, + ( + SELECT COUNT(pp.ID) + FROM forums_posts AS pp + JOIN forums_topics AS tt ON pp.TopicID = tt.ID + WHERE tt.ForumID = '$ForumID' + ), + t.IsLocked, + t.IsSticky + FROM forums_topics AS t + JOIN forums_posts AS p ON p.ID = t.LastPostID + LEFT JOIN users_main AS um ON um.ID = p.AuthorID + WHERE t.ForumID = '$ForumID' + GROUP BY t.ID + ORDER BY t.LastPostID DESC + LIMIT 1"); + list($NewLastTopic, $NewLastPostID, $NewLastTitle, $NewLastAuthorID, $NewLastAuthorName, $NewLastAddedTime, $NumPosts, $NewLocked, $NewSticky) = $DB->next_record(MYSQLI_NUM, false); + + $DB->query(" + UPDATE forums + SET + NumTopics = NumTopics - 1, + NumPosts = NumPosts - '$Posts', + LastPostTopicID = '$NewLastTopic', + LastPostID = '$NewLastPostID', + LastPostAuthorID = '$NewLastAuthorID', + LastPostTime = '$NewLastAddedTime' + WHERE ID = '$ForumID'"); + $Cache->delete_value("forums_$ForumID"); + + $Cache->delete_value("thread_$TopicID"); + + $Cache->begin_transaction('forums_list'); + $UpdateArray = array( + 'NumPosts' => $NumPosts, + 'NumTopics' => '-1', + 'LastPostID' => $NewLastPostID, + 'LastPostAuthorID' => $NewLastAuthorID, + 'LastPostTopicID' => $NewLastTopic, + 'LastPostTime' => $NewLastAddedTime, + 'Title' => $NewLastTitle, + 'IsLocked' => $NewLocked, + 'IsSticky' => $NewSticky + ); + + $Cache->update_row($ForumID, $UpdateArray); + $Cache->commit_transaction(0); + $Cache->delete_value("thread_{$TopicID}_info"); + + // subscriptions + Subscriptions::move_subscriptions('forums', $TopicID, null); + + // quote notifications + Subscriptions::flush_quote_notifications('forums', $TopicID); + $DB->query(" + DELETE FROM users_notify_quoted + WHERE Page = 'forums' + AND PageID = '$TopicID'"); + + header("Location: forums.php?action=viewforum&forumid=$ForumID"); } else { // If we're just editing it - $Action = 'editing'; - - if (isset($_POST['trash'])) { - $ForumID = $TrashForumID; - $Action = 'trashing'; - } - - if (isset($_POST['resolve'])) { - $ForumID = $ResolveForumID; - $Action = 'resolving'; - } - - $Cache->begin_transaction("thread_{$TopicID}_info"); - $UpdateArray = array( - 'IsSticky' => $Sticky, - 'Ranking' => $Ranking, - 'IsLocked' => $Locked, - 'Title' => Format::cut_string($RawTitle, 150, 1, 0), - 'ForumID' => $ForumID - ); - $Cache->update_row(false, $UpdateArray); - $Cache->commit_transaction(0); - - $DB->query(" - UPDATE forums_topics - SET - IsSticky = '$Sticky', - Ranking = '$Ranking', - IsLocked = '$Locked', - Title = '$Title', - ForumID = '$ForumID' - WHERE ID = '$TopicID'"); - - // always clear cache when editing a thread. - // if a thread title, etc. is changed, this cache key must be cleared so the thread listing - // properly shows the new thread title. - $Cache->delete_value("forums_$ForumID"); - - if ($ForumID != $OldForumID) { // If we're moving a thread, change the forum stats - $Cache->delete_value("forums_$OldForumID"); - - $DB->query(" - SELECT MinClassRead, MinClassWrite, Name - FROM forums - WHERE ID = '$ForumID'"); - list($MinClassRead, $MinClassWrite, $ForumName) = $DB->next_record(MYSQLI_NUM, false); - $Cache->begin_transaction("thread_{$TopicID}_info"); - $UpdateArray = array( - 'ForumName' => $ForumName, - 'MinClassRead' => $MinClassRead, - 'MinClassWrite' => $MinClassWrite - ); - $Cache->update_row(false, $UpdateArray); - $Cache->commit_transaction(3600 * 24 * 5); - - $Cache->begin_transaction('forums_list'); - - // Forum we're moving from - $DB->query(" - SELECT - t.ID, - t.LastPostID, - t.Title, - p.AuthorID, - um.Username, - p.AddedTime, - ( - SELECT COUNT(pp.ID) - FROM forums_posts AS pp - JOIN forums_topics AS tt ON pp.TopicID = tt.ID - WHERE tt.ForumID = '$OldForumID' - ), - t.IsLocked, - t.IsSticky, - t.Ranking - FROM forums_topics AS t - JOIN forums_posts AS p ON p.ID = t.LastPostID - LEFT JOIN users_main AS um ON um.ID = p.AuthorID - WHERE t.ForumID = '$OldForumID' - ORDER BY t.LastPostID DESC - LIMIT 1"); - list($NewLastTopic, $NewLastPostID, $NewLastTitle, $NewLastAuthorID, $NewLastAuthorName, $NewLastAddedTime, $NumPosts, $NewLocked, $NewSticky, $NewRanking) = $DB->next_record(MYSQLI_NUM, false); - - $DB->query(" - UPDATE forums - SET - NumTopics = NumTopics - 1, - NumPosts = NumPosts - '$Posts', - LastPostTopicID = '$NewLastTopic', - LastPostID = '$NewLastPostID', - LastPostAuthorID = '$NewLastAuthorID', - LastPostTime = '$NewLastAddedTime' - WHERE ID = '$OldForumID'"); - - - $UpdateArray = array( - 'NumPosts' => $NumPosts, - 'NumTopics' => '-1', - 'LastPostID' => $NewLastPostID, - 'LastPostAuthorID' => $NewLastAuthorID, - 'LastPostTopicID' => $NewLastTopic, - 'LastPostTime' => $NewLastAddedTime, - 'Title' => $NewLastTitle, - 'IsLocked' => $NewLocked, - 'IsSticky' => $NewSticky, - 'Ranking' => $NewRanking - ); - - - $Cache->update_row($OldForumID, $UpdateArray); - - // Forum we're moving to - - $DB->query(" - SELECT - t.ID, - t.LastPostID, - t.Title, - p.AuthorID, - um.Username, - p.AddedTime, - ( - SELECT COUNT(pp.ID) - FROM forums_posts AS pp - JOIN forums_topics AS tt ON pp.TopicID = tt.ID - WHERE tt.ForumID = '$ForumID' - ) - FROM forums_topics AS t - JOIN forums_posts AS p ON p.ID = t.LastPostID - LEFT JOIN users_main AS um ON um.ID = p.AuthorID - WHERE t.ForumID = '$ForumID' - ORDER BY t.LastPostID DESC - LIMIT 1"); - list($NewLastTopic, $NewLastPostID, $NewLastTitle, $NewLastAuthorID, $NewLastAuthorName, $NewLastAddedTime, $NumPosts) = $DB->next_record(MYSQLI_NUM, false); - - $DB->query(" - UPDATE forums - SET - NumTopics = NumTopics + 1, - NumPosts = NumPosts + '$Posts', - LastPostTopicID = '$NewLastTopic', - LastPostID = '$NewLastPostID', - LastPostAuthorID = '$NewLastAuthorID', - LastPostTime = '$NewLastAddedTime' - WHERE ID = '$ForumID'"); - - - $UpdateArray = array( - 'NumPosts' => ($NumPosts + $Posts), - 'NumTopics' => '+1', - 'LastPostID' => $NewLastPostID, - 'LastPostAuthorID' => $NewLastAuthorID, - 'LastPostTopicID' => $NewLastTopic, - 'LastPostTime' => $NewLastAddedTime, - 'Title' => $NewLastTitle - ); - - $Cache->update_row($ForumID, $UpdateArray); - - $Cache->commit_transaction(0); - - if ($ForumID == $TrashForumID) { - $Action = 'trashing'; - } - if ($ForumID == $ResolveForumID) { - $Action = 'resolving'; - } - } else { // Editing - $DB->query(" - SELECT LastPostTopicID - FROM forums - WHERE ID = '$ForumID'"); - list($LastTopicID) = $DB->next_record(); - if ($LastTopicID == $TopicID) { - $UpdateArray = array( - 'Title' => $RawTitle, - 'IsLocked' => $Locked, - 'IsSticky' => $Sticky, - 'Ranking' => $Ranking - ); - $Cache->begin_transaction('forums_list'); - $Cache->update_row($ForumID, $UpdateArray); - $Cache->commit_transaction(0); - } - } - if ($Locked) { - $CatalogueID = floor($NumPosts / THREAD_CATALOGUE); - for ($i = 0; $i <= $CatalogueID; $i++) { - $Cache->expire_value("thread_{$TopicID}_catalogue_$i", 3600 * 24 * 7); // 7 days - } - $Cache->expire_value("thread_{$TopicID}_info", 3600 * 24 * 7); // 7 days - - $DB->query(" - UPDATE forums_polls - SET Closed = '0' - WHERE TopicID = '$TopicID'"); - $Cache->delete_value("polls_$TopicID"); - } - - // topic notes and notifications - $TopicNotes = []; - switch ($Action) { - case 'editing': - if ($OldTitle != $RawTitle) { - // title edited - $TopicNotes[] = "Title edited from \"$OldTitle\" to \"$RawTitle\""; - } - if ($OldLocked != $Locked) { - if (!$OldLocked) { - $TopicNotes[] = 'Locked'; - } else { - $TopicNotes[] = 'Unlocked'; - } - } - if ($OldSticky != $Sticky) { - if (!$OldSticky) { - $TopicNotes[] = 'Stickied'; - } else { - $TopicNotes[] = 'Unstickied'; - } - } - if ($OldRanking != $Ranking) { - $TopicNotes[] = "Ranking changed from \"$OldRanking\" to \"$Ranking\""; - } - if ($ForumID != $OldForumID) { - $TopicNotes[] = "Moved from [url=" . site_url() . "forums.php?action=viewforum&forumid=$OldForumID]{$OldForumName}[/url] to [url=" . site_url() . "forums.php?action=viewforum&forumid=$ForumID]{$ForumName}[/url]"; - } - break; - case 'trashing': - $TopicNotes[] = "Trashed (moved from [url=" . site_url() . "forums.php?action=viewforum&forumid=$OldForumID]{$OldForumName}[/url] to [url=" . site_url() . "forums.php?action=viewforum&forumid=$ForumID]{$ForumName}[/url])"; - $Notification = "Your thread \"$NewLastTitle\" has been trashed"; - break; - case 'resolving': - $TopicNotes[] = "Resolved (moved from [url=" . site_url() . "forums.php?action=viewforum&forumid=$OldForumID]{$OldForumName}[/url] to [url=" . site_url() . "forums.php?action=viewforum&forumid=$ForumID]{$ForumName}[/url])"; - $Notification = "Your thread \"$NewLastTitle\" has been resolved"; - break; - default: - break; - } - if (isset($Notification)) { - NotificationsManager::notify_user($ThreadAuthorID, NotificationsManager::FORUMALERTS, $Notification, "forums.php?action=viewthread&threadid=$TopicID"); - } - if (count($TopicNotes) > 0) { - Forums::add_topic_note($TopicID, implode("\n", $TopicNotes)); - } - header("Location: forums.php?action=viewthread&threadid=$TopicID&page=$Page"); + if ($Action == '') { + $Action = 'editing'; + } + + $Cache->begin_transaction("thread_{$TopicID}_info"); + $UpdateArray = array( + 'IsSticky' => $Sticky, + 'Ranking' => $Ranking, + 'IsLocked' => $Locked, + 'Title' => Format::cut_string($RawTitle, 150, 1, 0), + 'ForumID' => $ForumID + ); + $Cache->update_row(false, $UpdateArray); + $Cache->commit_transaction(0); + + $DB->query(" + UPDATE forums_topics + SET + IsSticky = '$Sticky', + Ranking = '$Ranking', + IsLocked = '$Locked', + Title = '$Title', + ForumID = '$ForumID' + WHERE ID = '$TopicID'"); + + // always clear cache when editing a thread. + // if a thread title, etc. is changed, this cache key must be cleared so the thread listing + // properly shows the new thread title. + $Cache->delete_value("forums_$ForumID"); + + if ($ForumID != $OldForumID) { // If we're moving a thread, change the forum stats + $Cache->delete_value("forums_$OldForumID"); + + $DB->query(" + SELECT MinClassRead, MinClassWrite, Name + FROM forums + WHERE ID = '$ForumID'"); + list($MinClassRead, $MinClassWrite, $ForumName) = $DB->next_record(MYSQLI_NUM, false); + $Cache->begin_transaction("thread_{$TopicID}_info"); + $UpdateArray = array( + 'ForumName' => $ForumName, + 'MinClassRead' => $MinClassRead, + 'MinClassWrite' => $MinClassWrite + ); + $Cache->update_row(false, $UpdateArray); + $Cache->commit_transaction(3600 * 24 * 5); + + $Cache->begin_transaction('forums_list'); + + // Forum we're moving from + $DB->query(" + SELECT + t.ID, + t.LastPostID, + t.Title, + p.AuthorID, + um.Username, + p.AddedTime, + ( + SELECT COUNT(pp.ID) + FROM forums_posts AS pp + JOIN forums_topics AS tt ON pp.TopicID = tt.ID + WHERE tt.ForumID = '$OldForumID' + ), + t.IsLocked, + t.IsSticky, + t.Ranking + FROM forums_topics AS t + JOIN forums_posts AS p ON p.ID = t.LastPostID + LEFT JOIN users_main AS um ON um.ID = p.AuthorID + WHERE t.ForumID = '$OldForumID' + ORDER BY t.LastPostID DESC + LIMIT 1"); + list($NewLastTopic, $NewLastPostID, $NewLastTitle, $NewLastAuthorID, $NewLastAuthorName, $NewLastAddedTime, $NumPosts, $NewLocked, $NewSticky, $NewRanking) = $DB->next_record(MYSQLI_NUM, false); + + $DB->query(" + UPDATE forums + SET + NumTopics = NumTopics - 1, + NumPosts = NumPosts - '$Posts', + LastPostTopicID = '$NewLastTopic', + LastPostID = '$NewLastPostID', + LastPostAuthorID = '$NewLastAuthorID', + LastPostTime = '$NewLastAddedTime' + WHERE ID = '$OldForumID'"); + + + $UpdateArray = array( + 'NumPosts' => $NumPosts, + 'NumTopics' => '-1', + 'LastPostID' => $NewLastPostID, + 'LastPostAuthorID' => $NewLastAuthorID, + 'LastPostTopicID' => $NewLastTopic, + 'LastPostTime' => $NewLastAddedTime, + 'Title' => $NewLastTitle, + 'IsLocked' => $NewLocked, + 'IsSticky' => $NewSticky, + 'Ranking' => $NewRanking + ); + + + $Cache->update_row($OldForumID, $UpdateArray); + + // Forum we're moving to + + $DB->query(" + SELECT + t.ID, + t.LastPostID, + t.Title, + p.AuthorID, + um.Username, + p.AddedTime, + ( + SELECT COUNT(pp.ID) + FROM forums_posts AS pp + JOIN forums_topics AS tt ON pp.TopicID = tt.ID + WHERE tt.ForumID = '$ForumID' + ) + FROM forums_topics AS t + JOIN forums_posts AS p ON p.ID = t.LastPostID + LEFT JOIN users_main AS um ON um.ID = p.AuthorID + WHERE t.ForumID = '$ForumID' + ORDER BY t.LastPostID DESC + LIMIT 1"); + list($NewLastTopic, $NewLastPostID, $NewLastTitle, $NewLastAuthorID, $NewLastAuthorName, $NewLastAddedTime, $NumPosts) = $DB->next_record(MYSQLI_NUM, false); + + $DB->query(" + UPDATE forums + SET + NumTopics = NumTopics + 1, + NumPosts = NumPosts + '$Posts', + LastPostTopicID = '$NewLastTopic', + LastPostID = '$NewLastPostID', + LastPostAuthorID = '$NewLastAuthorID', + LastPostTime = '$NewLastAddedTime' + WHERE ID = '$ForumID'"); + + + $UpdateArray = array( + 'NumPosts' => ($NumPosts + $Posts), + 'NumTopics' => '+1', + 'LastPostID' => $NewLastPostID, + 'LastPostAuthorID' => $NewLastAuthorID, + 'LastPostTopicID' => $NewLastTopic, + 'LastPostTime' => $NewLastAddedTime, + 'Title' => $NewLastTitle + ); + + $Cache->update_row($ForumID, $UpdateArray); + + $Cache->commit_transaction(0); + } else { // Editing + $DB->query(" + SELECT LastPostTopicID + FROM forums + WHERE ID = '$ForumID'"); + list($LastTopicID) = $DB->next_record(); + if ($LastTopicID == $TopicID) { + $UpdateArray = array( + 'Title' => $RawTitle, + 'IsLocked' => $Locked, + 'IsSticky' => $Sticky, + 'Ranking' => $Ranking + ); + $Cache->begin_transaction('forums_list'); + $Cache->update_row($ForumID, $UpdateArray); + $Cache->commit_transaction(0); + } + } + if ($Locked) { + $CatalogueID = floor($NumPosts / THREAD_CATALOGUE); + for ($i = 0; $i <= $CatalogueID; $i++) { + $Cache->expire_value("thread_{$TopicID}_catalogue_$i", 3600 * 24 * 7); // 7 days + } + $Cache->expire_value("thread_{$TopicID}_info", 3600 * 24 * 7); // 7 days + + $DB->query(" + UPDATE forums_polls + SET Closed = '0' + WHERE TopicID = '$TopicID'"); + $Cache->delete_value("polls_$TopicID"); + } + + // topic notes and notifications + $TopicNotes = []; + switch ($Action) { + case 'editing': + if ($OldTitle != $RawTitle) { + // title edited + $TopicNotes[] = "Title edited from \"$OldTitle\" to \"$RawTitle\""; + } + if ($OldLocked != $Locked) { + if (!$OldLocked) { + $TopicNotes[] = 'Locked'; + } else { + $TopicNotes[] = 'Unlocked'; + } + } + if ($OldSticky != $Sticky) { + if (!$OldSticky) { + $TopicNotes[] = 'Stickied'; + } else { + $TopicNotes[] = 'Unstickied'; + } + } + if ($OldRanking != $Ranking) { + $TopicNotes[] = "Ranking changed from \"$OldRanking\" to \"$Ranking\""; + } + if ($ForumID != $OldForumID) { + $TopicNotes[] = "Moved from [url=" . site_url() . "forums.php?action=viewforum&forumid=$OldForumID]{$OldForumName}[/url] to [url=" . site_url() . "forums.php?action=viewforum&forumid=$ForumID]{$ForumName}[/url]"; + } + break; + case 'trashing': + $TopicNotes[] = "Trashed (moved from [url=" . site_url() . "forums.php?action=viewforum&forumid=$OldForumID]{$OldForumName}[/url] to [url=" . site_url() . "forums.php?action=viewforum&forumid=$ForumID]{$ForumName}[/url])"; + $Notification = "Your thread \"$NewLastTitle\" has been trashed"; + break; + case 'resolving': + $TopicNotes[] = "Resolved (moved from [url=" . site_url() . "forums.php?action=viewforum&forumid=$OldForumID]{$OldForumName}[/url] to [url=" . site_url() . "forums.php?action=viewforum&forumid=$ForumID]{$ForumName}[/url])"; + $Notification = "Your thread \"$NewLastTitle\" has been resolved"; + break; + case 'transitioning': + $TopicNotes[] = "Moved from [url=" . site_url() . "forums.php?action=viewforum&forumid=$OldForumID]{$OldForumName}[/url] to [url=" . site_url() . "forums.php?action=viewforum&forumid=$ForumID]{$ForumName}[/url] (" . $transition['label'] . " transition)"; + break; + default: + break; + } + if (isset($Notification)) { + NotificationsManager::notify_user($ThreadAuthorID, NotificationsManager::FORUMALERTS, $Notification, "forums.php?action=viewthread&threadid=$TopicID"); + } + if (count($TopicNotes) > 0) { + Forums::add_topic_note($TopicID, implode("\n", $TopicNotes)); + } + header("Location: forums.php?action=viewthread&threadid=$TopicID&page=$Page"); } diff --git a/sections/forums/newthread.php b/sections/forums/newthread.php index 212206c72..3762ad92e 100644 --- a/sections/forums/newthread.php +++ b/sections/forums/newthread.php @@ -1,167 +1,174 @@ -
    -

    Forums > > New Topic

    - -
    -
    - - - - - - - - - - - - - - - - -Forums > > New Topic +
    Title:
    Body:
    - onchange="$('#subscribeboxpreview').raw().checked=this.checked;" /> - -
    + + + + + + + + + + + + + + + +
    + #XXXXXX + by + Just now + + + Report +   + + +
    + + +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - -
    Title:
    Body:
    + onchange="$('#subscribeboxpreview').raw().checked=this.checked;" /> + +
    - Poll Settings - View -
    - -
    - - - -
    - -
    + function RemoveAnswerField() { + if (AnswerCount == 1) { + return; + } + var x = $('#answer_block').raw(); + for (i = 0; i < 2; i++) { + x.removeChild(x.lastChild); + } + AnswerCount--; + } + //]]> + + + + Poll Settings + View + + + + Question: + + + + Answers: + + + + + + + + + + +
    + + + +
    + +
    - + diff --git a/sections/forums/poll_mod.php b/sections/forums/poll_mod.php index 9eec66f75..b476f2372 100644 --- a/sections/forums/poll_mod.php +++ b/sections/forums/poll_mod.php @@ -1,60 +1,60 @@ -get_value('polls_'.$TopicID)) { - $DB->query(" - SELECT Question, Answers, Featured, Closed - FROM forums_polls - WHERE TopicID='".$TopicID."'"); - list($Question, $Answers, $Featured, $Closed) = $DB->next_record(MYSQLI_NUM, array(1)); - $Answers = unserialize($Answers); - $DB->query(" - SELECT Vote, COUNT(UserID) - FROM forums_polls_votes - WHERE TopicID = '$TopicID' - AND Vote != '0' - GROUP BY Vote"); - $VoteArray = $DB->to_array(false, MYSQLI_NUM); + $DB->query(" + SELECT Question, Answers, Featured, Closed + FROM forums_polls + WHERE TopicID='".$TopicID."'"); + list($Question, $Answers, $Featured, $Closed) = $DB->next_record(MYSQLI_NUM, array(1)); + $Answers = unserialize($Answers); + $DB->query(" + SELECT Vote, COUNT(UserID) + FROM forums_polls_votes + WHERE TopicID = '$TopicID' + AND Vote != '0' + GROUP BY Vote"); + $VoteArray = $DB->to_array(false, MYSQLI_NUM); - $Votes = array(); - foreach ($VoteArray as $VoteSet) { - list($Key,$Value) = $VoteSet; - $Votes[$Key] = $Value; - } + $Votes = []; + foreach ($VoteArray as $VoteSet) { + list($Key,$Value) = $VoteSet; + $Votes[$Key] = $Value; + } - for ($i = 1, $il = count($Answers); $i <= $il; ++$i) { - if (!isset($Votes[$i])) { - $Votes[$i] = 0; - } - } + for ($i = 1, $il = count($Answers); $i <= $il; ++$i) { + if (!isset($Votes[$i])) { + $Votes[$i] = 0; + } + } } if (isset($_POST['feature'])) { - if (!$Featured || $Featured == '0000-00-00 00:00:00') { - $Featured = sqltime(); - $Cache->cache_value('polls_featured',$TopicID,0); - $DB->query(' - UPDATE forums_polls - SET Featured=\''.sqltime().'\' - WHERE TopicID=\''.$TopicID.'\''); - } + if (!$Featured || $Featured == '0000-00-00 00:00:00') { + $Featured = sqltime(); + $Cache->cache_value('polls_featured',$TopicID,0); + $DB->query(' + UPDATE forums_polls + SET Featured=\''.sqltime().'\' + WHERE TopicID=\''.$TopicID.'\''); + } } if (isset($_POST['close'])) { - $Closed = !$Closed; - $DB->query(' - UPDATE forums_polls - SET Closed=\''.$Closed.'\' - WHERE TopicID=\''.$TopicID.'\''); + $Closed = !$Closed; + $DB->query(' + UPDATE forums_polls + SET Closed=\''.$Closed.'\' + WHERE TopicID=\''.$TopicID.'\''); } $Cache->cache_value('polls_'.$TopicID, array($Question,$Answers,$Votes,$Featured,$Closed), 0); diff --git a/sections/forums/poll_vote.php b/sections/forums/poll_vote.php index 697e8be0b..c5fe56d01 100644 --- a/sections/forums/poll_vote.php +++ b/sections/forums/poll_vote.php @@ -1,170 +1,172 @@ -get_value("thread_$TopicID".'_info')) { - $DB->query(" - SELECT - t.Title, - t.ForumID, - t.IsLocked, - t.IsSticky, - COUNT(fp.id) AS Posts, - t.LastPostAuthorID, - ISNULL(p.TopicID) AS NoPoll - FROM forums_topics AS t - JOIN forums_posts AS fp ON fp.TopicID = t.ID - LEFT JOIN forums_polls AS p ON p.TopicID = t.ID - WHERE t.ID = '$TopicID' - GROUP BY fp.TopicID"); - if (!$DB->has_results()) { - die(); - } - $ThreadInfo = $DB->next_record(MYSQLI_ASSOC); - if (!$ThreadInfo['IsLocked'] || $ThreadInfo['IsSticky']) { - $Cache->cache_value("thread_$TopicID".'_info', $ThreadInfo, 0); - } + $DB->query(" + SELECT + t.Title, + t.ForumID, + t.IsLocked, + t.IsSticky, + COUNT(fp.id) AS Posts, + t.LastPostAuthorID, + ISNULL(p.TopicID) AS NoPoll + FROM forums_topics AS t + JOIN forums_posts AS fp ON fp.TopicID = t.ID + LEFT JOIN forums_polls AS p ON p.TopicID = t.ID + WHERE t.ID = '$TopicID' + GROUP BY fp.TopicID"); + if (!$DB->has_results()) { + die(); + } + $ThreadInfo = $DB->next_record(MYSQLI_ASSOC); + if (!$ThreadInfo['IsLocked'] || $ThreadInfo['IsSticky']) { + $Cache->cache_value("thread_$TopicID".'_info', $ThreadInfo, 0); + } } $ForumID = $ThreadInfo['ForumID']; if (!list($Question, $Answers, $Votes, $Featured, $Closed) = $Cache->get_value("polls_$TopicID")) { - $DB->query(" - SELECT - Question, - Answers, - Featured, - Closed - FROM forums_polls - WHERE TopicID = '$TopicID'"); - list($Question, $Answers, $Featured, $Closed) = $DB->next_record(MYSQLI_NUM, array(1)); - $Answers = unserialize($Answers); - $DB->query(" - SELECT Vote, COUNT(UserID) - FROM forums_polls_votes - WHERE TopicID = '$TopicID' - AND Vote != '0' - GROUP BY Vote"); - $VoteArray = $DB->to_array(false, MYSQLI_NUM); + $DB->query(" + SELECT + Question, + Answers, + Featured, + Closed + FROM forums_polls + WHERE TopicID = '$TopicID'"); + list($Question, $Answers, $Featured, $Closed) = $DB->next_record(MYSQLI_NUM, array(1)); + $Answers = unserialize($Answers); + $DB->query(" + SELECT Vote, COUNT(UserID) + FROM forums_polls_votes + WHERE TopicID = '$TopicID' + AND Vote != '0' + GROUP BY Vote"); + $VoteArray = $DB->to_array(false, MYSQLI_NUM); - $Votes = array(); - foreach ($VoteArray as $VoteSet) { - list($Key,$Value) = $VoteSet; - $Votes[$Key] = $Value; - } + $Votes = []; + foreach ($VoteArray as $VoteSet) { + list($Key,$Value) = $VoteSet; + $Votes[$Key] = $Value; + } - for ($i = 1, $il = count($Answers); $i <= $il; ++$i) { - if (!isset($Votes[$i])) { - $Votes[$i] = 0; - } - } - $Cache->cache_value("polls_$TopicID", array($Question, $Answers, $Votes, $Featured, $Closed), 0); + for ($i = 1, $il = count($Answers); $i <= $il; ++$i) { + if (!isset($Votes[$i])) { + $Votes[$i] = 0; + } + } + $Cache->cache_value("polls_$TopicID", array($Question, $Answers, $Votes, $Featured, $Closed), 0); } if ($Closed) { - error(403,true); + error(403,true); } if (!empty($Votes)) { - $TotalVotes = array_sum($Votes); - $MaxVotes = max($Votes); + $TotalVotes = array_sum($Votes); + $MaxVotes = max($Votes); } else { - $TotalVotes = 0; - $MaxVotes = 0; + $TotalVotes = 0; + $MaxVotes = 0; } if (!isset($_POST['vote']) || !is_number($_POST['vote'])) { ?> Please select an option.
    - - - - - - -
    - -


    - + + + + + + +
    + +


    +
    -query(" - INSERT IGNORE INTO forums_polls_votes - (TopicID, UserID, Vote) - VALUES - ($TopicID, " . $LoggedUser['ID'] . ", $Vote)"); - if ($DB->affected_rows() == 1 && $Vote != 0) { - $Cache->begin_transaction("polls_$TopicID"); - $Cache->update_row(2, array($Vote => '+1')); - $Cache->commit_transaction(0); - $Votes[$Vote]++; - $TotalVotes++; - $MaxVotes++; - } + //Add our vote + $DB->query(" + INSERT IGNORE INTO forums_polls_votes + (TopicID, UserID, Vote) + VALUES + ($TopicID, " . $LoggedUser['ID'] . ", $Vote)"); + if ($DB->affected_rows() == 1 && $Vote != 0) { + $Cache->begin_transaction("polls_$TopicID"); + $Cache->update_row(2, array($Vote => '+1')); + $Cache->commit_transaction(0); + $Votes[$Vote]++; + $TotalVotes++; + $MaxVotes++; + } - if ($Vote != 0) { - $Answers[$Vote] = '=> '.$Answers[$Vote]; - } + if ($Vote != 0) { + $Answers[$Vote] = '=> '.$Answers[$Vote]; + } ?> -
      - 0) { - $Ratio = $Votes[$i] / $MaxVotes; - $Percent = $Votes[$i] / $TotalVotes; - } else { - $Ratio = 0; - $Percent = 0; - } +
        + 0) { + $Ratio = $Votes[$i] / $MaxVotes; + $Percent = $Votes[$i] / $TotalVotes; + } else { + $Ratio = 0; + $Percent = 0; + } ?> -
      • (%)
      • -
      • - - - -
      • -query(" - SELECT GROUP_CONCAT(um.Username SEPARATOR ', '), - fpv.Vote - FROM users_main AS um - JOIN forums_polls_votes AS fpv ON um.ID = fpv.UserID - WHERE TopicID = $TopicID - GROUP BY fpv.Vote"); +
      • (%)
      • +
      • + + + +
      • +query(" + SELECT GROUP_CONCAT(um.Username SEPARATOR ', '), + fpv.Vote + FROM users_main AS um + JOIN forums_polls_votes AS fpv ON um.ID = fpv.UserID + WHERE TopicID = $TopicID + GROUP BY fpv.Vote"); - $StaffVotes = $DB->to_array(); - foreach ($StaffVotes as $StaffVote) { - list($StaffString, $StaffVoted) = $StaffVote; + $StaffVotes = $DB->to_array(); + foreach ($StaffVotes as $StaffVote) { + list($StaffString, $StaffVoted) = $StaffVote; ?> -
      • -
      • - - + -
      -
      Votes: - +
      Votes: +query(" - SELECT ID - FROM users_main - WHERE Username = '".db_string($User)."'"); - list($AuthorID) = $DB->next_record(); - if ($AuthorID === null) { - $AuthorID = 0; - //this will cause the search to return 0 results. - //workaround in line 276 to display that the username was wrong. - } + $User = trim($_GET['user']); + $DB->query(" + SELECT ID + FROM users_main + WHERE Username = '".db_string($User)."'"); + list($AuthorID) = $DB->next_record(); + if ($AuthorID === null) { + $AuthorID = 0; + //this will cause the search to return 0 results. + //workaround in line 276 to display that the username was wrong. + } } else { - $User = ''; + $User = ''; } // Are we looking in individual forums? if (isset($_GET['forums']) && is_array($_GET['forums'])) { - $ForumArray = array(); - foreach ($_GET['forums'] as $Forum) { - if (is_number($Forum)) { - $ForumArray[] = $Forum; - } - } - if (count($ForumArray) > 0) { - $SearchForums = implode(', ', $ForumArray); - } + $ForumArray = []; + foreach ($_GET['forums'] as $Forum) { + if (is_number($Forum)) { + $ForumArray[] = $Forum; + } + } + if (count($ForumArray) > 0) { + $SearchForums = implode(', ', $ForumArray); + } } // Searching for posts in a specific thread if (!empty($_GET['threadid']) && is_number($_GET['threadid'])) { - $ThreadID = $_GET['threadid']; - $Type = 'body'; - $SQL = " - SELECT - Title - FROM forums_topics AS t - JOIN forums AS f ON f.ID = t.ForumID - WHERE t.ID = $ThreadID - AND " . Forums::user_forums_sql(); - $DB->query($SQL); - if (list($Title) = $DB->next_record()) { - $Title = " > $Title"; - } else { - error(404); - } + $ThreadID = $_GET['threadid']; + $Type = 'body'; + $SQL = " + SELECT + Title + FROM forums_topics AS t + JOIN forums AS f ON f.ID = t.ForumID + WHERE t.ID = $ThreadID + AND " . Forums::user_forums_sql(); + $DB->query($SQL); + if (list($Title) = $DB->next_record()) { + $Title = " > $Title"; + } else { + error(404); + } } else { - $ThreadID = ''; + $ThreadID = ''; } // Let's hope we got some results - start printing out the content. View::show_header('Forums > Search', 'bbcode,forum_search,datetime_picker', 'datetime_picker'); ?>
      -
      -

      Forums > Search

      -
      -
      - - - - - - - - - - - - - - - +

      Forums > Search

      + + + +
      Search for: - -
      Posted by: - -
      Topic created: - After: - - Before: - -
      + + + + + + + + + + + + + - - - - - > - - - - - - + + + + + + + + + + + +
      Search for: + +
      Posted by: + +
      Topic created: + After: + + Before: + +
      Search in: - /> - - /> - -
      Post created: - After: - - Before: - -
      Forums: - + + + + + > + + + + + + - - - - -
      Search in: + /> + + /> + +
      Post created: + After: + + Before: + +
      Forums: + - - - + + - - + - - - - - - - - - - - - - -
      - - Check all -
      - /> - -
      - - - -
      - -
      - -
      + + Check all +
      + /> + +
      + + + + + + + + + + + + + - - - - - - - -has_results()) { ?> - - +
      ForumTopic creation timeLast post time
      Nothing found!
      + + + + + + +has_results()) { ?> + +next_record()) { - $Row = $Row === 'a' ? 'b' : 'a'; - // Print results + $Row = $Row === 'a' ? 'b' : 'a'; + // Print results ?> - - - - - - - - - - -"> + + + + + + + + + + -
      ForumTopic creation timeLast post time
      Nothing found!
      - - - - - - - - (Show) "> - - - - - -
      + + + + + + + + (Show) "> + + + + + +
      + - +
      - + diff --git a/sections/forums/sticky_post.php b/sections/forums/sticky_post.php index 098c2cc23..e85794cf5 100644 --- a/sections/forums/sticky_post.php +++ b/sections/forums/sticky_post.php @@ -1,8 +1,8 @@ -query(" - SELECT - CEIL(COUNT(ID)/".POSTS_PER_PAGE.") AS Pages, - CEIL(SUM(IF(ID<=$PostID,1,0))/".POSTS_PER_PAGE.") AS Page - FROM forums_posts - WHERE TopicID=$ThreadID - GROUP BY TopicID"); + SELECT + CEIL(COUNT(ID)/".POSTS_PER_PAGE.") AS Pages, + CEIL(SUM(IF(ID<=$PostID,1,0))/".POSTS_PER_PAGE.") AS Page + FROM forums_posts + WHERE TopicID=$ThreadID + GROUP BY TopicID"); if ($DB->has_results()) { - list($Pages,$Page) = $DB->next_record(); - if ($Delete) { - $DB->query(" - UPDATE forums_topics - SET StickyPostID = 0 - WHERE ID = $ThreadID"); - Forums::add_topic_note($ThreadID, "Post $PostID unstickied"); - } else { - $DB->query(" - UPDATE forums_topics - SET StickyPostID = $PostID - WHERE ID = $ThreadID"); - Forums::add_topic_note($ThreadID, "Post $PostID stickied"); - } - $Cache->delete_value('thread_'.$ThreadID.'_info'); - $ThisCatalogue = floor((POSTS_PER_PAGE * $Page - POSTS_PER_PAGE) / THREAD_CATALOGUE); - $LastCatalogue = floor((POSTS_PER_PAGE * $Pages - POSTS_PER_PAGE) / THREAD_CATALOGUE); - for ($i = $ThisCatalogue; $i <= $LastCatalogue; $i++) { - $Cache->delete_value('thread_'.$ThreadID.'_catalogue_'.$i); - } + list($Pages,$Page) = $DB->next_record(); + if ($Delete) { + $DB->query(" + UPDATE forums_topics + SET StickyPostID = 0 + WHERE ID = $ThreadID"); + Forums::add_topic_note($ThreadID, "Post $PostID unstickied"); + } else { + $DB->query(" + UPDATE forums_topics + SET StickyPostID = $PostID + WHERE ID = $ThreadID"); + Forums::add_topic_note($ThreadID, "Post $PostID stickied"); + } + $Cache->delete_value('thread_'.$ThreadID.'_info'); + $ThisCatalogue = floor((POSTS_PER_PAGE * $Page - POSTS_PER_PAGE) / THREAD_CATALOGUE); + $LastCatalogue = floor((POSTS_PER_PAGE * $Pages - POSTS_PER_PAGE) / THREAD_CATALOGUE); + for ($i = $ThisCatalogue; $i <= $LastCatalogue; $i++) { + $Cache->delete_value('thread_'.$ThreadID.'_catalogue_'.$i); + } } header('Location: forums.php?action=viewthread&threadid='.$ThreadID); diff --git a/sections/forums/take_new_thread.php b/sections/forums/take_new_thread.php index ff347db59..5446d279f 100644 --- a/sections/forums/take_new_thread.php +++ b/sections/forums/take_new_thread.php @@ -3,43 +3,43 @@ /* 'new' if the user is creating a new thread - It will be accompanied with: - $_POST['forum'] - $_POST['title'] - $_POST['body'] - - and optionally include: - $_POST['question'] - $_POST['answers'] - the latter of which is an array + It will be accompanied with: + $_POST['forum'] + $_POST['title'] + $_POST['body'] + + and optionally include: + $_POST['question'] + $_POST['answers'] + the latter of which is an array */ if (isset($LoggedUser['PostsPerPage'])) { - $PerPage = $LoggedUser['PostsPerPage']; + $PerPage = $LoggedUser['PostsPerPage']; } else { - $PerPage = POSTS_PER_PAGE; + $PerPage = POSTS_PER_PAGE; } if (isset($_POST['thread']) && !is_number($_POST['thread'])) { - error(0); + error(0); } if (isset($_POST['forum']) && !is_number($_POST['forum'])) { - error(0); + error(0); } // If you're not sending anything, go back if (empty($_POST['body']) || empty($_POST['title'])) { - $Location = (empty($_SERVER['HTTP_REFERER'])) ? "forums.php?action=viewforum&forumid={$_POST['forum']}": $_SERVER['HTTP_REFERER']; - header("Location: {$Location}"); - die(); + $Location = (empty($_SERVER['HTTP_REFERER'])) ? "forums.php?action=viewforum&forumid={$_POST['forum']}": $_SERVER['HTTP_REFERER']; + header("Location: {$Location}"); + die(); } $Body = $_POST['body']; if ($LoggedUser['DisablePosting']) { - error('Your posting privileges have been removed.'); + error('Your posting privileges have been removed.'); } $Title = Format::cut_string(trim($_POST['title']), 150, 1, 0); @@ -48,151 +48,151 @@ $ForumID = $_POST['forum']; if (!isset($Forums[$ForumID])) { - error(404); + error(404); } if (!Forums::check_forumperm($ForumID, 'Write') || !Forums::check_forumperm($ForumID, 'Create')) { - error(403); + error(403); } if (empty($_POST['question']) || empty($_POST['answers']) || !check_perms('forums_polls_create')) { - $NoPoll = 1; + $NoPoll = 1; } else { - $NoPoll = 0; - $Question = trim($_POST['question']); - $Answers = array(); - $Votes = array(); - - //This can cause polls to have answer IDs of 1 3 4 if the second box is empty - foreach ($_POST['answers'] as $i => $Answer) { - if ($Answer == '') { - continue; - } - $Answers[$i + 1] = $Answer; - $Votes[$i + 1] = 0; - } - - if (count($Answers) < 2) { - error('You cannot create a poll with only one answer.'); - } elseif (count($Answers) > 25) { - error('You cannot create a poll with greater than 25 answers.'); - } + $NoPoll = 0; + $Question = trim($_POST['question']); + $Answers = []; + $Votes = []; + + //This can cause polls to have answer IDs of 1 3 4 if the second box is empty + foreach ($_POST['answers'] as $i => $Answer) { + if ($Answer == '') { + continue; + } + $Answers[$i + 1] = $Answer; + $Votes[$i + 1] = 0; + } + + if (count($Answers) < 2) { + error('You cannot create a poll with only one answer.'); + } elseif (count($Answers) > 25) { + error('You cannot create a poll with greater than 25 answers.'); + } } $sqltime = sqltime(); $DB->query(" - INSERT INTO forums_topics - (Title, AuthorID, ForumID, LastPostTime, LastPostAuthorID, CreatedTime) - Values - ('".db_string($Title)."', '".$LoggedUser['ID']."', '$ForumID', '".$sqltime."', '".$LoggedUser['ID']."', '".$sqltime."')"); + INSERT INTO forums_topics + (Title, AuthorID, ForumID, LastPostTime, LastPostAuthorID, CreatedTime) + Values + ('".db_string($Title)."', '".$LoggedUser['ID']."', '$ForumID', '".$sqltime."', '".$LoggedUser['ID']."', '".$sqltime."')"); $TopicID = $DB->inserted_id(); $DB->query(" - INSERT INTO forums_posts - (TopicID, AuthorID, AddedTime, Body) - VALUES - ('$TopicID', '".$LoggedUser['ID']."', '".$sqltime."', '".db_string($Body)."')"); + INSERT INTO forums_posts + (TopicID, AuthorID, AddedTime, Body) + VALUES + ('$TopicID', '".$LoggedUser['ID']."', '".$sqltime."', '".db_string($Body)."')"); $PostID = $DB->inserted_id(); $DB->query(" - UPDATE forums - SET - NumPosts = NumPosts + 1, - NumTopics = NumTopics + 1, - LastPostID = '$PostID', - LastPostAuthorID = '".$LoggedUser['ID']."', - LastPostTopicID = '$TopicID', - LastPostTime = '".$sqltime."' - WHERE ID = '$ForumID'"); + UPDATE forums + SET + NumPosts = NumPosts + 1, + NumTopics = NumTopics + 1, + LastPostID = '$PostID', + LastPostAuthorID = '".$LoggedUser['ID']."', + LastPostTopicID = '$TopicID', + LastPostTime = '".$sqltime."' + WHERE ID = '$ForumID'"); $DB->query(" - UPDATE forums_topics - SET - NumPosts = NumPosts + 1, - LastPostID = '$PostID', - LastPostAuthorID = '".$LoggedUser['ID']."', - LastPostTime = '".$sqltime."' - WHERE ID = '$TopicID'"); + UPDATE forums_topics + SET + NumPosts = NumPosts + 1, + LastPostID = '$PostID', + LastPostAuthorID = '".$LoggedUser['ID']."', + LastPostTime = '".$sqltime."' + WHERE ID = '$TopicID'"); if (isset($_POST['subscribe'])) { - Subscriptions::subscribe($TopicID); + Subscriptions::subscribe($TopicID); } if (!$NoPoll) { // god, I hate double negatives... - $DB->query(" - INSERT INTO forums_polls - (TopicID, Question, Answers) - VALUES - ('$TopicID', '".db_string($Question)."', '".db_string(serialize($Answers))."')"); - $Cache->cache_value("polls_$TopicID", array($Question, $Answers, $Votes, '0000-00-00 00:00:00', '0'), 0); - - if ($ForumID == STAFF_FORUM) { - send_irc('PRIVMSG '.ADMIN_CHAN.' :!mod Poll created by '.$LoggedUser['Username'].": \"$Question\" ".site_url()."forums.php?action=viewthread&threadid=$TopicID"); - } + $DB->query(" + INSERT INTO forums_polls + (TopicID, Question, Answers) + VALUES + ('$TopicID', '".db_string($Question)."', '".db_string(serialize($Answers))."')"); + $Cache->cache_value("polls_$TopicID", array($Question, $Answers, $Votes, '0000-00-00 00:00:00', '0'), 0); + + if ($ForumID == STAFF_FORUM) { + send_irc('PRIVMSG '.ADMIN_CHAN.' :!mod Poll created by '.$LoggedUser['Username'].": \"$Question\" ".site_url()."forums.php?action=viewthread&threadid=$TopicID"); + } } // if cache exists modify it, if not, then it will be correct when selected next, and we can skip this block if ($Forum = $Cache->get_value("forums_$ForumID")) { - list($Forum,,,$Stickies) = $Forum; - - // Remove the last thread from the index - if (count($Forum) == TOPICS_PER_PAGE && $Stickies < TOPICS_PER_PAGE) { - array_pop($Forum); - } - - if ($Stickies > 0) { - $Part1 = array_slice($Forum, 0, $Stickies, true); // Stickies - $Part3 = array_slice($Forum, $Stickies, TOPICS_PER_PAGE - $Stickies - 1, true); // Rest of page - } else { - $Part1 = array(); - $Part3 = $Forum; - } - $Part2 = array($TopicID => array( - 'ID' => $TopicID, - 'Title' => $Title, - 'AuthorID' => $LoggedUser['ID'], - 'IsLocked' => 0, - 'IsSticky' => 0, - 'NumPosts' => 1, - 'LastPostID' => $PostID, - 'LastPostTime' => $sqltime, - 'LastPostAuthorID' => $LoggedUser['ID'], - 'NoPoll' => $NoPoll - )); // Bumped - $Forum = $Part1 + $Part2 + $Part3; - - $Cache->cache_value("forums_$ForumID", array($Forum, '', 0, $Stickies), 0); - - // Update the forum root - $Cache->begin_transaction('forums_list'); - $Cache->update_row($ForumID, array( - 'NumPosts' => '+1', - 'NumTopics' => '+1', - 'LastPostID' => $PostID, - 'LastPostAuthorID' => $LoggedUser['ID'], - 'LastPostTopicID' => $TopicID, - 'LastPostTime' => $sqltime, - 'Title' => $Title, - 'IsLocked' => 0, - 'IsSticky' => 0 - )); - $Cache->commit_transaction(0); + list($Forum,,,$Stickies) = $Forum; + + // Remove the last thread from the index + if (count($Forum) == TOPICS_PER_PAGE && $Stickies < TOPICS_PER_PAGE) { + array_pop($Forum); + } + + if ($Stickies > 0) { + $Part1 = array_slice($Forum, 0, $Stickies, true); // Stickies + $Part3 = array_slice($Forum, $Stickies, TOPICS_PER_PAGE - $Stickies - 1, true); // Rest of page + } else { + $Part1 = []; + $Part3 = $Forum; + } + $Part2 = array($TopicID => array( + 'ID' => $TopicID, + 'Title' => $Title, + 'AuthorID' => $LoggedUser['ID'], + 'IsLocked' => 0, + 'IsSticky' => 0, + 'NumPosts' => 1, + 'LastPostID' => $PostID, + 'LastPostTime' => $sqltime, + 'LastPostAuthorID' => $LoggedUser['ID'], + 'NoPoll' => $NoPoll + )); // Bumped + $Forum = $Part1 + $Part2 + $Part3; + + $Cache->cache_value("forums_$ForumID", array($Forum, '', 0, $Stickies), 0); + + // Update the forum root + $Cache->begin_transaction('forums_list'); + $Cache->update_row($ForumID, array( + 'NumPosts' => '+1', + 'NumTopics' => '+1', + 'LastPostID' => $PostID, + 'LastPostAuthorID' => $LoggedUser['ID'], + 'LastPostTopicID' => $TopicID, + 'LastPostTime' => $sqltime, + 'Title' => $Title, + 'IsLocked' => 0, + 'IsSticky' => 0 + )); + $Cache->commit_transaction(0); } else { - // If there's no cache, we have no data, and if there's no data - $Cache->delete_value('forums_list'); + // If there's no cache, we have no data, and if there's no data + $Cache->delete_value('forums_list'); } $Cache->begin_transaction("thread_$TopicID".'_catalogue_0'); $Post = array( - 'ID' => $PostID, - 'AuthorID' => $LoggedUser['ID'], - 'AddedTime' => $sqltime, - 'Body' => $Body, - 'EditedUserID' => 0, - 'EditedTime' => '0000-00-00 00:00:00' - ); + 'ID' => $PostID, + 'AuthorID' => $LoggedUser['ID'], + 'AddedTime' => $sqltime, + 'Body' => $Body, + 'EditedUserID' => 0, + 'EditedTime' => '0000-00-00 00:00:00' + ); $Cache->insert('', $Post); $Cache->commit_transaction(0); diff --git a/sections/forums/take_reply.php b/sections/forums/take_reply.php index a7d8a9dd3..f20c15e61 100644 --- a/sections/forums/take_reply.php +++ b/sections/forums/take_reply.php @@ -12,9 +12,9 @@ $_POST['action'] is what the user is trying to do. It can be: 'reply' if the user is replying to a thread - It will be accompanied with: - $_POST['thread'] - $_POST['body'] + It will be accompanied with: + $_POST['thread'] + $_POST['body'] \*********************************************************************/ @@ -22,247 +22,247 @@ // Quick SQL injection checks if (isset($LoggedUser['PostsPerPage'])) { - $PerPage = $LoggedUser['PostsPerPage']; + $PerPage = $LoggedUser['PostsPerPage']; } else { - $PerPage = POSTS_PER_PAGE; + $PerPage = POSTS_PER_PAGE; } if (isset($_POST['thread']) && !is_number($_POST['thread'])) { - error(0); + error(0); } if (isset($_POST['forum']) && !is_number($_POST['forum'])) { - error(0); + error(0); } // If you're not sending anything, go back if ($_POST['body'] === '' || !isset($_POST['body'])) { - $Location = empty($_SERVER['HTTP_REFERER']) ? "forums.php?action=viewthread&threadid={$_POST['thread']}" : $_SERVER['HTTP_REFERER']; - header("Location: {$Location}"); - die(); + $Location = empty($_SERVER['HTTP_REFERER']) ? "forums.php?action=viewthread&threadid={$_POST['thread']}" : $_SERVER['HTTP_REFERER']; + header("Location: {$Location}"); + die(); } $Body = $_POST['body']; if (!empty($LoggedUser['DisablePosting'])) { - error('Your posting privileges have been removed.'); + error('Your posting privileges have been removed.'); } $TopicID = $_POST['thread']; $ThreadInfo = Forums::get_thread_info($TopicID); if ($ThreadInfo === null) { - error(404); + error(404); } $ForumID = $ThreadInfo['ForumID']; $SQLTime = sqltime(); if (!Forums::check_forumperm($ForumID)) { - error(403); + error(403); } if (!Forums::check_forumperm($ForumID, 'Write') || $LoggedUser['DisablePosting'] || $ThreadInfo['IsLocked'] == '1' && !check_perms('site_moderate_forums')) { - error(403); + error(403); } if (isset($_POST['subscribe']) && Subscriptions::has_subscribed($TopicID) === false) { - Subscriptions::subscribe($TopicID); + Subscriptions::subscribe($TopicID); } //Now lets handle the special case of merging posts, we can skip bumping the thread and all that fun if ($ThreadInfo['LastPostAuthorID'] == $LoggedUser['ID'] && isset($_POST['merge'])) { - //Get the id for this post in the database to append - $DB->query(" - SELECT ID, Body - FROM forums_posts - WHERE TopicID = '$TopicID' - AND AuthorID = '".$LoggedUser['ID']."' - ORDER BY ID DESC - LIMIT 1"); - list($PostID, $OldBody) = $DB->next_record(MYSQLI_NUM, false); - - //Edit the post - $DB->query(" - UPDATE forums_posts - SET - Body = CONCAT(Body,'\n\n".db_string($Body)."'), - EditedUserID = '".$LoggedUser['ID']."', - EditedTime = '$SQLTime' - WHERE ID = '$PostID'"); - - //Store edit history - $DB->query(" - INSERT INTO comments_edits - (Page, PostID, EditUser, EditTime, Body) - VALUES - ('forums', $PostID, ".$LoggedUser['ID'].", '$SQLTime', '".db_string($OldBody)."')"); - $Cache->delete_value("forums_edits_$PostID"); - - //Get the catalogue it is in - $CatalogueID = floor((POSTS_PER_PAGE * ceil($ThreadInfo['Posts'] / POSTS_PER_PAGE) - POSTS_PER_PAGE) / THREAD_CATALOGUE); - - //Get the catalogue value for the post we're appending to - if ($ThreadInfo['Posts'] % THREAD_CATALOGUE == 0) { - $Key = THREAD_CATALOGUE - 1; - } else { - $Key = ($ThreadInfo['Posts'] % THREAD_CATALOGUE) - 1; - } - if ($ThreadInfo['StickyPostID'] == $PostID) { - $ThreadInfo['StickyPost']['Body'] .= "\n\n$Body"; - $ThreadInfo['StickyPost']['EditedUserID'] = $LoggedUser['ID']; - $ThreadInfo['StickyPost']['EditedTime'] = $SQLTime; - $Cache->cache_value("thread_$TopicID".'_info', $ThreadInfo, 0); - } - - //Edit the post in the cache - $Cache->begin_transaction("thread_$TopicID"."_catalogue_$CatalogueID"); - $Cache->update_row($Key, array( - 'Body' => $Cache->MemcacheDBArray[$Key]['Body']."\n\n$Body", - 'EditedUserID' => $LoggedUser['ID'], - 'EditedTime' => $SQLTime, - 'Username' => $LoggedUser['Username'] - )); - $Cache->commit_transaction(0); + //Get the id for this post in the database to append + $DB->query(" + SELECT ID, Body + FROM forums_posts + WHERE TopicID = '$TopicID' + AND AuthorID = '".$LoggedUser['ID']."' + ORDER BY ID DESC + LIMIT 1"); + list($PostID, $OldBody) = $DB->next_record(MYSQLI_NUM, false); + + //Edit the post + $DB->query(" + UPDATE forums_posts + SET + Body = CONCAT(Body,'\n\n".db_string($Body)."'), + EditedUserID = '".$LoggedUser['ID']."', + EditedTime = '$SQLTime' + WHERE ID = '$PostID'"); + + //Store edit history + $DB->query(" + INSERT INTO comments_edits + (Page, PostID, EditUser, EditTime, Body) + VALUES + ('forums', $PostID, ".$LoggedUser['ID'].", '$SQLTime', '".db_string($OldBody)."')"); + $Cache->delete_value("forums_edits_$PostID"); + + //Get the catalogue it is in + $CatalogueID = floor((POSTS_PER_PAGE * ceil($ThreadInfo['Posts'] / POSTS_PER_PAGE) - POSTS_PER_PAGE) / THREAD_CATALOGUE); + + //Get the catalogue value for the post we're appending to + if ($ThreadInfo['Posts'] % THREAD_CATALOGUE == 0) { + $Key = THREAD_CATALOGUE - 1; + } else { + $Key = ($ThreadInfo['Posts'] % THREAD_CATALOGUE) - 1; + } + if ($ThreadInfo['StickyPostID'] == $PostID) { + $ThreadInfo['StickyPost']['Body'] .= "\n\n$Body"; + $ThreadInfo['StickyPost']['EditedUserID'] = $LoggedUser['ID']; + $ThreadInfo['StickyPost']['EditedTime'] = $SQLTime; + $Cache->cache_value("thread_$TopicID".'_info', $ThreadInfo, 0); + } + + //Edit the post in the cache + $Cache->begin_transaction("thread_$TopicID"."_catalogue_$CatalogueID"); + $Cache->update_row($Key, array( + 'Body' => $Cache->MemcacheDBArray[$Key]['Body']."\n\n$Body", + 'EditedUserID' => $LoggedUser['ID'], + 'EditedTime' => $SQLTime, + 'Username' => $LoggedUser['Username'] + )); + $Cache->commit_transaction(0); //Now we're dealing with a normal post } else { - //Insert the post into the posts database - $DB->query(" - INSERT INTO forums_posts (TopicID, AuthorID, AddedTime, Body) - VALUES ('$TopicID', '".$LoggedUser['ID']."', '$SQLTime', '".db_string($Body)."')"); - - $PostID = $DB->inserted_id(); - - //This updates the root index - $DB->query(" - UPDATE forums - SET - NumPosts = NumPosts + 1, - LastPostID = '$PostID', - LastPostAuthorID = '".$LoggedUser['ID']."', - LastPostTopicID = '$TopicID', - LastPostTime = '$SQLTime' - WHERE ID = '$ForumID'"); - - //Update the topic - $DB->query(" - UPDATE forums_topics - SET - NumPosts = NumPosts + 1, - LastPostID = '$PostID', - LastPostAuthorID = '".$LoggedUser['ID']."', - LastPostTime = '$SQLTime' - WHERE ID = '$TopicID'"); - - // if cache exists modify it, if not, then it will be correct when selected next, and we can skip this block - if ($Forum = $Cache->get_value("forums_$ForumID")) { - list($Forum,,,$Stickies) = $Forum; - - // if the topic is already on this page - if (array_key_exists($TopicID, $Forum)) { - $Thread = $Forum[$TopicID]; - unset($Forum[$TopicID]); - $Thread['NumPosts'] = $Thread['NumPosts'] + 1; // Increment post count - $Thread['LastPostID'] = $PostID; // Set post ID for read/unread - $Thread['LastPostTime'] = $SQLTime; // Time of last post - $Thread['LastPostAuthorID'] = $LoggedUser['ID']; // Last poster ID - $Part2 = array($TopicID => $Thread); // Bumped thread - - // if we're bumping from an older page - } else { - // Remove the last thread from the index - if (count($Forum) == TOPICS_PER_PAGE && $Stickies < TOPICS_PER_PAGE) { - array_pop($Forum); - } - // Never know if we get a page full of stickies... - if ($Stickies < TOPICS_PER_PAGE || $ThreadInfo['IsSticky'] == 1) { - //Pull the data for the thread we're bumping - $DB->query(" - SELECT - f.AuthorID, - f.IsLocked, - f.IsSticky, - f.NumPosts, - ISNULL(p.TopicID) AS NoPoll - FROM forums_topics AS f - LEFT JOIN forums_polls AS p ON p.TopicID = f.ID - WHERE f.ID = '$TopicID'"); - list($AuthorID, $IsLocked, $IsSticky, $NumPosts, $NoPoll) = $DB->next_record(); - $Part2 = array($TopicID => array( - 'ID' => $TopicID, - 'Title' => $ThreadInfo['Title'], - 'AuthorID' => $AuthorID, - 'IsLocked' => $IsLocked, - 'IsSticky' => $IsSticky, - 'NumPosts' => $NumPosts, - 'LastPostID' => $PostID, - 'LastPostTime' => $SQLTime, - 'LastPostAuthorID' => $LoggedUser['ID'], - 'NoPoll' => $NoPoll - )); //Bumped - } else { - $Part2 = array(); - } - } - if ($Stickies > 0) { - $Part1 = array_slice($Forum, 0, $Stickies, true); //Stickies - $Part3 = array_slice($Forum, $Stickies, TOPICS_PER_PAGE - $Stickies - 1, true); //Rest of page - } else { - $Part1 = array(); - $Part3 = $Forum; - } - if (is_null($Part1)) { - $Part1 = array(); - } - if (is_null($Part3)) { - $Part3 = array(); - } - if ($ThreadInfo['IsSticky'] == 1) { - $Forum = $Part2 + $Part1 + $Part3; //Merge it - } else { - $Forum = $Part1 + $Part2 + $Part3; //Merge it - } - $Cache->cache_value("forums_$ForumID", array($Forum, '', 0, $Stickies), 0); - - //Update the forum root - $Cache->begin_transaction('forums_list'); - $Cache->update_row($ForumID, array( - 'NumPosts'=>'+1', - 'LastPostID'=>$PostID, - 'LastPostAuthorID'=>$LoggedUser['ID'], - 'LastPostTopicID'=>$TopicID, - 'LastPostTime'=>$SQLTime, - 'Title'=>$ThreadInfo['Title'], - 'IsLocked'=>$ThreadInfo['IsLocked'], - 'IsSticky'=>$ThreadInfo['IsSticky'] - )); - $Cache->commit_transaction(0); - } else { - //If there's no cache, we have no data, and if there's no data - $Cache->delete_value('forums_list'); - } - - - //This calculates the block of 500 posts that this one will fall under - $CatalogueID = floor((POSTS_PER_PAGE * ceil($ThreadInfo['Posts'] / POSTS_PER_PAGE) - POSTS_PER_PAGE) / THREAD_CATALOGUE); - - //Insert the post into the thread catalogue (block of 500 posts) - $Cache->begin_transaction("thread_$TopicID"."_catalogue_$CatalogueID"); - $Cache->insert('', array( - 'ID'=>$PostID, - 'AuthorID'=>$LoggedUser['ID'], - 'AddedTime'=>$SQLTime, - 'Body'=>$Body, - 'EditedUserID'=>0, - 'EditedTime'=>'0000-00-00 00:00:00', - 'Username'=>$LoggedUser['Username'] //TODO: Remove, it's never used? - )); - $Cache->commit_transaction(0); - - //Update the thread info - $Cache->begin_transaction("thread_$TopicID".'_info'); - $Cache->update_row(false, array('Posts' => '+1', 'LastPostAuthorID' => $LoggedUser['ID'], 'LastPostTime' => $SQLTime)); - $Cache->commit_transaction(0); - - //Increment this now to make sure we redirect to the correct page - $ThreadInfo['Posts']++; + //Insert the post into the posts database + $DB->query(" + INSERT INTO forums_posts (TopicID, AuthorID, AddedTime, Body) + VALUES ('$TopicID', '".$LoggedUser['ID']."', '$SQLTime', '".db_string($Body)."')"); + + $PostID = $DB->inserted_id(); + + //This updates the root index + $DB->query(" + UPDATE forums + SET + NumPosts = NumPosts + 1, + LastPostID = '$PostID', + LastPostAuthorID = '".$LoggedUser['ID']."', + LastPostTopicID = '$TopicID', + LastPostTime = '$SQLTime' + WHERE ID = '$ForumID'"); + + //Update the topic + $DB->query(" + UPDATE forums_topics + SET + NumPosts = NumPosts + 1, + LastPostID = '$PostID', + LastPostAuthorID = '".$LoggedUser['ID']."', + LastPostTime = '$SQLTime' + WHERE ID = '$TopicID'"); + + // if cache exists modify it, if not, then it will be correct when selected next, and we can skip this block + if ($Forum = $Cache->get_value("forums_$ForumID")) { + list($Forum,,,$Stickies) = $Forum; + + // if the topic is already on this page + if (array_key_exists($TopicID, $Forum)) { + $Thread = $Forum[$TopicID]; + unset($Forum[$TopicID]); + $Thread['NumPosts'] = $Thread['NumPosts'] + 1; // Increment post count + $Thread['LastPostID'] = $PostID; // Set post ID for read/unread + $Thread['LastPostTime'] = $SQLTime; // Time of last post + $Thread['LastPostAuthorID'] = $LoggedUser['ID']; // Last poster ID + $Part2 = array($TopicID => $Thread); // Bumped thread + + // if we're bumping from an older page + } else { + // Remove the last thread from the index + if (count($Forum) == TOPICS_PER_PAGE && $Stickies < TOPICS_PER_PAGE) { + array_pop($Forum); + } + // Never know if we get a page full of stickies... + if ($Stickies < TOPICS_PER_PAGE || $ThreadInfo['IsSticky'] == 1) { + //Pull the data for the thread we're bumping + $DB->query(" + SELECT + f.AuthorID, + f.IsLocked, + f.IsSticky, + f.NumPosts, + ISNULL(p.TopicID) AS NoPoll + FROM forums_topics AS f + LEFT JOIN forums_polls AS p ON p.TopicID = f.ID + WHERE f.ID = '$TopicID'"); + list($AuthorID, $IsLocked, $IsSticky, $NumPosts, $NoPoll) = $DB->next_record(); + $Part2 = array($TopicID => array( + 'ID' => $TopicID, + 'Title' => $ThreadInfo['Title'], + 'AuthorID' => $AuthorID, + 'IsLocked' => $IsLocked, + 'IsSticky' => $IsSticky, + 'NumPosts' => $NumPosts, + 'LastPostID' => $PostID, + 'LastPostTime' => $SQLTime, + 'LastPostAuthorID' => $LoggedUser['ID'], + 'NoPoll' => $NoPoll + )); //Bumped + } else { + $Part2 = []; + } + } + if ($Stickies > 0) { + $Part1 = array_slice($Forum, 0, $Stickies, true); //Stickies + $Part3 = array_slice($Forum, $Stickies, TOPICS_PER_PAGE - $Stickies - 1, true); //Rest of page + } else { + $Part1 = []; + $Part3 = $Forum; + } + if (is_null($Part1)) { + $Part1 = []; + } + if (is_null($Part3)) { + $Part3 = []; + } + if ($ThreadInfo['IsSticky'] == 1) { + $Forum = $Part2 + $Part1 + $Part3; //Merge it + } else { + $Forum = $Part1 + $Part2 + $Part3; //Merge it + } + $Cache->cache_value("forums_$ForumID", array($Forum, '', 0, $Stickies), 0); + + //Update the forum root + $Cache->begin_transaction('forums_list'); + $Cache->update_row($ForumID, array( + 'NumPosts'=>'+1', + 'LastPostID'=>$PostID, + 'LastPostAuthorID'=>$LoggedUser['ID'], + 'LastPostTopicID'=>$TopicID, + 'LastPostTime'=>$SQLTime, + 'Title'=>$ThreadInfo['Title'], + 'IsLocked'=>$ThreadInfo['IsLocked'], + 'IsSticky'=>$ThreadInfo['IsSticky'] + )); + $Cache->commit_transaction(0); + } else { + //If there's no cache, we have no data, and if there's no data + $Cache->delete_value('forums_list'); + } + + + //This calculates the block of 500 posts that this one will fall under + $CatalogueID = floor((POSTS_PER_PAGE * ceil($ThreadInfo['Posts'] / POSTS_PER_PAGE) - POSTS_PER_PAGE) / THREAD_CATALOGUE); + + //Insert the post into the thread catalogue (block of 500 posts) + $Cache->begin_transaction("thread_$TopicID"."_catalogue_$CatalogueID"); + $Cache->insert('', array( + 'ID'=>$PostID, + 'AuthorID'=>$LoggedUser['ID'], + 'AddedTime'=>$SQLTime, + 'Body'=>$Body, + 'EditedUserID'=>0, + 'EditedTime'=>'0000-00-00 00:00:00', + 'Username'=>$LoggedUser['Username'] //TODO: Remove, it's never used? + )); + $Cache->commit_transaction(0); + + //Update the thread info + $Cache->begin_transaction("thread_$TopicID".'_info'); + $Cache->update_row(false, array('Posts' => '+1', 'LastPostAuthorID' => $LoggedUser['ID'], 'LastPostTime' => $SQLTime)); + $Cache->commit_transaction(0); + + //Increment this now to make sure we redirect to the correct page + $ThreadInfo['Posts']++; } Subscriptions::flush_subscriptions('forums', $TopicID); diff --git a/sections/forums/take_topic_notes.php b/sections/forums/take_topic_notes.php index 11ebcf9be..b7ceb0acc 100644 --- a/sections/forums/take_topic_notes.php +++ b/sections/forums/take_topic_notes.php @@ -1,12 +1,12 @@ - $LoggedUser['Class']) { - error(403); + error(403); } $URL = site_url() . "forums.php?action=viewthread&postid=$PostID#post$PostID"; if ($WarningLength !== 'verbal') { - $Time = (int)$WarningLength * (7 * 24 * 60 * 60); - Tools::warn_user($UserID, $Time, "$URL - $Reason"); - $Subject = 'You have received a warning'; - $PrivateMessage = "You have received a $WarningLength week warning for [url=$URL]this post[/url].\n\n" . $PrivateMessage; + $Time = (int)$WarningLength * (7 * 24 * 60 * 60); + Tools::warn_user($UserID, $Time, "$URL - $Reason"); + $Subject = 'You have received a warning'; + $PrivateMessage = "You have received a $WarningLength week warning for [url=$URL]this post[/url].\n\n" . $PrivateMessage; - $WarnTime = time_plus($Time); - $AdminComment = date('Y-m-d') . " - Warned until $WarnTime by " . $LoggedUser['Username'] . " for $URL\nReason: $Reason\n\n"; + $WarnTime = time_plus($Time); + $AdminComment = date('Y-m-d') . " - Warned until $WarnTime by " . $LoggedUser['Username'] . " for $URL\nReason: $Reason\n\n"; } else { - $Subject = 'You have received a verbal warning'; - $PrivateMessage = "You have received a verbal warning for [url=$URL]this post[/url].\n\n" . $PrivateMessage; - $AdminComment = date('Y-m-d') . ' - Verbally warned by ' . $LoggedUser['Username'] . " for $URL\nReason: $Reason\n\n"; - Tools::update_user_notes($UserID, $AdminComment); + $Subject = 'You have received a verbal warning'; + $PrivateMessage = "You have received a verbal warning for [url=$URL]this post[/url].\n\n" . $PrivateMessage; + $AdminComment = date('Y-m-d') . ' - Verbally warned by ' . $LoggedUser['Username'] . " for $URL\nReason: $Reason\n\n"; + Tools::update_user_notes($UserID, $AdminComment); } $DB->query(" - INSERT INTO users_warnings_forums - (UserID, Comment) - VALUES - ('$UserID', '" . db_string($AdminComment) . "') - ON DUPLICATE KEY UPDATE - Comment = CONCAT('" . db_string($AdminComment) . "', Comment)"); + INSERT INTO users_warnings_forums + (UserID, Comment) + VALUES + ('$UserID', '" . db_string($AdminComment) . "') + ON DUPLICATE KEY UPDATE + Comment = CONCAT('" . db_string($AdminComment) . "', Comment)"); Misc::send_pm($UserID, $LoggedUser['ID'], $Subject, $PrivateMessage); //edit the post $DB->query(" - SELECT - p.Body, - p.AuthorID, - p.TopicID, - t.ForumID, - CEIL( - ( - SELECT COUNT(p2.ID) - FROM forums_posts AS p2 - WHERE p2.TopicID = p.TopicID - AND p2.ID <= '$PostID' - ) / " . POSTS_PER_PAGE . " - ) AS Page - FROM forums_posts AS p - JOIN forums_topics AS t ON p.TopicID = t.ID - JOIN forums AS f ON t.ForumID = f.ID - WHERE p.ID = '$PostID'"); + SELECT + p.Body, + p.AuthorID, + p.TopicID, + t.ForumID, + CEIL( + ( + SELECT COUNT(p2.ID) + FROM forums_posts AS p2 + WHERE p2.TopicID = p.TopicID + AND p2.ID <= '$PostID' + ) / " . POSTS_PER_PAGE . " + ) AS Page + FROM forums_posts AS p + JOIN forums_topics AS t ON p.TopicID = t.ID + JOIN forums AS f ON t.ForumID = f.ID + WHERE p.ID = '$PostID'"); list($OldBody, $AuthorID, $TopicID, $ForumID, $Page) = $DB->next_record(); // Perform the update $DB->query(" - UPDATE forums_posts - SET Body = '" . db_string($Body) . "', - EditedUserID = '$UserID', - EditedTime = '$SQLTime' - WHERE ID = '$PostID'"); + UPDATE forums_posts + SET Body = '" . db_string($Body) . "', + EditedUserID = '$UserID', + EditedTime = '$SQLTime' + WHERE ID = '$PostID'"); $CatalogueID = floor((POSTS_PER_PAGE * $Page - POSTS_PER_PAGE) / THREAD_CATALOGUE); $Cache->begin_transaction("thread_$TopicID" . "_catalogue_$CatalogueID"); if ($Cache->MemcacheDBArray[$Key]['ID'] != $PostID) { - $Cache->cancel_transaction(); - $Cache->delete_value("thread_$TopicID" . "_catalogue_$CatalogueID"); - //just clear the cache for would be cache-screwer-uppers + $Cache->cancel_transaction(); + $Cache->delete_value("thread_$TopicID" . "_catalogue_$CatalogueID"); + //just clear the cache for would be cache-screwer-uppers } else { - $Cache->update_row($Key, array( - 'ID' => $Cache->MemcacheDBArray[$Key]['ID'], - 'AuthorID' => $Cache->MemcacheDBArray[$Key]['AuthorID'], - 'AddedTime' => $Cache->MemcacheDBArray[$Key]['AddedTime'], - 'Body' => $Body, //Don't url decode. - 'EditedUserID' => $LoggedUser['ID'], - 'EditedTime' => $SQLTime, - 'Username' => $LoggedUser['Username'])); - $Cache->commit_transaction(3600 * 24 * 5); + $Cache->update_row($Key, array( + 'ID' => $Cache->MemcacheDBArray[$Key]['ID'], + 'AuthorID' => $Cache->MemcacheDBArray[$Key]['AuthorID'], + 'AddedTime' => $Cache->MemcacheDBArray[$Key]['AddedTime'], + 'Body' => $Body, //Don't url decode. + 'EditedUserID' => $LoggedUser['ID'], + 'EditedTime' => $SQLTime, + 'Username' => $LoggedUser['Username'])); + $Cache->commit_transaction(3600 * 24 * 5); } $ThreadInfo = Forums::get_thread_info($TopicID); if ($ThreadInfo === null) { - error(404); + error(404); } if ($ThreadInfo['StickyPostID'] == $PostID) { - $ThreadInfo['StickyPost']['Body'] = $Body; - $ThreadInfo['StickyPost']['EditedUserID'] = $LoggedUser['ID']; - $ThreadInfo['StickyPost']['EditedTime'] = $SQLTime; - $Cache->cache_value("thread_$TopicID" . '_info', $ThreadInfo, 0); + $ThreadInfo['StickyPost']['Body'] = $Body; + $ThreadInfo['StickyPost']['EditedUserID'] = $LoggedUser['ID']; + $ThreadInfo['StickyPost']['EditedTime'] = $SQLTime; + $Cache->cache_value("thread_$TopicID" . '_info', $ThreadInfo, 0); } $DB->query(" - INSERT INTO comments_edits - (Page, PostID, EditUser, EditTime, Body) - VALUES - ('forums', $PostID, $UserID, '$SQLTime', '" . db_string($OldBody) . "')"); + INSERT INTO comments_edits + (Page, PostID, EditUser, EditTime, Body) + VALUES + ('forums', $PostID, $UserID, '$SQLTime', '" . db_string($OldBody) . "')"); $Cache->delete_value("forums_edits_$PostID"); header("Location: forums.php?action=viewthread&postid=$PostID#post$PostID"); diff --git a/sections/forums/takeedit.php b/sections/forums/takeedit.php index 34528ae29..e6710177f 100644 --- a/sections/forums/takeedit.php +++ b/sections/forums/takeedit.php @@ -1,4 +1,4 @@ -query(" - SELECT - p.Body, - p.AuthorID, - p.TopicID, - t.IsLocked, - t.ForumID, - f.MinClassWrite, - CEIL(( - SELECT COUNT(p2.ID) - FROM forums_posts AS p2 - WHERE p2.TopicID = p.TopicID - AND p2.ID <= '$PostID' - ) / ".POSTS_PER_PAGE." - ) AS Page - FROM forums_posts AS p - JOIN forums_topics AS t ON p.TopicID = t.ID - JOIN forums AS f ON t.ForumID = f.ID - WHERE p.ID = '$PostID'"); + SELECT + p.Body, + p.AuthorID, + p.TopicID, + t.IsLocked, + t.ForumID, + f.MinClassWrite, + CEIL(( + SELECT COUNT(p2.ID) + FROM forums_posts AS p2 + WHERE p2.TopicID = p.TopicID + AND p2.ID <= '$PostID' + ) / ".POSTS_PER_PAGE." + ) AS Page + FROM forums_posts AS p + JOIN forums_topics AS t ON p.TopicID = t.ID + JOIN forums AS f ON t.ForumID = f.ID + WHERE p.ID = '$PostID'"); list($OldBody, $AuthorID, $TopicID, $IsLocked, $ForumID, $MinClassWrite, $Page) = $DB->next_record(); // Make sure they aren't trying to edit posts they shouldn't // We use die() here instead of error() because whatever we spit out is displayed to the user in the box where his forum post is if (!Forums::check_forumperm($ForumID, 'Write') || ($IsLocked && !check_perms('site_moderate_forums'))) { - error('Either the thread is locked, or you lack the permission to edit this post.', true); + error('Either the thread is locked, or you lack the permission to edit this post.', true); } if ($UserID != $AuthorID && !check_perms('site_moderate_forums')) { - error(403,true); + error(403,true); } if ($LoggedUser['DisablePosting']) { - error('Your posting privileges have been removed.', true); + error('Your posting privileges have been removed.', true); } if (!$DB->has_results()) { - error(404,true); + error(404,true); } // Send a PM to the user to notify them of the edit if ($UserID != $AuthorID && $DoPM) { - $PMSubject = "Your post #$PostID has been edited"; - $PMurl = site_url()."forums.php?action=viewthread&postid=$PostID#post$PostID"; - $ProfLink = '[url='.site_url()."user.php?id=$UserID]".$LoggedUser['Username'].'[/url]'; - $PMBody = "One of your posts has been edited by $ProfLink: [url]{$PMurl}[/url]"; - Misc::send_pm($AuthorID, 0, $PMSubject, $PMBody); + $PMSubject = "Your post #$PostID has been edited"; + $PMurl = site_url()."forums.php?action=viewthread&postid=$PostID#post$PostID"; + $ProfLink = '[url='.site_url()."user.php?id=$UserID]".$LoggedUser['Username'].'[/url]'; + $PMBody = "One of your posts has been edited by $ProfLink: [url]{$PMurl}[/url]"; + Misc::send_pm($AuthorID, 0, $PMSubject, $PMBody); } // Perform the update $DB->query(" - UPDATE forums_posts - SET - Body = '" . db_string($Body) . "', - EditedUserID = '$UserID', - EditedTime = '$SQLTime' - WHERE ID = '$PostID'"); + UPDATE forums_posts + SET + Body = '" . db_string($Body) . "', + EditedUserID = '$UserID', + EditedTime = '$SQLTime' + WHERE ID = '$PostID'"); $CatalogueID = floor((POSTS_PER_PAGE * $Page - POSTS_PER_PAGE) / THREAD_CATALOGUE); $Cache->begin_transaction("thread_$TopicID"."_catalogue_$CatalogueID"); if ($Cache->MemcacheDBArray[$Key]['ID'] != $PostID) { - $Cache->cancel_transaction(); - $Cache->delete_value("thread_$TopicID"."_catalogue_$CatalogueID"); //just clear the cache for would be cache-screwer-uppers + $Cache->cancel_transaction(); + $Cache->delete_value("thread_$TopicID"."_catalogue_$CatalogueID"); //just clear the cache for would be cache-screwer-uppers } else { - $Cache->update_row($Key, array( - 'ID'=>$Cache->MemcacheDBArray[$Key]['ID'], - 'AuthorID'=>$Cache->MemcacheDBArray[$Key]['AuthorID'], - 'AddedTime'=>$Cache->MemcacheDBArray[$Key]['AddedTime'], - 'Body'=>$Body, //Don't url decode. - 'EditedUserID'=>$LoggedUser['ID'], - 'EditedTime'=>$SQLTime, - 'Username'=>$LoggedUser['Username'] - )); - $Cache->commit_transaction(3600 * 24 * 5); + $Cache->update_row($Key, array( + 'ID'=>$Cache->MemcacheDBArray[$Key]['ID'], + 'AuthorID'=>$Cache->MemcacheDBArray[$Key]['AuthorID'], + 'AddedTime'=>$Cache->MemcacheDBArray[$Key]['AddedTime'], + 'Body'=>$Body, //Don't url decode. + 'EditedUserID'=>$LoggedUser['ID'], + 'EditedTime'=>$SQLTime, + 'Username'=>$LoggedUser['Username'] + )); + $Cache->commit_transaction(3600 * 24 * 5); } $ThreadInfo = Forums::get_thread_info($TopicID); if ($ThreadInfo === null) { - error(404); + error(404); } if ($ThreadInfo['StickyPostID'] == $PostID) { - $ThreadInfo['StickyPost']['Body'] = $Body; - $ThreadInfo['StickyPost']['EditedUserID'] = $LoggedUser['ID']; - $ThreadInfo['StickyPost']['EditedTime'] = $SQLTime; - $Cache->cache_value("thread_$TopicID".'_info', $ThreadInfo, 0); + $ThreadInfo['StickyPost']['Body'] = $Body; + $ThreadInfo['StickyPost']['EditedUserID'] = $LoggedUser['ID']; + $ThreadInfo['StickyPost']['EditedTime'] = $SQLTime; + $Cache->cache_value("thread_$TopicID".'_info', $ThreadInfo, 0); } $DB->query(" - INSERT INTO comments_edits - (Page, PostID, EditUser, EditTime, Body) - VALUES - ('forums', $PostID, $UserID, '$SQLTime', '".db_string($OldBody)."')"); + INSERT INTO comments_edits + (Page, PostID, EditUser, EditTime, Body) + VALUES + ('forums', $PostID, $UserID, '$SQLTime', '".db_string($OldBody)."')"); $Cache->delete_value("forums_edits_$PostID"); // This gets sent to the browser, which echoes it in place of the old body echo Text::full_format($Body); diff --git a/sections/forums/thread.php b/sections/forums/thread.php index cdd442db0..57832908e 100644 --- a/sections/forums/thread.php +++ b/sections/forums/thread.php @@ -3,9 +3,9 @@ /**********|| Page to show individual threads || ********************************\ Things to expect in $_GET: - ThreadID: ID of the forum curently being browsed - page: The page the user's on. - page = 1 is the same as no page + ThreadID: ID of the forum curently being browsed + page: The page the user's on. + page = 1 is the same as no page ********************************************************************************/ @@ -16,31 +16,31 @@ // Check for lame SQL injection attempts if (!isset($_GET['threadid']) || !is_number($_GET['threadid'])) { - if (isset($_GET['topicid']) && is_number($_GET['topicid'])) { - $ThreadID = $_GET['topicid']; - } elseif (isset($_GET['postid']) && is_number($_GET['postid'])) { - $DB->query(" - SELECT TopicID - FROM forums_posts - WHERE ID = $_GET[postid]"); - list($ThreadID) = $DB->next_record(); - if ($ThreadID) { - header("Location: forums.php?action=viewthread&threadid=$ThreadID&postid=$_GET[postid]#post$_GET[postid]"); - die(); - } else { - error(404); - } - } else { - error(404); - } + if (isset($_GET['topicid']) && is_number($_GET['topicid'])) { + $ThreadID = $_GET['topicid']; + } elseif (isset($_GET['postid']) && is_number($_GET['postid'])) { + $DB->query(" + SELECT TopicID + FROM forums_posts + WHERE ID = $_GET[postid]"); + list($ThreadID) = $DB->next_record(); + if ($ThreadID) { + header("Location: forums.php?action=viewthread&threadid=$ThreadID&postid=$_GET[postid]#post$_GET[postid]"); + die(); + } else { + error(404); + } + } else { + error(404); + } } else { - $ThreadID = $_GET['threadid']; + $ThreadID = $_GET['threadid']; } if (isset($LoggedUser['PostsPerPage'])) { - $PerPage = $LoggedUser['PostsPerPage']; + $PerPage = $LoggedUser['PostsPerPage']; } else { - $PerPage = POSTS_PER_PAGE; + $PerPage = POSTS_PER_PAGE; } //---------- Get some data to start processing @@ -48,7 +48,7 @@ // Thread information, constant across all pages $ThreadInfo = Forums::get_thread_info($ThreadID, true, true); if ($ThreadInfo === null) { - error(404); + error(404); } $ForumID = $ThreadInfo['ForumID']; @@ -56,7 +56,7 @@ // Make sure they're allowed to look at the page if (!Forums::check_forumperm($ForumID)) { - error(403); + error(403); } //Escape strings for later display $ThreadTitle = display_str($ThreadInfo['Title']); @@ -64,51 +64,51 @@ //Post links utilize the catalogue & key params to prevent issues with custom posts per page if ($ThreadInfo['Posts'] > $PerPage) { - if (isset($_GET['post']) && is_number($_GET['post'])) { - $PostNum = $_GET['post']; - } elseif (isset($_GET['postid']) && is_number($_GET['postid']) && $_GET['postid'] != $ThreadInfo['StickyPostID']) { - $SQL = " - SELECT COUNT(ID) - FROM forums_posts - WHERE TopicID = $ThreadID - AND ID <= $_GET[postid]"; - if ($ThreadInfo['StickyPostID'] < $_GET['postid']) { - $SQL .= " AND ID != $ThreadInfo[StickyPostID]"; - } - $DB->query($SQL); - list($PostNum) = $DB->next_record(); - } else { - $PostNum = 1; - } + if (isset($_GET['post']) && is_number($_GET['post'])) { + $PostNum = $_GET['post']; + } elseif (isset($_GET['postid']) && is_number($_GET['postid']) && $_GET['postid'] != $ThreadInfo['StickyPostID']) { + $SQL = " + SELECT COUNT(ID) + FROM forums_posts + WHERE TopicID = $ThreadID + AND ID <= $_GET[postid]"; + if ($ThreadInfo['StickyPostID'] < $_GET['postid']) { + $SQL .= " AND ID != $ThreadInfo[StickyPostID]"; + } + $DB->query($SQL); + list($PostNum) = $DB->next_record(); + } else { + $PostNum = 1; + } } else { - $PostNum = 1; + $PostNum = 1; } list($Page, $Limit) = Format::page_limit($PerPage, min($ThreadInfo['Posts'],$PostNum)); if (($Page - 1) * $PerPage > $ThreadInfo['Posts']) { - $Page = ceil($ThreadInfo['Posts'] / $PerPage); + $Page = ceil($ThreadInfo['Posts'] / $PerPage); } list($CatalogueID,$CatalogueLimit) = Format::catalogue_limit($Page, $PerPage, THREAD_CATALOGUE); // Cache catalogue from which the page is selected, allows block caches and future ability to specify posts per page if (!$Catalogue = $Cache->get_value("thread_{$ThreadID}_catalogue_$CatalogueID")) { - $DB->query(" - SELECT - p.ID, - p.AuthorID, - p.AddedTime, - p.Body, - p.EditedUserID, - p.EditedTime, - ed.Username - FROM forums_posts AS p - LEFT JOIN users_main AS ed ON ed.ID = p.EditedUserID - WHERE p.TopicID = '$ThreadID' - AND p.ID != '".$ThreadInfo['StickyPostID']."' - LIMIT $CatalogueLimit"); - $Catalogue = $DB->to_array(false, MYSQLI_ASSOC); - if (!$ThreadInfo['IsLocked'] || $ThreadInfo['IsSticky']) { - $Cache->cache_value("thread_{$ThreadID}_catalogue_$CatalogueID", $Catalogue, 0); - } + $DB->query(" + SELECT + p.ID, + p.AuthorID, + p.AddedTime, + p.Body, + p.EditedUserID, + p.EditedTime, + ed.Username + FROM forums_posts AS p + LEFT JOIN users_main AS ed ON ed.ID = p.EditedUserID + WHERE p.TopicID = '$ThreadID' + AND p.ID != '".$ThreadInfo['StickyPostID']."' + LIMIT $CatalogueLimit"); + $Catalogue = $DB->to_array(false, MYSQLI_ASSOC); + if (!$ThreadInfo['IsLocked'] || $ThreadInfo['IsSticky']) { + $Cache->cache_value("thread_{$ThreadID}_catalogue_$CatalogueID", $Catalogue, 0); + } } $Thread = Format::catalogue_select($Catalogue, $Page, $PerPage, THREAD_CATALOGUE); $LastPost = end($Thread); @@ -116,554 +116,598 @@ $FirstPost = reset($Thread); $FirstPost = $FirstPost['ID']; if ($ThreadInfo['Posts'] <= $PerPage*$Page && $ThreadInfo['StickyPostID'] > $LastPost) { - $LastPost = $ThreadInfo['StickyPostID']; + $LastPost = $ThreadInfo['StickyPostID']; } //Handle last read if (!$ThreadInfo['IsLocked'] || $ThreadInfo['IsSticky']) { - $DB->query(" - SELECT PostID - FROM forums_last_read_topics - WHERE UserID = '$LoggedUser[ID]' - AND TopicID = '$ThreadID'"); - list($LastRead) = $DB->next_record(); - if ($LastRead < $LastPost) { - $DB->query(" - INSERT INTO forums_last_read_topics - (UserID, TopicID, PostID) - VALUES - ('$LoggedUser[ID]', '$ThreadID', '".db_string($LastPost)."') - ON DUPLICATE KEY UPDATE - PostID = '$LastPost'"); - } + $DB->query(" + SELECT PostID + FROM forums_last_read_topics + WHERE UserID = '$LoggedUser[ID]' + AND TopicID = '$ThreadID'"); + list($LastRead) = $DB->next_record(); + if ($LastRead < $LastPost) { + $DB->query(" + INSERT INTO forums_last_read_topics + (UserID, TopicID, PostID) + VALUES + ('$LoggedUser[ID]', '$ThreadID', '".db_string($LastPost)."') + ON DUPLICATE KEY UPDATE + PostID = '$LastPost'"); + } } //Handle subscriptions $UserSubscriptions = Subscriptions::get_subscriptions(); if (empty($UserSubscriptions)) { - $UserSubscriptions = array(); + $UserSubscriptions = []; } if (in_array($ThreadID, $UserSubscriptions)) { - $Cache->delete_value('subscriptions_user_new_'.$LoggedUser['ID']); + $Cache->delete_value('subscriptions_user_new_'.$LoggedUser['ID']); } $QuoteNotificationsCount = $Cache->get_value('notify_quoted_' . $LoggedUser['ID']); if ($QuoteNotificationsCount === false || $QuoteNotificationsCount > 0) { - $DB->query(" - UPDATE users_notify_quoted - SET UnRead = false - WHERE UserID = '$LoggedUser[ID]' - AND Page = 'forums' - AND PageID = '$ThreadID' - AND PostID >= '$FirstPost' - AND PostID <= '$LastPost'"); - $Cache->delete_value('notify_quoted_' . $LoggedUser['ID']); + $DB->query(" + UPDATE users_notify_quoted + SET UnRead = false + WHERE UserID = '$LoggedUser[ID]' + AND Page = 'forums' + AND PageID = '$ThreadID' + AND PostID >= '$FirstPost' + AND PostID <= '$LastPost'"); + $Cache->delete_value('notify_quoted_' . $LoggedUser['ID']); } // Start printing View::show_header($ThreadInfo['Title'] . ' < '.$Forums[$ForumID]['Name'].' < Forums','comments,subscriptions,bbcode', $IsDonorForum ? 'donor' : ''); ?>
      -

      - Forums > - > - -

      - - + + + + + + + +
      + + + + + +
      + +
      + + $Post) { - list($PostID, $AuthorID, $AddedTime, $Body, $EditedUserID, $EditedTime, $EditedUsername) = array_values($Post); - list($AuthorID, $Username, $PermissionID, $Paranoia, $Artist, $Donor, $Warned, $Avatar, $Enabled, $UserTitle) = array_values(Users::user_info($AuthorID)); + list($PostID, $AuthorID, $AddedTime, $Body, $EditedUserID, $EditedTime, $EditedUsername) = array_values($Post); + list($AuthorID, $Username, $PermissionID, $Paranoia, $Artist, $Donor, $Warned, $Avatar, $Enabled, $UserTitle) = array_values(Users::user_info($AuthorID)); ?> - - - - - - - - - + + + + + + + - - - - - - + + + + + + - + + + +
      -
      # - - - - Quote - - - Edit - 1) { ?> - - Delete - - - - - X - $LastRead + && strtotime($AddedTime) > $LoggedUser['CatchupTime'] + ) || (isset($RequestKey) && $Key == $RequestKey) + ) { + echo ' forum_unread'; + } + if (!Users::has_avatars_enabled()) { + echo ' noavatar'; + } + if ($ThreadInfo['OP'] == $AuthorID) { + echo ' important_user'; + } + if ($PostID == $ThreadInfo['StickyPostID']) { + echo ' sticky_post'; + } ?>" id="post"> +
      +
      # + + + - Quote + + - Edit + 1) { ?> + - Delete + + + + - X + - - -&postid=&auth=" title="Sticky this post" class="brackets tooltip">⇕ + -
      -
      - Report -= $AuthorInfo['Class']) { +
      +
      + Report += $AuthorInfo['Class']) { ?> - - - Warn -" action="" method="post"> + + + + + + - Warn + -   - -
      -
      - - > -
      - - -
      -
      +   + +
      +
      + + > +
      + + +
      +
      - - « - - Last edited by - + + « + + Last edited by + - -
      -
      - + - 'Post reply', - 'InputName' => 'thread', - 'InputID' => $ThreadID, - 'ForumID' => $ForumID, - 'TextareaCols' => 90 - )); - } + if (Forums::check_forumperm($ForumID, 'Write') && !$LoggedUser['DisablePosting']) { + View::parse('generic/reply/quickreply.php', array( + 'InputTitle' => 'Post reply', + 'InputName' => 'thread', + 'InputID' => $ThreadID, + 'ForumID' => $ForumID, + 'TextareaCols' => 90 + )); + } +} +$transitions = Forums::get_thread_transitions($ForumID); +if (count($transitions) > 0 && !check_perms('site_moderate_forums')) { +?> +

      Edit thread

      + + + + +
      + +
      + + + + + + + +
      + +
      +query(" - SELECT ID, AuthorID, AddedTime, Body - FROM forums_topic_notes - WHERE TopicID = $ThreadID - ORDER BY ID ASC"); - $Notes = G::$DB->to_array(); + G::$DB->query(" + SELECT ID, AuthorID, AddedTime, Body + FROM forums_topic_notes + WHERE TopicID = $ThreadID + ORDER BY ID ASC"); + $Notes = G::$DB->to_array(); ?> -
      -

      Thread notes

      Toggle -
      - - - - - +

      Thread notes

      Toggle + + + + + + - - + - - - - -
      -
      -

      Edit thread

      -
      -
      - - - - -
      - - - - - - > - - - - - - - - - - - - - - + +
      - tabindex="2" /> -
      - tabindex="2" /> -
      - -
      - +
      + +
      +
      +
      +

      Edit thread

      +
      +
      + + + + +
      + + + + + + > + + + + + + + + + + + + + + - - - - - - - - - - - -
      + tabindex="2" /> +
      + tabindex="2" /> +
      + +
      + -
      - -
      - - - - - - - -
      -
      -"> + + + + + + + + + + + + + + + + + + + 0) { +?> + + + +
      + + + + + + + +
      + + + + + + + +
      + -query(" - SELECT p.Body, t.ForumID - FROM forums_posts AS p - JOIN forums_topics AS t ON p.TopicID = t.ID - WHERE p.ID = '$PostID'"); + SELECT p.Body, t.ForumID + FROM forums_posts AS p + JOIN forums_topics AS t ON p.TopicID = t.ID + WHERE p.ID = '$PostID'"); list($PostBody, $ForumID) = $DB -> next_record(); View::show_header('Warn User'); ?>
      -
      -

      Warning

      -
      -
      -
      - - - - - - - - - - - - - - - - - - - - - -
      Reason: - -
      Length: - -
      Private message: - -
      Edit post: - -
      - -
      -
      -
      +
      +

      Warning

      +
      +
      +
      + + + + + + + + + + + + + + + + + + + + + +
      Reason: + +
      Length: + +
      Private message: + +
      Edit post: + +
      + +
      +
      +
      - + diff --git a/sections/friends/add.php b/sections/friends/add.php index 945384588..d5db530f1 100644 --- a/sections/friends/add.php +++ b/sections/friends/add.php @@ -1,23 +1,23 @@ query(" - SELECT 1 - FROM users_main - WHERE ID = '$FriendID'"); + SELECT 1 + FROM users_main + WHERE ID = '$FriendID'"); if (!$DB->has_results()) { - error(404); + error(404); } $DB->query(" - INSERT IGNORE INTO friends - (UserID, FriendID) - VALUES - ('$LoggedUser[ID]', '$FriendID')"); + INSERT IGNORE INTO friends + (UserID, FriendID) + VALUES + ('$LoggedUser[ID]', '$FriendID')"); header('Location: friends.php'); diff --git a/sections/friends/comment.php b/sections/friends/comment.php index 94a5186e3..7f031ed62 100644 --- a/sections/friends/comment.php +++ b/sections/friends/comment.php @@ -1,9 +1,9 @@ -query(" - UPDATE friends - SET Comment='$P[comment]' - WHERE UserID='$LoggedUser[ID]' - AND FriendID='$P[friendid]'"); + UPDATE friends + SET Comment='$P[comment]' + WHERE UserID='$LoggedUser[ID]' + AND FriendID='$P[friendid]'"); header('Location: friends.php'); ?> diff --git a/sections/friends/friends.php b/sections/friends/friends.php index 6694954c7..309564101 100644 --- a/sections/friends/friends.php +++ b/sections/friends/friends.php @@ -18,35 +18,32 @@ $UserID = $LoggedUser['ID']; - -$Select = "f.FriendID"; -$Where = "f.UserID = '$UserID'"; -$Join1 = "f.FriendID = m.ID"; -$Join2 = "f.FriendID = i.UserID"; - - +$Where = ""; list($Page, $Limit) = Format::page_limit(FRIENDS_PER_PAGE); // Main query -$DB->query(" - SELECT - SQL_CALC_FOUND_ROWS - $Select, - f.Comment, - m.Username, - m.Uploaded, - m.Downloaded, - m.PermissionID, - m.Paranoia, - m.LastAccess, - i.Avatar - FROM friends AS f - JOIN users_main AS m ON $Join1 - JOIN users_info AS i ON $Join2 - WHERE $Where - ORDER BY Username - LIMIT $Limit"); +$DB->prepared_query(' + SELECT + SQL_CALC_FOUND_ROWS + f.FriendID, + f.Comment, + m.Username, + uls.Uploaded, + uls.Downloaded, + m.PermissionID, + m.Paranoia, + m.LastAccess, + i.Avatar + FROM friends AS f + INNER JOIN users_main AS m ON (m.ID = f.FriendID) + INNER JOIN users_info AS i ON (i.UserID = f.FriendID) + INNER JOIN users_leech_stats AS uls ON (uls.UserID = f.FriendID) + WHERE f.UserID = ? + ORDER BY m.Username + LIMIT ? + ', $UserID, $Limit +); $Friends = $DB->to_array(false, MYSQLI_BOTH, array(6, 'Paranoia')); // Number of results (for pagination) @@ -56,80 +53,86 @@ // Start printing stuff ?>
      -
      -

      Friends List

      -
      - + -
      - +
      +You have no friends! :(

      '; + echo '

      You have no friends! :(

      '; } // Start printing out friends foreach ($Friends as $Friend) { - list($FriendID, $Comment, $Username, $Uploaded, $Downloaded, $Class, $Paranoia, $LastAccess, $Avatar) = $Friend; + list($FriendID, $Comment, $Username, $Uploaded, $Downloaded, $Class, $Paranoia, $LastAccess, $Avatar) = $Friend; ?>
      - - - - + + + + + + + + +
      - - -  Ratio: -" /> + + + - - - - - - - - -
      + + +  Ratio: + -  Up: - + -  Down: - - - - - -
      - - - - - - -
      -
      -
      -
      +  Down: + +
      + + + +
      + + + + + + +
      +
      +
      +
      - ?> -
      - - */ ?> +
      + + */ ?>
      - diff --git a/sections/friends/index.php b/sections/friends/index.php index 81662be71..c1fbcf973 100644 --- a/sections/friends/index.php +++ b/sections/friends/index.php @@ -1,31 +1,31 @@ - diff --git a/sections/friends/remove.php b/sections/friends/remove.php index 9bd8f0194..6c221fe43 100644 --- a/sections/friends/remove.php +++ b/sections/friends/remove.php @@ -1,8 +1,8 @@ -query(" - DELETE FROM friends - WHERE UserID='$LoggedUser[ID]' - AND FriendID='$P[friendid]'"); + DELETE FROM friends + WHERE UserID='$LoggedUser[ID]' + AND FriendID='$P[friendid]'"); header('Location: friends.php'); ?> diff --git a/sections/image/index.php b/sections/image/index.php index f25e9887d..89753aeb9 100644 --- a/sections/image/index.php +++ b/sections/image/index.php @@ -1,146 +1,146 @@ -get_value('image_cache_'.md5($URL)); - $Cached = true; + list($Data, $FileType) = $Cache->get_value('image_cache_'.md5($URL)); + $Cached = true; } if (!isset($Data) || !$Data) { - $Cached = false; - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $URL); - curl_setopt($ch, CURLOPT_HEADER, false); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_TIMEOUT, 15); - curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.1 Safari/537.11'); - $Data = curl_exec($ch); - $rescode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - - if (!$Data || empty($Data)) { - img_error('timeout'); - } - $FileType = image_type($Data); - if ($FileType && function_exists("imagecreatefrom$FileType")) { - $Image = imagecreatefromstring($Data); - if (invisible($Image)) { - img_error('invisible'); - } - if (verysmall($Image)) { - img_error('small'); - } - } - - if (isset($_GET['c']) && strlen($Data) < 262144) { - $Cache->cache_value('image_cache_'.md5($URL), array($Data, $FileType), 3600 * 24 * 7); - } + $Cached = false; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $URL); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_TIMEOUT, 15); + curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.1 Safari/537.11'); + $Data = curl_exec($ch); + $rescode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if (!$Data || empty($Data)) { + img_error('timeout'); + } + $FileType = image_type($Data); + if ($FileType && function_exists("imagecreatefrom$FileType")) { + $Image = imagecreatefromstring($Data); + if (invisible($Image)) { + img_error('invisible'); + } + if (verysmall($Image)) { + img_error('small'); + } + } + + if (isset($_GET['c']) && strlen($Data) < 262144) { + $Cache->cache_value('image_cache_'.md5($URL), array($Data, $FileType), 3600 * 24 * 7); + } } // Reset avatar, add mod note function reset_image($UserID, $Type, $AdminComment, $PrivMessage) { - if ($Type === 'avatar') { - $CacheKey = "user_info_$UserID"; - $DBTable = 'users_info'; - $DBColumn = 'Avatar'; - $PMSubject = 'Your avatar has been automatically reset'; - } elseif ($Type === 'avatar2') { - $CacheKey = "donor_info_$UserID"; - $DBTable = 'donor_rewards'; - $DBColumn = 'SecondAvatar'; - $PMSubject = 'Your second avatar has been automatically reset'; - } elseif ($Type === 'donoricon') { - $CacheKey = "donor_info_$UserID"; - $DBTable = 'donor_rewards'; - $DBColumn = 'CustomIcon'; - $PMSubject = 'Your donor icon has been automatically reset'; - } - - $UserInfo = G::$Cache->get_value($CacheKey, true); - if ($UserInfo !== false) { - if ($UserInfo[$DBColumn] === '') { - // This image has already been reset - return; - } - $UserInfo[$DBColumn] = ''; - G::$Cache->cache_value($CacheKey, $UserInfo, 2592000); // cache for 30 days - } - - // reset the avatar or donor icon URL - G::$DB->query(" - UPDATE $DBTable - SET $DBColumn = '' - WHERE UserID = '$UserID'"); - - // write comment to staff notes - G::$DB->query(" - UPDATE users_info - SET AdminComment = CONCAT('".sqltime().' - '.db_string($AdminComment)."\n\n', AdminComment) - WHERE UserID = '$UserID'"); - - // clear cache keys - G::$Cache->delete_value($CacheKey); - - Misc::send_pm($UserID, 0, $PMSubject, $PrivMessage); + if ($Type === 'avatar') { + $CacheKey = "user_info_$UserID"; + $DBTable = 'users_info'; + $DBColumn = 'Avatar'; + $PMSubject = 'Your avatar has been automatically reset'; + } elseif ($Type === 'avatar2') { + $CacheKey = "donor_info_$UserID"; + $DBTable = 'donor_rewards'; + $DBColumn = 'SecondAvatar'; + $PMSubject = 'Your second avatar has been automatically reset'; + } elseif ($Type === 'donoricon') { + $CacheKey = "donor_info_$UserID"; + $DBTable = 'donor_rewards'; + $DBColumn = 'CustomIcon'; + $PMSubject = 'Your donor icon has been automatically reset'; + } + + $UserInfo = G::$Cache->get_value($CacheKey, true); + if ($UserInfo !== false) { + if ($UserInfo[$DBColumn] === '') { + // This image has already been reset + return; + } + $UserInfo[$DBColumn] = ''; + G::$Cache->cache_value($CacheKey, $UserInfo, 2592000); // cache for 30 days + } + + // reset the avatar or donor icon URL + G::$DB->query(" + UPDATE $DBTable + SET $DBColumn = '' + WHERE UserID = '$UserID'"); + + // write comment to staff notes + G::$DB->query(" + UPDATE users_info + SET AdminComment = CONCAT('".sqltime().' - '.db_string($AdminComment)."\n\n', AdminComment) + WHERE UserID = '$UserID'"); + + // clear cache keys + G::$Cache->delete_value($CacheKey); + + Misc::send_pm($UserID, 0, $PMSubject, $PrivMessage); } // Enforce avatar rules if (isset($_GET['type']) && isset($_GET['userid'])) { - $ValidTypes = array('avatar', 'avatar2', 'donoricon'); - if (!is_number($_GET['userid']) || !in_array($_GET['type'], $ValidTypes)) { - die(); - } - $UserID = $_GET['userid']; - $Type = $_GET['type']; - - if ($Type === 'avatar' || $Type === 'avatar2') { - $MaxFileSize = 256 * 1024; // 256 kB - $MaxImageHeight = 400; // pixels - $TypeName = $Type === 'avatar' ? 'avatar' : 'second avatar'; - } elseif ($Type === 'donoricon') { - $MaxFileSize = 64 * 1024; // 64 kB - $MaxImageHeight = 100; // pixels - $TypeName = 'donor icon'; - } - - $Height = image_height($FileType, $Data); - if (strlen($Data) > $MaxFileSize || $Height > $MaxImageHeight) { - // Sometimes the cached image we have isn't the actual image - if ($Cached) { - $Data2 = @file_get_contents($URL, 0, stream_context_create(array('http' => array('timeout' => 15)))); - } else { - $Data2 = $Data; - } - - if (strlen($Data2) > $MaxFileSize || image_height($FileType, $Data2) > $MaxImageHeight) { - require_once(SERVER_ROOT.'/classes/mysql.class.php'); - require_once(SERVER_ROOT.'/classes/time.class.php'); - $DBURL = db_string($URL); - $AdminComment = ucfirst($TypeName)." reset automatically (Size: ".number_format((strlen($Data)) / 1024)." kB, Height: ".$Height."px). Used to be $DBURL"; - $PrivMessage = SITE_NAME." has the following requirements for {$TypeName}s:\n\n". - "[b]".ucfirst($TypeName)."s must not exceed ".($MaxFileSize / 1024)." kB or be vertically longer than {$MaxImageHeight}px.[/b]\n\n". - "Your $TypeName at $DBURL has been found to exceed these rules. As such, it has been automatically reset. You are welcome to reinstate your $TypeName once it has been resized down to an acceptable size."; - reset_image($UserID, $Type, $AdminComment, $PrivMessage); - } - } + $ValidTypes = array('avatar', 'avatar2', 'donoricon'); + if (!is_number($_GET['userid']) || !in_array($_GET['type'], $ValidTypes)) { + die(); + } + $UserID = $_GET['userid']; + $Type = $_GET['type']; + + if ($Type === 'avatar' || $Type === 'avatar2') { + $MaxFileSize = 256 * 1024; // 256 kB + $MaxImageHeight = 400; // pixels + $TypeName = $Type === 'avatar' ? 'avatar' : 'second avatar'; + } elseif ($Type === 'donoricon') { + $MaxFileSize = 64 * 1024; // 64 kB + $MaxImageHeight = 100; // pixels + $TypeName = 'donor icon'; + } + + $Height = image_height($FileType, $Data); + if (strlen($Data) > $MaxFileSize || $Height > $MaxImageHeight) { + // Sometimes the cached image we have isn't the actual image + if ($Cached) { + $Data2 = @file_get_contents($URL, 0, stream_context_create(array('http' => array('timeout' => 15)))); + } else { + $Data2 = $Data; + } + + if (strlen($Data2) > $MaxFileSize || image_height($FileType, $Data2) > $MaxImageHeight) { + require_once(SERVER_ROOT.'/classes/mysql.class.php'); + require_once(SERVER_ROOT.'/classes/time.class.php'); + $DBURL = db_string($URL); + $AdminComment = ucfirst($TypeName)." reset automatically (Size: ".number_format((strlen($Data)) / 1024)." kB, Height: ".$Height."px). Used to be $DBURL"; + $PrivMessage = SITE_NAME." has the following requirements for {$TypeName}s:\n\n". + "[b]".ucfirst($TypeName)."s must not exceed ".($MaxFileSize / 1024)." kB or be vertically longer than {$MaxImageHeight}px.[/b]\n\n". + "Your $TypeName at $DBURL has been found to exceed these rules. As such, it has been automatically reset. You are welcome to reinstate your $TypeName once it has been resized down to an acceptable size."; + reset_image($UserID, $Type, $AdminComment, $PrivMessage); + } + } } if (!isset($FileType)) { - img_error('timeout'); + img_error('timeout'); } header("Content-type: image/$FileType"); diff --git a/sections/inbox/compose.php b/sections/inbox/compose.php index fb4a6af33..6a4d976bf 100644 --- a/sections/inbox/compose.php +++ b/sections/inbox/compose.php @@ -1,54 +1,54 @@ -query(" - SELECT Username - FROM users_main - WHERE ID='$ToID'"); + SELECT Username + FROM users_main + WHERE ID='$ToID'"); list($Username) = $DB->next_record(); if (!$Username) { - error(404); + error(404); } View::show_header('Compose', 'inbox,bbcode,jquery.validate,form_validate'); ?>
      -
      -

      Send a message to

      -
      -
      -
      - - - -
      -

      Subject

      -
      -

      Body

      - -
      - -
      - - -
      -
      -
      +
      +

      Send a message to

      +
      +
      +
      + + + +
      +

      Subject

      +
      +

      Body

      + +
      + +
      + + +
      +
      +
      - diff --git a/sections/inbox/conversation.php b/sections/inbox/conversation.php index e8de315fb..043a1f2c5 100644 --- a/sections/inbox/conversation.php +++ b/sections/inbox/conversation.php @@ -1,18 +1,18 @@ -query(" - SELECT InInbox, InSentbox - FROM pm_conversations_users - WHERE UserID = '$UserID' - AND ConvID = '$ConvID'"); + SELECT InInbox, InSentbox + FROM pm_conversations_users + WHERE UserID = '$UserID' + AND ConvID = '$ConvID'"); if (!$DB->has_results()) { - error(403); + error(403); } list($InInbox, $InSentbox) = $DB->next_record(); @@ -20,35 +20,35 @@ if (!$InInbox && !$InSentbox) { - error(404); + error(404); } // Get information on the conversation $DB->query(" - SELECT - c.Subject, - cu.Sticky, - cu.UnRead, - cu.ForwardedTo - FROM pm_conversations AS c - JOIN pm_conversations_users AS cu ON c.ID = cu.ConvID - WHERE c.ID = '$ConvID' - AND UserID = '$UserID'"); + SELECT + c.Subject, + cu.Sticky, + cu.UnRead, + cu.ForwardedTo + FROM pm_conversations AS c + JOIN pm_conversations_users AS cu ON c.ID = cu.ConvID + WHERE c.ID = '$ConvID' + AND UserID = '$UserID'"); list($Subject, $Sticky, $UnRead, $ForwardedID) = $DB->next_record(); $DB->query(" - SELECT um.ID, Username - FROM pm_messages AS pm - JOIN users_main AS um ON um.ID = pm.SenderID - WHERE pm.ConvID = '$ConvID'"); + SELECT um.ID, Username + FROM pm_messages AS pm + JOIN users_main AS um ON um.ID = pm.SenderID + WHERE pm.ConvID = '$ConvID'"); $ConverstionParticipants = $DB->to_array(); foreach ($ConverstionParticipants as $Participant) { - $PMUserID = (int)$Participant['ID']; - $Users[$PMUserID]['UserStr'] = Users::format_username($PMUserID, true, true, true, true); - $Users[$PMUserID]['Username'] = $Participant['Username']; + $PMUserID = (int)$Participant['ID']; + $Users[$PMUserID]['UserStr'] = Users::format_username($PMUserID, true, true, true, true); + $Users[$PMUserID]['Username'] = $Participant['Username']; } $Users[0]['UserStr'] = 'System'; // in case it's a message from the system @@ -57,142 +57,144 @@ if ($UnRead == '1') { - $DB->query(" - UPDATE pm_conversations_users - SET UnRead = '0' - WHERE ConvID = '$ConvID' - AND UserID = '$UserID'"); - // Clear the caches of the inbox and sentbox - $Cache->decrement("inbox_new_$UserID"); + $DB->query(" + UPDATE pm_conversations_users + SET UnRead = '0' + WHERE ConvID = '$ConvID' + AND UserID = '$UserID'"); + // Clear the caches of the inbox and sentbox + $Cache->decrement("inbox_new_$UserID"); } View::show_header("View conversation $Subject", 'comments,inbox,bbcode,jquery.validate,form_validate'); // Get messages $DB->query(" - SELECT SentDate, SenderID, Body, ID - FROM pm_messages - WHERE ConvID = '$ConvID' - ORDER BY ID"); + SELECT SentDate, SenderID, Body, ID + FROM pm_messages + WHERE ConvID = '$ConvID' + ORDER BY ID"); ?>
      -

      0 ? " (Forwarded to $ForwardedName)" : '')?>

      - - 0 ? " (Forwarded to $ForwardedName)" : '')?> + +next_record()) { ?> -
      -
      -
      - - 0) { ?> - - Quote - -
      - -
      -
      - -
      -
      - +
      +
      + + 0) { ?> + - Quote + +
      + +
      +
      + +
      +
      +query(" - SELECT UserID - FROM pm_conversations_users - WHERE UserID != '$LoggedUser[ID]' - AND ConvID = '$ConvID' - AND (ForwardedTo = 0 OR ForwardedTo = UserID)"); + SELECT UserID + FROM pm_conversations_users + WHERE UserID != '$LoggedUser[ID]' + AND ConvID = '$ConvID' + AND (ForwardedTo = 0 OR ForwardedTo = UserID)"); $ReceiverIDs = $DB->collect('UserID'); if (!empty($ReceiverIDs) && (empty($LoggedUser['DisablePM']) || array_intersect($ReceiverIDs, array_keys($StaffIDs)))) { ?> -

      Reply

      -
      -
      - - - - -
      - -
      - - -
      -
      -
      -Reply +
      +
      + + + + +
      + +
      + + +
      +
      +
      + -

      Manage conversation

      -
      -
      - - - - - - - - - - - - - - - - - -
      - /> - - - - -
      -
      -
      -Manage conversation +
      +
      + + + + + + + + + + + + + + + + + +
      + /> + + + + +
      +
      +
      +query(" - SELECT SupportFor - FROM users_info - WHERE UserID = ".$LoggedUser['ID']); + SELECT SupportFor + FROM users_info + WHERE UserID = ".$LoggedUser['ID']); list($FLS) = $DB->next_record(); if ((check_perms('users_mod') || $FLS != '') && (!$ForwardedID || $ForwardedID == $LoggedUser['ID'])) { ?> -

      Forward conversation

      -
      -
      - - - - - + + + + - -
      -
      - + + + + - diff --git a/sections/inbox/forward.php b/sections/inbox/forward.php index 9e5eec574..739c9c106 100644 --- a/sections/inbox/forward.php +++ b/sections/inbox/forward.php @@ -1,51 +1,51 @@ -query(" - SELECT ConvID - FROM pm_conversations_users - WHERE UserID = '$UserID' - AND InInbox = '1' - AND (ForwardedTo = 0 OR ForwardedTo = UserID) - AND ConvID = '$ConvID'"); + SELECT ConvID + FROM pm_conversations_users + WHERE UserID = '$UserID' + AND InInbox = '1' + AND (ForwardedTo = 0 OR ForwardedTo = UserID) + AND ConvID = '$ConvID'"); if (!$DB->has_results()) { - error(403); + error(403); } $DB->query(" - SELECT ConvID - FROM pm_conversations_users - WHERE UserID = '$ReceiverID' - AND (ForwardedTo = 0 OR ForwardedTo = UserID) - AND InInbox = '1' - AND ConvID = '$ConvID'"); + SELECT ConvID + FROM pm_conversations_users + WHERE UserID = '$ReceiverID' + AND (ForwardedTo = 0 OR ForwardedTo = UserID) + AND InInbox = '1' + AND ConvID = '$ConvID'"); if (!$DB->has_results()) { - $DB->query(" - INSERT IGNORE INTO pm_conversations_users - (UserID, ConvID, InInbox, InSentbox, ReceivedDate) - VALUES ('$ReceiverID', '$ConvID', '1', '0', NOW()) - ON DUPLICATE KEY UPDATE - ForwardedTo = 0, - UnRead = 1"); - $DB->query(" - UPDATE pm_conversations_users - SET ForwardedTo = '$ReceiverID' - WHERE ConvID = '$ConvID' - AND UserID = '$UserID'"); - $Cache->delete_value("inbox_new_$ReceiverID"); - header('Location: ' . Inbox::get_inbox_link()); + $DB->query(" + INSERT IGNORE INTO pm_conversations_users + (UserID, ConvID, InInbox, InSentbox, ReceivedDate) + VALUES ('$ReceiverID', '$ConvID', '1', '0', NOW()) + ON DUPLICATE KEY UPDATE + ForwardedTo = 0, + UnRead = 1"); + $DB->query(" + UPDATE pm_conversations_users + SET ForwardedTo = '$ReceiverID' + WHERE ConvID = '$ConvID' + AND UserID = '$UserID'"); + $Cache->delete_value("inbox_new_$ReceiverID"); + header('Location: ' . Inbox::get_inbox_link()); } else { - error("$StaffIDs[$ReceiverID] already has this conversation in their inbox."); - header("Location: inbox.php?action=viewconv&id=$ConvID"); + error("$StaffIDs[$ReceiverID] already has this conversation in their inbox."); + header("Location: inbox.php?action=viewconv&id=$ConvID"); } //View::show_footer(); ?> diff --git a/sections/inbox/get_post.php b/sections/inbox/get_post.php index 38b6a840e..bff33dd9c 100644 --- a/sections/inbox/get_post.php +++ b/sections/inbox/get_post.php @@ -1,4 +1,4 @@ -query(" - SELECT m.Body - FROM pm_messages AS m - JOIN pm_conversations_users AS u ON m.ConvID = u.ConvID - WHERE m.ID = '$PostID' - AND u.UserID = ".$LoggedUser['ID']); + SELECT m.Body + FROM pm_messages AS m + JOIN pm_conversations_users AS u ON m.ConvID = u.ConvID + WHERE m.ID = '$PostID' + AND u.UserID = ".$LoggedUser['ID']); list($Body) = $DB->next_record(MYSQLI_NUM); // This gets sent to the browser, which echoes it wherever diff --git a/sections/inbox/inbox.php b/sections/inbox/inbox.php index 5a4ef27d5..879cd6244 100644 --- a/sections/inbox/inbox.php +++ b/sections/inbox/inbox.php @@ -6,13 +6,13 @@ if (empty($_GET['action'])) { - $Section = 'inbox'; - $_GET['action'] = 'inbox'; + $Section = 'inbox'; + $_GET['action'] = 'inbox'; } else { - $Section = $_GET['action']; // either 'inbox' or 'sentbox' + $Section = $_GET['action']; // either 'inbox' or 'sentbox' } if (!in_array($Section, array('inbox', 'sentbox'))) { - error(404); + error(404); } list($Page, $Limit) = Format::page_limit(MESSAGES_PER_PAGE); @@ -20,60 +20,60 @@ View::show_header('Inbox'); ?>
      -

      - +
      - diff --git a/sections/inbox/index.php b/sections/inbox/index.php index 4cde17209..eab9ace98 100644 --- a/sections/inbox/index.php +++ b/sections/inbox/index.php @@ -1,45 +1,45 @@ -get_value('staff_ids'); if (!is_array($StaffIDs)) { - $DB->query(" - SELECT m.ID, m.Username - FROM users_main AS m - JOIN permissions AS p ON p.ID=m.PermissionID - WHERE p.DisplayStaff='1'"); - while (list($StaffID, $StaffName) = $DB->next_record()) { - $StaffIDs[$StaffID] = $StaffName; - } - uasort($StaffIDs, 'strcasecmp'); - $Cache->cache_value('staff_ids', $StaffIDs); + $DB->query(" + SELECT m.ID, m.Username + FROM users_main AS m + JOIN permissions AS p ON p.ID=m.PermissionID + WHERE p.DisplayStaff='1'"); + while (list($StaffID, $StaffName) = $DB->next_record()) { + $StaffIDs[$StaffID] = $StaffName; + } + uasort($StaffIDs, 'strcasecmp'); + $Cache->cache_value('staff_ids', $StaffIDs); } if (!isset($_REQUEST['action'])) { - $_REQUEST['action'] = ''; + $_REQUEST['action'] = ''; } switch ($_REQUEST['action']) { - case 'takecompose': - require('takecompose.php'); - break; - case 'takeedit': - require('takeedit.php'); - break; - case 'compose': - require('compose.php'); - break; - case 'viewconv': - require('conversation.php'); - break; - case 'masschange': - require('massdelete_handle.php'); - break; - case 'get_post': - require('get_post.php'); - break; - case 'forward': - require('forward.php'); - break; - default: - require(SERVER_ROOT.'/sections/inbox/inbox.php'); + case 'takecompose': + require('takecompose.php'); + break; + case 'takeedit': + require('takeedit.php'); + break; + case 'compose': + require('compose.php'); + break; + case 'viewconv': + require('conversation.php'); + break; + case 'masschange': + require('massdelete_handle.php'); + break; + case 'get_post': + require('get_post.php'); + break; + case 'forward': + require('forward.php'); + break; + default: + require(SERVER_ROOT.'/sections/inbox/inbox.php'); } diff --git a/sections/inbox/massdelete_handle.php b/sections/inbox/massdelete_handle.php index 8091561c2..9d1cd331a 100644 --- a/sections/inbox/massdelete_handle.php +++ b/sections/inbox/massdelete_handle.php @@ -1,4 +1,4 @@ -query(" - SELECT COUNT(ConvID) - FROM pm_conversations_users - WHERE ConvID IN ($ConvIDs) - AND UserID=$UserID"); + SELECT COUNT(ConvID) + FROM pm_conversations_users + WHERE ConvID IN ($ConvIDs) + AND UserID=$UserID"); list($MessageCount) = $DB->next_record(); if ($MessageCount != count($Messages)) { - error(0); + error(0); } if (isset($_POST['delete'])) { - $DB->query(" - UPDATE pm_conversations_users - SET - InInbox='0', - InSentbox='0', - Sticky='0', - UnRead='0' - WHERE ConvID IN($ConvIDs) - AND UserID=$UserID"); + $DB->query(" + UPDATE pm_conversations_users + SET + InInbox='0', + InSentbox='0', + Sticky='0', + UnRead='0' + WHERE ConvID IN($ConvIDs) + AND UserID=$UserID"); } elseif (isset($_POST['unread'])) { - $DB->query(" - UPDATE pm_conversations_users - SET Unread='1' - WHERE ConvID IN($ConvIDs) AND UserID=$UserID"); + $DB->query(" + UPDATE pm_conversations_users + SET Unread='1' + WHERE ConvID IN($ConvIDs) AND UserID=$UserID"); } elseif (isset($_POST['read'])) { - $DB->query(" - UPDATE pm_conversations_users - SET Unread='0' - WHERE ConvID IN($ConvIDs) AND UserID=$UserID"); + $DB->query(" + UPDATE pm_conversations_users + SET Unread='0' + WHERE ConvID IN($ConvIDs) AND UserID=$UserID"); } $Cache->delete_value('inbox_new_'.$UserID); diff --git a/sections/inbox/takecompose.php b/sections/inbox/takecompose.php index 245d0e046..26a10594d 100644 --- a/sections/inbox/takecompose.php +++ b/sections/inbox/takecompose.php @@ -2,54 +2,54 @@ authorize(); if (empty($_POST['toid'])) { - error(404); + error(404); } if (!empty($LoggedUser['DisablePM']) && !isset($StaffIDs[$_POST['toid']])) { - error(403); + error(403); } if (isset($_POST['convid']) && is_number($_POST['convid'])) { - $ConvID = $_POST['convid']; - $Subject = ''; - $ToID = explode(',', $_POST['toid']); - foreach ($ToID as $TID) { - if (!is_number($TID)) { - $Err = 'A recipient does not exist.'; - } - } - $DB->query(" - SELECT UserID - FROM pm_conversations_users - WHERE UserID = '$LoggedUser[ID]' - AND ConvID = '$ConvID'"); - if (!$DB->has_results()) { - error(403); - } + $ConvID = $_POST['convid']; + $Subject = ''; + $ToID = explode(',', $_POST['toid']); + foreach ($ToID as $TID) { + if (!is_number($TID)) { + $Err = 'A recipient does not exist.'; + } + } + $DB->query(" + SELECT UserID + FROM pm_conversations_users + WHERE UserID = '$LoggedUser[ID]' + AND ConvID = '$ConvID'"); + if (!$DB->has_results()) { + error(403); + } } else { - $ConvID = ''; - if (!is_number($_POST['toid'])) { - $Err = 'This recipient does not exist.'; - } else { - $ToID = $_POST['toid']; - } - $Subject = trim($_POST['subject']); - if (empty($Subject)) { - $Err = 'You cannot send a message without a subject.'; - } + $ConvID = ''; + if (!is_number($_POST['toid'])) { + $Err = 'This recipient does not exist.'; + } else { + $ToID = $_POST['toid']; + } + $Subject = trim($_POST['subject']); + if (empty($Subject)) { + $Err = 'You cannot send a message without a subject.'; + } } $Body = trim($_POST['body']); if ($Body === '' || $Body === false) { - $Err = 'You cannot send a message without a body.'; + $Err = 'You cannot send a message without a body.'; } if (!empty($Err)) { - error($Err); - //header('Location: inbox.php?action=compose&to='.$_POST['toid']); - $ToID = $_POST['toid']; - $Return = true; - include(SERVER_ROOT.'/sections/inbox/compose.php'); - die(); + error($Err); + //header('Location: inbox.php?action=compose&to='.$_POST['toid']); + $ToID = $_POST['toid']; + $Return = true; + include(SERVER_ROOT.'/sections/inbox/compose.php'); + die(); } $ConvID = Misc::send_pm($ToID, $LoggedUser['ID'], $Subject, $Body, $ConvID); diff --git a/sections/inbox/takeedit.php b/sections/inbox/takeedit.php index 290b80294..4454d253b 100644 --- a/sections/inbox/takeedit.php +++ b/sections/inbox/takeedit.php @@ -1,46 +1,46 @@ -query(" - SELECT UserID - FROM pm_conversations_users - WHERE UserID='$UserID' AND ConvID='$ConvID'"); + SELECT UserID + FROM pm_conversations_users + WHERE UserID='$UserID' AND ConvID='$ConvID'"); if (!$DB->has_results()) { - error(403); + error(403); } if (isset($_POST['delete'])) { - $DB->query(" - UPDATE pm_conversations_users - SET - InInbox='0', - InSentbox='0', - Sticky='0' - WHERE ConvID='$ConvID' AND UserID='$UserID'"); + $DB->query(" + UPDATE pm_conversations_users + SET + InInbox='0', + InSentbox='0', + Sticky='0' + WHERE ConvID='$ConvID' AND UserID='$UserID'"); } else { - if (isset($_POST['sticky'])) { - $DB->query(" - UPDATE pm_conversations_users - SET Sticky='1' - WHERE ConvID='$ConvID' AND UserID='$UserID'"); - } else { - $DB->query(" - UPDATE pm_conversations_users - SET Sticky='0' - WHERE ConvID='$ConvID' AND UserID='$UserID'"); - } - if (isset($_POST['mark_unread'])) { - $DB->query(" - UPDATE pm_conversations_users - SET Unread='1' - WHERE ConvID='$ConvID' AND UserID='$UserID'"); - $Cache->increment('inbox_new_'.$UserID); - } + if (isset($_POST['sticky'])) { + $DB->query(" + UPDATE pm_conversations_users + SET Sticky='1' + WHERE ConvID='$ConvID' AND UserID='$UserID'"); + } else { + $DB->query(" + UPDATE pm_conversations_users + SET Sticky='0' + WHERE ConvID='$ConvID' AND UserID='$UserID'"); + } + if (isset($_POST['mark_unread'])) { + $DB->query(" + UPDATE pm_conversations_users + SET Unread='1' + WHERE ConvID='$ConvID' AND UserID='$UserID'"); + $Cache->increment('inbox_new_'.$UserID); + } } header('Location: ' . Inbox::get_inbox_link()); ?> diff --git a/sections/index/contest_leaderboard.php b/sections/index/contest_leaderboard.php index 9b274fe84..713af41ab 100644 --- a/sections/index/contest_leaderboard.php +++ b/sections/index/contest_leaderboard.php @@ -1,31 +1,33 @@ get_current_contest(); if (empty($Contest)) { - return; + return; } -$Leaderboard = Contest::get_leaderboard($Contest['ID']); +$Leaderboard = $ContestMgr->get_leaderboard($Contest['ID']); if (empty($Leaderboard)) { - return; + return; } ?>
      -
      Contest Leaderboard
      - +
      Contest Leaderboard
      +
      - - - - + + + + -
      -
      - View More -
      -
      \ No newline at end of file + +
      + View More +
      + diff --git a/sections/index/feat_merch.php b/sections/index/feat_merch.php index 1452555ef..023de031b 100644 --- a/sections/index/feat_merch.php +++ b/sections/index/feat_merch.php @@ -1,44 +1,44 @@ -get_value('featured_merch'); - if ($FeaturedMerch === false) { - $DB->query(' - SELECT ProductID, Title, Image, ArtistID - FROM featured_merch - WHERE Ended = 0'); - $FeaturedMerch = $DB->next_record(MYSQLI_ASSOC); - $Cache->cache_value('featured_merch', $FeaturedMerch, 0); - } + $FeaturedMerch = $Cache->get_value('featured_merch'); + if ($FeaturedMerch === false) { + $DB->query(' + SELECT ProductID, Title, Image, ArtistID + FROM featured_merch + WHERE Ended = 0'); + $FeaturedMerch = $DB->next_record(MYSQLI_ASSOC); + $Cache->cache_value('featured_merch', $FeaturedMerch, 0); + } - if ($FeaturedMerch != null) { + if ($FeaturedMerch != null) { ?>
      -
      - Featured Product -
      -
      - Featured Product Image -
      -
      - Product Page - 0) { - $UserInfo = Users::user_info($FeaturedMerch['ArtistID']); -?> - Artist: - -
      +
      + Featured Product +
      +
      + Featured Product Image +
      +
      + Product Page + 0) { + $UserInfo = Users::user_info($FeaturedMerch['ArtistID']); +?> - Artist: + +
      - +
      -
      - It's a mystery! -
      -
      - You may want to put an image here. -
      +
      + It's a mystery! +
      +
      + You may want to put an image here. +
      diff --git a/sections/index/index.php b/sections/index/index.php index 38814cbf3..7f0e8e66a 100644 --- a/sections/index/index.php +++ b/sections/index/index.php @@ -1,17 +1,17 @@ - diff --git a/sections/index/month_album.php b/sections/index/month_album.php index 4127f8d8d..c3e5dae94 100644 --- a/sections/index/month_album.php +++ b/sections/index/month_album.php @@ -1,33 +1,33 @@ -get_value('album_of_the_month'); if ($FeaturedAlbum === false) { - $DB->query(' - SELECT - fa.GroupID, - tg.Name, - tg.WikiImage, - fa.ThreadID, - fa.Title - FROM featured_albums AS fa - JOIN torrents_group AS tg ON tg.ID = fa.GroupID - WHERE Ended = 0 AND type = 0'); - $FeaturedAlbum = $DB->next_record(); - $Cache->cache_value('album_of_the_month', $FeaturedAlbum, 0); + $DB->query(' + SELECT + fa.GroupID, + tg.Name, + tg.WikiImage, + fa.ThreadID, + fa.Title + FROM featured_albums AS fa + JOIN torrents_group AS tg ON tg.ID = fa.GroupID + WHERE Ended = 0 AND type = 0'); + $FeaturedAlbum = $DB->next_record(); + $Cache->cache_value('album_of_the_month', $FeaturedAlbum, 0); } if (is_number($FeaturedAlbum['GroupID'])) { - $Artists = Artists::get_artist($FeaturedAlbum['GroupID']); + $Artists = Artists::get_artist($FeaturedAlbum['GroupID']); ?> -
      -
      - Album of the Month - [Discuss] -
      -
      - - <?=Artists::display_artists($Artists, false, false)?> - <?=$FeaturedAlbum['Name']?> - -
      -
      - +
      + Album of the Month + [Discuss] +
      +
      + + <?=Artists::display_artists($Artists, false, false)?> - <?=$FeaturedAlbum['Name']?> + +
      + + \ No newline at end of file +?> diff --git a/sections/index/private.php b/sections/index/private.php index d643a1cc7..203ac236f 100644 --- a/sections/index/private.php +++ b/sections/index/private.php @@ -3,561 +3,552 @@ $NewsCount = 5; if (!$News = $Cache->get_value('news')) { - $DB->query(" - SELECT - ID, - Title, - Body, - Time - FROM news - ORDER BY Time DESC - LIMIT $NewsCount"); - $News = $DB->to_array(false, MYSQLI_NUM, false); - $Cache->cache_value('news', $News, 3600 * 24 * 30); - if (count($News) > 0) { - $Cache->cache_value('news_latest_id', $News[0][0], 0); - $Cache->cache_value('news_latest_title', $News[0][1], 0); - } + $DB->query(" + SELECT + ID, + Title, + Body, + Time + FROM news + ORDER BY Time DESC + LIMIT $NewsCount"); + $News = $DB->to_array(false, MYSQLI_NUM, false); + $Cache->cache_value('news', $News, 3600 * 24 * 30); + if (count($News) > 0) { + $Cache->cache_value('news_latest_id', $News[0][0], 0); + $Cache->cache_value('news_latest_title', $News[0][1], 0); + } } if (count($News) > 0 && $LoggedUser['LastReadNews'] != $News[0][0]) { - $Cache->begin_transaction("user_info_heavy_$UserID"); - $Cache->update_row(false, array('LastReadNews' => $News[0][0])); - $Cache->commit_transaction(0); - $DB->query(" - UPDATE users_info - SET LastReadNews = '".$News[0][0]."' - WHERE UserID = $UserID"); - $LoggedUser['LastReadNews'] = $News[0][0]; + $Cache->begin_transaction("user_info_heavy_$UserID"); + $Cache->update_row(false, array('LastReadNews' => $News[0][0])); + $Cache->commit_transaction(0); + $DB->query(" + UPDATE users_info + SET LastReadNews = '".$News[0][0]."' + WHERE UserID = $UserID"); + $LoggedUser['LastReadNews'] = $News[0][0]; } View::show_header('News', 'bbcode,news_ajax'); ?>
      -
    + - -
    -
    Stats
    -
      - 0) { ?> -
    • Maximum users:
    • - +
      Stats
      +
        + 0) { ?> +
      • Maximum users:
      • +get_value('stats_user_count')) === false) { - $DB->query(" - SELECT COUNT(ID) - FROM users_main - WHERE Enabled = '1'"); - list($UserCount) = $DB->next_record(); - $Cache->cache_value('stats_user_count', $UserCount, 0); //inf cache -} -$UserCount = (int)$UserCount; +$UserCount = Users::get_enabled_users_count(); ?> -
      • Enabled users: Details
      • -Enabled users: Details +get_value('stats_users')) === false) { - $DB->query(" - SELECT COUNT(ID) - FROM users_main - WHERE Enabled = '1' - AND LastAccess > '".time_minus(3600 * 24)."'"); - list($UserStats['Day']) = $DB->next_record(); - - $DB->query(" - SELECT COUNT(ID) - FROM users_main - WHERE Enabled = '1' - AND LastAccess > '".time_minus(3600 * 24 * 7)."'"); - list($UserStats['Week']) = $DB->next_record(); - - $DB->query(" - SELECT COUNT(ID) - FROM users_main - WHERE Enabled = '1' - AND LastAccess > '".time_minus(3600 * 24 * 30)."'"); - list($UserStats['Month']) = $DB->next_record(); - - $Cache->cache_value('stats_users', $UserStats, 0); + $DB->query(" + SELECT COUNT(ID) + FROM users_main + WHERE Enabled = '1' + AND LastAccess > '".time_minus(3600 * 24)."'"); + list($UserStats['Day']) = $DB->next_record(); + + $DB->query(" + SELECT COUNT(ID) + FROM users_main + WHERE Enabled = '1' + AND LastAccess > '".time_minus(3600 * 24 * 7)."'"); + list($UserStats['Week']) = $DB->next_record(); + + $DB->query(" + SELECT COUNT(ID) + FROM users_main + WHERE Enabled = '1' + AND LastAccess > '".time_minus(3600 * 24 * 30)."'"); + list($UserStats['Month']) = $DB->next_record(); + + $Cache->cache_value('stats_users', $UserStats, 0); } ?> -
      • Users active today: (%)
      • -
      • Users active this week: (%)
      • -
      • Users active this month: (%)
      • -Users active today: (%) +
      • Users active this week: (%)
      • +
      • Users active this month: (%)
      • +get_value('stats_torrent_count')) === false) { - $DB->query(" - SELECT COUNT(ID) - FROM torrents"); - list($TorrentCount) = $DB->next_record(); - $Cache->cache_value('stats_torrent_count', $TorrentCount, 604800); // staggered 1 week cache + $DB->query(" + SELECT COUNT(ID) + FROM torrents"); + list($TorrentCount) = $DB->next_record(); + $Cache->cache_value('stats_torrent_count', $TorrentCount, 604800); // staggered 1 week cache } if (($AlbumCount = $Cache->get_value('stats_album_count')) === false) { - $DB->query(" - SELECT COUNT(ID) - FROM torrents_group - WHERE CategoryID = '1'"); - list($AlbumCount) = $DB->next_record(); - $Cache->cache_value('stats_album_count', $AlbumCount, 604830); // staggered 1 week cache + $DB->query(" + SELECT COUNT(ID) + FROM torrents_group + WHERE CategoryID = '1'"); + list($AlbumCount) = $DB->next_record(); + $Cache->cache_value('stats_album_count', $AlbumCount, 604830); // staggered 1 week cache } if (($ArtistCount = $Cache->get_value('stats_artist_count')) === false) { - $DB->query(" - SELECT COUNT(ArtistID) - FROM artists_group"); - list($ArtistCount) = $DB->next_record(); - $Cache->cache_value('stats_artist_count', $ArtistCount, 604860); // staggered 1 week cache + $DB->query(" + SELECT COUNT(ArtistID) + FROM artists_group"); + list($ArtistCount) = $DB->next_record(); + $Cache->cache_value('stats_artist_count', $ArtistCount, 604860); // staggered 1 week cache } if (($PerfectCount = $Cache->get_value('stats_perfect_count')) === false) { - $DB->query(" - SELECT COUNT(ID) - FROM torrents - WHERE ((LogScore = 100 AND Format = 'FLAC') - OR (Media = 'Vinyl' AND Format = 'FLAC') - OR (Media = 'WEB' AND Format = 'FLAC') - OR (Media = 'DVD' AND Format = 'FLAC') - OR (Media = 'Soundboard' AND Format = 'FLAC') + $DB->query(" + SELECT COUNT(ID) + FROM torrents + WHERE ((LogScore = 100 AND Format = 'FLAC') + OR (Media = 'Vinyl' AND Format = 'FLAC') + OR (Media = 'WEB' AND Format = 'FLAC') + OR (Media = 'DVD' AND Format = 'FLAC') + OR (Media = 'Soundboard' AND Format = 'FLAC') OR (Media = 'BD' AND Format = 'FLAC') OR (Media = 'SASD' AND Format = 'FLAC') OR (Media = 'BD' AND Format = 'FLAC') - )"); - list($PerfectCount) = $DB->next_record(); - $Cache->cache_value('stats_perfect_count', $PerfectCount, 3600); // staggered 1 week cache + )"); + list($PerfectCount) = $DB->next_record(); + $Cache->cache_value('stats_perfect_count', $PerfectCount, 3600); // staggered 1 week cache } ?> -
      • Torrents:
      • -
      • Releases:
      • -
      • Artists:
      • -
      • "Perfect" FLACs:
      • -Torrents: +
      • Releases:
      • +
      • Artists:
      • +
      • "Perfect" FLACs:
      • +get_value('stats_collages')) === false) { - $DB->query(" - SELECT COUNT(ID) - FROM collages"); - list($CollageCount) = $DB->next_record(); - $Cache->cache_value('stats_collages', $CollageCount, 11280); //staggered 1 week cache + $DB->query(" + SELECT COUNT(ID) + FROM collages"); + list($CollageCount) = $DB->next_record(); + $Cache->cache_value('stats_collages', $CollageCount, 11280); //staggered 1 week cache } ?> -
      • Collages:
      • -Collages: +get_value('stats_requests')) === false) { - $DB->query(" - SELECT COUNT(ID) - FROM requests"); - list($RequestCount) = $DB->next_record(); - $DB->query(" - SELECT COUNT(ID) - FROM requests - WHERE FillerID > 0"); - list($FilledCount) = $DB->next_record(); - $Cache->cache_value('stats_requests', array($RequestCount, $FilledCount), 11280); + $DB->query(" + SELECT COUNT(ID) + FROM requests"); + list($RequestCount) = $DB->next_record(); + $DB->query(" + SELECT COUNT(ID) + FROM requests + WHERE FillerID > 0"); + list($FilledCount) = $DB->next_record(); + $Cache->cache_value('stats_requests', array($RequestCount, $FilledCount), 11280); } else { - list($RequestCount, $FilledCount) = $RequestStats; + list($RequestCount, $FilledCount) = $RequestStats; } $RequestPercentage = $RequestCount > 0 ? $FilledCount / $RequestCount * 100: 0; ?> -
      • Requests: (% filled)
      • -Requests: (% filled) +get_value('stats_snatches')) { ?> -
      • Snatches:
      • -Snatches: +get_value('stats_peers')) === false) { - //Cache lock! - $PeerStatsLocked = $Cache->get_value('stats_peers_lock'); - if (!$PeerStatsLocked) { - $Cache->cache_value('stats_peers_lock', 1, 30); - $DB->query(" - SELECT IF(remaining=0,'Seeding','Leeching') AS Type, COUNT(uid) - FROM xbt_files_users - WHERE active = 1 - GROUP BY Type"); - $PeerCount = $DB->to_array(0, MYSQLI_NUM, false); - $SeederCount = $PeerCount['Seeding'][1] ?: 0; - $LeecherCount = $PeerCount['Leeching'][1] ?: 0; - $Cache->cache_value('stats_peers', array($LeecherCount, $SeederCount), 1209600); // 2 week cache - $Cache->delete_value('stats_peers_lock'); - } + //Cache lock! + $PeerStatsLocked = $Cache->get_value('stats_peers_lock'); + if (!$PeerStatsLocked) { + $Cache->cache_value('stats_peers_lock', 1, 30); + $DB->query(" + SELECT IF(remaining=0,'Seeding','Leeching') AS Type, COUNT(uid) + FROM xbt_files_users + WHERE active = 1 + GROUP BY Type"); + $PeerCount = $DB->to_array(0, MYSQLI_NUM, false); + $SeederCount = $PeerCount['Seeding'][1] ?: 0; + $LeecherCount = $PeerCount['Leeching'][1] ?: 0; + $Cache->cache_value('stats_peers', array($LeecherCount, $SeederCount), 1209600); // 2 week cache + $Cache->delete_value('stats_peers_lock'); + } } else { - $PeerStatsLocked = false; - list($LeecherCount, $SeederCount) = $PeerStats; + $PeerStatsLocked = false; + list($LeecherCount, $SeederCount) = $PeerStats; } if (!$PeerStatsLocked) { - $Ratio = Format::get_ratio_html($SeederCount, $LeecherCount); - $PeerCount = number_format($SeederCount + $LeecherCount); - $SeederCount = number_format($SeederCount); - $LeecherCount = number_format($LeecherCount); + $Ratio = Format::get_ratio_html($SeederCount, $LeecherCount); + $PeerCount = number_format($SeederCount + $LeecherCount); + $SeederCount = number_format($SeederCount); + $LeecherCount = number_format($LeecherCount); } else { - $PeerCount = $SeederCount = $LeecherCount = $Ratio = 'Server busy'; + $PeerCount = $SeederCount = $LeecherCount = $Ratio = 'Server busy'; } ?> -
      • Peers:
      • -
      • Seeders:
      • -
      • Leechers:
      • -
      • Seeder/leecher ratio:
      • -
      -
    -Peers: +
  • Seeders:
  • +
  • Leechers:
  • +
  • Seeder/leecher ratio:
  • + + +get_value('polls_featured')) === false) { - $DB->query(" - SELECT TopicID - FROM forums_polls - ORDER BY Featured DESC - LIMIT 1"); - list($TopicID) = $DB->next_record(); - $Cache->cache_value('polls_featured', $TopicID, 0); + $DB->query(" + SELECT TopicID + FROM forums_polls + ORDER BY Featured DESC + LIMIT 1"); + list($TopicID) = $DB->next_record(); + $Cache->cache_value('polls_featured', $TopicID, 0); } if ($TopicID) { - if (($Poll = $Cache->get_value("polls_$TopicID")) === false) { - $DB->query(" - SELECT Question, Answers, Featured, Closed - FROM forums_polls - WHERE TopicID = '$TopicID'"); - list($Question, $Answers, $Featured, $Closed) = $DB->next_record(MYSQLI_NUM, array(1)); - $Answers = unserialize($Answers); - $DB->query(" - SELECT Vote, COUNT(UserID) - FROM forums_polls_votes - WHERE TopicID = '$TopicID' - AND Vote != '0' - GROUP BY Vote"); - $VoteArray = $DB->to_array(false, MYSQLI_NUM); - - $Votes = array(); - foreach ($VoteArray as $VoteSet) { - list($Key,$Value) = $VoteSet; - $Votes[$Key] = $Value; - } - - for ($i = 1, $il = count($Answers); $i <= $il; ++$i) { - if (!isset($Votes[$i])) { - $Votes[$i] = 0; - } - } - $Cache->cache_value("polls_$TopicID", array($Question, $Answers, $Votes, $Featured, $Closed), 0); - } else { - list($Question, $Answers, $Votes, $Featured, $Closed) = $Poll; - } - - if (!empty($Votes)) { - $TotalVotes = array_sum($Votes); - $MaxVotes = max($Votes); - } else { - $TotalVotes = 0; - $MaxVotes = 0; - } - - $DB->query(" - SELECT Vote - FROM forums_polls_votes - WHERE UserID = '".$LoggedUser['ID']."' - AND TopicID = '$TopicID'"); - list($UserResponse) = $DB->next_record(); - if (!empty($UserResponse) && $UserResponse != 0) { - $Answers[$UserResponse] = '» '.$Answers[$UserResponse]; - } + if (($Poll = $Cache->get_value("polls_$TopicID")) === false) { + $DB->query(" + SELECT Question, Answers, Featured, Closed + FROM forums_polls + WHERE TopicID = '$TopicID'"); + list($Question, $Answers, $Featured, $Closed) = $DB->next_record(MYSQLI_NUM, array(1)); + $Answers = unserialize($Answers); + $DB->query(" + SELECT Vote, COUNT(UserID) + FROM forums_polls_votes + WHERE TopicID = '$TopicID' + AND Vote != '0' + GROUP BY Vote"); + $VoteArray = $DB->to_array(false, MYSQLI_NUM); + + $Votes = []; + foreach ($VoteArray as $VoteSet) { + list($Key,$Value) = $VoteSet; + $Votes[$Key] = $Value; + } + + for ($i = 1, $il = count($Answers); $i <= $il; ++$i) { + if (!isset($Votes[$i])) { + $Votes[$i] = 0; + } + } + $Cache->cache_value("polls_$TopicID", array($Question, $Answers, $Votes, $Featured, $Closed), 0); + } else { + list($Question, $Answers, $Votes, $Featured, $Closed) = $Poll; + } + + if (!empty($Votes)) { + $TotalVotes = array_sum($Votes); + $MaxVotes = max($Votes); + } else { + $TotalVotes = 0; + $MaxVotes = 0; + } + + $DB->query(" + SELECT Vote + FROM forums_polls_votes + WHERE UserID = '".$LoggedUser['ID']."' + AND TopicID = '$TopicID'"); + list($UserResponse) = $DB->next_record(); + if (!empty($UserResponse) && $UserResponse != 0) { + $Answers[$UserResponse] = '» '.$Answers[$UserResponse]; + } ?> -
    -
    Poll
    -
    -

    - -
      - $Answer) { - if ($TotalVotes > 0) { - $Ratio = $Votes[$i] / $MaxVotes; - $Percent = $Votes[$i] / $TotalVotes; - } else { - $Ratio = 0; - $Percent = 0; - } -?>
    • (%)
    • -
    • - - - -
      -
    • - -
    - Votes:
    - -
    -
    - - - - $Answer) { ?> - -
    - -


    - -
    -
    - -
    Topic: Visit -
    -
    - +
    Poll
    +
    +

    + +
      + $Answer) { + if ($TotalVotes > 0) { + $Ratio = $Votes[$i] / $MaxVotes; + $Percent = $Votes[$i] / $TotalVotes; + } else { + $Ratio = 0; + $Percent = 0; + } +?>
    • (%)
    • +
    • + + + +
      +
    • + +
    + Votes:
    + +
    +
    + + + + $Answer) { ?> + +
    + +


    + +
    +
    + +
    Topic: Visit +
    + + - -
    - +
    +get_value('recommend'); $Recommend_artists = $Cache->get_value('recommend_artists'); if (!is_array($Recommend) || !is_array($Recommend_artists)) { - $DB->query(" - SELECT - tr.GroupID, - tr.UserID, - u.Username, - tg.Name, - tg.TagList - FROM torrents_recommended AS tr - JOIN torrents_group AS tg ON tg.ID = tr.GroupID - LEFT JOIN users_main AS u ON u.ID = tr.UserID - ORDER BY tr.Time DESC - LIMIT 10"); - $Recommend = $DB->to_array(); - $Cache->cache_value('recommend', $Recommend, 1209600); - - $Recommend_artists = Artists::get_artists($DB->collect('GroupID')); - $Cache->cache_value('recommend_artists', $Recommend_artists, 1209600); + $DB->query(" + SELECT + tr.GroupID, + tr.UserID, + u.Username, + tg.Name, + tg.TagList + FROM torrents_recommended AS tr + JOIN torrents_group AS tg ON tg.ID = tr.GroupID + LEFT JOIN users_main AS u ON u.ID = tr.UserID + ORDER BY tr.Time DESC + LIMIT 10"); + $Recommend = $DB->to_array(); + $Cache->cache_value('recommend', $Recommend, 1209600); + + $Recommend_artists = Artists::get_artists($DB->collect('GroupID')); + $Cache->cache_value('recommend_artists', $Recommend_artists, 1209600); } if (count($Recommend) >= 4) { $Cache->increment('usage_index'); ?> - + + + + (by ) + + + + + +
    - time()) { - continue; - } + list($NewsID, $Title, $Body, $NewsTime) = $NewsItem; + if (strtotime($NewsTime) > time()) { + continue; + } ?> -
    -
    - - - - Edit - - Hide -
    - -
    -
    - ($NewsCount - 1)) { - break; - } +
    +
    + + + - Edit + + Hide +
    + +
    +
    + ($NewsCount - 1)) { + break; + } } ?> -
    -
    - Click to load more news. To browse old news posts, click here. -
    -
    -
    +
    +
    + Click to load more news. To browse old news posts, click here. +
    +
    + -true)); function contest() { - global $DB, $Cache, $LoggedUser; - - list($Contest, $TotalPoints) = $Cache->get_value('contest'); - if (!$Contest) { - $DB->query(" - SELECT - UserID, - SUM(Points), - Username - FROM users_points AS up - JOIN users_main AS um ON um.ID = up.UserID - GROUP BY UserID - ORDER BY SUM(Points) DESC - LIMIT 20"); - $Contest = $DB->to_array(); - - $DB->query(" - SELECT SUM(Points) - FROM users_points"); - list($TotalPoints) = $DB->next_record(); - - $Cache->cache_value('contest', array($Contest, $TotalPoints), 600); - } + global $DB, $Cache, $LoggedUser; + + list($Contest, $TotalPoints) = $Cache->get_value('contest'); + if (!$Contest) { + $DB->query(" + SELECT + UserID, + SUM(Points), + Username + FROM users_points AS up + JOIN users_main AS um ON um.ID = up.UserID + GROUP BY UserID + ORDER BY SUM(Points) DESC + LIMIT 20"); + $Contest = $DB->to_array(); + + $DB->query(" + SELECT SUM(Points) + FROM users_points"); + list($TotalPoints) = $DB->next_record(); + + $Cache->cache_value('contest', array($Contest, $TotalPoints), 600); + } ?> -
    -
    Quality time scoreboard
    -
    -
      - +
      Quality time scoreboard
      +
      +
        + -
      1. ()
      2. - -
      - Total uploads:
      - Full scoreboard -
      -
    - - () + + + Total uploads:
    + Full scoreboard +
    + + + diff --git a/sections/index/public.php b/sections/index/public.php index 1c295177b..b49da2f8d 100644 --- a/sections/index/public.php +++ b/sections/index/public.php @@ -9,6 +9,7 @@ echo << +

    Orpheus with his lute made trees
    And the mountain tops that freeze
    diff --git a/sections/index/vanity_album.php b/sections/index/vanity_album.php index a35d2e1c8..3834bd6fd 100644 --- a/sections/index/vanity_album.php +++ b/sections/index/vanity_album.php @@ -1,30 +1,30 @@ -get_value('vanity_house_album'); if ($FeaturedAlbum === false) { - $DB->query(' - SELECT - fa.GroupID, - tg.Name, - tg.WikiImage, - fa.ThreadID, - fa.Title - FROM featured_albums AS fa - JOIN torrents_group AS tg ON tg.ID = fa.GroupID - WHERE Ended = 0 AND type = 1'); - $FeaturedAlbum = $DB->next_record(); - $Cache->cache_value('vanity_house_album', $FeaturedAlbum, 0); + $DB->query(' + SELECT + fa.GroupID, + tg.Name, + tg.WikiImage, + fa.ThreadID, + fa.Title + FROM featured_albums AS fa + JOIN torrents_group AS tg ON tg.ID = fa.GroupID + WHERE Ended = 0 AND type = 1'); + $FeaturedAlbum = $DB->next_record(); + $Cache->cache_value('vanity_house_album', $FeaturedAlbum, 0); } if (is_number($FeaturedAlbum['GroupID'])) { - $Artists = Artists::get_artist($FeaturedAlbum['GroupID']); + $Artists = Artists::get_artist($FeaturedAlbum['GroupID']); ?> -

    -
    Vanity House [Discuss]
    -
    - - <?=Artists::display_artists($Artists, false, false)?> - <?=$FeaturedAlbum['Name']?> - -
    -
    - +
    Vanity House [Discuss]
    +
    + + <?=Artists::display_artists($Artists, false, false)?> - <?=$FeaturedAlbum['Name']?> + +
    + + \ No newline at end of file +?> diff --git a/sections/locked/default.php b/sections/locked/default.php index 62ea4b7ac..13d49903b 100644 --- a/sections/locked/default.php +++ b/sections/locked/default.php @@ -1,12 +1,14 @@ -

    Locked Account

    - +

    Your account has been locked. Please send a Staff PM to find out how this happened.

    -
    -
    -

    Site log

    -
    -
    -
    - - - - - -
    Search for: - /> -   - -
    -
    -
    +
    +

    Site log

    +
    +
    +
    + + + + + +
    Search for: + /> +   + +
    +
    +
    - LOG_ENTRIES_PER_PAGE) { ?> - - - - - - - - - -has_results()) { ?> - - LOG_ENTRIES_PER_PAGE) { ?> + + +
    TimeMessage
    Search request failed ().
    Nothing found!
    + + + + + + +has_results()) { ?> + +next_record()) { - $MessageParts = explode(' ', $Message); - $Message = ''; - $Color = $Colon = false; - for ($i = 0, $PartCount = sizeof($MessageParts); $i < $PartCount; $i++) { - if ((strpos($MessageParts[$i], 'https://'.SSL_SITE_URL) === 0 - && $Offset = strlen('https://'.SSL_SITE_URL.'/')) - || (strpos($MessageParts[$i], 'http://'.NONSSL_SITE_URL) === 0 - && $Offset = strlen('http://'.NONSSL_SITE_URL.'/')) - ) { - $MessageParts[$i] = ''.substr($MessageParts[$i], $Offset).''; - } - switch ($MessageParts[$i]) { - case 'Torrent': - case 'torrent': - $TorrentID = $MessageParts[$i + 1]; - if (is_numeric($TorrentID)) { - $Message = $Message.' '.$MessageParts[$i]." $TorrentID"; - $i++; - } else { - $Message = $Message.' '.$MessageParts[$i]; - } - break; - case 'Request': - $RequestID = $MessageParts[$i + 1]; - if (is_numeric($RequestID)) { - $Message = $Message.' '.$MessageParts[$i]." $RequestID"; - $i++; - } else { - $Message = $Message.' '.$MessageParts[$i]; - } - break; - case 'Artist': - case 'artist': - $ArtistID = $MessageParts[$i + 1]; - if (is_numeric($ArtistID)) { - $Message = $Message.' '.$MessageParts[$i]." $ArtistID"; - $i++; - } else { - $Message = $Message.' '.$MessageParts[$i]; - } - break; - case 'group': - case 'Group': - $GroupID = $MessageParts[$i + 1]; - if (is_numeric($GroupID)) { - $Message = $Message.' '.$MessageParts[$i]." $GroupID"; - } else { - $Message = $Message.' '.$MessageParts[$i]; - } - $i++; - break; - case 'by': - $UserID = 0; - $User = ''; - $URL = ''; - if ($MessageParts[$i + 1] == 'user') { - $i++; - if (is_numeric($MessageParts[$i + 1])) { - $UserID = $MessageParts[++$i]; - } - $URL = "user $UserID (".substr($MessageParts[++$i], 1, -1).')'; - } elseif (in_array($MessageParts[$i - 1], array('deleted', 'uploaded', 'edited', 'created', 'recovered'))) { - $User = $MessageParts[++$i]; - if (substr($User, -1) == ':') { - $User = substr($User, 0, -1); - $Colon = true; - } - if (!isset($Usernames[$User])) { - $DB->query(" - SELECT ID - FROM users_main - WHERE Username = _utf8 '" . db_string($User) . "' - COLLATE utf8_bin"); - list($UserID) = $DB->next_record(); - $Usernames[$User] = $UserID ? $UserID : ''; - } else { - $UserID = $Usernames[$User]; - } - $DB->set_query_id($Log); - $URL = $Usernames[$User] ? "$User".($Colon ? ':' : '') : $User; - } - $Message = "$Message by $URL"; - break; - case 'uploaded': - if ($Color === false) { - $Color = 'green'; - } - $Message = $Message.' '.$MessageParts[$i]; - break; - case 'deleted': - if ($Color === false || $Color === 'green') { - $Color = 'red'; - } - $Message = $Message.' '.$MessageParts[$i]; - break; - case 'edited': - if ($Color === false) { - $Color = 'blue'; - } - $Message = $Message.' '.$MessageParts[$i]; - break; - case 'un-filled': - if ($Color === false) { - $Color = ''; - } - $Message = $Message.' '.$MessageParts[$i]; - break; - case 'marked': - if ($i == 1) { - $User = $MessageParts[$i - 1]; - if (!isset($Usernames[$User])) { - $DB->query(" - SELECT ID - FROM users_main - WHERE Username = _utf8 '" . db_string($User) . "' - COLLATE utf8_bin"); - list($UserID) = $DB->next_record(); - $Usernames[$User] = $UserID ? $UserID : ''; - $DB->set_query_id($Log); - } else { - $UserID = $Usernames[$User]; - } - $URL = $Usernames[$User] ? "$User" : $User; - $Message = $URL." ".$MessageParts[$i]; - } else { - $Message = $Message.' '.$MessageParts[$i]; - } - break; - case 'Collage': - $CollageID = $MessageParts[$i + 1]; - if (is_numeric($CollageID)) { - $Message = $Message.' '.$MessageParts[$i]." $CollageID"; - $i++; - } else { - $Message = $Message.' '.$MessageParts[$i]; - } - break; - default: - $Message = $Message.' '.$MessageParts[$i]; - } - } - $Row = $Row === 'a' ? 'b' : 'a'; + $MessageParts = explode(' ', $Message); + $Message = ''; + $Color = $Colon = false; + for ($i = 0, $PartCount = sizeof($MessageParts); $i < $PartCount; $i++) { + if ((strpos($MessageParts[$i], 'https://'.SSL_SITE_URL) === 0 + && $Offset = strlen('https://'.SSL_SITE_URL.'/')) + || (strpos($MessageParts[$i], 'http://'.NONSSL_SITE_URL) === 0 + && $Offset = strlen('http://'.NONSSL_SITE_URL.'/')) + ) { + $MessageParts[$i] = ''.substr($MessageParts[$i], $Offset).''; + } + switch ($MessageParts[$i]) { + case 'Torrent': + case 'torrent': + $TorrentID = $MessageParts[$i + 1]; + if (is_numeric($TorrentID)) { + $Message = $Message.' '.$MessageParts[$i]." $TorrentID"; + $i++; + } else { + $Message = $Message.' '.$MessageParts[$i]; + } + break; + case 'Request': + $RequestID = $MessageParts[$i + 1]; + if (is_numeric($RequestID)) { + $Message = $Message.' '.$MessageParts[$i]." $RequestID"; + $i++; + } else { + $Message = $Message.' '.$MessageParts[$i]; + } + break; + case 'Artist': + case 'artist': + $ArtistID = $MessageParts[$i + 1]; + if (is_numeric($ArtistID)) { + $Message = $Message.' '.$MessageParts[$i]." $ArtistID"; + $i++; + } else { + $Message = $Message.' '.$MessageParts[$i]; + } + break; + case 'group': + case 'Group': + $GroupID = $MessageParts[$i + 1]; + if (is_numeric($GroupID)) { + $Message = $Message.' '.$MessageParts[$i]." $GroupID"; + } else { + $Message = $Message.' '.$MessageParts[$i]; + } + $i++; + break; + case 'by': + $UserID = 0; + $User = ''; + $URL = ''; + if ($MessageParts[$i + 1] == 'user') { + $i++; + if (is_numeric($MessageParts[$i + 1])) { + $UserID = $MessageParts[++$i]; + } + $URL = "user $UserID (".substr($MessageParts[++$i], 1, -1).')'; + } elseif (in_array($MessageParts[$i - 1], array('deleted', 'uploaded', 'edited', 'created', 'recovered'))) { + $User = $MessageParts[++$i]; + if (substr($User, -1) == ':') { + $User = substr($User, 0, -1); + $Colon = true; + } + if (!isset($Usernames[$User])) { + $DB->query(" + SELECT ID + FROM users_main + WHERE Username = _utf8 '" . db_string($User) . "' + COLLATE utf8_bin"); + list($UserID) = $DB->next_record(); + $Usernames[$User] = $UserID ? $UserID : ''; + } else { + $UserID = $Usernames[$User]; + } + $DB->set_query_id($Log); + $URL = $Usernames[$User] ? "$User".($Colon ? ':' : '') : $User; + } + $Message = "$Message by $URL"; + break; + case 'uploaded': + if ($Color === false) { + $Color = 'green'; + } + $Message = $Message.' '.$MessageParts[$i]; + break; + case 'deleted': + if ($Color === false || $Color === 'green') { + $Color = 'red'; + } + $Message = $Message.' '.$MessageParts[$i]; + break; + case 'edited': + if ($Color === false) { + $Color = 'blue'; + } + $Message = $Message.' '.$MessageParts[$i]; + break; + case 'un-filled': + if ($Color === false) { + $Color = ''; + } + $Message = $Message.' '.$MessageParts[$i]; + break; + case 'marked': + if ($i == 1) { + $User = $MessageParts[$i - 1]; + if (!isset($Usernames[$User])) { + $DB->query(" + SELECT ID + FROM users_main + WHERE Username = _utf8 '" . db_string($User) . "' + COLLATE utf8_bin"); + list($UserID) = $DB->next_record(); + $Usernames[$User] = $UserID ? $UserID : ''; + $DB->set_query_id($Log); + } else { + $UserID = $Usernames[$User]; + } + $URL = $Usernames[$User] ? "$User" : $User; + $Message = $URL." ".$MessageParts[$i]; + } else { + $Message = $Message.' '.$MessageParts[$i]; + } + break; + case 'Collage': + $CollageID = $MessageParts[$i + 1]; + if (is_numeric($CollageID)) { + $Message = $Message.' '.$MessageParts[$i]." $CollageID"; + $i++; + } else { + $Message = $Message.' '.$MessageParts[$i]; + } + break; + default: + $Message = $Message.' '.$MessageParts[$i]; + } + } + $Row = $Row === 'a' ? 'b' : 'a'; ?> - - - - -" id="log_"> + + + + -
    TimeMessage
    Search request failed ().
    Nothing found!
    - - - style="color: ;"> -
    + + + style="color: ;"> +
    - LOG_ENTRIES_PER_PAGE) { ?> - - + + LOG_ENTRIES_PER_PAGE) { ?> + +
    - diff --git a/sections/log/sphinx.php b/sections/log/sphinx.php index ae0ca6b3c..37aa11ef0 100644 --- a/sections/log/sphinx.php +++ b/sections/log/sphinx.php @@ -1,56 +1,56 @@ query(" - SELECT ID, Message, Time - FROM log - ORDER BY ID DESC - LIMIT $Offset, ".LOG_ENTRIES_PER_PAGE); - $NumResults = $DB->record_count(); - if (!$NumResults) { - $TotalMatches = 0; - } elseif ($NumResults == LOG_ENTRIES_PER_PAGE) { - // This is a lot faster than SQL_CALC_FOUND_ROWS - $SphQL = new SphinxqlQuery(); - $Result = $SphQL->select('id')->from('log, log_delta')->limit(0, 1, 1)->query(); - $Debug->log_var($Result, '$Result'); - $TotalMatches = min(SPHINX_MAX_MATCHES, $Result->get_meta('total_found')); - } else { - $TotalMatches = $NumResults + $Offset; - } - $QueryStatus = 0; + $Log = $DB->query(" + SELECT ID, Message, Time + FROM log + ORDER BY ID DESC + LIMIT $Offset, ".LOG_ENTRIES_PER_PAGE); + $NumResults = $DB->record_count(); + if (!$NumResults) { + $TotalMatches = 0; + } elseif ($NumResults == LOG_ENTRIES_PER_PAGE) { + // This is a lot faster than SQL_CALC_FOUND_ROWS + $SphQL = new SphinxqlQuery(); + $Result = $SphQL->select('id')->from('log, log_delta')->limit(0, 1, 1)->query(); + $Debug->log_var($Result, '$Result'); + $TotalMatches = min(SPHINX_MAX_MATCHES, $Result->get_meta('total_found')); + } else { + $TotalMatches = $NumResults + $Offset; + } + $QueryStatus = 0; } else { - $Page = min(SPHINX_MAX_MATCHES / TORRENTS_PER_PAGE, $Page); - $SphQL = new SphinxqlQuery(); - $SphQL->select('id') - ->from('log, log_delta') - ->where_match($_GET['search'], 'message') - ->order_by('id', 'DESC') - ->limit($Offset, LOG_ENTRIES_PER_PAGE, $Offset + LOG_ENTRIES_PER_PAGE); + $Page = min(SPHINX_MAX_MATCHES / TORRENTS_PER_PAGE, $Page); + $SphQL = new SphinxqlQuery(); + $SphQL->select('id') + ->from('log, log_delta') + ->where_match($_GET['search'], 'message') + ->order_by('id', 'DESC') + ->limit($Offset, LOG_ENTRIES_PER_PAGE, $Offset + LOG_ENTRIES_PER_PAGE); - $Result = $SphQL->query(); - $Debug->log_var($Result, '$Result'); - $Debug->set_flag('Finished SphQL query'); - if ($QueryStatus = $Result->Errno) { - $QueryError = $Result->Error; - } - $NumResults = $Result->get_result_info('num_rows'); - $TotalMatches = min(SPHINX_MAX_MATCHES, $Result->get_meta('total_found')); - if ($NumResults > 0) { - $LogIDs = $Result->collect('id'); - $Log = $DB->query(' - SELECT ID, Message, Time - FROM log - WHERE ID IN ('.implode(',', $LogIDs).') - ORDER BY ID DESC'); - } else { - $Log = $DB->query(' - SET @nothing = 0'); - } + $Result = $SphQL->query(); + $Debug->log_var($Result, '$Result'); + $Debug->set_flag('Finished SphQL query'); + if ($QueryStatus = $Result->Errno) { + $QueryError = $Result->Error; + } + $NumResults = $Result->get_result_info('num_rows'); + $TotalMatches = min(SPHINX_MAX_MATCHES, $Result->get_meta('total_found')); + if ($NumResults > 0) { + $LogIDs = $Result->collect('id'); + $Log = $DB->query(' + SELECT ID, Message, Time + FROM log + WHERE ID IN ('.implode(',', $LogIDs).') + ORDER BY ID DESC'); + } else { + $Log = $DB->query(' + SET @nothing = 0'); + } } diff --git a/sections/log/sql.php b/sections/log/sql.php index f9bb0cbfe..9debc06d8 100644 --- a/sections/log/sql.php +++ b/sections/log/sql.php @@ -2,35 +2,35 @@ list($Page, $Limit) = Format::page_limit(LOG_ENTRIES_PER_PAGE); if (!empty($_GET['search'])) { - $Search = db_string($_GET['search']); + $Search = db_string($_GET['search']); } else { - $Search = false; + $Search = false; } $Words = explode(' ', $Search); $SQL = ' - SELECT - SQL_CALC_FOUND_ROWS - ID, - Message, - Time - FROM log '; + SELECT + SQL_CALC_FOUND_ROWS + ID, + Message, + Time + FROM log '; if ($Search) { - $SQL .= "WHERE Message LIKE '%"; - $SQL .= implode("%' AND Message LIKE '%", $Words); - $SQL .= "%' "; + $SQL .= "WHERE Message LIKE '%"; + $SQL .= implode("%' AND Message LIKE '%", $Words); + $SQL .= "%' "; } if (!check_perms('site_view_full_log')) { - if ($Search) { - $SQL .= ' AND '; - } else { - $SQL .= ' WHERE '; - } - $SQL .= " Time>'".time_minus(3600 * 24 * 28)."' "; + if ($Search) { + $SQL .= ' AND '; + } else { + $SQL .= ' WHERE '; + } + $SQL .= " Time>'".time_minus(3600 * 24 * 28)."' "; } $SQL .= " - ORDER BY ID DESC - LIMIT $Limit"; + ORDER BY ID DESC + LIMIT $Limit"; $Log = $DB->query($SQL); $DB->query('SELECT FOUND_ROWS()'); diff --git a/sections/logchecker/index.php b/sections/logchecker/index.php index 712c74610..8a79b7663 100644 --- a/sections/logchecker/index.php +++ b/sections/logchecker/index.php @@ -1,28 +1,28 @@ - $TmpFile, 'name' => $TmpFile); + $ValidateChecksum = false; + $TmpFile = tempnam('/tmp', 'log_'); + file_put_contents($TmpFile, $_POST["pastelog"]); + $File = array('tmp_name' => $TmpFile, 'name' => $TmpFile); } else { - error('No log file uploaded or file is empty.'); + error('No log file uploaded or file is empty.'); } @@ -22,11 +22,11 @@ echo << - Test Another Log File - Upload Missing Logs + Test Another Log File + Upload Missing Logs
    -

    Logchecker Test Results

    +

    Logchecker Test Results

    HTML; $Log = new Logchecker(); @@ -36,60 +36,60 @@ list($Score, $Bad, $Checksum, $Text) = $Log->parse(); if($Score == 100) { - $Color = '#418B00'; + $Color = '#418B00'; } elseif($Score > 90) { - $Color = '#74C42E'; + $Color = '#74C42E'; } elseif($Score > 75) { - $Color = '#FFAA00'; + $Color = '#FFAA00'; } elseif($Score > 50) { - $Color = '#FF5E00'; + $Color = '#FF5E00'; } else { - $Color = '#FF0000'; + $Color = '#FF0000'; } if (!$Checksum) { - echo << - Trumpable For: -

    - Bad/No Checksum(s) - + echo << + Trumpable For: +

    + Bad/No Checksum(s) + HTML; } echo << - Score: {$Score} (out of 100) - +
    + Score: {$Score} (out of 100) +
    HTML; if($Bad){ - echo << -

    Log validation report:

    -
      + echo << +

      Log validation report:

      +
        HTML; - foreach($Bad as $Property) { - echo "\t\t\t
      • {$Property}
      • "; - } + foreach($Bad as $Property) { + echo "\t\t\t
      • {$Property}
      • "; + } echo << - +
      + HTML; } echo << -
      {$Text}
      - +
      +
      {$Text}
      +
    HTML; View::show_footer(); if (!empty($TmpFile) && is_file($TmpFile)) { - unlink($TmpFile); + unlink($TmpFile); } diff --git a/sections/logchecker/take_upload.php b/sections/logchecker/take_upload.php index 1b59143b2..157eece41 100644 --- a/sections/logchecker/take_upload.php +++ b/sections/logchecker/take_upload.php @@ -5,7 +5,7 @@ enforce_login(); if (empty($_POST['torrentid'])) { - error('No torrent is selected.'); + error('No torrent is selected.'); } $TorrentID = intval($_POST['torrentid']) ?? null; // Some browsers will report an empty file when you submit, prune those out @@ -18,105 +18,105 @@ $Extra = check_perms('users_mod') ? '' : " AND t.UserID = '{$LoggedUser['ID']}'"; $DB->query(" - SELECT t.ID, t.GroupID - FROM torrents t - WHERE t.ID = {$TorrentID} AND t.HasLog='1'" . $Extra); + SELECT t.ID, t.GroupID + FROM torrents t + WHERE t.ID = {$TorrentID} AND t.HasLog='1'" . $Extra); -$DetailsArray = array(); +$DetailsArray = []; $Logchecker = new Logchecker(); if ($TorrentID != 0 && $DB->has_results() && $FileCount > 0) { - list($TorrentID, $GroupID) = $DB->next_record(MYSQLI_BOTH); - $DB->query("SELECT LogID FROM torrents_logs WHERE TorrentID='{$TorrentID}'"); - while(list($LogID) = $DB->next_record(MYSQLI_NUM)) { - @unlink(SERVER_ROOT . "/logs/{$TorrentID}_{$LogID}.log"); - } - $DB->query("DELETE FROM torrents_logs WHERE TorrentID='{$TorrentID}'"); - ini_set('upload_max_filesize', 1000000); - foreach ($_FILES['logfiles']['name'] as $Pos => $File) { - if (!$_FILES['logfiles']['size'][$Pos]) { - break; - } - $FileName = $_FILES['logfiles']['name'][$Pos]; - $LogPath = $_FILES['logfiles']['tmp_name'][$Pos]; - $Logchecker->new_file($LogPath); - list($Score, $Details, $Checksum, $LogText) = $Logchecker->parse(); - $Details = trim(implode("\r\n", $Details)); - $DetailsArray[] = $Details; - $LogScore = min($LogScore, $Score); - $LogChecksum = min(intval($Checksum), $LogChecksum); - $Logs[] = array($Details, $LogText); - $DB->query("INSERT INTO torrents_logs (TorrentID, Log, Details, Score, `Checksum`, `FileName`) VALUES ($TorrentID, '".db_string($LogText)."', '".db_string($Details)."', $Score, '".enum_boolean($Checksum)."', '".db_string($FileName)."')"); - $LogID = $DB->inserted_id(); - if (move_uploaded_file($LogPath, SERVER_ROOT . "/logs/{$TorrentID}_{$LogID}.log") === false) { - die("Could not copy logfile to the server."); - } - } - - $DB->query("UPDATE torrents SET HasLogDB='1', LogScore={$LogScore}, LogChecksum='".enum_boolean($LogChecksum)."' WHERE ID='{$TorrentID}'"); - $Cache->delete_value("torrent_group_{$GroupID}"); - $Cache->delete_value("torrents_details_{$GroupID}"); + list($TorrentID, $GroupID) = $DB->next_record(MYSQLI_BOTH); + $DB->query("SELECT LogID FROM torrents_logs WHERE TorrentID='{$TorrentID}'"); + while(list($LogID) = $DB->next_record(MYSQLI_NUM)) { + @unlink(SERVER_ROOT . "/logs/{$TorrentID}_{$LogID}.log"); + } + $DB->query("DELETE FROM torrents_logs WHERE TorrentID='{$TorrentID}'"); + ini_set('upload_max_filesize', 1000000); + foreach ($_FILES['logfiles']['name'] as $Pos => $File) { + if (!$_FILES['logfiles']['size'][$Pos]) { + break; + } + $FileName = $_FILES['logfiles']['name'][$Pos]; + $LogPath = $_FILES['logfiles']['tmp_name'][$Pos]; + $Logchecker->new_file($LogPath); + list($Score, $Details, $Checksum, $LogText) = $Logchecker->parse(); + $Details = trim(implode("\r\n", $Details)); + $DetailsArray[] = $Details; + $LogScore = min($LogScore, $Score); + $LogChecksum = min(intval($Checksum), $LogChecksum); + $Logs[] = array($Details, $LogText); + $DB->query("INSERT INTO torrents_logs (TorrentID, Log, Details, Score, `Checksum`, `FileName`) VALUES ($TorrentID, '".db_string($LogText)."', '".db_string($Details)."', $Score, '".enum_boolean($Checksum)."', '".db_string($FileName)."')"); + $LogID = $DB->inserted_id(); + if (move_uploaded_file($LogPath, SERVER_ROOT . "/logs/{$TorrentID}_{$LogID}.log") === false) { + die("Could not copy logfile to the server."); + } + } + + $DB->query("UPDATE torrents SET HasLogDB='1', LogScore={$LogScore}, LogChecksum='".enum_boolean($LogChecksum)."' WHERE ID='{$TorrentID}'"); + $Cache->delete_value("torrent_group_{$GroupID}"); + $Cache->delete_value("torrents_details_{$GroupID}"); } else { - error('No log file uploaded or invalid torrent id was selected.'); + error('No log file uploaded or invalid torrent id was selected.'); } View::show_header(); echo << -
    Upload another log file +
    Upload another log file
    HTML; if($LogScore == 100) { - $Color = '#418B00'; + $Color = '#418B00'; } elseif($LogScore > 90) { - $Color = '#74C42E'; + $Color = '#74C42E'; } elseif($LogScore > 75) { - $Color = '#FFAA00'; + $Color = '#FFAA00'; } elseif($LogScore > 50) { - $Color = '#FF5E00'; + $Color = '#FF5E00'; } else { - $Color = '#FF0000'; + $Color = '#FF0000'; } echo "
    Score: $LogScore (out of 100)
    "; if ($LogChecksum === 0) { - echo << - Trumpable For: -

    - Bad/No Checksum(s) - + echo << + Trumpable For: +

    + Bad/No Checksum(s) + HTML; } foreach ($Logs as $Log) { - list($Details, $Text) = $Log; - if (!empty($Details)) { - $Details = explode("\r\n", $Details); - print << -

    Log validation report:

    -
      + list($Details, $Text) = $Log; + if (!empty($Details)) { + $Details = explode("\r\n", $Details); + print << +

      Log validation report:

      +
        HTML; - foreach ($Details as $Property) { - print "\t\t
      • {$Property}
      • "; - } - print << - + foreach ($Details as $Property) { + print "\t\t
      • {$Property}
      • "; + } + print << + HTML; - } + } - echo << -
        {$Text}
        - + echo << +
        {$Text}
        +
    HTML; diff --git a/sections/logchecker/test.php b/sections/logchecker/test.php index 4ba3eaef0..e6b8966c0 100644 --- a/sections/logchecker/test.php +++ b/sections/logchecker/test.php @@ -13,10 +13,10 @@ WHERE t.HasLog='1' AND t.LogScore=0 AND t.UserID = " . $LoggedUser['ID']); if ($DB->has_results()) { - $output = ''; - while (list($ID, $AlbumName, $ArtistName, $Year, $Format, $Encoding) = $DB->next_record()) { - $output .= "$ArtistName - $AlbumName [$Year] [$Format/$Encoding]"; - } + $output = ''; + while (list($ID, $AlbumName, $ArtistName, $Year, $Format, $Encoding) = $DB->next_record()) { + $output .= "$ArtistName - $AlbumName [$Year] [$Format/$Encoding]"; + } } */ @@ -24,47 +24,47 @@ echo << - Upload Missing Logs - Update Uploaded Logs + Upload Missing Logs + Update Uploaded Logs
    -

    Orpheus Logchecker: EAC and XLD.

    -
    -

    - Use this page to test our logchecker. You can either upload a log or paste it into the - text box below. This will then run the file/text against our logchecker displaying to you - what it would look like on our site. To verify checksum, you need to upload log file. -

    - - - - - - - -
    Upload file
    -
    - - - -
    -
    - - - - - - - -
    Paste log (No checksum verification)
    -
    - - -

    - -
    -
    -
    +

    Orpheus Logchecker: EAC and XLD.

    +
    +

    + Use this page to test our logchecker. You can either upload a log or paste it into the + text box below. This will then run the file/text against our logchecker displaying to you + what it would look like on our site. To verify checksum, you need to upload log file. +

    + + + + + + + +
    Upload file
    +
    + + + +
    +
    + + + + + + + +
    Paste log (No checksum verification)
    +
    + + +

    + +
    +
    +
    HTML; diff --git a/sections/logchecker/update.php b/sections/logchecker/update.php index 091cab68a..1ea602618 100644 --- a/sections/logchecker/update.php +++ b/sections/logchecker/update.php @@ -6,106 +6,106 @@ print << - Test Logchecker - Upload Missing Logs + Test Logchecker + Upload Missing Logs
    -

    Update Log

    -
    -

    - This form allows you to update the logs for any torrent that you've uploaded. - Select a torrent and upload the log files in the form below, making sure to add - all logs that you wish to upload. This will overwrite any previously uploaded logs for - this torrent. If you wish to just have a torrent manually rescored, please report it - to staff. -

    -
    -
    - - - - - - +

    Update Log

    +
    +

    + This form allows you to update the logs for any torrent that you've uploaded. + Select a torrent and upload the log files in the form below, making sure to add + all logs that you wish to upload. This will overwrite any previously uploaded logs for + this torrent. If you wish to just have a torrent manually rescored, please report it + to staff. +

    +
    + + + +
    Select a Torrent
    + + + HTML; $DB->query(" - SELECT - ID, GroupID, `Format`, Encoding, HasCue, HasLog, HasLogDB, LogScore, - LogChecksum - FROM torrents - WHERE HasLog='1' AND HasLogDB='1' AND UserID = " . $LoggedUser['ID']); + SELECT + ID, GroupID, `Format`, Encoding, HasCue, HasLog, HasLogDB, LogScore, + LogChecksum + FROM torrents + WHERE HasLog='1' AND HasLogDB='1' AND UserID = " . $LoggedUser['ID']); if ($DB->has_results()) { - $GroupIDs = $DB->collect('GroupID'); - $TorrentsInfo = $DB->to_array('ID'); - $Groups = Torrents::get_groups($GroupIDs); - foreach ($TorrentsInfo as $TorrentID => $Torrent) { - list($ID, $GroupID, $Format, $Encoding, $HasCue, $HasLog, $HasLogDB, $LogScore, $LogChecksum) = $Torrent; - $Group = $Groups[(int) $GroupID]; - $GroupName = $Group['Name']; - $GroupYear = $Group['Year']; - $ExtendedArtists = $Group['ExtendedArtists']; - $Artists = $Group['Artists']; - if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5])) { - unset($ExtendedArtists[2]); - unset($ExtendedArtists[3]); - $DisplayName = Artists::display_artists($ExtendedArtists); - } elseif (!empty($Artists)) { - $DisplayName = Artists::display_artists(array(1 => $Artists)); - } else { - $DisplayName = ''; - } - $DisplayName .= ''.$GroupName.''; - if ($GroupYear > 0) { - $DisplayName .= " [{$GroupYear}]"; - } - $Info = array(); - if (!empty($Data['Format'])) { - $Info[] = $Data['Format']; - } - if (!empty($Data['Encoding'])) { - $Info[] = $Data['Encoding']; - } - if (!empty($Info)) { - $DisplayName .= ' [' . implode('/', $Info) . ']'; - } - if ($HasLog == '1') { - $DisplayName .= ' / Log'.($HasLogDB == '1' ? " ({$LogScore}%)" : ""); - } - if ($HasCue == '1') { - $DisplayName .= ' / Cue'; - } - if ($LogChecksum == '0') { - $DisplayName .= ' / ' . Format::torrent_label('Bad/Missing Checksum'); - } - echo "\t\t\t\t"; - } - $AcceptValues = Logchecker::get_accept_values(); - echo << - - - - - - - - + $GroupIDs = $DB->collect('GroupID'); + $TorrentsInfo = $DB->to_array('ID'); + $Groups = Torrents::get_groups($GroupIDs); + foreach ($TorrentsInfo as $TorrentID => $Torrent) { + list($ID, $GroupID, $Format, $Encoding, $HasCue, $HasLog, $HasLogDB, $LogScore, $LogChecksum) = $Torrent; + $Group = $Groups[(int) $GroupID]; + $GroupName = $Group['Name']; + $GroupYear = $Group['Year']; + $ExtendedArtists = $Group['ExtendedArtists']; + $Artists = $Group['Artists']; + if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5])) { + unset($ExtendedArtists[2]); + unset($ExtendedArtists[3]); + $DisplayName = Artists::display_artists($ExtendedArtists); + } elseif (!empty($Artists)) { + $DisplayName = Artists::display_artists(array(1 => $Artists)); + } else { + $DisplayName = ''; + } + $DisplayName .= ''.$GroupName.''; + if ($GroupYear > 0) { + $DisplayName .= " [{$GroupYear}]"; + } + $Info = []; + if (!empty($Data['Format'])) { + $Info[] = $Data['Format']; + } + if (!empty($Data['Encoding'])) { + $Info[] = $Data['Encoding']; + } + if (!empty($Info)) { + $DisplayName .= ' [' . implode('/', $Info) . ']'; + } + if ($HasLog == '1') { + $DisplayName .= ' / Log'.($HasLogDB == '1' ? " ({$LogScore}%)" : ""); + } + if ($HasCue == '1') { + $DisplayName .= ' / Cue'; + } + if ($LogChecksum == '0') { + $DisplayName .= ' / ' . Format::torrent_label('Bad/Missing Checksum'); + } + echo "\t\t\t\t"; + } + $AcceptValues = Logchecker::get_accept_values(); + echo << + + + + + + + + HTML; } else { - echo "\t\t\t"; + echo "\t\t\t"; } print << - +
    Select a Torrent
    {$DisplayName}
    Upload Logs for This Torrent
    - Check your log files before uploading here. For multi-disc releases, click the "+" button to add multiple log files.
    - + -
    - -
    {$DisplayName}
    Upload Logs for This Torrent
    + Check your log files before uploading here. For multi-disc releases, click the "+" button to add multiple log files.
    + + +
    + +
    No uploads found.
    No uploads found.
    +
    HTML; diff --git a/sections/logchecker/upload.php b/sections/logchecker/upload.php index 3f545ab63..2e299ce0c 100644 --- a/sections/logchecker/upload.php +++ b/sections/logchecker/upload.php @@ -6,106 +6,106 @@ echo << - Test Logchecker - Update Logs + Test Logchecker + Update Logs
    -

    Upload Missing Logs

    -
    -

    - These torrents are your uploads that state that there are logs within the torrent, but none were - uploaded to the site. To fix this, please select a torrent and then some torrents to upload below. -

    - If you'd like to upload new logs for your uploaded torrents that have been scored, please go here. - Additionally, you can report any torrent to staff for them to be manually rescored by staff. -

    -
    -
    - - - - - - +

    Upload Missing Logs

    +
    +

    + These torrents are your uploads that state that there are logs within the torrent, but none were + uploaded to the site. To fix this, please select a torrent and then some torrents to upload below. +

    + If you'd like to upload new logs for your uploaded torrents that have been scored, please go here. + Additionally, you can report any torrent to staff for them to be manually rescored by staff. +

    +
    + + + +
    Select a Torrent
    + + + HTML; $DB->query(" - SELECT - ID, GroupID, `Format`, Encoding, HasCue, HasLog, HasLogDB, LogScore, LogChecksum - FROM torrents - WHERE HasLog='1' AND HasLogDB='0' AND UserID = ".$LoggedUser['ID']); + SELECT + ID, GroupID, `Format`, Encoding, HasCue, HasLog, HasLogDB, LogScore, LogChecksum + FROM torrents + WHERE HasLog='1' AND HasLogDB='0' AND UserID = ".$LoggedUser['ID']); if ($DB->has_results()) { - $GroupIDs = $DB->collect('GroupID'); - $TorrentsInfo = $DB->to_array('ID'); - $Groups = Torrents::get_groups($GroupIDs); + $GroupIDs = $DB->collect('GroupID'); + $TorrentsInfo = $DB->to_array('ID'); + $Groups = Torrents::get_groups($GroupIDs); - foreach ($TorrentsInfo as $TorrentID => $Torrent) { - list($ID, $GroupID, $Format, $Encoding, $HasCue, $HasLog, $HasLogDB, $LogScore, $LogChecksum) = $Torrent; - $Group = $Groups[(int) $GroupID]; - $GroupName = $Group['Name']; - $GroupYear = $Group['Year']; - $ExtendedArtists = $Group['ExtendedArtists']; - $Artists = $Group['Artists']; - if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5])) { - unset($ExtendedArtists[2]); - unset($ExtendedArtists[3]); - $DisplayName = Artists::display_artists($ExtendedArtists); - } elseif (!empty($Artists)) { - $DisplayName = Artists::display_artists(array(1 => $Artists)); - } else { - $DisplayName = ''; - } - $DisplayName .= ''.$GroupName.''; - if ($GroupYear > 0) { - $DisplayName .= " [{$GroupYear}]"; - } - $Info = array(); - if (!empty($Data['Format'])) { - $Info[] = $Data['Format']; - } - if (!empty($Data['Encoding'])) { - $Info[] = $Data['Encoding']; - } - if (!empty($Info)) { - $DisplayName .= ' [' . implode('/', $Info) . ']'; - } - if ($HasLog == '1') { - $DisplayName .= ' / Log'.($HasLogDB == '1' ? " ({$LogScore}%)" : ""); - } - if ($HasCue == '1') { - $DisplayName .= ' / Cue'; - } - if ($LogChecksum == '0') { - $DisplayName .= ' / ' . Format::torrent_label('Bad/Missing Checksum'); - } - echo "\t\t\t\t"; - } - $AcceptValues = Logchecker::get_accept_values(); - echo << - - - - - - - - + foreach ($TorrentsInfo as $TorrentID => $Torrent) { + list($ID, $GroupID, $Format, $Encoding, $HasCue, $HasLog, $HasLogDB, $LogScore, $LogChecksum) = $Torrent; + $Group = $Groups[(int) $GroupID]; + $GroupName = $Group['Name']; + $GroupYear = $Group['Year']; + $ExtendedArtists = $Group['ExtendedArtists']; + $Artists = $Group['Artists']; + if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5])) { + unset($ExtendedArtists[2]); + unset($ExtendedArtists[3]); + $DisplayName = Artists::display_artists($ExtendedArtists); + } elseif (!empty($Artists)) { + $DisplayName = Artists::display_artists(array(1 => $Artists)); + } else { + $DisplayName = ''; + } + $DisplayName .= ''.$GroupName.''; + if ($GroupYear > 0) { + $DisplayName .= " [{$GroupYear}]"; + } + $Info = []; + if (!empty($Data['Format'])) { + $Info[] = $Data['Format']; + } + if (!empty($Data['Encoding'])) { + $Info[] = $Data['Encoding']; + } + if (!empty($Info)) { + $DisplayName .= ' [' . implode('/', $Info) . ']'; + } + if ($HasLog == '1') { + $DisplayName .= ' / Log'.($HasLogDB == '1' ? " ({$LogScore}%)" : ""); + } + if ($HasCue == '1') { + $DisplayName .= ' / Cue'; + } + if ($LogChecksum == '0') { + $DisplayName .= ' / ' . Format::torrent_label('Bad/Missing Checksum'); + } + echo "\t\t\t\t"; + } + $AcceptValues = Logchecker::get_accept_values(); + echo << + + + + + + + + HTML; } else { - echo "\t\t\t\t"; + echo "\t\t\t\t"; } echo << - - +
    Select a Torrent
    {$DisplayName}
    Upload Logs for This Torrent
    - Check your log files before uploading here. For multi-disc releases, click the "+" button to add multiple log files.
    - + -
    - -
    {$DisplayName}
    Upload Logs for This Torrent
    + Check your log files before uploading here. For multi-disc releases, click the "+" button to add multiple log files.
    + + +
    + +
    No uploads found.
    No uploads found.
    + +
    HTML; diff --git a/sections/login/2fa.php b/sections/login/2fa.php index eb688e858..751d3bac3 100644 --- a/sections/login/2fa.php +++ b/sections/login/2fa.php @@ -1,50 +1,50 @@ - + - -
    - + + query(" - UPDATE login_attempts - SET BannedUntil = '0000-00-00 00:00:00', Attempts = '0' - WHERE ID = '" . db_string($AttemptID) . "'"); - $Attempts = 0; - } - if (isset($Err)) { - ?> -

    - - 0) { ?> - You have attempts remaining.

    - WARNING: You will be banned for 6 hours after your login attempts run out!

    - - - - - - + if (!empty($BannedUntil) && $BannedUntil != '0000-00-00 00:00:00') { + $DB->query(" + UPDATE login_attempts + SET BannedUntil = '0000-00-00 00:00:00', Attempts = '0' + WHERE ID = '" . db_string($AttemptID) . "'"); + $Attempts = 0; + } + if (isset($Err)) { + ?> +

    + + 0) { ?> + You have attempts remaining.

    + WARNING: You will be banned for 6 hours after your login attempts run out!

    + +
    2FA Key  - -
    + + + + - - - - -
    2FA Key  + +
    -
    -

    - Use a recovery key? - + + + + + +

    + Use a recovery key? + - You are banned from logging in for another . - + You are banned from logging in for another . + @@ -56,4 +56,4 @@ $('#no-cookies').gshow(); } - + diff --git a/sections/login/2fa_recovery.php b/sections/login/2fa_recovery.php index 00988fd7d..60c6f30bb 100644 --- a/sections/login/2fa_recovery.php +++ b/sections/login/2fa_recovery.php @@ -1,59 +1,59 @@ - + - -
    - + + query(" - UPDATE login_attempts - SET BannedUntil = '0000-00-00 00:00:00', Attempts = '0' - WHERE ID = '" . db_string($AttemptID) . "'"); - $Attempts = 0; - } - if (isset($Err)) { - ?> -

    - - 0) { ?> - You have attempts remaining.

    - WARNING: You will be banned for 6 hours after your login attempts run out!

    - - Note: You will only be able to use a recovery key once! - - - - - + if (!empty($BannedUntil) && $BannedUntil != '0000-00-00 00:00:00') { + $DB->query(" + UPDATE login_attempts + SET BannedUntil = '0000-00-00 00:00:00', Attempts = '0' + WHERE ID = '" . db_string($AttemptID) . "'"); + $Attempts = 0; + } + if (isset($Err)) { + ?> +

    + + 0) { ?> + You have attempts remaining.

    + WARNING: You will be banned for 6 hours after your login attempts run out!

    + + Note: You will only be able to use a recovery key once! +
    2FA Recovery Key  - -
    + + + + - - - - -
    2FA Recovery Key  + +
    -
    -

    - + + + + + +

    + - You are banned from logging in for another . - + You are banned from logging in for another . + - + diff --git a/sections/login/disabled.php b/sections/login/disabled.php index 98408a168..17e4bd376 100644 --- a/sections/login/disabled.php +++ b/sections/login/disabled.php @@ -1,30 +1,32 @@ -
    Back"; + $Output .= "

    Back"; } if ((empty($_POST['submit']) || empty($_POST['username'])) && !isset($Output)) { ?>

    Your account has been disabled.
    This is either due to inactivity or rule violation(s).

    - + If you believe your account was in good standing and was disabled for inactivity, you may request it be re-enabled via email using the form below.
    Please note that you will need access to the email account associated with your account at Orpheus for this to work;
    if you do not, please see the section after this form.

    - - + +


    - + If you are unsure why your account is disabled, or you wish to discuss this with staff, come to our IRC network at:
    And join

    Be honest. At this point, lying will get you nowhere.


    @@ -34,17 +36,17 @@ @@ -54,50 +56,50 @@ function toggle_visibility(id) {


    - - - - + + + +
    -
    -
    -

    Disabled IRC

    -
    -
    -
    -

    Please read the topic carefully.

    -
    - - - - - - - - - - - - - - -
    +
    +

    Disabled IRC

    +
    +
    +
    +

    Please read the topic carefully.

    +
    + + + + + + + + + + + + + + +
    -query(" - SELECT - m.ID, - m.Email, - m.ipcc, - i.ResetExpires - FROM users_main as m - INNER JOIN users_info AS i ON i.UserID = m.ID - WHERE i.ResetKey = '".db_string($_REQUEST['key'])."' - AND i.ResetKey != '' - AND m.Enabled = '1'"); - list($UserID, $Email, $Country, $Expires) = $DB->next_record(); - if ($UserID && strtotime($Expires) > time()) { - - // If the user has requested a password change, and his key has not expired - $Validate->SetFields('password', '1', 'regex', 'You entered an invalid password. A strong password is 8 characters or longer, contains at least 1 lowercase and uppercase letter, and contains at least a number or symbol, or is 20 characters or longer', array('regex' => '/(?=^.{8,}$)(?=.*[^a-zA-Z])(?=.*[A-Z])(?=.*[a-z]).*$|.{20,}/')); - $Validate->SetFields('verifypassword', '1', 'compare', 'Your passwords did not match.', array('comparefield' => 'password')); - - if (!empty($_REQUEST['password'])) { - // If the user has entered a password. - // If the user has not entered a password, $Reset is not set to 1, and the success message is not shown - $Err = $Validate->ValidateForm($_REQUEST); - if ($Err == '') { - // Form validates without error, set new secret and password. - $DB->query(" - UPDATE - users_main AS m, - users_info AS i - SET - m.PassHash = '".db_string(Users::make_password_hash($_REQUEST['password']))."', - i.ResetKey = '', - i.ResetExpires = '0000-00-00 00:00:00' - WHERE m.ID = '$UserID' - AND i.UserID = m.ID"); - $DB->query(" - INSERT INTO users_history_passwords - (UserID, ChangerIP, ChangeTime) - VALUES - ('$UserID', '$_SERVER[REMOTE_ADDR]', '".sqltime()."')"); - $Reset = true; // Past tense form of "to reset", meaning that password has now been reset - $LoggedUser['ID'] = $UserID; // Set $LoggedUser['ID'] for logout_all_sessions() to work - - logout_all_sessions(); - } - } - - // Either a form asking for them to enter the password - // Or a success message if $Reset is 1 - require('recover_step2.php'); - - } else { - // Either his key has expired, or he hasn't requested a pass change at all - if (strtotime($Expires) < time() && $UserID) { - // If his key has expired, clear all the reset information - $DB->query(" - UPDATE users_info - SET ResetKey = '', - ResetExpires = '0000-00-00 00:00:00' - WHERE UserID = '$UserID'"); - $_SESSION['reseterr'] = 'The link you were given has expired.'; // Error message to display on form - } - // Show him the first form (enter email address) - header('Location: login.php?act=recover'); - } - - } // End step 2 - - // User has not clicked the link in his email, use step 1 - else { - $Validate->SetFields('email', '1', 'email', 'You entered an invalid email address.'); - - if (!empty($_REQUEST['email'])) { - // User has entered email and submitted form - $Err = $Validate->ValidateForm($_REQUEST); - - if (!$Err) { - // Form validates correctly - $DB->query(" - SELECT - ID, - Username, - Email - FROM users_main - WHERE Email = '".db_string($_REQUEST['email'])."' - AND Enabled = '1'"); - list($UserID, $Username, $Email) = $DB->next_record(); - - if ($UserID) { - // Email exists in the database - // Set ResetKey, send out email, and set $Sent to 1 to show success page - Users::resetPassword($UserID, $Username, $Email); - - $Sent = 1; // If $Sent is 1, recover_step1.php displays a success message - - //Log out all of the users current sessions - $Cache->delete_value("user_info_$UserID"); - $Cache->delete_value("user_info_heavy_$UserID"); - $Cache->delete_value("user_stats_$UserID"); - $Cache->delete_value("enabled_$UserID"); - - $DB->query(" - SELECT SessionID - FROM users_sessions - WHERE UserID = '$UserID'"); - while (list($SessionID) = $DB->next_record()) { - $Cache->delete_value("session_$UserID"."_$SessionID"); - } - $DB->query(" - UPDATE users_sessions - SET Active = 0 - WHERE UserID = '$UserID' - AND Active = 1"); - } - $Err = "Email sent with further instructions."; - } - - } elseif (!empty($_SESSION['reseterr'])) { - // User has not entered email address, and there is an error set in session data - // This is typically because their key has expired. - // Stick the error into $Err so recover_step1.php can take care of it - $Err = $_SESSION['reseterr']; - unset($_SESSION['reseterr']); - } - - // Either a form for the user's email address, or a success message - require('recover_step1.php'); - } // End if (step 1) + // Recover password + if (!empty($_REQUEST['key'])) { + // User has entered a new password, use step 2 + + $DB->query(" + SELECT + m.ID, + m.Email, + m.ipcc, + i.ResetExpires + FROM users_main as m + INNER JOIN users_info AS i ON i.UserID = m.ID + WHERE i.ResetKey = '".db_string($_REQUEST['key'])."' + AND i.ResetKey != '' + AND m.Enabled = '1'"); + list($UserID, $Email, $Country, $Expires) = $DB->next_record(); + if ($UserID && strtotime($Expires) > time()) { + + // If the user has requested a password change, and his key has not expired + $Validate->SetFields('password', '1', 'regex', 'You entered an invalid password. A strong password is 8 characters or longer, contains at least 1 lowercase and uppercase letter, and contains at least a number or symbol, or is 20 characters or longer', array('regex' => '/(?=^.{8,}$)(?=.*[^a-zA-Z])(?=.*[A-Z])(?=.*[a-z]).*$|.{20,}/')); + $Validate->SetFields('verifypassword', '1', 'compare', 'Your passwords did not match.', array('comparefield' => 'password')); + + if (!empty($_REQUEST['password'])) { + // If the user has entered a password. + // If the user has not entered a password, $Reset is not set to 1, and the success message is not shown + $Err = $Validate->ValidateForm($_REQUEST); + if ($Err == '') { + // Form validates without error, set new secret and password. + $DB->query(" + UPDATE + users_main AS m, + users_info AS i + SET + m.PassHash = '".db_string(Users::make_password_hash($_REQUEST['password']))."', + i.ResetKey = '', + i.ResetExpires = '0000-00-00 00:00:00' + WHERE m.ID = '$UserID' + AND i.UserID = m.ID"); + $DB->query(" + INSERT INTO users_history_passwords + (UserID, ChangerIP, ChangeTime) + VALUES + ('$UserID', '$_SERVER[REMOTE_ADDR]', '".sqltime()."')"); + $Reset = true; // Past tense form of "to reset", meaning that password has now been reset + $LoggedUser['ID'] = $UserID; // Set $LoggedUser['ID'] for logout_all_sessions() to work + + logout_all_sessions(); + } + } + + // Either a form asking for them to enter the password + // Or a success message if $Reset is 1 + require('recover_step2.php'); + + } else { + // Either his key has expired, or he hasn't requested a pass change at all + if (strtotime($Expires) < time() && $UserID) { + // If his key has expired, clear all the reset information + $DB->query(" + UPDATE users_info + SET ResetKey = '', + ResetExpires = '0000-00-00 00:00:00' + WHERE UserID = '$UserID'"); + $_SESSION['reseterr'] = 'The link you were given has expired.'; // Error message to display on form + } + // Show him the first form (enter email address) + header('Location: login.php?act=recover'); + } + + } // End step 2 + + // User has not clicked the link in his email, use step 1 + else { + $Validate->SetFields('email', '1', 'email', 'You entered an invalid email address.'); + + if (!empty($_REQUEST['email'])) { + // User has entered email and submitted form + $Err = $Validate->ValidateForm($_REQUEST); + + if (!$Err) { + // Form validates correctly + $DB->query(" + SELECT + ID, + Username, + Email + FROM users_main + WHERE Email = '".db_string($_REQUEST['email'])."' + AND Enabled = '1'"); + list($UserID, $Username, $Email) = $DB->next_record(); + + if ($UserID) { + // Email exists in the database + // Set ResetKey, send out email, and set $Sent to 1 to show success page + Users::resetPassword($UserID, $Username, $Email); + + $Sent = 1; // If $Sent is 1, recover_step1.php displays a success message + + //Log out all of the users current sessions + $Cache->delete_value("user_info_$UserID"); + $Cache->delete_value("user_info_heavy_$UserID"); + $Cache->delete_value("user_stats_$UserID"); + $Cache->delete_value("enabled_$UserID"); + + $DB->query(" + SELECT SessionID + FROM users_sessions + WHERE UserID = '$UserID'"); + while (list($SessionID) = $DB->next_record()) { + $Cache->delete_value("session_$UserID"."_$SessionID"); + } + $DB->query(" + UPDATE users_sessions + SET Active = 0 + WHERE UserID = '$UserID' + AND Active = 1"); + } + $Err = "Email sent with further instructions."; + } + + } elseif (!empty($_SESSION['reseterr'])) { + // User has not entered email address, and there is an error set in session data + // This is typically because their key has expired. + // Stick the error into $Err so recover_step1.php can take care of it + $Err = $_SESSION['reseterr']; + unset($_SESSION['reseterr']); + } + + // Either a form for the user's email address, or a success message + require('recover_step1.php'); + } // End if (step 1) } // End password recovery elseif (isset($_REQUEST['act']) && $_REQUEST['act'] === '2fa_recovery') { - if (!isset($_SESSION['temp_user_data'])) { - header('Location: login.php'); - exit; - } - elseif (empty($_POST['2fa_recovery_key'])) { - require('2fa_recovery.php'); - } - else { - list($UserID, $PermissionID, $CustomPermissions, $PassHash, $Secret, $Enabled, $TFAKey, $Recovery) = $_SESSION['temp_user_data']; - $Recovery = (!empty($Recovery)) ? unserialize($Recovery) : array(); - if (($Key = array_search($_POST['2fa_recovery_key'], $Recovery)) !== false) { - $SessionID = Users::make_secret(); - $Cookie = Crypto::encrypt(Crypto::encrypt($SessionID . '|~|' . $UserID, ENCKEY), ENCKEY); - if ($_SESSION['temp_stay_logged']) { - $KeepLogged = 1; - setcookie('session', $Cookie, time() + 60 * 60 * 24 * 365, '/', '', $SSL, true); - } else { - $KeepLogged = 0; - setcookie('session', $Cookie, 0, '/', '', $SSL, true); - } - - unset($_SESSION['temp_stay_logged'], $_SESSION['temp_user_data']); - - //TODO: another tracker might enable this for donors, I think it's too stupid to bother adding that - // Because we <3 our staff - $Permissions = Permissions::get_permissions($PermissionID); - $CustomPermissions = unserialize($CustomPermissions); - if (isset($Permissions['Permissions']['site_disable_ip_history']) - || isset($CustomPermissions['site_disable_ip_history']) - ) { - $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; - } - - $DB->query(" - INSERT INTO users_sessions - (UserID, SessionID, KeepLogged, Browser, OperatingSystem, IP, LastUpdate, FullUA) - VALUES - ('$UserID', '" . db_string($SessionID) . "', '$KeepLogged', '$Browser', '$OperatingSystem', '" . db_string($_SERVER['REMOTE_ADDR']) . "', '" . sqltime() . "', '" . db_string($_SERVER['HTTP_USER_AGENT']) . "')"); - - $Cache->begin_transaction("users_sessions_$UserID"); - $Cache->insert_front($SessionID, array( - 'SessionID' => $SessionID, - 'Browser' => $Browser, - 'OperatingSystem' => $OperatingSystem, - 'IP' => $_SERVER['REMOTE_ADDR'], - 'LastUpdate' => sqltime() - )); - $Cache->commit_transaction(0); - - unset($Recovery[$Key]); - $Recovery = serialize($Recovery); - $Sql = " - UPDATE users_main - SET - LastLogin = '" . sqltime() . "', - LastAccess = '" . sqltime() . "', - Recovery = '" . db_string($Recovery) . "' - WHERE ID = '" . db_string($UserID) . "'"; - - $DB->query($Sql); - - if (!empty($_COOKIE['redirect'])) { - $URL = $_COOKIE['redirect']; - setcookie('redirect', '', time() - 60 * 60 * 24, '/', '', false); - header("Location: $URL"); - die(); - } else { - header('Location: index.php'); - die(); - } - } - else { - $DB->query(" - SELECT ID, Attempts, Bans, BannedUntil - FROM login_attempts - WHERE IP = '".db_string($_SERVER['REMOTE_ADDR'])."'"); - list($AttemptID, $Attempts, $Bans, $BannedUntil) = $DB->next_record(); - - // Function to log a user's login attempt - function log_attempt($UserID) { - global $DB, $Cache, $AttemptID, $Attempts, $Bans, $BannedUntil; - $IPStr = $_SERVER['REMOTE_ADDR']; - $IPA = substr($IPStr, 0, strcspn($IPStr, '.')); - $IP = Tools::ip_to_unsigned($IPStr); - if ($AttemptID) { // User has attempted to log in recently - $Attempts++; - if ($Attempts > 5) { // Only 6 allowed login attempts, ban user's IP - $BannedUntil = time_plus(60 * 60 * 6); - $DB->query(" - UPDATE login_attempts - SET - LastAttempt = '".sqltime()."', - Attempts = '".db_string($Attempts)."', - BannedUntil = '".db_string($BannedUntil)."', - Bans = Bans + 1 - WHERE ID = '".db_string($AttemptID)."'"); - - if ($Bans > 9) { // Automated bruteforce prevention - $DB->query(" - SELECT Reason - FROM ip_bans - WHERE $IP BETWEEN FromIP AND ToIP"); - if ($DB->has_results()) { - //Ban exists already, only add new entry if not for same reason - list($Reason) = $DB->next_record(MYSQLI_BOTH, false); - if ($Reason != 'Automated ban per >60 failed login attempts') { - $DB->query(" - UPDATE ip_bans - SET Reason = CONCAT('Automated ban per >60 failed login attempts AND ', Reason) - WHERE FromIP = $IP - AND ToIP = $IP"); - } - } else { - //No ban - $DB->query(" - INSERT IGNORE INTO ip_bans - (FromIP, ToIP, Reason) - VALUES - ('$IP','$IP', 'Automated ban per >60 failed login attempts')"); - $Cache->delete_value("ip_bans_$IPA"); - } - } - } else { - // User has attempted fewer than 6 logins - $DB->query(" - UPDATE login_attempts - SET - LastAttempt = '".sqltime()."', - Attempts = '".db_string($Attempts)."', - BannedUntil = '0000-00-00 00:00:00' - WHERE ID = '".db_string($AttemptID)."'"); - } - } else { // User has not attempted to log in recently - $Attempts = 1; - $DB->query(" - INSERT INTO login_attempts - (UserID, IP, LastAttempt, Attempts) - VALUES - ('".db_string($UserID)."', '".db_string($IPStr)."', '".sqltime()."', 1)"); - } - } // end log_attempt function - log_attempt($UserID); - unset($_SESSION['temp_stay_logged'], $_SESSION['temp_user_data']); - header('Location: login.php'); - } - } + if (!isset($_SESSION['temp_user_data'])) { + header('Location: login.php'); + exit; + } + elseif (empty($_POST['2fa_recovery_key'])) { + require('2fa_recovery.php'); + } + else { + list($UserID, $PermissionID, $CustomPermissions, $PassHash, $Secret, $Enabled, $TFAKey, $Recovery) = $_SESSION['temp_user_data']; + $Recovery = (!empty($Recovery)) ? unserialize($Recovery) : []; + if (($Key = array_search($_POST['2fa_recovery_key'], $Recovery)) !== false) { + $SessionID = Users::make_secret(); + $Cookie = Crypto::encrypt(Crypto::encrypt($SessionID . '|~|' . $UserID, ENCKEY), ENCKEY); + if ($_SESSION['temp_stay_logged']) { + $KeepLogged = 1; + setcookie('session', $Cookie, time() + 60 * 60 * 24 * 365, '/', '', $SSL, true); + } else { + $KeepLogged = 0; + setcookie('session', $Cookie, 0, '/', '', $SSL, true); + } + + unset($_SESSION['temp_stay_logged'], $_SESSION['temp_user_data']); + + //TODO: another tracker might enable this for donors, I think it's too stupid to bother adding that + // Because we <3 our staff + $Permissions = Permissions::get_permissions($PermissionID); + $CustomPermissions = unserialize($CustomPermissions); + if (isset($Permissions['Permissions']['site_disable_ip_history']) + || isset($CustomPermissions['site_disable_ip_history']) + ) { + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + } + + $DB->query(" + INSERT INTO users_sessions + (UserID, SessionID, KeepLogged, Browser, OperatingSystem, IP, LastUpdate, FullUA) + VALUES + ('$UserID', '" . db_string($SessionID) . "', '$KeepLogged', '$Browser', '$OperatingSystem', '" . db_string($_SERVER['REMOTE_ADDR']) . "', '" . sqltime() . "', '" . db_string($_SERVER['HTTP_USER_AGENT']) . "')"); + + $Cache->begin_transaction("users_sessions_$UserID"); + $Cache->insert_front($SessionID, array( + 'SessionID' => $SessionID, + 'Browser' => $Browser, + 'OperatingSystem' => $OperatingSystem, + 'IP' => $_SERVER['REMOTE_ADDR'], + 'LastUpdate' => sqltime() + )); + $Cache->commit_transaction(0); + + unset($Recovery[$Key]); + $Recovery = serialize($Recovery); + $Sql = " + UPDATE users_main + SET + LastLogin = '" . sqltime() . "', + LastAccess = '" . sqltime() . "', + Recovery = '" . db_string($Recovery) . "' + WHERE ID = '" . db_string($UserID) . "'"; + + $DB->query($Sql); + + if (!empty($_COOKIE['redirect'])) { + $URL = $_COOKIE['redirect']; + setcookie('redirect', '', time() - 60 * 60 * 24, '/', '', false); + header("Location: $URL"); + die(); + } else { + header('Location: index.php'); + die(); + } + } + else { + $DB->query(" + SELECT ID, Attempts, Bans, BannedUntil + FROM login_attempts + WHERE IP = '".db_string($_SERVER['REMOTE_ADDR'])."'"); + list($AttemptID, $Attempts, $Bans, $BannedUntil) = $DB->next_record(); + + // Function to log a user's login attempt + function log_attempt($UserID) { + global $DB, $Cache, $AttemptID, $Attempts, $Bans, $BannedUntil; + $IPStr = $_SERVER['REMOTE_ADDR']; + $IPA = substr($IPStr, 0, strcspn($IPStr, '.')); + $IP = Tools::ip_to_unsigned($IPStr); + if ($AttemptID) { // User has attempted to log in recently + $Attempts++; + if ($Attempts > 5) { // Only 6 allowed login attempts, ban user's IP + $BannedUntil = time_plus(60 * 60 * 6); + $DB->query(" + UPDATE login_attempts + SET + LastAttempt = '".sqltime()."', + Attempts = '".db_string($Attempts)."', + BannedUntil = '".db_string($BannedUntil)."', + Bans = Bans + 1 + WHERE ID = '".db_string($AttemptID)."'"); + + if ($Bans > 9) { // Automated bruteforce prevention + $DB->query(" + SELECT Reason + FROM ip_bans + WHERE $IP BETWEEN FromIP AND ToIP"); + if ($DB->has_results()) { + //Ban exists already, only add new entry if not for same reason + list($Reason) = $DB->next_record(MYSQLI_BOTH, false); + if ($Reason != 'Automated ban per >60 failed login attempts') { + $DB->query(" + UPDATE ip_bans + SET Reason = CONCAT('Automated ban per >60 failed login attempts AND ', Reason) + WHERE FromIP = $IP + AND ToIP = $IP"); + } + } else { + //No ban + $DB->query(" + INSERT IGNORE INTO ip_bans + (FromIP, ToIP, Reason) + VALUES + ('$IP','$IP', 'Automated ban per >60 failed login attempts')"); + $Cache->delete_value("ip_bans_$IPA"); + } + } + } else { + // User has attempted fewer than 6 logins + $DB->query(" + UPDATE login_attempts + SET + LastAttempt = '".sqltime()."', + Attempts = '".db_string($Attempts)."', + BannedUntil = '0000-00-00 00:00:00' + WHERE ID = '".db_string($AttemptID)."'"); + } + } else { // User has not attempted to log in recently + $Attempts = 1; + $DB->query(" + INSERT INTO login_attempts + (UserID, IP, LastAttempt, Attempts) + VALUES + ('".db_string($UserID)."', '".db_string($IPStr)."', '".sqltime()."', 1)"); + } + } // end log_attempt function + log_attempt($UserID); + unset($_SESSION['temp_stay_logged'], $_SESSION['temp_user_data']); + header('Location: login.php'); + } + } } elseif (isset($_REQUEST['act']) && $_REQUEST['act'] === '2fa') { - if (!isset($_SESSION['temp_user_data'])) { - header('Location: login.php'); - exit; - } - - if (empty($_POST['2fa'])) { - require('2fa.php'); - } else { - include(SERVER_ROOT . '/classes/google_authenticator.class.php'); - - list($UserID, $PermissionID, $CustomPermissions, $PassHash, $Secret, $Enabled, $TFAKey, $Recovery) = $_SESSION['temp_user_data']; - - if (!(new PHPGangsta_GoogleAuthenticator())->verifyCode($TFAKey, $_POST['2fa'], 2)) { - // invalid 2fa key, log the user completely out - unset($_SESSION['temp_stay_logged'], $_SESSION['temp_user_data']); - header('Location: login.php?invalid2fa'); - } else { - $SessionID = Users::make_secret(); - $Cookie = Crypto::encrypt(Crypto::encrypt($SessionID . '|~|' . $UserID, ENCKEY), ENCKEY); - - if ($_SESSION['temp_stay_logged']) { - $KeepLogged = 1; - setcookie('session', $Cookie, time() + 60 * 60 * 24 * 365, '/', '', $SSL, true); - } else { - $KeepLogged = 0; - setcookie('session', $Cookie, 0, '/', '', $SSL, true); - } - - unset($_SESSION['temp_stay_logged'], $_SESSION['temp_user_data']); - - //TODO: another tracker might enable this for donors, I think it's too stupid to bother adding that - // Because we <3 our staff - $Permissions = Permissions::get_permissions($PermissionID); - $CustomPermissions = unserialize($CustomPermissions); - if (isset($Permissions['Permissions']['site_disable_ip_history']) - || isset($CustomPermissions['site_disable_ip_history']) - ) { - $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; - } - - $DB->query(" - INSERT INTO users_sessions - (UserID, SessionID, KeepLogged, Browser, OperatingSystem, IP, LastUpdate, FullUA) - VALUES - ('$UserID', '" . db_string($SessionID) . "', '$KeepLogged', '$Browser', '$OperatingSystem', '" . db_string($_SERVER['REMOTE_ADDR']) . "', '" . sqltime() . "', '" . db_string($_SERVER['HTTP_USER_AGENT']) . "')"); - - $Cache->begin_transaction("users_sessions_$UserID"); - $Cache->insert_front($SessionID, array( - 'SessionID' => $SessionID, - 'Browser' => $Browser, - 'OperatingSystem' => $OperatingSystem, - 'IP' => $_SERVER['REMOTE_ADDR'], - 'LastUpdate' => sqltime() - )); - $Cache->commit_transaction(0); - - $Sql = " - UPDATE users_main - SET - LastLogin = '" . sqltime() . "', - LastAccess = '" . sqltime() . "' - WHERE ID = '" . db_string($UserID) . "'"; - - $DB->query($Sql); - - if (!empty($_COOKIE['redirect'])) { - $URL = $_COOKIE['redirect']; - setcookie('redirect', '', time() - 60 * 60 * 24, '/', '', false); - header("Location: $URL"); - die(); - } else { - header('Location: index.php'); - die(); - } - } - } + if (!isset($_SESSION['temp_user_data'])) { + header('Location: login.php'); + exit; + } + + if (empty($_POST['2fa'])) { + require('2fa.php'); + } else { + include(SERVER_ROOT . '/classes/google_authenticator.class.php'); + + list($UserID, $PermissionID, $CustomPermissions, $PassHash, $Secret, $Enabled, $TFAKey, $Recovery) = $_SESSION['temp_user_data']; + + if (!(new PHPGangsta_GoogleAuthenticator())->verifyCode($TFAKey, $_POST['2fa'], 2)) { + // invalid 2fa key, log the user completely out + unset($_SESSION['temp_stay_logged'], $_SESSION['temp_user_data']); + header('Location: login.php?invalid2fa'); + } else { + $SessionID = Users::make_secret(); + $Cookie = Crypto::encrypt(Crypto::encrypt($SessionID . '|~|' . $UserID, ENCKEY), ENCKEY); + + if ($_SESSION['temp_stay_logged']) { + $KeepLogged = 1; + setcookie('session', $Cookie, time() + 60 * 60 * 24 * 365, '/', '', $SSL, true); + } else { + $KeepLogged = 0; + setcookie('session', $Cookie, 0, '/', '', $SSL, true); + } + + unset($_SESSION['temp_stay_logged'], $_SESSION['temp_user_data']); + + //TODO: another tracker might enable this for donors, I think it's too stupid to bother adding that + // Because we <3 our staff + $Permissions = Permissions::get_permissions($PermissionID); + $CustomPermissions = unserialize($CustomPermissions); + if (isset($Permissions['Permissions']['site_disable_ip_history']) + || isset($CustomPermissions['site_disable_ip_history']) + ) { + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + } + + $DB->query(" + INSERT INTO users_sessions + (UserID, SessionID, KeepLogged, Browser, OperatingSystem, IP, LastUpdate, FullUA) + VALUES + ('$UserID', '" . db_string($SessionID) . "', '$KeepLogged', '$Browser', '$OperatingSystem', '" . db_string($_SERVER['REMOTE_ADDR']) . "', '" . sqltime() . "', '" . db_string($_SERVER['HTTP_USER_AGENT']) . "')"); + + $Cache->begin_transaction("users_sessions_$UserID"); + $Cache->insert_front($SessionID, array( + 'SessionID' => $SessionID, + 'Browser' => $Browser, + 'OperatingSystem' => $OperatingSystem, + 'IP' => $_SERVER['REMOTE_ADDR'], + 'LastUpdate' => sqltime() + )); + $Cache->commit_transaction(0); + + $Sql = " + UPDATE users_main + SET + LastLogin = '" . sqltime() . "', + LastAccess = '" . sqltime() . "' + WHERE ID = '" . db_string($UserID) . "'"; + + $DB->query($Sql); + + if (!empty($_COOKIE['redirect'])) { + $URL = $_COOKIE['redirect']; + setcookie('redirect', '', time() - 60 * 60 * 24, '/', '', false); + header("Location: $URL"); + die(); + } else { + header('Location: index.php'); + die(); + } + } + } } // Normal login else { - if (isset($_SESSION['temp_user_data'])) { - header('Location: login.php?act=2fa'); - exit; - } - - $Validate->SetFields('username', true, 'regex', 'You did not enter a valid username.', array('regex' => USERNAME_REGEX)); - $Validate->SetFields('password', '1', 'string', 'You entered an invalid password.', array('minlength' => '6', 'maxlength' => -1)); - - $DB->query(" - SELECT ID, Attempts, Bans, BannedUntil - FROM login_attempts - WHERE IP = '".db_string($_SERVER['REMOTE_ADDR'])."'"); - list($AttemptID, $Attempts, $Bans, $BannedUntil) = $DB->next_record(); - - // Function to log a user's login attempt - function log_attempt($UserID) { - global $DB, $Cache, $AttemptID, $Attempts, $Bans, $BannedUntil; - $IPStr = $_SERVER['REMOTE_ADDR']; - $IPA = substr($IPStr, 0, strcspn($IPStr, '.')); - $IP = Tools::ip_to_unsigned($IPStr); - if ($AttemptID) { // User has attempted to log in recently - $Attempts++; - if ($Attempts > 5) { // Only 6 allowed login attempts, ban user's IP - $BannedUntil = time_plus(60 * 60 * 6); - $DB->query(" - UPDATE login_attempts - SET - LastAttempt = '".sqltime()."', - Attempts = '".db_string($Attempts)."', - BannedUntil = '".db_string($BannedUntil)."', - Bans = Bans + 1 - WHERE ID = '".db_string($AttemptID)."'"); - - if ($Bans > 9) { // Automated bruteforce prevention - $DB->query(" - SELECT Reason - FROM ip_bans - WHERE $IP BETWEEN FromIP AND ToIP"); - if ($DB->has_results()) { - //Ban exists already, only add new entry if not for same reason - list($Reason) = $DB->next_record(MYSQLI_BOTH, false); - if ($Reason != 'Automated ban per >60 failed login attempts') { - $DB->query(" - UPDATE ip_bans - SET Reason = CONCAT('Automated ban per >60 failed login attempts AND ', Reason) - WHERE FromIP = $IP - AND ToIP = $IP"); - } - } else { - //No ban - $DB->query(" - INSERT IGNORE INTO ip_bans - (FromIP, ToIP, Reason) - VALUES - ('$IP','$IP', 'Automated ban per >60 failed login attempts')"); - $Cache->delete_value("ip_bans_$IPA"); - } - } - } else { - // User has attempted fewer than 6 logins - $DB->query(" - UPDATE login_attempts - SET - LastAttempt = '".sqltime()."', - Attempts = '".db_string($Attempts)."', - BannedUntil = '0000-00-00 00:00:00' - WHERE ID = '".db_string($AttemptID)."'"); - } - } else { // User has not attempted to log in recently - $Attempts = 1; - $DB->query(" - INSERT INTO login_attempts - (UserID, IP, LastAttempt, Attempts) - VALUES - ('".db_string($UserID)."', '".db_string($IPStr)."', '".sqltime()."', 1)"); - } - } // end log_attempt function - - // If user has submitted form - if (isset($_POST['username']) && !empty($_POST['username']) && isset($_POST['password']) && !empty($_POST['password'])) { - if (strtotime($BannedUntil) > time()) { - header("Location: login.php"); - die(); - } - $Err = $Validate->ValidateForm($_POST); - - if (!$Err) { - // Passes preliminary validation (username and password "look right") - $DB->query(" - SELECT - ID, - PermissionID, - CustomPermissions, - PassHash, - Secret, - Enabled, - 2FA_Key, - Recovery - FROM users_main - WHERE Username = '".db_string($_POST['username'])."' - AND Username != ''"); - $UserData = $DB->next_record(MYSQLI_NUM, array(2, 7)); - list($UserID, $PermissionID, $CustomPermissions, $PassHash, $Secret, $Enabled, $TFAKey) = $UserData; - if (strtotime($BannedUntil) < time()) { - if ($UserID && Users::check_password($_POST['password'], $PassHash)) { - if (password_needs_rehash($PassHash, PASSWORD_DEFAULT)) { - $DB->prepared_query(" - UPDATE users_main - SET passhash = ? - WHERE ID = ?", Users::make_password_hash($_POST['password']), $UserID); - } - - if ($Enabled == 1) { - $SessionID = Users::make_secret(); - $Cookie = Crypto::encrypt(Crypto::encrypt($SessionID . '|~|' . $UserID, ENCKEY), ENCKEY); - - if ($TFAKey) { - // user has TFA enabled! :) - $_SESSION['temp_stay_logged'] = (isset($_POST['keeplogged']) && $_POST['keeplogged']); - $_SESSION['temp_user_data'] = $UserData; - header('Location: login.php?act=2fa'); - exit; - } - - if (isset($_POST['keeplogged']) && $_POST['keeplogged']) { - $KeepLogged = 1; - setcookie('session', $Cookie, time() + 60 * 60 * 24 * 365, '/', '', $SSL, true); - } else { - $KeepLogged = 0; - setcookie('session', $Cookie, 0, '/', '', $SSL, true); - } - - //TODO: another tracker might enable this for donors, I think it's too stupid to bother adding that - // Because we <3 our staff - $Permissions = Permissions::get_permissions($PermissionID); - $CustomPermissions = unserialize($CustomPermissions); - if (isset($Permissions['Permissions']['site_disable_ip_history']) - || isset($CustomPermissions['site_disable_ip_history']) - ) { - $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; - } - - $DB->query(" - INSERT INTO users_sessions - (UserID, SessionID, KeepLogged, Browser, OperatingSystem, IP, LastUpdate, FullUA) - VALUES - ('$UserID', '".db_string($SessionID)."', '$KeepLogged', '$Browser', '$OperatingSystem', '".db_string($_SERVER['REMOTE_ADDR'])."', '".sqltime()."', '".db_string($_SERVER['HTTP_USER_AGENT'])."')"); - - $Cache->begin_transaction("users_sessions_$UserID"); - $Cache->insert_front($SessionID, array( - 'SessionID' => $SessionID, - 'Browser' => $Browser, - 'OperatingSystem' => $OperatingSystem, - 'IP' => $_SERVER['REMOTE_ADDR'], - 'LastUpdate' => sqltime() - )); - $Cache->commit_transaction(0); - - $Sql = " - UPDATE users_main - SET - LastLogin = '".sqltime()."', - LastAccess = '".sqltime()."' - WHERE ID = '".db_string($UserID)."'"; - - $DB->query($Sql); - - if (!empty($_COOKIE['redirect'])) { - $URL = $_COOKIE['redirect']; - setcookie('redirect', '', time() - 60 * 60 * 24, '/', '', false); - header("Location: $URL"); - die(); - } else { - header('Location: index.php'); - die(); - } - } else { - log_attempt($UserID); - if ($Enabled == 2) { - - // Save the username in a cookie for the disabled page - setcookie('username', db_string($_POST['username']), time() + 60 * 60, '/', '', false); - header('Location: login.php?action=disabled'); - } elseif ($Enabled == 0) { - $Err = 'Your account has not been confirmed.
    Please check your email.'; - } - setcookie('keeplogged', '', time() + 60 * 60 * 24 * 365, '/', '', false); - } - } else { - log_attempt($UserID); - - $Err = 'Your username or password was incorrect.'; - setcookie('keeplogged', '', time() + 60 * 60 * 24 * 365, '/', '', false); - } - - } else { - log_attempt($UserID); - setcookie('keeplogged', '', time() + 60 * 60 * 24 * 365, '/', '', false); - } - - } else { - log_attempt('0'); - setcookie('keeplogged', '', time() + 60 * 60 * 24 * 365, '/', '', false); - } - } - require('sections/login/login.php'); + if (isset($_SESSION['temp_user_data'])) { + header('Location: login.php?act=2fa'); + exit; + } + + $Validate->SetFields('username', true, 'regex', 'You did not enter a valid username.', array('regex' => USERNAME_REGEX)); + $Validate->SetFields('password', '1', 'string', 'You entered an invalid password.', array('minlength' => '6', 'maxlength' => -1)); + + $DB->query(" + SELECT ID, Attempts, Bans, BannedUntil + FROM login_attempts + WHERE IP = '".db_string($_SERVER['REMOTE_ADDR'])."'"); + list($AttemptID, $Attempts, $Bans, $BannedUntil) = $DB->next_record(); + + // Function to log a user's login attempt + function log_attempt($UserID) { + global $DB, $Cache, $AttemptID, $Attempts, $Bans, $BannedUntil; + $IPStr = $_SERVER['REMOTE_ADDR']; + $IPA = substr($IPStr, 0, strcspn($IPStr, '.')); + $IP = Tools::ip_to_unsigned($IPStr); + if ($AttemptID) { // User has attempted to log in recently + $Attempts++; + if ($Attempts > 5) { // Only 6 allowed login attempts, ban user's IP + $BannedUntil = time_plus(60 * 60 * 6); + $DB->query(" + UPDATE login_attempts + SET + LastAttempt = '".sqltime()."', + Attempts = '".db_string($Attempts)."', + BannedUntil = '".db_string($BannedUntil)."', + Bans = Bans + 1 + WHERE ID = '".db_string($AttemptID)."'"); + + if ($Bans > 9) { // Automated bruteforce prevention + $DB->query(" + SELECT Reason + FROM ip_bans + WHERE $IP BETWEEN FromIP AND ToIP"); + if ($DB->has_results()) { + //Ban exists already, only add new entry if not for same reason + list($Reason) = $DB->next_record(MYSQLI_BOTH, false); + if ($Reason != 'Automated ban per >60 failed login attempts') { + $DB->query(" + UPDATE ip_bans + SET Reason = CONCAT('Automated ban per >60 failed login attempts AND ', Reason) + WHERE FromIP = $IP + AND ToIP = $IP"); + } + } else { + //No ban + $DB->query(" + INSERT IGNORE INTO ip_bans + (FromIP, ToIP, Reason) + VALUES + ('$IP','$IP', 'Automated ban per >60 failed login attempts')"); + $Cache->delete_value("ip_bans_$IPA"); + } + } + } else { + // User has attempted fewer than 6 logins + $DB->query(" + UPDATE login_attempts + SET + LastAttempt = '".sqltime()."', + Attempts = '".db_string($Attempts)."', + BannedUntil = '0000-00-00 00:00:00' + WHERE ID = '".db_string($AttemptID)."'"); + } + } else { // User has not attempted to log in recently + $Attempts = 1; + $DB->query(" + INSERT INTO login_attempts + (UserID, IP, LastAttempt, Attempts) + VALUES + ('".db_string($UserID)."', '".db_string($IPStr)."', '".sqltime()."', 1)"); + } + } // end log_attempt function + + // If user has submitted form + if (isset($_POST['username']) && !empty($_POST['username']) && isset($_POST['password']) && !empty($_POST['password'])) { + if (strtotime($BannedUntil) > time()) { + header("Location: login.php"); + die(); + } + $Err = $Validate->ValidateForm($_POST); + + if (!$Err) { + // Passes preliminary validation (username and password "look right") + $DB->query(" + SELECT + ID, + PermissionID, + CustomPermissions, + PassHash, + Secret, + Enabled, + 2FA_Key, + Recovery + FROM users_main + WHERE Username = '".db_string($_POST['username'])."' + AND Username != ''"); + $UserData = $DB->next_record(MYSQLI_NUM, array(2, 7)); + list($UserID, $PermissionID, $CustomPermissions, $PassHash, $Secret, $Enabled, $TFAKey) = $UserData; + if (strtotime($BannedUntil) < time()) { + if ($UserID && Users::check_password($_POST['password'], $PassHash)) { + if (password_needs_rehash($PassHash, PASSWORD_DEFAULT) || Users::check_password_old($_POST['password'], $PassHash)) { + $DB->prepared_query(" + UPDATE users_main + SET passhash = ? + WHERE ID = ?", Users::make_password_hash($_POST['password']), $UserID); + } + + if ($Enabled == 1) { + $SessionID = Users::make_secret(); + $Cookie = Crypto::encrypt(Crypto::encrypt($SessionID . '|~|' . $UserID, ENCKEY), ENCKEY); + + if ($TFAKey) { + // user has TFA enabled! :) + $_SESSION['temp_stay_logged'] = (isset($_POST['keeplogged']) && $_POST['keeplogged']); + $_SESSION['temp_user_data'] = $UserData; + header('Location: login.php?act=2fa'); + exit; + } + + if (isset($_POST['keeplogged']) && $_POST['keeplogged']) { + $KeepLogged = 1; + setcookie('session', $Cookie, time() + 60 * 60 * 24 * 365, '/', '', $SSL, true); + } else { + $KeepLogged = 0; + setcookie('session', $Cookie, 0, '/', '', $SSL, true); + } + + //TODO: another tracker might enable this for donors, I think it's too stupid to bother adding that + // Because we <3 our staff + $Permissions = Permissions::get_permissions($PermissionID); + $CustomPermissions = unserialize($CustomPermissions); + if (isset($Permissions['Permissions']['site_disable_ip_history']) + || isset($CustomPermissions['site_disable_ip_history']) + ) { + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + } + + $DB->query(" + INSERT INTO users_sessions + (UserID, SessionID, KeepLogged, Browser, OperatingSystem, IP, LastUpdate, FullUA) + VALUES + ('$UserID', '".db_string($SessionID)."', '$KeepLogged', '$Browser', '$OperatingSystem', '".db_string($_SERVER['REMOTE_ADDR'])."', '".sqltime()."', '".db_string($_SERVER['HTTP_USER_AGENT'])."')"); + + $Cache->begin_transaction("users_sessions_$UserID"); + $Cache->insert_front($SessionID, array( + 'SessionID' => $SessionID, + 'Browser' => $Browser, + 'OperatingSystem' => $OperatingSystem, + 'IP' => $_SERVER['REMOTE_ADDR'], + 'LastUpdate' => sqltime() + )); + $Cache->commit_transaction(0); + + $Sql = " + UPDATE users_main + SET + LastLogin = '".sqltime()."', + LastAccess = '".sqltime()."' + WHERE ID = '".db_string($UserID)."'"; + + $DB->query($Sql); + + if (!empty($_COOKIE['redirect'])) { + $URL = $_COOKIE['redirect']; + setcookie('redirect', '', time() - 60 * 60 * 24, '/', '', false); + header("Location: $URL"); + die(); + } else { + header('Location: index.php'); + die(); + } + } else { + log_attempt($UserID); + if ($Enabled == 2) { + + // Save the username in a cookie for the disabled page + setcookie('username', db_string($_POST['username']), time() + 60 * 60, '/', '', false); + header('Location: login.php?action=disabled'); + } elseif ($Enabled == 0) { + $Err = 'Your account has not been confirmed.
    Please check your email.'; + } + setcookie('keeplogged', '', time() + 60 * 60 * 24 * 365, '/', '', false); + } + } else { + log_attempt($UserID); + + $Err = 'Your username or password was incorrect.'; + setcookie('keeplogged', '', time() + 60 * 60 * 24 * 365, '/', '', false); + } + + } else { + log_attempt($UserID); + setcookie('keeplogged', '', time() + 60 * 60 * 24 * 365, '/', '', false); + } + + } else { + log_attempt('0'); + setcookie('keeplogged', '', time() + 60 * 60 * 24 * 365, '/', '', false); + } + } + require('sections/login/login.php'); } diff --git a/sections/login/login.php b/sections/login/login.php index aeae86709..6f2ad37b2 100644 --- a/sections/login/login.php +++ b/sections/login/login.php @@ -1,74 +1,87 @@ - - - - + + +

    + Welcome. +

    + If you had an account on Apollo at the time of the backup (June 2017) or have an invite email,
    please use the Recovery page to restore your account. +

    + If you would like to join Orpheus and you are on 32P, EMP, MTV or PTP, use the Referral page. +

    +
    + -
    - +query(" - UPDATE login_attempts - SET BannedUntil = '0000-00-00 00:00:00', Attempts = '0' - WHERE ID = '".db_string($AttemptID)."'"); - $Attempts = 0; - } - if (isset($Err)) { + if (!empty($BannedUntil) && $BannedUntil != '0000-00-00 00:00:00') { + $DB->query(" + UPDATE login_attempts + SET BannedUntil = '0000-00-00 00:00:00', Attempts = '0' + WHERE ID = '".db_string($AttemptID)."'"); + $Attempts = 0; + } + if (isset($Err)) { ?> -

    - - 0) { ?> - You have attempts remaining.

    - WARNING: You will be banned for 6 hours after your login attempts run out!

    - - - You have entered an invalid two-factor authentication key. Please login again. - - - - - - - - - - - - - - - -
    Username  - -
    Password  - -
    - /> - -
    -
    -

    + + 0) { ?> + You have attempts remaining.

    + WARNING: You will be banned for 6 hours after your login attempts run out!

    + + + You have entered an invalid two-factor authentication key. Please login again. + + + + + + + + + + + + + + + +
    Username  + +
    Password  + +
    + /> + +
    + + - You are banned from logging in for another . -You are banned from logging in for another . + 0) { ?> -

    - Lost your password? Recover it here! -
    + Lost your password? Recover it here! + -GenerateJS('recoverform'); ?>
    -
    - Reset your password - Step 1

    - + Reset your password - Step 1

    + -

    - - If the email address you gave is in our records, a message will be sent to it, with information on how to reset your password.

    - - - - - - - - -
    Email address: 
    -


    + + If the email address you gave is in our records, a message will be sent to it, with information on how to reset your password.

    + + + + + + + + +
    Email address: 
    + - An email has been sent to you; please follow the directions in that email to reset your password. - -
    +
    - true]); ?> diff --git a/sections/login/recover_step2.php b/sections/login/recover_step2.php index 5ccd29c8f..4b9eeb5d6 100644 --- a/sections/login/recover_step2.php +++ b/sections/login/recover_step2.php @@ -1,38 +1,41 @@ -GenerateJS('recoverform'); ?>
    - -
    - Reset your password - Final Step

    -" /> +
    + Reset your password - Final Step

    + -

    - A strong password is 8 characters or longer, contains at least 1 lowercase and uppercase letter, and contains at least a number or symbol, or is 20 characters or longer.

    - - - - - - - - - - - - -
    Password 
    Confirm Password 
    - - Your password has been successfully reset.
    - Please click here to log in using your new password. - -
    +

    + A strong password is 8 characters or longer, contains at least 1 lowercase and uppercase letter, and contains at least a number or symbol, or is 20 characters or longer.

    + + + + + + + + + + + + +
    Password 
    Confirm Password 
    + + Your password has been successfully reset.
    + Please click here to log in using your new password. + +
    - true]); ?> diff --git a/sections/logout/index.php b/sections/logout/index.php index 56f895c13..14a9f93a6 100644 --- a/sections/logout/index.php +++ b/sections/logout/index.php @@ -1,4 +1,4 @@ -'; + View::show_header(); + echo '
    ';
     }
     
     ignore_user_abort();
    @@ -18,89 +18,90 @@
     $Cache->InternalCache = false; // We don't want PHP to cache all results internally
     $DB->query("TRUNCATE TABLE torrents_peerlists_compare");
     $DB->query("
    -	INSERT INTO torrents_peerlists_compare
    -	SELECT ID, GroupID, Seeders, Leechers, Snatched
    -	FROM torrents
    -	ON DUPLICATE KEY UPDATE
    -		Seeders = VALUES(Seeders),
    -		Leechers = VALUES(Leechers),
    -		Snatches = VALUES(Snatches)");
    +    INSERT INTO torrents_peerlists_compare
    +    SELECT t.ID, t.GroupID, tls.Seeders, tls.Leechers, tls.Snatched
    +    FROM torrents t
    +    INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID)
    +    ON DUPLICATE KEY UPDATE
    +        Seeders = VALUES(Seeders),
    +        Leechers = VALUES(Leechers),
    +        Snatches = VALUES(Snatches)");
     $DB->query("
    -	CREATE TEMPORARY TABLE tpc_temp
    -		(TorrentID int, GroupID int, Seeders int, Leechers int, Snatched int,
    -	PRIMARY KEY (GroupID, TorrentID))");
    +    CREATE TEMPORARY TABLE tpc_temp
    +        (TorrentID int, GroupID int, Seeders int, Leechers int, Snatched int,
    +    PRIMARY KEY (GroupID, TorrentID))");
     $DB->query("
    -	INSERT INTO tpc_temp
    -	SELECT t2.*
    -	FROM torrents_peerlists AS t1
    -		JOIN torrents_peerlists_compare AS t2
    -	USING(TorrentID)
    -	WHERE t1.Seeders != t2.Seeders
    -		OR t1.Leechers != t2.Leechers
    -		OR t1.Snatches != t2.Snatches");
    +    INSERT INTO tpc_temp
    +    SELECT t2.*
    +    FROM torrents_peerlists AS t1
    +        JOIN torrents_peerlists_compare AS t2
    +    USING(TorrentID)
    +    WHERE t1.Seeders != t2.Seeders
    +        OR t1.Leechers != t2.Leechers
    +        OR t1.Snatches != t2.Snatches");
     
     $StepSize = 30000;
     $DB->query("
    -	SELECT *
    -	FROM tpc_temp
    -	ORDER BY GroupID ASC, TorrentID ASC
    -	LIMIT $StepSize");
    +    SELECT *
    +    FROM tpc_temp
    +    ORDER BY GroupID ASC, TorrentID ASC
    +    LIMIT $StepSize");
     
     $RowNum = 0;
     $LastGroupID = 0;
     $UpdatedKeys = $UncachedGroups = 0;
     list($TorrentID, $GroupID, $Seeders, $Leechers, $Snatches) = $DB->next_record(MYSQLI_NUM, false);
     while ($TorrentID) {
    -	if ($LastGroupID != $GroupID) {
    -		$CachedData = $Cache->get_value("torrent_group_$GroupID");
    -		if ($CachedData !== false) {
    -			if (isset($CachedData['ver']) && $CachedData['ver'] == CACHE::GROUP_VERSION) {
    -				$CachedStats = &$CachedData['d']['Torrents'];
    -			}
    -		} else {
    -			$UncachedGroups++;
    -		}
    -		$LastGroupID = $GroupID;
    -	}
    -	while ($LastGroupID == $GroupID) {
    -		$RowNum++;
    -		if (isset($CachedStats) && is_array($CachedStats[$TorrentID])) {
    -			$OldValues = &$CachedStats[$TorrentID];
    -			$OldValues['Seeders'] = $Seeders;
    -			$OldValues['Leechers'] = $Leechers;
    -			$OldValues['Snatched'] = $Snatches;
    -			$Changed = true;
    -			unset($OldValues);
    -		}
    -		if (!($RowNum % $StepSize)) {
    -			$DB->query("
    -				SELECT *
    -				FROM tpc_temp
    -				WHERE GroupID > $GroupID
    -					OR (GroupID = $GroupID AND TorrentID > $TorrentID)
    -				ORDER BY GroupID ASC, TorrentID ASC
    -				LIMIT $StepSize");
    -		}
    -		$LastGroupID = $GroupID;
    -		list($TorrentID, $GroupID, $Seeders, $Leechers, $Snatches) = $DB->next_record(MYSQLI_NUM, false);
    -	}
    -	if ($Changed) {
    -		$Cache->cache_value("torrent_group_$LastGroupID", $CachedData, 0);
    -		unset($CachedStats);
    -		$UpdatedKeys++;
    -		$Changed = false;
    -	}
    +    if ($LastGroupID != $GroupID) {
    +        $CachedData = $Cache->get_value("torrent_group_$GroupID");
    +        if ($CachedData !== false) {
    +            if (isset($CachedData['ver']) && $CachedData['ver'] == CACHE::GROUP_VERSION) {
    +                $CachedStats = &$CachedData['d']['Torrents'];
    +            }
    +        } else {
    +            $UncachedGroups++;
    +        }
    +        $LastGroupID = $GroupID;
    +    }
    +    while ($LastGroupID == $GroupID) {
    +        $RowNum++;
    +        if (isset($CachedStats) && is_array($CachedStats[$TorrentID])) {
    +            $OldValues = &$CachedStats[$TorrentID];
    +            $OldValues['Seeders'] = $Seeders;
    +            $OldValues['Leechers'] = $Leechers;
    +            $OldValues['Snatched'] = $Snatches;
    +            $Changed = true;
    +            unset($OldValues);
    +        }
    +        if (!($RowNum % $StepSize)) {
    +            $DB->query("
    +                SELECT *
    +                FROM tpc_temp
    +                WHERE GroupID > $GroupID
    +                    OR (GroupID = $GroupID AND TorrentID > $TorrentID)
    +                ORDER BY GroupID ASC, TorrentID ASC
    +                LIMIT $StepSize");
    +        }
    +        $LastGroupID = $GroupID;
    +        list($TorrentID, $GroupID, $Seeders, $Leechers, $Snatches) = $DB->next_record(MYSQLI_NUM, false);
    +    }
    +    if ($Changed) {
    +        $Cache->cache_value("torrent_group_$LastGroupID", $CachedData, 0);
    +        unset($CachedStats);
    +        $UpdatedKeys++;
    +        $Changed = false;
    +    }
     }
     printf("Updated %d keys, skipped %d keys in %.6fs (%d kB memory)\n", $UpdatedKeys, $UncachedGroups, microtime(true) - $ScriptStartTime, memory_get_usage(true) >> 10);
     
     $DB->query("TRUNCATE TABLE torrents_peerlists");
     $DB->query("
    -	INSERT INTO torrents_peerlists
    -	SELECT *
    -	FROM torrents_peerlists_compare");
    +    INSERT INTO torrents_peerlists
    +    SELECT *
    +    FROM torrents_peerlists_compare");
     
     if (check_perms('admin_schedule')) {
    -	echo '
    ';
    -	View::show_footer();
    +    echo '
    ';
    +    View::show_footer();
     }
     ?>
    diff --git a/sections/questions/ajax_get_answers.php b/sections/questions/ajax_get_answers.php
    index e3dde0fc8..7c5a97c17 100644
    --- a/sections/questions/ajax_get_answers.php
    +++ b/sections/questions/ajax_get_answers.php
    @@ -1,35 +1,35 @@
    -query("
    -		SELECT UserID, Answer, Date
    -		FROM staff_answers
    -		WHERE QuestionID = '$ID'
    -			$UserIDSQL
    -		ORDER BY DATE DESC");
    +        SELECT UserID, Answer, Date
    +        FROM staff_answers
    +        WHERE QuestionID = '$ID'
    +            $UserIDSQL
    +        ORDER BY DATE DESC");
     
     $Answers = G::$DB->to_array(false, MYSQLI_ASSOC);
     foreach($Answers as $Answer) {
     ?>
    -	
    -
    - - Answer by - - -
    -
    - -
    -
    - +
    + + Answer by - + +
    +
    + +
    + + diff --git a/sections/questions/answer_question.php b/sections/questions/answer_question.php index fee224598..60433f5e8 100644 --- a/sections/questions/answer_question.php +++ b/sections/questions/answer_question.php @@ -1,53 +1,53 @@ -query(" - SELECT ID, Question, UserID, Date - FROM user_questions - WHERE ID = '$ID'"); + SELECT ID, Question, UserID, Date + FROM user_questions + WHERE ID = '$ID'"); $Question = $DB->next_record(); View::show_header('Ask the Staff', 'bbcode'); ?>
    -

    - Answer Question -

    - -query(" + case 'collage': + $DB->query(" SELECT r1.ID FROM collages AS r1 JOIN (SELECT (RAND() * @@ -14,11 +14,11 @@ WHERE r1.ID >= r2.ID ORDER BY r1.ID ASC LIMIT 1"); - $collage = $DB->next_record(); - header("Location: collages.php?id={$collage['ID']}"); - break; - case 'artist': - $DB->query(" + $collage = $DB->next_record(); + header("Location: collages.php?id={$collage['ID']}"); + break; + case 'artist': + $DB->query(" SELECT r1.ArtistID FROM artists_group AS r1 JOIN (SELECT (RAND() * @@ -28,12 +28,12 @@ WHERE r1.ArtistID >= r2.ArtistID ORDER BY r1.ArtistID ASC LIMIT 1"); - $artist = $DB->next_record(); - header("Location: artist.php?id={$artist['ArtistID']}"); - break; - case 'torrent': - default: - $DB->query(" + $artist = $DB->next_record(); + header("Location: artist.php?id={$artist['ArtistID']}"); + break; + case 'torrent': + default: + $DB->query(" SELECT r1.ID FROM torrents_group AS r1 JOIN (SELECT (RAND() * @@ -43,7 +43,7 @@ WHERE r1.ID >= r2.ID ORDER BY r1.ID ASC LIMIT 1"); - $torrent = $DB->next_record(); - header("Location: torrents.php?id={$torrent['ID']}"); - break; -} \ No newline at end of file + $torrent = $DB->next_record(); + header("Location: torrents.php?id={$torrent['ID']}"); + break; +} diff --git a/sections/randomcollage/index.php b/sections/randomcollage/index.php index ee3bef18f..3b16d990e 100644 --- a/sections/randomcollage/index.php +++ b/sections/randomcollage/index.php @@ -13,4 +13,4 @@ ORDER BY r1.ID ASC LIMIT 1"); $collage = $DB->next_record(); -header("Location: collages.php?id={$collage['ID']}"); \ No newline at end of file +header("Location: collages.php?id={$collage['ID']}"); diff --git a/sections/recovery/admin.php b/sections/recovery/admin.php new file mode 100644 index 000000000..6a48d1b3f --- /dev/null +++ b/sections/recovery/admin.php @@ -0,0 +1,135 @@ + 0) + ? (int)$_GET['id'] + : 0; + if ($id > 0) { + switch ($_GET['task']) { + case 'accept'; + $ok = \Gazelle\Recovery::accept($id, G::$LoggedUser['ID'], G::$LoggedUser['Username'], G::$DB); + $message = $ok ? 'Invite sent' : 'Invite not sent, check log'; + break; + case 'deny'; + \Gazelle\Recovery::deny($id, G::$LoggedUser['ID'], G::$LoggedUser['Username'], G::$DB); + $message = sprintf('Request %d was denied', $id); + break; + case 'unclaim'; + \Gazelle\Recovery::unclaim($id, G::$LoggedUser['Username'], G::$DB); + $message = sprintf('Request %d was unclaimed', $id); + break; + default: + error(403); + break; + } + } +} +else { + foreach (explode(' ', 'token username announce email') as $field) { + if (array_key_exists($field, $_POST)) { + $value = trim($_POST[$field]); + if (strlen($value)) { + header("Location: /recovery.php?action=search&$field=$value"); + exit; + } + } + } +} + + +$Page = (isset($_GET['page']) && (int)$_GET['page'] > 0) + ? (int)$_GET['page'] : 1; +$Limit = 100; +$Offset = $Limit * ($Page-1); + +$State = isset($_GET['state']) ? $_GET['state'] : 'pending'; +$Total = \Gazelle\Recovery::get_total($State, G::$LoggedUser['ID'], G::$DB); +$Info = \Gazelle\Recovery::get_list($Limit, $Offset, $State, G::$LoggedUser['ID'], G::$DB); + +$Pages = Format::get_pages($Page, $Total, $Limit); +?> + +
    + + + +
    + + + + + + +
    TokenUsername
    AnnounceEmail
    + +

    recovery requests

    + + +
    + + + + +
    +
    Registrations
    +
    + + + + + + + + + + + + + + + + + + + + + + + +
    IDUsernameTokenEmailAnnounceCreatedUpdatedAction
    + View + + Claim + +
    +
    +
    + + + +
    + +
    + + + + +
    +No $class matched $target

    "; + } + else { +?> +
    Users matched search criteria
    +

    The following match in the previous site

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    UsernameIDEmailUploadedDownloadedEnabledTorrentsAnnounce
    + +
    + + +
    +
    Browse recovery details
    + +
    + +

    Enter one of the following fields to search (Use % as a wildcard character, e.g. C17%).

    + + + + + + + + + + + + +
    Username
    Email
    Announce
    + + +
    +
    +Recovery is currently closed. Please join irc.orpheus.network (port 6669 or +7000 for SSL) for +more information (#recovery channel).

    diff --git a/sections/recovery/form.php b/sections/recovery/form.php new file mode 100644 index 000000000..a09fdc7e7 --- /dev/null +++ b/sections/recovery/form.php @@ -0,0 +1,88 @@ +

    Membership recovery

    + +

    Some people are in the recovered backup, some are not. If you had registered before 2017-06-18, +you are in. You will need to supply only your username and email or announce key.

    + +

    If you signed up afterwards, or you have lost (or never received) your invite email, things are a +little more complicated. We will consider any proof you may be able to supply, such as the complete +signup email, or screenshots of your profile page. Staff has the final say in whether the proof is +sufficient.

    + +
    + +
    Your username
    + +

    Should be obvious – your username on Apollo. When you receive your invite link, you have the +chance to change it to something else if that is your wish.

    + + + +
    Your email
    + +

    The email address used for registration. If you changed your address at some point, any address +is valid, assuming it is the the backup.

    + + + +
    Your Password
    + +

    This will be hashed upon reception and compared with the existing hash of your password. +When you receive an invite to , you should not reuse this password.

    + + + +
    Your announce key
    + +

    You can look this up by viewing the properties of an APL torrent. The key is a long string of +hexadecimal (digits 0 to and 9 and letters A to F) characters. This is optional, but provides +additional proof that you are who you say you are and can be used in lieu of a password.

    + + + +
    Your original APL/XNX invitation
    + +

    Please copy-paste the original raw message.

    + +

    In Protonmail, this requires two steps. Firstly, you need to obtain the headers. View the message +and hover over the rightmost button (More) in the navigation bar. Choose "View Headers". Copy and +paste this into the field below. Then copy the text body of the message.

    + +

    In Gmail, this is "View Original". In Exchange and Thunderbird, this is "View Source". +Other mail clients will have similar options.

    + +

    If you signed up after 2017-06-18, your account is not in the backup. If you have an +invitation email, this is accepted proof.

    + + + +
    Screenshots
    + +

    Bundle up any screenshots or HTML "Save as" copies of your profile into a tarball, or any +standard archive format, compressed as you see fit. We should be able to figure out most formats. +Size is limited to 10MiB. Only HTML and PNG and JPEG image formats are accepted. All other +filetypes will be discarded.

    + + + +
    Additional information
    + +

    You may optionally supply any additional information you deem necessary, such as your final +user class on Apollo as you remember it.

    + + + +

    Upon clicking "Send", your uploaded information will be stored, validated and then reviewed by +Staff. If everything checks out, an invite email will be sent to your address. Information you +supply will be stored in a holding pen and will be deleted after thirty days (so if you haven't +heard back by then, you never will).

    + +

    On the next page, a unique token will be generated for you. You should make a note of it, as it +can be used as an identifier on the IRC #recovery channel if you need to get in touch with us. +Never paste it in any channel, a Staff member will ask you to send it via a private message.

    + + + +

    Rate limiting applies. You cannot submit this form more than once every five minutes from the +same IP address.

    + +
    diff --git a/sections/recovery/index.php b/sections/recovery/index.php new file mode 100644 index 000000000..34bdb7e9a --- /dev/null +++ b/sections/recovery/index.php @@ -0,0 +1,33 @@ +$curr_id."; + } + else { + $curr = \Users::user_info($curr_id); + $prev_id = (int)trim($_POST['prev']); + $prev = \Gazelle\Recovery::get_candidate_by_id($prev_id, G::$DB); + if (!$prev) { + $Result = "No previous ID found for $prev_id."; + } + elseif ($Map = \Gazelle\Recovery::is_mapped($prev_id, G::$DB)) { + $ID = $Map[0]['ID']; + $Result = "Previous id $prev_id already mapped to " . \Users::format_username($ID); + } + elseif ($Map = \Gazelle\Recovery::is_mapped_local($curr_id, G::$DB)) { + $ID = $Map[0]['ID']; + $Result = \Users::format_username($curr_id) . " is already mapped to previous id $ID"; + } + else { + list($Prev, $Confirm) = \Gazelle\Recovery::get_pair_confirmation($prev_id, $curr_id, G::$DB); + if (!($Prev && $Confirm)) { + $Result = "No database information to pair from $prev_id to $curr_id"; + } + if (array_key_exists('check', $_POST)) { + if ($_POST['check'] != security_checksum($prev_id, $curr_id)) { + $Result = "Security checksum failed"; + } + else { + $Result = \Gazelle\Recovery::map_to_previous($curr_id, $prev_id, G::$LoggedUser['Username'], G::$DB) + ? \Users::format_username($curr_id) . " has been successfully mapped to previous user " .$Confirm['Username'] . "." + : "DB Error: could not map $curr_id to $prev_id" + ; + unset($Confirm); + } + } + } + } +} + +View::show_header('Recovery pair users'); +?> +
    + + + + +
    +
    Result
    +
    +
    + +
    +
    Confirm
    + +
    +

    Please confirm the following pairing:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Previous
    ID
    Username
    Userclass
    Email
    Announce
    Torrents
    + + + + +

    +
    + + +
    +
    Pair user
    + +

    In the following section you will be asked to pair a user on with their original account on the previous site. + Once this assocation has been recorded, torrents, buffer, bookmarks etc, from the previous account will be assigned to + the account.

    + +
    +
    + + + + + + + + + +
    ID
    Previous ID
    + +
    +
    +
    + + + + +<?= SITE_NAME ?> :: Membership recovery + + + + + + + + + + +
    +

    +

    + +
    + +
    + + + diff --git a/sections/recovery/save.php b/sections/recovery/save.php new file mode 100644 index 000000000..194708fc3 --- /dev/null +++ b/sections/recovery/save.php @@ -0,0 +1,104 @@ + + + +<?= SITE_NAME ?> :: Membership recovery + + + + + + + + + +
    +get_value($key)) { + $msg = "Rate limiting in force.
    You tried to save this page too rapidly following the previous save."; +} +else { + $info = \Gazelle\Recovery::validate($_POST); + if (count($info)) { + $info['ipaddr'] = $ipaddr; + $info['password_ok'] = \Gazelle\Recovery::check_password($info['username'], $_POST['password'], G::$DB); + + list($ok, $filename) = \Gazelle\Recovery::save_screenshot($_FILES); + if (!$ok) { + $msg = $filename; // the reason we were unable to save the screenshot info + } + else { + $info['screenshot'] = $filename; + + $token = ''; + for ($i = 0; $i < 16; ++$i) { + $token .= chr(mt_rand(97, 97+25)); + if (($i+1) % 4 == 0 && $i < 15) { + $token .= '-'; + } + } + $info['token'] = $token; + + if (\Gazelle\Recovery::persist($info, G::$DB)) { + $msg = 'ok'; + } + else { + $msg = "Unable to save, are you sure you haven't registered already?"; + } + } + } + else { + $msg = "Your upload was not accepted."; + } +} +G::$Cache->cache_value($key, 1, 300); + +if ($msg == 'ok') { +?> +

    Success!

    +

    Your information has been uploaded and secured. It will be held for the next 30 days and then removed.

    + +

    Please save the following token away for future reference. If you need to get in touch with Staff, this is the only +way you will be able to associate yourself with what you have just uploaded.

    + +

    + +

    Keep an eye on your mailbox, and we hope to see you soon. If you don't receive anything in an hour (check and +recheck your spam folder), join the #recovery channel on IRC.

    + +
    +

    IRC details

    +

    Server: irc.orpheus.network
    +Port: 6667 or +7000 for SSL

    +
    + + +

    There was a problem

    +

    Your information was not saved for the following reason.

    + +
    + +

    Please wait five minutes and try again.

    + +
    + + diff --git a/sections/recovery/view.php b/sections/recovery/view.php new file mode 100644 index 000000000..b696f31ac --- /dev/null +++ b/sections/recovery/view.php @@ -0,0 +1,172 @@ + 0) { + $ID = (int)$_GET['id']; + $search = false; +} +elseif (isset($_GET['action']) && $_GET['action'] == 'search') { + $search = true; +} +else { + error(404); +} + +if ($search) { + $terms = []; + foreach (explode(' ', 'token username email announce') as $key) { + if (isset($_GET[$key])) { + $terms[] = [$key => $_GET[$key]]; + } + } + $Info = \Gazelle\Recovery::search($terms, G::$DB); + $ID = $Info['recovery_id']; +} +else { + if (isset($_GET['claim']) and (int)$_GET['claim'] > 0) { + $claim_id = (int)$_GET['claim']; + if ($claim_id == G::$LoggedUser['ID']) { + \Gazelle\Recovery::claim($ID, $claim_id, G::$LoggedUser['Username'], G::$DB); + } + } + $Info = \Gazelle\Recovery::get_details($ID, G::$DB); +} + +$Email = ($Info['email'] == $Info['email_clean']) + ? $Info['email'] + : $Info['email_clean'] . "
    (cleaned from " . $Info['email'] . ")"; + +$Candidate = \Gazelle\Recovery::get_candidate($Info['username'], G::$DB); +$enabled = ['Unconfirmed', 'Enabled', 'Disabled']; +?> + +
    + + + + +

    Nobody home

    + +

    No recovery request matched the search terms

    +

    +
      + $value) { + echo "
    • $field: $value
    • "; + } + } +?> +
    +
    + + + +

    View recovery details for

    + +
    +
    Registration details
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    UsernameUsername matches (ID=' . $Candidate['UserID'] . ')' : 'No match for username' ?>state
    + torrents up down
    Password verifiedClaimed by
    emailCreated
    Announce keyUpdated
    Remote IP', $Candidate['ips']) ?>Token
    Screenshot info
    Invitation email
    info
    log
    + +

    Actions

    + +

    Accept - An invite will be emailed to the user

    +

    Deny - The request is denied, no e-mail will be sent

    + +

    Unclaim - Release the claim on this request, you don't know what to do.

    + +

    Claim - Claim this request, you need to contact the person via IRC.

    + +
    +
    + + +
    +services_list(); - - // head to step 2 if are ready to verify an account at the external service - if (!empty($_POST['username']) && $_POST['submit'] === 'Verify') { - include('referral_step_2.php'); + // redirect if referrals are currently closed + if (!OPEN_EXTERNAL_REFERRALS) { + View::show_header("Referrals are closed"); +?> +
    + Sorry, the site is not accepting referral invites. +
    +getActiveAccounts(); View::show_header('External Tracker Referrals'); ?> - -
    +
    +

    External Tracker Referrals


    Here you are able to gain access to by verifying that you are a member of another private tracker that we trust.

    @@ -50,22 +36,115 @@
  • Join !
  • - +

    Choose a Tracker:


    -
    -

    '; - } ?> -
    - -
    - +
    +
    + +
    + "/> + +
    +
    +
    + +
    + + +
    +
    +

    Sorry, we aren't accepting external tracker referrals at this time. Please try again later.


    - +generateToken(); + $_SESSION['referral_token'] = $Token; + $Account = $ReferralManager->getAccount($_POST['service']); +?> +
    +

    Step 2: Paste Your Code

    +
    +

    Copy and paste the code below into the profile of your account. It can go anywhere in your profile body (commonly known as "Profile info 1") as long as it is in one piece.

    +
    +

    +
    +

    Enter the you use at exactly as it appears on the site. This is critical in verifying your account.

    +
    +
    +
    + +
    +
    + + + " /> + +
    +
    + +
    +
    +
    +getFullAccount($_POST['service']); + if ($Account["UserIsId"] && !preg_match('/^\d+$/', $_POST['username'])) { + $Error = "You appear to have entered a username instead of your user id."; + } else { + $Verified = $ReferralManager->verifyAccount($Account, $_POST['username'], $Token); + if ($Verified === true) { + list($Success, $Invite) = $ReferralManager->generateInvite($Account, $_POST['username'], $Email, G::$Twig); + if (!$Success) { + $Error = $Invite; + } + else if ($Invite === false) { + $Error = "Failed to generate invite."; + } + } else { + $Error = $Verified; + } + } + } else { + $Error = "Invalid email address."; + } +?> +
    +

    Step 2: Join

    +
    + +

    There was an error verifying your account at . Please refresh the page and try again.

    +
    +

    + +

    Congratulations, you have verified your account at . We have sent you an email to the address you specified. Make sure to check your spam folder! Welcome to !

    + +

    Congratulations, you have verified your account at . Click here to register. Welcome to

    +
    - \ No newline at end of file + diff --git a/sections/register/closed.php b/sections/register/closed.php index 88e1b443b..41589e2aa 100644 --- a/sections/register/closed.php +++ b/sections/register/closed.php @@ -1,9 +1,9 @@ -
    - Sorry, the site is currently invite only. + Sorry, the site is currently invite only.
    - diff --git a/sections/register/code.php b/sections/register/code.php index 0d95d5284..1c868dab3 100644 --- a/sections/register/code.php +++ b/sections/register/code.php @@ -1,20 +1,20 @@ -
    -
    - Please enter your invite code into the box below.

    - - - - - - - - -
    Invite 
    -
    +
    + Please enter your invite code into the box below.

    + + + + + + + + +
    Invite 
    +
    - diff --git a/sections/register/index.php b/sections/register/index.php index 7cb84d3bc..16a65a366 100644 --- a/sections/register/index.php +++ b/sections/register/index.php @@ -1,11 +1,11 @@ -query(" - SELECT ID - FROM users_main - WHERE torrent_pass = '".db_string($_REQUEST['confirm'])."' - AND Enabled = '0'"); - list($UserID) = $DB->next_record(); - - if ($UserID) { - $DB->query(" - UPDATE users_main - SET Enabled = '1' - WHERE ID = '$UserID'"); - $Cache->delete_value("user_info_{$UserID}"); - $Cache->increment('stats_user_count'); - include('step2.php'); - } + // Confirm registration + $DB->query(" + SELECT ID + FROM users_main + WHERE torrent_pass = '".db_string($_REQUEST['confirm'])."' + AND Enabled = '0'"); + list($UserID) = $DB->next_record(); + + if ($UserID) { + $DB->query(" + UPDATE users_main + SET Enabled = '1' + WHERE ID = '$UserID'"); + $Cache->delete_value("user_info_{$UserID}"); + $Cache->increment('stats_user_count'); + include('step2.php'); + } } elseif (OPEN_REGISTRATION || !empty($_REQUEST['invite'])) { - $Val->SetFields('username', true, 'regex', 'You did not enter a valid username.', array('regex' => USERNAME_REGEX)); - $Val->SetFields('email', true, 'email', 'You did not enter a valid email address.'); - $Val->SetFields('password', true, 'regex', 'A strong password is 8 characters or longer, contains at least 1 lowercase and uppercase letter, and contains at least a number or symbol, or is 20 characters or longer', array('regex'=>'/(?=^.{8,}$)(?=.*[^a-zA-Z])(?=.*[A-Z])(?=.*[a-z]).*$|.{20,}/')); - $Val->SetFields('confirm_password', true, 'compare', 'Your passwords do not match.', array('comparefield' => 'password')); - $Val->SetFields('readrules', true, 'checkbox', 'You did not select the box that says you will read the rules.'); - $Val->SetFields('readwiki', true, 'checkbox', 'You did not select the box that says you will read the wiki.'); - $Val->SetFields('agereq', true, 'checkbox', 'You did not select the box that says you are 13 years of age or older.'); - //$Val->SetFields('captcha', true, 'string', 'You did not enter a captcha code.', array('minlength' => 6, 'maxlength' => 6)); - - if (!empty($_POST['submit'])) { - // User has submitted registration form - $Err = $Val->ValidateForm($_REQUEST); - /* - if (!$Err && strtolower($_SESSION['captcha']) != strtolower($_REQUEST['captcha'])) { - $Err = 'You did not enter the correct captcha code.'; - } - */ - if (!$Err) { - // Don't allow a username of "0" or "1" due to PHP's type juggling - if (trim($_POST['username']) == '0' || trim($_POST['username']) == '1') { - $Err = 'You cannot have a username of "0" or "1".'; - } - - $DB->query(" - SELECT COUNT(ID) - FROM users_main - WHERE Username LIKE '".db_string(trim($_POST['username']))."'"); - list($UserCount) = $DB->next_record(); - - if ($UserCount) { - $Err = 'There is already someone registered with that username.'; - $_REQUEST['username'] = ''; - } - - if ($_REQUEST['invite']) { - $DB->query(" - SELECT InviterID, Email, Reason - FROM invites - WHERE InviteKey = '".db_string($_REQUEST['invite'])."'"); - if (!$DB->has_results()) { - $Err = 'Invite does not exist.'; - $InviterID = 0; - } else { - list($InviterID, $InviteEmail, $InviteReason) = $DB->next_record(MYSQLI_NUM, false); - } - } else { - $InviterID = 0; - $InviteEmail = $_REQUEST['email']; - $InviteReason = ''; - } - } - - if (!$Err) { - $torrent_pass = Users::make_secret(); - - // Previously SELECT COUNT(ID) FROM users_main, which is a lot slower. - $DB->query(" - SELECT ID - FROM users_main - LIMIT 1"); - $UserCount = $DB->record_count(); - if ($UserCount == 0) { - $NewInstall = true; - $Class = SYSOP; - $Enabled = '1'; - } else { - $NewInstall = false; - $Class = USER; - $Enabled = '0'; - } - - $IPcc = Tools::geoip($_SERVER['REMOTE_ADDR']); - - $DB->query(" - INSERT INTO users_main - (Username, Email, PassHash, torrent_pass, IP, PermissionID, Enabled, Invites, Uploaded, ipcc) - VALUES - ('".db_string(trim($_POST['username']))."', '".db_string($_POST['email'])."', '".db_string(Users::make_password_hash($_POST['password']))."', '".db_string($torrent_pass)."', '".db_string($_SERVER['REMOTE_ADDR'])."', '$Class', '$Enabled', '".STARTING_INVITES."', '".STARTING_UPLOAD."', '$IPcc')"); - - $UserID = $DB->inserted_id(); - - - // User created, delete invite. If things break after this point, then it's better to have a broken account to fix than a 'free' invite floating around that can be reused - $DB->query(" - DELETE FROM invites - WHERE InviteKey = '".db_string($_REQUEST['invite'])."'"); - - $DB->query(" - SELECT ID - FROM stylesheets - WHERE `Default` = '1'"); - list($StyleID) = $DB->next_record(); - $AuthKey = Users::make_secret(); - - if ($InviteReason !== '') { - $InviteReason = db_string(sqltime()." - $InviteReason"); - } - $DB->query(" - INSERT INTO users_info - (UserID, StyleID, AuthKey, Inviter, JoinDate, AdminComment) - VALUES - ('$UserID', '$StyleID', '".db_string($AuthKey)."', '$InviterID', '".sqltime()."', '$InviteReason')"); - - $DB->query(" - INSERT INTO users_history_ips - (UserID, IP, StartTime) - VALUES - ('$UserID', '".db_string($_SERVER['REMOTE_ADDR'])."', '".sqltime()."')"); - $DB->query(" - INSERT INTO users_notifications_settings - (UserID) - VALUES - ('$UserID')"); - - - $DB->query(" - INSERT INTO users_history_emails - (UserID, Email, Time, IP) - VALUES - ('$UserID', '".db_string($_REQUEST['email'])."', '0000-00-00 00:00:00', '".db_string($_SERVER['REMOTE_ADDR'])."')"); - - if ($_REQUEST['email'] != $InviteEmail) { - $DB->query(" - INSERT INTO users_history_emails - (UserID, Email, Time, IP) - VALUES - ('$UserID', '".db_string($InviteEmail)."', '".sqltime()."', '".db_string($_SERVER['REMOTE_ADDR'])."')"); - } - - - - // Manage invite trees, delete invite - - if ($InviterID !== null) { - $DB->query(" - SELECT TreePosition, TreeID, TreeLevel - FROM invite_tree - WHERE UserID = '$InviterID'"); - list($InviterTreePosition, $TreeID, $TreeLevel) = $DB->next_record(); - - // If the inviter doesn't have an invite tree - // Note: This should never happen unless you've transferred from another database, like What.CD did - if (!$DB->has_results()) { - $DB->query(" - SELECT MAX(TreeID) + 1 - FROM invite_tree"); - list($TreeID) = $DB->next_record(); - - $DB->query(" - INSERT INTO invite_tree - (UserID, InviterID, TreePosition, TreeID, TreeLevel) - VALUES ('$InviterID', '0', '1', '$TreeID', '1')"); - - $TreePosition = 2; - $TreeLevel = 2; - } else { - $DB->query(" - SELECT TreePosition - FROM invite_tree - WHERE TreePosition > '$InviterTreePosition' - AND TreeLevel <= '$TreeLevel' - AND TreeID = '$TreeID' - ORDER BY TreePosition - LIMIT 1"); - list($TreePosition) = $DB->next_record(); - - if ($TreePosition) { - $DB->query(" - UPDATE invite_tree - SET TreePosition = TreePosition + 1 - WHERE TreeID = '$TreeID' - AND TreePosition >= '$TreePosition'"); - } else { - $DB->query(" - SELECT TreePosition + 1 - FROM invite_tree - WHERE TreeID = '$TreeID' - ORDER BY TreePosition DESC - LIMIT 1"); - list($TreePosition) = $DB->next_record(); - } - $TreeLevel++; - - // Create invite tree record - $DB->query(" - INSERT INTO invite_tree - (UserID, InviterID, TreePosition, TreeID, TreeLevel) - VALUES - ('$UserID', '$InviterID', '$TreePosition', '$TreeID', '$TreeLevel')"); - } - } else { // No inviter (open registration) - $DB->query(" - SELECT MAX(TreeID) - FROM invite_tree"); - list($TreeID) = $DB->next_record(); - $TreeID++; - $InviterID = 0; - $TreePosition = 1; - $TreeLevel = 1; - } - - include(SERVER_ROOT.'/classes/templates.class.php'); - $TPL = NEW TEMPLATE; - $TPL->open(SERVER_ROOT.'/templates/new_registration.tpl'); - - $TPL->set('Username', $_REQUEST['username']); - $TPL->set('TorrentKey', $torrent_pass); - $TPL->set('SITE_NAME', SITE_NAME); - $TPL->set('SITE_URL', SITE_URL); - - Misc::send_email($_REQUEST['email'], 'New account confirmation at '.SITE_NAME, $TPL->get(), 'noreply'); - Tracker::update_tracker('add_user', array('id' => $UserID, 'passkey' => $torrent_pass)); - $Sent = 1; - - - } - } elseif ($_GET['invite']) { - // If they haven't submitted the form, check to see if their invite is good - $DB->query(" - SELECT InviteKey - FROM invites - WHERE InviteKey = '".db_string($_GET['invite'])."'"); - if (!$DB->has_results()) { - error('Invite not found!'); - } - } - - include('step1.php'); + $Val->SetFields('username', true, 'regex', 'You did not enter a valid username.', array('regex' => USERNAME_REGEX)); + $Val->SetFields('email', true, 'email', 'You did not enter a valid email address.'); + $Val->SetFields('password', true, 'regex', 'A strong password is 8 characters or longer, contains at least 1 lowercase and uppercase letter, and contains at least a number or symbol, or is 20 characters or longer', array('regex'=>'/(?=^.{8,}$)(?=.*[^a-zA-Z])(?=.*[A-Z])(?=.*[a-z]).*$|.{20,}/')); + $Val->SetFields('confirm_password', true, 'compare', 'Your passwords do not match.', array('comparefield' => 'password')); + $Val->SetFields('readrules', true, 'checkbox', 'You did not select the box that says you will read the rules.'); + $Val->SetFields('readwiki', true, 'checkbox', 'You did not select the box that says you will read the wiki.'); + $Val->SetFields('agereq', true, 'checkbox', 'You did not select the box that says you are 13 years of age or older.'); + //$Val->SetFields('captcha', true, 'string', 'You did not enter a captcha code.', array('minlength' => 6, 'maxlength' => 6)); + + if (!empty($_POST['submit'])) { + // User has submitted registration form + $Err = $Val->ValidateForm($_REQUEST); + /* + if (!$Err && strtolower($_SESSION['captcha']) != strtolower($_REQUEST['captcha'])) { + $Err = 'You did not enter the correct captcha code.'; + } + */ + if (!$Err) { + // Don't allow a username of "0" or "1" due to PHP's type juggling + if (trim($_POST['username']) == '0' || trim($_POST['username']) == '1') { + $Err = 'You cannot have a username of "0" or "1".'; + } + + $DB->query(" + SELECT COUNT(ID) + FROM users_main + WHERE Username LIKE '".db_string(trim($_POST['username']))."'"); + list($UserCount) = $DB->next_record(); + + if ($UserCount) { + $Err = 'There is already someone registered with that username.'; + $_REQUEST['username'] = ''; + } + + if ($_REQUEST['invite']) { + $DB->query(" + SELECT InviterID, Email, Reason + FROM invites + WHERE InviteKey = '".db_string($_REQUEST['invite'])."'"); + if (!$DB->has_results()) { + $Err = 'Invite does not exist.'; + $InviterID = 0; + } else { + list($InviterID, $InviteEmail, $InviteReason) = $DB->next_record(MYSQLI_NUM, false); + } + } else { + $InviterID = 0; + $InviteEmail = $_REQUEST['email']; + $InviteReason = ''; + } + } + + if (!$Err) { + $torrent_pass = Users::make_secret(); + + // Previously SELECT COUNT(ID) FROM users_main, which is a lot slower. + $DB->query(" + SELECT ID + FROM users_main + LIMIT 1"); + $UserCount = $DB->record_count(); + if ($UserCount == 0) { + $NewInstall = true; + $Class = SYSOP; + $Enabled = '1'; + } else { + $NewInstall = false; + $Class = USER; + $Enabled = '0'; + } + + $DB->prepared_query(' + INSERT INTO users_main + (Username, Email, PassHash, torrent_pass, IP, PermissionID, Enabled, Invites, ipcc) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + ', + trim($_POST['username']), + trim($_POST['email']), + Users::make_password_hash($_POST['password']), + $torrent_pass, + $_SERVER['REMOTE_ADDR'], + $Class, + $Enabled, + STARTING_INVITES, + Tools::geoip($_SERVER['REMOTE_ADDR']) + ); + $UserID = $DB->inserted_id(); + + // User created, delete invite. If things break after this point, then it's better to have a broken account to fix than a 'free' invite floating around that can be reused + $DB->prepared_query(' + DELETE FROM invites + WHERE InviteKey = ? + ', $_REQUEST['invite'] + ); + + $DB->prepared_query(' + INSERT INTO users_leech_stats + (UserID, Uploaded) + VALUES (?, ?) + ', + $UserID, + STARTING_UPLOAD + ); + + $DB->query(" + SELECT ID + FROM stylesheets + WHERE `Default` = '1'"); + list($StyleID) = $DB->next_record(); + $AuthKey = Users::make_secret(); + + if ($InviteReason !== '') { + $InviteReason = db_string(sqltime()." - $InviteReason"); + } + $DB->prepared_query(' + INSERT INTO users_info + (UserID, StyleID, AuthKey, Inviter, AdminComment, JoinDate) + VALUES (?, ?, ?, ?, ?, now()) + ', $UserID, $StyleID, $AuthKey, $InviterID, $InviteReason + ); + $DB->prepared_query(' + INSERT INTO users_history_ips + (UserID, IP, StartTime) + VALUES (?, ?, now()) + ', $UserID, $_SERVER['REMOTE_ADDR'] + ); + $DB->prepared_query(' + INSERT INTO users_notifications_settings + (UserID) + VALUES (?) + ', $UserID + ); + $DB->prepared_query(' + INSERT INTO users_history_emails + (UserID, Email, IP) + VALUES (?, ?, ?) + ', $UserID, trim($_REQUEST['email']), $_SERVER['REMOTE_ADDR'] + ); + + if ($_REQUEST['email'] != $InviteEmail) { + $DB->prepared_query(' + INSERT INTO users_history_emails + (UserID, Email, IP, Time) + VALUES (?, ?, ?, now()) + ', $UserID, trim($InviteEmail), $_SERVER['REMOTE_ADDR'] + ); + } + + $DB->prepared_query(" + UPDATE referral_users + SET UserID = ?, + Joined = ?, + Active = 1, + InviteKey = '' + WHERE InviteKey = ?", + $UserID, sqltime(), $_REQUEST['invite']); + + // Manage invite trees, delete invite + + if ($InviterID !== null) { + $DB->query(" + SELECT TreePosition, TreeID, TreeLevel + FROM invite_tree + WHERE UserID = '$InviterID'"); + list($InviterTreePosition, $TreeID, $TreeLevel) = $DB->next_record(); + + // If the inviter doesn't have an invite tree + // Note: This should never happen unless you've transferred from another database, like What.CD did + if (!$DB->has_results()) { + $DB->query(" + SELECT MAX(TreeID) + 1 + FROM invite_tree"); + list($TreeID) = $DB->next_record(); + + $DB->query(" + INSERT INTO invite_tree + (UserID, InviterID, TreePosition, TreeID, TreeLevel) + VALUES ('$InviterID', '0', '1', '$TreeID', '1')"); + + $TreePosition = 2; + $TreeLevel = 2; + } else { + $DB->query(" + SELECT TreePosition + FROM invite_tree + WHERE TreePosition > '$InviterTreePosition' + AND TreeLevel <= '$TreeLevel' + AND TreeID = '$TreeID' + ORDER BY TreePosition + LIMIT 1"); + list($TreePosition) = $DB->next_record(); + + if ($TreePosition) { + $DB->query(" + UPDATE invite_tree + SET TreePosition = TreePosition + 1 + WHERE TreeID = '$TreeID' + AND TreePosition >= '$TreePosition'"); + } else { + $DB->query(" + SELECT TreePosition + 1 + FROM invite_tree + WHERE TreeID = '$TreeID' + ORDER BY TreePosition DESC + LIMIT 1"); + list($TreePosition) = $DB->next_record(); + } + $TreeLevel++; + + // Create invite tree record + $DB->query(" + INSERT INTO invite_tree + (UserID, InviterID, TreePosition, TreeID, TreeLevel) + VALUES + ('$UserID', '$InviterID', '$TreePosition', '$TreeID', '$TreeLevel')"); + } + } else { // No inviter (open registration) + $DB->query(" + SELECT MAX(TreeID) + FROM invite_tree"); + list($TreeID) = $DB->next_record(); + $TreeID++; + $InviterID = 0; + $TreePosition = 1; + $TreeLevel = 1; + } + + $message = G::$Twig->render('emails/new_registration.twig', [ + 'Username' => $_REQUEST['username'], + 'TorrentKey' => $torrent_pass, + 'SITE_NAME' => SITE_NAME, + 'SITE_URL' => SITE_URL + ]); + + Misc::send_email($_REQUEST['email'], 'New account confirmation at '.SITE_NAME, $message, 'noreply'); + Tracker::update_tracker('add_user', array('id' => $UserID, 'passkey' => $torrent_pass)); + $Sent = 1; + + + } + } elseif ($_GET['invite']) { + // If they haven't submitted the form, check to see if their invite is good + $DB->query(" + SELECT InviteKey + FROM invites + WHERE InviteKey = '".db_string($_GET['invite'])."'"); + if (!$DB->has_results()) { + error('Invite not found!'); + } + } + + include('step1.php'); } elseif (!OPEN_REGISTRATION) { - if (isset($_GET['welcome'])) { - include('code.php'); - } else { - include('closed.php'); - } + if (isset($_GET['welcome'])) { + include('code.php'); + } else { + include('closed.php'); + } } ?> diff --git a/sections/register/step1.php b/sections/register/step1.php index a0bb32211..4a5ff8c3b 100644 --- a/sections/register/step1.php +++ b/sections/register/step1.php @@ -1,4 +1,4 @@ -GenerateJS('registerform'); ?> @@ -6,84 +6,87 @@
    - -" /> +'."\n"; - } - if (!empty($Err)) { + if (!empty($_REQUEST['invite'])) { + echo ''."\n"; + } + if (!empty($Err)) { ?> -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Username  - -

    Use common sense when choosing your username. Offensive usernames will not be tolerated. Do not choose a username that can be associated with your real name. If you do so, we will not be changing it for you.

    -
    Email address  - -
    Password  - -
    Verify password  - -

    A strong password is 8 characters or longer, contains at least 1 lowercase and uppercase letter, and contains at least a number or a symbol, or is 20 characters or longer.

    -
    - checked="checked" /> - -
    - checked="checked" /> - -
    - checked="checked" /> - -
    - - An email has been sent to the address that you provided. After you confirm your email address, you will be able to log into your account. - Congratulations! Your account has been created.
    - //You can now log into your account using the login page. - ?> +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Username  + +

    Use common sense when choosing your username. Offensive usernames will not be tolerated. Do not choose a username that can be associated with your real name. If you do so, we will not be changing it for you.

    +
    Email address  + +
    Password  + +
    Verify password  + +

    A strong password is 8 characters or longer, contains at least 1 lowercase and uppercase letter, and contains at least a number or a symbol, or is 20 characters or longer.

    +
    + checked="checked" /> + +
    + checked="checked" /> + +
    + checked="checked" /> + +
    + + An email has been sent to the address that you provided. After you confirm your email address, you will be able to log into your account. + Congratulations! Your account has been created.
    + //You can now log into your account using the login page. + ?> -
    -
    - Congratulations! Your account has been created.
    - You can now log into your account using the login page. + Congratulations! Your account has been created.
    + You can now log into your account using the login page.
    - diff --git a/sections/reports/ajax_add_notes.php b/sections/reports/ajax_add_notes.php index 00e2b7102..1827fb9f0 100644 --- a/sections/reports/ajax_add_notes.php +++ b/sections/reports/ajax_add_notes.php @@ -1,13 +1,13 @@ 'failure' - ) - ); - die(); + print + json_encode( + array( + 'status' => 'failure' + ) + ); + die(); } $ID = (int)$_POST['id']; @@ -16,13 +16,13 @@ $Notes = db_string($Notes); $DB->query(" - UPDATE reports - SET Notes = '$Notes' - WHERE ID = '$ID'"); + UPDATE reports + SET Notes = '$Notes' + WHERE ID = '$ID'"); print - json_encode( - array( - 'status' => 'success' - ) - ); + json_encode( + array( + 'status' => 'success' + ) + ); die(); diff --git a/sections/reports/ajax_claim_report.php b/sections/reports/ajax_claim_report.php index 344687814..041699482 100644 --- a/sections/reports/ajax_claim_report.php +++ b/sections/reports/ajax_claim_report.php @@ -1,41 +1,41 @@ 'failure' - ) - ); - die(); + print + json_encode( + array( + 'status' => 'failure' + ) + ); + die(); } $ID = (int)$_POST['id']; $DB->query(" - SELECT ClaimerID - FROM reports - WHERE ID = '$ID'"); + SELECT ClaimerID + FROM reports + WHERE ID = '$ID'"); list($ClaimerID) = $DB->next_record(); if ($ClaimerID) { - print - json_encode( - array( - 'status' => 'dupe' - ) - ); - die(); + print + json_encode( + array( + 'status' => 'dupe' + ) + ); + die(); } else { - $UserID = $LoggedUser['ID']; - $DB->query(" - UPDATE reports - SET ClaimerID = '$UserID' - WHERE ID = '$ID'"); - print - json_encode( - array( - 'status' => 'success', - 'username' => $LoggedUser['Username'] - ) - ); - die(); + $UserID = $LoggedUser['ID']; + $DB->query(" + UPDATE reports + SET ClaimerID = '$UserID' + WHERE ID = '$ID'"); + print + json_encode( + array( + 'status' => 'success', + 'username' => $LoggedUser['Username'] + ) + ); + die(); } diff --git a/sections/reports/ajax_resolve_report.php b/sections/reports/ajax_resolve_report.php index 790557f50..98dbf89e5 100644 --- a/sections/reports/ajax_resolve_report.php +++ b/sections/reports/ajax_resolve_report.php @@ -1,52 +1,52 @@ -query(" - SELECT Type - FROM reports - WHERE ID = $ReportID"); + SELECT Type + FROM reports + WHERE ID = $ReportID"); list($Type) = $DB->next_record(); if (!check_perms('admin_reports')) { - if (check_perms('site_moderate_forums')) { - if (!in_array($Type, array('comment', 'post', 'thread'))) { - ajax_error(); - } - } + if (check_perms('site_moderate_forums')) { + if (!in_array($Type, array('comment', 'post', 'thread'))) { + ajax_error(); + } + } } $DB->query(" - UPDATE reports - SET Status = 'Resolved', - ResolvedTime = '".sqltime()."', - ResolverID = '".$LoggedUser['ID']."' - WHERE ID = '".db_string($ReportID)."'"); + UPDATE reports + SET Status = 'Resolved', + ResolvedTime = '".sqltime()."', + ResolverID = '".$LoggedUser['ID']."' + WHERE ID = '".db_string($ReportID)."'"); -$Channels = array(); +$Channels = []; if ($Type == 'request_update') { - $Channels[] = '#requestedits'; - $Cache->decrement('num_update_reports'); + $Channels[] = '#requestedits'; + $Cache->decrement('num_update_reports'); } if (in_array($Type, array('comment', 'post', 'thread'))) { - $Channels[] = '#forumreports'; - $Cache->decrement('num_forum_reports'); + $Channels[] = '#forumreports'; + $Cache->decrement('num_forum_reports'); } $DB->query(" - SELECT COUNT(ID) - FROM reports - WHERE Status = 'New'"); + SELECT COUNT(ID) + FROM reports + WHERE Status = 'New'"); list($Remaining) = $DB->next_record(); foreach ($Channels as $Channel) { - send_irc("PRIVMSG $Channel :Report $ReportID resolved by ".preg_replace('/^(.{2})/', '$1·', $LoggedUser['Username']).' on site ('.(int)$Remaining.' remaining).'); + send_irc("PRIVMSG $Channel :Report $ReportID resolved by ".preg_replace('/^(.{2})/', '$1·', $LoggedUser['Username']).' on site ('.(int)$Remaining.' remaining).'); } $Cache->delete_value('num_other_reports'); @@ -54,12 +54,12 @@ ajax_success(); function ajax_error($Error = 'error') { - echo json_encode(array('status' => $Error)); - die(); + echo json_encode(array('status' => $Error)); + die(); } function ajax_success() { - echo json_encode(array('status' => 'success')); - die(); + echo json_encode(array('status' => 'success')); + die(); } ?> diff --git a/sections/reports/ajax_unclaim_report.php b/sections/reports/ajax_unclaim_report.php index 5a2408c4b..c6a8fdc85 100644 --- a/sections/reports/ajax_unclaim_report.php +++ b/sections/reports/ajax_unclaim_report.php @@ -1,20 +1,20 @@ 'failure' - ) - ); - die(); + print + json_encode( + array( + 'status' => 'failure' + ) + ); + die(); } $ID = (int)$_POST['id']; $DB->query("UPDATE reports SET ClaimerID = '0' WHERE ID = '$ID'"); print - json_encode( - array( - 'status' => 'success', - ) - ); + json_encode( + array( + 'status' => 'success', + ) + ); die(); diff --git a/sections/reports/array.php b/sections/reports/array.php index 23c6c77d8..2f0e607d1 100644 --- a/sections/reports/array.php +++ b/sections/reports/array.php @@ -1,83 +1,83 @@ - array( - "title" => "User", - "guidelines" => array( - "The Report User option is for reporting a user who has broken one of the golden rules outlined here, or if you need to alert staff to something specific about a user that cannot be reported elsewhere.", - "We encourage all users to use this feature whenever possible. This will get quicker action than PMing a staff member will.", - "Please do not report a user in this section for breaking rules such as: -
      -
    • Torrent uploads
    • -
    • Forum threads/posts
    • -
    • Collages
    • -
    • Requests
    • -
    ", - "These have their own specific section for reporting, found on their respective pages.", - "In your report description below, please be specific and include as much information as possible that will help our staff resolve the issue." - ) - ), - "request_update" => array( - "title" => "Request (Update)", - "guidelines" => array( - "This option is for asking the moderators to update your request to the new system.", - "If your request has no other votes, you can just edit it yourself!", - "If possible, please include a Discogs or MusicBrainz link in the comments field." - ) - ), - "request" => array( - "title" => "Request", - "guidelines" => array( - "The report request option is for reporting a request which breaks any of the rules found here and for making minor, generally cosmetic, changes to a request (e.g. adding album art, revising a to-be-announced release title, etc.).", - "We encourage all users to use this feature whenever possible. This will get quicker action than PMing a staff member will.", - "In your report description below, please be specific and include as much information as possible that will help our staff resolve the issue. Links to reliable, external sources of information are extremely useful when resolving reports. Examples of such sources include the artist's official web site, Discogs, and MusicBrainz.", - "Do not report requests simply because they are unfillable. Requests for currently unfillable releases are allowed because the request may become fillable in the future. An example of such a scenario would be a request for a physical media rip of a currently iTunes-only release because a physical media release could occur at some future date. The probability of such a physical release is not relevant.", - "If you are reporting this request to get it updated to the new requests system, please go back and click '[Request update]'." - ) - ), - "thread" => array( - "title" => "Forum Thread", - "guidelines" => array( - "Please use the "Report thread" option in the following situations: -
      -
    • Reporting when chat rules have been broken, such as posts containing racism, offensive language, flaming, pornography, and other rules violations. We encourage all users to use this feature when they see a rules violation of any form.
    • -
    • Requesting that a thread be unlocked.
    • -
    • Reporting threads that are in the wrong forum.
    • -
    • Reporting answered questions in the Help forum.
    • -
    ", - "This will get quicker action than PMing a staff member will.", - "Please restrict the use of this feature to reporting rules violations, and remember, this is for reporting threads, not replying to them." - ) - ), - "post" => array( - "title" => "Forum Post", - "guidelines" => array( - "The report comment option is specifically for reporting when the chat rules have been broken, such as posts containing racism, offensive language, flaming, pornography, and other rules violations.", - "We encourage all users to use this feature when they see a rules violation of any form.", - "This will get quicker action than PMing a staff member will.", - "Please restrict the use of this feature to reporting rules violations, and remember, this is for reporting comments, not replying to them." - ) - ), - "collage" => array( - "title" => "Collage", - "guidelines" => array( - "The report collage option is for reporting a collage which breaks one of the collage guidelines found on the collage rules page.", - "Collage rules are an interpreted concept, and it is up to staff to interpret these rules. If you feel like this collage might be a borderline case between allowed and not allowed, feel free to report it, and we will look into it.", - "We encourage all users to use this feature whenever possible. This will get quicker action than PMing a staff member will.", - "In your report description below, please be specific and include as much information as possible that will help our staff resolve the issue." - ) - ), - "comment" => array( - "title" => "Comment", - "guidelines" => array( - "The report comment option is specifically for reporting when the chat rules have been broken, such as posts containing racism, offensive language, flaming, pornography, and other rules violations.", - "We encourage all users to use this feature when they see a rules violation of any form.", - "This will get quicker action than PMing a staff member will.", - "Please restrict the use of this feature to reporting rules violations, and remember, this is for reporting comments, not replying to them." - ) - ) - ); + "user" => array( + "title" => "User", + "guidelines" => array( + "The Report User option is for reporting a user who has broken one of the golden rules outlined here, or if you need to alert staff to something specific about a user that cannot be reported elsewhere.", + "We encourage all users to use this feature whenever possible. This will get quicker action than PMing a staff member will.", + "Please do not report a user in this section for breaking rules such as: +
      +
    • Torrent uploads
    • +
    • Forum threads/posts
    • +
    • Collages
    • +
    • Requests
    • +
    ", + "These have their own specific section for reporting, found on their respective pages.", + "In your report description below, please be specific and include as much information as possible that will help our staff resolve the issue." + ) + ), + "request_update" => array( + "title" => "Request (Update)", + "guidelines" => array( + "This option is for asking the moderators to update your request to the new system.", + "If your request has no other votes, you can just edit it yourself!", + "If possible, please include a Discogs or MusicBrainz link in the comments field." + ) + ), + "request" => array( + "title" => "Request", + "guidelines" => array( + "The report request option is for reporting a request which breaks any of the rules found here and for making minor, generally cosmetic, changes to a request (e.g. adding album art, revising a to-be-announced release title, etc.).", + "We encourage all users to use this feature whenever possible. This will get quicker action than PMing a staff member will.", + "In your report description below, please be specific and include as much information as possible that will help our staff resolve the issue. Links to reliable, external sources of information are extremely useful when resolving reports. Examples of such sources include the artist's official web site, Discogs, and MusicBrainz.", + "Do not report requests simply because they are unfillable. Requests for currently unfillable releases are allowed because the request may become fillable in the future. An example of such a scenario would be a request for a physical media rip of a currently iTunes-only release because a physical media release could occur at some future date. The probability of such a physical release is not relevant.", + "If you are reporting this request to get it updated to the new requests system, please go back and click '[Request update]'." + ) + ), + "thread" => array( + "title" => "Forum Thread", + "guidelines" => array( + "Please use the "Report thread" option in the following situations: +
      +
    • Reporting when chat rules have been broken, such as posts containing racism, offensive language, flaming, pornography, and other rules violations. We encourage all users to use this feature when they see a rules violation of any form.
    • +
    • Requesting that a thread be unlocked.
    • +
    • Reporting threads that are in the wrong forum.
    • +
    • Reporting answered questions in the Help forum.
    • +
    ", + "This will get quicker action than PMing a staff member will.", + "Please restrict the use of this feature to reporting rules violations, and remember, this is for reporting threads, not replying to them." + ) + ), + "post" => array( + "title" => "Forum Post", + "guidelines" => array( + "The report comment option is specifically for reporting when the chat rules have been broken, such as posts containing racism, offensive language, flaming, pornography, and other rules violations.", + "We encourage all users to use this feature when they see a rules violation of any form.", + "This will get quicker action than PMing a staff member will.", + "Please restrict the use of this feature to reporting rules violations, and remember, this is for reporting comments, not replying to them." + ) + ), + "collage" => array( + "title" => "Collage", + "guidelines" => array( + "The report collage option is for reporting a collage which breaks one of the collage guidelines found on the collage rules page.", + "Collage rules are an interpreted concept, and it is up to staff to interpret these rules. If you feel like this collage might be a borderline case between allowed and not allowed, feel free to report it, and we will look into it.", + "We encourage all users to use this feature whenever possible. This will get quicker action than PMing a staff member will.", + "In your report description below, please be specific and include as much information as possible that will help our staff resolve the issue." + ) + ), + "comment" => array( + "title" => "Comment", + "guidelines" => array( + "The report comment option is specifically for reporting when the chat rules have been broken, such as posts containing racism, offensive language, flaming, pornography, and other rules violations.", + "We encourage all users to use this feature when they see a rules violation of any form.", + "This will get quicker action than PMing a staff member will.", + "Please restrict the use of this feature to reporting rules violations, and remember, this is for reporting comments, not replying to them." + ) + ) + ); ?> diff --git a/sections/reports/compose.php b/sections/reports/compose.php index 5d79a65b7..496274b7d 100644 --- a/sections/reports/compose.php +++ b/sections/reports/compose.php @@ -1,18 +1,18 @@ -query(" - SELECT Username - FROM users_main - WHERE ID = '$ToID'"); + SELECT Username + FROM users_main + WHERE ID = '$ToID'"); list($ComposeToUsername) = $DB->next_record(); if (!$ComposeToUsername) { - error(404); + error(404); } View::show_header('Compose', 'inbox,bbcode'); // $TypeLink is placed directly in the -
    - -
    - - -
    -
    - +
    +

    + Send a message to +

    +
    +
    +
    + + + +
    +

    Subject

    + +
    +

    Body

    + +
    + +
    + + +
    +
    +
    - diff --git a/sections/reports/index.php b/sections/reports/index.php index 86bc3b959..94d06723e 100644 --- a/sections/reports/index.php +++ b/sections/reports/index.php @@ -1,43 +1,43 @@ - diff --git a/sections/reports/report.php b/sections/reports/report.php index 609442d82..87891f993 100644 --- a/sections/reports/report.php +++ b/sections/reports/report.php @@ -1,13 +1,13 @@ -query(" - SELECT Username - FROM users_main - WHERE ID = $ID"); - if (!$DB->has_results()) { - error(404); - } - list($Username) = $DB->next_record(); - break; + case 'user': + $DB->query(" + SELECT Username + FROM users_main + WHERE ID = $ID"); + if (!$DB->has_results()) { + error(404); + } + list($Username) = $DB->next_record(); + break; - case 'request_update': - $NoReason = true; - $DB->query(" - SELECT Title, Description, TorrentID, CategoryID, Year - FROM requests - WHERE ID = $ID"); - if (!$DB->has_results()) { - error(404); - } - list($Name, $Desc, $Filled, $CategoryID, $Year) = $DB->next_record(); - if ($Filled || ($CategoryID != 0 && ($Categories[$CategoryID - 1] != 'Music' || $Year != 0))) { - error(403); - } - break; + case 'request_update': + $NoReason = true; + $DB->query(" + SELECT Title, Description, TorrentID, CategoryID, Year + FROM requests + WHERE ID = $ID"); + if (!$DB->has_results()) { + error(404); + } + list($Name, $Desc, $Filled, $CategoryID, $Year) = $DB->next_record(); + if ($Filled || ($CategoryID != 0 && ($Categories[$CategoryID - 1] != 'Music' || $Year != 0))) { + error(403); + } + break; - case 'request': - $DB->query(" - SELECT Title, Description, TorrentID - FROM requests - WHERE ID = $ID"); - if (!$DB->has_results()) { - error(404); - } - list($Name, $Desc, $Filled) = $DB->next_record(); - break; + case 'request': + $DB->query(" + SELECT Title, Description, TorrentID + FROM requests + WHERE ID = $ID"); + if (!$DB->has_results()) { + error(404); + } + list($Name, $Desc, $Filled) = $DB->next_record(); + break; - case 'collage': - $DB->query(" - SELECT Name, Description - FROM collages - WHERE ID = $ID"); - if (!$DB->has_results()) { - error(404); - } - list($Name, $Desc) = $DB->next_record(); - break; + case 'collage': + $DB->query(" + SELECT Name, Description + FROM collages + WHERE ID = $ID"); + if (!$DB->has_results()) { + error(404); + } + list($Name, $Desc) = $DB->next_record(); + break; - case 'thread': - $DB->query(" - SELECT ft.Title, ft.ForumID, um.Username - FROM forums_topics AS ft - JOIN users_main AS um ON um.ID = ft.AuthorID - WHERE ft.ID = $ID"); - if (!$DB->has_results()) { - error(404); - } - list($Title, $ForumID, $Username) = $DB->next_record(); - $DB->query(" - SELECT MinClassRead - FROM forums - WHERE ID = $ForumID"); - list($MinClassRead) = $DB->next_record(); - if (!empty($LoggedUser['DisableForums']) - || ($MinClassRead > $LoggedUser['EffectiveClass'] && (!isset($LoggedUser['CustomForums'][$ForumID]) || $LoggedUser['CustomForums'][$ForumID] == 0)) - || (isset($LoggedUser['CustomForums'][$ForumID]) && $LoggedUser['CustomForums'][$ForumID] == 0)) { - error(403); - } - break; + case 'thread': + $DB->query(" + SELECT ft.Title, ft.ForumID, um.Username + FROM forums_topics AS ft + JOIN users_main AS um ON um.ID = ft.AuthorID + WHERE ft.ID = $ID"); + if (!$DB->has_results()) { + error(404); + } + list($Title, $ForumID, $Username) = $DB->next_record(); + $DB->query(" + SELECT MinClassRead + FROM forums + WHERE ID = $ForumID"); + list($MinClassRead) = $DB->next_record(); + if (!empty($LoggedUser['DisableForums']) + || ($MinClassRead > $LoggedUser['EffectiveClass'] && (!isset($LoggedUser['CustomForums'][$ForumID]) || $LoggedUser['CustomForums'][$ForumID] == 0)) + || (isset($LoggedUser['CustomForums'][$ForumID]) && $LoggedUser['CustomForums'][$ForumID] == 0)) { + error(403); + } + break; - case 'post': - $DB->query(" - SELECT fp.Body, fp.TopicID, um.Username - FROM forums_posts AS fp - JOIN users_main AS um ON um.ID = fp.AuthorID - WHERE fp.ID = $ID"); - if (!$DB->has_results()) { - error(404); - } - list($Body, $TopicID, $Username) = $DB->next_record(); - $DB->query(" - SELECT ForumID - FROM forums_topics - WHERE ID = $TopicID"); - list($ForumID) = $DB->next_record(); - $DB->query(" - SELECT MinClassRead - FROM forums - WHERE ID = $ForumID"); - list($MinClassRead) = $DB->next_record(); - if (!empty($LoggedUser['DisableForums']) - || ($MinClassRead > $LoggedUser['EffectiveClass'] && (!isset($LoggedUser['CustomForums'][$ForumID]) || $LoggedUser['CustomForums'][$ForumID] == 0)) - || (isset($LoggedUser['CustomForums'][$ForumID]) && $LoggedUser['CustomForums'][$ForumID] == 0)) { - error(403); - } - break; + case 'post': + $DB->query(" + SELECT fp.Body, fp.TopicID, um.Username + FROM forums_posts AS fp + JOIN users_main AS um ON um.ID = fp.AuthorID + WHERE fp.ID = $ID"); + if (!$DB->has_results()) { + error(404); + } + list($Body, $TopicID, $Username) = $DB->next_record(); + $DB->query(" + SELECT ForumID + FROM forums_topics + WHERE ID = $TopicID"); + list($ForumID) = $DB->next_record(); + $DB->query(" + SELECT MinClassRead + FROM forums + WHERE ID = $ForumID"); + list($MinClassRead) = $DB->next_record(); + if (!empty($LoggedUser['DisableForums']) + || ($MinClassRead > $LoggedUser['EffectiveClass'] && (!isset($LoggedUser['CustomForums'][$ForumID]) || $LoggedUser['CustomForums'][$ForumID] == 0)) + || (isset($LoggedUser['CustomForums'][$ForumID]) && $LoggedUser['CustomForums'][$ForumID] == 0)) { + error(403); + } + break; - case 'comment': - $DB->query(" - SELECT c.Body, um.Username - FROM comments AS c - JOIN users_main AS um ON um.ID = c.AuthorID - WHERE c.ID = $ID"); - if (!$DB->has_results()) { - error(404); - } - list($Body, $Username) = $DB->next_record(); - break; + case 'comment': + $DB->query(" + SELECT c.Body, um.Username + FROM comments AS c + JOIN users_main AS um ON um.ID = c.AuthorID + WHERE c.ID = $ID"); + if (!$DB->has_results()) { + error(404); + } + list($Body, $Username) = $DB->next_record(); + break; } View::show_header('Report a '.$Type['title'], 'bbcode,jquery.validate,form_validate'); ?>
    -
    -

    Report

    -
    -

    Reporting guidelines

    -
    -

    Following these guidelines will help the moderators deal with your report in a timely fashion.

    -
      - -
    • - -
    -

    In short, please include as much detail as possible when reporting. Thank you.

    -
    - +

    Report

    +
    +

    Reporting guidelines

    +
    +

    Following these guidelines will help the moderators deal with your report in a timely fashion.

    +
      + +
    • + +
    +

    In short, please include as much detail as possible when reporting. Thank you.

    +
    + -

    You are reporting the user

    -You are reporting the user

    + -

    You are reporting the request:

    - - - - - - - - - - - -
    TitleDescriptionFilled?
    -
    +

    You are reporting the request:

    + + + + + + + + + + + +
    TitleDescriptionFilled?
    +
    -
    -

    It will greatly increase the turnover rate of the updates if you can fill in as much of the following details as possible.

    -
    - - - - - - - - - - - - - - - - - -
    Year (required) - -
    Release type - -
    Comment - -
    -
    -
    - -
    -
    - +

    It will greatly increase the turnover rate of the updates if you can fill in as much of the following details as possible.

    +
    + + + + + + + + + + + + + + + + + +
    Year (required) + +
    Release type + +
    Comment + +
    +
    +
    + +
    + + -

    You are reporting the request:

    - - - - - - - - - - - -
    TitleDescriptionFilled?
    -You are reporting the request:

    + + + + + + + + + + + +
    TitleDescriptionFilled?
    + -

    You are reporting the collage:

    - - - - - - - - - -
    TitleDescription
    -You are reporting the collage:

    + + + + + + + + + +
    TitleDescription
    + -

    You are reporting the thread:

    - - - - - - - - - -
    UsernameTitle
    -You are reporting the thread:

    + + + + + + + + + +
    UsernameTitle
    + -

    You are reporting the post:

    - - - - - - - - - -
    UsernameBody
    -You are reporting the post:

    + + + + + + + + + +
    UsernameBody
    + -

    You are reporting the :

    - - - - - - - - - -
    UsernameBody
    -You are reporting the :

    + + + + + + + + + +
    UsernameBody
    + -

    Reason

    -
    -
    - - - - -

    - -
    -
    - */ ?> +

    Reason

    +
    +
    + + + + +

    + +
    +
    + - diff --git a/sections/reports/reports.php b/sections/reports/reports.php index 4d750b632..690833c6e 100644 --- a/sections/reports/reports.php +++ b/sections/reports/reports.php @@ -1,8 +1,8 @@ -query(" - SELECT - SQL_CALC_FOUND_ROWS - r.ID, - r.UserID, - um.Username, - r.ThingID, - r.Type, - r.ReportedTime, - r.Reason, - r.Status, - r.ClaimerID, - r.Notes, - r.ResolverID - FROM reports AS r - JOIN users_main AS um ON r.UserID = um.ID - WHERE $Where - ORDER BY ReportedTime DESC - LIMIT $Limit"); + SELECT + SQL_CALC_FOUND_ROWS + r.ID, + r.UserID, + um.Username, + r.ThingID, + r.Type, + r.ReportedTime, + r.Reason, + r.Status, + r.ClaimerID, + r.Notes, + r.ResolverID + FROM reports AS r + JOIN users_main AS um ON r.UserID = um.ID + WHERE $Where + ORDER BY ReportedTime DESC + LIMIT $Limit"); // Number of results (for pagination) $DB->query('SELECT FOUND_ROWS()'); @@ -69,184 +69,184 @@ // Start printing stuff ?>
    -
    -

    Active Reports

    - -
    - + -next_record()) { - $Type = $Types[$Short]; - $Reference = "reports.php?id=$ReportID#report$ReportID"; + $Pages = Format::get_pages($Page, $Results, REPORTS_PER_PAGE, 11); + echo $Pages; + ?> +
    +next_record()) { + $Type = $Types[$Short]; + $Reference = "reports.php?id=$ReportID#report$ReportID"; ?> -
    - - - - - - - - - - - - - + + + +
    Report # - was reported by - Contact -
    - -query(" - SELECT Username - FROM users_main - WHERE ID = $ThingID"); - if (!$DB->has_results()) { - echo 'No user with the reported ID found'; - } else { - list($Username) = $DB->next_record(); - echo "" . display_str($Username) . ''; - } - break; - case 'request': - case 'request_update': - $DB->query(" - SELECT Title - FROM requests - WHERE ID = $ThingID"); - if (!$DB->has_results()) { - echo 'No request with the reported ID found'; - } else { - list($Name) = $DB->next_record(); - echo "" . display_str($Name) . ''; - } - break; - case 'collage': - $DB->query(" - SELECT Name - FROM collages - WHERE ID = $ThingID"); - if (!$DB->has_results()) { - echo 'No collage with the reported ID found'; - } else { - list($Name) = $DB->next_record(); - echo "" . display_str($Name) . ''; - } - break; - case 'thread': - $DB->query(" - SELECT Title - FROM forums_topics - WHERE ID = $ThingID"); - if (!$DB->has_results()) { - echo 'No forum thread with the reported ID found'; - } else { - list($Title) = $DB->next_record(); - echo "" . display_str($Title) . ''; - } - break; - case 'post': - if (isset($LoggedUser['PostsPerPage'])) { - $PerPage = $LoggedUser['PostsPerPage']; - } else { - $PerPage = POSTS_PER_PAGE; - } - $DB->query(" - SELECT - p.ID, - p.Body, - p.TopicID, - ( - SELECT COUNT(p2.ID) - FROM forums_posts AS p2 - WHERE p2.TopicID = p.TopicID - AND p2.ID <= p.ID - ) AS PostNum - FROM forums_posts AS p - WHERE p.ID = $ThingID"); - if (!$DB->has_results()) { - echo 'No forum post with the reported ID found'; - } else { - list($PostID, $Body, $TopicID, $PostNum) = $DB->next_record(); - echo "FORUM POST ID #$PostID"; - } - break; - case 'comment': - $DB->query(" - SELECT 1 - FROM comments - WHERE ID = $ThingID"); - if (!$DB->has_results()) { - echo 'No comment with the reported ID found'; - } else { - echo "COMMENT"; - } - break; - } - ?> - -
    - - Claimed by Unclaim - - Claimed by - - Claim - -    - Toggle notes +
    + + + + + + + + + + + + + - - - - - -" style="display: ;"> + +
    + + + + + + + + + - - - - -
    Report # + was reported by + Contact +
    + +query(" + SELECT Username + FROM users_main + WHERE ID = $ThingID"); + if (!$DB->has_results()) { + echo 'No user with the reported ID found'; + } else { + list($Username) = $DB->next_record(); + echo "" . display_str($Username) . ''; + } + break; + case 'request': + case 'request_update': + $DB->query(" + SELECT Title + FROM requests + WHERE ID = $ThingID"); + if (!$DB->has_results()) { + echo 'No request with the reported ID found'; + } else { + list($Name) = $DB->next_record(); + echo "" . display_str($Name) . ''; + } + break; + case 'collage': + $DB->query(" + SELECT Name + FROM collages + WHERE ID = $ThingID"); + if (!$DB->has_results()) { + echo 'No collage with the reported ID found'; + } else { + list($Name) = $DB->next_record(); + echo "" . display_str($Name) . ''; + } + break; + case 'thread': + $DB->query(" + SELECT Title + FROM forums_topics + WHERE ID = $ThingID"); + if (!$DB->has_results()) { + echo 'No forum thread with the reported ID found'; + } else { + list($Title) = $DB->next_record(); + echo "" . display_str($Title) . ''; + } + break; + case 'post': + if (isset($LoggedUser['PostsPerPage'])) { + $PerPage = $LoggedUser['PostsPerPage']; + } else { + $PerPage = POSTS_PER_PAGE; + } + $DB->query(" + SELECT + p.ID, + p.Body, + p.TopicID, + ( + SELECT COUNT(p2.ID) + FROM forums_posts AS p2 + WHERE p2.TopicID = p.TopicID + AND p2.ID <= p.ID + ) AS PostNum + FROM forums_posts AS p + WHERE p.ID = $ThingID"); + if (!$DB->has_results()) { + echo 'No forum post with the reported ID found'; + } else { + list($PostID, $Body, $TopicID, $PostNum) = $DB->next_record(); + echo "FORUM POST ID #$PostID"; + } + break; + case 'comment': + $DB->query(" + SELECT 1 + FROM comments + WHERE ID = $ThingID"); + if (!$DB->has_results()) { + echo 'No comment with the reported ID found'; + } else { + echo "COMMENT"; + } + break; + } + ?> + +
    + + Claimed by Unclaim + + Claimed by + + Claim + +    + Toggle notes -
    - -
    - -
    -
    -
    - - - -
    -
    +
    + + + +
    +
    - Resolved by -
    -
    -set_query_id($Reports); - } - ?> - +
    + Resolved by +
    +
    +set_query_id($Reports); + } + ?> + - diff --git a/sections/reports/stats.php b/sections/reports/stats.php index 8c95cc319..84bb7c3ae 100644 --- a/sections/reports/stats.php +++ b/sections/reports/stats.php @@ -1,200 +1,204 @@ -
    -

    Other reports stats!

    - +

    Other reports stats!

    +
    -
    - +query(" - SELECT um.Username, - COUNT(r.ID) AS Reports - FROM reports AS r - JOIN users_main AS um ON um.ID = r.ResolverID - WHERE r.ReportedTime > '2009-08-21 22:39:41' - AND r.ReportedTime > NOW() - INTERVAL 24 HOUR - GROUP BY r.ResolverID - ORDER BY Reports DESC"); + SELECT um.Username, + COUNT(r.ID) AS Reports + FROM reports AS r + JOIN users_main AS um ON um.ID = r.ResolverID + WHERE r.ReportedTime > '2009-08-21 22:39:41' + AND r.ReportedTime > NOW() - INTERVAL 24 HOUR + GROUP BY r.ResolverID + ORDER BY Reports DESC"); $Results = $DB->to_array(); ?> -

    Reports resolved in the last 24 hours

    - - - - - -Reports resolved in the last 24 hours +
    UsernameReports
    + + + + + - > - - - - -
    UsernameReports
    -> + + + + + +query(" - SELECT um.Username, - COUNT(r.ID) AS Reports - FROM reports AS r - JOIN users_main AS um ON um.ID = r.ResolverID - WHERE r.ReportedTime > '2009-08-21 22:39:41' - AND r.ReportedTime > NOW() - INTERVAL 1 WEEK - GROUP BY r.ResolverID - ORDER BY Reports DESC"); + SELECT um.Username, + COUNT(r.ID) AS Reports + FROM reports AS r + JOIN users_main AS um ON um.ID = r.ResolverID + WHERE r.ReportedTime > '2009-08-21 22:39:41' + AND r.ReportedTime > NOW() - INTERVAL 1 WEEK + GROUP BY r.ResolverID + ORDER BY Reports DESC"); $Results = $DB->to_array(); ?> -

    Reports resolved in the last week

    - - - - - -Reports resolved in the last week +
    UsernameReports
    + + + + + - > - - - - -
    UsernameReports
    -> + + + + + +query(" - SELECT um.Username, - COUNT(r.ID) AS Reports - FROM reports AS r - JOIN users_main AS um ON um.ID = r.ResolverID - WHERE r.ReportedTime > '2009-08-21 22:39:41' - AND r.ReportedTime > NOW() - INTERVAL 1 MONTH - GROUP BY r.ResolverID - ORDER BY Reports DESC"); + SELECT um.Username, + COUNT(r.ID) AS Reports + FROM reports AS r + JOIN users_main AS um ON um.ID = r.ResolverID + WHERE r.ReportedTime > '2009-08-21 22:39:41' + AND r.ReportedTime > NOW() - INTERVAL 1 MONTH + GROUP BY r.ResolverID + ORDER BY Reports DESC"); $Results = $DB->to_array(); ?> -

    Reports resolved in the last month

    - - - - - -Reports resolved in the last month +
    UsernameReports
    + + + + + - > - - - - -
    UsernameReports
    -> + + + + + +query(" - SELECT um.Username, - COUNT(r.ID) AS Reports - FROM reports AS r - JOIN users_main AS um ON um.ID = r.ResolverID - GROUP BY r.ResolverID - ORDER BY Reports DESC"); + SELECT um.Username, + COUNT(r.ID) AS Reports + FROM reports AS r + JOIN users_main AS um ON um.ID = r.ResolverID + GROUP BY r.ResolverID + ORDER BY Reports DESC"); $Results = $DB->to_array(); ?> -

    Reports resolved since "other" reports (2009-08-21)

    - - - - - -Reports resolved since "other" reports (2009-08-21) +
    UsernameReports
    + + + + + - > - - - - -
    UsernameReports
    -> + + + + + + -
    -
    - +
    +query(" - SELECT u.Username, - COUNT(f.LastPostAuthorID) as Trashed - FROM forums_topics AS f - LEFT JOIN users_main AS u ON u.ID = f.LastPostAuthorID - WHERE f.ForumID IN ($TrashForumIDs) - GROUP BY f.LastPostAuthorID - ORDER BY Trashed DESC - LIMIT 30"); - $Results = $DB->to_array(); + $DB->query(" + SELECT u.Username, + COUNT(f.LastPostAuthorID) as Trashed + FROM forums_topics AS f + LEFT JOIN users_main AS u ON u.ID = f.LastPostAuthorID + WHERE f.ForumID IN ($TrashForumIDs) + GROUP BY f.LastPostAuthorID + ORDER BY Trashed DESC + LIMIT 30"); + $Results = $DB->to_array(); ?> -

    Threads trashed since the beginning of time

    - - - - - - -Threads trashed since the beginning of time +
    PlaceUsernameTrashed
    + + + + + + - > - - - - -> + + + + + -
    PlaceUsernameTrashed
    -
    + +
    - diff --git a/sections/reports/takecompose.php b/sections/reports/takecompose.php index 3e72a1e62..2ee9c3c04 100644 --- a/sections/reports/takecompose.php +++ b/sections/reports/takecompose.php @@ -1,57 +1,57 @@ -query(" - SELECT UserID - FROM pm_conversations_users - WHERE UserID = '$LoggedUser[ID]' - AND ConvID = '$ConvID'"); - if (!$DB->has_results()) { - error(403); - } + $ConvID = $_POST['convid']; + $Subject = ''; + $ToID = explode(',', $_POST['toid']); + foreach ($ToID as $TID) { + if (!is_number($TID)) { + $Err = 'A recipient does not exist.'; + } + } + $DB->query(" + SELECT UserID + FROM pm_conversations_users + WHERE UserID = '$LoggedUser[ID]' + AND ConvID = '$ConvID'"); + if (!$DB->has_results()) { + error(403); + } } else { - $ConvID = ''; - if (!is_number($_POST['toid'])) { - $Err = 'This recipient does not exist.'; - } else { - $ToID = $_POST['toid']; - } - $Subject = trim($_POST['subject']); - if (empty($Subject)) { - $Err = "You can't send a message without a subject."; - } + $ConvID = ''; + if (!is_number($_POST['toid'])) { + $Err = 'This recipient does not exist.'; + } else { + $ToID = $_POST['toid']; + } + $Subject = trim($_POST['subject']); + if (empty($Subject)) { + $Err = "You can't send a message without a subject."; + } } $Body = trim($_POST['body']); if ($Body === '' || $Body === false) { - $Err = "You can't send a message without a body!"; + $Err = "You can't send a message without a body!"; } if (!empty($Err)) { - error($Err); - //header('Location: inbox.php?action=compose&to='.$_POST['toid']); - $ToID = $_POST['toid']; - $Return = true; - include(SERVER_ROOT.'/sections/inbox/compose.php'); - die(); + error($Err); + //header('Location: inbox.php?action=compose&to='.$_POST['toid']); + $ToID = $_POST['toid']; + $Return = true; + include(SERVER_ROOT.'/sections/inbox/compose.php'); + die(); } $ConvID = Misc::send_pm($ToID, $LoggedUser['ID'], $Subject, $Body, $ConvID); diff --git a/sections/reports/takereport.php b/sections/reports/takereport.php index 2f3b25dc5..522f50db8 100644 --- a/sections/reports/takereport.php +++ b/sections/reports/takereport.php @@ -1,88 +1,88 @@ -query(" - SELECT - p.ID, - p.TopicID, - ( - SELECT COUNT(p2.ID) - FROM forums_posts AS p2 - WHERE p2.TopicID = p.TopicID - AND p2.ID <= p.ID - ) AS PostNum - FROM forums_posts AS p - WHERE p.ID = $ID"); - list($PostID, $TopicID, $PostNum) = $DB->next_record(); - $Link = "forums.php?action=viewthread&threadid=$TopicID&post=$PostNum#post$PostID"; - break; - case 'comment': - $Link = "comments.php?action=jump&postid=$ID"; - break; + case 'request': + case 'request_update': + $Link = "requests.php?action=view&id=$ID"; + break; + case 'user': + $Link = "user.php?id=$ID"; + break; + case 'collage': + $Link = "collages.php?id=$ID"; + break; + case 'thread': + $Link = "forums.php?action=viewthread&threadid=$ID"; + break; + case 'post': + $DB->query(" + SELECT + p.ID, + p.TopicID, + ( + SELECT COUNT(p2.ID) + FROM forums_posts AS p2 + WHERE p2.TopicID = p.TopicID + AND p2.ID <= p.ID + ) AS PostNum + FROM forums_posts AS p + WHERE p.ID = $ID"); + list($PostID, $TopicID, $PostNum) = $DB->next_record(); + $Link = "forums.php?action=viewthread&threadid=$TopicID&post=$PostNum#post$PostID"; + break; + case 'comment': + $Link = "comments.php?action=jump&postid=$ID"; + break; } $DB->query(' - INSERT INTO reports - (UserID, ThingID, Type, ReportedTime, Reason) - VALUES - ('.db_string($LoggedUser['ID']).", $ID, '$Short', '".sqltime()."', '".db_string($Reason)."')"); + INSERT INTO reports + (UserID, ThingID, Type, ReportedTime, Reason) + VALUES + ('.db_string($LoggedUser['ID']).", $ID, '$Short', '".sqltime()."', '".db_string($Reason)."')"); $ReportID = $DB->inserted_id(); -$Channels = array(); +$Channels = []; if ($Short === 'request_update') { - $Channels[] = '#requestedits'; - $Cache->increment('num_update_reports'); + $Channels[] = '#requestedits'; + $Cache->increment('num_update_reports'); } if (in_array($Short, array('comment', 'post', 'thread'))) { - $Channels[] = '#forumreports'; + $Channels[] = '#forumreports'; } foreach ($Channels as $Channel) { - send_irc("PRIVMSG $Channel :$ReportID - ".$LoggedUser['Username']." just reported a $Short: ".site_url()."$Link : ".strtr($Reason, "\n", ' ')); + send_irc("PRIVMSG $Channel :$ReportID - ".$LoggedUser['Username']." just reported a $Short: ".site_url()."$Link : ".strtr($Reason, "\n", ' ')); } $Cache->delete_value('num_other_reports'); diff --git a/sections/reports/takeresolve.php b/sections/reports/takeresolve.php index e9114bee3..9ceb3fab4 100644 --- a/sections/reports/takeresolve.php +++ b/sections/reports/takeresolve.php @@ -1,52 +1,52 @@ -query(" - SELECT Type - FROM reports - WHERE ID = $ReportID"); + SELECT Type + FROM reports + WHERE ID = $ReportID"); list($Type) = $DB->next_record(); if (!check_perms('admin_reports')) { - if (check_perms('site_moderate_forums')) { - if (!in_array($Type, array('comment', 'post', 'thread'))) { - error($Type); - } - } + if (check_perms('site_moderate_forums')) { + if (!in_array($Type, array('comment', 'post', 'thread'))) { + error($Type); + } + } } $DB->query(" - UPDATE reports - SET Status = 'Resolved', - ResolvedTime = '".sqltime()."', - ResolverID = '".$LoggedUser['ID']."' - WHERE ID = '".db_string($ReportID)."'"); + UPDATE reports + SET Status = 'Resolved', + ResolvedTime = '".sqltime()."', + ResolverID = '".$LoggedUser['ID']."' + WHERE ID = '".db_string($ReportID)."'"); -$Channels = array(); +$Channels = []; if ($Type == 'request_update') { - $Channels[] = '#requestedits'; - $Cache->decrement('num_update_reports'); + $Channels[] = '#requestedits'; + $Cache->decrement('num_update_reports'); } if (in_array($Type, array('comment', 'post', 'thread'))) { - $Channels[] = '#forumreports'; - $Cache->decrement('num_forum_reports'); + $Channels[] = '#forumreports'; + $Cache->decrement('num_forum_reports'); } $DB->query(" - SELECT COUNT(ID) - FROM reports - WHERE Status = 'New'"); + SELECT COUNT(ID) + FROM reports + WHERE Status = 'New'"); list($Remaining) = $DB->next_record(); foreach ($Channels as $Channel) { - send_irc("PRIVMSG $Channel :Report $ReportID resolved by ".preg_replace('/^(.{2})/', '$1·', $LoggedUser['Username']).' on site ('.(int)$Remaining.' remaining).'); + send_irc("PRIVMSG $Channel :Report $ReportID resolved by ".preg_replace('/^(.{2})/', '$1·', $LoggedUser['Username']).' on site ('.(int)$Remaining.' remaining).'); } $Cache->delete_value('num_other_reports'); diff --git a/sections/reportsv2/ajax_change_resolve.php b/sections/reportsv2/ajax_change_resolve.php index 4c62e90b7..3645568d0 100644 --- a/sections/reportsv2/ajax_change_resolve.php +++ b/sections/reportsv2/ajax_change_resolve.php @@ -1,40 +1,40 @@ - AUTH_KEY - * [torrentid] => TORRENT_ID - * [type] => TYPE - * [otherid] => OTHER_ID + * [auth] => AUTH_KEY + * [torrentid] => TORRENT_ID + * [type] => TYPE + * [otherid] => OTHER_ID * * It should not be used on site as is, except in its current use (Switch) as it is lacking for any purpose but this. */ if (!check_perms('admin_reports')) { - error(403); + error(403); } authorize(); if (!is_number($_POST['torrentid'])) { - echo 'No Torrent ID'; - die(); + echo 'No Torrent ID'; + die(); } else { - $TorrentID = $_POST['torrentid']; + $TorrentID = $_POST['torrentid']; } $DB->query(" - SELECT tg.CategoryID - FROM torrents_group AS tg - JOIN torrents AS t ON t.GroupID = tg.ID - WHERE t.ID = $TorrentID"); + SELECT tg.CategoryID + FROM torrents_group AS tg + JOIN torrents AS t ON t.GroupID = tg.ID + WHERE t.ID = $TorrentID"); if (!$DB->has_results()) { - $Err = 'No torrent with that ID exists!'; + $Err = 'No torrent with that ID exists!'; } else { - list($CategoryID) = $DB->next_record(); + list($CategoryID) = $DB->next_record(); } if (!isset($_POST['type'])) { - echo 'Missing Type'; - die(); + echo 'Missing Type'; + die(); } elseif (array_key_exists($_POST['type'], $Types[$CategoryID])) { - $Type = $_POST['type']; - $ReportType = $Types[$CategoryID][$Type]; + $Type = $_POST['type']; + $ReportType = $Types[$CategoryID][$Type]; } elseif (array_key_exists($_POST['type'], $Types['master'])) { - $Type = $_POST['type']; - $ReportType = $Types['master'][$Type]; + $Type = $_POST['type']; + $ReportType = $Types['master'][$Type]; } else { - //There was a type but it wasn't an option! - echo 'Wrong type'; - die(); + //There was a type but it wasn't an option! + echo 'Wrong type'; + die(); } $ExtraID = db_string($_POST['otherid']); if (!empty($_POST['extra'])) { - $Extra = db_string($_POST['extra']); + $Extra = db_string($_POST['extra']); } else { - $Extra = ''; + $Extra = ''; } if (!empty($Err)) { - echo $Err; - die(); + echo $Err; + die(); } $DB->query(" - SELECT ID - FROM reportsv2 - WHERE TorrentID = $TorrentID - AND ReporterID = ".db_string($LoggedUser['ID'])." - AND ReportedTime > '".time_minus(3)."'"); + SELECT ID + FROM reportsv2 + WHERE TorrentID = $TorrentID + AND ReporterID = ".db_string($LoggedUser['ID'])." + AND ReportedTime > '".time_minus(3)."'"); if ($DB->has_results()) { - die(); + die(); } $DB->query(" - INSERT INTO reportsv2 - (ReporterID, TorrentID, Type, UserComment, Status, ReportedTime, ExtraID) - VALUES - (".db_string($LoggedUser['ID']).", $TorrentID, '$Type', '$Extra', 'New', '".sqltime()."', '$ExtraID')"); + INSERT INTO reportsv2 + (ReporterID, TorrentID, Type, UserComment, Status, ReportedTime, ExtraID) + VALUES + (".db_string($LoggedUser['ID']).", $TorrentID, '$Type', '$Extra', 'New', '".sqltime()."', '$ExtraID')"); $ReportID = $DB->inserted_id(); diff --git a/sections/reportsv2/ajax_giveback_report.php b/sections/reportsv2/ajax_giveback_report.php index 4e6c17bd8..dbf3f0f4b 100644 --- a/sections/reportsv2/ajax_giveback_report.php +++ b/sections/reportsv2/ajax_giveback_report.php @@ -1,21 +1,21 @@ -query(" - SELECT Status - FROM reportsv2 - WHERE ID = ".$_GET['id']); + SELECT Status + FROM reportsv2 + WHERE ID = ".$_GET['id']); list($Status) = $DB->next_record(); if (isset($Status)) { - $DB->query(" - UPDATE reportsv2 - SET Status = 'New', ResolverID = 0 - WHERE ID = ".$_GET['id']); + $DB->query(" + UPDATE reportsv2 + SET Status = 'New', ResolverID = 0 + WHERE ID = ".$_GET['id']); } ?> diff --git a/sections/reportsv2/ajax_grab_report.php b/sections/reportsv2/ajax_grab_report.php index c01240677..4846c1b2c 100644 --- a/sections/reportsv2/ajax_grab_report.php +++ b/sections/reportsv2/ajax_grab_report.php @@ -1,26 +1,26 @@ -query(" - UPDATE reportsv2 - SET Status = 'InProgress', - ResolverID = " . $LoggedUser['ID'] . " - WHERE ID = " . $_GET['id']); + UPDATE reportsv2 + SET Status = 'InProgress', + ResolverID = " . $LoggedUser['ID'] . " + WHERE ID = " . $_GET['id']); if ($DB->affected_rows() == 0) { - echo '0'; + echo '0'; } else { - echo '1'; + echo '1'; } diff --git a/sections/reportsv2/ajax_new_report.php b/sections/reportsv2/ajax_new_report.php index 26e41cf05..301a46fb3 100644 --- a/sections/reportsv2/ajax_new_report.php +++ b/sections/reportsv2/ajax_new_report.php @@ -1,4 +1,4 @@ -query(" - SELECT - r.ID, - r.ReporterID, - reporter.Username, - r.TorrentID, - r.Type, - r.UserComment, - r.ResolverID, - resolver.Username, - r.Status, - r.ReportedTime, - r.LastChangeTime, - r.ModComment, - r.Track, - r.Image, - r.ExtraID, - r.Link, - r.LogMessage, - tg.Name, - tg.ID, - CASE COUNT(ta.GroupID) - WHEN 1 THEN aa.ArtistID - WHEN 0 THEN '0' - ELSE '0' - END AS ArtistID, - CASE COUNT(ta.GroupID) - WHEN 1 THEN aa.Name - WHEN 0 THEN '' - ELSE 'Various Artists' - END AS ArtistName, - tg.Year, - tg.CategoryID, - t.Time, - t.Remastered, - t.RemasterTitle, - t.RemasterYear, - t.Media, - t.Format, - t.Encoding, - t.Size, - t.HasLog, - t.HasCue, - t.HasLogDB, - t.LogScore, - t.LogChecksum, - t.UserID AS UploaderID, - uploader.Username - FROM reportsv2 AS r - LEFT JOIN torrents AS t ON t.ID = r.TorrentID - LEFT JOIN torrents_group AS tg ON tg.ID = t.GroupID - LEFT JOIN torrents_artists AS ta ON ta.GroupID = tg.ID AND ta.Importance = '1' - LEFT JOIN artists_alias AS aa ON aa.AliasID = ta.AliasID - LEFT JOIN users_main AS resolver ON resolver.ID = r.ResolverID - LEFT JOIN users_main AS reporter ON reporter.ID = r.ReporterID - LEFT JOIN users_main AS uploader ON uploader.ID = t.UserID - WHERE r.Status = 'New' - GROUP BY r.ID - ORDER BY ReportedTime ASC - LIMIT 1"); + SELECT + r.ID, + r.ReporterID, + reporter.Username, + r.TorrentID, + r.Type, + r.UserComment, + r.ResolverID, + resolver.Username, + r.Status, + r.ReportedTime, + r.LastChangeTime, + r.ModComment, + r.Track, + r.Image, + r.ExtraID, + r.Link, + r.LogMessage, + tg.Name, + tg.ID, + CASE COUNT(ta.GroupID) + WHEN 1 THEN aa.ArtistID + WHEN 0 THEN '0' + ELSE '0' + END AS ArtistID, + CASE COUNT(ta.GroupID) + WHEN 1 THEN aa.Name + WHEN 0 THEN '' + ELSE 'Various Artists' + END AS ArtistName, + tg.Year, + tg.CategoryID, + t.Time, + t.Remastered, + t.RemasterTitle, + t.RemasterYear, + t.Media, + t.Format, + t.Encoding, + t.Size, + t.HasLog, + t.HasCue, + t.HasLogDB, + t.LogScore, + t.LogChecksum, + t.UserID AS UploaderID, + uploader.Username + FROM reportsv2 AS r + LEFT JOIN torrents AS t ON t.ID = r.TorrentID + LEFT JOIN torrents_group AS tg ON tg.ID = t.GroupID + LEFT JOIN torrents_artists AS ta ON ta.GroupID = tg.ID AND ta.Importance = '1' + LEFT JOIN artists_alias AS aa ON aa.AliasID = ta.AliasID + LEFT JOIN users_main AS resolver ON resolver.ID = r.ResolverID + LEFT JOIN users_main AS reporter ON reporter.ID = r.ReporterID + LEFT JOIN users_main AS uploader ON uploader.ID = t.UserID + WHERE r.Status = 'New' + GROUP BY r.ID + ORDER BY ReportedTime ASC + LIMIT 1"); - if (!$DB->has_results()) { - die(); - } + if (!$DB->has_results()) { + die(); + } - list($ReportID, $ReporterID, $ReporterName, $TorrentID, $Type, $UserComment, $ResolverID, $ResolverName, $Status, $ReportedTime, $LastChangeTime, - $ModComment, $Tracks, $Images, $ExtraIDs, $Links, $LogMessage, $GroupName, $GroupID, $ArtistID, $ArtistName, $Year, $CategoryID, $Time, $Remastered, $RemasterTitle, - $RemasterYear, $Media, $Format, $Encoding, $Size, $HasLog, $HasCue, $HasLogDB, $LogScore, $LogChecksum, $UploaderID, $UploaderName) = $DB->next_record(MYSQLI_BOTH, array("ModComment")); + list($ReportID, $ReporterID, $ReporterName, $TorrentID, $Type, $UserComment, $ResolverID, $ResolverName, $Status, $ReportedTime, $LastChangeTime, + $ModComment, $Tracks, $Images, $ExtraIDs, $Links, $LogMessage, $GroupName, $GroupID, $ArtistID, $ArtistName, $Year, $CategoryID, $Time, $Remastered, $RemasterTitle, + $RemasterYear, $Media, $Format, $Encoding, $Size, $HasLog, $HasCue, $HasLogDB, $LogScore, $LogChecksum, $UploaderID, $UploaderName) = $DB->next_record(MYSQLI_BOTH, array("ModComment")); - if (!$GroupID) { - //Torrent already deleted - $DB->query(" - UPDATE reportsv2 - SET - Status = 'Resolved', - LastChangeTime = '".sqltime()."', - ModComment = 'Report already dealt with (torrent deleted)' - WHERE ID = $ReportID"); - $Cache->decrement('num_torrent_reportsv2'); + if (!$GroupID) { + //Torrent already deleted + $DB->query(" + UPDATE reportsv2 + SET + Status = 'Resolved', + LastChangeTime = '".sqltime()."', + ModComment = 'Report already dealt with (torrent deleted)' + WHERE ID = $ReportID"); + $Cache->decrement('num_torrent_reportsv2'); ?> -
    - Report for torrent (deleted) has been automatically resolved. -
    -query(" - UPDATE reportsv2 - SET Status = 'InProgress', - ResolverID = ".$LoggedUser['ID']." - WHERE ID = $ReportID"); +
    + Report for torrent (deleted) has been automatically resolved. +
    +query(" + UPDATE reportsv2 + SET Status = 'InProgress', + ResolverID = ".$LoggedUser['ID']." + WHERE ID = $ReportID"); - if (array_key_exists($Type, $Types[$CategoryID])) { - $ReportType = $Types[$CategoryID][$Type]; - } elseif (array_key_exists($Type,$Types['master'])) { - $ReportType = $Types['master'][$Type]; - } else { - //There was a type but it wasn't an option! - $Type = 'other'; - $ReportType = $Types['master']['other']; - } - $RemasterDisplayString = Reports::format_reports_remaster_info($Remastered, $RemasterTitle, $RemasterYear); + if (array_key_exists($Type, $Types[$CategoryID])) { + $ReportType = $Types[$CategoryID][$Type]; + } elseif (array_key_exists($Type,$Types['master'])) { + $ReportType = $Types['master'][$Type]; + } else { + //There was a type but it wasn't an option! + $Type = 'other'; + $ReportType = $Types['master']['other']; + } + $RemasterDisplayString = Reports::format_reports_remaster_info($Remastered, $RemasterTitle, $RemasterYear); - if ($ArtistID == 0 && empty($ArtistName)) { - $RawName = $GroupName.($Year ? " ($Year)" : '').($Format || $Encoding || $Media ? " [$Format/$Encoding/$Media]" : '') . $RemasterDisplayString . ($HasCue ? ' (Cue)' : '').($HasLog ? " (Log".($HasLogDB ? ": {$LogScore}%" : '').')' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - $LinkName = "$GroupName".($Year ? " ($Year)" : '')." ".($Format || $Encoding || $Media ? " [$Format/$Encoding/$Media]" : '') . $RemasterDisplayString . ' '.($HasCue ? ' (Cue)' : '').($HasLog ? " (Log".($HasLogDB ? ": {$LogScore}%" : '').')' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - $BBName = "[url=torrents.php?id=$GroupID]$GroupName".($Year ? " ($Year)" : '')."[/url] [url=torrents.php?torrentid=$TorrentID][$Format/$Encoding/$Media]{$RemasterDisplayString}[/url] ".($HasCue ? ' (Cue)' : '').($HasLog ? " [url=torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID](Log".($HasLogDB ? ": {$LogScore}%" : '').')[/url]' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - } elseif ($ArtistID == 0 && $ArtistName == 'Various Artists') { - $RawName = "Various Artists - $GroupName".($Year ? " ($Year)" : '')." [$Format/$Encoding/$Media]$RemasterDisplayString" . ($HasCue ? ' (Cue)' : '').($HasLog ? " (Log".($HasLogDB ? ": {$LogScore}%" : '').')' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - $LinkName = "Various Artists - $GroupName".($Year ? " ($Year)" : '')." [$Format/$Encoding/$Media]$RemasterDisplayString ".($HasCue ? ' (Cue)' : '').($HasLog ? " (Log".($HasLogDB ? ": {$LogScore}%" : '').')' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - $BBName = "Various Artists - [url=torrents.php?id=$GroupID]$GroupName".($Year ? " ($Year)" : '')."[/url] [url=torrents.php?torrentid=$TorrentID][$Format/$Encoding/$Media]{$RemasterDisplayString}[/url] ".($HasCue ? ' (Cue)' : '').($HasLog ? " [url=torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID](Log".($HasLogDB ? ": {$LogScore}%" : '').')[/url]' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - } else { - $RawName = "$ArtistName - $GroupName".($Year ? " ($Year)" : '')." [$Format/$Encoding/$Media]$RemasterDisplayString" . ($HasCue ? ' (Cue)' : '').($HasLogDB ? " (Log: {$LogScore}%)" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - $LinkName = "$ArtistName - $GroupName".($Year ? " ($Year)" : '')." [$Format/$Encoding/$Media]$RemasterDisplayString ".($HasCue ? ' (Cue)' : '').($HasLog ? " (Log".($HasLogDB ? ": {$LogScore}%" : '').')' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - $BBName = "[url=artist.php?id=$ArtistID]".$ArtistName."[/url] - [url=torrents.php?id=$GroupID]$GroupName".($Year ? " ($Year)" : '')."[/url] [url=torrents.php?torrentid=$TorrentID][$Format/$Encoding/$Media]{$RemasterDisplayString}[/url] ".($HasCue ? ' (Cue)' : '').($HasLog ? " [url=torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID](Log".($HasLogDB ? ": {$LogScore}%" : '').')[/url]' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - } - ?> -
    -
    - -
    - - - - - - - - - -
    - - - - + + + + + + + + + + + + + + +
    Reported torrent: - - (Deleted) - - - DL - uploaded by -
    -
    was reported by for the reason:
    -query(" - SELECT r.ID - FROM reportsv2 AS r - LEFT JOIN torrents AS t ON t.ID = r.TorrentID - WHERE r.Status != 'Resolved' - AND t.GroupID = $GroupID"); - $GroupOthers = ($DB->record_count() - 1); + if ($ArtistID == 0 && empty($ArtistName)) { + $RawName = $GroupName.($Year ? " ($Year)" : '').($Format || $Encoding || $Media ? " [$Format/$Encoding/$Media]" : '') . $RemasterDisplayString . ($HasCue ? ' (Cue)' : '').($HasLog ? " (Log".($HasLogDB ? ": {$LogScore}%" : '').')' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + $LinkName = "$GroupName".($Year ? " ($Year)" : '')." ".($Format || $Encoding || $Media ? " [$Format/$Encoding/$Media]" : '') . $RemasterDisplayString . ' '.($HasCue ? ' (Cue)' : '').($HasLog ? " (Log".($HasLogDB ? ": {$LogScore}%" : '').')' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + $BBName = "[url=torrents.php?id=$GroupID]$GroupName".($Year ? " ($Year)" : '')."[/url] [url=torrents.php?torrentid=$TorrentID][$Format/$Encoding/$Media]{$RemasterDisplayString}[/url] ".($HasCue ? ' (Cue)' : '').($HasLog ? " [url=torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID](Log".($HasLogDB ? ": {$LogScore}%" : '').')[/url]' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + } elseif ($ArtistID == 0 && $ArtistName == 'Various Artists') { + $RawName = "Various Artists - $GroupName".($Year ? " ($Year)" : '')." [$Format/$Encoding/$Media]$RemasterDisplayString" . ($HasCue ? ' (Cue)' : '').($HasLog ? " (Log".($HasLogDB ? ": {$LogScore}%" : '').')' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + $LinkName = "Various Artists - $GroupName".($Year ? " ($Year)" : '')." [$Format/$Encoding/$Media]$RemasterDisplayString ".($HasCue ? ' (Cue)' : '').($HasLog ? " (Log".($HasLogDB ? ": {$LogScore}%" : '').')' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + $BBName = "Various Artists - [url=torrents.php?id=$GroupID]$GroupName".($Year ? " ($Year)" : '')."[/url] [url=torrents.php?torrentid=$TorrentID][$Format/$Encoding/$Media]{$RemasterDisplayString}[/url] ".($HasCue ? ' (Cue)' : '').($HasLog ? " [url=torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID](Log".($HasLogDB ? ": {$LogScore}%" : '').')[/url]' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + } else { + $RawName = "$ArtistName - $GroupName".($Year ? " ($Year)" : '')." [$Format/$Encoding/$Media]$RemasterDisplayString" . ($HasCue ? ' (Cue)' : '').($HasLogDB ? " (Log: {$LogScore}%)" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + $LinkName = "$ArtistName - $GroupName".($Year ? " ($Year)" : '')." [$Format/$Encoding/$Media]$RemasterDisplayString ".($HasCue ? ' (Cue)' : '').($HasLog ? " (Log".($HasLogDB ? ": {$LogScore}%" : '').')' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + $BBName = "[url=artist.php?id=$ArtistID]".$ArtistName."[/url] - [url=torrents.php?id=$GroupID]$GroupName".($Year ? " ($Year)" : '')."[/url] [url=torrents.php?torrentid=$TorrentID][$Format/$Encoding/$Media]{$RemasterDisplayString}[/url] ".($HasCue ? ' (Cue)' : '').($HasLog ? " [url=torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID](Log".($HasLogDB ? ": {$LogScore}%" : '').')[/url]' : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + } + ?> +
    + + +
    + + + + + + + + + +
    + + + + - - - - - - -query(" + SELECT DISTINCT req.ID, + req.FillerID, + um.Username, + req.TimeFilled + FROM requests AS req + LEFT JOIN torrents AS t ON t.ID = req.TorrentID + LEFT JOIN reportsv2 AS rep ON rep.TorrentID = t.ID + JOIN users_main AS um ON um.ID = req.FillerID + WHERE rep.Status != 'Resolved' + AND req.TimeFilled > '2010-03-04 02:31:49' + AND req.TorrentID = $TorrentID"); + $Requests = $DB->has_results(); + if ($Requests > 0) { + while (list($RequestID, $FillerID, $FillerName, $FilledTime) = $DB->next_record()) { + ?> +
    + used this torrent to fill this request +
    + + + + + + + + + - - - + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - -
    Reported torrent: + + (Deleted) + + + DL + uploaded by +
    +
    was reported by for the reason:
    +query(" + SELECT r.ID + FROM reportsv2 AS r + LEFT JOIN torrents AS t ON t.ID = r.TorrentID + WHERE r.Status != 'Resolved' + AND t.GroupID = $GroupID"); + $GroupOthers = ($DB->record_count() - 1); - if ($GroupOthers > 0) { ?> - -query(" - SELECT t.UserID - FROM reportsv2 AS r - JOIN torrents AS t ON t.ID = r.TorrentID - WHERE r.Status != 'Resolved' - AND t.UserID = $UploaderID"); - $UploaderOthers = ($DB->record_count() - 1); + if ($GroupOthers > 0) { ?> + +query(" + SELECT t.UserID + FROM reportsv2 AS r + JOIN torrents AS t ON t.ID = r.TorrentID + WHERE r.Status != 'Resolved' + AND t.UserID = $UploaderID"); + $UploaderOthers = ($DB->record_count() - 1); - if ($UploaderOthers > 0) { ?> - - 0) { ?> + +query(" - SELECT DISTINCT req.ID, - req.FillerID, - um.Username, - req.TimeFilled - FROM requests AS req - LEFT JOIN torrents AS t ON t.ID = req.TorrentID - LEFT JOIN reportsv2 AS rep ON rep.TorrentID = t.ID - JOIN users_main AS um ON um.ID = req.FillerID - WHERE rep.Status != 'Resolved' - AND req.TimeFilled > '2010-03-04 02:31:49' - AND req.TorrentID = $TorrentID"); - $Requests = $DB->has_results(); - if ($Requests > 0) { - while (list($RequestID, $FillerID, $FillerName, $FilledTime) = $DB->next_record()) { - ?> -
    - used this torrent to fill this request -
    - -
    Relevant tracks: - -
    Relevant tracks: + +
    Relevant links: - +
    Relevant links: + - - -
    Relevant other torrents: -query(" - SELECT - tg.Name, - tg.ID, - CASE COUNT(ta.GroupID) - WHEN 1 THEN aa.ArtistID - WHEN 0 THEN '0' - ELSE '0' - END AS ArtistID, - CASE COUNT(ta.GroupID) - WHEN 1 THEN aa.Name - WHEN 0 THEN '' - ELSE 'Various Artists' - END AS ArtistName, - tg.Year, - t.Time, - t.Remastered, - t.RemasterTitle, - t.RemasterYear, - t.Media, - t.Format, - t.Encoding, - t.Size, - t.HasLog, - t.HasCue, - t.HasLogDB, - t.LogScore, - t.LogChecksum, - t.UserID AS UploaderID, - uploader.Username - FROM torrents AS t - LEFT JOIN torrents_group AS tg ON tg.ID = t.GroupID - LEFT JOIN torrents_artists AS ta ON ta.GroupID = tg.ID AND ta.Importance = '1' - LEFT JOIN artists_alias AS aa ON aa.AliasID = ta.AliasID - LEFT JOIN users_main AS uploader ON uploader.ID = t.UserID - WHERE t.ID = '$ExtraID' - GROUP BY tg.ID"); + if ($local_url = Text::local_url($Link)) { + $Link = $local_url; + } ?> + + +
    Relevant other torrents: +query(" + SELECT + tg.Name, + tg.ID, + CASE COUNT(ta.GroupID) + WHEN 1 THEN aa.ArtistID + WHEN 0 THEN '0' + ELSE '0' + END AS ArtistID, + CASE COUNT(ta.GroupID) + WHEN 1 THEN aa.Name + WHEN 0 THEN '' + ELSE 'Various Artists' + END AS ArtistName, + tg.Year, + t.Time, + t.Remastered, + t.RemasterTitle, + t.RemasterYear, + t.Media, + t.Format, + t.Encoding, + t.Size, + t.HasLog, + t.HasCue, + t.HasLogDB, + t.LogScore, + t.LogChecksum, + t.UserID AS UploaderID, + uploader.Username + FROM torrents AS t + LEFT JOIN torrents_group AS tg ON tg.ID = t.GroupID + LEFT JOIN torrents_artists AS ta ON ta.GroupID = tg.ID AND ta.Importance = '1' + LEFT JOIN artists_alias AS aa ON aa.AliasID = ta.AliasID + LEFT JOIN users_main AS uploader ON uploader.ID = t.UserID + WHERE t.ID = '$ExtraID' + GROUP BY tg.ID"); - list($ExtraGroupName, $ExtraGroupID, $ExtraArtistID, $ExtraArtistName, $ExtraYear, $ExtraTime, $ExtraRemastered, $ExtraRemasterTitle, - $ExtraRemasterYear, $ExtraMedia, $ExtraFormat, $ExtraEncoding, $ExtraSize, $ExtraHasLog, $ExtraHasCue, $ExtraHasLogDB, $ExtraLogScore, $ExtraLogChecksum, - $ExtraUploaderID, $ExtraUploaderName) = Misc::display_array($DB->next_record()); + list($ExtraGroupName, $ExtraGroupID, $ExtraArtistID, $ExtraArtistName, $ExtraYear, $ExtraTime, $ExtraRemastered, $ExtraRemasterTitle, + $ExtraRemasterYear, $ExtraMedia, $ExtraFormat, $ExtraEncoding, $ExtraSize, $ExtraHasLog, $ExtraHasCue, $ExtraHasLogDB, $ExtraLogScore, $ExtraLogChecksum, + $ExtraUploaderID, $ExtraUploaderName) = Misc::display_array($DB->next_record()); - if ($ExtraGroupName) { - $ExtraRemasterDisplayString = Reports::format_reports_remaster_info($ExtraRemastered, $ExtraRemasterTitle, $ExtraRemasterYear); + if ($ExtraGroupName) { + $ExtraRemasterDisplayString = Reports::format_reports_remaster_info($ExtraRemastered, $ExtraRemasterTitle, $ExtraRemasterYear); - if ($ArtistID == 0 && empty($ArtistName)) { - $ExtraLinkName = "$ExtraGroupName".($ExtraYear ? " ($ExtraYear)" : '')." [$ExtraFormat/$ExtraEncoding/$ExtraMedia]$ExtraRemasterDisplayString " . ($ExtraHasLog ? " (Log".($ExtraHasLogDB ? ": {$ExtraLogScore}%" : '').')' : '').' ('.number_format($ExtraSize / (1024 * 1024), 2).' MB)'; - } elseif ($ArtistID == 0 && $ArtistName == 'Various Artists') { - $ExtraLinkName = "Various Artists - $ExtraGroupName".($ExtraYear ? " ($ExtraYear)" : '')." [$ExtraFormat/$ExtraEncoding/$ExtraMedia]$ExtraRemasterDisplayString " . ($ExtraHasLog ? " (Log".($ExtraHasLogDB ? ": {$ExtraLogScore}%" : '').')' : '').' ('.number_format($ExtraSize / (1024 * 1024), 2).' MB)'; - } else { - $ExtraLinkName = "$ExtraArtistName - $ExtraGroupName".($ExtraYear ? " ($ExtraYear)" : '')." [$ExtraFormat/$ExtraEncoding/$ExtraMedia]$ExtraRemasterDisplayString " . ($ExtraHasLog ? " (Log".($ExtraHasLogDB ? ": {$ExtraLogScore}%" : '').')' : '').' ('.number_format($ExtraSize / (1024 * 1024), 2).' MB)'; - } - ?> - ')?> - - DL - uploaded by Switch - -
    Relevant images: - - Relevant image - -
    User comment:
    Report comment: - - -
    - Resolve: - -
    Relevant images: + + Relevant image + +
    User comment:
    Report comment: + + +
    + Resolve: + + - - - - - - - - - - - - - - - -    - - - - -
    - PM - : - - - -
    Extra log message: - /> - Extra staff notes: - -
    - - - | - | - | Multi-resolve - | -
    - -
    + + + + + + + + + + + + + + + + + + +    + + + + +
    + PM + : + + + +
    Extra log message: + /> + Extra staff notes: + +
    + + + | + | + | Multi-resolve + | +
    + +
    diff --git a/sections/reportsv2/ajax_report.php b/sections/reportsv2/ajax_report.php index 1e7f9c452..55333ad27 100644 --- a/sections/reportsv2/ajax_report.php +++ b/sections/reportsv2/ajax_report.php @@ -1,4 +1,4 @@ -
      - -
    • - +

    - - - - - - + + + + - - - - - + + + + - - - - - + + + + - - - - + + + + - - - - - - + + + + - - - - + + + +
    - Image(s)(Required):' : '')?> - - -
    + Image(s)(Required):' : '')?> + + +
    - Track Number(s)(Required):' : '')?> - - All' : '')?> -
    + Track Number(s)(Required):' : '')?> + + All' : '')?> +
    - Link(s) to external source(Required):' : '')?> - - -
    + Link(s) to external source(Required):' : '')?> + + +
    - Permalink to other relevant torrent(s)(Required):' : '')?> - - -
    + Permalink to other relevant torrent(s)(Required):' : '')?> + + +
    - Link(s) to proof images(Required):' : '')?>: - - -
    + Link(s) to proof images(Required):' : '')?>: + + +
    - Comments (Required): - - -
    + Comments (Required): + + +
    diff --git a/sections/reportsv2/ajax_take_pm.php b/sections/reportsv2/ajax_take_pm.php index 48320d421..d0898e555 100644 --- a/sections/reportsv2/ajax_take_pm.php +++ b/sections/reportsv2/ajax_take_pm.php @@ -1,4 +1,4 @@ -query(" - SELECT ModComment - FROM reportsv2 - WHERE ID = $ReportID"); + SELECT ModComment + FROM reportsv2 + WHERE ID = $ReportID"); list($ModComment) = $DB->next_record(); if (isset($ModComment)) { - $DB->query(" - UPDATE reportsv2 - SET ModComment = '$Message' - WHERE ID = $ReportID"); + $DB->query(" + UPDATE reportsv2 + SET ModComment = '$Message' + WHERE ID = $ReportID"); } diff --git a/sections/reportsv2/ajax_update_resolve.php b/sections/reportsv2/ajax_update_resolve.php index f2e6928b1..82af608df 100644 --- a/sections/reportsv2/ajax_update_resolve.php +++ b/sections/reportsv2/ajax_update_resolve.php @@ -1,13 +1,13 @@ - $Value) { - $Priorities[$Key] = $Value['priority']; - } - array_multisort($Priorities, SORT_ASC, $TypeList); + $TypeList = $Types['master'] + $Types[$CategoryID]; + $Priorities = []; + foreach ($TypeList as $Key => $Value) { + $Priorities[$Key] = $Value['priority']; + } + array_multisort($Priorities, SORT_ASC, $TypeList); } else { - $TypeList = $Types['master']; + $TypeList = $Types['master']; } if (!array_key_exists($NewType, $TypeList)) { - echo "No resolve from that category"; - die(); + echo "No resolve from that category"; + die(); } $DB->query(" - UPDATE reportsv2 - SET Type = '$NewType' - WHERE ID = $ReportID"); + UPDATE reportsv2 + SET Type = '$NewType' + WHERE ID = $ReportID"); diff --git a/sections/reportsv2/array.php b/sections/reportsv2/array.php index 43aa7c7ae..a81af5501 100644 --- a/sections/reportsv2/array.php +++ b/sections/reportsv2/array.php @@ -1,4 +1,4 @@ - array( - 'dupe' => array( - 'priority' => '10', - 'reason' => '0', - 'title' => 'Dupe', - 'report_messages' => array( - 'Please specify a link to the original torrent.' - ), - 'report_fields' => array( - 'sitelink' => '1' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '1', - 'pm' => '[rule]h2.2[/rule]. Your torrent was reported because it was a duplicate of another torrent.' - ) - ), - 'banned' => array( - 'priority' => '230', - 'reason' => '14', - 'title' => 'Specifically Banned', - 'report_messages' => array( - 'Please specify exactly which entry on the Do Not Upload list this is violating.' - ), - 'report_fields' => array( - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '4', - 'delete' => '1', - 'pm' => '[rule]h1.2[/rule]. You have uploaded material that is currently forbidden. Items on the Do Not Upload (DNU) list (at the top of the [url='.site_url().'upload.php]upload page[/url]) and in the [url='.site_url().'rules.php?p=upload#h1.2]Specifically Banned[/url] portion of the uploading rules cannot be uploaded to the site. Do not upload them unless your torrent meets a condition specified in the comments of the DNU list. + 'master' => array( + 'dupe' => array( + 'priority' => '10', + 'reason' => '0', + 'title' => 'Dupe', + 'report_messages' => array( + 'Please specify a link to the original torrent.' + ), + 'report_fields' => array( + 'sitelink' => '1' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '1', + 'pm' => '[rule]h2.2[/rule]. Your torrent was reported because it was a duplicate of another torrent.' + ) + ), + 'banned' => array( + 'priority' => '230', + 'reason' => '14', + 'title' => 'Specifically Banned', + 'report_messages' => array( + 'Please specify exactly which entry on the Do Not Upload list this is violating.' + ), + 'report_fields' => array( + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '4', + 'delete' => '1', + 'pm' => '[rule]h1.2[/rule]. You have uploaded material that is currently forbidden. Items on the Do Not Upload (DNU) list (at the top of the [url='.site_url().'upload.php]upload page[/url]) and in the [url='.site_url().'rules.php?p=upload#h1.2]Specifically Banned[/url] portion of the uploading rules cannot be uploaded to the site. Do not upload them unless your torrent meets a condition specified in the comments of the DNU list. Your torrent was reported because it contained material from the DNU list or from the Specifically Banned section of the rules.' - ) - ), - 'urgent' => array( - 'priority' => '280', - 'reason' => '-1', - 'title' => 'Urgent', - 'report_messages' => array( - 'This report type is only for very urgent reports, usually for personal information being found within a torrent.', - 'Abusing the "Urgent" report type could result in a warning or worse.', - 'As this report type gives the staff absolutely no information about the problem, please be as clear as possible in your comments about what the problem is.' - ), - 'report_fields' => array( - 'sitelink' => '0', - 'track' => '0', - 'link' => '0', - 'image' => '0', - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '0', - 'pm' => '' - ) - ), - 'other' => array( - 'priority' => '200', - 'reason' => '-1', - 'title' => 'Other', - 'report_messages' => array( - 'Please include as much information as possible to verify the report.' - ), - 'report_fields' => array( - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '0', - 'pm' => '' - ) - ), - 'trump' => array( - 'priority' => '20', - 'reason' => '1', - 'title' => 'Trump', - 'report_messages' => array( - 'Please list the specific reason(s) the newer torrent trumps the older one.', - 'Please make sure you are reporting the torrent which has been trumped and should be deleted, not the torrent that you think should remain on site.' - ), - 'report_fields' => array( - 'sitelink' => '1' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '1', - 'pm' => '[rule]h2.2[/rule]. Your torrent was reported because it was trumped by another torrent.' - ) - ) - ), + ) + ), + 'urgent' => array( + 'priority' => '280', + 'reason' => '-1', + 'title' => 'Urgent', + 'report_messages' => array( + 'This report type is only for very urgent reports, usually for personal information being found within a torrent.', + 'Abusing the "Urgent" report type could result in a warning or worse.', + 'As this report type gives the staff absolutely no information about the problem, please be as clear as possible in your comments about what the problem is.' + ), + 'report_fields' => array( + 'sitelink' => '0', + 'track' => '0', + 'link' => '0', + 'image' => '0', + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '0', + 'pm' => '' + ) + ), + 'other' => array( + 'priority' => '200', + 'reason' => '-1', + 'title' => 'Other', + 'report_messages' => array( + 'Please include as much information as possible to verify the report.' + ), + 'report_fields' => array( + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '0', + 'pm' => '' + ) + ), + 'trump' => array( + 'priority' => '20', + 'reason' => '1', + 'title' => 'Trump', + 'report_messages' => array( + 'Please list the specific reason(s) the newer torrent trumps the older one.', + 'Please make sure you are reporting the torrent which has been trumped and should be deleted, not the torrent that you think should remain on site.' + ), + 'report_fields' => array( + 'sitelink' => '1' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '1', + 'pm' => '[rule]h2.2[/rule]. Your torrent was reported because it was trumped by another torrent.' + ) + ) + ), - '1' => array( //Music Resolves - 'tag_trump' => array ( - 'priority' => '50', - 'reason' => '4', - 'title' => 'Tag Trump', - 'report_messages' => array( - 'Please list the specific tag(s) the newer torrent trumps the older one.', - 'Please make sure you are reporting the torrent which has been trumped and should be deleted, not the torrent that you think should remain on site.' - ), - 'report_fields' => array( - 'sitelink' => '1' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '1', - 'pm' => '[rule]2.3.16[/rule]. Properly tag your music files. Certain meta tags (e.g., ID3, Vorbis) are required on all music uploads. Make sure to use the appropriate tag format for your files (e.g., no ID3 tags for FLAC - see [rule]2.2.10.8[/rule]). ID3v2 tags for files are highly recommended over ID3v1. ID3 are recommended for AC3 torrents but are not mandatory because the format does not natively support file metadata tagging (for AC3, the file names become the vehicle for correctly labeling media files). Torrents uploaded with both good ID3v1 tags and blank ID3v2 tags (a dual set of tags) are trumpable by torrents with either just good ID3v1 tags or good ID3v2 tags (a single set of tags). If you upload an album missing one or more of the required tags, then another user may add the tags, re-upload, and report your torrent for deletion. + '1' => array( //Music Resolves + 'tag_trump' => array ( + 'priority' => '50', + 'reason' => '4', + 'title' => 'Tag Trump', + 'report_messages' => array( + 'Please list the specific tag(s) the newer torrent trumps the older one.', + 'Please make sure you are reporting the torrent which has been trumped and should be deleted, not the torrent that you think should remain on site.' + ), + 'report_fields' => array( + 'sitelink' => '1' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '1', + 'pm' => '[rule]2.3.16[/rule]. Properly tag your music files. Certain meta tags (e.g., ID3, Vorbis) are required on all music uploads. Make sure to use the appropriate tag format for your files (e.g., no ID3 tags for FLAC - see [rule]2.2.10.8[/rule]). ID3v2 tags for files are highly recommended over ID3v1. ID3 are recommended for AC3 torrents but are not mandatory because the format does not natively support file metadata tagging (for AC3, the file names become the vehicle for correctly labeling media files). Torrents uploaded with both good ID3v1 tags and blank ID3v2 tags (a dual set of tags) are trumpable by torrents with either just good ID3v1 tags or good ID3v2 tags (a single set of tags). If you upload an album missing one or more of the required tags, then another user may add the tags, re-upload, and report your torrent for deletion. Your torrent was reported because it was trumped by another torrent with improved metadata tags.' - ) - ), - 'vinyl_trump' => array( - 'priority' => '60', - 'reason' => '1', - 'title' => 'Vinyl Trump', - 'report_messages' => array( - 'Please list the specific reason(s) the newer torrent trumps the older one.', - 'Please be as thorough as possible and include as much detail as you can. Refer to specific tracks and time positions to justify your report.', - 'Please make sure you are reporting the torrent which has been trumped and should be deleted, not the torrent that you think should remain on site.' - ), - 'report_fields' => array( - 'sitelink' => '1' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '1', - 'pm' => '[rule]2.5.5[/rule]. Vinyl rips may be trumped by better-sounding rips of the same bit depth, regardless of lineage information (see [rule]2.3.9[/rule]). + ) + ), + 'vinyl_trump' => array( + 'priority' => '60', + 'reason' => '1', + 'title' => 'Vinyl Trump', + 'report_messages' => array( + 'Please list the specific reason(s) the newer torrent trumps the older one.', + 'Please be as thorough as possible and include as much detail as you can. Refer to specific tracks and time positions to justify your report.', + 'Please make sure you are reporting the torrent which has been trumped and should be deleted, not the torrent that you think should remain on site.' + ), + 'report_fields' => array( + 'sitelink' => '1' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '1', + 'pm' => '[rule]2.5.5[/rule]. Vinyl rips may be trumped by better-sounding rips of the same bit depth, regardless of lineage information (see [rule]2.3.9[/rule]). Your torrent was reported as it was trumped by a better-sounding vinyl rip.' - ) - ), - 'folder_trump' => array ( - 'priority' => '40', - 'reason' => '3', - 'title' => 'Bad Folder Name Trump', - 'report_messages' => array( - 'Please list the folder name and what is wrong with it.', - 'Please make sure you are reporting the torrent which has been trumped and should be deleted, not the torrent that you think should remain on site.' - ), - 'report_fields' => array( - 'sitelink' => '1' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '1', - 'pm' => '[rule]2.3.2[/rule]. Name your directories with meaningful titles, such as "Artist - Album (Year) - Format". The minimum acceptable is "Album" although it is preferable to include more information. If the directory name does not include this minimum then another user can rename the directory, re-upload, and report your torrent for deletion. In addition, torrent folders that are named using the scene convention will be trumpable if the Scene label is absent from the torrent. + ) + ), + 'folder_trump' => array ( + 'priority' => '40', + 'reason' => '3', + 'title' => 'Bad Folder Name Trump', + 'report_messages' => array( + 'Please list the folder name and what is wrong with it.', + 'Please make sure you are reporting the torrent which has been trumped and should be deleted, not the torrent that you think should remain on site.' + ), + 'report_fields' => array( + 'sitelink' => '1' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '1', + 'pm' => '[rule]2.3.2[/rule]. Name your directories with meaningful titles, such as "Artist - Album (Year) - Format". The minimum acceptable is "Album" although it is preferable to include more information. If the directory name does not include this minimum then another user can rename the directory, re-upload, and report your torrent for deletion. In addition, torrent folders that are named using the scene convention will be trumpable if the Scene label is absent from the torrent. [rule]2.3.3[/rule]. Avoid creating unnecessary nested folders (such as an extra folder for the actual album) inside your properly named directory. A torrent with unnecessary nested folders is trumpable by a torrent with such folders removed. For single disc albums, all audio files must be included in the main torrent folder. For multi-disc albums, the main torrent folder may include one sub-folder that holds the audio file contents for each disc in the box set, i.e., the main torrent folder is "Adele - 19 (2008) - FLAC" while appropriate sub-folders may include "19 (Disc 1of2)" or "19" and "Live From The Hotel Cafe (Disc 2of2)" or "Acoustic Set Live From The Hotel Cafe, Los Angeles." Additional folders are unnecessary because they do nothing to improve the organization of the torrent. If you are uncertain about what to do for other cases, PM a staff member for guidance. Your torrent was reported because it was trumped by another torrent with an improved folder name and directory structure.' - ) - ), - 'file_trump' => array ( - 'priority' => '30', - 'reason' => '2', - 'title' => 'Bad File Names Trump', - 'report_messages' => array( - 'Please describe what is wrong with the file names.', - 'Please make sure you are reporting the torrent which has been trumped and should be deleted, not the torrent that you think should remain on site.' - ), - 'report_fields' => array( - 'sitelink' => '1' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '1', - 'pm' => '[rule]2.3.11[/rule]. File names must accurately reflect the song titles. You may not have file names like 01track.mp3, 02track.mp3, etc. Torrents containing files that are named with incorrect song titles can be trumped by properly labeled torrents. Also, torrents that are sourced from the scene but do not have the Scene label must comply with site naming rules (no release group names in the file names, no advertisements in the file names, etc.). If all the letters in the track titles are capitalized, the torrent is trumpable. If you upload an album with improper file names, then another user may fix the file names, re-upload, and report yours for deletion. + ) + ), + 'file_trump' => array ( + 'priority' => '30', + 'reason' => '2', + 'title' => 'Bad File Names Trump', + 'report_messages' => array( + 'Please describe what is wrong with the file names.', + 'Please make sure you are reporting the torrent which has been trumped and should be deleted, not the torrent that you think should remain on site.' + ), + 'report_fields' => array( + 'sitelink' => '1' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '1', + 'pm' => '[rule]2.3.11[/rule]. File names must accurately reflect the song titles. You may not have file names like 01track.mp3, 02track.mp3, etc. Torrents containing files that are named with incorrect song titles can be trumped by properly labeled torrents. Also, torrents that are sourced from the scene but do not have the Scene label must comply with site naming rules (no release group names in the file names, no advertisements in the file names, etc.). If all the letters in the track titles are capitalized, the torrent is trumpable. If you upload an album with improper file names, then another user may fix the file names, re-upload, and report yours for deletion. Your torrent was reported because it was trumped by another torrent with improved file names.' - ) - ), - 'tracks_missing' => array( - 'priority' => '240', - 'reason' => '15', - 'title' => 'Track(s) Missing', - 'report_messages' => array( - 'Please list the track number and title of the missing track.', - 'If possible, please provide a link to Amazon.com or another source showing the proper track listing.' - ), - 'report_fields' => array( - 'track' => '2', - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]2.1.19[/rule]. All music torrents must represent a complete release, and may not be missing tracks (or discs in the case of a multi-disc release). + ) + ), + 'tracks_missing' => array( + 'priority' => '240', + 'reason' => '15', + 'title' => 'Track(s) Missing', + 'report_messages' => array( + 'Please list the track number and title of the missing track.', + 'If possible, please provide a link to Amazon.com or another source showing the proper track listing.' + ), + 'report_fields' => array( + 'track' => '2', + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]2.1.19[/rule]. All music torrents must represent a complete release, and may not be missing tracks (or discs in the case of a multi-disc release). [rule]2.1.19.2[/rule]. A single track (e.g., one MP3 file) cannot be uploaded on its own unless it is an officially released single. If a specific track can only be found on an album, the entire album must be uploaded in the torrent. Your torrent was reported because it was missing tracks.' - ) - ), - 'discs_missing' => array( - 'priority' => '120', - 'reason' => '6', - 'title' => 'Disc(s) Missing', - 'report_messages' => array( - 'If possible, please provide a link to Amazon.com or another source showing the proper track listing.' - ), - 'report_fields' => array( - 'track' => '0', - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]2.1.19[/rule]. All music torrents must represent a complete release, and may not be missing tracks (or discs in the case of a multi-disc release). + ) + ), + 'discs_missing' => array( + 'priority' => '120', + 'reason' => '6', + 'title' => 'Disc(s) Missing', + 'report_messages' => array( + 'If possible, please provide a link to Amazon.com or another source showing the proper track listing.' + ), + 'report_fields' => array( + 'track' => '0', + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]2.1.19[/rule]. All music torrents must represent a complete release, and may not be missing tracks (or discs in the case of a multi-disc release). [rule]2.1.19.1[/rule]. If an album is released as a multi-disc set (or box set) of CDs or vinyl discs, then it must be uploaded as a single torrent. Preferably, each individual CD rip in a multi-disc set should be organized in its own folder (see [rule]2.3.12[/rule]). Your torrent was reported because it was missing discs.' - ) - ), - 'mqa' => array( - 'priority' => '130', - 'reason' => '14', - 'title' => 'MQA Banned', - 'report_messages' => array( - 'Please show screenshot proof that this is an MQA-encoded file (unless it is explicitly stated in the Release Description).' - ), - 'extra_log' => 'MQA-encoded torrent', - 'report_fields' => array( - 'image' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '1', - 'pm' => '[rule]1.2.9[/rule]. You have uploaded material that is currently forbidden. MQA-encoded FLAC torrents are not allowed on ' . SITE_NAME .'. For more information, see [[MQA]].' - ) - ), - 'bonus_tracks' => array( - 'priority' => '90', - 'reason' => '-1', - 'title' => 'Bonus Tracks Only', - 'report_messages' => array( - 'If possible, please provide a link to Amazon.com or another source showing the proper track listing.', - 'Per rule 2.4.5, exclusive WEB-sourced bonus tracks are allowed to be uploaded separately.' - ), - 'report_fields' => array( - 'track' => '0', - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]2.1.19.3[/rule]. Bonus discs may be uploaded separately in accordance with [rule]h2.4[/rule]. Please note that individual bonus tracks cannot be uploaded without the rest of the album. Bonus tracks are not bonus discs. Enhanced audio CDs with data or video tracks must be uploaded without the non-audio tracks. If you want to share the videos or data, you may host the files off-site with a file sharing service and include the link to that service in your torrent description. + ) + ), + 'mqa' => array( + 'priority' => '130', + 'reason' => '14', + 'title' => 'MQA Banned', + 'report_messages' => array( + 'Please show screenshot proof that this is an MQA-encoded file (unless it is explicitly stated in the Release Description).' + ), + 'extra_log' => 'MQA-encoded torrent', + 'report_fields' => array( + 'image' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '1', + 'pm' => '[rule]1.2.9[/rule]. You have uploaded material that is currently forbidden. MQA-encoded FLAC torrents are not allowed on ' . SITE_NAME .'. For more information, see [[MQA]].' + ) + ), + 'bonus_tracks' => array( + 'priority' => '90', + 'reason' => '-1', + 'title' => 'Bonus Tracks Only', + 'report_messages' => array( + 'If possible, please provide a link to Amazon.com or another source showing the proper track listing.', + 'Per rule 2.4.5, exclusive WEB-sourced bonus tracks are allowed to be uploaded separately.' + ), + 'report_fields' => array( + 'track' => '0', + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]2.1.19.3[/rule]. Bonus discs may be uploaded separately in accordance with [rule]h2.4[/rule]. Please note that individual bonus tracks cannot be uploaded without the rest of the album. Bonus tracks are not bonus discs. Enhanced audio CDs with data or video tracks must be uploaded without the non-audio tracks. If you want to share the videos or data, you may host the files off-site with a file sharing service and include the link to that service in your torrent description. Your torrent was reported because it contained only bonus tracks without the full album.' - ) - ), - 'transcode' => array( - 'priority' => '250', - 'reason' => '16', - 'title' => 'Transcode', - 'report_messages' => array( - "Please list the tracks you checked, and the method used to determine the transcode.", - "If possible, please include at least one screenshot of any spectral analysis done. You may include more than one." - ), - 'report_fields' => array( - 'image' => '0', - 'track' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '2', - 'delete' => '1', - 'pm' => '[rule]2.1.2[/rule]. No transcodes or re-encodes of lossy releases are acceptable here. + ) + ), + 'transcode' => array( + 'priority' => '250', + 'reason' => '16', + 'title' => 'Transcode', + 'report_messages' => array( + "Please list the tracks you checked, and the method used to determine the transcode.", + "If possible, please include at least one screenshot of any spectral analysis done. You may include more than one." + ), + 'report_fields' => array( + 'image' => '0', + 'track' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '2', + 'delete' => '1', + 'pm' => '[rule]2.1.2[/rule]. No transcodes or re-encodes of lossy releases are acceptable here. Your torrent was reported because it contained transcoded audio files.' - ) - ), - 'low' => array( - 'priority' => '170', - 'reason' => '10', - 'title' => 'Low Bitrate', - 'report_messages' => array( - "Please tell us the actual bitrate and the software used to check." - ), - 'report_fields' => array( - 'track' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '2', - 'delete' => '1', - 'pm' => '[rule]2.1.3[/rule]. Music releases must have an average bitrate of at least 192 kbps regardless of the format. Exceptions: The following VBR encodes may go under the 192 kbps limit: LAME V2 (VBR), V1 (VBR), V0 (VBR), APS (VBR), APX (VBR), MP3 192 (VBR), and AAC ~192 (VBR) to AAC ~256 (VBR) releases. + ) + ), + 'low' => array( + 'priority' => '170', + 'reason' => '10', + 'title' => 'Low Bitrate', + 'report_messages' => array( + "Please tell us the actual bitrate and the software used to check." + ), + 'report_fields' => array( + 'track' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '2', + 'delete' => '1', + 'pm' => '[rule]2.1.3[/rule]. Music releases must have an average bitrate of at least 192 kbps regardless of the format. Exceptions: The following VBR encodes may go under the 192 kbps limit: LAME V2 (VBR), V1 (VBR), V0 (VBR), APS (VBR), APX (VBR), MP3 192 (VBR), and AAC ~192 (VBR) to AAC ~256 (VBR) releases. Your torrent was reported because it contained one or more audio files that did not meet the minimum bitrate requirement.' - ) - ), - 'mutt' => array( - 'priority' => '180', - 'reason' => '11', - 'title' => 'Mutt Rip', - 'report_messages' => array( - "Please list at least two (2) tracks which have different bitrates and/or encoders." - ), - 'report_fields' => array( - 'track' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '2', - 'delete' => '1', - 'pm' => '[rule]2.1.6[/rule]. All music torrents must be encoded with a single encoder using the same settings. + ) + ), + 'mutt' => array( + 'priority' => '180', + 'reason' => '11', + 'title' => 'Mutt Rip', + 'report_messages' => array( + "Please list at least two (2) tracks which have different bitrates and/or encoders." + ), + 'report_fields' => array( + 'track' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '2', + 'delete' => '1', + 'pm' => '[rule]2.1.6[/rule]. All music torrents must be encoded with a single encoder using the same settings. Your torrent was reported because it contained one or more audio files that were encoded by different audio encoders or with different encoder settings.' - ) - ), - 'single_track' => array( - 'priority' => '270', - 'reason' => '18', - 'title' => 'Unsplit Album Rip', - 'report_messages' => array( - "If possible, please provide a link to Amazon.com or another source showing the proper track listing.", - "This option is for uploads of CDs ripped as a single track when it should be split as on the CD.", - "This option is not to be confused with uploads of a single track, taken from a CD with multiple tracks (Tracks Missing)." - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]2.1.5[/rule]. Albums must not be ripped or uploaded as a single track. + ) + ), + 'single_track' => array( + 'priority' => '270', + 'reason' => '18', + 'title' => 'Unsplit Album Rip', + 'report_messages' => array( + "If possible, please provide a link to Amazon.com or another source showing the proper track listing.", + "This option is for uploads of CDs ripped as a single track when it should be split as on the CD.", + "This option is not to be confused with uploads of a single track, taken from a CD with multiple tracks (Tracks Missing)." + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]2.1.5[/rule]. Albums must not be ripped or uploaded as a single track. [rule]2.1.5.1[/rule]. If the tracks on the original CD were separate, you must rip them to separate files. Any unsplit FLAC rips lacking a cue sheet will be deleted outright. Any unsplit FLAC rip that includes a cue sheet will be trumpable by a properly split FLAC torrent. CDs with single tracks can be uploaded without prior splitting. Your torrent was reported because it contained a single-track rip instead of a rip consisting of separate audio files.' - ) - ), - 'tags_lots' => array( - 'priority' => '82', - 'reason' => '4', - 'title' => 'Bad Tags / No Tags at All', - 'report_messages' => array( - "Please specify which tags are missing, and whether they're missing from all tracks.", - "Ideally, you will replace this torrent with one with fixed tags and report this with the reason \"Tag Trump\"." - ), - 'report_fields' => array( - 'track' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '0', - 'pm' => "[rule]2.3.16[/rule]. Properly tag your music files. + ) + ), + 'tags_lots' => array( + 'priority' => '82', + 'reason' => '4', + 'title' => 'Bad Tags / No Tags at All', + 'report_messages' => array( + "Please specify which tags are missing, and whether they're missing from all tracks.", + "Ideally, you will replace this torrent with one with fixed tags and report this with the reason \"Tag Trump\"." + ), + 'report_fields' => array( + 'track' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '0', + 'pm' => "[rule]2.3.16[/rule]. Properly tag your music files. The Uploading Rules require that all uploads be properly tagged. Your torrent has been marked as having bad tags. It is now listed on [url=".site_url()."better.php]better.php[/url] and is eligible for trumping. You are of course free to fix this torrent yourself. Add or fix the required tags and upload the replacement torrent to the site. Then, report (RP) the older torrent using the category \"Tag Trump\" and indicate in the report comments that you have fixed the tags. Be sure to provide a link (PL) to the new replacement torrent." - ) - ), - 'folders_bad' => array( - 'priority' => '81', - 'reason' => '3', - 'title' => 'Bad Folder Names', - 'report_messages' => array( - "Please specify the issue with the folder names.", - "Ideally you will replace this torrent with one with fixed folder names and report this with the reason \"Bad Folder Name Trump\"." - ), - 'report_fields' => array(), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '0', - 'pm' => "[rule]2.3.2[/rule]. Name your directories with meaningful titles, such as \"Artist - Album (Year) - Format\". + ) + ), + 'folders_bad' => array( + 'priority' => '81', + 'reason' => '3', + 'title' => 'Bad Folder Names', + 'report_messages' => array( + "Please specify the issue with the folder names.", + "Ideally you will replace this torrent with one with fixed folder names and report this with the reason \"Bad Folder Name Trump\"." + ), + 'report_fields' => [], + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '0', + 'pm' => "[rule]2.3.2[/rule]. Name your directories with meaningful titles, such as \"Artist - Album (Year) - Format\". The Uploading Rules require that all uploads contain torrent directories with meaningful names. Your torrent has been marked as having a poorly named torrent directory. It is now listed on [url=".site_url()."better.php]better.php[/url] and is eligible for trumping. You are of course free to fix this torrent yourself. Add or fix the folder/directory name(s) and upload the replacement torrent to the site. Then, report (RP) the older torrent using the category \"Folder Trump\" and indicate in the report comments that you have fixed the directory name(s). Be sure to provide a link (PL) to the new replacement torrent." - ) - ), - 'wrong_format' => array( - 'priority' => '320', - 'reason' => '20', - 'title' => 'Wrong Specified Format', - 'report_messages' => array( - "Please specify the correct format." - ), - 'report_fields' => array( - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '0', - 'pm' => '[rule]2.1.4[/rule]. Bitrates must accurately reflect encoder presets or the average bitrate of the audio files. You are responsible for supplying correct format and bitrate information on the upload page. + ) + ), + 'wrong_format' => array( + 'priority' => '320', + 'reason' => '20', + 'title' => 'Wrong Specified Format', + 'report_messages' => array( + "Please specify the correct format." + ), + 'report_fields' => array( + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '0', + 'pm' => '[rule]2.1.4[/rule]. Bitrates must accurately reflect encoder presets or the average bitrate of the audio files. You are responsible for supplying correct format and bitrate information on the upload page. Your torrent has now been labeled using the appropriate format and bitrate.' - ) - ), - 'wrong_media' => array( - 'priority' => '330', - 'reason' => '21', - 'title' => 'Wrong Specified Media', - 'report_messages' => array( - "Please specify the correct media." - ), - 'report_fields' => array( - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '0', - 'pm' => '' - ) - ), - 'format' => array( - 'priority' => '100', - 'reason' => '5', - 'title' => 'Disallowed Format', - 'report_messages' => array( - "If applicable, list the relevant tracks." - ), - 'report_fields' => array( - 'track' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]2.1.1[/rule]. The only formats allowed for music are: + ) + ), + 'wrong_media' => array( + 'priority' => '330', + 'reason' => '21', + 'title' => 'Wrong Specified Media', + 'report_messages' => array( + "Please specify the correct media." + ), + 'report_fields' => array( + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '0', + 'pm' => '' + ) + ), + 'format' => array( + 'priority' => '100', + 'reason' => '5', + 'title' => 'Disallowed Format', + 'report_messages' => array( + "If applicable, list the relevant tracks." + ), + 'report_fields' => array( + 'track' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]2.1.1[/rule]. The only formats allowed for music are: Lossy: MP3, AAC, AC3, DTS Lossless: FLAC Your torrent was reported because it contained a disallowed format.' - ) - ), - 'bitrate' => array( - 'priority' => '150', - 'reason' => '9', - 'title' => 'Inaccurate Bitrate', - 'report_messages' => array( - "Please tell us the actual bitrate and the software used to check.", - "If the correct bitrate would make this torrent a duplicate, please report it as a dupe, and describe the mislabeling in \"Comments\".", - "If the correct bitrate would result in this torrent trumping another, please report it as a trump, and describe the mislabeling in \"Comments\"." - ), - 'report_fields' => array( - 'track' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '0', - 'pm' => '[rule]2.1.4[/rule]. Bitrates must accurately reflect encoder presets or the average bitrate of the audio files. You are responsible for supplying correct format and bitrate information on the upload page. + ) + ), + 'bitrate' => array( + 'priority' => '150', + 'reason' => '9', + 'title' => 'Inaccurate Bitrate', + 'report_messages' => array( + "Please tell us the actual bitrate and the software used to check.", + "If the correct bitrate would make this torrent a duplicate, please report it as a dupe, and describe the mislabeling in \"Comments\".", + "If the correct bitrate would result in this torrent trumping another, please report it as a trump, and describe the mislabeling in \"Comments\"." + ), + 'report_fields' => array( + 'track' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '0', + 'pm' => '[rule]2.1.4[/rule]. Bitrates must accurately reflect encoder presets or the average bitrate of the audio files. You are responsible for supplying correct format and bitrate information on the upload page. Your torrent was reported because the bitrates of one or more audio files had been misrepresented.' - ) - ), - 'source' => array( - 'priority' => '210', - 'reason' => '12', - 'title' => 'Radio/TV/FM/WEB Rip', - 'report_messages' => array( - "Please include as much information as possible to verify the report." - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '2', - 'delete' => '1', - 'pm' => '[rule]2.1.11[/rule]. Music ripped from the radio (Satellite or FM), television, the web, or podcasts are not allowed. + ) + ), + 'source' => array( + 'priority' => '210', + 'reason' => '12', + 'title' => 'Radio/TV/FM/WEB Rip', + 'report_messages' => array( + "Please include as much information as possible to verify the report." + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '2', + 'delete' => '1', + 'pm' => '[rule]2.1.11[/rule]. Music ripped from the radio (Satellite or FM), television, the web, or podcasts are not allowed. The only allowable media formats are CD, DVD, Vinyl, Soundboard, SACD, DAT, Cassette, WEB, and Blu-ray.' - ) - ), - 'discog' => array( - 'priority' => '130', - 'reason' => '7', - 'title' => 'Discography', - 'report_messages' => array( - "Please include as much information as possible to verify the report." - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]2.1.20[/rule]. User made discographies may not be uploaded. Multi-album torrents are not allowed on the site under any circumstances. That means no discographies, Pitchfork compilations, etc. If releases (e.g., CD singles) were never released as a bundled set, do not upload them together. Live Soundboard material should be uploaded as one torrent per night, per show, or per venue. Including more than one show in a torrent results in a multi-album torrent. + ) + ), + 'discog' => array( + 'priority' => '130', + 'reason' => '7', + 'title' => 'Discography', + 'report_messages' => array( + "Please include as much information as possible to verify the report." + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]2.1.20[/rule]. User made discographies may not be uploaded. Multi-album torrents are not allowed on the site under any circumstances. That means no discographies, Pitchfork compilations, etc. If releases (e.g., CD singles) were never released as a bundled set, do not upload them together. Live Soundboard material should be uploaded as one torrent per night, per show, or per venue. Including more than one show in a torrent results in a multi-album torrent. Your torrent was reported because it consisted of a discography.' - ) - ), - 'user_discog' => array( - 'priority' => '290', - 'reason' => '19', - 'title' => 'User Compilation', - 'report_messages' => array( - "Please include as much information as possible to verify the report." - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]2.1.16[/rule]. User-made compilations are not allowed. + ) + ), + 'user_discog' => array( + 'priority' => '290', + 'reason' => '19', + 'title' => 'User Compilation', + 'report_messages' => array( + "Please include as much information as possible to verify the report." + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]2.1.16[/rule]. User-made compilations are not allowed. [rule]2.1.16.1[/rule]. These are defined as compilations made by the uploader or anyone else who does not officially represent the artist or the label. Compilations must be reasonably official. User-made and unofficial multichannel mixes are also not allowed. Your torrent was reported because it was a user compilation.' - ) - ), - 'lineage' => array( - 'priority' => '190', - 'reason' => '-1', - 'title' => 'No Lineage Info', - 'report_messages' => array( - "Please list the specific information missing from the torrent (hardware, software, etc.)." - ), - 'report_fields' => array( - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '0', - 'pm' => '[rule]2.3.9[/rule]. All lossless analog rips should include clear information about source lineage. All lossless SACD digital layer analog rips and vinyl rips must include clear information about recording equipment used (see [rule]h2.8[/rule]). If you used a USB turntable for a vinyl rip, clearly indicate this in your lineage information. Also include all intermediate steps up to lossless encoding, such as the program used for mastering, sound card used, etc. Lossless analog rips missing rip information can be trumped by better documented lossless analog rips of equal or better quality. In order to trump a lossless analog rip without a lineage, this lineage must be included as a .txt or .log file within the new torrent. + ) + ), + 'lineage' => array( + 'priority' => '190', + 'reason' => '-1', + 'title' => 'No Lineage Info', + 'report_messages' => array( + "Please list the specific information missing from the torrent (hardware, software, etc.)." + ), + 'report_fields' => array( + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '0', + 'pm' => '[rule]2.3.9[/rule]. All lossless analog rips should include clear information about source lineage. All lossless SACD digital layer analog rips and vinyl rips must include clear information about recording equipment used (see [rule]h2.8[/rule]). If you used a USB turntable for a vinyl rip, clearly indicate this in your lineage information. Also include all intermediate steps up to lossless encoding, such as the program used for mastering, sound card used, etc. Lossless analog rips missing rip information can be trumped by better documented lossless analog rips of equal or better quality. In order to trump a lossless analog rip without a lineage, this lineage must be included as a .txt or .log file within the new torrent. Your torrent is now eligible for trumping by a better-sounding rip with complete lineage information.' - ) - ), - 'edited' => array( - 'priority' => '140', - 'reason' => '8', - 'title' => 'Edited Log', - 'report_messages' => array( - "Please explain exactly where you believe the log was edited.", - "The torrent will not show 'reported' on the group page, but rest assured that the report will be seen by moderators." - ), - 'report_fields' => array( - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '4', - 'delete' => '1', - 'pm' => '[rule]2.2.10.9[/rule]. No log editing is permitted. + ) + ), + 'edited' => array( + 'priority' => '140', + 'reason' => '8', + 'title' => 'Edited Log', + 'report_messages' => array( + "Please explain exactly where you believe the log was edited.", + "The torrent will not show 'reported' on the group page, but rest assured that the report will be seen by moderators." + ), + 'report_fields' => array( + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '4', + 'delete' => '1', + 'pm' => '[rule]2.2.10.9[/rule]. No log editing is permitted. [rule]2.2.10.9.1[/rule]. Forging log data is a serious misrepresentation of quality, and will result in a warning and the loss of your uploading privileges when the edited log is found. We recommend that you do not open the rip log file for any reason. However, if you must open the rip log, do not edit anything in the file for any reason. If you discover that one of your software settings is incorrect in the ripping software preferences, you must rip the CD again with the proper settings. Do not consolidate logs under any circumstances. If you must re-rip specific tracks or an entire disc and the rip results happen to have the new log appended to the original, leave them as is. Do not remove any part of either log, and never copy/paste parts of a new log over an old log. Your torrent was reported because it contained an edited log (either edited by you or someone else). For questions about your uploading privileges, you must PM the staff member who handled this log case.' - ) - ), - 'audience' => array( - 'priority' => '70', - 'reason' => '22', - 'title' => 'Audience Recording', - 'report_messages' => array( - "Please include as much information as possible to verify the report." - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]2.1.12[/rule]. No unofficial audience recordings may be uploaded. These include but are not limited to AUD (Audience), IEM (In Ear Monitor), ALD (Assistive Listening Device), Mini-Disc, and Matrix-sourced recordings (see [rule]2.6.3[/rule]). + ) + ), + 'audience' => array( + 'priority' => '70', + 'reason' => '22', + 'title' => 'Audience Recording', + 'report_messages' => array( + "Please include as much information as possible to verify the report." + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]2.1.12[/rule]. No unofficial audience recordings may be uploaded. These include but are not limited to AUD (Audience), IEM (In Ear Monitor), ALD (Assistive Listening Device), Mini-Disc, and Matrix-sourced recordings (see [rule]2.6.3[/rule]). Your torrent was reported because it was sourced from an audience recording.' - ) - ), - 'filename' => array( - 'priority' => '80', - 'reason' => '2', - 'title' => 'Bad File Names', - 'report_messages' => array( - ), - 'report_fields' => array( - 'track' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '0', - 'pm' => '[rule]2.3.11[/rule]. File names must accurately reflect the song titles. You may not have file names like 01track.mp3, 02track.mp3, etc. Torrents containing files that are named with incorrect song titles can be trumped by properly labeled torrents. Also, torrents that are sourced from the scene but do not have the "Scene" label must comply with site naming rules (no release group names in the file names, no advertisements in the file names, etc.). If all the letters in the track titles are capitalized, the torrent is trumpable. + ) + ), + 'filename' => array( + 'priority' => '80', + 'reason' => '2', + 'title' => 'Bad File Names', + 'report_messages' => array( + ), + 'report_fields' => array( + 'track' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '0', + 'pm' => '[rule]2.3.11[/rule]. File names must accurately reflect the song titles. You may not have file names like 01track.mp3, 02track.mp3, etc. Torrents containing files that are named with incorrect song titles can be trumped by properly labeled torrents. Also, torrents that are sourced from the scene but do not have the "Scene" label must comply with site naming rules (no release group names in the file names, no advertisements in the file names, etc.). If all the letters in the track titles are capitalized, the torrent is trumpable. [rule]2.3.13[/rule]. Track numbers are required in file names (e.g., "01 - TrackName.mp3"). If a torrent without track numbers in the file names is uploaded, then a torrent with the track numbers in the file names can take its place. When formatted properly, file names will sort in order by track number or playing order. Also see [rule]2.3.14[/rule]. The Uploading Rules require that all uploads contain audio tracks with accurate file names. Your torrent has been marked as having incorrect or incomplete file names. It is now listed on [url='.site_url().'better.php]better.php[/url] and is eligible for trumping. You are of course free to fix this torrent yourself. Add or fix the file names and upload the replacement torrent to the site. Then, report (RP) the older torrent using the category "Bad File Names Trump" and indicate in the report comments that you have fixed the file names. Be sure to provide a permalink (PL) to the new replacement torrent.' - ) - ), - 'skips' => array( - 'priority' => '220', - 'reason' => '13', - 'title' => 'Skips / Encode Errors', - 'report_messages' => array( - 'Please be as thorough as possible and include as much detail as you can. Refer to specific tracks and time positions to justify your report.' - ), - 'report_fields' => array( - 'track' => '2' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '1', - 'pm' => '[rule]2.1.8[/rule]. Music not sourced from vinyl must not contain pops, clicks, or skips. They will be deleted for rip/encode errors if reported. + ) + ), + 'skips' => array( + 'priority' => '220', + 'reason' => '13', + 'title' => 'Skips / Encode Errors', + 'report_messages' => array( + 'Please be as thorough as possible and include as much detail as you can. Refer to specific tracks and time positions to justify your report.' + ), + 'report_fields' => array( + 'track' => '2' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '1', + 'pm' => '[rule]2.1.8[/rule]. Music not sourced from vinyl must not contain pops, clicks, or skips. They will be deleted for rip/encode errors if reported. Your torrent was reported because one or more tracks contain encoding errors.' - ) - ), - 'rescore' => array( - 'priority' => '160', - 'reason' => '-1', - 'title' => 'Log Rescore Request', - 'report_messages' => array( - "It could help us if you say exactly why you believe this log requires rescoring.", - "For example, if it's a foreign log which needs scoring, or if the log wasn't uploaded at all." - ), - 'report_fields' => array( - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '0', - 'pm' => '[rule]2.2.10.3[/rule]. A FLAC upload with an EAC or XLD rip log that scores 100% on the log checker replaces one with a lower score... . Note: A FLAC upload with a log that scores 95% for not defeating the audio cache may be rescored to 100% following the procedure outlined in [url='.site_url().'wiki.php?action=article&id=79]this wiki[/url]. + ) + ), + 'rescore' => array( + 'priority' => '160', + 'reason' => '-1', + 'title' => 'Log Rescore Request', + 'report_messages' => array( + "It could help us if you say exactly why you believe this log requires rescoring.", + "For example, if it's a foreign log which needs scoring, or if the log wasn't uploaded at all." + ), + 'report_fields' => array( + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '0', + 'pm' => '[rule]2.2.10.3[/rule]. A FLAC upload with an EAC or XLD rip log that scores 100% on the log checker replaces one with a lower score... . Note: A FLAC upload with a log that scores 95% for not defeating the audio cache may be rescored to 100% following the procedure outlined in [url='.site_url().'wiki.php?action=article&id=79]this wiki[/url]. [rule]2.2.10.5[/rule]. XLD and EAC logs in languages other than English require a manual log checker score adjustment by staff. [rule]2.2.10.6.2[/rule]. If you created a CD range rip that has matching CRCs for test and copy, and where every track has an AccurateRip score of 2 or more, then you may submit your torrent for manual score adjustment. [rule]2.2.10.9.2[/rule]. If you find that an appended log has not been scored properly, please report the torrent and use the log rescore option. Your torrent has now been properly scored by the staff.' - ) - ), - 'lossyapproval' => array( - 'priority' => '161', - 'reason' => '-1', - 'title' => 'Lossy Master Approval Request', - 'report_messages' => array( - 'Please include as much information as possible to verify the report, including spectral analysis images.', - 'For WEB purchases, please include a link to the webstore where you obtained the album and a screenshot of your invoice.', - 'For CDs or other physical media, please include a photograph of the album next to a piece of paper with your username written on it.', - 'Anything included in the proof images field will only be viewable by staff.' - ), - 'report_fields' => array( - 'proofimages' => '2' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '0' - ) - ), - 'upload_contest' => array( - 'priority' => '162', - 'reason' => '-1', - 'title' => 'Upload Contest Approval Request', - 'report_messages' => array( - 'Please include a photograph of the CD next to a piece of paper with your username written on it.', - 'Anything included in the proof images field will only be viewable by staff.' - ), - 'report_fields' => array( - 'proofimages' => '2' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '0' - ) - ) - ), + ) + ), + 'lossyapproval' => array( + 'priority' => '161', + 'reason' => '-1', + 'title' => 'Lossy Master Approval Request', + 'report_messages' => array( + 'Please include as much information as possible to verify the report, including spectral analysis images.', + 'For WEB purchases, please include a link to the webstore where you obtained the album and a screenshot of your invoice.', + 'For CDs or other physical media, please include a photograph of the album next to a piece of paper with your username written on it.', + 'Anything included in the proof images field will only be viewable by staff.' + ), + 'report_fields' => array( + 'proofimages' => '2' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '0' + ) + ), + 'upload_contest' => array( + 'priority' => '162', + 'reason' => '-1', + 'title' => 'Upload Contest Approval Request', + 'report_messages' => array( + 'Please include a photograph of the CD next to a piece of paper with your username written on it.', + 'Anything included in the proof images field will only be viewable by staff.' + ), + 'report_fields' => array( + 'proofimages' => '2' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '0' + ) + ) + ), - '2' => array( //Applications Rules Broken - 'missing_crack' => array( - 'priority' => '70', - 'reason' => '-1', - 'title' => 'No Crack/Keygen/Patch', - 'report_messages' => array( - 'Please include as much information as possible to verify the report.', - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]4.1.2[/rule]. All applications must come with a crack, keygen, or other method of ensuring that downloaders can install them easily. App torrents with keygens, cracks, or patches that do not work or torrents missing clear installation instructions will be deleted if reported. No exceptions. + '2' => array( //Applications Rules Broken + 'missing_crack' => array( + 'priority' => '70', + 'reason' => '-1', + 'title' => 'No Crack/Keygen/Patch', + 'report_messages' => array( + 'Please include as much information as possible to verify the report.', + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]4.1.2[/rule]. All applications must come with a crack, keygen, or other method of ensuring that downloaders can install them easily. App torrents with keygens, cracks, or patches that do not work or torrents missing clear installation instructions will be deleted if reported. No exceptions. Your torrent was reported because it was missing an installation method.' - ) - ), - 'game' => array( - 'priority' => '50', - 'reason' => '-1', - 'title' => 'Game', - 'report_messages' => array( - 'Please include as much information as possible to verify the report.', - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '4', - 'delete' => '1', - 'pm' => '[rule]1.2.5[/rule]. Games of any kind. No games of any kind for PC, Mac, Linux, mobile devices, or any other platform are allowed. + ) + ), + 'game' => array( + 'priority' => '50', + 'reason' => '-1', + 'title' => 'Game', + 'report_messages' => array( + 'Please include as much information as possible to verify the report.', + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '4', + 'delete' => '1', + 'pm' => '[rule]1.2.5[/rule]. Games of any kind. No games of any kind for PC, Mac, Linux, mobile devices, or any other platform are allowed. [rule]4.1.7[/rule]. Games of any kind are prohibited (see [rule]1.2.5[/rule]). Your torrent was reported because it contained a game disc rip.' - ) - ), - 'free' => array( - 'priority' => '40', - 'reason' => '-1', - 'title' => 'Freely Available', - 'report_messages' => array( - 'Please include a link to a source of information or to the freely available app itself.', - ), - 'report_fields' => array( - 'link' => '1' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]4.1.3[/rule]. App releases must not be freely available tools. Application releases cannot be freely downloaded anywhere from any official source. Nor may you upload open source applications where the source code is available for free. + ) + ), + 'free' => array( + 'priority' => '40', + 'reason' => '-1', + 'title' => 'Freely Available', + 'report_messages' => array( + 'Please include a link to a source of information or to the freely available app itself.', + ), + 'report_fields' => array( + 'link' => '1' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]4.1.3[/rule]. App releases must not be freely available tools. Application releases cannot be freely downloaded anywhere from any official source. Nor may you upload open source applications where the source code is available for free. Your torrent was reported because it contained a freely available application.' - ) - ), - 'description' => array( - 'priority' => '80', - 'reason' => '-1', - 'title' => 'No Description', - 'report_messages' => array( - 'If possible, please provide a link to an accurate description.', - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]4.1.4[/rule]. Release descriptions for applications must contain good information about the application. You should either have a small description of the program (either taken from its web site or from an NFO file) or a link to the information — but ideally both. Torrents missing this information will be deleted when reported. + ) + ), + 'description' => array( + 'priority' => '80', + 'reason' => '-1', + 'title' => 'No Description', + 'report_messages' => array( + 'If possible, please provide a link to an accurate description.', + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]4.1.4[/rule]. Release descriptions for applications must contain good information about the application. You should either have a small description of the program (either taken from its web site or from an NFO file) or a link to the information — but ideally both. Torrents missing this information will be deleted when reported. Your torrent was reported because it lacked adequate release information.' - ) - ), - 'pack' => array( - 'priority' => '20', - 'reason' => '-1', - 'title' => 'Archived Pack', - 'report_messages' => array( - 'Please include as much information as possible to verify the report.' - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]2.1.18[/rule]. Sound Sample Packs must be uploaded as applications. + ) + ), + 'pack' => array( + 'priority' => '20', + 'reason' => '-1', + 'title' => 'Archived Pack', + 'report_messages' => array( + 'Please include as much information as possible to verify the report.' + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]2.1.18[/rule]. Sound Sample Packs must be uploaded as applications. [rule]4.1.9[/rule]. Sound sample packs, template collections, and font collections are allowed if they are official releases, not freely available, and unarchived. Sound sample packs, template collections, and font collections must be official compilations and they must not be uploaded as an archive. The files contained inside the torrent must not be archived so that users can see what the pack contains. That means if sound sample packs are in WAV format, they must be uploaded as WAV. If the font collection, template collection, or sound sample pack was originally released as an archive, you must unpack the files before uploading them in a torrent. None of the contents in these packs and collections may be freely available. Your torrent was reported because it was an archived collection.' - ) - ), - 'collection' => array( - 'priority' => '30', - 'reason' => '-1', - 'title' => 'Collection of Cracks', - 'report_messages' => array( - 'Please include as much information as possible to verify the report.' - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]4.1.11[/rule]. Collections of cracks, keygens or serials are not allowed. The crack, keygen, or serial for an application must be in a torrent with its corresponding application. It cannot be uploaded separately from the application. + ) + ), + 'collection' => array( + 'priority' => '30', + 'reason' => '-1', + 'title' => 'Collection of Cracks', + 'report_messages' => array( + 'Please include as much information as possible to verify the report.' + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]4.1.11[/rule]. Collections of cracks, keygens or serials are not allowed. The crack, keygen, or serial for an application must be in a torrent with its corresponding application. It cannot be uploaded separately from the application. Your torrent was reported because it contained a collection of serials, keygens, or cracks.' - ) - ), - 'hack' => array( - 'priority' => '60', - 'reason' => '-1', - 'title' => 'Hacking Tool', - 'report_messages' => array( - 'Please include as much information as possible to verify the report.', - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]4.1.12[/rule]. Torrents containing hacking or cracking tools are prohibited. + ) + ), + 'hack' => array( + 'priority' => '60', + 'reason' => '-1', + 'title' => 'Hacking Tool', + 'report_messages' => array( + 'Please include as much information as possible to verify the report.', + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]4.1.12[/rule]. Torrents containing hacking or cracking tools are prohibited. Your torrent was reported because it contained a hacking tool.' - ) - ), - 'virus' => array( - 'priority' => '60', - 'reason' => '-1', - 'title' => 'Contains Virus', - 'report_messages' => array( - 'Please include as much information as possible to verify the report. Please also double-check that your virus scanner is not incorrectly identifying a keygen or crack as a virus.', - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]4.1.14[/rule]. All applications must be complete. + ) + ), + 'virus' => array( + 'priority' => '60', + 'reason' => '-1', + 'title' => 'Contains Virus', + 'report_messages' => array( + 'Please include as much information as possible to verify the report. Please also double-check that your virus scanner is not incorrectly identifying a keygen or crack as a virus.', + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]4.1.14[/rule]. All applications must be complete. The torrent was determined to be infected with a virus or trojan. In the future, please scan all potential uploads with an antivirus program such as AVG, Avast, or MS Security Essentials. Your torrent was reported because it contained a virus or trojan.' - ) - ), - 'notwork' => array( - 'priority' => '60', - 'reason' => '-1', - 'title' => 'Not Working', - 'report_messages' => array( - 'Please include as much information as possible to verify the report.', - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '1', - 'pm' => '[rule]4.1.14[/rule]. All applications must be complete. + ) + ), + 'notwork' => array( + 'priority' => '60', + 'reason' => '-1', + 'title' => 'Not Working', + 'report_messages' => array( + 'Please include as much information as possible to verify the report.', + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '1', + 'pm' => '[rule]4.1.14[/rule]. All applications must be complete. This program was determined to be not fully functional. Your torrent was reported because it contained a program that did not work or no longer works.' - ) - ) - ), + ) + ) + ), - '3' => array( //Ebook Rules Broken - 'unrelated' => array( - 'priority' => '270', - 'reason' => '-1', - 'title' => 'Ebook Collection', - 'report_messages' => array( - 'Please include as much information as possible to verify the report.' - ), - 'report_fields' => array( - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '1', - 'pm' => '[rule]6.5[/rule]. Collections/packs of ebooks are prohibited, even if each title is somehow related to other ebook titles in some way. All ebooks must be uploaded individually and cannot be archived (users must be able to see the ebook format in the torrent). + '3' => array( //Ebook Rules Broken + 'unrelated' => array( + 'priority' => '270', + 'reason' => '-1', + 'title' => 'Ebook Collection', + 'report_messages' => array( + 'Please include as much information as possible to verify the report.' + ), + 'report_fields' => array( + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '1', + 'pm' => '[rule]6.5[/rule]. Collections/packs of ebooks are prohibited, even if each title is somehow related to other ebook titles in some way. All ebooks must be uploaded individually and cannot be archived (users must be able to see the ebook format in the torrent). Your torrent was reported because it contained a collection or pack of ebooks.' - ) - ) - ), + ) + ) + ), - '4' => array( //Audiobook Rules Broken - 'skips' => array( - 'priority' => '210', - 'reason' => '13', - 'title' => 'Skips / Encode Errors', - 'report_messages' => array( - 'Please be as thorough as possible and include as much detail as you can. Refer to specific tracks and time positions to justify your report.' - ), - 'report_fields' => array( - 'track' => '2' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '0', - 'delete' => '1', - 'pm' => '[rule]2.1.8[/rule]. Music not sourced from vinyl must not contain pops, clicks, or skips. They will be deleted for rip/encode errors if reported. + '4' => array( //Audiobook Rules Broken + 'skips' => array( + 'priority' => '210', + 'reason' => '13', + 'title' => 'Skips / Encode Errors', + 'report_messages' => array( + 'Please be as thorough as possible and include as much detail as you can. Refer to specific tracks and time positions to justify your report.' + ), + 'report_fields' => array( + 'track' => '2' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '0', + 'delete' => '1', + 'pm' => '[rule]2.1.8[/rule]. Music not sourced from vinyl must not contain pops, clicks, or skips. They will be deleted for rip/encode errors if reported. Your torrent was reported because one or more audiobook tracks contain encoding errors.' - ) - ) - ), + ) + ) + ), - '5' => array( //E-Learning videos Rules Broken - 'dissallowed' => array( - 'priority' => '20', - 'reason' => '-1', - 'title' => 'Disallowed Topic', - 'report_messages' => array( - 'Please include as much information as possible to verify the report.' - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]7.3[/rule]. Tutorials on how to use musical instruments, vocal training, producing music, or otherwise learning the theory and practice of music are the only allowed topics. No material outside of these topics is allowed. For example, instruction videos about Kung Fu training, dance lessons, beer brewing, or photography are not permitted here. What is considered allowable under these topics is ultimately at the discretion of the staff. + '5' => array( //E-Learning videos Rules Broken + 'dissallowed' => array( + 'priority' => '20', + 'reason' => '-1', + 'title' => 'Disallowed Topic', + 'report_messages' => array( + 'Please include as much information as possible to verify the report.' + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]7.3[/rule]. Tutorials on how to use musical instruments, vocal training, producing music, or otherwise learning the theory and practice of music are the only allowed topics. No material outside of these topics is allowed. For example, instruction videos about Kung Fu training, dance lessons, beer brewing, or photography are not permitted here. What is considered allowable under these topics is ultimately at the discretion of the staff. Your torrent was reported because it contained a video that has no relevance to the allowed music-related topics on the site.' - ) - ) - ), + ) + ) + ), - '6' => array( //Comedy Rules Broken - 'talkshow' => array( - 'priority' => '270', - 'reason' => '-1', - 'title' => 'Talkshow/Podcast', - 'report_messages' => array( - 'Please include as much information as possible to verify the report.' - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '1', - 'delete' => '1', - 'pm' => '[rule]3.3[/rule]. No radio talk shows or podcasts are allowed. Those recordings do not belong in any torrent category. + '6' => array( //Comedy Rules Broken + 'talkshow' => array( + 'priority' => '270', + 'reason' => '-1', + 'title' => 'Talkshow/Podcast', + 'report_messages' => array( + 'Please include as much information as possible to verify the report.' + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '1', + 'delete' => '1', + 'pm' => '[rule]3.3[/rule]. No radio talk shows or podcasts are allowed. Those recordings do not belong in any torrent category. Your torrent was reported because it contained audio files sourced from a talk show or podcast.' - ) - ) - ), + ) + ) + ), - '7' => array( //Comics Rules Broken - 'titles' => array( - 'priority' => '180', - 'reason' => '-1', - 'title' => 'Multiple Comic Titles', - 'report_messages' => array( - 'Please include as much information as possible to verify the report.' - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '', - 'delete' => '1', - 'pm' => '[rule]5.2.3[/rule]. Collections may not span more than one comic title. You may not include multiple, different comic titles in a single collection, e.g., "The Amazing Spider-Man #1" and "The Incredible Hulk #1." + '7' => array( //Comics Rules Broken + 'titles' => array( + 'priority' => '180', + 'reason' => '-1', + 'title' => 'Multiple Comic Titles', + 'report_messages' => array( + 'Please include as much information as possible to verify the report.' + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '', + 'delete' => '1', + 'pm' => '[rule]5.2.3[/rule]. Collections may not span more than one comic title. You may not include multiple, different comic titles in a single collection, e.g., "The Amazing Spider-Man #1" and "The Incredible Hulk #1." Your torrent was reported because it contained comics from multiple unrelated series.' - ) - ), - 'volumes' => array( - 'priority' => '190', - 'reason' => '-1', - 'title' => 'Multiple Volumes', - 'report_messages' => array( - 'Please include as much information as possible to verify the report.' - ), - 'report_fields' => array( - 'link' => '0' - ), - 'resolve_options' => array( - 'upload' => '0', - 'warn' => '', - 'delete' => '1', - 'pm' => '[rule]5.2.6[/rule]. Torrents spanning multiple volumes are too large and must be uploaded as separate volumes. + ) + ), + 'volumes' => array( + 'priority' => '190', + 'reason' => '-1', + 'title' => 'Multiple Volumes', + 'report_messages' => array( + 'Please include as much information as possible to verify the report.' + ), + 'report_fields' => array( + 'link' => '0' + ), + 'resolve_options' => array( + 'upload' => '0', + 'warn' => '', + 'delete' => '1', + 'pm' => '[rule]5.2.6[/rule]. Torrents spanning multiple volumes are too large and must be uploaded as separate volumes. Your torrent was reported because it contained multiple comic volumes.' - ) - ) - ) - ); + ) + ) + ) + ); diff --git a/sections/reportsv2/header.php b/sections/reportsv2/header.php index 0196a65f5..ca2d6707c 100644 --- a/sections/reportsv2/header.php +++ b/sections/reportsv2/header.php @@ -1,13 +1,13 @@ - diff --git a/sections/reportsv2/index.php b/sections/reportsv2/index.php index 72c674418..231781abe 100644 --- a/sections/reportsv2/index.php +++ b/sections/reportsv2/index.php @@ -1,4 +1,4 @@ - diff --git a/sections/reportsv2/report.php b/sections/reportsv2/report.php index e46ea3bbf..21b1d3a7c 100644 --- a/sections/reportsv2/report.php +++ b/sections/reportsv2/report.php @@ -8,148 +8,148 @@ //If we're not coming from torrents.php, check we're being returned because of an error. if (!isset($_GET['id']) || !is_number($_GET['id'])) { - if (!isset($Err)) { - error(404); - } + if (!isset($Err)) { + error(404); + } } else { - $TorrentID = $_GET['id']; - $DB->query(" - SELECT tg.CategoryID, t.GroupID - FROM torrents_group AS tg - LEFT JOIN torrents AS t ON t.GroupID = tg.ID - WHERE t.ID = " . $_GET['id']); - list($CategoryID, $GroupID) = $DB->next_record(); - if (empty($CategoryID) || empty($GroupID)) { - // Deleted torrent - header("Location: log.php?search=Torrent+" . $TorrentID); - die(); - } - $Artists = Artists::get_artist($GroupID); - $TorrentCache = get_group_info($GroupID, true); - $GroupDetails = $TorrentCache[0]; - $TorrentList = $TorrentCache[1]; - // Resolve the torrentlist to the one specific torrent being reported - foreach ($TorrentList as &$Torrent) { - // Remove unneeded entries - if ($Torrent['ID'] != $TorrentID) { - unset($TorrentList[$Torrent['ID']]); - } - } - // Group details - list($WikiBody, $WikiImage, $GroupID, $GroupName, $GroupYear, - $GroupRecordLabel, $GroupCatalogueNumber, $ReleaseType, $GroupCategoryID, - $GroupTime, $GroupVanityHouse, $TorrentTags, $TorrentTagIDs, $TorrentTagUserIDs, - $TagPositiveVotes, $TagNegativeVotes, $GroupFlags) = array_values($GroupDetails); + $TorrentID = $_GET['id']; + $DB->query(" + SELECT tg.CategoryID, t.GroupID + FROM torrents_group AS tg + LEFT JOIN torrents AS t ON t.GroupID = tg.ID + WHERE t.ID = " . $_GET['id']); + list($CategoryID, $GroupID) = $DB->next_record(); + if (empty($CategoryID) || empty($GroupID)) { + // Deleted torrent + header("Location: log.php?search=Torrent+" . $TorrentID); + die(); + } + $Artists = Artists::get_artist($GroupID); + $TorrentCache = get_group_info($GroupID, true); + $GroupDetails = $TorrentCache[0]; + $TorrentList = $TorrentCache[1]; + // Resolve the torrentlist to the one specific torrent being reported + foreach ($TorrentList as &$Torrent) { + // Remove unneeded entries + if ($Torrent['ID'] != $TorrentID) { + unset($TorrentList[$Torrent['ID']]); + } + } + // Group details + list($WikiBody, $WikiImage, $GroupID, $GroupName, $GroupYear, + $GroupRecordLabel, $GroupCatalogueNumber, $ReleaseType, $GroupCategoryID, + $GroupTime, $GroupVanityHouse, $TorrentTags, $TorrentTagIDs, $TorrentTagUserIDs, + $TagPositiveVotes, $TagNegativeVotes, $GroupFlags) = array_values($GroupDetails); - $DisplayName = $GroupName; - $AltName = $GroupName; // Goes in the alt text of the image - $Title = $GroupName; // goes in - $WikiBody = Text::full_format($WikiBody); + $DisplayName = $GroupName; + $AltName = $GroupName; // Goes in the alt text of the image + $Title = $GroupName; // goes in <title> + $WikiBody = Text::full_format($WikiBody); - //Get the artist name, group name etc. - $Artists = Artists::get_artist($GroupID); - if ($Artists) { - $DisplayName = '<span dir="ltr">' . Artists::display_artists($Artists, true) . "<a href=\"torrents.php?torrentid=$TorrentID\">$DisplayName</a></span>"; - $AltName = display_str(Artists::display_artists($Artists, false)) . $AltName; - $Title = $AltName; - } - if ($GroupYear > 0) { - $DisplayName .= " [$GroupYear]"; - $AltName .= " [$GroupYear]"; - $Title .= " [$GroupYear]"; - } - if ($GroupVanityHouse) { - $DisplayName .= ' [Vanity House]'; - $AltName .= ' [Vanity House]'; - } - if ($GroupCategoryID == 1) { - $DisplayName .= ' [' . $ReleaseTypes[$ReleaseType] . ']'; - $AltName .= ' [' . $ReleaseTypes[$ReleaseType] . ']'; - } + //Get the artist name, group name etc. + $Artists = Artists::get_artist($GroupID); + if ($Artists) { + $DisplayName = '<span dir="ltr">' . Artists::display_artists($Artists, true) . "<a href=\"torrents.php?torrentid=$TorrentID\">$DisplayName</a></span>"; + $AltName = display_str(Artists::display_artists($Artists, false)) . $AltName; + $Title = $AltName; + } + if ($GroupYear > 0) { + $DisplayName .= " [$GroupYear]"; + $AltName .= " [$GroupYear]"; + $Title .= " [$GroupYear]"; + } + if ($GroupVanityHouse) { + $DisplayName .= ' [Vanity House]'; + $AltName .= ' [Vanity House]'; + } + if ($GroupCategoryID == 1) { + $DisplayName .= ' [' . $ReleaseTypes[$ReleaseType] . ']'; + $AltName .= ' [' . $ReleaseTypes[$ReleaseType] . ']'; + } } View::show_header('Report', 'reportsv2,browse,torrent,bbcode,recommend'); ?> <div class="thin"> - <div class="header"> - <h2>Report a torrent</h2> - </div> - <div class="header"> - <h3><?=$DisplayName?></h3> - </div> - <div class="thin"> - <table class="torrent_table details<?=($GroupFlags['IsSnatched'] ? ' snatched' : '')?>" id="torrent_details"> - <tr class="colhead_dark"> - <td width="80%"><strong>Reported torrent</strong></td> - <td><strong>Size</strong></td> - <td class="sign snatches"><img src="static/styles/<?=($LoggedUser['StyleName'])?>/images/snatched.png" class="tooltip" alt="Snatches" title="Snatches" /></td> - <td class="sign seeders"><img src="static/styles/<?=($LoggedUser['StyleName'])?>/images/seeders.png" class="tooltip" alt="Seeders" title="Seeders" /></td> - <td class="sign leechers"><img src="static/styles/<?=($LoggedUser['StyleName'])?>/images/leechers.png" class="tooltip" alt="Leechers" title="Leechers" /></td> - </tr> - <?php - build_torrents_table($Cache, $DB, $LoggedUser, $GroupID, $GroupName, $GroupCategoryID, $ReleaseType, $TorrentList, $Types); - ?> - </table> - </div> + <div class="header"> + <h2>Report a torrent</h2> + </div> + <div class="header"> + <h3><?=$DisplayName?></h3> + </div> + <div class="thin"> + <table class="torrent_table details<?=($GroupFlags['IsSnatched'] ? ' snatched' : '')?>" id="torrent_details"> + <tr class="colhead_dark"> + <td width="80%"><strong>Reported torrent</strong></td> + <td><strong>Size</strong></td> + <td class="sign snatches"><img src="static/styles/<?=($LoggedUser['StyleName'])?>/images/snatched.png" class="tooltip" alt="Snatches" title="Snatches" /></td> + <td class="sign seeders"><img src="static/styles/<?=($LoggedUser['StyleName'])?>/images/seeders.png" class="tooltip" alt="Seeders" title="Seeders" /></td> + <td class="sign leechers"><img src="static/styles/<?=($LoggedUser['StyleName'])?>/images/leechers.png" class="tooltip" alt="Leechers" title="Leechers" /></td> + </tr> + <?php + build_torrents_table($Cache, $DB, $LoggedUser, $GroupID, $GroupName, $GroupCategoryID, $ReleaseType, $TorrentList, $Types); + ?> + </table> + </div> - <form class="create_form" name="report" action="reportsv2.php?action=takereport" enctype="multipart/form-data" method="post" id="reportform"> - <div> - <input type="hidden" name="submit" value="true" /> - <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> - <input type="hidden" name="torrentid" value="<?=$TorrentID?>" /> - <input type="hidden" name="categoryid" value="<?=$CategoryID?>" /> - </div> + <form class="create_form" name="report" action="reportsv2.php?action=takereport" enctype="multipart/form-data" method="post" id="reportform"> + <div> + <input type="hidden" name="submit" value="true" /> + <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> + <input type="hidden" name="torrentid" value="<?=$TorrentID?>" /> + <input type="hidden" name="categoryid" value="<?=$CategoryID?>" /> + </div> - <h3>Report Information</h3> - <div class="box pad"> - <table class="layout"> - <tr> - <td class="label">Reason:</td> - <td> - <select id="type" name="type" onchange="ChangeReportType();"> -<? - if (!empty($Types[$CategoryID])) { - $TypeList = $Types['master'] + $Types[$CategoryID]; - $Priorities = array(); - foreach ($TypeList as $Key => $Value) { - $Priorities[$Key] = $Value['priority']; - } - array_multisort($Priorities, SORT_ASC, $TypeList); - } else { - $TypeList = $Types['master']; - } - foreach ($TypeList as $Type => $Data) { - ?> - <option value="<?=($Type)?>"><?=($Data['title'])?></option> -<? } ?> - </select> - </td> - </tr> - </table> - <p>Fields that contain lists of values (for example, listing more than one track number) should be separated by a space.</p> - <br /> - <p><strong>Following the below report type specific guidelines will help the moderators deal with your report in a timely fashion. </strong></p> - <br /> + <h3>Report Information</h3> + <div class="box pad"> + <table class="layout"> + <tr> + <td class="label">Reason:</td> + <td> + <select id="type" name="type" onchange="ChangeReportType();"> +<?php + if (!empty($Types[$CategoryID])) { + $TypeList = $Types['master'] + $Types[$CategoryID]; + $Priorities = []; + foreach ($TypeList as $Key => $Value) { + $Priorities[$Key] = $Value['priority']; + } + array_multisort($Priorities, SORT_ASC, $TypeList); + } else { + $TypeList = $Types['master']; + } + foreach ($TypeList as $Type => $Data) { + ?> + <option value="<?=($Type)?>"><?=($Data['title'])?></option> +<?php } ?> + </select> + </td> + </tr> + </table> + <p>Fields that contain lists of values (for example, listing more than one track number) should be separated by a space.</p> + <br /> + <p><strong>Following the below report type specific guidelines will help the moderators deal with your report in a timely fashion. </strong></p> + <br /> - <div id="dynamic_form"> -<? - /* - * THIS IS WHERE SEXY AJAX COMES IN - * The following malarky is needed so that if you get sent back here, the fields are filled in. - */ - ?> - <input id="sitelink" type="hidden" name="sitelink" size="50" value="<?=(!empty($_POST['sitelink']) ? display_str($_POST['sitelink']) : '')?>" /> - <input id="image" type="hidden" name="image" size="50" value="<?=(!empty($_POST['image']) ? display_str($_POST['image']) : '')?>" /> - <input id="track" type="hidden" name="track" size="8" value="<?=(!empty($_POST['track']) ? display_str($_POST['track']) : '')?>" /> - <input id="link" type="hidden" name="link" size="50" value="<?=(!empty($_POST['link']) ? display_str($_POST['link']) : '')?>" /> - <input id="extra" type="hidden" name="extra" value="<?=(!empty($_POST['extra']) ? display_str($_POST['extra']) : '')?>" /> + <div id="dynamic_form"> +<?php + /* + * THIS IS WHERE SEXY AJAX COMES IN + * The following malarky is needed so that if you get sent back here, the fields are filled in. + */ + ?> + <input id="sitelink" type="hidden" name="sitelink" size="50" value="<?=(!empty($_POST['sitelink']) ? display_str($_POST['sitelink']) : '')?>" /> + <input id="image" type="hidden" name="image" size="50" value="<?=(!empty($_POST['image']) ? display_str($_POST['image']) : '')?>" /> + <input id="track" type="hidden" name="track" size="8" value="<?=(!empty($_POST['track']) ? display_str($_POST['track']) : '')?>" /> + <input id="link" type="hidden" name="link" size="50" value="<?=(!empty($_POST['link']) ? display_str($_POST['link']) : '')?>" /> + <input id="extra" type="hidden" name="extra" value="<?=(!empty($_POST['extra']) ? display_str($_POST['extra']) : '')?>" /> - <script type="text/javascript">ChangeReportType();</script> - </div> - </div> - <input type="submit" value="Submit report" /> - </form> + <script type="text/javascript">ChangeReportType();</script> + </div> + </div> + <input type="submit" value="Submit report" /> + </form> </div> <?php View::show_footer(); diff --git a/sections/reportsv2/reports.php b/sections/reportsv2/reports.php index d8dece341..f85422fb9 100644 --- a/sections/reportsv2/reports.php +++ b/sections/reportsv2/reports.php @@ -1,27 +1,27 @@ -<? +<?php /* * This is the outline page for auto reports. It calls the AJAX functions * that actually populate the page and shows the proper header and footer. * The important function is AddMore(). */ if (!check_perms('admin_reports')) { - error(403); + error(403); } View::show_header('Reports V2!', 'reportsv2'); ?> <div class="header"> - <h2>New reports, auto assigned!</h2> -<? include('header.php'); ?> + <h2>New reports, auto assigned!</h2> +<?php include('header.php'); ?> </div> <div class="buttonbox pad center"> - <input type="button" onclick="AddMore();" value="Add more" /> <input type="text" name="repop_amount" id="repop_amount" size="2" value="10" /> - | <span class="tooltip" title="Changes whether to automatically replace resolved ones with new ones"><input type="checkbox" checked="checked" id="dynamic" /> <label for="dynamic">Dynamic</label></span> - | <span class="tooltip" title="Resolves *all* checked reports with their respective resolutions"><input type="button" onclick="MultiResolve();" value="Multi-resolve" /></span> - | <span class="tooltip" title="Unclaim all of the reports currently displayed"><input type="button" onclick="GiveBack();" value="Unclaim all" /></span> + <input type="button" onclick="AddMore();" value="Add more" /> <input type="text" name="repop_amount" id="repop_amount" size="2" value="10" /> + | <span class="tooltip" title="Changes whether to automatically replace resolved ones with new ones"><input type="checkbox" checked="checked" id="dynamic" /> <label for="dynamic">Dynamic</label></span> + | <span class="tooltip" title="Resolves *all* checked reports with their respective resolutions"><input type="button" onclick="MultiResolve();" value="Multi-resolve" /></span> + | <span class="tooltip" title="Unclaim all of the reports currently displayed"><input type="button" onclick="GiveBack();" value="Unclaim all" /></span> </div> <div id="all_reports" style="width: 80%; margin-left: auto; margin-right: auto;"> </div> -<? +<?php View::show_footer(); ?> diff --git a/sections/reportsv2/search.php b/sections/reportsv2/search.php index 938949207..94133f939 100644 --- a/sections/reportsv2/search.php +++ b/sections/reportsv2/search.php @@ -1,190 +1,193 @@ -<? +<?php if (!check_perms('admin_reports')) { - error(403); + error(403); } View::show_header('Reports V2', 'reportsv2'); $report_name_cache = []; foreach ($ReportCategories as $label => $key) { - foreach (array_keys($Types[$label]) as $type) { - $report_name_cache[$type] = $Types[$label][$type]['title'] . " ($key)"; - } + foreach (array_keys($Types[$label]) as $type) { + $report_name_cache[$type] = $Types[$label][$type]['title'] . " ($key)"; + } } if (isset($_GET['report-type'])) { - foreach ($_GET['report-type'] as $t) { - if (array_key_exists($t, $report_name_cache)) { - $filter['report-type'][] = $t; - } - } + foreach ($_GET['report-type'] as $t) { + if (array_key_exists($t, $report_name_cache)) { + $filter['report-type'][] = $t; + } + } } foreach(['reporter', 'handler', 'uploader'] as $role) { - if (isset($_GET[$role]) && preg_match('/([\w.-]+)/', $_GET[$role], $match)) { - $filter[$role] = $match[1]; - } + if (isset($_GET[$role]) && preg_match('/([\w.-]+)/', $_GET[$role], $match)) { + $filter[$role] = $match[1]; + } } if (isset($_GET['torrent'])) { - if (preg_match('/^\s*(\d+)\s*$/', $_GET['torrent'], $match)) { - $filter['torrent'] = $match[1]; - } - elseif (preg_match('#^https?://[^/]+/torrents\.php.*torrentid=(\d+)#', $_GET['torrent'], $match)) { - $filter['torrent'] = $match[1]; - } + if (preg_match('/^\s*(\d+)\s*$/', $_GET['torrent'], $match)) { + $filter['torrent'] = $match[1]; + } + elseif (preg_match('#^https?://[^/]+/torrents\.php.*torrentid=(\d+)#', $_GET['torrent'], $match)) { + $filter['torrent'] = $match[1]; + } } if (isset($_GET['group'])) { - if (preg_match('/^\s*(\d+)\s*$/', $_GET['group'], $match)) { - $filter['group'] = $match[1]; - } - elseif (preg_match('#^https?://[^/]+/torrents\.php.*[?&]id=(\d+)#', $_GET['group'], $match)) { - $filter['group'] = $match[1]; - } + if (preg_match('/^\s*(\d+)\s*$/', $_GET['group'], $match)) { + $filter['group'] = $match[1]; + } + elseif (preg_match('#^https?://[^/]+/torrents\.php.*[?&]id=(\d+)#', $_GET['group'], $match)) { + $filter['group'] = $match[1]; + } } if (isset($_GET['dt-from']) && preg_match('/(\d\d\d\d-\d\d-\d\d)/', $_GET['dt-from'], $match)) { - $filter['dt-from'] = $match[1]; - $dt_from = $match[1]; + $filter['dt-from'] = $match[1]; + $dt_from = $match[1]; } if (isset($_GET['dt-until']) && preg_match('/(\d\d\d\d-\d\d-\d\d)/', $_GET['dt-until'], $match)) { - $filter['dt-until'] = $match[1]; - $dt_until = $match[1]; + $filter['dt-until'] = $match[1]; + $dt_until = $match[1]; } if (isset($filter)) { - $filter['page'] = (isset($_GET['page']) && preg_match('/(\d+)/', $_GET['page'], $match)) - ? $match[1] : 1; - list ($Results, $Total) = \Gazelle\Report::search(G::$DB, $filter); + $filter['page'] = (isset($_GET['page']) && preg_match('/(\d+)/', $_GET['page'], $match)) + ? $match[1] : 1; + list ($Results, $Total) = \Gazelle\Report::search(G::$DB, $filter); } if (!isset($dt_from)) { - $dt_from = date('Y-m-d', strtotime(date('Y-m-d', strtotime(date('Y-m-d'))) . '-1 month')); + $dt_from = date('Y-m-d', strtotime(date('Y-m-d', strtotime(date('Y-m-d'))) . '-1 month')); } if (!isset($dt_until)) { - $dt_until = date('Y-m-d'); + $dt_until = date('Y-m-d'); } ?> <div class="header"> - <h2>Search Reports</h2> -<? include('header.php'); ?> + <h2>Search Reports</h2> +<?php include('header.php'); ?> </div> -<? +<?php if (isset($Results)) { - $Page = max(1, isset($_GET['page']) ? intval($_GET['page']) : 1); - $Pages = Format::get_pages($Page, $Total, TORRENTS_PER_PAGE); + $Page = max(1, isset($_GET['page']) ? intval($_GET['page']) : 1); + $Pages = Format::get_pages($Page, $Total, TORRENTS_PER_PAGE); ?> <div class="linkbox"> - <?= $Pages ?> + <?= $Pages ?> </div> <div class="thin box pad"> - <table> - <thead> - <tr> - <td>Report</td> - <td>Uploaded by</td> - <td>Reported by</td> - <td>Handled by</td> - <td>Torrent</td> - <td>Report type</td> - <td width="120px">Date reported</td> - </tr> - </thead> - <tbody> -<? - $user_cache = []; + <table> + <thead> + <tr> + <td>Report</td> + <td>Uploaded by</td> + <td>Reported by</td> + <td>Handled by</td> + <td>Torrent</td> + <td>Report type</td> + <td width="120px">Date reported</td> + </tr> + </thead> + <tbody> +<?php + $user_cache = []; - foreach ($Results as $r) { - if (!array_key_exists($r['UserID'], $user_cache)) { - $user_cache[$r['UserID']] = Users::format_username($r['UserID']); - } - if (!array_key_exists($r['ReporterID'], $user_cache)) { - $user_cache[$r['ReporterID']] = Users::format_username($r['ReporterID']); - } - if (!array_key_exists($r['ResolverID'], $user_cache)) { - $user_cache[$r['ResolverID']] = $r['ResolverID'] - ? Users::format_username($r['ResolverID']) - : '<i>unclaimed</i>'; - } - if ($r['GroupID']) { - $name = Artists::display_artists(Artists::get_artist($r['GroupID'])) - . sprintf('<a href=/torrents.php?id=%d&torrentid=%d#torrent%d>%s</a>', - $r['GroupID'], $r['TorrentID'], $r['TorrentID'], display_str($r['Name']) - ) - . " [" . $r['Year'] . ']'; - } - else { - $name = $r['Name']; - } + foreach ($Results as $r) { + if (!array_key_exists($r['UserID'], $user_cache)) { + $user_cache[$r['UserID']] = Users::format_username($r['UserID']); + } + if (!array_key_exists($r['ReporterID'], $user_cache)) { + $user_cache[$r['ReporterID']] = Users::format_username($r['ReporterID']); + } + if (!array_key_exists($r['ResolverID'], $user_cache)) { + $user_cache[$r['ResolverID']] = $r['ResolverID'] + ? Users::format_username($r['ResolverID']) + : '<i>unclaimed</i>'; + } + if ($r['GroupID']) { + $name = Artists::display_artists(Artists::get_artist($r['GroupID'])) + . sprintf('<a href=/torrents.php?id=%d&torrentid=%d#torrent%d>%s</a>', + $r['GroupID'], $r['TorrentID'], $r['TorrentID'], display_str($r['Name']) + ) + . " [" . $r['Year'] . ']'; + } + else { + $name = $r['Name']; + } ?> - <tr> - <td align="right"><a href="/reportsv2.php?view=report&id=<?= $r['ID'] ?>"><?= $r['ID'] ?></a></td> - <td><?= $r['UserID'] ? $user_cache[$r['UserID']] : '<i>unknown</i>' ?></td> - <td><?= $user_cache[$r['ReporterID']] ?></td> - <td><?= $user_cache[$r['ResolverID']] ?></td> - <td><?= $name ?></td> - <td><?= $report_name_cache[$r['Type']] ?></td> - <td><?= time_diff($r['ReportedTime']) ?></td> - </tr> -<? } ?> - </tbody> - </table> + <tr> + <td align="right"><a href="/reportsv2.php?view=report&id=<?= $r['ID'] ?>"><?= $r['ID'] ?></a></td> + <td><?= $r['UserID'] ? $user_cache[$r['UserID']] : '<i>unknown</i>' ?></td> + <td><?= $user_cache[$r['ReporterID']] ?></td> + <td><?= $user_cache[$r['ResolverID']] ?></td> + <td><?= $name ?></td> + <td><?= $report_name_cache[$r['Type']] ?></td> + <td><?= time_diff($r['ReportedTime']) ?></td> + </tr> +<?php + } ?> + </tbody> + </table> </div> <div class="linkbox"> - <?= $Pages ?> + <?= $Pages ?> </div> <br /> -<? } ?> +<?php +} ?> <div class="thin box pad"> - <form method="get" action="/reportsv2.php"> - <table> - <tr> - <td width="150px">Reported by</td> - <td><input type="text" name="reporter" size="20" value="<?= isset($_GET['reporter']) ? $_GET['reporter'] : '' ?>" /></td> - </tr> - <tr> - <td width="150px">Handled by</td> - <td><input type="text" name="handler" size="20" value="<?= isset($_GET['handler']) ? $_GET['handler'] : '' ?>" /></td> - </tr> - <tr> - <td width="150px">Uploaded by</td> - <td><input type="text" name="uploader" size="20" value="<?= isset($_GET['uploader']) ? $_GET['uploader'] : '' ?>" /></td> - </tr> - <tr> - <td width="150px">Single Torrent</td> - <td><input type="text" name="torrent" size="80" value="<?= isset($_GET['torrent']) ? $_GET['torrent'] : '' ?>" /></td> - </tr> - <tr> - <td width="150px">Torrent Group</td> - <td><input type="text" name="group" size="80" value="<?= isset($_GET['group']) ? $_GET['torrent'] : '' ?>" /></td> - </tr> - <tr> - <td width="150px">Report Type</td> - <td> - <select multiple="multiple" size="8" name="report-type[]"> - <option value="0">Don't Care</option> -<? + <form method="get" action="/reportsv2.php"> + <table> + <tr> + <td width="150px">Reported by</td> + <td><input type="text" name="reporter" size="20" value="<?= isset($_GET['reporter']) ? $_GET['reporter'] : '' ?>" /></td> + </tr> + <tr> + <td width="150px">Handled by</td> + <td><input type="text" name="handler" size="20" value="<?= isset($_GET['handler']) ? $_GET['handler'] : '' ?>" /></td> + </tr> + <tr> + <td width="150px">Uploaded by</td> + <td><input type="text" name="uploader" size="20" value="<?= isset($_GET['uploader']) ? $_GET['uploader'] : '' ?>" /></td> + </tr> + <tr> + <td width="150px">Single Torrent</td> + <td><input type="text" name="torrent" size="80" value="<?= isset($_GET['torrent']) ? $_GET['torrent'] : '' ?>" /></td> + </tr> + <tr> + <td width="150px">Torrent Group</td> + <td><input type="text" name="group" size="80" value="<?= isset($_GET['group']) ? $_GET['group'] : '' ?>" /></td> + </tr> + <tr> + <td width="150px">Report Type</td> + <td> + <select multiple="multiple" size="8" name="report-type[]"> + <option value="0">Don't Care</option> +<?php foreach ($report_name_cache as $key => $label) { - $selected = array_key_exists('report-type', $_GET) && in_array($key, $_GET['report-type']) ? ' selected="selected"' : ''; + $selected = array_key_exists('report-type', $_GET) && in_array($key, $_GET['report-type']) ? ' selected="selected"' : ''; ?> - <option value="<?= $key ?>"<?= $selected ?>><?= $label ?></option> -<? } ?> - </select> - </td> - </tr> - <tr> - <td width="150px">Created</td> - <td> - From <input type="text" name="dt-from" size="10" value="<?= $dt_from ?>" /> and until <input type="text" name="dt-until" size="10" value="<?= $dt_until ?>" /> - </td> - </tr> - <tr> - <td colspan="2"> - <input type="hidden" name="action" value="search" /> - <input type="submit" value="Search reports" /> - </td> - </tr> - </table> - </form> + <option value="<?= $key ?>"<?= $selected ?>><?= $label ?></option> +<?php +} ?> + </select> + </td> + </tr> + <tr> + <td width="150px">Created</td> + <td> + From <input type="text" name="dt-from" size="10" value="<?= $dt_from ?>" /> and until <input type="text" name="dt-until" size="10" value="<?= $dt_until ?>" /> + </td> + </tr> + <tr> + <td colspan="2"> + <input type="hidden" name="action" value="search" /> + <input type="submit" value="Search reports" /> + </td> + </tr> + </table> + </form> </div> -<? +<?php View::show_footer(); diff --git a/sections/reportsv2/static.php b/sections/reportsv2/static.php index f57464a3b..069db13df 100644 --- a/sections/reportsv2/static.php +++ b/sections/reportsv2/static.php @@ -10,7 +10,7 @@ */ if (!check_perms('admin_reports')) { - error(403); + error(403); } include(SERVER_ROOT.'/classes/reports.class.php'); @@ -20,189 +20,189 @@ if (isset($_GET['view'])) { - $View = $_GET['view']; + $View = $_GET['view']; } else { - error(404); + error(404); } if (isset($_GET['id'])) { - if (!is_number($_GET['id']) && $View !== 'type') { - error(404); - } else { - $ID = db_string($_GET['id']); - } + if (!is_number($_GET['id']) && $View !== 'type') { + error(404); + } else { + $ID = db_string($_GET['id']); + } } else { - $ID = ''; + $ID = ''; } $Order = 'ORDER BY r.ReportedTime ASC'; if (!$ID) { - switch ($View) { - case 'resolved': - $Title = 'All the old smelly reports'; - $Where = "WHERE r.Status = 'Resolved'"; - $Order = 'ORDER BY r.LastChangeTime DESC'; - break; - case 'unauto': - $Title = 'New reports, not auto assigned!'; - $Where = "WHERE r.Status = 'New'"; - break; - default: - error(404); - break; - } + switch ($View) { + case 'resolved': + $Title = 'All the old smelly reports'; + $Where = "WHERE r.Status = 'Resolved'"; + $Order = 'ORDER BY r.LastChangeTime DESC'; + break; + case 'unauto': + $Title = 'New reports, not auto assigned!'; + $Where = "WHERE r.Status = 'New'"; + break; + default: + error(404); + break; + } } else { - switch ($View) { - case 'staff': - $DB->query(" - SELECT Username - FROM users_main - WHERE ID = $ID"); - list($Username) = $DB->next_record(); - if ($Username) { - $Title = "$Username's in-progress reports"; - } else { - $Title = "$ID's in-progress reports"; - } - $Where = " - WHERE r.Status = 'InProgress' - AND r.ResolverID = $ID"; - break; - case 'resolver': - $DB->query(" - SELECT Username - FROM users_main - WHERE ID = $ID"); - list($Username) = $DB->next_record(); - if ($Username) { - $Title = "$Username's resolved reports"; - } else { - $Title = "$ID's resolved reports"; - } - $Where = " - WHERE r.Status = 'Resolved' - AND r.ResolverID = $ID"; - $Order = 'ORDER BY r.LastChangeTime DESC'; - break; - case 'group': - $Title = "Unresolved reports for the group $ID"; - $Where = " - WHERE r.Status != 'Resolved' - AND tg.ID = $ID"; - break; - case 'torrent': - $Title = "All reports for the torrent $ID"; - $Where = "WHERE r.TorrentID = $ID"; - break; - case 'report': - $Title = "Viewing resolution of report $ID"; - $Where = "WHERE r.ID = $ID"; - break; - case 'reporter': - $DB->query(" - SELECT Username - FROM users_main - WHERE ID = $ID"); - list($Username) = $DB->next_record(); - if ($Username) { - $Title = "All torrents reported by $Username"; - } else { - $Title = "All torrents reported by user $ID"; - } - $Where = "WHERE r.ReporterID = $ID"; - $Order = 'ORDER BY r.ReportedTime DESC'; - break; - case 'uploader': - $DB->query(" - SELECT Username - FROM users_main - WHERE ID = $ID"); - list($Username) = $DB->next_record(); - if ($Username) { - $Title = "All reports for torrents uploaded by $Username"; - } else { - $Title = "All reports for torrents uploaded by user $ID"; - } - $Where = " - WHERE r.Status != 'Resolved' - AND t.UserID = $ID"; - break; - case 'type': - $Title = 'All new reports for the chosen type'; - $Where = " - WHERE r.Status = 'New' - AND r.Type = '$ID'"; - break; - break; - default: - error(404); - break; - } + switch ($View) { + case 'staff': + $DB->query(" + SELECT Username + FROM users_main + WHERE ID = $ID"); + list($Username) = $DB->next_record(); + if ($Username) { + $Title = "$Username's in-progress reports"; + } else { + $Title = "$ID's in-progress reports"; + } + $Where = " + WHERE r.Status = 'InProgress' + AND r.ResolverID = $ID"; + break; + case 'resolver': + $DB->query(" + SELECT Username + FROM users_main + WHERE ID = $ID"); + list($Username) = $DB->next_record(); + if ($Username) { + $Title = "$Username's resolved reports"; + } else { + $Title = "$ID's resolved reports"; + } + $Where = " + WHERE r.Status = 'Resolved' + AND r.ResolverID = $ID"; + $Order = 'ORDER BY r.LastChangeTime DESC'; + break; + case 'group': + $Title = "Unresolved reports for the group $ID"; + $Where = " + WHERE r.Status != 'Resolved' + AND tg.ID = $ID"; + break; + case 'torrent': + $Title = "All reports for the torrent $ID"; + $Where = "WHERE r.TorrentID = $ID"; + break; + case 'report': + $Title = "Viewing resolution of report $ID"; + $Where = "WHERE r.ID = $ID"; + break; + case 'reporter': + $DB->query(" + SELECT Username + FROM users_main + WHERE ID = $ID"); + list($Username) = $DB->next_record(); + if ($Username) { + $Title = "All torrents reported by $Username"; + } else { + $Title = "All torrents reported by user $ID"; + } + $Where = "WHERE r.ReporterID = $ID"; + $Order = 'ORDER BY r.ReportedTime DESC'; + break; + case 'uploader': + $DB->query(" + SELECT Username + FROM users_main + WHERE ID = $ID"); + list($Username) = $DB->next_record(); + if ($Username) { + $Title = "All reports for torrents uploaded by $Username"; + } else { + $Title = "All reports for torrents uploaded by user $ID"; + } + $Where = " + WHERE r.Status != 'Resolved' + AND t.UserID = $ID"; + break; + case 'type': + $Title = 'All new reports for the chosen type'; + $Where = " + WHERE r.Status = 'New' + AND r.Type = '$ID'"; + break; + break; + default: + error(404); + break; + } } $DB->query(" - SELECT - SQL_CALC_FOUND_ROWS - r.ID, - r.ReporterID, - reporter.Username, - r.TorrentID, - r.Type, - r.UserComment, - r.ResolverID, - resolver.Username, - r.Status, - r.ReportedTime, - r.LastChangeTime, - r.ModComment, - r.Track, - r.Image, - r.ExtraID, - r.Link, - r.LogMessage, - tg.Name, - tg.ID, - CASE COUNT(ta.GroupID) - WHEN 1 THEN aa.ArtistID - WHEN 0 THEN '0' - ELSE '0' - END AS ArtistID, - CASE COUNT(ta.GroupID) - WHEN 1 THEN aa.Name - WHEN 0 THEN '' - ELSE 'Various Artists' - END AS ArtistName, - tg.Year, - tg.CategoryID, - t.Time, - t.Remastered, - t.RemasterTitle, - t.RemasterYear, - t.Media, - t.Format, - t.Encoding, - t.Size, - t.HasLog, - t.HasCue, - t.HasLogDB, - t.LogScore, - t.LogChecksum, - t.UserID AS UploaderID, - uploader.Username - FROM reportsv2 AS r - LEFT JOIN torrents AS t ON t.ID = r.TorrentID - LEFT JOIN torrents_group AS tg ON tg.ID = t.GroupID - LEFT JOIN torrents_artists AS ta ON ta.GroupID = tg.ID AND ta.Importance = '1' - LEFT JOIN artists_alias AS aa ON aa.AliasID = ta.AliasID - LEFT JOIN users_main AS resolver ON resolver.ID = r.ResolverID - LEFT JOIN users_main AS reporter ON reporter.ID = r.ReporterID - LEFT JOIN users_main AS uploader ON uploader.ID = t.UserID - $Where - GROUP BY r.ID - $Order - LIMIT $Limit"); + SELECT + SQL_CALC_FOUND_ROWS + r.ID, + r.ReporterID, + reporter.Username, + r.TorrentID, + r.Type, + r.UserComment, + r.ResolverID, + resolver.Username, + r.Status, + r.ReportedTime, + r.LastChangeTime, + r.ModComment, + r.Track, + r.Image, + r.ExtraID, + r.Link, + r.LogMessage, + tg.Name, + tg.ID, + CASE COUNT(ta.GroupID) + WHEN 1 THEN aa.ArtistID + WHEN 0 THEN '0' + ELSE '0' + END AS ArtistID, + CASE COUNT(ta.GroupID) + WHEN 1 THEN aa.Name + WHEN 0 THEN '' + ELSE 'Various Artists' + END AS ArtistName, + tg.Year, + tg.CategoryID, + t.Time, + t.Remastered, + t.RemasterTitle, + t.RemasterYear, + t.Media, + t.Format, + t.Encoding, + t.Size, + t.HasLog, + t.HasCue, + t.HasLogDB, + t.LogScore, + t.LogChecksum, + t.UserID AS UploaderID, + uploader.Username + FROM reportsv2 AS r + LEFT JOIN torrents AS t ON t.ID = r.TorrentID + LEFT JOIN torrents_group AS tg ON tg.ID = t.GroupID + LEFT JOIN torrents_artists AS ta ON ta.GroupID = tg.ID AND ta.Importance = '1' + LEFT JOIN artists_alias AS aa ON aa.AliasID = ta.AliasID + LEFT JOIN users_main AS resolver ON resolver.ID = r.ResolverID + LEFT JOIN users_main AS reporter ON reporter.ID = r.ReporterID + LEFT JOIN users_main AS uploader ON uploader.ID = t.UserID + $Where + GROUP BY r.ID + $Order + LIMIT $Limit"); $Reports = $DB->to_array(); @@ -213,458 +213,465 @@ View::show_header('Reports V2!', 'reportsv2,bbcode'); ?> <div class="header"> - <h2><?=$Title?></h2> -<? include('header.php'); ?> + <h2><?=$Title?></h2> +<?php +include('header.php'); ?> </div> <div class="buttonbox pad center"> -<? if ($View !== 'resolved') { ?> - <span class="tooltip" title="Resolves *all* checked reports with their respective resolutions"><input type="button" onclick="MultiResolve();" value="Multi-resolve" /></span> - <span class="tooltip" title="Assigns all of the reports on the page to you!"><input type="button" onclick="Grab();" value="Claim all" /></span> -<? } - if ($View === 'staff' && $LoggedUser['ID'] == $ID) { ?> - | <span class="tooltip" title="Unclaim all of the reports currently displayed"><input type="button" onclick="GiveBack();" value="Unclaim all" /></span> -<? } ?> +<?php +if ($View !== 'resolved') { ?> + <span class="tooltip" title="Resolves *all* checked reports with their respective resolutions"><input type="button" onclick="MultiResolve();" value="Multi-resolve" /></span> + <span class="tooltip" title="Assigns all of the reports on the page to you!"><input type="button" onclick="Grab();" value="Claim all" /></span> +<?php +} +if ($View === 'staff' && $LoggedUser['ID'] == $ID) { ?> + | <span class="tooltip" title="Unclaim all of the reports currently displayed"><input type="button" onclick="GiveBack();" value="Unclaim all" /></span> +<?php +} ?> </div> -<? if ($PageLinks) { ?> +<?php +if ($PageLinks) { ?> <div class="linkbox"> - <?=$PageLinks?> + <?=$PageLinks?> </div> -<? } ?> +<?php +} ?> <div id="all_reports" style="width: 80%; margin-left: auto; margin-right: auto;"> -<? +<?php if (count($Reports) === 0) { ?> - <div class="box pad center"> - <strong>No new reports! \o/</strong> - </div> -<? + <div class="box pad center"> + <strong>No new reports! \o/</strong> + </div> +<?php } else { - foreach ($Reports as $Report) { - - list($ReportID, $ReporterID, $ReporterName, $TorrentID, $Type, $UserComment, $ResolverID, $ResolverName, $Status, $ReportedTime, $LastChangeTime, - $ModComment, $Tracks, $Images, $ExtraIDs, $Links, $LogMessage, $GroupName, $GroupID, $ArtistID, $ArtistName, $Year, $CategoryID, $Time, $Remastered, $RemasterTitle, - $RemasterYear, $Media, $Format, $Encoding, $Size, $HasLog, $HasCue, $HasLogDB, $LogScore, $LogChecksum, $UploaderID, $UploaderName) = Misc::display_array($Report, array('ModComment')); - - if (!$GroupID && $Status != 'Resolved') { - //Torrent already deleted - $DB->query(" - UPDATE reportsv2 - SET - Status = 'Resolved', - LastChangeTime = '".sqltime()."', - ModComment = 'Report already dealt with (torrent deleted)' - WHERE ID = $ReportID"); - $Cache->decrement('num_torrent_reportsv2'); + foreach ($Reports as $Report) { + + list($ReportID, $ReporterID, $ReporterName, $TorrentID, $Type, $UserComment, $ResolverID, $ResolverName, $Status, $ReportedTime, $LastChangeTime, + $ModComment, $Tracks, $Images, $ExtraIDs, $Links, $LogMessage, $GroupName, $GroupID, $ArtistID, $ArtistName, $Year, $CategoryID, $Time, $Remastered, $RemasterTitle, + $RemasterYear, $Media, $Format, $Encoding, $Size, $HasLog, $HasCue, $HasLogDB, $LogScore, $LogChecksum, $UploaderID, $UploaderName) = Misc::display_array($Report, array('ModComment')); + + if (!$GroupID && $Status != 'Resolved') { + //Torrent already deleted + $DB->query(" + UPDATE reportsv2 + SET + Status = 'Resolved', + LastChangeTime = '".sqltime()."', + ModComment = 'Report already dealt with (torrent deleted)' + WHERE ID = $ReportID"); + $Cache->decrement('num_torrent_reportsv2'); ?> - <div id="report<?=$ReportID?>" class="report box pad center"> - <a href="reportsv2.php?view=report&id=<?=$ReportID?>">Report <?=$ReportID?></a> for torrent <?=$TorrentID?> (deleted) has been automatically resolved. <input type="button" value="Hide" onclick="ClearReport(<?=$ReportID?>);" /> - </div> -<? - } else { - if (!$CategoryID) { - //Torrent was deleted - } else { - if (array_key_exists($Type, $Types[$CategoryID])) { - $ReportType = $Types[$CategoryID][$Type]; - } elseif (array_key_exists($Type, $Types['master'])) { - $ReportType = $Types['master'][$Type]; - } else { - //There was a type but it wasn't an option! - $Type = 'other'; - $ReportType = $Types['master']['other']; - } - } - $RemasterDisplayString = Reports::format_reports_remaster_info($Remastered, $RemasterTitle, $RemasterYear); - - if ($ArtistID == 0 && empty($ArtistName)) { - $RawName = $GroupName.($Year ? " ($Year)" : '').($Format || $Encoding || $Media ? " [$Format/$Encoding/$Media]" : '') . $RemasterDisplayString . ($HasCue ? ' (Cue)' : '').($HasLogDB ? " (Log: {$LogScore}%)" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - - $LinkName = "<a href=\"torrents.php?id=$GroupID\">$GroupName".($Year ? " ($Year)" : '')."</a> <a href=\"torrents.php?torrentid=$TorrentID\">".($Format || $Encoding || $Media ? " [$Format/$Encoding/$Media]" : '') . $RemasterDisplayString . '</a> '.($HasCue ? ' (Cue)' : '').($HasLog ? " <a href=\"torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID\">(Log: {$LogScore}%)</a>" : '').' ('.number_format($Size / (1024 * 1024), 2)." MB)"; - - $BBName = "[url=torrents.php?id=$GroupID]$GroupName".($Year ? " ($Year)" : '')."[/url] [url=torrents.php?torrentid=$TorrentID][$Format/$Encoding/$Media]{$RemasterDisplayString}[/url] ".($HasCue ? ' (Cue)' : '').($HasLog ? " [url=torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID](Log: {$LogScore}%)[/url]" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - } elseif ($ArtistID == 0 && $ArtistName == 'Various Artists') { - $RawName = "Various Artists - $GroupName".($Year ? " ($Year)" : '')." [$Format/$Encoding/$Media]{$RemasterDisplayString}" . ($HasCue ? ' (Cue)' : '').($HasLogDB ? " (Log: {$LogScore}%)" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - - $LinkName = "Various Artists - <a href=\"torrents.php?id=$GroupID\">$GroupName".($Year ? " ($Year)" : '')."</a> <a href=\"torrents.php?torrentid=$TorrentID\"> [$Format/$Encoding/$Media]$RemasterDisplayString</a> ".($HasCue ? ' (Cue)' : '').($HasLogDB ? " <a href=\"torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID\">(Log: {$LogScore}%)</a>" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - - $BBName = "Various Artists - [url=torrents.php?id=$GroupID]$GroupName".($Year ? " ($Year)" : '')."[/url] [url=torrents.php?torrentid=$TorrentID][$Format/$Encoding/$Media]{$RemasterDisplayString}[/url] ".($HasCue ? ' (Cue)' : '').($HasLogDB ? " [url=torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID](Log: {$LogScore}%)[/url]" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - } else { - $RawName = "$ArtistName - $GroupName".($Year ? " ($Year)" : '')." [$Format/$Encoding/$Media]{$RemasterDisplayString}" . ($HasCue ? ' (Cue)' : '').($HasLogDB ? " (Log: {$LogScore}%)" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - - $LinkName = "<a href=\"artist.php?id=$ArtistID\">$ArtistName</a> - <a href=\"torrents.php?id=$GroupID\">$GroupName".($Year ? " ($Year)" : '')."</a> <a href=\"torrents.php?torrentid=$TorrentID\"> [$Format/$Encoding/$Media]{$RemasterDisplayString}</a> ".($HasCue ? ' (Cue)' : '').($HasLogDB ? " <a href=\"torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID\">(Log: {$LogScore}%)</a>" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - - $BBName = "[url=artist.php?id=$ArtistID]".$ArtistName."[/url] - [url=torrents.php?id=$GroupID]$GroupName".($Year ? " ($Year)" : '')."[/url] [url=torrents.php?torrentid=$TorrentID][$Format/$Encoding/$Media]{$RemasterDisplayString}[/url] ".($HasCue ? ' (Cue)' : '').($HasLogDB ? " [url=torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID](Log: {$LogScore}%)[/url]" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; - } + <div id="report<?=$ReportID?>" class="report box pad center"> + <a href="reportsv2.php?view=report&id=<?=$ReportID?>">Report <?=$ReportID?></a> for torrent <?=$TorrentID?> (deleted) has been automatically resolved. <input type="button" value="Hide" onclick="ClearReport(<?=$ReportID?>);" /> + </div> +<?php + } else { + if (!$CategoryID) { + //Torrent was deleted + } else { + if (array_key_exists($Type, $Types[$CategoryID])) { + $ReportType = $Types[$CategoryID][$Type]; + } elseif (array_key_exists($Type, $Types['master'])) { + $ReportType = $Types['master'][$Type]; + } else { + //There was a type but it wasn't an option! + $Type = 'other'; + $ReportType = $Types['master']['other']; + } + } + $RemasterDisplayString = Reports::format_reports_remaster_info($Remastered, $RemasterTitle, $RemasterYear); + + if ($ArtistID == 0 && empty($ArtistName)) { + $RawName = $GroupName.($Year ? " ($Year)" : '').($Format || $Encoding || $Media ? " [$Format/$Encoding/$Media]" : '') . $RemasterDisplayString . ($HasCue ? ' (Cue)' : '').($HasLogDB ? " (Log: {$LogScore}%)" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + + $LinkName = "<a href=\"torrents.php?id=$GroupID\">$GroupName".($Year ? " ($Year)" : '')."</a> <a href=\"torrents.php?torrentid=$TorrentID\">".($Format || $Encoding || $Media ? " [$Format/$Encoding/$Media]" : '') . $RemasterDisplayString . '</a> '.($HasCue ? ' (Cue)' : '').($HasLog ? " <a href=\"torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID\">(Log: {$LogScore}%)</a>" : '').' ('.number_format($Size / (1024 * 1024), 2)." MB)"; + + $BBName = "[url=torrents.php?id=$GroupID]$GroupName".($Year ? " ($Year)" : '')."[/url] [url=torrents.php?torrentid=$TorrentID][$Format/$Encoding/$Media]{$RemasterDisplayString}[/url] ".($HasCue ? ' (Cue)' : '').($HasLog ? " [url=torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID](Log: {$LogScore}%)[/url]" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + } elseif ($ArtistID == 0 && $ArtistName == 'Various Artists') { + $RawName = "Various Artists - $GroupName".($Year ? " ($Year)" : '')." [$Format/$Encoding/$Media]{$RemasterDisplayString}" . ($HasCue ? ' (Cue)' : '').($HasLogDB ? " (Log: {$LogScore}%)" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + + $LinkName = "Various Artists - <a href=\"torrents.php?id=$GroupID\">$GroupName".($Year ? " ($Year)" : '')."</a> <a href=\"torrents.php?torrentid=$TorrentID\"> [$Format/$Encoding/$Media]$RemasterDisplayString</a> ".($HasCue ? ' (Cue)' : '').($HasLogDB ? " <a href=\"torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID\">(Log: {$LogScore}%)</a>" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + + $BBName = "Various Artists - [url=torrents.php?id=$GroupID]$GroupName".($Year ? " ($Year)" : '')."[/url] [url=torrents.php?torrentid=$TorrentID][$Format/$Encoding/$Media]{$RemasterDisplayString}[/url] ".($HasCue ? ' (Cue)' : '').($HasLogDB ? " [url=torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID](Log: {$LogScore}%)[/url]" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + } else { + $RawName = "$ArtistName - $GroupName".($Year ? " ($Year)" : '')." [$Format/$Encoding/$Media]{$RemasterDisplayString}" . ($HasCue ? ' (Cue)' : '').($HasLogDB ? " (Log: {$LogScore}%)" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + + $LinkName = "<a href=\"artist.php?id=$ArtistID\">$ArtistName</a> - <a href=\"torrents.php?id=$GroupID\">$GroupName".($Year ? " ($Year)" : '')."</a> <a href=\"torrents.php?torrentid=$TorrentID\"> [$Format/$Encoding/$Media]{$RemasterDisplayString}</a> ".($HasCue ? ' (Cue)' : '').($HasLogDB ? " <a href=\"torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID\">(Log: {$LogScore}%)</a>" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + + $BBName = "[url=artist.php?id=$ArtistID]".$ArtistName."[/url] - [url=torrents.php?id=$GroupID]$GroupName".($Year ? " ($Year)" : '')."[/url] [url=torrents.php?torrentid=$TorrentID][$Format/$Encoding/$Media]{$RemasterDisplayString}[/url] ".($HasCue ? ' (Cue)' : '').($HasLogDB ? " [url=torrents.php?action=viewlog&torrentid=$TorrentID&groupid=$GroupID](Log: {$LogScore}%)[/url]" : '').' ('.number_format($Size / (1024 * 1024), 2).' MB)'; + } ?> - <div id="report<?=$ReportID?>"> - <form class="manage_form" name="report" id="reportform_<?=$ReportID?>" action="reports.php" method="post"> -<? + <div id="report<?=$ReportID?>"> + <form class="manage_form" name="report" id="reportform_<?=$ReportID?>" action="reports.php" method="post"> +<?php /* * Some of these are for takeresolve, namely the ones that aren't inputs, some for the JavaScript. */ ?> - <div> - <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> - <input type="hidden" id="reportid<?=$ReportID?>" name="reportid" value="<?=$ReportID?>" /> - <input type="hidden" id="torrentid<?=$ReportID?>" name="torrentid" value="<?=$TorrentID?>" /> - <input type="hidden" id="uploader<?=$ReportID?>" name="uploader" value="<?=$UploaderName?>" /> - <input type="hidden" id="uploaderid<?=$ReportID?>" name="uploaderid" value="<?=$UploaderID?>" /> - <input type="hidden" id="reporterid<?=$ReportID?>" name="reporterid" value="<?=$ReporterID?>" /> - <input type="hidden" id="report_reason<?=$ReportID?>" name="report_reason" value="<?=$UserComment?>" /> - <input type="hidden" id="raw_name<?=$ReportID?>" name="raw_name" value="<?=$RawName?>" /> - <input type="hidden" id="type<?=$ReportID?>" name="type" value="<?=$Type?>" /> - <input type="hidden" id="categoryid<?=$ReportID?>" name="categoryid" value="<?=$CategoryID?>" /> - </div> - <table class="box layout" cellpadding="5"> - <tr> - <td class="label"><a href="reportsv2.php?view=report&id=<?=$ReportID?>">Reported</a> torrent:</td> - <td colspan="3"> -<? if (!$GroupID) { ?> - <a href="log.php?search=Torrent+<?=$TorrentID?>"><?=$TorrentID?></a> (Deleted) -<? } else { ?> - <?=$LinkName?> - <a href="torrents.php?action=download&id=<?=$TorrentID?>&authkey=<?=$LoggedUser['AuthKey']?>&torrent_pass=<?=$LoggedUser['torrent_pass']?>" title="Download" class="brackets tooltip">DL</a> - uploaded by <a href="user.php?id=<?=$UploaderID?>"><?=$UploaderName?></a> on <span title="<?= time_diff($Time, 3, false) ?>"><?= $Time ?></span> - <br /> -<? if ($ReporterName == '') { - $ReporterName = 'System'; - } ?> - <div style="text-align: right;">was reported by <a href="user.php?id=<?=$ReporterID?>"><?=$ReporterName?></a> <?=time_diff($ReportedTime)?> for the reason: <strong><?=$ReportType['title']?></strong></div> -<? if ($Status != 'Resolved') { - - $DB->query(" - SELECT r.ID - FROM reportsv2 AS r - LEFT JOIN torrents AS t ON t.ID = r.TorrentID - WHERE r.Status != 'Resolved' - AND t.GroupID = $GroupID"); - $GroupOthers = ($DB->record_count() - 1); - - if ($GroupOthers > 0) { ?> - <div style="text-align: right;"> - <a href="reportsv2.php?view=group&id=<?=$GroupID?>">There <?=(($GroupOthers > 1) ? "are $GroupOthers other reports" : "is 1 other report")?> for torrent(s) in this group</a> - </div> -<? } - - $DB->query(" - SELECT t.UserID - FROM reportsv2 AS r - JOIN torrents AS t ON t.ID = r.TorrentID - WHERE r.Status != 'Resolved' - AND t.UserID = $UploaderID"); - $UploaderOthers = ($DB->record_count() - 1); - - if ($UploaderOthers > 0) { ?> - <div style="text-align: right;"> - <a href="reportsv2.php?view=uploader&id=<?=$UploaderID?>">There <?=(($UploaderOthers > 1) ? "are $UploaderOthers other reports" : "is 1 other report")?> for torrent(s) uploaded by this user</a> - </div> -<? } - - $DB->query(" - SELECT DISTINCT req.ID, - req.FillerID, - um.Username, - req.TimeFilled - FROM requests AS req - LEFT JOIN torrents AS t ON t.ID = req.TorrentID - LEFT JOIN reportsv2 AS rep ON rep.TorrentID = t.ID - JOIN users_main AS um ON um.ID = req.FillerID - WHERE rep.Status != 'Resolved' - AND req.TimeFilled > '2010-03-04 02:31:49' - AND req.TorrentID = $TorrentID"); - $Requests = ($DB->has_results()); - if ($Requests > 0) { - while (list($RequestID, $FillerID, $FillerName, $FilledTime) = $DB->next_record()) { + <div> + <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> + <input type="hidden" id="reportid<?=$ReportID?>" name="reportid" value="<?=$ReportID?>" /> + <input type="hidden" id="torrentid<?=$ReportID?>" name="torrentid" value="<?=$TorrentID?>" /> + <input type="hidden" id="uploader<?=$ReportID?>" name="uploader" value="<?=$UploaderName?>" /> + <input type="hidden" id="uploaderid<?=$ReportID?>" name="uploaderid" value="<?=$UploaderID?>" /> + <input type="hidden" id="reporterid<?=$ReportID?>" name="reporterid" value="<?=$ReporterID?>" /> + <input type="hidden" id="report_reason<?=$ReportID?>" name="report_reason" value="<?=$UserComment?>" /> + <input type="hidden" id="raw_name<?=$ReportID?>" name="raw_name" value="<?=$RawName?>" /> + <input type="hidden" id="type<?=$ReportID?>" name="type" value="<?=$Type?>" /> + <input type="hidden" id="categoryid<?=$ReportID?>" name="categoryid" value="<?=$CategoryID?>" /> + </div> + <table class="box layout" cellpadding="5"> + <tr> + <td class="label"><a href="reportsv2.php?view=report&id=<?=$ReportID?>">Reported</a> torrent:</td> + <td colspan="3"> +<?php if (!$GroupID) { ?> + <a href="log.php?search=Torrent+<?=$TorrentID?>"><?=$TorrentID?></a> (Deleted) +<?php } else { ?> + <?=$LinkName?> + <a href="torrents.php?action=download&id=<?=$TorrentID?>&authkey=<?=$LoggedUser['AuthKey']?>&torrent_pass=<?=$LoggedUser['torrent_pass']?>" title="Download" class="brackets tooltip">DL</a> + uploaded by <a href="user.php?id=<?=$UploaderID?>"><?=$UploaderName?></a> on <span title="<?= time_diff($Time, 3, false) ?>"><?= $Time ?></span> + <br /> +<?php if ($ReporterName == '') { + $ReporterName = 'System'; + } ?> + <div style="text-align: right;">was reported by <a href="user.php?id=<?=$ReporterID?>"><?=$ReporterName?></a> <?=time_diff($ReportedTime)?> for the reason: <strong><?=$ReportType['title']?></strong></div> +<?php if ($Status != 'Resolved') { + $DB->query(" + SELECT r.ID + FROM reportsv2 AS r + LEFT JOIN torrents AS t ON t.ID = r.TorrentID + WHERE r.Status != 'Resolved' + AND t.GroupID = $GroupID"); + $GroupOthers = ($DB->record_count() - 1); + + if ($GroupOthers > 0) { ?> + <div style="text-align: right;"> + <a href="reportsv2.php?view=group&id=<?=$GroupID?>">There <?=(($GroupOthers > 1) ? "are $GroupOthers other reports" : "is 1 other report")?> for torrent(s) in this group</a> + </div> +<?php } + + $DB->query(" + SELECT t.UserID + FROM reportsv2 AS r + JOIN torrents AS t ON t.ID = r.TorrentID + WHERE r.Status != 'Resolved' + AND t.UserID = $UploaderID"); + $UploaderOthers = ($DB->record_count() - 1); + + if ($UploaderOthers > 0) { ?> + <div style="text-align: right;"> + <a href="reportsv2.php?view=uploader&id=<?=$UploaderID?>">There <?=(($UploaderOthers > 1) ? "are $UploaderOthers other reports" : "is 1 other report")?> for torrent(s) uploaded by this user</a> + </div> +<?php } + + $DB->query(" + SELECT DISTINCT req.ID, + req.FillerID, + um.Username, + req.TimeFilled + FROM requests AS req + LEFT JOIN torrents AS t ON t.ID = req.TorrentID + LEFT JOIN reportsv2 AS rep ON rep.TorrentID = t.ID + JOIN users_main AS um ON um.ID = req.FillerID + WHERE rep.Status != 'Resolved' + AND req.TimeFilled > '2010-03-04 02:31:49' + AND req.TorrentID = $TorrentID"); + $Requests = ($DB->has_results()); + if ($Requests > 0) { + while (list($RequestID, $FillerID, $FillerName, $FilledTime) = $DB->next_record()) { ?> - <div style="text-align: right;"> - <strong class="important_text"><a href="user.php?id=<?=$FillerID?>"><?=$FillerName?></a> used this torrent to fill <a href="requests.php?action=view&id=<?=$RequestID?>">this request</a> <?=time_diff($FilledTime)?></strong> - </div> -<? } - } - } - } + <div style="text-align: right;"> + <strong class="important_text"><a href="user.php?id=<?=$FillerID?>"><?=$FillerName?></a> used this torrent to fill <a href="requests.php?action=view&id=<?=$RequestID?>">this request</a> <?=time_diff($FilledTime)?></strong> + </div> +<?php } + } + } + } ?> - </td> - </tr> -<? if ($Tracks) { ?> - <tr> - <td class="label">Relevant tracks:</td> - <td colspan="3"> - <?=str_replace(' ', ', ', $Tracks)?> - </td> - </tr> -<? - } - - if ($Links) { ?> - <tr> - <td class="label">Relevant links:</td> - <td colspan="3"> -<? - $Links = explode(' ', $Links); - foreach ($Links as $Link) { - - if ($local_url = Text::local_url($Link)) { - $Link = $local_url; - } + </td> + </tr> +<?php if ($Tracks) { ?> + <tr> + <td class="label">Relevant tracks:</td> + <td colspan="3"> + <?=str_replace(' ', ', ', $Tracks)?> + </td> + </tr> +<?php + } + + if ($Links) { ?> + <tr> + <td class="label">Relevant links:</td> + <td colspan="3"> +<?php + $Links = explode(' ', $Links); + foreach ($Links as $Link) { + + if ($local_url = Text::local_url($Link)) { + $Link = $local_url; + } ?> - <a href="<?=$Link?>"><?=$Link?></a> -<? } ?> - </td> - </tr> -<? - } - - if ($ExtraIDs) { ?> - <tr> - <td class="label">Relevant other torrents:</td> - <td colspan="3"> -<? - $First = true; - $Extras = explode(' ', $ExtraIDs); - foreach ($Extras as $ExtraID) { - $DB->query(" - SELECT - tg.Name, - tg.ID, - CASE COUNT(ta.GroupID) - WHEN 1 THEN aa.ArtistID - WHEN 0 THEN '0' - ELSE '0' - END AS ArtistID, - CASE COUNT(ta.GroupID) - WHEN 1 THEN aa.Name - WHEN 0 THEN '' - ELSE 'Various Artists' - END AS ArtistName, - tg.Year, - t.Time, - t.Remastered, - t.RemasterTitle, - t.RemasterYear, - t.Media, - t.Format, - t.Encoding, - t.Size, - t.HasCue, - t.HasLog, - t.LogScore, - t.UserID AS UploaderID, - uploader.Username - FROM torrents AS t - LEFT JOIN torrents_group AS tg ON tg.ID = t.GroupID - LEFT JOIN torrents_artists AS ta ON ta.GroupID = tg.ID AND ta.Importance = '1' - LEFT JOIN artists_alias AS aa ON aa.AliasID = ta.AliasID - LEFT JOIN users_main AS uploader ON uploader.ID = t.UserID - WHERE t.ID = '$ExtraID' - GROUP BY tg.ID"); - - list($ExtraGroupName, $ExtraGroupID, $ExtraArtistID, $ExtraArtistName, $ExtraYear, $ExtraTime, $ExtraRemastered, $ExtraRemasterTitle, - $ExtraRemasterYear, $ExtraMedia, $ExtraFormat, $ExtraEncoding, $ExtraSize, $ExtraHasCue, $ExtraHasLog, $ExtraLogScore, $ExtraUploaderID, $ExtraUploaderName) = Misc::display_array($DB->next_record()); - - if ($ExtraGroupName) { - $ExtraRemasterDisplayString = Reports::format_reports_remaster_info($ExtraRemastered, $ExtraRemasterTitle, $ExtraRemasterYear); - - if ($ArtistID == 0 && empty($ArtistName)) { - $ExtraLinkName = "<a href=\"torrents.php?id=$ExtraGroupID\">$ExtraGroupName".($ExtraYear ? " ($ExtraYear)" : '')."</a> <a href=\"torrents.php?torrentid=$ExtraID\"> [$ExtraFormat/$ExtraEncoding/$ExtraMedia]$ExtraRemasterDisplayString</a> " . ($ExtraHasCue == '1' ? ' (Cue)' : '').($ExtraHasLog == '1' ? " <a href=\"torrents.php?action=viewlog&torrentid=$ExtraID&groupid=$ExtraGroupID\">(Log: {$ExtraLogScore}%)</a>" : '').' ('.number_format($ExtraSize / (1024 * 1024), 2).' MB)'; - } elseif ($ArtistID == 0 && $ArtistName == 'Various Artists') { - $ExtraLinkName = "Various Artists - <a href=\"torrents.php?id=$ExtraGroupID\">$ExtraGroupName".($ExtraYear ? " ($ExtraYear)" : '')."</a> <a href=\"torrents.php?torrentid=$ExtraID\"> [$ExtraFormat/$ExtraEncoding/$ExtraMedia]$ExtraRemasterDisplayString</a> " . ($ExtraHasCue == '1' ? ' (Cue)' : '').($ExtraHasLog == '1' ? " <a href=\"torrents.php?action=viewlog&torrentid=$ExtraID&groupid=$ExtraGroupID\">(Log: {$ExtraLogScore}%)</a>" : '').' ('.number_format($ExtraSize / (1024 * 1024), 2).' MB)'; - } else { - $ExtraLinkName = "<a href=\"artist.php?id=$ExtraArtistID\">$ExtraArtistName</a> - <a href=\"torrents.php?id=$ExtraGroupID\">$ExtraGroupName".($ExtraYear ? " ($ExtraYear)" : '')."</a> <a href=\"torrents.php?torrentid=$ExtraID\"> [$ExtraFormat/$ExtraEncoding/$ExtraMedia]$ExtraRemasterDisplayString</a> " . ($ExtraHasCue == '1' ? ' (Cue)' : '').($ExtraHasLog == '1' ? " <a href=\"torrents.php?action=viewlog&torrentid=$ExtraID&groupid=$ExtraGroupID\">(Log: {$ExtraLogScore}%)</a>" : '').' ('.number_format($ExtraSize / (1024 * 1024), 2).' MB)'; - } + <a href="<?=$Link?>"><?=$Link?></a> +<?php } ?> + </td> + </tr> +<?php + } + + if ($ExtraIDs) { ?> + <tr> + <td class="label">Relevant other torrents:</td> + <td colspan="3"> +<?php + $First = true; + $Extras = explode(' ', $ExtraIDs); + foreach ($Extras as $ExtraID) { + $DB->query(" + SELECT + tg.Name, + tg.ID, + CASE COUNT(ta.GroupID) + WHEN 1 THEN aa.ArtistID + WHEN 0 THEN '0' + ELSE '0' + END AS ArtistID, + CASE COUNT(ta.GroupID) + WHEN 1 THEN aa.Name + WHEN 0 THEN '' + ELSE 'Various Artists' + END AS ArtistName, + tg.Year, + t.Time, + t.Remastered, + t.RemasterTitle, + t.RemasterYear, + t.Media, + t.Format, + t.Encoding, + t.Size, + t.HasCue, + t.HasLog, + t.LogScore, + t.UserID AS UploaderID, + uploader.Username + FROM torrents AS t + LEFT JOIN torrents_group AS tg ON tg.ID = t.GroupID + LEFT JOIN torrents_artists AS ta ON ta.GroupID = tg.ID AND ta.Importance = '1' + LEFT JOIN artists_alias AS aa ON aa.AliasID = ta.AliasID + LEFT JOIN users_main AS uploader ON uploader.ID = t.UserID + WHERE t.ID = '$ExtraID' + GROUP BY tg.ID"); + + list($ExtraGroupName, $ExtraGroupID, $ExtraArtistID, $ExtraArtistName, $ExtraYear, $ExtraTime, $ExtraRemastered, $ExtraRemasterTitle, + $ExtraRemasterYear, $ExtraMedia, $ExtraFormat, $ExtraEncoding, $ExtraSize, $ExtraHasCue, $ExtraHasLog, $ExtraLogScore, $ExtraUploaderID, $ExtraUploaderName) = Misc::display_array($DB->next_record()); + + if ($ExtraGroupName) { + $ExtraRemasterDisplayString = Reports::format_reports_remaster_info($ExtraRemastered, $ExtraRemasterTitle, $ExtraRemasterYear); + + if ($ArtistID == 0 && empty($ArtistName)) { + $ExtraLinkName = "<a href=\"torrents.php?id=$ExtraGroupID\">$ExtraGroupName".($ExtraYear ? " ($ExtraYear)" : '')."</a> <a href=\"torrents.php?torrentid=$ExtraID\"> [$ExtraFormat/$ExtraEncoding/$ExtraMedia]$ExtraRemasterDisplayString</a> " . ($ExtraHasCue == '1' ? ' (Cue)' : '').($ExtraHasLog == '1' ? " <a href=\"torrents.php?action=viewlog&torrentid=$ExtraID&groupid=$ExtraGroupID\">(Log: {$ExtraLogScore}%)</a>" : '').' ('.number_format($ExtraSize / (1024 * 1024), 2).' MB)'; + } elseif ($ArtistID == 0 && $ArtistName == 'Various Artists') { + $ExtraLinkName = "Various Artists - <a href=\"torrents.php?id=$ExtraGroupID\">$ExtraGroupName".($ExtraYear ? " ($ExtraYear)" : '')."</a> <a href=\"torrents.php?torrentid=$ExtraID\"> [$ExtraFormat/$ExtraEncoding/$ExtraMedia]$ExtraRemasterDisplayString</a> " . ($ExtraHasCue == '1' ? ' (Cue)' : '').($ExtraHasLog == '1' ? " <a href=\"torrents.php?action=viewlog&torrentid=$ExtraID&groupid=$ExtraGroupID\">(Log: {$ExtraLogScore}%)</a>" : '').' ('.number_format($ExtraSize / (1024 * 1024), 2).' MB)'; + } else { + $ExtraLinkName = "<a href=\"artist.php?id=$ExtraArtistID\">$ExtraArtistName</a> - <a href=\"torrents.php?id=$ExtraGroupID\">$ExtraGroupName".($ExtraYear ? " ($ExtraYear)" : '')."</a> <a href=\"torrents.php?torrentid=$ExtraID\"> [$ExtraFormat/$ExtraEncoding/$ExtraMedia]$ExtraRemasterDisplayString</a> " . ($ExtraHasCue == '1' ? ' (Cue)' : '').($ExtraHasLog == '1' ? " <a href=\"torrents.php?action=viewlog&torrentid=$ExtraID&groupid=$ExtraGroupID\">(Log: {$ExtraLogScore}%)</a>" : '').' ('.number_format($ExtraSize / (1024 * 1024), 2).' MB)'; + } ?> - <?=($First ? '' : '<br />')?> - <?=$ExtraLinkName?> - <a href="torrents.php?action=download&id=<?=$ExtraID?>&authkey=<?=$LoggedUser['AuthKey']?>&torrent_pass=<?=$LoggedUser['torrent_pass']?>" title="Download" class="brackets tooltip">DL</a> - uploaded by <a href="user.php?id=<?=$ExtraUploaderID?>"><?=$ExtraUploaderName?></a> on <span title="<?= time_diff($ExtraTime, 3, false) ?>"><?= $ExtraTime ?></span> + <?=($First ? '' : '<br />')?> + <?=$ExtraLinkName?> + <a href="torrents.php?action=download&id=<?=$ExtraID?>&authkey=<?=$LoggedUser['AuthKey']?>&torrent_pass=<?=$LoggedUser['torrent_pass']?>" title="Download" class="brackets tooltip">DL</a> + uploaded by <a href="user.php?id=<?=$ExtraUploaderID?>"><?=$ExtraUploaderName?></a> on <span title="<?= time_diff($ExtraTime, 3, false) ?>"><?= $ExtraTime ?></span> </td> </tr> <tr> <td class="label">Switch:</td> <td colspan="3"><a href="#" onclick="Switch(<?=$ReportID?>, <?=$TorrentID?>, <?=$ExtraID?>); return false;" class="brackets">Switch</a> the source and target torrents (you become the report owner). -<? - $First = false; - } - } +<?php + $First = false; + } + } ?> - </td> - </tr> -<? - } + </td> + </tr> +<?php + } - if ($Images) { + if ($Images) { ?> - <tr> - <td class="label">Relevant images:</td> - <td colspan="3"> -<? - $Images = explode(' ', $Images); - foreach ($Images as $Image) { + <tr> + <td class="label">Relevant images:</td> + <td colspan="3"> +<?php + $Images = explode(' ', $Images); + foreach ($Images as $Image) { ?> - <img style="max-width: 200px;" onclick="lightbox.init(this, 200);" src="<?=ImageTools::process($Image)?>" alt="Relevant image" /> -<? } ?> - </td> - </tr> -<? - } ?> - <tr> - <td class="label">User comment:</td> - <td colspan="3" class="wrap_overflow"><?=Text::full_format($UserComment)?></td> - </tr> -<? // END REPORTED STUFF :|: BEGIN MOD STUFF - if ($Status == 'InProgress') { ?> - <tr> - <td class="label">In progress by:</td> - <td colspan="3"> - <a href="user.php?id=<?=$ResolverID?>"><?=$ResolverName?></a> - </td> - </tr> -<? } - if ($Status != 'Resolved') { ?> - <tr> - <td class="label">Report comment:</td> - <td colspan="3"> - <input type="text" name="comment" id="comment<?=$ReportID?>" size="70" value="<?=$ModComment?>" /> - <input type="button" value="Update now" onclick="UpdateComment(<?=$ReportID?>);" /> - </td> - </tr> - <tr> - <td class="label"> - <a href="javascript:Load('<?=$ReportID?>')" class="tooltip" title="Click here to reset the resolution options to their default values.">Resolve</a>: - </td> - <td colspan="3"> - <select name="resolve_type" id="resolve_type<?=$ReportID?>" onchange="ChangeResolve(<?=$ReportID?>);"> -<? - $TypeList = $Types['master'] + $Types[$CategoryID]; - $Priorities = array(); - foreach ($TypeList as $Key => $Value) { - $Priorities[$Key] = $Value['priority']; - } - array_multisort($Priorities, SORT_ASC, $TypeList); - - foreach ($TypeList as $Type => $Data) { ?> - <option value="<?=$Type?>"><?=$Data['title']?></option> -<? } ?> - </select> - | <span id="options<?=$ReportID?>"> - <span class="tooltip" title="Warning length in weeks"> - <label for="warning<?=$ReportID?>"><strong>Warning</strong></label> - <select name="warning" id="warning<?=$ReportID?>"> -<? for ($i = 0; $i < 9; $i++) { ?> - <option value="<?=$i?>"><?=$i?></option> -<? } ?> - </select> - </span> | -<? if (check_perms('users_mod')) { ?> - <span class="tooltip" title="Delete torrent?"> - <input type="checkbox" name="delete" id="delete<?=$ReportID?>" /> <label for="delete<?=$ReportID?>"><strong>Delete</strong></label> - </span> | -<? } ?> - <span class="tooltip" title="Remove upload privileges?"> - <input type="checkbox" name="upload" id="upload<?=$ReportID?>" /> <label for="upload<?=$ReportID?>"><strong>Remove upload privileges</strong></label> - </span> | - <span class="tooltip" title="Update resolve type"> - <input type="button" name="update_resolve" id="update_resolve<?=$ReportID?>" value="Update now" onclick="UpdateResolve(<?=$ReportID?>);" /> - </span> - </span> - </td> - </tr> - <tr> - <td class="label tooltip" title="Uploader: Appended to the regular message unless using "Send now". Reporter: Must be used with "Send now"."> - PM - <select name="pm_type" id="pm_type<?=$ReportID?>"> - <option value="Uploader">Uploader</option> - <option value="Reporter" selected="selected">Reporter</option> - </select>: - </td> - <td colspan="3"> - <textarea name="uploader_pm" id="uploader_pm<?=$ReportID?>" cols="50" rows="2"></textarea> - <input type="button" value="Send now" onclick="SendPM(<?=$ReportID?>);" /> - </td> - </tr> - <tr> - <td class="label"><strong>Extra</strong> log message:</td> - <td> - <input type="text" name="log_message" id="log_message<?=$ReportID?>" size="40"<? - if ($ExtraIDs) { - $Extras = explode(' ', $ExtraIDs); - $Value = ''; - foreach ($Extras as $ExtraID) { - $Value .= site_url()."torrents.php?torrentid=$ExtraID "; - } - echo ' value="'.trim($Value).'"'; - } elseif (isset($ReportType['extra_log'])) { - printf(' value="%s"', $ReportType['extra_log']); - } ?> - /> - </td> - <td class="label"><strong>Extra</strong> staff notes:</td> - <td> - <input type="text" name="admin_message" id="admin_message<?=$ReportID?>" size="40" /> - </td> - </tr> - <tr> - <td colspan="4" style="text-align: center;"> - <input type="button" value="Invalidate report" onclick="Dismiss(<?=$ReportID?>);" /> - | <input type="button" value="Resolve report manually" onclick="ManualResolve(<?=$ReportID?>);" /> -<? if ($Status == 'InProgress' && $LoggedUser['ID'] == $ResolverID) { ?> - | <input type="button" value="Unclaim" onclick="GiveBack(<?=$ReportID?>);" /> -<? } else { ?> - | <input id="grab<?=$ReportID?>" type="button" value="Claim" onclick="Grab(<?=$ReportID?>);" /> -<? } ?> - | <span class="tooltip" title="All checked reports will be resolved via the Multi-resolve button"> - <input type="checkbox" name="multi" id="multi<?=$ReportID?>" /> <label for="multi">Multi-resolve</label> - </span> - | <input type="button" id="submit_<?=$ReportID?>" value="Submit" onclick="TakeResolve(<?=$ReportID?>);" /> - </td> - </tr> -<? } else { ?> - <tr> - <td class="label">Resolver:</td> - <td colspan="3"> - <a href="user.php?id=<?=$ResolverID?>"><?=$ResolverName?></a> - </td> - </tr> - <tr> - <td class="label">Resolve time:</td> - <td colspan="3"> - <?=time_diff($LastChangeTime); echo "\n"; ?> - </td> - </tr> - <tr> - <td class="label">Report comments:</td> - <td colspan="3"> - <?=$ModComment; echo "\n"; ?> - </td> - </tr> - <tr> - <td class="label">Log message:</td> - <td colspan="3"> - <?=$LogMessage; echo "\n"; ?> - </td> - </tr> -<? if ($GroupID) { ?> - <tr> - <td colspan="4" style="text-align: center;"> - <input id="grab<?=$ReportID?>" type="button" value="Claim" onclick="Grab(<?=$ReportID?>);" /> - </td> - </tr> -<? } - } ?> - </table> - </form> - </div> - <script type="text/javascript">//<![CDATA[ - Load(<?=$ReportID?>); - //]]> - </script> -<? - } - } + <img style="max-width: 200px;" onclick="lightbox.init(this, 200);" src="<?=ImageTools::process($Image)?>" alt="Relevant image" /> +<?php } ?> + </td> + </tr> +<?php + } ?> + <tr> + <td class="label">User comment:</td> + <td colspan="3" class="wrap_overflow"><?=Text::full_format($UserComment)?></td> + </tr> +<?php // END REPORTED STUFF :|: BEGIN MOD STUFF + if ($Status == 'InProgress') { ?> + <tr> + <td class="label">In progress by:</td> + <td colspan="3"> + <a href="user.php?id=<?=$ResolverID?>"><?=$ResolverName?></a> + </td> + </tr> +<?php } + if ($Status != 'Resolved') { ?> + <tr> + <td class="label">Report comment:</td> + <td colspan="3"> + <input type="text" name="comment" id="comment<?=$ReportID?>" size="70" value="<?=$ModComment?>" /> + <input type="button" value="Update now" onclick="UpdateComment(<?=$ReportID?>);" /> + </td> + </tr> + <tr> + <td class="label"> + <a href="javascript:Load('<?=$ReportID?>')" class="tooltip" title="Click here to reset the resolution options to their default values.">Resolve</a>: + </td> + <td colspan="3"> + <select name="resolve_type" id="resolve_type<?=$ReportID?>" onchange="ChangeResolve(<?=$ReportID?>);"> +<?php + $TypeList = $Types['master'] + $Types[$CategoryID]; + $Priorities = []; + foreach ($TypeList as $Key => $Value) { + $Priorities[$Key] = $Value['priority']; + } + array_multisort($Priorities, SORT_ASC, $TypeList); + + foreach ($TypeList as $Type => $Data) { ?> + <option value="<?=$Type?>"><?=$Data['title']?></option> +<?php } ?> + </select> + | <span id="options<?=$ReportID?>"> + <span class="tooltip" title="Warning length in weeks"> + <label for="warning<?=$ReportID?>"><strong>Warning</strong></label> + <select name="warning" id="warning<?=$ReportID?>"> +<?php for ($i = 0; $i < 9; $i++) { ?> + <option value="<?=$i?>"><?=$i?></option> +<?php } ?> + </select> + </span> | +<?php if (check_perms('users_mod')) { ?> + <span class="tooltip" title="Delete torrent?"> + <input type="checkbox" name="delete" id="delete<?=$ReportID?>" /> <label for="delete<?=$ReportID?>"><strong>Delete</strong></label> + </span> | +<?php } ?> + <span class="tooltip" title="Remove upload privileges?"> + <input type="checkbox" name="upload" id="upload<?=$ReportID?>" /> <label for="upload<?=$ReportID?>"><strong>Remove upload privileges</strong></label> + </span> | + <span class="tooltip" title="Update resolve type"> + <input type="button" name="update_resolve" id="update_resolve<?=$ReportID?>" value="Update now" onclick="UpdateResolve(<?=$ReportID?>);" /> + </span> + </span> + </td> + </tr> + <tr> + <td class="label tooltip" title="Uploader: Appended to the regular message unless using "Send now". Reporter: Must be used with "Send now"."> + PM + <select name="pm_type" id="pm_type<?=$ReportID?>"> + <option value="Uploader">Uploader</option> + <option value="Reporter" selected="selected">Reporter</option> + </select>: + </td> + <td colspan="3"> + <textarea name="uploader_pm" id="uploader_pm<?=$ReportID?>" cols="50" rows="2"></textarea> + <input type="button" value="Send now" onclick="SendPM(<?=$ReportID?>);" /> + </td> + </tr> + <tr> + <td class="label"><strong>Extra</strong> log message:</td> + <td> + <input type="text" name="log_message" id="log_message<?=$ReportID?>" size="40"<?php + if ($ExtraIDs) { + $Extras = explode(' ', $ExtraIDs); + $Value = ''; + foreach ($Extras as $ExtraID) { + $Value .= site_url()."torrents.php?torrentid=$ExtraID "; + } + echo ' value="'.trim($Value).'"'; + } elseif (isset($ReportType['extra_log'])) { + printf(' value="%s"', $ReportType['extra_log']); + } ?> + /> + </td> + <td class="label"><strong>Extra</strong> staff notes:</td> + <td> + <input type="text" name="admin_message" id="admin_message<?=$ReportID?>" size="40" /> + </td> + </tr> + <tr> + <td colspan="4" style="text-align: center;"> + <input type="button" value="Invalidate report" onclick="Dismiss(<?=$ReportID?>);" /> + | <input type="button" value="Resolve report manually" onclick="ManualResolve(<?=$ReportID?>);" /> +<?php if ($Status == 'InProgress' && $LoggedUser['ID'] == $ResolverID) { ?> + | <input type="button" value="Unclaim" onclick="GiveBack(<?=$ReportID?>);" /> +<?php } else { ?> + | <input id="grab<?=$ReportID?>" type="button" value="Claim" onclick="Grab(<?=$ReportID?>);" /> +<?php } ?> + | <span class="tooltip" title="All checked reports will be resolved via the Multi-resolve button"> + <input type="checkbox" name="multi" id="multi<?=$ReportID?>" /> <label for="multi">Multi-resolve</label> + </span> + | <input type="button" id="submit_<?=$ReportID?>" value="Submit" onclick="TakeResolve(<?=$ReportID?>);" /> + </td> + </tr> +<?php } else { ?> + <tr> + <td class="label">Resolver:</td> + <td colspan="3"> + <a href="user.php?id=<?=$ResolverID?>"><?=$ResolverName?></a> + </td> + </tr> + <tr> + <td class="label">Resolve time:</td> + <td colspan="3"> + <?=time_diff($LastChangeTime); echo "\n"; ?> + </td> + </tr> + <tr> + <td class="label">Report comments:</td> + <td colspan="3"> + <?=$ModComment; echo "\n"; ?> + </td> + </tr> + <tr> + <td class="label">Log message:</td> + <td colspan="3"> + <?=$LogMessage; echo "\n"; ?> + </td> + </tr> +<?php if ($GroupID) { ?> + <tr> + <td colspan="4" style="text-align: center;"> + <input id="grab<?=$ReportID?>" type="button" value="Claim" onclick="Grab(<?=$ReportID?>);" /> + </td> + </tr> +<?php } + } ?> + </table> + </form> + </div> + <script type="text/javascript">//<![CDATA[ + Load(<?=$ReportID?>); + //]]> + </script> +<?php + } + } } ?> </div> -<? if ($PageLinks) { ?> +<?php +if ($PageLinks) { ?> <div class="linkbox pager"><?=$PageLinks?></div> -<? } ?> -<? View::show_footer(); ?> +<?php +} ?> +<?php View::show_footer(); ?> diff --git a/sections/reportsv2/takereport.php b/sections/reportsv2/takereport.php index 617dba076..afbe3c43f 100644 --- a/sections/reportsv2/takereport.php +++ b/sections/reportsv2/takereport.php @@ -1,10 +1,10 @@ -<? +<?php /* * This page handles the backend from when a user submits a report. * It checks for (in order): * 1. The usual POST injections, then checks that things. * 2. Things that are required by the report type are filled - * ('1' in the report_fields array). + * ('1' in the report_fields array). * 3. Things that are filled are filled with correct things. * 4. That the torrent you're reporting still exists. * @@ -14,121 +14,121 @@ authorize(); if (!is_number($_POST['torrentid'])) { - error(404); + error(404); } else { - $TorrentID = $_POST['torrentid']; + $TorrentID = $_POST['torrentid']; } if (!is_number($_POST['categoryid'])) { - error(404); + error(404); } else { - $CategoryID = $_POST['categoryid']; + $CategoryID = $_POST['categoryid']; } if (!isset($_POST['type'])) { - error(404); + error(404); } elseif (array_key_exists($_POST['type'], $Types[$CategoryID])) { - $Type = $_POST['type']; - $ReportType = $Types[$CategoryID][$Type]; + $Type = $_POST['type']; + $ReportType = $Types[$CategoryID][$Type]; } elseif (array_key_exists($_POST['type'], $Types['master'])) { - $Type = $_POST['type']; - $ReportType = $Types['master'][$Type]; + $Type = $_POST['type']; + $ReportType = $Types['master'][$Type]; } else { - //There was a type but it wasn't an option! - error(403); + //There was a type but it wasn't an option! + error(403); } foreach ($ReportType['report_fields'] as $Field => $Value) { - if ($Value == '1') { - if (empty($_POST[$Field])) { - $Err = "You are missing a required field ($Field) for a ".$ReportType['title'].' report.'; - } - } + if ($Value == '1') { + if (empty($_POST[$Field])) { + $Err = "You are missing a required field ($Field) for a ".$ReportType['title'].' report.'; + } + } } if (!empty($_POST['sitelink'])) { - if (preg_match_all('/'.TORRENT_REGEX.'/i', $_POST['sitelink'], $Matches)) { - $ExtraIDs = implode(' ', $Matches[4]); - if (in_array($TorrentID, $Matches[4])) { - $Err = "The extra permalinks you gave included the link to the torrent you're reporting!"; - } - } else { - $Err = 'The permalink was incorrect. Please copy the torrent permalink URL, which is labelled as [PL] and is found next to the [DL] buttons.'; - } + if (preg_match_all('/'.TORRENT_REGEX.'/i', $_POST['sitelink'], $Matches)) { + $ExtraIDs = implode(' ', $Matches[4]); + if (in_array($TorrentID, $Matches[4])) { + $Err = "The extra permalinks you gave included the link to the torrent you're reporting!"; + } + } else { + $Err = 'The permalink was incorrect. Please copy the torrent permalink URL, which is labelled as [PL] and is found next to the [DL] buttons.'; + } } else { - $ExtraIDs = ''; + $ExtraIDs = ''; } if (!empty($_POST['link'])) { - //resource_type://domain:port/filepathname?query_string#anchor - // http:// www .foo.com /bar - if (preg_match_all('/'.URL_REGEX.'/is', $_POST['link'], $Matches)) { - $Links = implode(' ', $Matches[0]); - } else { - $Err = "The extra links you provided weren't links..."; - } + //resource_type://domain:port/filepathname?query_string#anchor + // http:// www .foo.com /bar + if (preg_match_all('/'.URL_REGEX.'/is', $_POST['link'], $Matches)) { + $Links = implode(' ', $Matches[0]); + } else { + $Err = "The extra links you provided weren't links..."; + } } else { - $Links = ''; + $Links = ''; } if (!empty($_POST['image'])) { - if (preg_match("/^(".IMAGE_REGEX.")( ".IMAGE_REGEX.")*$/is", trim($_POST['image']), $Matches)) { - $Images = $Matches[0]; - } else { - $Err = "The extra image links you provided weren't links to images..."; - } + if (preg_match("/^(".IMAGE_REGEX.")( ".IMAGE_REGEX.")*$/is", trim($_POST['image']), $Matches)) { + $Images = $Matches[0]; + } else { + $Err = "The extra image links you provided weren't links to images..."; + } } else { - $Images = ''; + $Images = ''; } if (!empty($_POST['track'])) { - if (preg_match('/([0-9]+( [0-9]+)*)|All/is', $_POST['track'], $Matches)) { - $Tracks = $Matches[0]; - } else { - $Err = 'Tracks should be given in a space-separated list of numbers with no other characters.'; - } + if (preg_match('/([0-9]+( [0-9]+)*)|All/is', $_POST['track'], $Matches)) { + $Tracks = $Matches[0]; + } else { + $Err = 'Tracks should be given in a space-separated list of numbers with no other characters.'; + } } else { - $Tracks = ''; + $Tracks = ''; } if (!empty($_POST['extra'])) { - $Extra = db_string($_POST['extra']); + $Extra = db_string($_POST['extra']); } else { - $Err = 'As useful as blank reports are, could you be a tiny bit more helpful? (Leave a comment)'; + $Err = 'As useful as blank reports are, could you be a tiny bit more helpful? (Leave a comment)'; } $DB->query(" - SELECT GroupID - FROM torrents - WHERE ID = $TorrentID"); + SELECT GroupID + FROM torrents + WHERE ID = $TorrentID"); if (!$DB->has_results()) { - $Err = "A torrent with that ID doesn't exist!"; + $Err = "A torrent with that ID doesn't exist!"; } list($GroupID) = $DB->next_record(); if (!empty($Err)) { - error($Err); - include(SERVER_ROOT.'/sections/reportsv2/report.php'); - die(); + error($Err); + include(SERVER_ROOT.'/sections/reportsv2/report.php'); + die(); } $DB->query(" - SELECT ID - FROM reportsv2 - WHERE TorrentID = $TorrentID - AND ReporterID = ".db_string($LoggedUser['ID'])." - AND ReportedTime > '".time_minus(3)."'"); + SELECT ID + FROM reportsv2 + WHERE TorrentID = $TorrentID + AND ReporterID = ".db_string($LoggedUser['ID'])." + AND ReportedTime > '".time_minus(3)."'"); if ($DB->has_results()) { - header("Location: torrents.php?torrentid=$TorrentID"); - die(); + header("Location: torrents.php?torrentid=$TorrentID"); + die(); } $DB->query(" - INSERT INTO reportsv2 - (ReporterID, TorrentID, Type, UserComment, Status, ReportedTime, Track, Image, ExtraID, Link) - VALUES - (".db_string($LoggedUser['ID']).", $TorrentID, '".db_string($Type)."', '$Extra', 'New', '".sqltime()."', '".db_string($Tracks)."', '".db_string($Images)."', '".db_string($ExtraIDs)."', '".db_string($Links)."')"); + INSERT INTO reportsv2 + (ReporterID, TorrentID, Type, UserComment, Status, ReportedTime, Track, Image, ExtraID, Link) + VALUES + (".db_string($LoggedUser['ID']).", $TorrentID, '".db_string($Type)."', '$Extra', 'New', '".sqltime()."', '".db_string($Tracks)."', '".db_string($Images)."', '".db_string($ExtraIDs)."', '".db_string($Links)."')"); $ReportID = $DB->inserted_id(); diff --git a/sections/reportsv2/takeresolve.php b/sections/reportsv2/takeresolve.php index bd5746466..35ff1e044 100644 --- a/sections/reportsv2/takeresolve.php +++ b/sections/reportsv2/takeresolve.php @@ -1,4 +1,4 @@ -<? +<?php /* * This is the backend of the AJAXy reports resolve (When you press the shiny submit button). * This page shouldn't output anything except in error. If you do want output, it will be put @@ -7,7 +7,7 @@ */ if (!check_perms('admin_reports')) { - error(403); + error(403); } authorize(); @@ -17,351 +17,351 @@ //If we're here from the delete torrent page instead of the reports page. if (!isset($Escaped['from_delete'])) { - $Report = true; + $Report = true; } elseif (!is_number($Escaped['from_delete'])) { - echo 'Hax occurred in from_delete'; + echo 'Hax occurred in from_delete'; } else { - $Report = false; + $Report = false; } $PMMessage = $_POST['uploader_pm']; if (is_number($Escaped['reportid'])) { - $ReportID = $Escaped['reportid']; + $ReportID = $Escaped['reportid']; } else { - echo 'Hax occurred in the reportid'; - die(); + echo 'Hax occurred in the reportid'; + die(); } if ($Escaped['pm_type'] != 'Uploader') { - $Escaped['uploader_pm'] = ''; + $Escaped['uploader_pm'] = ''; } $UploaderID = (int)$Escaped['uploaderid']; if (!is_number($UploaderID)) { - echo 'Hax occurring on the uploaderid'; - die(); + echo 'Hax occurring on the uploaderid'; + die(); } $Warning = (int)$Escaped['warning']; if (!is_number($Warning)) { - echo 'Hax occurring on the warning'; - die(); + echo 'Hax occurring on the warning'; + die(); } $CategoryID = $Escaped['categoryid']; if (!isset($CategoryID)) { - echo 'Hax occurring on the categoryid'; - die(); + echo 'Hax occurring on the categoryid'; + die(); } $TorrentID = $Escaped['torrentid']; $RawName = $Escaped['raw_name']; if (isset($Escaped['delete']) && $Cache->get_value("torrent_$TorrentID".'_lock')) { - echo "You requested to delete the torrent $TorrentID, but this is currently not possible because the upload process is still running. Please try again later."; - die(); + echo "You requested to delete the torrent $TorrentID, but this is currently not possible because the upload process is still running. Please try again later."; + die(); } if (($Escaped['resolve_type'] == 'manual' || $Escaped['resolve_type'] == 'dismiss') && $Report) { - if ($Escaped['comment']) { - $Comment = $Escaped['comment']; - } else { - if ($Escaped['resolve_type'] == 'manual') { - $Comment = 'Report was resolved manually.'; - } elseif ($Escaped['resolve_type'] == 'dismiss') { - $Comment = 'Report was dismissed as invalid.'; - } - } - - $DB->query(" - UPDATE reportsv2 - SET - Status = 'Resolved', - LastChangeTime = '".sqltime()."', - ModComment = '$Comment', - ResolverID = '".$LoggedUser['ID']."' - WHERE ID = '$ReportID' - AND Status != 'Resolved'"); - - if ($DB->affected_rows() > 0) { - $Cache->delete_value('num_torrent_reportsv2'); - $Cache->delete_value("reports_torrent_$TorrentID"); - } else { - //Someone beat us to it. Inform the staffer. + if ($Escaped['comment']) { + $Comment = $Escaped['comment']; + } else { + if ($Escaped['resolve_type'] == 'manual') { + $Comment = 'Report was resolved manually.'; + } elseif ($Escaped['resolve_type'] == 'dismiss') { + $Comment = 'Report was dismissed as invalid.'; + } + } + + $DB->query(" + UPDATE reportsv2 + SET + Status = 'Resolved', + LastChangeTime = '".sqltime()."', + ModComment = '$Comment', + ResolverID = '".$LoggedUser['ID']."' + WHERE ID = '$ReportID' + AND Status != 'Resolved'"); + + if ($DB->affected_rows() > 0) { + $Cache->delete_value('num_torrent_reportsv2'); + $Cache->delete_value("reports_torrent_$TorrentID"); + } else { + //Someone beat us to it. Inform the staffer. ?> - <table class="layout" cellpadding="5"> - <tr> - <td> - <a href="reportsv2.php?view=report&id=<?=$ReportID?>">Somebody has already resolved this report</a> - <input type="button" value="Clear" onclick="ClearReport(<?=$ReportID?>);" /> - </td> - </tr> - </table> -<? - } - die(); + <table class="layout" cellpadding="5"> + <tr> + <td> + <a href="reportsv2.php?view=report&id=<?=$ReportID?>">Somebody has already resolved this report</a> + <input type="button" value="Clear" onclick="ClearReport(<?=$ReportID?>);" /> + </td> + </tr> + </table> +<?php + } + die(); } if (!isset($Escaped['resolve_type'])) { - echo 'No resolve type'; - die(); + echo 'No resolve type'; + die(); } elseif (array_key_exists($_POST['resolve_type'], $Types[$CategoryID])) { - $ResolveType = $Types[$CategoryID][$_POST['resolve_type']]; + $ResolveType = $Types[$CategoryID][$_POST['resolve_type']]; } elseif (array_key_exists($_POST['resolve_type'], $Types['master'])) { - $ResolveType = $Types['master'][$_POST['resolve_type']]; + $ResolveType = $Types['master'][$_POST['resolve_type']]; } else { - //There was a type but it wasn't an option! - echo 'HAX (Invalid Resolve Type)'; - die(); + //There was a type but it wasn't an option! + echo 'HAX (Invalid Resolve Type)'; + die(); } $DB->query(" - SELECT ID - FROM torrents - WHERE ID = $TorrentID"); + SELECT ID + FROM torrents + WHERE ID = $TorrentID"); $TorrentExists = ($DB->has_results()); if (!$TorrentExists) { - $DB->query(" - UPDATE reportsv2 - SET Status = 'Resolved', - LastChangeTime = '".sqltime()."', - ResolverID = '".$LoggedUser['ID']."', - ModComment = 'Report already dealt with (torrent deleted).' - WHERE ID = $ReportID"); - - $Cache->decrement('num_torrent_reportsv2'); + $DB->query(" + UPDATE reportsv2 + SET Status = 'Resolved', + LastChangeTime = '".sqltime()."', + ResolverID = '".$LoggedUser['ID']."', + ModComment = 'Report already dealt with (torrent deleted).' + WHERE ID = $ReportID"); + + $Cache->decrement('num_torrent_reportsv2'); } if ($Report) { - //Resolve with a parallel check - $DB->query(" - UPDATE reportsv2 - SET Status = 'Resolved', - LastChangeTime = '".sqltime()."', - ResolverID = '".$LoggedUser['ID']."' - WHERE ID = $ReportID - AND Status != 'Resolved'"); + //Resolve with a parallel check + $DB->query(" + UPDATE reportsv2 + SET Status = 'Resolved', + LastChangeTime = '".sqltime()."', + ResolverID = '".$LoggedUser['ID']."' + WHERE ID = $ReportID + AND Status != 'Resolved'"); } //See if it we managed to resolve if ($DB->affected_rows() > 0 || !$Report) { - //We did, lets do all our shit - if ($Report) { - $Cache->decrement('num_torrent_reportsv2'); - } - - if (isset($Escaped['upload'])) { - $Upload = true; - } else { - $Upload = false; - } - - if ($_POST['resolve_type'] === 'tags_lots') { - $DB->query(" - INSERT IGNORE INTO torrents_bad_tags - (TorrentID, UserID, TimeAdded) - VALUES - ($TorrentID, ".$LoggedUser['ID']." , '".sqltime()."')"); - $DB->query(" - SELECT GroupID - FROM torrents - WHERE ID = $TorrentID"); - list($GroupID) = $DB->next_record(); - $Cache->delete_value("torrents_details_$GroupID"); - $SendPM = true; - } - elseif ($_POST['resolve_type'] === 'folders_bad') { - $DB->query(" - INSERT IGNORE INTO torrents_bad_folders - (TorrentID, UserID, TimeAdded) - VALUES - ($TorrentID, ".$LoggedUser['ID'].", '".sqltime()."')"); - $DB->query(" - SELECT GroupID - FROM torrents - WHERE ID = $TorrentID"); - list($GroupID) = $DB->next_record(); - $Cache->delete_value("torrents_details_$GroupID"); - $SendPM = true; - } - elseif ($_POST['resolve_type'] === 'filename') { - $DB->query(" - INSERT IGNORE INTO torrents_bad_files - (TorrentID, UserID, TimeAdded) - VALUES - ($TorrentID, ".$LoggedUser['ID'].", '".sqltime()."')"); - $DB->query(" - SELECT GroupID - FROM torrents - WHERE ID = $TorrentID"); - list($GroupID) = $DB->next_record(); - $Cache->delete_value("torrents_details_$GroupID"); - $SendPM = true; - } - elseif ($_POST['resolve_type'] === 'lineage') { - $DB->query(" - INSERT IGNORE INTO torrents_missing_lineage - (TorrentID, UserID, TimeAdded) - VALUES - ($TorrentID, ".$LoggedUser['ID'].", '".sqltime()."')"); - $DB->query(" - SELECT GroupID - FROM torrents - WHERE ID = $TorrentID"); - list($GroupID) = $DB->next_record(); - $Cache->delete_value("torrents_details_$GroupID"); - } - elseif ($_POST['resolve_type'] === 'lossyapproval') { - $DB->query(" - INSERT INTO torrents_lossymaster_approved - VALUES ($TorrentID, ".$LoggedUser['ID'].", '".sqltime()."')"); - $DB->query(" - SELECT GroupID - FROM torrents - WHERE ID = $TorrentID"); - list($GroupID) = $DB->next_record(); - $Cache->delete_value("torrents_details_$GroupID"); - } - elseif ($_POST['resolve_type'] === 'upload_contest') { - $DB->query(" - INSERT INTO upload_contest - VALUES ($TorrentID, $UploaderID)"); - } - - //Log and delete - if (isset($Escaped['delete']) && check_perms('users_mod')) { - $DB->query(" - SELECT Username - FROM users_main - WHERE ID = $UploaderID"); - list($UpUsername) = $DB->next_record(); - $Log = "Torrent $TorrentID ($RawName) uploaded by $UpUsername was deleted by ".$LoggedUser['Username']; - $Log .= ($Escaped['resolve_type'] == 'custom' ? '' : ' for the reason: '.$ResolveType['title']."."); - if (isset($Escaped['log_message']) && $Escaped['log_message'] != '') { - $Log .= ' ( '.$Escaped['log_message'].' )'; - } - $DB->query(" - SELECT GroupID, hex(info_hash) - FROM torrents - WHERE ID = $TorrentID"); - list($GroupID, $InfoHash) = $DB->next_record(); - Torrents::delete_torrent($TorrentID, 0, $ResolveType['reason']); - - //$InfoHash = unpack("H*", $InfoHash); - $Log .= ' ('.strtoupper($InfoHash).')'; - Misc::write_log($Log); - $Log = 'deleted torrent for the reason: '.$ResolveType['title'].'. ( '.$Escaped['log_message'].' )'; - Torrents::write_group_log($GroupID, $TorrentID, $LoggedUser['ID'], $Log, 0); - $TrumpID = 0; - if ($Escaped['resolve_type'] === 'trump') { - if (preg_match('/torrentid=([0-9]+)/', $Escaped['log_message'], $Matches) === 1) { - $TrumpID = $Matches[1]; - } - } - - Torrents::send_pm($TorrentID, $UploaderID, $RawName, $Log, $TrumpID, (!$Escaped['uploader_pm'] && $Warning <= 0 && !isset($Escaped['delete']) && !$SendPM)); - - } else { - $Log = "No log message (torrent wasn't deleted)."; - } - - //Warnings / remove upload - if ($Upload) { - $Cache->begin_transaction("user_info_heavy_$UploaderID"); - $Cache->update_row(false, array('DisableUpload' => '1')); - $Cache->commit_transaction(0); - - $DB->query(" - UPDATE users_info - SET DisableUpload = '1' - WHERE UserID = $UploaderID"); - } - - if ($Warning > 0) { - $WarnLength = $Warning * (7 * 24 * 60 * 60); - $Reason = "Uploader of torrent ($TorrentID) $RawName which was resolved with the preset: ".$ResolveType['title'].'.'; - if ($Escaped['admin_message']) { - $Reason .= ' ('.$Escaped['admin_message'].').'; - } - if ($Upload) { - $Reason .= ' (Upload privileges removed).'; - } - - Tools::warn_user($UploaderID, $WarnLength, $Reason); - } else { - //This is a bitch for people that don't warn but do other things, it makes me sad. - $AdminComment = ''; - if ($Upload) { - //They removed upload - $AdminComment .= 'Upload privileges removed by '.$LoggedUser['Username']; - $AdminComment .= "\nReason: Uploader of torrent ($TorrentID) ".db_string($RawName).' which was resolved with the preset: '.$ResolveType['title'].". (Report ID: $ReportID)"; - } - if ($Escaped['admin_message']) { - //They did nothing of note, but still want to mark it (Or upload and mark) - $AdminComment .= ' ('.$Escaped['admin_message'].')'; - } - if ($AdminComment) { - $AdminComment = date('Y-m-d') . " - $AdminComment\n\n"; - - $DB->query(" - UPDATE users_info - SET AdminComment = CONCAT('".db_string($AdminComment)."', AdminComment) - WHERE UserID = '".db_string($UploaderID)."'"); - } - } - - //PM - if ($Escaped['uploader_pm'] || $Warning > 0 || isset($Escaped['delete']) || $SendPM) { - if (isset($Escaped['delete'])) { - $PM = '[url='.site_url()."torrents.php?torrentid=$TorrentID]Your above torrent[/url] was reported and has been deleted.\n\n"; - } else { - $PM = '[url='.site_url()."torrents.php?torrentid=$TorrentID]Your above torrent[/url] was reported but not deleted.\n\n"; - } - - $Preset = $ResolveType['resolve_options']['pm']; - - if ($Preset != '') { - $PM .= "Reason: $Preset\n\n"; - } - - if ($Warning > 0) { - $PM .= "This has resulted in a [url=".site_url()."wiki.php?action=article&name=warnings]$Warning week warning.[/url]\n\n"; - } - - if ($Upload) { - $PM .= 'This has '.($Warning > 0 ? 'also ' : '')."resulted in the loss of your upload privileges.\n\n"; - } - - if ($Log) { - $PM .= "Log Message: $Log\n\n"; - } - - if ($Escaped['uploader_pm']) { - $PM .= "Message from ".$LoggedUser['Username'].": $PMMessage\n\n"; - } - - $PM .= "Report was handled by [user]".$LoggedUser['Username'].'[/user].'; - - Misc::send_pm($UploaderID, 0, $Escaped['raw_name'], $PM); - } - - $Cache->delete_value("reports_torrent_$TorrentID"); - - // Now we've done everything, update the DB with values - if ($Report) { - $DB->query(" - UPDATE reportsv2 - SET - Type = '".$Escaped['resolve_type']."', - LogMessage = '".db_string($Log)."', - ModComment = '".$Escaped['comment']."' - WHERE ID = $ReportID"); - } + //We did, lets do all our shit + if ($Report) { + $Cache->decrement('num_torrent_reportsv2'); + } + + if (isset($Escaped['upload'])) { + $Upload = true; + } else { + $Upload = false; + } + + if ($_POST['resolve_type'] === 'tags_lots') { + $DB->query(" + INSERT IGNORE INTO torrents_bad_tags + (TorrentID, UserID, TimeAdded) + VALUES + ($TorrentID, ".$LoggedUser['ID']." , '".sqltime()."')"); + $DB->query(" + SELECT GroupID + FROM torrents + WHERE ID = $TorrentID"); + list($GroupID) = $DB->next_record(); + $Cache->delete_value("torrents_details_$GroupID"); + $SendPM = true; + } + elseif ($_POST['resolve_type'] === 'folders_bad') { + $DB->query(" + INSERT IGNORE INTO torrents_bad_folders + (TorrentID, UserID, TimeAdded) + VALUES + ($TorrentID, ".$LoggedUser['ID'].", '".sqltime()."')"); + $DB->query(" + SELECT GroupID + FROM torrents + WHERE ID = $TorrentID"); + list($GroupID) = $DB->next_record(); + $Cache->delete_value("torrents_details_$GroupID"); + $SendPM = true; + } + elseif ($_POST['resolve_type'] === 'filename') { + $DB->query(" + INSERT IGNORE INTO torrents_bad_files + (TorrentID, UserID, TimeAdded) + VALUES + ($TorrentID, ".$LoggedUser['ID'].", '".sqltime()."')"); + $DB->query(" + SELECT GroupID + FROM torrents + WHERE ID = $TorrentID"); + list($GroupID) = $DB->next_record(); + $Cache->delete_value("torrents_details_$GroupID"); + $SendPM = true; + } + elseif ($_POST['resolve_type'] === 'lineage') { + $DB->query(" + INSERT IGNORE INTO torrents_missing_lineage + (TorrentID, UserID, TimeAdded) + VALUES + ($TorrentID, ".$LoggedUser['ID'].", '".sqltime()."')"); + $DB->query(" + SELECT GroupID + FROM torrents + WHERE ID = $TorrentID"); + list($GroupID) = $DB->next_record(); + $Cache->delete_value("torrents_details_$GroupID"); + } + elseif ($_POST['resolve_type'] === 'lossyapproval') { + $DB->query(" + INSERT INTO torrents_lossymaster_approved + VALUES ($TorrentID, ".$LoggedUser['ID'].", '".sqltime()."')"); + $DB->query(" + SELECT GroupID + FROM torrents + WHERE ID = $TorrentID"); + list($GroupID) = $DB->next_record(); + $Cache->delete_value("torrents_details_$GroupID"); + } + elseif ($_POST['resolve_type'] === 'upload_contest') { + $DB->query(" + INSERT INTO upload_contest + VALUES ($TorrentID, $UploaderID)"); + } + + //Log and delete + if (isset($Escaped['delete']) && check_perms('users_mod')) { + $DB->query(" + SELECT Username + FROM users_main + WHERE ID = $UploaderID"); + list($UpUsername) = $DB->next_record(); + $Log = "Torrent $TorrentID ($RawName) uploaded by $UpUsername was deleted by ".$LoggedUser['Username']; + $Log .= ($Escaped['resolve_type'] == 'custom' ? '' : ' for the reason: '.$ResolveType['title']."."); + if (isset($Escaped['log_message']) && $Escaped['log_message'] != '') { + $Log .= ' ( '.$Escaped['log_message'].' )'; + } + $DB->query(" + SELECT GroupID, hex(info_hash) + FROM torrents + WHERE ID = $TorrentID"); + list($GroupID, $InfoHash) = $DB->next_record(); + Torrents::delete_torrent($TorrentID, 0, $ResolveType['reason']); + + //$InfoHash = unpack("H*", $InfoHash); + $Log .= ' ('.strtoupper($InfoHash).')'; + Misc::write_log($Log); + $Log = 'deleted torrent for the reason: '.$ResolveType['title'].'. ( '.$Escaped['log_message'].' )'; + Torrents::write_group_log($GroupID, $TorrentID, $LoggedUser['ID'], $Log, 0); + $TrumpID = 0; + if ($Escaped['resolve_type'] === 'trump') { + if (preg_match('/torrentid=([0-9]+)/', $Escaped['log_message'], $Matches) === 1) { + $TrumpID = $Matches[1]; + } + } + + Torrents::send_pm($TorrentID, $UploaderID, $RawName, $Log, $TrumpID, (!$Escaped['uploader_pm'] && $Warning <= 0 && !isset($Escaped['delete']) && !$SendPM)); + + } else { + $Log = "No log message (torrent wasn't deleted)."; + } + + //Warnings / remove upload + if ($Upload) { + $Cache->begin_transaction("user_info_heavy_$UploaderID"); + $Cache->update_row(false, array('DisableUpload' => '1')); + $Cache->commit_transaction(0); + + $DB->query(" + UPDATE users_info + SET DisableUpload = '1' + WHERE UserID = $UploaderID"); + } + + if ($Warning > 0) { + $WarnLength = $Warning * (7 * 24 * 60 * 60); + $Reason = "Uploader of torrent ($TorrentID) $RawName which was resolved with the preset: ".$ResolveType['title'].'.'; + if ($Escaped['admin_message']) { + $Reason .= ' ('.$Escaped['admin_message'].').'; + } + if ($Upload) { + $Reason .= ' (Upload privileges removed).'; + } + + Tools::warn_user($UploaderID, $WarnLength, $Reason); + } else { + //This is a bitch for people that don't warn but do other things, it makes me sad. + $AdminComment = ''; + if ($Upload) { + //They removed upload + $AdminComment .= 'Upload privileges removed by '.$LoggedUser['Username']; + $AdminComment .= "\nReason: Uploader of torrent ($TorrentID) ".db_string($RawName).' which was resolved with the preset: '.$ResolveType['title'].". (Report ID: $ReportID)"; + } + if ($Escaped['admin_message']) { + //They did nothing of note, but still want to mark it (Or upload and mark) + $AdminComment .= ' ('.$Escaped['admin_message'].')'; + } + if ($AdminComment) { + $AdminComment = date('Y-m-d') . " - $AdminComment\n\n"; + + $DB->query(" + UPDATE users_info + SET AdminComment = CONCAT('".db_string($AdminComment)."', AdminComment) + WHERE UserID = '".db_string($UploaderID)."'"); + } + } + + //PM + if ($Escaped['uploader_pm'] || $Warning > 0 || isset($Escaped['delete']) || $SendPM) { + if (isset($Escaped['delete'])) { + $PM = '[url='.site_url()."torrents.php?torrentid=$TorrentID]Your above torrent[/url] was reported and has been deleted.\n\n"; + } else { + $PM = '[url='.site_url()."torrents.php?torrentid=$TorrentID]Your above torrent[/url] was reported but not deleted.\n\n"; + } + + $Preset = $ResolveType['resolve_options']['pm']; + + if ($Preset != '') { + $PM .= "Reason: $Preset\n\n"; + } + + if ($Warning > 0) { + $PM .= "This has resulted in a [url=".site_url()."wiki.php?action=article&name=warnings]$Warning week warning.[/url]\n\n"; + } + + if ($Upload) { + $PM .= 'This has '.($Warning > 0 ? 'also ' : '')."resulted in the loss of your upload privileges.\n\n"; + } + + if ($Log) { + $PM .= "Log Message: $Log\n\n"; + } + + if ($Escaped['uploader_pm']) { + $PM .= "Message from ".$LoggedUser['Username'].": $PMMessage\n\n"; + } + + $PM .= "Report was handled by [user]".$LoggedUser['Username'].'[/user].'; + + Misc::send_pm($UploaderID, 0, $Escaped['raw_name'], $PM); + } + + $Cache->delete_value("reports_torrent_$TorrentID"); + + // Now we've done everything, update the DB with values + if ($Report) { + $DB->query(" + UPDATE reportsv2 + SET + Type = '".$Escaped['resolve_type']."', + LogMessage = '".db_string($Log)."', + ModComment = '".$Escaped['comment']."' + WHERE ID = $ReportID"); + } } else { - // Someone beat us to it. Inform the staffer. + // Someone beat us to it. Inform the staffer. ?> <a href="reportsv2.php?view=report&id=<?=$ReportID?>">Somebody has already resolved this report</a> <input type="button" value="Clear" onclick="ClearReport(<?=$ReportID?>);" /> -<? +<?php } diff --git a/sections/reportsv2/views.php b/sections/reportsv2/views.php index 06a8ab6b5..a8827c507 100644 --- a/sections/reportsv2/views.php +++ b/sections/reportsv2/views.php @@ -1,4 +1,4 @@ -<? +<?php /* * This page is to outline all of the views built into reports v2. * It's used as the main page as it also lists the current reports by type @@ -6,7 +6,7 @@ * All the different views are self explanatory by their names. */ if (!check_perms('admin_reports')) { - error(403); + error(403); } View::show_header('Reports V2!', 'reportsv2'); @@ -14,300 +14,301 @@ //Grab owner's ID, just for examples $DB->query(" - SELECT ID, Username - FROM users_main - ORDER BY ID ASC - LIMIT 1"); + SELECT ID, Username + FROM users_main + ORDER BY ID ASC + LIMIT 1"); list($OwnerID, $Owner) = $DB->next_record(); $Owner = display_str($Owner); ?> <div class="header"> - <h2>Reports v2 Information!</h2> -<? include('header.php'); ?> + <h2>Reports v2 Information!</h2> +<?php include('header.php'); ?> </div> <div class="thin float_clear"> - <div class="two_columns pad"> -<? + <div class="two_columns pad"> +<?php $DB->query(" - SELECT - um.ID, - um.Username, - COUNT(r.ID) AS Reports - FROM reportsv2 AS r - JOIN users_main AS um ON um.ID = r.ResolverID - WHERE r.LastChangeTime > NOW() - INTERVAL 24 HOUR - GROUP BY r.ResolverID - ORDER BY Reports DESC"); + SELECT + um.ID, + um.Username, + COUNT(r.ID) AS Reports + FROM reportsv2 AS r + JOIN users_main AS um ON um.ID = r.ResolverID + WHERE r.LastChangeTime > NOW() - INTERVAL 24 HOUR + GROUP BY r.ResolverID + ORDER BY Reports DESC"); $Results = $DB->to_array(); ?> - <h3>Reports resolved in the last 24 hours</h3> - <table class="box border"> - <tr class="colhead"> - <td class="colhead_dark">Username</td> - <td class="colhead_dark number_column">Reports</td> - </tr> -<? + <h3>Reports resolved in the last 24 hours</h3> + <table class="box border"> + <tr class="colhead"> + <td class="colhead_dark">Username</td> + <td class="colhead_dark number_column">Reports</td> + </tr> +<?php foreach ($Results as $Result) { - list($UserID, $Username, $Reports) = $Result; - if ($Username == $LoggedUser['Username']) { - $RowClass = ' class="rowa"'; - } else { - $RowClass = ''; - } + list($UserID, $Username, $Reports) = $Result; + if ($Username == $LoggedUser['Username']) { + $RowClass = ' class="rowa"'; + } else { + $RowClass = ''; + } ?> - <tr<?=$RowClass?>> - <td><a href="reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> - <td class="number_column"><?=number_format($Reports)?></td> - </tr> -<? + <tr<?=$RowClass?>> + <td><a href="reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> + <td class="number_column"><?=number_format($Reports)?></td> + </tr> +<?php } ?> - </table> -<? + </table> +<?php $DB->query(" - SELECT - um.ID, - um.Username, - COUNT(r.ID) AS Reports - FROM reportsv2 AS r - JOIN users_main AS um ON um.ID = r.ResolverID - WHERE r.LastChangeTime > NOW() - INTERVAL 1 WEEK - GROUP BY r.ResolverID - ORDER BY Reports DESC"); + SELECT + um.ID, + um.Username, + COUNT(r.ID) AS Reports + FROM reportsv2 AS r + JOIN users_main AS um ON um.ID = r.ResolverID + WHERE r.LastChangeTime > NOW() - INTERVAL 1 WEEK + GROUP BY r.ResolverID + ORDER BY Reports DESC"); $Results = $DB->to_array(); ?> - <h3>Reports resolved in the last week</h3> - <table class="box border"> - <tr class="colhead"> - <td class="colhead_dark">Username</td> - <td class="colhead_dark number_column">Reports</td> - </tr> -<? + <h3>Reports resolved in the last week</h3> + <table class="box border"> + <tr class="colhead"> + <td class="colhead_dark">Username</td> + <td class="colhead_dark number_column">Reports</td> + </tr> +<?php foreach ($Results as $Result) { - list($UserID, $Username, $Reports) = $Result; - if ($Username == $LoggedUser['Username']) { - $RowClass = ' class="rowa"'; - } else { - $RowClass = ''; - } + list($UserID, $Username, $Reports) = $Result; + if ($Username == $LoggedUser['Username']) { + $RowClass = ' class="rowa"'; + } else { + $RowClass = ''; + } ?> - <tr<?=$RowClass?>> - <td><a href="reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> - <td class="number_column"><?=number_format($Reports)?></td> - </tr> -<? + <tr<?=$RowClass?>> + <td><a href="reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> + <td class="number_column"><?=number_format($Reports)?></td> + </tr> +<?php } ?> - </table> -<? + </table> +<?php $DB->query(" - SELECT - um.ID, - um.Username, - COUNT(r.ID) AS Reports - FROM reportsv2 AS r - JOIN users_main AS um ON um.ID = r.ResolverID - WHERE r.LastChangeTime > NOW() - INTERVAL 1 MONTH - GROUP BY r.ResolverID - ORDER BY Reports DESC"); + SELECT + um.ID, + um.Username, + COUNT(r.ID) AS Reports + FROM reportsv2 AS r + JOIN users_main AS um ON um.ID = r.ResolverID + WHERE r.LastChangeTime > NOW() - INTERVAL 1 MONTH + GROUP BY r.ResolverID + ORDER BY Reports DESC"); $Results = $DB->to_array(); ?> - <h3>Reports resolved in the last month</h3> - <table class="box border"> - <tr class="colhead"> - <td class="colhead_dark">Username</td> - <td class="colhead_dark number_column">Reports</td> - </tr> -<? + <h3>Reports resolved in the last month</h3> + <table class="box border"> + <tr class="colhead"> + <td class="colhead_dark">Username</td> + <td class="colhead_dark number_column">Reports</td> + </tr> +<?php foreach ($Results as $Result) { - list($UserID, $Username, $Reports) = $Result; - if ($Username == $LoggedUser['Username']) { - $RowClass = ' class="rowa"'; - } else { - $RowClass = ''; - } + list($UserID, $Username, $Reports) = $Result; + if ($Username == $LoggedUser['Username']) { + $RowClass = ' class="rowa"'; + } else { + $RowClass = ''; + } ?> - <tr<?=$RowClass?>> - <td><a href="reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> - <td class="number_column"><?=number_format($Reports)?></td> - </tr> -<? + <tr<?=$RowClass?>> + <td><a href="reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> + <td class="number_column"><?=number_format($Reports)?></td> + </tr> +<?php } ?> - </table> -<? + </table> +<?php $DB->query(" - SELECT - um.ID, - um.Username, - COUNT(r.ID) AS Reports - FROM reportsv2 AS r - JOIN users_main AS um ON um.ID = r.ResolverID - GROUP BY r.ResolverID - ORDER BY Reports DESC"); + SELECT + um.ID, + um.Username, + COUNT(r.ID) AS Reports + FROM reportsv2 AS r + JOIN users_main AS um ON um.ID = r.ResolverID + GROUP BY r.ResolverID + ORDER BY Reports DESC"); $Results = $DB->to_array(); ?> - <h3>Reports resolved since Reports v2 (2009-07-27)</h3> - <table class="box border"> - <tr class="colhead"> - <td class="colhead_dark">Username</td> - <td class="colhead_dark number_column">Reports</td> - </tr> -<? + <h3>Reports resolved since Reports v2 (2009-07-27)</h3> + <table class="box border"> + <tr class="colhead"> + <td class="colhead_dark">Username</td> + <td class="colhead_dark number_column">Reports</td> + </tr> +<?php foreach ($Results as $Result) { - list($UserID, $Username, $Reports) = $Result; - if ($Username == $LoggedUser['Username']) { - $RowClass = ' class="rowa"'; - } else { - $RowClass = ''; - } + list($UserID, $Username, $Reports) = $Result; + if ($Username == $LoggedUser['Username']) { + $RowClass = ' class="rowa"'; + } else { + $RowClass = ''; + } ?> - <tr<?=$RowClass?>> - <td><a href="reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> - <td class="number_column"><?=number_format($Reports)?></td> - </tr> -<? + <tr<?=$RowClass?>> + <td><a href="reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> + <td class="number_column"><?=number_format($Reports)?></td> + </tr> +<?php } ?> - </table> - <h3>Different view modes by person</h3> - <div class="box pad"> - <strong>By ID of torrent reported:</strong> - <ul> - <li> - Reports of torrents with ID = 1 - </li> - <li> - <a href="reportsv2.php?view=torrent&id=1"><?=site_url()?>reportsv2.php?view=torrent&id=1</a> - </li> - </ul> - <strong>By group ID of torrent reported:</strong> - <ul> - <li> - Reports of torrents within the group with ID = 1 - </li> - <li> - <a href="reportsv2.php?view=group&id=1"><?=site_url()?>reportsv2.php?view=group&id=1</a> - </li> - </ul> - <strong>By report ID:</strong> - <ul> - <li> - The report with ID = 1 - </li> - <li> - <a href="reportsv2.php?view=report&id=1"><?=site_url()?>reportsv2.php?view=report&id=1</a> - </li> - </ul> - <strong>By reporter ID:</strong> - <ul> - <li> - Reports created by <?=$Owner?> - </li> - <li> - <a href="reportsv2.php?view=reporter&id=<?=$OwnerID?>"><?=site_url()?>reportsv2.php?view=reporter&id=<?=$OwnerID?></a> - </li> - </ul> - <strong>By uploader ID:</strong> - <ul> - <li> - Reports for torrents uploaded by <?=$Owner?> - </li> - <li> - <a href="reportsv2.php?view=uploader&id=<?=$OwnerID?>"><?=site_url()?>reportsv2.php?view=uploader&id=<?=$OwnerID?></a> - </li> - </ul> - <strong>By resolver ID:</strong> - <ul> - <li> - Reports for torrents resolved by <?=$Owner?> - </li> - <li> - <a href="reportsv2.php?view=resolver&id=<?=$OwnerID?>"><?=site_url()?>reportsv2.php?view=resolver&id=<?=$OwnerID?></a> - </li> - </ul> - <strong>For browsing anything more complicated than these, use the search feature.</strong> - </div> - </div> - <div class="two_columns pad"> -<? - $DB->query(" - SELECT - r.ResolverID, - um.Username, - COUNT(r.ID) AS Count - FROM reportsv2 AS r - LEFT JOIN users_main AS um ON r.ResolverID = um.ID - WHERE r.Status = 'InProgress' - GROUP BY r.ResolverID"); + </table> + <h3>Different view modes by person</h3> + <div class="box pad"> + <strong>By ID of torrent reported:</strong> + <ul> + <li> + Reports of torrents with ID = 1 + </li> + <li> + <a href="reportsv2.php?view=torrent&id=1"><?=site_url()?>reportsv2.php?view=torrent&id=1</a> + </li> + </ul> + <strong>By group ID of torrent reported:</strong> + <ul> + <li> + Reports of torrents within the group with ID = 1 + </li> + <li> + <a href="reportsv2.php?view=group&id=1"><?=site_url()?>reportsv2.php?view=group&id=1</a> + </li> + </ul> + <strong>By report ID:</strong> + <ul> + <li> + The report with ID = 1 + </li> + <li> + <a href="reportsv2.php?view=report&id=1"><?=site_url()?>reportsv2.php?view=report&id=1</a> + </li> + </ul> + <strong>By reporter ID:</strong> + <ul> + <li> + Reports created by <?=$Owner?> + </li> + <li> + <a href="reportsv2.php?view=reporter&id=<?=$OwnerID?>"><?=site_url()?>reportsv2.php?view=reporter&id=<?=$OwnerID?></a> + </li> + </ul> + <strong>By uploader ID:</strong> + <ul> + <li> + Reports for torrents uploaded by <?=$Owner?> + </li> + <li> + <a href="reportsv2.php?view=uploader&id=<?=$OwnerID?>"><?=site_url()?>reportsv2.php?view=uploader&id=<?=$OwnerID?></a> + </li> + </ul> + <strong>By resolver ID:</strong> + <ul> + <li> + Reports for torrents resolved by <?=$Owner?> + </li> + <li> + <a href="reportsv2.php?view=resolver&id=<?=$OwnerID?>"><?=site_url()?>reportsv2.php?view=resolver&id=<?=$OwnerID?></a> + </li> + </ul> + <strong>For browsing anything more complicated than these, use the search feature.</strong> + </div> + </div> + <div class="two_columns pad"> +<?php + $DB->query(" + SELECT + r.ResolverID, + um.Username, + COUNT(r.ID) AS Count + FROM reportsv2 AS r + LEFT JOIN users_main AS um ON r.ResolverID = um.ID + WHERE r.Status = 'InProgress' + GROUP BY r.ResolverID"); - $Staff = $DB->to_array(); + $Staff = $DB->to_array(); ?> - <h3>Currently assigned reports by staff member</h3> - <table class="box border"> - <tr class="colhead"> - <td class="colhead_dark">Staff Member</td> - <td class="colhead_dark number_column">Current Count</td> - </tr> -<? - foreach ($Staff as $Array) { - if ($Array['Username'] == $LoggedUser['Username']) { - $RowClass = ' class="rowa"'; - } else { - $RowClass = ''; - } + <h3>Currently assigned reports by staff member</h3> + <table class="box border"> + <tr class="colhead"> + <td class="colhead_dark">Staff Member</td> + <td class="colhead_dark number_column">Current Count</td> + </tr> +<?php + foreach ($Staff as $Array) { + if ($Array['Username'] == $LoggedUser['Username']) { + $RowClass = ' class="rowa"'; + } else { + $RowClass = ''; + } ?> - <tr<?=$RowClass?>> - <td> - <a href="reportsv2.php?view=staff&id=<?=$Array['ResolverID']?>"><?=display_str($Array['Username'])?>'s reports</a> - </td> - <td class="number_column"><?=number_format($Array['Count'])?></td> - </tr> -<? } ?> - </table> - <h3>Different view modes by report type</h3> -<? - $DB->query(" - SELECT - Type, - COUNT(ID) AS Count - FROM reportsv2 - WHERE Status = 'New' - GROUP BY Type"); - $Current = $DB->to_array(); - if (!empty($Current)) { + <tr<?=$RowClass?>> + <td> + <a href="reportsv2.php?view=staff&id=<?=$Array['ResolverID']?>"><?=display_str($Array['Username'])?>'s reports</a> + </td> + <td class="number_column"><?=number_format($Array['Count'])?></td> + </tr> +<?php + } ?> + </table> + <h3>Different view modes by report type</h3> +<?php + $DB->query(" + SELECT + Type, + COUNT(ID) AS Count + FROM reportsv2 + WHERE Status = 'New' + GROUP BY Type"); + $Current = $DB->to_array(); + if (!empty($Current)) { ?> - <table class="box border"> - <tr class="colhead"> - <td class="colhead_dark">Type</td> - <td class="colhead_dark number_column">Current Count</td> - </tr> -<? - foreach ($Current as $Array) { - //Ugliness - foreach ($Types as $Category) { - if (!empty($Category[$Array['Type']])) { - $Title = $Category[$Array['Type']]['title']; - break; - } - } + <table class="box border"> + <tr class="colhead"> + <td class="colhead_dark">Type</td> + <td class="colhead_dark number_column">Current Count</td> + </tr> +<?php + foreach ($Current as $Array) { + //Ugliness + foreach ($Types as $Category) { + if (!empty($Category[$Array['Type']])) { + $Title = $Category[$Array['Type']]['title']; + break; + } + } ?> - <tr<?=$Title === 'Urgent' ? ' class="rowa" style="font-weight: bold;"' : ''?>> - <td> - <a href="reportsv2.php?view=type&id=<?=display_str($Array['Type'])?>"><?=display_str($Title)?></a> - </td> - <td class="number_column"> - <?=number_format($Array['Count'])?> - </td> - </tr> -<? - } - } + <tr<?=$Title === 'Urgent' ? ' class="rowa" style="font-weight: bold;"' : ''?>> + <td> + <a href="reportsv2.php?view=type&id=<?=display_str($Array['Type'])?>"><?=display_str($Title)?></a> + </td> + <td class="number_column"> + <?=number_format($Array['Count'])?> + </td> + </tr> +<?php + } + } ?> - </table> - </div> + </table> + </div> </div> -<? +<?php View::show_footer(); ?> diff --git a/sections/requests/index.php b/sections/requests/index.php index 955ab0557..32c4946bb 100644 --- a/sections/requests/index.php +++ b/sections/requests/index.php @@ -1,4 +1,4 @@ -<? +<?php enforce_login(); $RequestTax = REQUEST_TAX; @@ -8,42 +8,42 @@ $MinimumVote = 20 * 1024 * 1024; if (!empty($LoggedUser['DisableRequests'])) { - error('Your request privileges have been removed.'); + error('Your request privileges have been removed.'); } if (!isset($_REQUEST['action'])) { - include(SERVER_ROOT.'/sections/requests/requests.php'); + include(SERVER_ROOT.'/sections/requests/requests.php'); } else { - switch ($_REQUEST['action']) { - case 'new': - case 'edit': - include(SERVER_ROOT.'/sections/requests/new_edit.php'); - break; - case 'takevote': - include(SERVER_ROOT.'/sections/requests/take_vote.php'); - break; - case 'takefill': - include(SERVER_ROOT.'/sections/requests/take_fill.php'); - break; - case 'takenew': - case 'takeedit': - include(SERVER_ROOT.'/sections/requests/take_new_edit.php'); - break; - case 'delete': - case 'unfill': - include(SERVER_ROOT.'/sections/requests/interim.php'); - break; - case 'takeunfill': - include(SERVER_ROOT.'/sections/requests/take_unfill.php'); - break; - case 'takedelete': - include(SERVER_ROOT.'/sections/requests/take_delete.php'); - break; - case 'view': - case 'viewrequest': - include(SERVER_ROOT.'/sections/requests/request.php'); - break; - default: - error(0); - } + switch ($_REQUEST['action']) { + case 'new': + case 'edit': + include(SERVER_ROOT.'/sections/requests/new_edit.php'); + break; + case 'takevote': + include(SERVER_ROOT.'/sections/requests/take_vote.php'); + break; + case 'takefill': + include(SERVER_ROOT.'/sections/requests/take_fill.php'); + break; + case 'takenew': + case 'takeedit': + include(SERVER_ROOT.'/sections/requests/take_new_edit.php'); + break; + case 'delete': + case 'unfill': + include(SERVER_ROOT.'/sections/requests/interim.php'); + break; + case 'takeunfill': + include(SERVER_ROOT.'/sections/requests/take_unfill.php'); + break; + case 'takedelete': + include(SERVER_ROOT.'/sections/requests/take_delete.php'); + break; + case 'view': + case 'viewrequest': + include(SERVER_ROOT.'/sections/requests/request.php'); + break; + default: + error(0); + } } diff --git a/sections/requests/interim.php b/sections/requests/interim.php index 8920b9322..d85319619 100644 --- a/sections/requests/interim.php +++ b/sections/requests/interim.php @@ -1,51 +1,53 @@ -<? +<?php if (!isset($_GET['id']) || !intval($_GET['id'])) { - error(404); + error(404); } $Action = $_GET['action']; if ($Action !== 'unfill' && $Action !== 'delete') { - error(404); + error(404); } $DB->prepared_query(' - SELECT UserID, FillerID - FROM requests - WHERE ID = ?', - $_GET['id']); + SELECT UserID, FillerID + FROM requests + WHERE ID = ?', + $_GET['id']); list($RequestorID, $FillerID) = $DB->next_record(); if ($Action === 'unfill') { - if ($LoggedUser['ID'] != $RequestorID && $LoggedUser['ID'] != $FillerID && !check_perms('site_moderate_requests')) { - error(403); - } + if ($LoggedUser['ID'] != $RequestorID && $LoggedUser['ID'] != $FillerID && !check_perms('site_moderate_requests')) { + error(403); + } } elseif ($Action === 'delete') { - if ($LoggedUser['ID'] != $RequestorID && !check_perms('site_moderate_requests')) { - error(403); - } + if ($LoggedUser['ID'] != $RequestorID && !check_perms('site_moderate_requests')) { + error(403); + } } View::show_header(ucwords($Action) . ' Request'); ?> <div class="thin center"> - <div class="box" style="width: 600px; margin: 0px auto;"> - <div class="head colhead"> - <?=ucwords($Action)?> Request - </div> - <div class="pad"> - <form class="<?=(($Action === 'delete') ? 'delete_form' : 'edit_form')?>" name="request" action="requests.php" method="post"> - <input type="hidden" name="action" value="take<?=$Action?>" /> - <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> - <input type="hidden" name="id" value="<?=$_GET['id']?>" /> -<? if ($Action === 'delete') { ?> - <div class="warning">You will <strong>not</strong> get your bounty back if you delete this request.</div> -<? } ?> - <strong>Reason:</strong> - <input type="text" name="reason" size="30" /> - <input value="<?=ucwords($Action)?>" type="submit" /> - </form> - </div> - </div> + <div class="box" style="width: 600px; margin: 0px auto;"> + <div class="head colhead"> + <?=ucwords($Action)?> Request + </div> + <div class="pad"> + <form class="<?=(($Action === 'delete') ? 'delete_form' : 'edit_form')?>" name="request" action="requests.php" method="post"> + <input type="hidden" name="action" value="take<?=$Action?>" /> + <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> + <input type="hidden" name="id" value="<?=$_GET['id']?>" /> +<?php + if ($Action === 'delete') { ?> + <div class="warning">You will <strong>not</strong> get your bounty back if you delete this request.</div> +<?php + } ?> + <strong>Reason:</strong> + <input type="text" name="reason" size="30" /> + <input value="<?=ucwords($Action)?>" type="submit" /> + </form> + </div> + </div> </div> -<? +<?php View::show_footer(); diff --git a/sections/requests/new_edit.php b/sections/requests/new_edit.php index 23c1c666f..c2d6074d3 100644 --- a/sections/requests/new_edit.php +++ b/sections/requests/new_edit.php @@ -1,4 +1,4 @@ -<? +<?php /* * Yeah, that's right, edit and new are the same place again. @@ -11,416 +11,416 @@ $RequestTaxPercent = ($RequestTax * 100); if (!$NewRequest) { - $RequestID = $_GET['id']; - if (!intval($RequestID)) { - error(404); - } + $RequestID = $_GET['id']; + if (!intval($RequestID)) { + error(404); + } } if ($NewRequest && ($LoggedUser['BytesUploaded'] < 250 * 1024 * 1024 || !check_perms('site_submit_requests'))) { - error('You do not have enough uploaded to make a request.'); + error('You do not have enough uploaded to make a request.'); } if (!$NewRequest) { - if (empty($ReturnEdit)) { + if (empty($ReturnEdit)) { - $Request = Requests::get_request($RequestID); - if ($Request === false) { - error(404); - } + $Request = Requests::get_request($RequestID); + if ($Request === false) { + error(404); + } - // Define these variables to simplify _GET['groupid'] requests later on - $CategoryID = $Request['CategoryID']; - $Title = $Request['Title']; - $Year = $Request['Year']; - $Image = $Request['Image']; - $ReleaseType = $Request['ReleaseType']; - $GroupID = $Request['GroupID']; + // Define these variables to simplify _GET['groupid'] requests later on + $CategoryID = $Request['CategoryID']; + $Title = $Request['Title']; + $Year = $Request['Year']; + $Image = $Request['Image']; + $ReleaseType = $Request['ReleaseType']; + $GroupID = $Request['GroupID']; - $VoteArray = Requests::get_votes_array($RequestID); - $VoteCount = count($VoteArray['Voters']); + $VoteArray = Requests::get_votes_array($RequestID); + $VoteCount = count($VoteArray['Voters']); - $LogCue = $Request['LogCue']; - $NeedCue = (strpos($LogCue, 'Cue') !== false); - $NeedLog = (strpos($LogCue, 'Log') !== false); - if ($NeedLog) { - if (strpos($LogCue, '%') !== false) { - preg_match('/\d+/', $LogCue, $Matches); - $MinLogScore = (int)$Matches[0]; - } - } - $Checksum = $Request['Checksum'] ? 1 : 0; + $LogCue = $Request['LogCue']; + $NeedCue = (strpos($LogCue, 'Cue') !== false); + $NeedLog = (strpos($LogCue, 'Log') !== false); + if ($NeedLog) { + if (strpos($LogCue, '%') !== false) { + preg_match('/\d+/', $LogCue, $Matches); + $MinLogScore = (int)$Matches[0]; + } + } + $Checksum = $Request['Checksum'] ? 1 : 0; - $IsFilled = !empty($Request['TorrentID']); - $CategoryName = $CategoriesV2[$CategoryID - 1]; + $IsFilled = !empty($Request['TorrentID']); + $CategoryName = $CategoriesV2[$CategoryID - 1]; - $CanEdit = ((!$IsFilled && $LoggedUser['ID'] === $Request['UserID'] && $VoteCount < 2) || check_perms('site_moderate_requests')); - if (!$CanEdit) { - error(403); - } + $CanEdit = ((!$IsFilled && $LoggedUser['ID'] === $Request['UserID'] && $VoteCount < 2) || check_perms('site_moderate_requests')); + if (!$CanEdit) { + error(403); + } - if ($CategoryName === 'Music') { - $ArtistForm = Requests::get_artists($RequestID); + if ($CategoryName === 'Music') { + $ArtistForm = Requests::get_artists($RequestID); - $BitrateArray = []; - if ($Request['BitrateList'] == 'Any') { - $BitrateArray = array_keys($Bitrates); - } else { - $BitrateArray = array_keys(array_intersect($Bitrates, explode('|', $Request['BitrateList']))); - } + $BitrateArray = []; + if ($Request['BitrateList'] == 'Any') { + $BitrateArray = array_keys($Bitrates); + } else { + $BitrateArray = array_keys(array_intersect($Bitrates, explode('|', $Request['BitrateList']))); + } - $FormatArray = []; - if ($Request['FormatList'] == 'Any') { - $FormatArray = array_keys($Formats); - } else { - foreach ($Formats as $Key => $Val) { - if (strpos($Request['FormatList'], $Val) !== false) { - $FormatArray[] = $Key; - } - } - } + $FormatArray = []; + if ($Request['FormatList'] == 'Any') { + $FormatArray = array_keys($Formats); + } else { + foreach ($Formats as $Key => $Val) { + if (strpos($Request['FormatList'], $Val) !== false) { + $FormatArray[] = $Key; + } + } + } - $MediaArray = []; - if ($Request['MediaList'] == 'Any') { - $MediaArray = array_keys($Media); - } else { - $MediaTemp = explode('|', $Request['MediaList']); - foreach ($Media as $Key => $Val) { - if (in_array($Val, $MediaTemp)) { - $MediaArray[] = $Key; - } - } - } - } + $MediaArray = []; + if ($Request['MediaList'] == 'Any') { + $MediaArray = array_keys($Media); + } else { + $MediaTemp = explode('|', $Request['MediaList']); + foreach ($Media as $Key => $Val) { + if (in_array($Val, $MediaTemp)) { + $MediaArray[] = $Key; + } + } + } + } - $Tags = implode(', ', $Request['Tags']); - } + $Tags = implode(', ', $Request['Tags']); + } } if ($NewRequest && !empty($_GET['artistid']) && intval($_GET['artistid'])) { - $DB->prepared_query(' - SELECT Name - FROM artists_group - WHERE artistid = ? - LIMIT 1', - $_GET['artistid'] - ); - list($ArtistName) = $DB->next_record(); - $ArtistForm = [ - 1 => [['name' => trim($ArtistName)]], - 2 => [], - 3 => [] - ]; + $DB->prepared_query(' + SELECT Name + FROM artists_group + WHERE artistid = ? + LIMIT 1', + $_GET['artistid'] + ); + list($ArtistName) = $DB->next_record(); + $ArtistForm = [ + 1 => [['name' => trim($ArtistName)]], + 2 => [], + 3 => [] + ]; } elseif ($NewRequest && !empty($_GET['groupid']) && intval($_GET['groupid'])) { - $ArtistForm = Artists::get_artist($_GET['groupid']); - $DB->prepared_query(" - SELECT - tg.Name, - tg.Year, - tg.ReleaseType, - tg.WikiImage, - GROUP_CONCAT(t.Name SEPARATOR ', '), - tg.CategoryID - FROM torrents_group AS tg - INNER JOIN torrents_tags AS tt ON (tt.GroupID = tg.ID) - INNER JOIN tags AS t ON (t.ID = tt.TagID) - WHERE tg.ID = ?", - $_GET['groupid'] - ); - if (list($Title, $Year, $ReleaseType, $Image, $Tags, $CategoryID) = $DB->next_record()) { - $GroupID = trim($_REQUEST['groupid']); - } + $ArtistForm = Artists::get_artist($_GET['groupid']); + $DB->prepared_query(" + SELECT + tg.Name, + tg.Year, + tg.ReleaseType, + tg.WikiImage, + GROUP_CONCAT(t.Name SEPARATOR ', '), + tg.CategoryID + FROM torrents_group AS tg + INNER JOIN torrents_tags AS tt ON (tt.GroupID = tg.ID) + INNER JOIN tags AS t ON (t.ID = tt.TagID) + WHERE tg.ID = ?", + $_GET['groupid'] + ); + if (list($Title, $Year, $ReleaseType, $Image, $Tags, $CategoryID) = $DB->next_record()) { + $GroupID = trim($_REQUEST['groupid']); + } } View::show_header(($NewRequest ? 'Create a request' : 'Edit a request'), 'requests,form_validate'); ?> <div class="thin"> - <div class="header"> - <h2><?=($NewRequest ? 'Create a request' : 'Edit a request')?></h2> - </div> + <div class="header"> + <h2><?=($NewRequest ? 'Create a request' : 'Edit a request')?></h2> + </div> - <div class="box pad"> - <form action="" method="post" id="request_form" onsubmit="Calculate();"> - <div> -<? if (!$NewRequest) { ?> - <input type="hidden" name="requestid" value="<?=$RequestID?>" /> -<? } ?> - <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> - <input type="hidden" name="action" value="<?=($NewRequest ? 'takenew' : 'takeedit')?>" /> - </div> + <div class="box pad"> + <form action="" method="post" id="request_form" onsubmit="Calculate();"> + <div> +<?php if (!$NewRequest) { ?> + <input type="hidden" name="requestid" value="<?=$RequestID?>" /> +<?php } ?> + <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> + <input type="hidden" name="action" value="<?=($NewRequest ? 'takenew' : 'takeedit')?>" /> + </div> - <table class="layout"> - <tr> - <td colspan="2" class="center">Please make sure your request follows <a href="rules.php?p=requests">the request rules</a>!</td> - </tr> -<? if ($NewRequest || $CanEdit) { ?> - <tr> - <td class="label"> - Type - </td> - <td> - <select id="categories" name="type" onchange="Categories();"> -<? foreach (Misc::display_array($CategoriesV2) as $Cat) { ?> - <option value="<?=$Cat?>"<?=(!empty($CategoryName) && ($CategoryName === $Cat) ? ' selected="selected"' : '')?>><?=$Cat?></option> -<? } ?> - </select> - </td> - </tr> - <tr id="artist_tr"> - <td class="label">Artist(s)</td> - <td id="artistfields"> - <p id="vawarning" class="hidden">Please use the multiple artists feature rather than adding "Various Artists" as an artist; read <a href="wiki.php?action=article&id=64" target="_blank">this</a> for more information.</p> -<? - if (!empty($ArtistForm)) { - $First = true; - $cnt = 0; - foreach ($ArtistForm as $Importance => $ArtistNames) { - foreach ($ArtistNames as $Artist) { + <table class="layout"> + <tr> + <td colspan="2" class="center">Please make sure your request follows <a href="rules.php?p=requests">the request rules</a>!</td> + </tr> +<?php if ($NewRequest || $CanEdit) { ?> + <tr> + <td class="label"> + Type + </td> + <td> + <select id="categories" name="type" onchange="Categories();"> +<?php foreach (Misc::display_array($CategoriesV2) as $Cat) { ?> + <option value="<?=$Cat?>"<?=(!empty($CategoryName) && ($CategoryName === $Cat) ? ' selected="selected"' : '')?>><?=$Cat?></option> +<?php } ?> + </select> + </td> + </tr> + <tr id="artist_tr"> + <td class="label">Artist(s)</td> + <td id="artistfields"> + <p id="vawarning" class="hidden">Please use the multiple artists feature rather than adding "Various Artists" as an artist; read <a href="wiki.php?action=article&id=64" target="_blank">this</a> for more information.</p> +<?php + if (!empty($ArtistForm)) { + $First = true; + $cnt = 0; + foreach ($ArtistForm as $Importance => $ArtistNames) { + foreach ($ArtistNames as $Artist) { ?> - <input type="text" id="artist_<?=$cnt ?>" name="artists[]"<? Users::has_autocomplete_enabled('other'); ?> size="45" value="<?=display_str($Artist['name']) ?>" /> - <select id="importance" name="importance[]"> - <option value="1"<?=($Importance == '1' ? ' selected="selected"' : '')?>>Main</option> - <option value="2"<?=($Importance == '2' ? ' selected="selected"' : '')?>>Guest</option> - <option value="4"<?=($Importance == '4' ? ' selected="selected"' : '')?>>Composer</option> - <option value="5"<?=($Importance == '5' ? ' selected="selected"' : '')?>>Conductor</option> - <option value="6"<?=($Importance == '6' ? ' selected="selected"' : '')?>>DJ / Compiler</option> - <option value="3"<?=($Importance == '3' ? ' selected="selected"' : '')?>>Remixer</option> - <option value="7"<?=($Importance == '7' ? ' selected="selected"' : '')?>>Producer</option> - </select> - <? if ($First) { ?><a href="#" onclick="AddArtistField(); return false;" class="brackets">+</a> <a href="#" onclick="RemoveArtistField(); return false;" class="brackets">−</a><? } $First = false; ?> - <br /> -<? - $cnt++; - } - } - } else { -?> <input type="text" id="artist_0" name="artists[]"<? Users::has_autocomplete_enabled('other'); ?> size="45" onblur="CheckVA();" /> - <select id="importance" name="importance[]"> - <option value="1">Main</option> - <option value="2">Guest</option> - <option value="4">Composer</option> - <option value="5">Conductor</option> - <option value="6">DJ / Compiler</option> - <option value="3">Remixer</option> - <option value="7">Producer</option> - </select> - <a href="#" onclick="AddArtistField(); return false;" class="brackets">+</a> <a href="#" onclick="RemoveArtistField(); return false;" class="brackets">−</a> -<? - } + <input type="text" id="artist_<?=$cnt ?>" name="artists[]"<?php Users::has_autocomplete_enabled('other'); ?> size="45" value="<?=display_str($Artist['name']) ?>" /> + <select id="importance" name="importance[]"> + <option value="1"<?=($Importance == '1' ? ' selected="selected"' : '')?>>Main</option> + <option value="2"<?=($Importance == '2' ? ' selected="selected"' : '')?>>Guest</option> + <option value="4"<?=($Importance == '4' ? ' selected="selected"' : '')?>>Composer</option> + <option value="5"<?=($Importance == '5' ? ' selected="selected"' : '')?>>Conductor</option> + <option value="6"<?=($Importance == '6' ? ' selected="selected"' : '')?>>DJ / Compiler</option> + <option value="3"<?=($Importance == '3' ? ' selected="selected"' : '')?>>Remixer</option> + <option value="7"<?=($Importance == '7' ? ' selected="selected"' : '')?>>Producer</option> + </select> + <?php if ($First) { ?><a href="#" onclick="AddArtistField(); return false;" class="brackets">+</a> <a href="#" onclick="RemoveArtistField(); return false;" class="brackets">−</a><?php } $First = false; ?> + <br /> +<?php + $cnt++; + } + } + } else { +?> <input type="text" id="artist_0" name="artists[]"<?php Users::has_autocomplete_enabled('other'); ?> size="45" onblur="CheckVA();" /> + <select id="importance" name="importance[]"> + <option value="1">Main</option> + <option value="2">Guest</option> + <option value="4">Composer</option> + <option value="5">Conductor</option> + <option value="6">DJ / Compiler</option> + <option value="3">Remixer</option> + <option value="7">Producer</option> + </select> + <a href="#" onclick="AddArtistField(); return false;" class="brackets">+</a> <a href="#" onclick="RemoveArtistField(); return false;" class="brackets">−</a> +<?php + } ?> - </td> - </tr> - <tr> - <td class="label">Title</td> - <td> - <input type="text" name="title" size="45" value="<?=(!empty($Title) ? $Title : '')?>" /> - </td> - </tr> - <tr id="recordlabel_tr"> - <td class="label">Record label</td> - <td> - <input type="text" name="recordlabel" size="45" value="<?=(!empty($Request['RecordLabel']) ? $Request['RecordLabel'] : '')?>" /> - </td> - </tr> - <tr id="cataloguenumber_tr"> - <td class="label">Catalogue number</td> - <td> - <input type="text" name="cataloguenumber" size="15" value="<?=(!empty($Request['CatalogueNumber']) ? $Request['CatalogueNumber'] : '')?>" /> - </td> - </tr> - <tr id="oclc_tr"> - <td class="label">WorldCat (OCLC) ID</td> - <td> - <input type="text" name="oclc" size="15" value="<?=(!empty($Request['OCLC']) ? $Request['OCLC'] : '')?>" /> - </td> - </tr> -<? } ?> - <tr id="year_tr"> - <td class="label">Year</td> - <td> - <input type="text" name="year" size="5" value="<?=(!empty($Year) ? $Year : '')?>" /> - </td> - </tr> -<? if ($NewRequest || $CanEdit) { ?> - <tr id="image_tr"> - <td class="label">Image</td> - <td> - <input type="text" name="image" size="45" value="<?=(!empty($Image) ? $Image : '')?>" /> - </td> - </tr> -<? } ?> - <tr> - <td class="label">Tags</td> - <td> -<? - $GenreTags = $Cache->get_value('genre_tags'); - if (!$GenreTags) { - $DB->prepared_query(' - SELECT Name - FROM tags - WHERE TagType = ? - ORDER BY Name', - 'genre' - ); - $GenreTags = $DB->collect('Name'); - $Cache->cache_value('genre_tags', $GenreTags, 3600 * 6); - } + </td> + </tr> + <tr> + <td class="label">Title</td> + <td> + <input type="text" name="title" size="45" value="<?=(!empty($Title) ? $Title : '')?>" /> + </td> + </tr> + <tr id="recordlabel_tr"> + <td class="label">Record label</td> + <td> + <input type="text" name="recordlabel" size="45" value="<?=(!empty($Request['RecordLabel']) ? $Request['RecordLabel'] : '')?>" /> + </td> + </tr> + <tr id="cataloguenumber_tr"> + <td class="label">Catalogue number</td> + <td> + <input type="text" name="cataloguenumber" size="15" value="<?=(!empty($Request['CatalogueNumber']) ? $Request['CatalogueNumber'] : '')?>" /> + </td> + </tr> + <tr id="oclc_tr"> + <td class="label">WorldCat (OCLC) ID</td> + <td> + <input type="text" name="oclc" size="15" value="<?=(!empty($Request['OCLC']) ? $Request['OCLC'] : '')?>" /> + </td> + </tr> +<?php } ?> + <tr id="year_tr"> + <td class="label">Year</td> + <td> + <input type="text" name="year" size="5" value="<?=(!empty($Year) ? $Year : '')?>" /> + </td> + </tr> +<?php if ($NewRequest || $CanEdit) { ?> + <tr id="image_tr"> + <td class="label">Image</td> + <td> + <input type="text" name="image" size="45" value="<?=(!empty($Image) ? $Image : '')?>" /> + </td> + </tr> +<?php } ?> + <tr> + <td class="label">Tags</td> + <td> +<?php + $GenreTags = $Cache->get_value('genre_tags'); + if (!$GenreTags) { + $DB->prepared_query(' + SELECT Name + FROM tags + WHERE TagType = ? + ORDER BY Name', + 'genre' + ); + $GenreTags = $DB->collect('Name'); + $Cache->cache_value('genre_tags', $GenreTags, 3600 * 6); + } ?> - <select id="genre_tags" name="genre_tags" onchange="add_tag(); return false;"> - <option>---</option> -<? foreach (Misc::display_array($GenreTags) as $Genre) { ?> - <option value="<?=$Genre?>"><?=$Genre?></option> -<? } ?> - </select> - <input type="text" id="tags" name="tags" size="45" value="<?=(!empty($Tags) ? display_str($Tags) : '')?>"<? Users::has_autocomplete_enabled('other'); ?> /> - <br /> - Tags should be comma-separated, and you should use a period (".") to separate words inside a tag — e.g. "<strong class="important_text_alt">hip.hop</strong>". - <br /><br /> - There is a list of official tags to the left of the text box. Please use these tags instead of "unofficial" tags (e.g. use the official "<strong class="important_text_alt">drum.and.bass</strong>" tag, instead of an unofficial "<strong class="important_text">dnb</strong>" tag.). - </td> - </tr> -<? if ($NewRequest || $CanEdit) { ?> - <tr id="releasetypes_tr"> - <td class="label">Release type</td> - <td> - <select id="releasetype" name="releasetype"> - <option value="0">---</option> -<? - foreach ($ReleaseTypes as $Key => $Val) { -?> <option value="<?=$Key?>"<?=!empty($ReleaseType) ? ($Key == $ReleaseType ? ' selected="selected"' : '') : '' ?>><?=$Val?></option> -<? - } + <select id="genre_tags" name="genre_tags" onchange="add_tag(); return false;"> + <option>---</option> +<?php foreach (Misc::display_array($GenreTags) as $Genre) { ?> + <option value="<?=$Genre?>"><?=$Genre?></option> +<?php } ?> + </select> + <input type="text" id="tags" name="tags" size="45" value="<?=(!empty($Tags) ? display_str($Tags) : '')?>"<?php Users::has_autocomplete_enabled('other'); ?> /> + <br /> + Tags should be comma-separated, and you should use a period (".") to separate words inside a tag — e.g. "<strong class="important_text_alt">hip.hop</strong>". + <br /><br /> + There is a list of official tags to the left of the text box. Please use these tags instead of "unofficial" tags (e.g. use the official "<strong class="important_text_alt">drum.and.bass</strong>" tag, instead of an unofficial "<strong class="important_text">dnb</strong>" tag.). + </td> + </tr> +<?php if ($NewRequest || $CanEdit) { ?> + <tr id="releasetypes_tr"> + <td class="label">Release type</td> + <td> + <select id="releasetype" name="releasetype"> + <option value="0">---</option> +<?php + foreach ($ReleaseTypes as $Key => $Val) { +?> <option value="<?=$Key?>"<?=!empty($ReleaseType) ? ($Key == $ReleaseType ? ' selected="selected"' : '') : '' ?>><?=$Val?></option> +<?php + } ?> - </select> - </td> - </tr> - <tr id="formats_tr"> - <td class="label">Allowed formats</td> - <td> - <input type="checkbox" name="all_formats" id="toggle_formats" onchange="Toggle('formats', <?=($NewRequest ? 1 : 0)?>);"<?=!empty($FormatArray) && (count($FormatArray) === count($Formats)) ? ' checked="checked"' : ''; ?> /><label for="toggle_formats"> All</label> - <span style="float: right;"><strong>NB: You cannot require a log or cue unless FLAC is an allowed format</strong></span> -<? foreach ($Formats as $Key => $Val) { - if ($Key % 8 === 0) { - echo '<br />'; - } ?> - <input type="checkbox" name="formats[]" value="<?=$Key?>" onchange="ToggleLogCue(); if (!this.checked) { $('#toggle_formats').raw().checked = false; }" id="format_<?=$Key?>" - <?=(!empty($FormatArray) && in_array($Key, $FormatArray) ? ' checked="checked"' : '')?> /><label for="format_<?=$Key?>"> <?=$Val?></label> -<? } ?> - </td> - </tr> - <tr id="bitrates_tr"> - <td class="label">Allowed bitrates</td> - <td> - <input type="checkbox" name="all_bitrates" id="toggle_bitrates" onchange="Toggle('bitrates', <?=($NewRequest ? 1 : 0)?>);"<?=(!empty($BitrateArray) && (count($BitrateArray) === count($Bitrates)) ? ' checked="checked"' : '')?> /><label for="toggle_bitrates"> All</label> -<? foreach ($Bitrates as $Key => $Val) { - if ($Key % 8 === 0) { - echo '<br />'; - } ?> - <input type="checkbox" name="bitrates[]" value="<?=$Key?>" id="bitrate_<?=$Key?>" - <?=(!empty($BitrateArray) && in_array($Key, $BitrateArray) ? ' checked="checked" ' : '')?> - onchange="if (!this.checked) { $('#toggle_bitrates').raw().checked = false; }" /><label for="bitrate_<?=$Key?>"> <?=$Val?></label> -<? } ?> - </td> - </tr> - <tr id="media_tr"> - <td class="label">Allowed media</td> - <td> - <input type="checkbox" name="all_media" id="toggle_media" onchange="Toggle('media', <?=($NewRequest ? 1 : 0)?>);"<?=(!empty($MediaArray) && (count($MediaArray) === count($Media)) ? ' checked="checked"' : '')?> /><label for="toggle_media"> All</label> -<? foreach ($Media as $Key => $Val) { - if ($Key % 8 === 0) { - echo '<br />'; - } ?> - <input type="checkbox" name="media[]" value="<?=$Key?>" id="media_<?=$Key?>" - <?=(!empty($MediaArray) && in_array($Key, $MediaArray) ? ' checked="checked" ' : '')?> - onchange="if (!this.checked) { $('#toggle_media').raw().checked = false; }" /><label for="media_<?=$Key?>"> <?=$Val?></label> -<? } ?> - </td> - </tr> - <tr id="logcue_tr" class="hidden"> - <td class="label">Log / Checksum / Cue<br />(CD FLAC only)</td> - <td> - <input type="checkbox" id="needlog" name="needlog" onchange="ToggleLogScore()" <?=(!empty($NeedLog) ? 'checked="checked" ' : '')?>/><label for="needlog"> Require log</label> - <span id="minlogscore_span" class="hidden"> <input type="text" name="minlogscore" id="minlogscore" size="4" value="<?=(!empty($MinLogScore) ? $MinLogScore : '')?>" /> Minimum log score</span> - <br /> - <input type="checkbox" id="needcksum" name="needcksum"<?=$Checksum ? ' checked="checked" ' : ''?>/><label for="needcksum"> Require checksum</label> - <br /> - <input type="checkbox" id="needcue" name="needcue" <?=(!empty($NeedCue) ? 'checked="checked" ' : '')?>/><label for="needcue"> Require cue file</label> - </td> - </tr> -<? } ?> - <tr> - <td class="label">Description</td> - <td> - <textarea name="description" cols="70" rows="7"><?=(!empty($Request['Description']) ? $Request['Description'] : '')?></textarea> <br /> - </td> - </tr> -<? if (check_perms('site_moderate_requests')) { ?> - <tr> - <td class="label">Torrent group</td> - <td> - <?=site_url()?>torrents.php?id=<input type="text" name="groupid" value="<?=$GroupID?>" size="15" /><br /> - If this request matches a torrent group <span style="font-weight: bold;">already existing</span> on the site, please indicate that here. - </td> - </tr> -<? } elseif ($GroupID && ($CategoryID == 1)) { ?> - <tr> - <td class="label">Torrent group</td> - <td> - <a href="torrents.php?id=<?=$GroupID?>"><?=site_url()?>torrents.php?id=<?=$GroupID?></a><br /> - This request <?=($NewRequest ? 'will be' : 'is')?> associated with the above torrent group. -<? if (!$NewRequest) { ?> - If this is incorrect, please <a href="reports.php?action=report&type=request&id=<?=$RequestID?>">report this request</a> so that staff can fix it. -<? } ?> - <input type="hidden" name="groupid" value="<?=$GroupID?>" /> - </td> - </tr> -<? } - if ($NewRequest) { ?> - <tr id="voting"> - <td class="label">Bounty (MB)</td> - <td> - <input type="text" id="amount_box" size="8" value="<?=(!empty($Bounty) ? $Bounty : '100')?>" onchange="Calculate();" /> - <select id="unit" name="unit" onchange="Calculate();"> - <option value="mb"<?=(!empty($_POST['unit']) && $_POST['unit'] === 'mb' ? ' selected="selected"' : '') ?>>MB</option> - <option value="gb"<?=(!empty($_POST['unit']) && $_POST['unit'] === 'gb' ? ' selected="selected"' : '') ?>>GB</option> - </select> - <input type="button" value="Preview" onclick="Calculate();" /> - <?= $RequestTax > 0 ? "<strong>{$RequestTaxPercent}% of this is deducted as tax by the system.</strong>" : '' ?> - <p>Bounty must be greater than or equal to 100 MB.</p> - </td> - </tr> - <tr> - <td class="label">Bounty information</td> - <td> - <input type="hidden" id="amount" name="amount" value="<?=(!empty($Bounty) ? $Bounty : '100')?>" /> - <input type="hidden" id="current_uploaded" value="<?=$LoggedUser['BytesUploaded']?>" /> - <input type="hidden" id="current_downloaded" value="<?=$LoggedUser['BytesDownloaded']?>" /> - <?= $RequestTax > 0 - ? 'Bounty after tax: <strong><span id="bounty_after_tax"><?=sprintf("%0.2f", 100 * (1 - $RequestTax))?> MB</span></strong><br />' - : '<span id="bounty_after_tax" style="display: none;"><?=sprintf("%0.2f", 100 * (1 - $RequestTax))?> MB</span>' - ?> - If you add the entered <strong><span id="new_bounty">100.00 MB</span></strong> of bounty, your new stats will be: <br /> - Uploaded: <span id="new_uploaded"><?=Format::get_size($LoggedUser['BytesUploaded'])?></span><br /> - Ratio: <span id="new_ratio"><?=Format::get_ratio_html($LoggedUser['BytesUploaded'], $LoggedUser['BytesDownloaded'])?></span> - </td> - </tr> - <tr> - <td colspan="2" class="center"> - <input type="submit" id="button" value="Create request" disabled="disabled" /> - </td> - </tr> -<? } else { ?> - <tr> - <td colspan="2" class="center"> - <input type="submit" id="button" value="Edit request" /> - </td> - </tr> -<? } ?> - </table> - </form> - <script type="text/javascript">ToggleLogCue();<?=$NewRequest ? " Calculate();" : '' ?></script> - <script type="text/javascript">Categories();</script> - </div> + </select> + </td> + </tr> + <tr id="formats_tr"> + <td class="label">Allowed formats</td> + <td> + <input type="checkbox" name="all_formats" id="toggle_formats" onchange="Toggle('formats', <?=($NewRequest ? 1 : 0)?>);"<?=!empty($FormatArray) && (count($FormatArray) === count($Formats)) ? ' checked="checked"' : ''; ?> /><label for="toggle_formats"> All</label> + <span style="float: right;"><strong>NB: You cannot require a log or cue unless FLAC is an allowed format</strong></span> +<?php foreach ($Formats as $Key => $Val) { + if ($Key % 8 === 0) { + echo '<br />'; + } ?> + <input type="checkbox" name="formats[]" value="<?=$Key?>" onchange="ToggleLogCue(); if (!this.checked) { $('#toggle_formats').raw().checked = false; }" id="format_<?=$Key?>" + <?=(!empty($FormatArray) && in_array($Key, $FormatArray) ? ' checked="checked"' : '')?> /><label for="format_<?=$Key?>"> <?=$Val?></label> +<?php } ?> + </td> + </tr> + <tr id="bitrates_tr"> + <td class="label">Allowed bitrates</td> + <td> + <input type="checkbox" name="all_bitrates" id="toggle_bitrates" onchange="Toggle('bitrates', <?=($NewRequest ? 1 : 0)?>);"<?=(!empty($BitrateArray) && (count($BitrateArray) === count($Bitrates)) ? ' checked="checked"' : '')?> /><label for="toggle_bitrates"> All</label> +<?php foreach ($Bitrates as $Key => $Val) { + if ($Key % 8 === 0) { + echo '<br />'; + } ?> + <input type="checkbox" name="bitrates[]" value="<?=$Key?>" id="bitrate_<?=$Key?>" + <?=(!empty($BitrateArray) && in_array($Key, $BitrateArray) ? ' checked="checked" ' : '')?> + onchange="if (!this.checked) { $('#toggle_bitrates').raw().checked = false; }" /><label for="bitrate_<?=$Key?>"> <?=$Val?></label> +<?php } ?> + </td> + </tr> + <tr id="media_tr"> + <td class="label">Allowed media</td> + <td> + <input type="checkbox" name="all_media" id="toggle_media" onchange="Toggle('media', <?=($NewRequest ? 1 : 0)?>);"<?=(!empty($MediaArray) && (count($MediaArray) === count($Media)) ? ' checked="checked"' : '')?> /><label for="toggle_media"> All</label> +<?php foreach ($Media as $Key => $Val) { + if ($Key % 8 === 0) { + echo '<br />'; + } ?> + <input type="checkbox" name="media[]" value="<?=$Key?>" id="media_<?=$Key?>" + <?=(!empty($MediaArray) && in_array($Key, $MediaArray) ? ' checked="checked" ' : '')?> + onchange="if (!this.checked) { $('#toggle_media').raw().checked = false; }" /><label for="media_<?=$Key?>"> <?=$Val?></label> +<?php } ?> + </td> + </tr> + <tr id="logcue_tr" class="hidden"> + <td class="label">Log / Checksum / Cue<br />(CD FLAC only)</td> + <td> + <input type="checkbox" id="needlog" name="needlog" onchange="ToggleLogScore()" <?=(!empty($NeedLog) ? 'checked="checked" ' : '')?>/><label for="needlog"> Require log</label> + <span id="minlogscore_span" class="hidden"> <input type="text" name="minlogscore" id="minlogscore" size="4" value="<?=(!empty($MinLogScore) ? $MinLogScore : '')?>" /> Minimum log score</span> + <br /> + <input type="checkbox" id="needcksum" name="needcksum"<?=$Checksum ? ' checked="checked" ' : ''?>/><label for="needcksum"> Require checksum</label> + <br /> + <input type="checkbox" id="needcue" name="needcue" <?=(!empty($NeedCue) ? 'checked="checked" ' : '')?>/><label for="needcue"> Require cue file</label> + </td> + </tr> +<?php } ?> + <tr> + <td class="label">Description</td> + <td> + <textarea name="description" cols="70" rows="7"><?=(!empty($Request['Description']) ? $Request['Description'] : '')?></textarea> <br /> + </td> + </tr> +<?php if (check_perms('site_moderate_requests')) { ?> + <tr> + <td class="label">Torrent group</td> + <td> + <?=site_url()?>torrents.php?id=<input type="text" name="groupid" value="<?=$GroupID?>" size="15" /><br /> + If this request matches a torrent group <span style="font-weight: bold;">already existing</span> on the site, please indicate that here. + </td> + </tr> +<?php } elseif ($GroupID && ($CategoryID == 1)) { ?> + <tr> + <td class="label">Torrent group</td> + <td> + <a href="torrents.php?id=<?=$GroupID?>"><?=site_url()?>torrents.php?id=<?=$GroupID?></a><br /> + This request <?=($NewRequest ? 'will be' : 'is')?> associated with the above torrent group. +<?php if (!$NewRequest) { ?> + If this is incorrect, please <a href="reports.php?action=report&type=request&id=<?=$RequestID?>">report this request</a> so that staff can fix it. +<?php } ?> + <input type="hidden" name="groupid" value="<?=$GroupID?>" /> + </td> + </tr> +<?php } + if ($NewRequest) { ?> + <tr id="voting"> + <td class="label">Bounty (MB)</td> + <td> + <input type="text" id="amount_box" size="8" value="<?=(!empty($Bounty) ? $Bounty : '100')?>" onchange="Calculate();" /> + <select id="unit" name="unit" onchange="Calculate();"> + <option value="mb"<?=(!empty($_POST['unit']) && $_POST['unit'] === 'mb' ? ' selected="selected"' : '') ?>>MB</option> + <option value="gb"<?=(!empty($_POST['unit']) && $_POST['unit'] === 'gb' ? ' selected="selected"' : '') ?>>GB</option> + </select> + <input type="button" value="Preview" onclick="Calculate();" /> + <?= $RequestTax > 0 ? "<strong>{$RequestTaxPercent}% of this is deducted as tax by the system.</strong>" : '' ?> + <p>Bounty must be greater than or equal to 100 MB.</p> + </td> + </tr> + <tr> + <td class="label">Bounty information</td> + <td> + <input type="hidden" id="amount" name="amount" value="<?=(!empty($Bounty) ? $Bounty : '100')?>" /> + <input type="hidden" id="current_uploaded" value="<?=$LoggedUser['BytesUploaded']?>" /> + <input type="hidden" id="current_downloaded" value="<?=$LoggedUser['BytesDownloaded']?>" /> + <?= $RequestTax > 0 + ? 'Bounty after tax: <strong><span id="bounty_after_tax"><?=sprintf("%0.2f", 100 * (1 - $RequestTax))?> MB</span></strong><br />' + : '<span id="bounty_after_tax" style="display: none;"><?=sprintf("%0.2f", 100 * (1 - $RequestTax))?> MB</span>' + ?> + If you add the entered <strong><span id="new_bounty">100.00 MB</span></strong> of bounty, your new stats will be: <br /> + Uploaded: <span id="new_uploaded"><?=Format::get_size($LoggedUser['BytesUploaded'])?></span><br /> + Ratio: <span id="new_ratio"><?=Format::get_ratio_html($LoggedUser['BytesUploaded'], $LoggedUser['BytesDownloaded'])?></span> + </td> + </tr> + <tr> + <td colspan="2" class="center"> + <input type="submit" id="button" value="Create request" disabled="disabled" /> + </td> + </tr> +<?php } else { ?> + <tr> + <td colspan="2" class="center"> + <input type="submit" id="button" value="Edit request" /> + </td> + </tr> +<?php } ?> + </table> + </form> + <script type="text/javascript">ToggleLogCue();<?=$NewRequest ? " Calculate();" : '' ?></script> + <script type="text/javascript">Categories();</script> + </div> </div> -<? +<?php View::show_footer(); diff --git a/sections/requests/request.php b/sections/requests/request.php index 33281bba2..0795a334e 100644 --- a/sections/requests/request.php +++ b/sections/requests/request.php @@ -1,11 +1,11 @@ -<? +<?php /* * This is the page that displays the request to the end user after being created. */ if (empty($_GET['id']) || !intval($_GET['id'])) { - error(0); + error(0); } $RequestID = $_GET['id']; @@ -15,7 +15,7 @@ $Request = Requests::get_request($RequestID); if ($Request === false) { - error(404); + error(404); } //Convenience variables @@ -23,9 +23,9 @@ $CanVote = !$IsFilled && check_perms('site_vote'); if ($Request['CategoryID'] === '0') { - $CategoryName = 'Unknown'; + $CategoryName = 'Unknown'; } else { - $CategoryName = $CategoriesV2[$Request['CategoryID'] - 1]; + $CategoryName = $CategoriesV2[$Request['CategoryID'] - 1]; } $ArtistForm = Requests::get_artists($RequestID); @@ -33,26 +33,26 @@ $ArtistLink = Artists::display_artists($ArtistForm, true, true); if ($IsFilled) { - $DisplayLink = "$ArtistLink<a href=\"torrents.php?torrentid=$Request[TorrentID]\" dir=\"ltr\">$Request[Title]</a> [$Request[Year]]"; + $DisplayLink = "$ArtistLink<a href=\"torrents.php?torrentid=$Request[TorrentID]\" dir=\"ltr\">$Request[Title]</a> [$Request[Year]]"; } else { - $DisplayLink = $ArtistLink.'<span dir="ltr">'.$Request['Title']."</span> [$Request[Year]]"; + $DisplayLink = $ArtistLink.'<span dir="ltr">'.$Request['Title']."</span> [$Request[Year]]"; } $FullName = $ArtistName.$Request['Title']." [$Request[Year]]"; if ($Request['BitrateList'] != '') { - $BitrateString = implode(', ', explode('|', $Request['BitrateList'])); - $FormatString = implode(', ', explode('|', $Request['FormatList'])); - $MediaString = implode(', ', explode('|', $Request['MediaList'])); + $BitrateString = implode(', ', explode('|', $Request['BitrateList'])); + $FormatString = implode(', ', explode('|', $Request['FormatList'])); + $MediaString = implode(', ', explode('|', $Request['MediaList'])); } else { - $BitrateString = 'Unknown, please read the description.'; - $FormatString = 'Unknown, please read the description.'; - $MediaString = 'Unknown, please read the description.'; + $BitrateString = 'Unknown, please read the description.'; + $FormatString = 'Unknown, please read the description.'; + $MediaString = 'Unknown, please read the description.'; } if (empty($Request['ReleaseType'])) { - $ReleaseName = 'Unknown'; + $ReleaseName = 'Unknown'; } else { - $ReleaseName = $ReleaseTypes[$Request['ReleaseType']]; + $ReleaseName = $ReleaseTypes[$Request['ReleaseType']]; } //Votes time @@ -67,30 +67,30 @@ View::show_header("View request: $FullName", 'comments,requests,bbcode,subscriptions'); ?> <div class="thin"> - <div class="header"> - <h2><a href="requests.php">Requests</a> > <?=$CategoryName?> > <?=$DisplayLink?></h2> - <div class="linkbox"> -<? if ($CanEdit) { ?> - <a href="requests.php?action=edit&id=<?=$RequestID?>" class="brackets">Edit</a> -<? } - if ($UserCanEdit || check_perms('users_mod')) { ?> - <a href="requests.php?action=delete&id=<?=$RequestID?>" class="brackets">Delete</a> -<? } - if (Bookmarks::has_bookmarked('request', $RequestID)) { ?> - <a href="#" id="bookmarklink_request_<?=$RequestID?>" onclick="Unbookmark('request', <?=$RequestID?>, 'Bookmark'); return false;" class="brackets">Remove bookmark</a> -<? } else { ?> - <a href="#" id="bookmarklink_request_<?=$RequestID?>" onclick="Bookmark('request', <?=$RequestID?>, 'Remove bookmark'); return false;" class="brackets">Bookmark</a> -<? } ?> - <a href="#" id="subscribelink_requests<?=$RequestID?>" class="brackets" onclick="SubscribeComments('requests',<?=$RequestID?>);return false;"><?=Subscriptions::has_subscribed_comments('requests', $RequestID) !== false ? 'Unsubscribe' : 'Subscribe'?></a> - <a href="reports.php?action=report&type=request&id=<?=$RequestID?>" class="brackets">Report request</a> -<? if (!$IsFilled) { ?> - <a href="upload.php?requestid=<?=$RequestID?><?=($Request['GroupID'] ? "&groupid=$Request[GroupID]" : '')?>" class="brackets">Upload request</a> -<? } - if (!$IsFilled && ($Request['CategoryID'] === '0' || ($CategoryName === 'Music' && $Request['Year'] === '0'))) { ?> - <a href="reports.php?action=report&type=request_update&id=<?=$RequestID?>" class="brackets">Request update</a> -<? } ?> + <div class="header"> + <h2><a href="requests.php">Requests</a> > <?=$CategoryName?> > <?=$DisplayLink?></h2> + <div class="linkbox"> +<?php if ($CanEdit) { ?> + <a href="requests.php?action=edit&id=<?=$RequestID?>" class="brackets">Edit</a> +<?php } + if ($UserCanEdit || check_perms('users_mod')) { ?> + <a href="requests.php?action=delete&id=<?=$RequestID?>" class="brackets">Delete</a> +<?php } + if (Bookmarks::has_bookmarked('request', $RequestID)) { ?> + <a href="#" id="bookmarklink_request_<?=$RequestID?>" onclick="Unbookmark('request', <?=$RequestID?>, 'Bookmark'); return false;" class="brackets">Remove bookmark</a> +<?php } else { ?> + <a href="#" id="bookmarklink_request_<?=$RequestID?>" onclick="Bookmark('request', <?=$RequestID?>, 'Remove bookmark'); return false;" class="brackets">Bookmark</a> +<?php } ?> + <a href="#" id="subscribelink_requests<?=$RequestID?>" class="brackets" onclick="SubscribeComments('requests',<?=$RequestID?>);return false;"><?=Subscriptions::has_subscribed_comments('requests', $RequestID) !== false ? 'Unsubscribe' : 'Subscribe'?></a> + <a href="reports.php?action=report&type=request&id=<?=$RequestID?>" class="brackets">Report request</a> +<?php if (!$IsFilled) { ?> + <a href="upload.php?requestid=<?=$RequestID?><?=($Request['GroupID'] ? "&groupid=$Request[GroupID]" : '')?>" class="brackets">Upload request</a> +<?php } + if (!$IsFilled && ($Request['CategoryID'] === '0' || ($CategoryName === 'Music' && $Request['Year'] === '0'))) { ?> + <a href="reports.php?action=report&type=request_update&id=<?=$RequestID?>" class="brackets">Request update</a> +<?php } ?> -<? +<?php // Create a search URL to WorldCat and Google based on title $encoded_title = urlencode(preg_replace("/\([^\)]+\)/", '', $Request['Title'])); $encoded_artist = substr(str_replace('&', 'and', $ArtistName), 0, -3); @@ -101,369 +101,369 @@ $worldcat_url = 'https://www.worldcat.org/search?qt=worldcat_org_all&q=' . "$encoded_artist%20$encoded_title"; $google_url = 'https://www.google.com/search?tbm=shop&q=' . "$encoded_artist%20$encoded_title"; ?> - <a href="<? echo $worldcat_url; ?>" class="brackets">Find in library</a> - <a href="<? echo $google_url; ?>" class="brackets">Find in stores</a> - </div> - </div> - <div class="sidebar"> -<? if ($Request['CategoryID'] !== '0') { ?> - <div class="box box_image box_image_albumart box_albumart"><!-- .box_albumart deprecated --> - <div class="head"><strong>Cover</strong></div> - <div id="covers"> - <div class="pad"> -<? - if (!empty($Request['Image'])) { + <a href="<?php echo $worldcat_url; ?>" class="brackets">Find in library</a> + <a href="<?php echo $google_url; ?>" class="brackets">Find in stores</a> + </div> + </div> + <div class="sidebar"> +<?php if ($Request['CategoryID'] !== '0') { ?> + <div class="box box_image box_image_albumart box_albumart"><!-- .box_albumart deprecated --> + <div class="head"><strong>Cover</strong></div> + <div id="covers"> + <div class="pad"> +<?php + if (!empty($Request['Image'])) { ?> - <p align="center"><img style="width: 100%;" src="<?=ImageTools::process($Request['Image'], true)?>" alt="<?=$FullName?>" onclick="lightbox.init('<?=ImageTools::process($Request['Image'])?>', 220);" /></p> -<? } else { ?> - <p align="center"><img style="width: 100%;" src="<?=STATIC_SERVER?>common/noartwork/<?=$CategoryIcons[$Request['CategoryID'] - 1]?>" alt="<?=$CategoryName?>" class="tooltip" title="<?=$CategoryName?>" height="220" border="0" /></p> -<? } ?> - </div> - </div> - </div> -<? - } - if ($CategoryName === 'Music') { ?> - <div class="box box_artists"> - <div class="head"><strong>Artists</strong></div> - <ul class="stats nobullet"> -<? if (!empty($ArtistForm[4]) && count($ArtistForm[4]) > 0) { ?> - <li class="artists_composer"><strong>Composers:</strong></li> -<? foreach ($ArtistForm[4] as $Artist) { ?> - <li class="artists_composer"> - <?=Artists::display_artist($Artist)?> - </li> -<? - } - } - if (!empty($ArtistForm[6]) && count($ArtistForm[6]) > 0) { + <p align="center"><img style="width: 100%;" src="<?=ImageTools::process($Request['Image'], true)?>" alt="<?=$FullName?>" onclick="lightbox.init('<?=ImageTools::process($Request['Image'])?>', 220);" /></p> +<?php } else { ?> + <p align="center"><img style="width: 100%;" src="<?=STATIC_SERVER?>common/noartwork/<?=$CategoryIcons[$Request['CategoryID'] - 1]?>" alt="<?=$CategoryName?>" class="tooltip" title="<?=$CategoryName?>" height="220" border="0" /></p> +<?php } ?> + </div> + </div> + </div> +<?php + } + if ($CategoryName === 'Music') { ?> + <div class="box box_artists"> + <div class="head"><strong>Artists</strong></div> + <ul class="stats nobullet"> +<?php if (!empty($ArtistForm[4]) && count($ArtistForm[4]) > 0) { ?> + <li class="artists_composer"><strong>Composers:</strong></li> +<?php foreach ($ArtistForm[4] as $Artist) { ?> + <li class="artists_composer"> + <?=Artists::display_artist($Artist)?> + </li> +<?php + } + } + if (!empty($ArtistForm[6]) && count($ArtistForm[6]) > 0) { ?> - <li class="artists_dj"><strong>DJ / Compiler:</strong></li> -<? foreach ($ArtistForm[6] as $Artist) { ?> - <li class="artists_dj"> - <?=Artists::display_artist($Artist)?> - </li> -<? - } - } - if (!empty($ArtistForm[6]) && !empty($ArtistForm[1]) && (count($ArtistForm[6]) > 0) && (count($ArtistForm[1]) > 0)) { - print ' <li class="artists_main"><strong>Artists:</strong></li>'; - } elseif (!empty($ArtistForm[4]) && !empty($ArtistForm[1]) && (count($ArtistForm[4]) > 0) && (count($ArtistForm[1]) > 0)) { - print ' <li class="artists_main"><strong>Performers:</strong></li>'; - } - foreach ($ArtistForm[1] as $Artist) { + <li class="artists_dj"><strong>DJ / Compiler:</strong></li> +<?php foreach ($ArtistForm[6] as $Artist) { ?> + <li class="artists_dj"> + <?=Artists::display_artist($Artist)?> + </li> +<?php + } + } + if (!empty($ArtistForm[6]) && !empty($ArtistForm[1]) && (count($ArtistForm[6]) > 0) && (count($ArtistForm[1]) > 0)) { + print ' <li class="artists_main"><strong>Artists:</strong></li>'; + } elseif (!empty($ArtistForm[4]) && !empty($ArtistForm[1]) && (count($ArtistForm[4]) > 0) && (count($ArtistForm[1]) > 0)) { + print ' <li class="artists_main"><strong>Performers:</strong></li>'; + } + foreach ($ArtistForm[1] as $Artist) { ?> - <li class="artists_main"> - <?=Artists::display_artist($Artist)?> - </li> -<? - } - if (!empty($ArtistForm[2]) && count($ArtistForm[2]) > 0) { + <li class="artists_main"> + <?=Artists::display_artist($Artist)?> + </li> +<?php + } + if (!empty($ArtistForm[2]) && count($ArtistForm[2]) > 0) { ?> - <li class="artists_with"><strong>With:</strong></li> -<? foreach ($ArtistForm[2] as $Artist) { ?> - <li class="artists_with"> - <?=Artists::display_artist($Artist)?> - </li> -<? - } - } - if (!empty($ArtistForm[5]) && count($ArtistForm[5]) > 0) { + <li class="artists_with"><strong>With:</strong></li> +<?php foreach ($ArtistForm[2] as $Artist) { ?> + <li class="artists_with"> + <?=Artists::display_artist($Artist)?> + </li> +<?php + } + } + if (!empty($ArtistForm[5]) && count($ArtistForm[5]) > 0) { ?> - <li class="artists_conductor"><strong>Conducted by:</strong></li> -<? foreach ($ArtistForm[5] as $Artist) { ?> - <li class="artist_guest"> - <?=Artists::display_artist($Artist)?> - </li> -<? - } - } - if (!empty($ArtistForm[3]) && count($ArtistForm[3]) > 0) { + <li class="artists_conductor"><strong>Conducted by:</strong></li> +<?php foreach ($ArtistForm[5] as $Artist) { ?> + <li class="artist_guest"> + <?=Artists::display_artist($Artist)?> + </li> +<?php + } + } + if (!empty($ArtistForm[3]) && count($ArtistForm[3]) > 0) { ?> - <li class="artists_remix"><strong>Remixed by:</strong></li> -<? foreach ($ArtistForm[3] as $Artist) { ?> - <li class="artists_remix"> - <?=Artists::display_artist($Artist)?> - </li> -<? - } - } - if (!empty($ArtistForm[7]) && count($ArtistForm[7]) > 0) { + <li class="artists_remix"><strong>Remixed by:</strong></li> +<?php foreach ($ArtistForm[3] as $Artist) { ?> + <li class="artists_remix"> + <?=Artists::display_artist($Artist)?> + </li> +<?php + } + } + if (!empty($ArtistForm[7]) && count($ArtistForm[7]) > 0) { ?> - <li class="artists_producer"><strong>Produced by:</strong></li> -<? foreach ($ArtistForm[7] as $Artist) { ?> - <li class="artists_remix"> - <?=Artists::display_artist($Artist)?> - </li> -<? - } - } + <li class="artists_producer"><strong>Produced by:</strong></li> +<?php foreach ($ArtistForm[7] as $Artist) { ?> + <li class="artists_remix"> + <?=Artists::display_artist($Artist)?> + </li> +<?php + } + } ?> - </ul> - </div> -<? } ?> - <div class="box box_tags"> - <div class="head"><strong>Tags</strong></div> - <ul class="stats nobullet"> -<? foreach ($Request['Tags'] as $TagID => $TagName) { ?> - <li> - <a href="torrents.php?taglist=<?=$TagName?>"><?=display_str($TagName)?></a> - <br style="clear: both;" /> - </li> -<? } ?> - </ul> - </div> - <div class="box box_votes"> - <div class="head"><strong>Top Contributors</strong></div> - <table class="layout" id="request_top_contrib"> -<? - $VoteMax = ($VoteCount < 5 ? $VoteCount : 5); - $ViewerVote = false; - for ($i = 0; $i < $VoteMax; $i++) { - $User = array_shift($RequestVotes['Voters']); - $Boldify = false; - if ($User['UserID'] === $LoggedUser['ID']) { - $ViewerVote = true; - $Boldify = true; - } + </ul> + </div> +<?php } ?> + <div class="box box_tags"> + <div class="head"><strong>Tags</strong></div> + <ul class="stats nobullet"> +<?php foreach ($Request['Tags'] as $TagID => $TagName) { ?> + <li> + <a href="torrents.php?taglist=<?=$TagName?>"><?=display_str($TagName)?></a> + <br style="clear: both;" /> + </li> +<?php } ?> + </ul> + </div> + <div class="box box_votes"> + <div class="head"><strong>Top Contributors</strong></div> + <table class="layout" id="request_top_contrib"> +<?php + $VoteMax = ($VoteCount < 5 ? $VoteCount : 5); + $ViewerVote = false; + for ($i = 0; $i < $VoteMax; $i++) { + $User = array_shift($RequestVotes['Voters']); + $Boldify = false; + if ($User['UserID'] === $LoggedUser['ID']) { + $ViewerVote = true; + $Boldify = true; + } ?> - <tr> - <td> - <a href="user.php?id=<?=$User['UserID']?>"><?=($Boldify ? '<strong>' : '') . display_str($User['Username']) . ($Boldify ? '</strong>' : '')?></a> - </td> - <td class="number_column"> - <?=($Boldify ? '<strong>' : '') . Format::get_size($User['Bounty']) . ($Boldify ? "</strong>\n" : "\n")?> - </td> - </tr> -<? } - reset($RequestVotes['Voters']); - if (!$ViewerVote) { - foreach ($RequestVotes['Voters'] as $User) { - if ($User['UserID'] === $LoggedUser['ID']) { ?> - <tr> - <td> - <a href="user.php?id=<?=$User['UserID']?>"><strong><?=display_str($User['Username'])?></strong></a> - </td> - <td class="number_column"> - <strong><?=Format::get_size($User['Bounty'])?></strong> - </td> - </tr> -<? } - } - } + <tr> + <td> + <a href="user.php?id=<?=$User['UserID']?>"><?=($Boldify ? '<strong>' : '') . display_str($User['Username']) . ($Boldify ? '</strong>' : '')?></a> + </td> + <td class="number_column"> + <?=($Boldify ? '<strong>' : '') . Format::get_size($User['Bounty']) . ($Boldify ? "</strong>\n" : "\n")?> + </td> + </tr> +<?php } + reset($RequestVotes['Voters']); + if (!$ViewerVote) { + foreach ($RequestVotes['Voters'] as $User) { + if ($User['UserID'] === $LoggedUser['ID']) { ?> + <tr> + <td> + <a href="user.php?id=<?=$User['UserID']?>"><strong><?=display_str($User['Username'])?></strong></a> + </td> + <td class="number_column"> + <strong><?=Format::get_size($User['Bounty'])?></strong> + </td> + </tr> +<?php } + } + } ?> - </table> - </div> - </div> - <div class="main_column"> - <table class="layout"> - <tr> - <td class="label">Created</td> - <td> - <?=time_diff($Request['TimeAdded'])?> by <strong><?=Users::format_username($Request['UserID'], false, false, false)?></strong> - </td> - </tr> -<? if ($CategoryName === 'Music') { - if (!empty($Request['RecordLabel'])) { ?> - <tr> - <td class="label">Record label</td> - <td><?=$Request['RecordLabel']?></td> - </tr> -<? } - if (!empty($Request['CatalogueNumber'])) { ?> - <tr> - <td class="label">Catalogue number</td> - <td><?=$Request['CatalogueNumber']?></td> - </tr> -<? } ?> - <tr> - <td class="label">Release type</td> - <td><?=$ReleaseName?></td> - </tr> - <tr> - <td class="label">Acceptable bitrates</td> - <td><?=$BitrateString?></td> - </tr> - <tr> - <td class="label">Acceptable formats</td> - <td><?=$FormatString?></td> - </tr> - <tr> - <td class="label">Acceptable media</td> - <td><?=$MediaString?></td> - </tr> -<? if (!empty($Request['LogCue']) || !empty($Request['Checksum'])) { ?> - <tr> - <td class="label">Required CD FLAC only extras</td> - <td><?=$Request['LogCue']?></td> - </tr> - <tr> - <td class="label">Required CD FLAC checksum</td> - <td><?=$Request['LogCue'] ? 'yes' : 'no'?></td> - </tr> -<? - } - } - $Worldcat = ''; - $OCLC = str_replace(' ', '', $Request['OCLC']); - if ($OCLC !== '') { - $OCLCs = explode(',', $OCLC); - for ($i = 0; $i < count($OCLCs); $i++) { - if (!empty($Worldcat)) { - $Worldcat .= ', <a href="https://www.worldcat.org/oclc/'.$OCLCs[$i].'">'.$OCLCs[$i].'</a>'; - } else { - $Worldcat = '<a href="https://www.worldcat.org/oclc/'.$OCLCs[$i].'">'.$OCLCs[$i].'</a>'; - } - } - } - if (!empty($Worldcat)) { + </table> + </div> + </div> + <div class="main_column"> + <table class="layout"> + <tr> + <td class="label">Created</td> + <td> + <?=time_diff($Request['TimeAdded'])?> by <strong><?=Users::format_username($Request['UserID'], false, false, false)?></strong> + </td> + </tr> +<?php if ($CategoryName === 'Music') { + if (!empty($Request['RecordLabel'])) { ?> + <tr> + <td class="label">Record label</td> + <td><?=$Request['RecordLabel']?></td> + </tr> +<?php } + if (!empty($Request['CatalogueNumber'])) { ?> + <tr> + <td class="label">Catalogue number</td> + <td><?=$Request['CatalogueNumber']?></td> + </tr> +<?php } ?> + <tr> + <td class="label">Release type</td> + <td><?=$ReleaseName?></td> + </tr> + <tr> + <td class="label">Acceptable bitrates</td> + <td><?=$BitrateString?></td> + </tr> + <tr> + <td class="label">Acceptable formats</td> + <td><?=$FormatString?></td> + </tr> + <tr> + <td class="label">Acceptable media</td> + <td><?=$MediaString?></td> + </tr> +<?php if (!empty($Request['LogCue']) || !empty($Request['Checksum'])) { ?> + <tr> + <td class="label">Required CD FLAC only extras</td> + <td><?=$Request['LogCue']?></td> + </tr> + <tr> + <td class="label">Required CD FLAC checksum</td> + <td><?=$Request['Checksum'] ? 'yes' : 'no'?></td> + </tr> +<?php + } + } + $Worldcat = ''; + $OCLC = str_replace(' ', '', $Request['OCLC']); + if ($OCLC !== '') { + $OCLCs = explode(',', $OCLC); + for ($i = 0; $i < count($OCLCs); $i++) { + if (!empty($Worldcat)) { + $Worldcat .= ', <a href="https://www.worldcat.org/oclc/'.$OCLCs[$i].'">'.$OCLCs[$i].'</a>'; + } else { + $Worldcat = '<a href="https://www.worldcat.org/oclc/'.$OCLCs[$i].'">'.$OCLCs[$i].'</a>'; + } + } + } + if (!empty($Worldcat)) { ?> - <tr> - <td class="label">WorldCat (OCLC) ID</td> - <td><?=$Worldcat?></td> - </tr> -<? - } - if ($Request['GroupID']) { + <tr> + <td class="label">WorldCat (OCLC) ID</td> + <td><?=$Worldcat?></td> + </tr> +<?php + } + if ($Request['GroupID']) { ?> - <tr> - <td class="label">Torrent group</td> - <td><a href="torrents.php?id=<?=$Request['GroupID']?>">torrents.php?id=<?=$Request['GroupID']?></a></td> - </tr> -<? } ?> - <tr> - <td class="label">Votes</td> - <td> - <span id="votecount"><?=number_format($VoteCount)?></span> -<? if ($CanVote) { ?> -   <a href="javascript:Vote(0)" class="brackets"><strong>+</strong></a> - <strong>Costs <?=Format::get_size($MinimumVote, 0)?></strong> -<? } ?> - </td> - </tr> -<? if ($Request['LastVote'] > $Request['TimeAdded']) { ?> - <tr> - <td class="label">Last voted</td> - <td><?=time_diff($Request['LastVote'])?></td> - </tr> -<? - } - if ($CanVote) { + <tr> + <td class="label">Torrent group</td> + <td><a href="torrents.php?id=<?=$Request['GroupID']?>">torrents.php?id=<?=$Request['GroupID']?></a></td> + </tr> +<?php } ?> + <tr> + <td class="label">Votes</td> + <td> + <span id="votecount"><?=number_format($VoteCount)?></span> +<?php if ($CanVote) { ?> +   <a href="javascript:Vote(0)" class="brackets"><strong>+</strong></a> + <strong>Costs <?=Format::get_size($MinimumVote, 0)?></strong> +<?php } ?> + </td> + </tr> +<?php if ($Request['LastVote'] > $Request['TimeAdded']) { ?> + <tr> + <td class="label">Last voted</td> + <td><?=time_diff($Request['LastVote'])?></td> + </tr> +<?php + } + if ($CanVote) { ?> - <tr id="voting"> - <td class="label tooltip" title="These units are in base 2, not base 10. For example, there are 1,024 MB in 1 GB.">Custom vote (MB)</td> - <td> - <input type="text" id="amount_box" size="8" onchange="Calculate();" /> - <select id="unit" name="unit" onchange="Calculate();"> - <option value="mb">MB</option> - <option value="gb">GB</option> - </select> - <input type="button" value="Preview" onclick="Calculate();" /> - <?= $RequestTax > 0 ? "<strong>{$RequestTaxPercent}% of this is deducted as tax by the system.</strong>" : '' ?> - <p>Bounty must be greater than or equal to 100 MB.</p> - </td> - </tr> - <tr> - <td class="label">Bounty information</td> - <td> - <form class="add_form" name="request" action="requests.php" method="get" id="request_form"> - <input type="hidden" name="action" value="vote" /> - <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> - <input type="hidden" id="request_tax" value="<?=$RequestTax?>" /> - <input type="hidden" id="requestid" name="id" value="<?=$RequestID?>" /> - <input type="hidden" id="auth" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> - <input type="hidden" id="amount" name="amount" value="0" /> - <input type="hidden" id="current_uploaded" value="<?=$LoggedUser['BytesUploaded']?>" /> - <input type="hidden" id="current_downloaded" value="<?=$LoggedUser['BytesDownloaded']?>" /> - <input type="hidden" id="current_rr" value="<?=(float)$LoggedUser['RequiredRatio']?>" /> - <input id="total_bounty" type="hidden" value="<?=$RequestVotes['TotalBounty']?>" /> - <?= $RequestTax > 0 - ? 'Bounty after tax: <strong><span id="bounty_after_tax"><?=sprintf("%0.2f", 100 * (1 - $RequestTax))?> MB</span></strong><br />' - : '<span id="bounty_after_tax" style="display: none;"><?=sprintf("%0.2f", 100 * (1 - $RequestTax))?> MB</span>' - ?> - If you add the entered <strong><span id="new_bounty">0.00 MB</span></strong> of bounty, your new stats will be: <br /> - Uploaded: <span id="new_uploaded"><?=Format::get_size($LoggedUser['BytesUploaded'])?></span><br /> - Ratio: <span id="new_ratio"><?=Format::get_ratio_html($LoggedUser['BytesUploaded'],$LoggedUser['BytesDownloaded'])?></span> - <input type="button" id="button" value="Vote!" disabled="disabled" onclick="Vote();" /> - </form> - </td> - </tr> -<? } ?> - <tr id="bounty"> - <td class="label">Bounty</td> - <td id="formatted_bounty"><?=Format::get_size($RequestVotes['TotalBounty'])?></td> - </tr> -<? if ($IsFilled) { ?> - <tr> - <td class="label">Filled</td> - <td> - <strong><a href="torrents.php?torrentid=<?=$Request['TorrentID']?>">Yes</a></strong>, - by user <?=Users::format_username($Request['FillerID'], false, false, false)?> -<? if ($LoggedUser['ID'] == $Request['UserID'] || $LoggedUser['ID'] == $Request['FillerID'] || check_perms('site_moderate_requests')) { ?> - <strong><a href="requests.php?action=unfill&id=<?=$RequestID?>" class="brackets">Unfill</a></strong> Unfilling a request without a valid, nontrivial reason will result in a warning. -<? } ?> - </td> - </tr> -<? } else { ?> - <tr> - <td class="label" valign="top">Fill request</td> - <td> - <form class="edit_form" name="request" action="" method="post"> - <div class="field_div"> - <input type="hidden" name="action" value="takefill" /> - <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> - <input type="hidden" name="requestid" value="<?=$RequestID?>" /> - <input type="text" size="50" name="link"<?=(!empty($Link) ? " value=\"$Link\"" : '')?> /> - <br /> - <strong>Should be the permalink (PL) to the torrent (e.g. <?=site_url()?>torrents.php?torrentid=xxxx).</strong> - </div> -<? if (check_perms('site_moderate_requests')) { ?> - <div class="field_div"> - For user: <input type="text" size="25" name="user"<?=(!empty($FillerUsername) ? " value=\"$FillerUsername\"" : '')?> /> - </div> -<? } ?> - <div class="submit_div"> - <input type="submit" value="Fill request" /> - </div> - </form> - </td> - </tr> -<? } ?> - </table> - <div class="box box2 box_request_desc"> - <div class="head"><strong>Description</strong></div> - <div class="pad"> -<?= Text::full_format($Request['Description']);?> - </div> - </div> - <div id="request_comments"> - <div class="linkbox"> - <a name="comments"></a> -<? + <tr id="voting"> + <td class="label tooltip" title="These units are in base 2, not base 10. For example, there are 1,024 MB in 1 GB.">Custom vote (MB)</td> + <td> + <input type="text" id="amount_box" size="8" onchange="Calculate();" /> + <select id="unit" name="unit" onchange="Calculate();"> + <option value="mb">MB</option> + <option value="gb">GB</option> + </select> + <input type="button" value="Preview" onclick="Calculate();" /> + <?= $RequestTax > 0 ? "<strong>{$RequestTaxPercent}% of this is deducted as tax by the system.</strong>" : '' ?> + <p>Bounty must be greater than or equal to 100 MB.</p> + </td> + </tr> + <tr> + <td class="label">Bounty information</td> + <td> + <form class="add_form" name="request" action="requests.php" method="get" id="request_form"> + <input type="hidden" name="action" value="vote" /> + <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> + <input type="hidden" id="request_tax" value="<?=$RequestTax?>" /> + <input type="hidden" id="requestid" name="id" value="<?=$RequestID?>" /> + <input type="hidden" id="auth" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> + <input type="hidden" id="amount" name="amount" value="0" /> + <input type="hidden" id="current_uploaded" value="<?=$LoggedUser['BytesUploaded']?>" /> + <input type="hidden" id="current_downloaded" value="<?=$LoggedUser['BytesDownloaded']?>" /> + <input type="hidden" id="current_rr" value="<?=(float)$LoggedUser['RequiredRatio']?>" /> + <input id="total_bounty" type="hidden" value="<?=$RequestVotes['TotalBounty']?>" /> + <?= $RequestTax > 0 + ? 'Bounty after tax: <strong><span id="bounty_after_tax"><?=sprintf("%0.2f", 100 * (1 - $RequestTax))?> MB</span></strong><br />' + : '<span id="bounty_after_tax" style="display: none;"><?=sprintf("%0.2f", 100 * (1 - $RequestTax))?> MB</span>' + ?> + If you add the entered <strong><span id="new_bounty">0.00 MB</span></strong> of bounty, your new stats will be: <br /> + Uploaded: <span id="new_uploaded"><?=Format::get_size($LoggedUser['BytesUploaded'])?></span><br /> + Ratio: <span id="new_ratio"><?=Format::get_ratio_html($LoggedUser['BytesUploaded'],$LoggedUser['BytesDownloaded'])?></span> + <input type="button" id="button" value="Vote!" disabled="disabled" onclick="Vote();" /> + </form> + </td> + </tr> +<?php } ?> + <tr id="bounty"> + <td class="label">Bounty</td> + <td id="formatted_bounty"><?=Format::get_size($RequestVotes['TotalBounty'])?></td> + </tr> +<?php if ($IsFilled) { ?> + <tr> + <td class="label">Filled</td> + <td> + <strong><a href="torrents.php?torrentid=<?=$Request['TorrentID']?>">Yes</a></strong>, + by user <?=Users::format_username($Request['FillerID'], false, false, false)?> +<?php if ($LoggedUser['ID'] == $Request['UserID'] || $LoggedUser['ID'] == $Request['FillerID'] || check_perms('site_moderate_requests')) { ?> + <strong><a href="requests.php?action=unfill&id=<?=$RequestID?>" class="brackets">Unfill</a></strong> Unfilling a request without a valid, nontrivial reason will result in a warning. +<?php } ?> + </td> + </tr> +<?php } else { ?> + <tr> + <td class="label" valign="top">Fill request</td> + <td> + <form class="edit_form" name="request" action="" method="post"> + <div class="field_div"> + <input type="hidden" name="action" value="takefill" /> + <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> + <input type="hidden" name="requestid" value="<?=$RequestID?>" /> + <input type="text" size="50" name="link"<?=(!empty($Link) ? " value=\"$Link\"" : '')?> /> + <br /> + <strong>Should be the permalink (PL) to the torrent (e.g. <?=site_url()?>torrents.php?torrentid=xxxx).</strong> + </div> +<?php if (check_perms('site_moderate_requests')) { ?> + <div class="field_div"> + For user: <input type="text" size="25" name="user"<?=(!empty($FillerUsername) ? " value=\"$FillerUsername\"" : '')?> /> + </div> +<?php } ?> + <div class="submit_div"> + <input type="submit" value="Fill request" /> + </div> + </form> + </td> + </tr> +<?php } ?> + </table> + <div class="box box2 box_request_desc"> + <div class="head"><strong>Description</strong></div> + <div class="pad"> +<?= Text::full_format($Request['Description']);?> + </div> + </div> + <div id="request_comments"> + <div class="linkbox"> + <a name="comments"></a> +<?php $Pages = Format::get_pages($Page, $NumComments, TORRENT_COMMENTS_PER_PAGE, 9, '#comments'); echo $Pages; ?> - </div> -<? + </div> +<?php //---------- Begin printing CommentsView::render_comments($Thread, $LastRead, "requests.php?action=view&id=$RequestID"); if ($Pages) { ?> - <div class="linkbox pager"><?=$Pages?></div> -<? + <div class="linkbox pager"><?=$Pages?></div> +<?php } View::parse('generic/reply/quickreply.php', array( - 'InputName' => 'pageid', - 'InputID' => $RequestID, - 'Action' => 'comments.php?page=requests', - 'InputAction' => 'take_post', - 'SubscribeBox' => true + 'InputName' => 'pageid', + 'InputID' => $RequestID, + 'Action' => 'comments.php?page=requests', + 'InputAction' => 'take_post', + 'SubscribeBox' => true )); ?> - </div> - </div> + </div> + </div> </div> -<? +<?php View::show_footer(); diff --git a/sections/requests/requests.php b/sections/requests/requests.php index f4431d75b..0208b3088 100644 --- a/sections/requests/requests.php +++ b/sections/requests/requests.php @@ -1,336 +1,336 @@ -<? +<?php $SphQL = new SphinxqlQuery(); $SphQL->select('id, votes, bounty')->from('requests, requests_delta'); $SortOrders = array( - 'votes' => 'votes', - 'bounty' => 'bounty', - 'lastvote' => 'lastvote', - 'filled' => 'timefilled', - 'year' => 'year', - 'created' => 'timeadded', - 'random' => false); + 'votes' => 'votes', + 'bounty' => 'bounty', + 'lastvote' => 'lastvote', + 'filled' => 'timefilled', + 'year' => 'year', + 'created' => 'timeadded', + 'random' => false); if (empty($_GET['order']) || !isset($SortOrders[$_GET['order']])) { - $_GET['order'] = 'created'; + $_GET['order'] = 'created'; } $OrderBy = $_GET['order']; if (!empty($_GET['sort']) && $_GET['sort'] === 'asc') { - $OrderWay = 'asc'; + $OrderWay = 'asc'; } else { - $_GET['sort'] = 'desc'; - $OrderWay = 'desc'; + $_GET['sort'] = 'desc'; + $OrderWay = 'desc'; } $NewSort = $_GET['sort'] === 'asc' ? 'desc' : 'asc'; if ($OrderBy === 'random') { - $SphQL->order_by('RAND()', ''); - unset($_GET['page']); + $SphQL->order_by('RAND()', ''); + unset($_GET['page']); } else { - $SphQL->order_by($SortOrders[$OrderBy], $OrderWay); + $SphQL->order_by($SortOrders[$OrderBy], $OrderWay); } $Submitted = !empty($_GET['submit']); //Paranoia if (!empty($_GET['userid'])) { - if (!intval($_GET['userid'])) { - error('User ID must be an integer'); - } - $UserInfo = Users::user_info($_GET['userid']); - if (empty($UserInfo)) { - error('That user does not exist'); - } - $Perms = Permissions::get_permissions($UserInfo['PermissionID']); - $UserClass = $Perms['Class']; + if (!intval($_GET['userid'])) { + error('User ID must be an integer'); + } + $UserInfo = Users::user_info($_GET['userid']); + if (empty($UserInfo)) { + error('That user does not exist'); + } + $Perms = Permissions::get_permissions($UserInfo['PermissionID']); + $UserClass = $Perms['Class']; } $BookmarkView = false; if (empty($_GET['type'])) { - $Title = 'Requests'; - if ($Submitted && !empty($_GET['showall'])) { - $SphQL->where('visible', 1); - } + $Title = 'Requests'; + if ($Submitted && !empty($_GET['showall'])) { + $SphQL->where('visible', 1); + } } else { - // Show filled defaults to on only for viewing types - if (!$Submitted) { - $_GET['show_filled'] = "on"; - } - switch ($_GET['type']) { - case 'created': - if (!empty($UserInfo)) { - if (!check_paranoia('requestsvoted_list', $UserInfo['Paranoia'], $Perms['Class'], $UserInfo['ID'])) { - error(403); - } - $Title = "Requests created by $UserInfo[Username]"; - $SphQL->where('userid', $UserInfo['ID']); - } else { - $Title = 'My requests'; - $SphQL->where('userid', $LoggedUser['ID']); - } - break; - case 'voted': - if (!empty($UserInfo)) { - if (!check_paranoia('requestsvoted_list', $UserInfo['Paranoia'], $Perms['Class'], $UserInfo['ID'])) { - error(403); - } - $Title = "Requests voted for by $UserInfo[Username]"; - $SphQL->where('voter', $UserInfo['ID']); - } else { - $Title = 'Requests I have voted on'; - $SphQL->where('voter', $LoggedUser['ID']); - } - break; - case 'filled': - if (!empty($UserInfo)) { - if (!check_paranoia('requestsfilled_list', $UserInfo['Paranoia'], $Perms['Class'], $UserInfo['ID'])) { - error(403); - } - $Title = "Requests filled by $UserInfo[Username]"; - $SphQL->where('fillerid', $UserInfo['ID']); - } else { - $Title = 'Requests I have filled'; - $SphQL->where('fillerid', $LoggedUser['ID']); - } - break; - case 'bookmarks': - $Title = 'Your bookmarked requests'; - $BookmarkView = true; - $SphQL->where('bookmarker', $LoggedUser['ID']); - break; - default: - error(404); - } + // Show filled defaults to on only for viewing types + if (!$Submitted) { + $_GET['show_filled'] = "on"; + } + switch ($_GET['type']) { + case 'created': + if (!empty($UserInfo)) { + if (!check_paranoia('requestsvoted_list', $UserInfo['Paranoia'], $Perms['Class'], $UserInfo['ID'])) { + error(403); + } + $Title = "Requests created by $UserInfo[Username]"; + $SphQL->where('userid', $UserInfo['ID']); + } else { + $Title = 'My requests'; + $SphQL->where('userid', $LoggedUser['ID']); + } + break; + case 'voted': + if (!empty($UserInfo)) { + if (!check_paranoia('requestsvoted_list', $UserInfo['Paranoia'], $Perms['Class'], $UserInfo['ID'])) { + error(403); + } + $Title = "Requests voted for by $UserInfo[Username]"; + $SphQL->where('voter', $UserInfo['ID']); + } else { + $Title = 'Requests I have voted on'; + $SphQL->where('voter', $LoggedUser['ID']); + } + break; + case 'filled': + if (!empty($UserInfo)) { + if (!check_paranoia('requestsfilled_list', $UserInfo['Paranoia'], $Perms['Class'], $UserInfo['ID'])) { + error(403); + } + $Title = "Requests filled by $UserInfo[Username]"; + $SphQL->where('fillerid', $UserInfo['ID']); + } else { + $Title = 'Requests I have filled'; + $SphQL->where('fillerid', $LoggedUser['ID']); + } + break; + case 'bookmarks': + $Title = 'Your bookmarked requests'; + $BookmarkView = true; + $SphQL->where('bookmarker', $LoggedUser['ID']); + break; + default: + error(404); + } } // We don't want to show filled by default on plain requests.php, // but we do show it by default if viewing a $_GET['type'] page // by default if ((!$Submitted && empty($_GET['type'])) || ($Submitted && empty($_GET['show_filled']))) { - $SphQL->where('torrentid', 0); + $SphQL->where('torrentid', 0); } $EnableNegation = false; // Sphinx needs at least one positive search condition to support the NOT operator if (!empty($_GET['formats'])) { - $FormatArray = $_GET['formats']; - if (count($FormatArray) !== count($Formats)) { - $FormatNameArray = array(); - foreach ($FormatArray as $Index => $MasterIndex) { - if (isset($Formats[$MasterIndex])) { - $FormatNameArray[$Index] = '"' . strtr(Sphinxql::sph_escape_string($Formats[$MasterIndex]), '-.', ' ') . '"'; - } - } - if (count($FormatNameArray) >= 1) { - $EnableNegation = true; - if (!empty($_GET['formats_strict'])) { - $SearchString = '(' . implode(' | ', $FormatNameArray) . ')'; - } else { - $SearchString = '(any | ' . implode(' | ', $FormatNameArray) . ')'; - } - $SphQL->where_match($SearchString, 'formatlist', false); - } - } + $FormatArray = $_GET['formats']; + if (count($FormatArray) !== count($Formats)) { + $FormatNameArray = []; + foreach ($FormatArray as $Index => $MasterIndex) { + if (isset($Formats[$MasterIndex])) { + $FormatNameArray[$Index] = '"' . strtr(Sphinxql::sph_escape_string($Formats[$MasterIndex]), '-.', ' ') . '"'; + } + } + if (count($FormatNameArray) >= 1) { + $EnableNegation = true; + if (!empty($_GET['formats_strict'])) { + $SearchString = '(' . implode(' | ', $FormatNameArray) . ')'; + } else { + $SearchString = '(any | ' . implode(' | ', $FormatNameArray) . ')'; + } + $SphQL->where_match($SearchString, 'formatlist', false); + } + } } if (!empty($_GET['media'])) { - $MediaArray = $_GET['media']; - if (count($MediaArray) !== count($Media)) { - $MediaNameArray = array(); - foreach ($MediaArray as $Index => $MasterIndex) { - if (isset($Media[$MasterIndex])) { - $MediaNameArray[$Index] = '"' . strtr(Sphinxql::sph_escape_string($Media[$MasterIndex]), '-.', ' ') . '"'; - } - } - - if (count($MediaNameArray) >= 1) { - $EnableNegation = true; - if (!empty($_GET['media_strict'])) { - $SearchString = '(' . implode(' | ', $MediaNameArray) . ')'; - } else { - $SearchString = '(any | ' . implode(' | ', $MediaNameArray) . ')'; - } - $SphQL->where_match($SearchString, 'medialist', false); - } - } + $MediaArray = $_GET['media']; + if (count($MediaArray) !== count($Media)) { + $MediaNameArray = []; + foreach ($MediaArray as $Index => $MasterIndex) { + if (isset($Media[$MasterIndex])) { + $MediaNameArray[$Index] = '"' . strtr(Sphinxql::sph_escape_string($Media[$MasterIndex]), '-.', ' ') . '"'; + } + } + + if (count($MediaNameArray) >= 1) { + $EnableNegation = true; + if (!empty($_GET['media_strict'])) { + $SearchString = '(' . implode(' | ', $MediaNameArray) . ')'; + } else { + $SearchString = '(any | ' . implode(' | ', $MediaNameArray) . ')'; + } + $SphQL->where_match($SearchString, 'medialist', false); + } + } } if (!empty($_GET['bitrates'])) { - $BitrateArray = $_GET['bitrates']; - if (count($BitrateArray) !== count($Bitrates)) { - $BitrateNameArray = array(); - foreach ($BitrateArray as $Index => $MasterIndex) { - if (isset($Bitrates[$MasterIndex])) { - $BitrateNameArray[$Index] = '"' . strtr(Sphinxql::sph_escape_string($Bitrates[$MasterIndex]), '-.', ' ') . '"'; - } - } - - if (count($BitrateNameArray) >= 1) { - $EnableNegation = true; - if (!empty($_GET['bitrate_strict'])) { - $SearchString = '(' . implode(' | ', $BitrateNameArray) . ')'; - } else { - $SearchString = '(any | ' . implode(' | ', $BitrateNameArray) . ')'; - } - $SphQL->where_match($SearchString, 'bitratelist', false); - } - } + $BitrateArray = $_GET['bitrates']; + if (count($BitrateArray) !== count($Bitrates)) { + $BitrateNameArray = []; + foreach ($BitrateArray as $Index => $MasterIndex) { + if (isset($Bitrates[$MasterIndex])) { + $BitrateNameArray[$Index] = '"' . strtr(Sphinxql::sph_escape_string($Bitrates[$MasterIndex]), '-.', ' ') . '"'; + } + } + + if (count($BitrateNameArray) >= 1) { + $EnableNegation = true; + if (!empty($_GET['bitrate_strict'])) { + $SearchString = '(' . implode(' | ', $BitrateNameArray) . ')'; + } else { + $SearchString = '(any | ' . implode(' | ', $BitrateNameArray) . ')'; + } + $SphQL->where_match($SearchString, 'bitratelist', false); + } + } } if (!empty($_GET['search'])) { - $SearchString = trim($_GET['search']); - - if ($SearchString !== '') { - $SearchWords = array('include' => array(), 'exclude' => array()); - $Words = explode(' ', $SearchString); - foreach ($Words as $Word) { - $Word = trim($Word); - // Skip isolated hyphens to enable "Artist - Title" searches - if ($Word === '-') { - continue; - } - if ($Word[0] === '!' && strlen($Word) >= 2) { - if (strpos($Word, '!', 1) === false) { - $SearchWords['exclude'][] = $Word; - } else { - $SearchWords['include'][] = $Word; - $EnableNegation = true; - } - } elseif ($Word !== '') { - $SearchWords['include'][] = $Word; - $EnableNegation = true; - } - } - } + $SearchString = trim($_GET['search']); + + if ($SearchString !== '') { + $SearchWords = array('include' => [], 'exclude' => []); + $Words = explode(' ', $SearchString); + foreach ($Words as $Word) { + $Word = trim($Word); + // Skip isolated hyphens to enable "Artist - Title" searches + if ($Word === '-') { + continue; + } + if ($Word[0] === '!' && strlen($Word) >= 2) { + if (strpos($Word, '!', 1) === false) { + $SearchWords['exclude'][] = $Word; + } else { + $SearchWords['include'][] = $Word; + $EnableNegation = true; + } + } elseif ($Word !== '') { + $SearchWords['include'][] = $Word; + $EnableNegation = true; + } + } + } } if (!isset($_GET['tags_type']) || $_GET['tags_type'] === '1') { - $TagType = 1; - $_GET['tags_type'] = '1'; + $TagType = 1; + $_GET['tags_type'] = '1'; } else { - $TagType = 0; - $_GET['tags_type'] = '0'; + $TagType = 0; + $_GET['tags_type'] = '0'; } if (!empty($_GET['tags'])) { - $SearchTags = array('include' => array(), 'exclude' => array()); - $Tags = explode(',', str_replace('.', '_', $_GET['tags'])); - foreach ($Tags as $Tag) { - $Tag = trim($Tag); - if ($Tag[0] === '!' && strlen($Tag) >= 2) { - if (strpos($Tag, '!', 1) === false) { - $SearchTags['exclude'][] = $Tag; - } else { - $SearchTags['include'][] = $Tag; - $EnableNegation = true; - } - } elseif ($Tag !== '') { - $SearchTags['include'][] = $Tag; - $EnableNegation = true; - } - } - - $TagFilter = Tags::tag_filter_sph($SearchTags, $EnableNegation, $TagType); - $TagNames = $TagFilter['input']; - - if (!empty($TagFilter['predicate'])) { - $SphQL->where_match($TagFilter['predicate'], 'taglist', false); - } + $SearchTags = array('include' => [], 'exclude' => []); + $Tags = explode(',', str_replace('.', '_', $_GET['tags'])); + foreach ($Tags as $Tag) { + $Tag = trim($Tag); + if ($Tag[0] === '!' && strlen($Tag) >= 2) { + if (strpos($Tag, '!', 1) === false) { + $SearchTags['exclude'][] = $Tag; + } else { + $SearchTags['include'][] = $Tag; + $EnableNegation = true; + } + } elseif ($Tag !== '') { + $SearchTags['include'][] = $Tag; + $EnableNegation = true; + } + } + + $TagFilter = Tags::tag_filter_sph($SearchTags, $EnableNegation, $TagType); + $TagNames = $TagFilter['input']; + + if (!empty($TagFilter['predicate'])) { + $SphQL->where_match($TagFilter['predicate'], 'taglist', false); + } } elseif (!isset($_GET['tags_type']) || $_GET['tags_type'] !== '0') { - $_GET['tags_type'] = 1; + $_GET['tags_type'] = 1; } else { - $_GET['tags_type'] = 0; + $_GET['tags_type'] = 0; } if (isset($SearchWords)) { - $QueryParts = array(); - if (!$EnableNegation && !empty($SearchWords['exclude'])) { - $SearchWords['include'] = array_merge($SearchWords['include'], $SearchWords['exclude']); - unset($SearchWords['exclude']); - } - foreach ($SearchWords['include'] as $Word) { - $QueryParts[] = Sphinxql::sph_escape_string($Word); - } - if (!empty($SearchWords['exclude'])) { - foreach ($SearchWords['exclude'] as $Word) { - $QueryParts[] = '!' . Sphinxql::sph_escape_string(substr($Word, 1)); - } - } - if (!empty($QueryParts)) { - $SearchString = implode(' ', $QueryParts); - $SphQL->where_match($SearchString, '*', false); - } + $QueryParts = []; + if (!$EnableNegation && !empty($SearchWords['exclude'])) { + $SearchWords['include'] = array_merge($SearchWords['include'], $SearchWords['exclude']); + unset($SearchWords['exclude']); + } + foreach ($SearchWords['include'] as $Word) { + $QueryParts[] = Sphinxql::sph_escape_string($Word); + } + if (!empty($SearchWords['exclude'])) { + foreach ($SearchWords['exclude'] as $Word) { + $QueryParts[] = '!' . Sphinxql::sph_escape_string(substr($Word, 1)); + } + } + if (!empty($QueryParts)) { + $SearchString = implode(' ', $QueryParts); + $SphQL->where_match($SearchString, '*', false); + } } if (!empty($_GET['filter_cat'])) { - $CategoryArray = array_keys($_GET['filter_cat']); - if (count($CategoryArray) !== count($CategoriesV2)) { - foreach ($CategoryArray as $Key => $Index) { - if (!isset($CategoriesV2[$Index - 1])) { - unset($CategoryArray[$Key]); - } - } - if (count($CategoryArray) >= 1) { - $SphQL->where('categoryid', $CategoryArray); - } - } + $CategoryArray = array_keys($_GET['filter_cat']); + if (count($CategoryArray) !== count($CategoriesV2)) { + foreach ($CategoryArray as $Key => $Index) { + if (!isset($CategoriesV2[$Index - 1])) { + unset($CategoryArray[$Key]); + } + } + if (count($CategoryArray) >= 1) { + $SphQL->where('categoryid', $CategoryArray); + } + } } if (!empty($_GET['releases'])) { - $ReleaseArray = $_GET['releases']; - if (count($ReleaseArray) !== count($ReleaseTypes)) { - foreach ($ReleaseArray as $Index => $Value) { - if (!isset($ReleaseTypes[$Value])) { - unset($ReleaseArray[$Index]); - } - } - if (count($ReleaseArray) >= 1) { - $SphQL->where('releasetype', $ReleaseArray); - } - } + $ReleaseArray = $_GET['releases']; + if (count($ReleaseArray) !== count($ReleaseTypes)) { + foreach ($ReleaseArray as $Index => $Value) { + if (!isset($ReleaseTypes[$Value])) { + unset($ReleaseArray[$Index]); + } + } + if (count($ReleaseArray) >= 1) { + $SphQL->where('releasetype', $ReleaseArray); + } + } } if (!empty($_GET['requestor'])) { - if (intval($_GET['requestor'])) { - $SphQL->where('userid', $_GET['requestor']); - } else { - error(404); - } + if (intval($_GET['requestor'])) { + $SphQL->where('userid', $_GET['requestor']); + } else { + error(404); + } } if (isset($_GET['year'])) { - if (intval($_GET['year']) || $_GET['year'] === '0') { - $SphQL->where('year', $_GET['year']); - } else { - error(404); - } + if (intval($_GET['year']) || $_GET['year'] === '0') { + $SphQL->where('year', $_GET['year']); + } else { + error(404); + } } if (!empty($_GET['page']) && intval($_GET['page']) && $_GET['page'] > 0) { - $Page = $_GET['page']; - $Offset = ($Page - 1) * REQUESTS_PER_PAGE; - $SphQL->limit($Offset, REQUESTS_PER_PAGE, $Offset + REQUESTS_PER_PAGE); + $Page = $_GET['page']; + $Offset = ($Page - 1) * REQUESTS_PER_PAGE; + $SphQL->limit($Offset, REQUESTS_PER_PAGE, $Offset + REQUESTS_PER_PAGE); } else { - $Page = 1; - $SphQL->limit(0, REQUESTS_PER_PAGE, REQUESTS_PER_PAGE); + $Page = 1; + $SphQL->limit(0, REQUESTS_PER_PAGE, REQUESTS_PER_PAGE); } $SphQLResult = $SphQL->query(); $NumResults = (int)$SphQLResult->get_meta('total_found'); if ($NumResults > 0) { - $SphRequests = $SphQLResult->to_array('id'); - if ($OrderBy === 'random') { - $NumResults = count($SphRequests); - } - if ($NumResults > REQUESTS_PER_PAGE) { - if (($Page - 1) * REQUESTS_PER_PAGE > $NumResults) { - $Page = 0; - } - $PageLinks = Format::get_pages($Page, $NumResults, REQUESTS_PER_PAGE); - } + $SphRequests = $SphQLResult->to_array('id'); + if ($OrderBy === 'random') { + $NumResults = count($SphRequests); + } + if ($NumResults > REQUESTS_PER_PAGE) { + if (($Page - 1) * REQUESTS_PER_PAGE > $NumResults) { + $Page = 0; + } + $PageLinks = Format::get_pages($Page, $NumResults, REQUESTS_PER_PAGE); + } } $CurrentURL = Format::get_url(array('order', 'sort', 'page')); @@ -338,324 +338,324 @@ ?> <div class="thin"> - <div class="header"> - <h2><?=$Title?></h2> - </div> - <div class="linkbox"> -<? if (!$BookmarkView) { - if (check_perms('site_submit_requests')) { ?> - <a href="requests.php?action=new" class="brackets">New request</a> - <a href="requests.php?type=created" class="brackets">My requests</a> -<? } - if (check_perms('site_vote')) { ?> - <a href="requests.php?type=voted" class="brackets">Requests I've voted on</a> -<? } ?> - <a href="bookmarks.php?type=requests" class="brackets">Bookmarked requests</a> -<? } else { ?> - <a href="bookmarks.php?type=torrents" class="brackets">Torrents</a> - <a href="bookmarks.php?type=artists" class="brackets">Artists</a> - <a href="bookmarks.php?type=collages" class="brackets">Collages</a> - <a href="bookmarks.php?type=requests" class="brackets">Requests</a> -<? } ?> - </div> -<? if ($BookmarkView && $NumResults === 0) { ?> - <div class="box pad" align="center"> - <h2>You have not bookmarked any requests.</h2> - </div> -<? } else { ?> - <form class="search_form" name="requests" action="" method="get"> -<? if ($BookmarkView) { ?> - <input type="hidden" name="action" value="view" /> - <input type="hidden" name="type" value="requests" /> -<? } elseif (isset($_GET['type'])) { ?> - <input type="hidden" name="type" value="<?=$_GET['type']?>" /> -<? } ?> - <input type="hidden" name="submit" value="true" /> -<? if (!empty($_GET['userid']) && intval($_GET['userid'])) { ?> - <input type="hidden" name="userid" value="<?=$_GET['userid']?>" /> -<? } ?> - <table cellpadding="6" cellspacing="1" border="0" class="layout border" width="100%"> - <tr id="search_terms"> - <td class="label">Search terms:</td> - <td> - <input type="search" name="search" size="75" value="<? if (isset($_GET['search'])) { echo display_str($_GET['search']); } ?>" /> - </td> - </tr> - <tr id="tagfilter"> - <td class="label">Tags (comma-separated):</td> - <td> - <input type="search" name="tags" id="tags" size="60" value="<?=!empty($TagNames) ? display_str($TagNames) : ''?>"<? Users::has_autocomplete_enabled('other'); ?> />  - <input type="radio" name="tags_type" id="tags_type0" value="0"<? Format::selected('tags_type', 0, 'checked')?> /><label for="tags_type0"> Any</label>   - <input type="radio" name="tags_type" id="tags_type1" value="1"<? Format::selected('tags_type', 1, 'checked')?> /><label for="tags_type1"> All</label> - </td> - </tr> - <tr id="include_filled"> - <td class="label"><label for="include_filled_box">Include filled:</label></td> - <td> - <input type="checkbox" id="include_filled_box" name="show_filled"<? if (!empty($_GET['show_filled']) || (!$Submitted && !empty($_GET['type']) && $_GET['type'] === 'filled')) { ?> checked="checked"<? } ?> /> - </td> - </tr> - <tr id="include_old"> - <td class="label"><label for="include_old_box">Include old:</label></td> - <td> - <input type="checkbox" id="include_old_box" name="showall"<? if (!$Submitted || !empty($_GET['showall'])) { ?> checked="checked"<? } ?> /> - </td> - </tr> -<? /* ?> - <tr> - <td class="label">Requested by:</td> - <td> - <input type="search" name="requester" size="75" value="<?=display_str($_GET['requester'])?>" /> - </td> - </tr> -<? */ ?> - </table> - <table class="layout cat_list"> -<? - $x = 1; - reset($CategoriesV2); - foreach ($CategoriesV2 as $CatKey => $CatName) { - if ($x % 8 === 0 || $x === 1) { + <div class="header"> + <h2><?=$Title?></h2> + </div> + <div class="linkbox"> +<?php if (!$BookmarkView) { + if (check_perms('site_submit_requests')) { ?> + <a href="requests.php?action=new" class="brackets">New request</a> + <a href="requests.php?type=created" class="brackets">My requests</a> +<?php } + if (check_perms('site_vote')) { ?> + <a href="requests.php?type=voted" class="brackets">Requests I've voted on</a> +<?php } ?> + <a href="bookmarks.php?type=requests" class="brackets">Bookmarked requests</a> +<?php } else { ?> + <a href="bookmarks.php?type=torrents" class="brackets">Torrents</a> + <a href="bookmarks.php?type=artists" class="brackets">Artists</a> + <a href="bookmarks.php?type=collages" class="brackets">Collages</a> + <a href="bookmarks.php?type=requests" class="brackets">Requests</a> +<?php } ?> + </div> +<?php if ($BookmarkView && $NumResults === 0) { ?> + <div class="box pad" align="center"> + <h2>You have not bookmarked any requests.</h2> + </div> +<?php } else { ?> + <form class="search_form" name="requests" action="" method="get"> +<?php if ($BookmarkView) { ?> + <input type="hidden" name="action" value="view" /> + <input type="hidden" name="type" value="requests" /> +<?php } elseif (isset($_GET['type'])) { ?> + <input type="hidden" name="type" value="<?=$_GET['type']?>" /> +<?php } ?> + <input type="hidden" name="submit" value="true" /> +<?php if (!empty($_GET['userid']) && intval($_GET['userid'])) { ?> + <input type="hidden" name="userid" value="<?=$_GET['userid']?>" /> +<?php } ?> + <table cellpadding="6" cellspacing="1" border="0" class="layout border" width="100%"> + <tr id="search_terms"> + <td class="label">Search terms:</td> + <td> + <input type="search" name="search" size="75" value="<?php if (isset($_GET['search'])) { echo display_str($_GET['search']); } ?>" /> + </td> + </tr> + <tr id="tagfilter"> + <td class="label">Tags (comma-separated):</td> + <td> + <input type="search" name="tags" id="tags" size="60" value="<?=!empty($TagNames) ? display_str($TagNames) : ''?>"<?php Users::has_autocomplete_enabled('other'); ?> />  + <input type="radio" name="tags_type" id="tags_type0" value="0"<?php Format::selected('tags_type', 0, 'checked')?> /><label for="tags_type0"> Any</label>   + <input type="radio" name="tags_type" id="tags_type1" value="1"<?php Format::selected('tags_type', 1, 'checked')?> /><label for="tags_type1"> All</label> + </td> + </tr> + <tr id="include_filled"> + <td class="label"><label for="include_filled_box">Include filled:</label></td> + <td> + <input type="checkbox" id="include_filled_box" name="show_filled"<?php if (!empty($_GET['show_filled']) || (!$Submitted && !empty($_GET['type']) && $_GET['type'] === 'filled')) { ?> checked="checked"<?php } ?> /> + </td> + </tr> + <tr id="include_old"> + <td class="label"><label for="include_old_box">Include old:</label></td> + <td> + <input type="checkbox" id="include_old_box" name="showall"<?php if (!$Submitted || !empty($_GET['showall'])) { ?> checked="checked"<?php } ?> /> + </td> + </tr> +<?php /* ?> + <tr> + <td class="label">Requested by:</td> + <td> + <input type="search" name="requester" size="75" value="<?=display_str($_GET['requester'])?>" /> + </td> + </tr> +<? */ ?> + </table> + <table class="layout cat_list"> +<?php + $x = 1; + reset($CategoriesV2); + foreach ($CategoriesV2 as $CatKey => $CatName) { + if ($x % 8 === 0 || $x === 1) { ?> - <tr> -<? } ?> - <td> - <input type="checkbox" name="filter_cat[<?=($CatKey + 1) ?>]" id="cat_<?=($CatKey + 1) ?>" value="1"<? if (isset($_GET['filter_cat'][$CatKey + 1])) { ?> checked="checked"<? } ?> /> - <label for="cat_<?=($CatKey + 1) ?>"><?=$CatName?></label> - </td> -<? if ($x % 7 === 0) { ?> - </tr> -<? - } - $x++; - } + <tr> +<?php } ?> + <td> + <input type="checkbox" name="filter_cat[<?=($CatKey + 1) ?>]" id="cat_<?=($CatKey + 1) ?>" value="1"<?php if (isset($_GET['filter_cat'][$CatKey + 1])) { ?> checked="checked"<?php } ?> /> + <label for="cat_<?=($CatKey + 1) ?>"><?=$CatName?></label> + </td> +<?php if ($x % 7 === 0) { ?> + </tr> +<?php + } + $x++; + } ?> - </table> - <table class="layout"> - <tr id="release_list"> - <td class="label">Release types</td> - <td> - <input type="checkbox" id="toggle_releases" onchange="Toggle('releases', 0);"<?=(!$Submitted || !empty($ReleaseArray) && count($ReleaseArray) === count($ReleaseTypes) ? ' checked="checked"' : '') ?> /> <label for="toggle_releases">All</label> -<? - $i = 0; - foreach ($ReleaseTypes as $Key => $Val) { - if ($i % 8 === 0) { - echo '<br />'; - } + </table> + <table class="layout"> + <tr id="release_list"> + <td class="label">Release types</td> + <td> + <input type="checkbox" id="toggle_releases" onchange="Toggle('releases', 0);"<?=(!$Submitted || !empty($ReleaseArray) && count($ReleaseArray) === count($ReleaseTypes) ? ' checked="checked"' : '') ?> /> <label for="toggle_releases">All</label> +<?php + $i = 0; + foreach ($ReleaseTypes as $Key => $Val) { + if ($i % 8 === 0) { + echo '<br />'; + } ?> - <input type="checkbox" name="releases[]" value="<?=$Key?>" id="release_<?=$Key?>" - <?=(!$Submitted || (!empty($ReleaseArray) && in_array($Key, $ReleaseArray)) ? ' checked="checked" ' : '')?> - /> <label for="release_<?=$Key?>"><?=$Val?></label> -<? - $i++; - } + <input type="checkbox" name="releases[]" value="<?=$Key?>" id="release_<?=$Key?>" + <?=(!$Submitted || (!empty($ReleaseArray) && in_array($Key, $ReleaseArray)) ? ' checked="checked" ' : '')?> + /> <label for="release_<?=$Key?>"><?=$Val?></label> +<?php + $i++; + } ?> - </td> - </tr> - <tr id="format_list"> - <td class="label">Formats</td> - <td> - <input type="checkbox" id="toggle_formats" onchange="Toggle('formats', 0);"<?=(!$Submitted || !empty($FormatArray) && count($FormatArray) === count($Formats) ? ' checked="checked"' : '') ?> /> - <label for="toggle_formats">All</label> - <input type="checkbox" id="formats_strict" name="formats_strict"<?=(!empty($_GET['formats_strict']) ? ' checked="checked"' : '')?> /> - <label for="formats_strict">Only specified</label> -<? - foreach ($Formats as $Key => $Val) { - if ($Key % 8 === 0) { - echo '<br />'; - } + </td> + </tr> + <tr id="format_list"> + <td class="label">Formats</td> + <td> + <input type="checkbox" id="toggle_formats" onchange="Toggle('formats', 0);"<?=(!$Submitted || !empty($FormatArray) && count($FormatArray) === count($Formats) ? ' checked="checked"' : '') ?> /> + <label for="toggle_formats">All</label> + <input type="checkbox" id="formats_strict" name="formats_strict"<?=(!empty($_GET['formats_strict']) ? ' checked="checked"' : '')?> /> + <label for="formats_strict">Only specified</label> +<?php + foreach ($Formats as $Key => $Val) { + if ($Key % 8 === 0) { + echo '<br />'; + } ?> - <input type="checkbox" name="formats[]" value="<?=$Key?>" id="format_<?=$Key?>" - <?=(!$Submitted || (!empty($FormatArray) && in_array($Key, $FormatArray)) ? ' checked="checked" ' : '')?> - /> <label for="format_<?=$Key?>"><?=$Val?></label> -<? } ?> - </td> - </tr> - <tr id="bitrate_list"> - <td class="label">Bitrates</td> - <td> - <input type="checkbox" id="toggle_bitrates" onchange="Toggle('bitrates', 0);"<?=(!$Submitted || !empty($BitrateArray) && count($BitrateArray) === count($Bitrates) ? ' checked="checked"' : '')?> /> - <label for="toggle_bitrates">All</label> - <input type="checkbox" id="bitrate_strict" name="bitrate_strict"<?=(!empty($_GET['bitrate_strict']) ? ' checked="checked"' : '') ?> /> - <label for="bitrate_strict">Only specified</label> -<? - foreach ($Bitrates as $Key => $Val) { - if ($Key % 8 === 0) { - echo '<br />'; - } + <input type="checkbox" name="formats[]" value="<?=$Key?>" id="format_<?=$Key?>" + <?=(!$Submitted || (!empty($FormatArray) && in_array($Key, $FormatArray)) ? ' checked="checked" ' : '')?> + /> <label for="format_<?=$Key?>"><?=$Val?></label> +<?php } ?> + </td> + </tr> + <tr id="bitrate_list"> + <td class="label">Bitrates</td> + <td> + <input type="checkbox" id="toggle_bitrates" onchange="Toggle('bitrates', 0);"<?=(!$Submitted || !empty($BitrateArray) && count($BitrateArray) === count($Bitrates) ? ' checked="checked"' : '')?> /> + <label for="toggle_bitrates">All</label> + <input type="checkbox" id="bitrate_strict" name="bitrate_strict"<?=(!empty($_GET['bitrate_strict']) ? ' checked="checked"' : '') ?> /> + <label for="bitrate_strict">Only specified</label> +<?php + foreach ($Bitrates as $Key => $Val) { + if ($Key % 8 === 0) { + echo '<br />'; + } ?> - <input type="checkbox" name="bitrates[]" value="<?=$Key?>" id="bitrate_<?=$Key?>" - <?=(!$Submitted || (!empty($BitrateArray) && in_array($Key, $BitrateArray)) ? ' checked="checked" ' : '')?> - /> <label for="bitrate_<?=$Key?>"><?=$Val?></label> -<? - } + <input type="checkbox" name="bitrates[]" value="<?=$Key?>" id="bitrate_<?=$Key?>" + <?=(!$Submitted || (!empty($BitrateArray) && in_array($Key, $BitrateArray)) ? ' checked="checked" ' : '')?> + /> <label for="bitrate_<?=$Key?>"><?=$Val?></label> +<?php + } ?> - </td> - </tr> - <tr id="media_list"> - <td class="label">Media</td> - <td> - <input type="checkbox" id="toggle_media" onchange="Toggle('media', 0);"<?=(!$Submitted || !empty($MediaArray) && count($MediaArray) === count($Media) ? ' checked="checked"' : '')?> /> - <label for="toggle_media">All</label> - <input type="checkbox" id="media_strict" name="media_strict"<?=(!empty($_GET['media_strict']) ? ' checked="checked"' : '')?> /> - <label for="media_strict">Only specified</label> -<? - foreach ($Media as $Key => $Val) { - if ($Key % 8 === 0) { - echo '<br />'; - } + </td> + </tr> + <tr id="media_list"> + <td class="label">Media</td> + <td> + <input type="checkbox" id="toggle_media" onchange="Toggle('media', 0);"<?=(!$Submitted || !empty($MediaArray) && count($MediaArray) === count($Media) ? ' checked="checked"' : '')?> /> + <label for="toggle_media">All</label> + <input type="checkbox" id="media_strict" name="media_strict"<?=(!empty($_GET['media_strict']) ? ' checked="checked"' : '')?> /> + <label for="media_strict">Only specified</label> +<?php + foreach ($Media as $Key => $Val) { + if ($Key % 8 === 0) { + echo '<br />'; + } ?> - <input type="checkbox" name="media[]" value="<?=$Key?>" id="media_<?=$Key?>" - <?=(!$Submitted || (!empty($MediaArray) && in_array($Key, $MediaArray)) ? ' checked="checked" ' : '')?> - /> <label for="media_<?=$Key?>"><?=$Val?></label> -<? } ?> - </td> - </tr> - <tr> - <td colspan="2" class="center"> - <input type="submit" value="Search requests" /> - </td> - </tr> - </table> - </form> -<? if (isset($PageLinks)) { ?> - <div class="linkbox"> - <?= $PageLinks?> - </div> -<? } ?> - <table id="request_table" class="request_table border m_table" cellpadding="6" cellspacing="1" border="0" width="100%"> - <tr class="colhead_dark"> - <td style="width: 38%;" class="m_th_left nobr"> - <strong>Request Name</strong> / <a href="?order=year&sort=<?=($OrderBy === 'year' ? $NewSort : 'desc')?>&<?=$CurrentURL?>"><strong>Year</strong></a> - </td> - <td class="m_th_right nobr"> - <a href="?order=votes&sort=<?=($OrderBy === 'votes' ? $NewSort : 'desc')?>&<?=$CurrentURL?>"><strong>Votes</strong></a> - </td> - <td class="m_th_right nobr"> - <a href="?order=bounty&sort=<?=($OrderBy === 'bounty' ? $NewSort : 'desc')?>&<?=$CurrentURL?>"><strong>Bounty</strong></a> - </td> - <td class="nobr"> - <a href="?order=filled&sort=<?=($OrderBy === 'filled' ? $NewSort : 'desc')?>&<?=$CurrentURL?>"><strong>Filled</strong></a> - </td> - <td class="nobr"> - <strong>Filled by</strong> - </td> - <td class="nobr"> - <strong>Requested by</strong> - </td> - <td class="nobr"> - <a href="?order=created&sort=<?=($OrderBy === 'created' ? $NewSort : 'desc')?>&<?=$CurrentURL?>"><strong>Created</strong></a> - </td> - <td class="nobr"> - <a href="?order=lastvote&sort=<?=($OrderBy === 'lastvote' ? $NewSort : 'desc')?>&<?=$CurrentURL?>"><strong>Last vote</strong></a> - </td> - </tr> -<? - if ($NumResults === 0) { - // not viewing bookmarks but no requests found + <input type="checkbox" name="media[]" value="<?=$Key?>" id="media_<?=$Key?>" + <?=(!$Submitted || (!empty($MediaArray) && in_array($Key, $MediaArray)) ? ' checked="checked" ' : '')?> + /> <label for="media_<?=$Key?>"><?=$Val?></label> +<?php } ?> + </td> + </tr> + <tr> + <td colspan="2" class="center"> + <input type="submit" value="Search requests" /> + </td> + </tr> + </table> + </form> +<?php if (isset($PageLinks)) { ?> + <div class="linkbox"> + <?= $PageLinks?> + </div> +<?php } ?> + <table id="request_table" class="request_table border m_table" cellpadding="6" cellspacing="1" border="0" width="100%"> + <tr class="colhead_dark"> + <td style="width: 38%;" class="m_th_left nobr"> + <strong>Request Name</strong> / <a href="?order=year&sort=<?=($OrderBy === 'year' ? $NewSort : 'desc')?>&<?=$CurrentURL?>"><strong>Year</strong></a> + </td> + <td class="m_th_right nobr"> + <a href="?order=votes&sort=<?=($OrderBy === 'votes' ? $NewSort : 'desc')?>&<?=$CurrentURL?>"><strong>Votes</strong></a> + </td> + <td class="m_th_right nobr"> + <a href="?order=bounty&sort=<?=($OrderBy === 'bounty' ? $NewSort : 'desc')?>&<?=$CurrentURL?>"><strong>Bounty</strong></a> + </td> + <td class="nobr"> + <a href="?order=filled&sort=<?=($OrderBy === 'filled' ? $NewSort : 'desc')?>&<?=$CurrentURL?>"><strong>Filled</strong></a> + </td> + <td class="nobr"> + <strong>Filled by</strong> + </td> + <td class="nobr"> + <strong>Requested by</strong> + </td> + <td class="nobr"> + <a href="?order=created&sort=<?=($OrderBy === 'created' ? $NewSort : 'desc')?>&<?=$CurrentURL?>"><strong>Created</strong></a> + </td> + <td class="nobr"> + <a href="?order=lastvote&sort=<?=($OrderBy === 'lastvote' ? $NewSort : 'desc')?>&<?=$CurrentURL?>"><strong>Last vote</strong></a> + </td> + </tr> +<?php + if ($NumResults === 0) { + // not viewing bookmarks but no requests found ?> - <tr class="rowb"> - <td colspan="8"> - Nothing found! - </td> - </tr> -<? } elseif ($Page === 0) { ?> - <tr class="rowb"> - <td colspan="8"> - The requested page contains no matches! - </td> - </tr> -<? - } else { - - $TimeCompare = 1267643718; // Requests v2 was implemented 2010-03-03 20:15:18 - $Requests = Requests::get_requests(array_keys($SphRequests)); - foreach ($Requests as $RequestID => $Request) { - $SphRequest = $SphRequests[$RequestID]; - $Bounty = $SphRequest['bounty'] * 1024; // Sphinx stores bounty in kB - $VoteCount = $SphRequest['votes']; - - if ($Request['CategoryID'] == 0) { - $CategoryName = 'Unknown'; - } else { - $CategoryName = $CategoriesV2[$Request['CategoryID'] - 1]; - } - - if ($Request['TorrentID'] != 0) { - $IsFilled = true; - $FillerInfo = Users::user_info($Request['FillerID']); - } else { - $IsFilled = false; - } - - if ($CategoryName === 'Music') { - $ArtistForm = Requests::get_artists($RequestID); - $ArtistLink = Artists::display_artists($ArtistForm, true, true); - $FullName = "$ArtistLink<a href=\"requests.php?action=view&id=$RequestID\"><span dir=\"ltr\">$Request[Title]</span> [$Request[Year]]</a>"; - } elseif ($CategoryName === 'Audiobooks' || $CategoryName === 'Comedy') { - $FullName = "<a href=\"requests.php?action=view&id=$RequestID\"><span dir=\"ltr\">$Request[Title]</span> [$Request[Year]]</a>"; - } else { - $FullName = "<a href=\"requests.php?action=view&id=$RequestID\" dir=\"ltr\">$Request[Title]</a>"; - } - $Tags = $Request['Tags']; + <tr class="rowb"> + <td colspan="8"> + Nothing found! + </td> + </tr> +<?php } elseif ($Page === 0) { ?> + <tr class="rowb"> + <td colspan="8"> + The requested page contains no matches! + </td> + </tr> +<?php + } else { + + $TimeCompare = 1267643718; // Requests v2 was implemented 2010-03-03 20:15:18 + $Requests = Requests::get_requests(array_keys($SphRequests)); + foreach ($Requests as $RequestID => $Request) { + $SphRequest = $SphRequests[$RequestID]; + $Bounty = $SphRequest['bounty'] * 1024; // Sphinx stores bounty in kB + $VoteCount = $SphRequest['votes']; + + if ($Request['CategoryID'] == 0) { + $CategoryName = 'Unknown'; + } else { + $CategoryName = $CategoriesV2[$Request['CategoryID'] - 1]; + } + + if ($Request['TorrentID'] != 0) { + $IsFilled = true; + $FillerInfo = Users::user_info($Request['FillerID']); + } else { + $IsFilled = false; + } + + if ($CategoryName === 'Music') { + $ArtistForm = Requests::get_artists($RequestID); + $ArtistLink = Artists::display_artists($ArtistForm, true, true); + $FullName = "$ArtistLink<a href=\"requests.php?action=view&id=$RequestID\"><span dir=\"ltr\">$Request[Title]</span> [$Request[Year]]</a>"; + } elseif ($CategoryName === 'Audiobooks' || $CategoryName === 'Comedy') { + $FullName = "<a href=\"requests.php?action=view&id=$RequestID\"><span dir=\"ltr\">$Request[Title]</span> [$Request[Year]]</a>"; + } else { + $FullName = "<a href=\"requests.php?action=view&id=$RequestID\" dir=\"ltr\">$Request[Title]</a>"; + } + $Tags = $Request['Tags']; ?> - <tr class="row<?=($i % 2 ? 'b' : 'a')?>"> - <td> - <?=$FullName?> - <div class="tags"> -<? - $TagList = array(); - foreach ($Request['Tags'] as $TagID => $TagName) { - $TagList[] = '<a href="?tags='.$TagName.($BookmarkView ? '&type=requests' : '').'">'.display_str($TagName).'</a>'; - } - $TagList = implode(', ', $TagList); + <tr class="row<?=($i % 2 ? 'b' : 'a')?>"> + <td> + <?=$FullName?> + <div class="tags"> +<?php + $TagList = []; + foreach ($Request['Tags'] as $TagID => $TagName) { + $TagList[] = '<a href="?tags='.$TagName.($BookmarkView ? '&type=requests' : '').'">'.display_str($TagName).'</a>'; + } + $TagList = implode(', ', $TagList); ?> - <?=$TagList?> - </div> - </td> - <td class="m_td_right nobr"> - <span id="vote_count_<?=$RequestID?>"><?=number_format($VoteCount)?></span> -<? if (!$IsFilled && check_perms('site_vote')) { ?> -    <a href="javascript:Vote(0, <?=$RequestID?>)" class="brackets"><strong>+</strong></a> -<? } ?> - </td> - <td class="m_td_right number_column nobr"> - <?=Format::get_size($Bounty)?> - </td> - <td class="m_hidden nobr"> -<? if ($IsFilled) { ?> - <a href="torrents.php?<?=(strtotime($Request['TimeFilled']) < $TimeCompare ? 'id=' : 'torrentid=') . $Request['TorrentID']?>"><strong><?=time_diff($Request['TimeFilled'], 1)?></strong></a> -<? } else { ?> - <strong>No</strong> -<? } ?> - </td> - <td> -<? if ($IsFilled) { ?> - <a href="user.php?id=<?=$FillerInfo['ID']?>"><?=$FillerInfo['Username']?></a> -<? } else { ?> - — -<? } ?> - </td> - <td> - <a href="user.php?id=<?=$Request['UserID']?>"><?=Users::format_username($Request['UserID'], false, false, false)?></a> - </td> - <td class="nobr"> - <?=time_diff($Request['TimeAdded'], 1)?> - </td> - <td class="nobr"> - <?=time_diff($Request['LastVote'], 1)?> - </td> - </tr> -<? - } // foreach - } // else - } // if ($BookmarkView && $NumResults < 1) + <?=$TagList?> + </div> + </td> + <td class="m_td_right nobr"> + <span id="vote_count_<?=$RequestID?>"><?=number_format($VoteCount)?></span> +<?php if (!$IsFilled && check_perms('site_vote')) { ?> +    <a href="javascript:Vote(0, <?=$RequestID?>)" class="brackets"><strong>+</strong></a> +<?php } ?> + </td> + <td class="m_td_right number_column nobr"> + <?=Format::get_size($Bounty)?> + </td> + <td class="m_hidden nobr"> +<?php if ($IsFilled) { ?> + <a href="torrents.php?<?=(strtotime($Request['TimeFilled']) < $TimeCompare ? 'id=' : 'torrentid=') . $Request['TorrentID']?>"><strong><?=time_diff($Request['TimeFilled'], 1)?></strong></a> +<?php } else { ?> + <strong>No</strong> +<?php } ?> + </td> + <td> +<?php if ($IsFilled) { ?> + <a href="user.php?id=<?=$FillerInfo['ID']?>"><?=$FillerInfo['Username']?></a> +<?php } else { ?> + — +<?php } ?> + </td> + <td> + <a href="user.php?id=<?=$Request['UserID']?>"><?=Users::format_username($Request['UserID'], false, false, false)?></a> + </td> + <td class="nobr"> + <?=time_diff($Request['TimeAdded'], 1)?> + </td> + <td class="nobr"> + <?=time_diff($Request['LastVote'], 1)?> + </td> + </tr> +<?php + } // foreach + } // else + } // if ($BookmarkView && $NumResults < 1) ?> - </table> -<? if (isset($PageLinks)) { ?> - <div class="linkbox"> - <?=$PageLinks?> - </div> -<? } ?> + </table> +<?php if (isset($PageLinks)) { ?> + <div class="linkbox"> + <?=$PageLinks?> + </div> +<?php } ?> </div> -<? View::show_footer(); ?> +<?php View::show_footer(); ?> diff --git a/sections/requests/take_delete.php b/sections/requests/take_delete.php index bf5a26d47..0311d848a 100644 --- a/sections/requests/take_delete.php +++ b/sections/requests/take_delete.php @@ -1,4 +1,4 @@ -<? +<?php //******************************************************************************// //--------------- Delete request -----------------------------------------------// @@ -6,32 +6,32 @@ $RequestID = $_POST['id']; if (!intval($RequestID)) { - error(0); + error(0); } $DB->prepared_query(' - SELECT - UserID, - Title, - CategoryID, - GroupID - FROM requests - WHERE ID = ?', $RequestID); + SELECT + UserID, + Title, + CategoryID, + GroupID + FROM requests + WHERE ID = ?', $RequestID); list($UserID, $Title, $CategoryID, $GroupID) = $DB->next_record(); if ($LoggedUser['ID'] != $UserID && !check_perms('site_moderate_requests')) { - error(403); + error(403); } $CategoryName = $CategoriesV2[$CategoryID - 1]; //Do we need to get artists? if ($CategoryName === 'Music') { - $ArtistForm = Requests::get_artists($RequestID); - $ArtistName = Artists::display_artists($ArtistForm, false, true); - $FullName = $ArtistName.$Title; + $ArtistForm = Requests::get_artists($RequestID); + $ArtistName = Artists::display_artists($ArtistForm, false, true); + $FullName = $ArtistName.$Title; } else { - $FullName = $Title; + $FullName = $Title; } @@ -43,26 +43,26 @@ Comments::delete_page('requests', $RequestID); $DB->prepared_query(' - SELECT ArtistID - FROM requests_artists - WHERE RequestID = ?', $RequestID); + SELECT ArtistID + FROM requests_artists + WHERE RequestID = ?', $RequestID); $RequestArtists = $DB->to_array(); foreach ($RequestArtists as $RequestArtist) { - $Cache->delete_value("artists_requests_$RequestArtist"); + $Cache->delete_value("artists_requests_$RequestArtist"); } $DB->prepared_query(' - DELETE FROM requests_artists - WHERE RequestID = ?', $RequestID); + DELETE FROM requests_artists + WHERE RequestID = ?', $RequestID); $Cache->delete_value("request_artists_$RequestID"); G::$DB->prepared_query(' - REPLACE INTO sphinx_requests_delta - (ID) - VALUES - (?)', $RequestID); + REPLACE INTO sphinx_requests_delta + (ID) + VALUES + (?)', $RequestID); if ($UserID != $LoggedUser['ID']) { - Misc::send_pm($UserID, 0, 'A request you created has been deleted', "The request \"$FullName\" was deleted by [url=".site_url().'user.php?id='.$LoggedUser['ID'].']'.$LoggedUser['Username'].'[/url] for the reason: [quote]'.$_POST['reason'].'[/quote]'); + Misc::send_pm($UserID, 0, 'A request you created has been deleted', "The request \"$FullName\" was deleted by [url=".site_url().'user.php?id='.$LoggedUser['ID'].']'.$LoggedUser['Username'].'[/url] for the reason: [quote]'.$_POST['reason'].'[/quote]'); } Misc::write_log("Request $RequestID ($FullName) was deleted by user ".$LoggedUser['ID'].' ('.$LoggedUser['Username'].') for the reason: '.$_POST['reason']); @@ -70,7 +70,7 @@ $Cache->delete_value("request_$RequestID"); $Cache->delete_value("request_votes_$RequestID"); if ($GroupID) { - $Cache->delete_value("requests_group_$GroupID"); + $Cache->delete_value("requests_group_$GroupID"); } header('Location: requests.php'); diff --git a/sections/requests/take_fill.php b/sections/requests/take_fill.php index a5ce13497..9d4a74eca 100644 --- a/sections/requests/take_fill.php +++ b/sections/requests/take_fill.php @@ -1,56 +1,56 @@ -<? +<?php //******************************************************************************// //--------------- Fill a request -----------------------------------------------// $RequestID = $_REQUEST['requestid']; if (!intval($RequestID)) { - error(0); + error(0); } authorize(); //VALIDATION if (!empty($_GET['torrentid']) && intval($_GET['torrentid'])) { - $TorrentID = $_GET['torrentid']; + $TorrentID = $_GET['torrentid']; } else { - if (empty($_POST['link'])) { - error('You forgot to supply a link to the filling torrent'); - } else { - $Link = $_POST['link']; - if (!preg_match('/'.TORRENT_REGEX.'/i', $Link, $Matches)) { - error('Your link does not appear to be valid (use the [PL] button to obtain the correct URL).'); - } else { - $TorrentID = $Matches[4]; - } - } - if (!$TorrentID || !intval($TorrentID)) { - error(404); - } + if (empty($_POST['link'])) { + error('You forgot to supply a link to the filling torrent'); + } else { + $Link = $_POST['link']; + if (!preg_match('/'.TORRENT_REGEX.'/i', $Link, $Matches)) { + error('Your link does not appear to be valid (use the [PL] button to obtain the correct URL).'); + } else { + $TorrentID = $Matches[4]; + } + } + if (!$TorrentID || !intval($TorrentID)) { + error(404); + } } //Torrent exists, check it's applicable $DB->prepared_query(" - SELECT - t.UserID, - t.Time, - tg.ReleaseType, - t.Encoding, - t.Format, - t.Media, - t.HasLog, - t.HasCue, - t.HasLogDB, - t.LogScore, - t.LogChecksum, - tg.CategoryID, - IF(t.Remastered = '1', t.RemasterCatalogueNumber, tg.CatalogueNumber), + SELECT + t.UserID, + t.Time, + tg.ReleaseType, + t.Encoding, + t.Format, + t.Media, + t.HasLog, + t.HasCue, + t.HasLogDB, + t.LogScore, + t.LogChecksum, + tg.CategoryID, + IF(t.Remastered = '1', t.RemasterCatalogueNumber, tg.CatalogueNumber), CASE WHEN t.Time + INTERVAL 1 HOUR > now() THEN 1 ELSE 0 END as GracePeriod - FROM torrents AS t - LEFT JOIN torrents_group AS tg ON (t.GroupID = tg.ID) - WHERE t.ID = ?", $TorrentID); + FROM torrents AS t + LEFT JOIN torrents_group AS tg ON (t.GroupID = tg.ID) + WHERE t.ID = ?", $TorrentID); if (!$DB->has_results()) { - error(404); + error(404); } list($UploaderID, $UploadTime, $TorrentReleaseType, $Bitrate, $Format, $Media, $HasLog, $HasCue, $HasLogDB, $LogScore, $LogChecksum, $TorrentCategoryID, $TorrentCatalogueNumber, $GracePeriod) = $DB->next_record(); @@ -59,105 +59,105 @@ $Err = []; if (!empty($_POST['user']) && check_perms('site_moderate_requests')) { - $FillerUsername = trim($_POST['user']); - $DB->prepared_query(' - SELECT ID - FROM users_main - WHERE Username = ?', $FillerUsername); - if (!$DB->has_results()) { - $Err[] = 'No such user to fill for!'; - } else { - list($FillerID) = $DB->next_record(); - } + $FillerUsername = trim($_POST['user']); + $DB->prepared_query(' + SELECT ID + FROM users_main + WHERE Username = ?', $FillerUsername); + if (!$DB->has_results()) { + $Err[] = 'No such user to fill for!'; + } else { + list($FillerID) = $DB->next_record(); + } } if ($GracePeriod && $UploaderID !== $FillerID && !check_perms('site_moderate_requests')) { - $Err[] = "There is a one hour grace period for new uploads to allow the uploader ($FillerUsername) to fill the request."; + $Err[] = "There is a one hour grace period for new uploads to allow the uploader ($FillerUsername) to fill the request."; } $DB->prepared_query(' - SELECT - Title, - UserID, - TorrentID, - CategoryID, - ReleaseType, - CatalogueNumber, - BitrateList, - FormatList, - MediaList, - LogCue, - Checksum - FROM requests - WHERE ID = ?', $RequestID); + SELECT + Title, + UserID, + TorrentID, + CategoryID, + ReleaseType, + CatalogueNumber, + BitrateList, + FormatList, + MediaList, + LogCue, + Checksum + FROM requests + WHERE ID = ?', $RequestID); list($Title, $RequesterID, $OldTorrentID, $RequestCategoryID, $RequestReleaseType, $RequestCatalogueNumber, $BitrateList, $FormatList, $MediaList, $LogCue, $Checksum) - = $DB->next_record(); + = $DB->next_record(); if (!empty($OldTorrentID)) { - $Err[] = 'This request has already been filled.'; + $Err[] = 'This request has already been filled.'; } if ($RequestCategoryID !== '0' && $TorrentCategoryID !== $RequestCategoryID) { - $Err[] = 'This torrent is of a different category than the request. If the request is actually miscategorized, please contact staff.'; + $Err[] = 'This torrent is of a different category than the request. If the request is actually miscategorized, please contact staff.'; } $CategoryName = $CategoriesV2[$RequestCategoryID - 1]; if ($Format === 'FLAC' && $LogCue && $Media === 'CD') { - if (strpos($LogCue, 'Log') !== false && !$HasLogDB) { - $Err[] = 'This request requires a log.'; - } + if (strpos($LogCue, 'Log') !== false && !$HasLogDB) { + $Err[] = 'This request requires a log.'; + } - if (strpos($LogCue, '%') !== false) { - preg_match('/\d+/', $LogCue, $Matches); - if ((int)$LogScore < (int)$Matches[0]) { - $Err[] = 'This torrent\'s log score is too low.'; - } - } + if (strpos($LogCue, '%') !== false) { + preg_match('/\d+/', $LogCue, $Matches); + if ((int)$LogScore < (int)$Matches[0]) { + $Err[] = 'This torrent\'s log score is too low.'; + } + } } if ($Checksum && !$LogChecksum) { - $Err[] = 'The ripping log for this torrent does not have a valid checksum.'; + $Err[] = 'The ripping log for this torrent does not have a valid checksum.'; } if ($BitrateList === 'Other') { - if (in_array($Bitrate, ['24bit Lossless', 'Lossless', 'V0 (VBR)', 'V1 (VBR)', 'V2 (VBR)', 'APS (VBR)', 'APX (VBR)', '256', '320'])) { - $Err[] = "$Bitrate is not an allowed bitrate for this request."; - } + if (in_array($Bitrate, ['24bit Lossless', 'Lossless', 'V0 (VBR)', 'V1 (VBR)', 'V2 (VBR)', 'APS (VBR)', 'APX (VBR)', '256', '320'])) { + $Err[] = "$Bitrate is not an allowed bitrate for this request."; + } } elseif ($BitrateList && $BitrateList != 'Any' && !Misc::search_joined_string($BitrateList, $Bitrate)) { - $Err[] = "$Bitrate is not an allowed bitrate for this request."; + $Err[] = "$Bitrate is not an allowed bitrate for this request."; } if ($FormatList && $FormatList != 'Any' && !Misc::search_joined_string($FormatList, $Format)) { - $Err[] = "$Format is not an allowed format for this request."; + $Err[] = "$Format is not an allowed format for this request."; } if ($MediaList && $MediaList != 'Any' && !Misc::search_joined_string($MediaList, $Media)) { - $Err[] = "$Media is not a permitted media for this request."; + $Err[] = "$Media is not a permitted media for this request."; } if (count($Err)) { - error(implode('<br />', $Err)); + error(implode('<br />', $Err)); } //We're all good! Fill! $DB->prepared_query(' - UPDATE requests - SET FillerID = ?, - TorrentID = ?, - TimeFilled = now() - WHERE ID = ?', - $FillerID, $TorrentID, $RequestID); + UPDATE requests + SET FillerID = ?, + TorrentID = ?, + TimeFilled = now() + WHERE ID = ?', + $FillerID, $TorrentID, $RequestID); $ArtistForm = Requests::get_artists($RequestID); $ArtistName = Artists::display_artists($ArtistForm, false, true); $FullName = $ArtistName.$Title; $DB->prepared_query(' - SELECT UserID - FROM requests_votes - WHERE RequestID = ?', $RequestID); + SELECT UserID + FROM requests_votes + WHERE RequestID = ?', $RequestID); $UserIDs = $DB->to_array(); foreach ($UserIDs as $User) { - list($VoterID) = $User; - Misc::send_pm($VoterID, 0, "The request \"$FullName\" has been filled", 'One of your requests — [url='.site_url()."requests.php?action=view&id=$RequestID]$FullName".'[/url] — has been filled. You can view it here: [url]'.site_url()."torrents.php?torrentid=$TorrentID".'[/url]'); + list($VoterID) = $User; + Misc::send_pm($VoterID, 0, "The request \"$FullName\" has been filled", 'One of your requests — [url='.site_url()."requests.php?action=view&id=$RequestID]$FullName".'[/url] — has been filled. You can view it here: [url]'.site_url()."torrents.php?torrentid=$TorrentID".'[/url]'); } $RequestVotes = Requests::get_votes_array($RequestID); @@ -165,23 +165,23 @@ // Give bounty $DB->prepared_query(' - UPDATE users_main - SET Uploaded = Uploaded + ? - WHERE ID = ?', $RequestVotes['TotalBounty'], $FillerID); + UPDATE users_leech_stats + SET Uploaded = Uploaded + ? + WHERE UserID = ?', $RequestVotes['TotalBounty'], $FillerID); $Cache->delete_value("user_stats_$FillerID"); $Cache->delete_value("request_$RequestID"); if ($GroupID) { - $Cache->delete_value("requests_group_$GroupID"); + $Cache->delete_value("requests_group_$GroupID"); } $DB->prepared_query(' - SELECT ArtistID - FROM requests_artists - WHERE RequestID = ?', $RequestID); + SELECT ArtistID + FROM requests_artists + WHERE RequestID = ?', $RequestID); $ArtistIDs = $DB->to_array(); foreach ($ArtistIDs as $ArtistID) { - $Cache->delete_value("artists_requests_$ArtistID"); + $Cache->delete_value("artists_requests_$ArtistID"); } Requests::update_sphinx_requests($RequestID); diff --git a/sections/requests/take_new_edit.php b/sections/requests/take_new_edit.php index eb8522855..5806b042e 100644 --- a/sections/requests/take_new_edit.php +++ b/sections/requests/take_new_edit.php @@ -1,191 +1,191 @@ -<? +<?php //******************************************************************************// //----------------- Take request -----------------------------------------------// authorize(); if ($_POST['action'] !== 'takenew' && $_POST['action'] !== 'takeedit') { - error(0); + error(0); } $NewRequest = ($_POST['action'] === 'takenew'); if (!$NewRequest) { - $ReturnEdit = true; + $ReturnEdit = true; } if ($NewRequest) { - if (!check_perms('site_submit_requests') || $LoggedUser['BytesUploaded'] < 250 * 1024 * 1024) { - error(403); - } + if (!check_perms('site_submit_requests') || $LoggedUser['BytesUploaded'] < 250 * 1024 * 1024) { + error(403); + } } else { - $RequestID = $_POST['requestid']; - if (!intval($RequestID)) { - error(0); - } - - $Request = Requests::get_request($RequestID); - if ($Request === false) { - error(404); - } - $VoteArray = Requests::get_votes_array($RequestID); - $VoteCount = count($VoteArray['Voters']); - $IsFilled = !empty($Request['TorrentID']); - $CategoryName = $CategoriesV2[$Request['CategoryID'] - 1]; - $CanEdit = ((!$IsFilled && $LoggedUser['ID'] === $Request['UserID'] && $VoteCount < 2) || check_perms('site_moderate_requests')); - - if (!$CanEdit) { - error(403); - } + $RequestID = $_POST['requestid']; + if (!intval($RequestID)) { + error(0); + } + + $Request = Requests::get_request($RequestID); + if ($Request === false) { + error(404); + } + $VoteArray = Requests::get_votes_array($RequestID); + $VoteCount = count($VoteArray['Voters']); + $IsFilled = !empty($Request['TorrentID']); + $CategoryName = $CategoriesV2[$Request['CategoryID'] - 1]; + $CanEdit = ((!$IsFilled && $LoggedUser['ID'] === $Request['UserID'] && $VoteCount < 2) || check_perms('site_moderate_requests')); + + if (!$CanEdit) { + error(403); + } } // Validate if (empty($_POST['type'])) { - error(0); + error(0); } $CategoryName = $_POST['type']; $CategoryID = (array_search($CategoryName, $CategoriesV2) + 1); if (empty($CategoryID)) { - error(0); + error(0); } if (empty($_POST['title'])) { - $Err = 'You forgot to enter the title!'; + $Err = 'You forgot to enter the title!'; } else { - $Title = trim($_POST['title']); + $Title = trim($_POST['title']); } if (empty($_POST['tags'])) { - $Err = 'You forgot to enter any tags!'; + $Err = 'You forgot to enter any tags!'; } else { - $Tags = trim($_POST['tags']); + $Tags = trim($_POST['tags']); } if ($NewRequest) { - if (empty($_POST['amount'])) { - $Err = 'You forgot to enter any bounty!'; - } else { - $Bounty = trim($_POST['amount']); - if (!intval($Bounty)) { - $Err = 'Your entered bounty is not a number'; - } elseif ($Bounty < 100 * 1024 * 1024) { - $Err = 'Minimum bounty is 100 MB.'; - } - $Bytes = $Bounty; //From MB to B - } + if (empty($_POST['amount'])) { + $Err = 'You forgot to enter any bounty!'; + } else { + $Bounty = trim($_POST['amount']); + if (!intval($Bounty)) { + $Err = 'Your entered bounty is not a number'; + } elseif ($Bounty < 100 * 1024 * 1024) { + $Err = 'Minimum bounty is 100 MB.'; + } + $Bytes = $Bounty; //From MB to B + } } if (empty($_POST['image'])) { - $Image = null; + $Image = null; } else { - ImageTools::blacklisted($_POST['image']); - if (preg_match('/'.IMAGE_REGEX.'/', trim($_POST['image'])) > 0) { - $Image = trim($_POST['image']); - } else { - $Err = display_str($_POST['image']).' does not appear to be a valid link to an image.'; - } + ImageTools::blacklisted($_POST['image']); + if (preg_match('/'.IMAGE_REGEX.'/', trim($_POST['image'])) > 0) { + $Image = trim($_POST['image']); + } else { + $Err = display_str($_POST['image']).' does not appear to be a valid link to an image.'; + } } if (empty($_POST['description'])) { - $Err = 'You forgot to enter a description.'; + $Err = 'You forgot to enter a description.'; } else { - $Description = trim($_POST['description']); + $Description = trim($_POST['description']); } if (empty($_POST['artists'])) { - $Err = 'You did not enter any artists.'; + $Err = 'You did not enter any artists.'; } else { - $Artists = $_POST['artists']; - $Importance = $_POST['importance']; + $Artists = $_POST['artists']; + $Importance = $_POST['importance']; } if (!intval($_POST['releasetype']) || !array_key_exists($_POST['releasetype'], $ReleaseTypes)) { - $Err = 'Please pick a release type'; + $Err = 'Please pick a release type'; } $ReleaseType = $_POST['releasetype']; if (empty($_POST['all_formats']) && count($_POST['formats']) !== count($Formats)) { - $FormatArray = $_POST['formats']; - if (count($FormatArray) < 1) { - $Err = 'You must require at least one format'; - } + $FormatArray = $_POST['formats']; + if (count($FormatArray) < 1) { + $Err = 'You must require at least one format'; + } } else { - $AllFormats = true; + $AllFormats = true; } if (empty($_POST['all_bitrates']) && count($_POST['bitrates']) !== count($Bitrates)) { - $BitrateArray = $_POST['bitrates']; - if (count($BitrateArray) < 1) { - $Err = 'You must require at least one bitrate'; - } + $BitrateArray = $_POST['bitrates']; + if (count($BitrateArray) < 1) { + $Err = 'You must require at least one bitrate'; + } } else { - $AllBitrates = true; + $AllBitrates = true; } if (empty($_POST['all_media']) && count($_POST['media']) !== count($Media)) { - $MediaArray = $_POST['media']; - if (count($MediaArray) < 1) { - $Err = 'You must require at least one medium.'; - } + $MediaArray = $_POST['media']; + if (count($MediaArray) < 1) { + $Err = 'You must require at least one medium.'; + } } else { - $AllMedia = true; + $AllMedia = true; } //$Bitrates[1] = FLAC if (!empty($FormatArray) && in_array(array_search('FLAC', $Formats), $FormatArray)) { - $NeedLog = empty($_POST['needlog']) ? false : true; - if ($NeedLog) { - if ($_POST['minlogscore']) { - $MinLogScore = trim($_POST['minlogscore']); - } else { - $MinLogScore = 0; - } - if (!intval($MinLogScore)) { - $Err = 'You have entered a minimum log score that is not a number.'; - } - } - $NeedCue = empty($_POST['needcue']) ? false : true; - //FLAC was picked, require either Lossless or 24 bit Lossless - if (!$AllBitrates && !in_array(array_search('Lossless', $Bitrates), $BitrateArray) && !in_array(array_search('24bit Lossless', $Bitrates), $BitrateArray)) { - $Err = 'You selected FLAC as a format but no possible bitrate to fill it (Lossless or 24bit Lossless)'; - } - $NeedChecksum = empty($_POST['needcksum']) ? false : true; - - if (($NeedCue || $NeedLog || $NeedChecksum)) { - if (empty($_POST['all_media']) && !(in_array('0', $MediaArray))) { - $Err = 'Only CD is allowed as media for FLAC + log/cue requests.'; - } - } + $NeedLog = empty($_POST['needlog']) ? false : true; + if ($NeedLog) { + if ($_POST['minlogscore']) { + $MinLogScore = trim($_POST['minlogscore']); + } else { + $MinLogScore = 0; + } + if (!intval($MinLogScore)) { + $Err = 'You have entered a minimum log score that is not a number.'; + } + } + $NeedCue = empty($_POST['needcue']) ? false : true; + //FLAC was picked, require either Lossless or 24 bit Lossless + if (!$AllBitrates && !in_array(array_search('Lossless', $Bitrates), $BitrateArray) && !in_array(array_search('24bit Lossless', $Bitrates), $BitrateArray)) { + $Err = 'You selected FLAC as a format but no possible bitrate to fill it (Lossless or 24bit Lossless)'; + } + $NeedChecksum = empty($_POST['needcksum']) ? false : true; + + if (($NeedCue || $NeedLog || $NeedChecksum)) { + if (empty($_POST['all_media']) && !(in_array('0', $MediaArray))) { + $Err = 'Only CD is allowed as media for FLAC + log/cue requests.'; + } + } } else { - $NeedLog = false; - $NeedCue = false; - $NeedChecksum = false; - $MinLogScore = false; + $NeedLog = false; + $NeedCue = false; + $NeedChecksum = false; + $MinLogScore = false; } // GroupID if (!empty($_POST['groupid'])) { - $GroupID = trim($_POST['groupid']); - if (preg_match('/^'.TORRENT_GROUP_REGEX.'/i', $GroupID, $Matches)) { - $GroupID = $Matches[4]; - } - if (intval($GroupID)) { - $DB->prepared_query(' - SELECT 1 - FROM torrents_group - WHERE ID = ? AND CategoryID = ?', - $GroupID, 1); - if (!$DB->has_results()) { - $Err = 'The torrent group, if entered, must correspond to a music torrent group on the site.'; - } - } else { - $Err = 'The torrent group, if entered, must correspond to a music torrent group on the site.'; - } + $GroupID = trim($_POST['groupid']); + if (preg_match('/^'.TORRENT_GROUP_REGEX.'/i', $GroupID, $Matches)) { + $GroupID = $Matches[4]; + } + if (intval($GroupID)) { + $DB->prepared_query(' + SELECT 1 + FROM torrents_group + WHERE ID = ? AND CategoryID = ?', + $GroupID, 1); + if (!$DB->has_results()) { + $Err = 'The torrent group, if entered, must correspond to a music torrent group on the site.'; + } + } else { + $Err = 'The torrent group, if entered, must correspond to a music torrent group on the site.'; + } } elseif ($_POST['groupid'] === '0') { - $GroupID = 0; + $GroupID = 0; } //Not required @@ -194,12 +194,12 @@ $RecordLabel = empty($_POST['recordlabel']) ? '' : trim($_POST['recordlabel']); if (empty($_POST['year'])) { - $Err = 'You forgot to enter the year!'; + $Err = 'You forgot to enter the year!'; } else { - $Year = trim($_POST['year']); - if (!intval($Year)) { - $Err = 'Your entered year is not a number.'; - } + $Year = trim($_POST['year']); + if (!intval($Year)) { + $Err = 'Your entered year is not a number.'; + } } //Apply OCLC to all types @@ -207,143 +207,143 @@ //For refilling on error if ($CategoryName === 'Music') { - $MainArtistCount = 0; - $ArtistNames = []; - $ArtistForm = [ - 1 => [], - 2 => [], - 3 => [] - ]; - for ($i = 0, $il = count($Artists); $i < $il; $i++) { - if (trim($Artists[$i]) !== '') { - if (!in_array($Artists[$i], $ArtistNames)) { - $ArtistForm[$Importance[$i]][] = ['name' => trim($Artists[$i])]; - if (in_array($Importance[$i], [1, 4, 5, 6])) { - $MainArtistCount++; - } - $ArtistNames[] = trim($Artists[$i]); - } - } - } - if ($MainArtistCount < 1) { - $Err = 'Please enter at least one main artist, conductor, composer, or DJ.'; - } - if (!isset($ArtistNames[0])) { - unset($ArtistForm); - } + $MainArtistCount = 0; + $ArtistNames = []; + $ArtistForm = [ + 1 => [], + 2 => [], + 3 => [] + ]; + for ($i = 0, $il = count($Artists); $i < $il; $i++) { + if (trim($Artists[$i]) !== '') { + if (!in_array($Artists[$i], $ArtistNames)) { + $ArtistForm[$Importance[$i]][] = ['name' => trim($Artists[$i])]; + if (in_array($Importance[$i], [1, 4, 5, 6])) { + $MainArtistCount++; + } + $ArtistNames[] = trim($Artists[$i]); + } + } + } + if ($MainArtistCount < 1) { + $Err = 'Please enter at least one main artist, conductor, composer, or DJ.'; + } + if (!isset($ArtistNames[0])) { + unset($ArtistForm); + } } if (!empty($Err)) { - error($Err); - $Div = $_POST['unit'] === 'mb' ? 1024 * 1024 : 1024 * 1024 * 1024; - $Bounty /= $Div; - include(SERVER_ROOT.'/sections/requests/new_edit.php'); - die(); + error($Err); + $Div = $_POST['unit'] === 'mb' ? 1024 * 1024 : 1024 * 1024 * 1024; + $Bounty /= $Div; + include(SERVER_ROOT.'/sections/requests/new_edit.php'); + die(); } //Databasify the input if (empty($AllBitrates)) { - foreach ($BitrateArray as $Index => $MasterIndex) { - if (array_key_exists($Index, $Bitrates)) { - $BitrateArray[$Index] = $Bitrates[$MasterIndex]; - } else { - //Hax - error(0); - } - } - $BitrateList = implode('|', $BitrateArray); + foreach ($BitrateArray as $Index => $MasterIndex) { + if (array_key_exists($Index, $Bitrates)) { + $BitrateArray[$Index] = $Bitrates[$MasterIndex]; + } else { + //Hax + error(0); + } + } + $BitrateList = implode('|', $BitrateArray); } else { - $BitrateList = 'Any'; + $BitrateList = 'Any'; } if (empty($AllFormats)) { - foreach ($FormatArray as $Index => $MasterIndex) { - if (array_key_exists($Index, $Formats)) { - $FormatArray[$Index] = $Formats[$MasterIndex]; - } else { - //Hax - error(0); - } - } - $FormatList = implode('|', $FormatArray); + foreach ($FormatArray as $Index => $MasterIndex) { + if (array_key_exists($Index, $Formats)) { + $FormatArray[$Index] = $Formats[$MasterIndex]; + } else { + //Hax + error(0); + } + } + $FormatList = implode('|', $FormatArray); } else { - $FormatList = 'Any'; + $FormatList = 'Any'; } if (empty($AllMedia)) { - foreach ($MediaArray as $Index => $MasterIndex) { - if (array_key_exists($Index, $Media)) { - $MediaArray[$Index] = $Media[$MasterIndex]; - } else { - //Hax - error(0); - } - } - $MediaList = implode('|', $MediaArray); + foreach ($MediaArray as $Index => $MasterIndex) { + if (array_key_exists($Index, $Media)) { + $MediaArray[$Index] = $Media[$MasterIndex]; + } else { + //Hax + error(0); + } + } + $MediaList = implode('|', $MediaArray); } else { - $MediaList = 'Any'; + $MediaList = 'Any'; } $LogCue = ''; if ($NeedLog) { - $LogCue .= 'Log'; - if ($MinLogScore > 0) { - if ($MinLogScore >= 100) { - $LogCue .= ' (100%)'; - } else { - $LogCue .= ' (>= '.$MinLogScore.'%)'; - } - } + $LogCue .= 'Log'; + if ($MinLogScore > 0) { + if ($MinLogScore >= 100) { + $LogCue .= ' (100%)'; + } else { + $LogCue .= ' (>= '.$MinLogScore.'%)'; + } + } } if ($NeedCue) { - if ($LogCue !== '') { - $LogCue .= ' + Cue'; - } else { - $LogCue = 'Cue'; - } + if ($LogCue !== '') { + $LogCue .= ' + Cue'; + } else { + $LogCue = 'Cue'; + } } //Query time! if ($NewRequest) { - $DB->prepared_query(' - INSERT INTO requests ( - TimeAdded, LastVote, Visible, UserID, CategoryID, Title, Year, Image, Description, RecordLabel, - CatalogueNumber, ReleaseType, BitrateList, FormatList, MediaList, LogCue, Checksum, GroupID, OCLC) - VALUES ( - now(), now(), 1, ?, ?, ?, ?, ?, ?, ?, - ?, ?, ?, ?, ?, ?, ?, ?, ?)', - $LoggedUser['ID'], $CategoryID, $Title, $Year, $Image, $Description, $RecordLabel, - $CatalogueNumber, $ReleaseType, $BitrateList, $FormatList, $MediaList, $LogCue, $NeedChecksum, $GroupID, $OCLC); - - $RequestID = $DB->inserted_id(); + $DB->prepared_query(' + INSERT INTO requests ( + TimeAdded, LastVote, Visible, UserID, CategoryID, Title, Year, Image, Description, RecordLabel, + CatalogueNumber, ReleaseType, BitrateList, FormatList, MediaList, LogCue, Checksum, GroupID, OCLC) + VALUES ( + now(), now(), 1, ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, ?, ?, ?, ?)', + $LoggedUser['ID'], $CategoryID, $Title, $Year, $Image, $Description, $RecordLabel, + $CatalogueNumber, $ReleaseType, $BitrateList, $FormatList, $MediaList, $LogCue, $NeedChecksum, $GroupID, $OCLC); + + $RequestID = $DB->inserted_id(); } else { - $DB->prepared_query(' - UPDATE requests - SET CategoryID = ?, Title = ?, Year = ?, Image = ?, Description = ?, CatalogueNumber = ?, RecordLabel = ?, - ReleaseType = ?, BitrateList = ?, FormatList = ?, MediaList = ?, LogCue = ?, Checksum = ?, GroupID = ?, OCLC = ? - WHERE ID = ?', - $CategoryID, $Title, $Year, $Image, $Description, $CatalogueNumber, $RecordLabel, - $ReleaseType, $BitrateList, $FormatList, $MediaList, $LogCue, $NeedChecksum, $GroupID, $OCLC, - $RequestID); - - // We need to be able to delete artists / tags - $DB->prepared_query(' - SELECT ArtistID - FROM requests_artists - WHERE RequestID = ?', $RequestID); - $RequestArtists = $DB->to_array(); - foreach ($RequestArtists as $RequestArtist) { - $Cache->delete_value("artists_requests_$RequestArtist"); - } - $DB->prepared_query(' - DELETE FROM requests_artists - WHERE RequestID = ?', $RequestID); - $Cache->delete_value("request_artists_$RequestID"); + $DB->prepared_query(' + UPDATE requests + SET CategoryID = ?, Title = ?, Year = ?, Image = ?, Description = ?, CatalogueNumber = ?, RecordLabel = ?, + ReleaseType = ?, BitrateList = ?, FormatList = ?, MediaList = ?, LogCue = ?, Checksum = ?, GroupID = ?, OCLC = ? + WHERE ID = ?', + $CategoryID, $Title, $Year, $Image, $Description, $CatalogueNumber, $RecordLabel, + $ReleaseType, $BitrateList, $FormatList, $MediaList, $LogCue, $NeedChecksum, $GroupID, $OCLC, + $RequestID); + + // We need to be able to delete artists / tags + $DB->prepared_query(' + SELECT ArtistID + FROM requests_artists + WHERE RequestID = ?', $RequestID); + $RequestArtists = $DB->to_array(); + foreach ($RequestArtists as $RequestArtist) { + $Cache->delete_value("artists_requests_$RequestArtist"); + } + $DB->prepared_query(' + DELETE FROM requests_artists + WHERE RequestID = ?', $RequestID); + $Cache->delete_value("request_artists_$RequestID"); } if ($GroupID) { - $Cache->delete_value("requests_group_$GroupID"); + $Cache->delete_value("requests_group_$GroupID"); } /* @@ -355,110 +355,110 @@ */ foreach ($ArtistForm as $Importance => $Artists) { - foreach ($Artists as $Num => $Artist) { - //1. See if each artist given already exists and if it does, grab the ID. - $DB->prepared_query(' - SELECT - ArtistID, - AliasID, - Name, - Redirect - FROM artists_alias - WHERE Name = ?', $Artist['name']); - - while (list($ArtistID, $AliasID, $AliasName, $Redirect) = $DB->next_record(MYSQLI_NUM, false)) { - if (!strcasecmp($Artist['name'], $AliasName)) { - if ($Redirect) { - $AliasID = $Redirect; - } - $ArtistForm[$Importance][$Num] = array('id' => $ArtistID, 'aliasid' => $AliasID, 'name' => $AliasName); - break; - } - } - if (!$ArtistID) { - //2. For each artist that didn't exist, create an artist. - $DB->prepared_query(' - INSERT INTO artists_group (Name) - VALUES (?)', $Artist['name']); - $ArtistID = $DB->inserted_id(); - - $Cache->increment('stats_artist_count'); - - $DB->prepared_query(' - INSERT INTO artists_alias (ArtistID, Name) - VALUES (?, ?)', $ArtistID, $Artist['name']); - $AliasID = $DB->inserted_id(); - - $ArtistForm[$Importance][$Num] = ['id' => $ArtistID, 'aliasid' => $AliasID, 'name' => $Artist['name']]; - } - } + foreach ($Artists as $Num => $Artist) { + //1. See if each artist given already exists and if it does, grab the ID. + $DB->prepared_query(' + SELECT + ArtistID, + AliasID, + Name, + Redirect + FROM artists_alias + WHERE Name = ?', $Artist['name']); + + while (list($ArtistID, $AliasID, $AliasName, $Redirect) = $DB->next_record(MYSQLI_NUM, false)) { + if (!strcasecmp($Artist['name'], $AliasName)) { + if ($Redirect) { + $AliasID = $Redirect; + } + $ArtistForm[$Importance][$Num] = array('id' => $ArtistID, 'aliasid' => $AliasID, 'name' => $AliasName); + break; + } + } + if (!$ArtistID) { + //2. For each artist that didn't exist, create an artist. + $DB->prepared_query(' + INSERT INTO artists_group (Name) + VALUES (?)', $Artist['name']); + $ArtistID = $DB->inserted_id(); + + $Cache->increment('stats_artist_count'); + + $DB->prepared_query(' + INSERT INTO artists_alias (ArtistID, Name) + VALUES (?, ?)', $ArtistID, $Artist['name']); + $AliasID = $DB->inserted_id(); + + $ArtistForm[$Importance][$Num] = ['id' => $ArtistID, 'aliasid' => $AliasID, 'name' => $Artist['name']]; + } + } } //3. Create a row in the requests_artists table for each artist, based on the ID. foreach ($ArtistForm as $Importance => $Artists) { - foreach ($Artists as $Num => $Artist) { - $DB->prepared_query(' - INSERT IGNORE INTO requests_artists - (RequestID, ArtistID, AliasID, Importance) - VALUES - (?, ?, ?, ?)', $RequestID, $Artist['id'], $Artist['aliasid'], $Importance); - $Cache->increment('stats_album_count'); - $Cache->delete_value('artists_requests_'.$Artist['id']); - } + foreach ($Artists as $Num => $Artist) { + $DB->prepared_query(' + INSERT IGNORE INTO requests_artists + (RequestID, ArtistID, AliasID, Importance) + VALUES + (?, ?, ?, ?)', $RequestID, $Artist['id'], $Artist['aliasid'], $Importance); + $Cache->increment('stats_album_count'); + $Cache->delete_value('artists_requests_'.$Artist['id']); + } } //Tags if (!$NewRequest) { - $DB->prepared_query(' - DELETE FROM requests_tags - WHERE RequestID = ?', $RequestID); + $DB->prepared_query(' + DELETE FROM requests_tags + WHERE RequestID = ?', $RequestID); } $Tags = array_unique(explode(',', $Tags)); foreach ($Tags as $Index => $Tag) { - $Tag = Misc::sanitize_tag($Tag); - $Tag = Misc::get_alias_tag($Tag); - $Tags[$Index] = $Tag; //For announce - $DB->prepared_query(' - INSERT INTO tags - (Name, UserID) - VALUES - (?, ?) - ON DUPLICATE KEY UPDATE - Uses = Uses + 1', - $Tag, $LoggedUser['ID']); - - $TagID = $DB->inserted_id(); - - $DB->prepared_query(' - INSERT IGNORE INTO requests_tags - (TagID, RequestID) - VALUES - (?, ?)', - $TagID, $RequestID); + $Tag = Misc::sanitize_tag($Tag); + $Tag = Misc::get_alias_tag($Tag); + $Tags[$Index] = $Tag; //For announce + $DB->prepared_query(' + INSERT INTO tags + (Name, UserID) + VALUES + (?, ?) + ON DUPLICATE KEY UPDATE + Uses = Uses + 1', + $Tag, $LoggedUser['ID']); + + $TagID = $DB->inserted_id(); + + $DB->prepared_query(' + INSERT IGNORE INTO requests_tags + (TagID, RequestID) + VALUES + (?, ?)', + $TagID, $RequestID); } if ($NewRequest) { - //Remove the bounty and create the vote - $DB->prepared_query(' - INSERT INTO requests_votes - (RequestID, UserID, Bounty) - VALUES - (?, ?, ?)', - $RequestID, $LoggedUser['ID'], $Bytes * (1 - $RequestTax)); - - $DB->prepared_query(' - UPDATE users_main - SET Uploaded = (Uploaded - ?) - WHERE ID = ?', - $Bytes, $LoggedUser['ID']); - $Cache->delete_value('user_stats_'.$LoggedUser['ID']); - - $Announce = "\"$Title\" - ".Artists::display_artists($ArtistForm, false, false).' '.site_url()."requests.php?action=view&id=$RequestID - ".implode(' ', $Tags); - send_irc("PRIVMSG #requests :{$Announce}"); + //Remove the bounty and create the vote + $DB->prepared_query(' + INSERT INTO requests_votes + (RequestID, UserID, Bounty) + VALUES + (?, ?, ?)', + $RequestID, $LoggedUser['ID'], $Bytes * (1 - $RequestTax)); + + $DB->prepared_query(' + UPDATE users_leech_stats + SET Uploaded = (Uploaded - ?) + WHERE UserID = ?', + $Bytes, $LoggedUser['ID']); + $Cache->delete_value('user_stats_'.$LoggedUser['ID']); + + $Announce = "\"$Title\" - ".Artists::display_artists($ArtistForm, false, false).' '.site_url()."requests.php?action=view&id=$RequestID - ".implode(' ', $Tags); + send_irc("PRIVMSG #requests :{$Announce}"); } else { - $Cache->delete_value("request_$RequestID"); - $Cache->delete_value("request_artists_$RequestID"); + $Cache->delete_value("request_$RequestID"); + $Cache->delete_value("request_artists_$RequestID"); } Requests::update_sphinx_requests($RequestID); diff --git a/sections/requests/take_unfill.php b/sections/requests/take_unfill.php index 64af8c6ef..83c67d1fe 100644 --- a/sections/requests/take_unfill.php +++ b/sections/requests/take_unfill.php @@ -1,4 +1,4 @@ -<? +<?php //******************************************************************************// //--------------- Take unfill request ------------------------------------------// @@ -6,34 +6,34 @@ $RequestID = $_POST['id']; if (!intval($RequestID)) { - error(0); + error(0); } $DB->prepared_query(' - SELECT - r.CategoryID, - r.UserID, - r.FillerID, - r.Title, - u.Uploaded, - r.GroupID - FROM requests AS r - LEFT JOIN users_main AS u ON (u.ID = FillerID) - WHERE r.ID = ?', $RequestID); + SELECT + r.CategoryID, + r.UserID, + r.FillerID, + r.Title, + uls.Uploaded, + r.GroupID + FROM requests AS r + LEFT JOIN users_leech_stats AS uls ON (uls.UserID = r.FillerID) + WHERE r.ID = ?', $RequestID); list($CategoryID, $UserID, $FillerID, $Title, $Uploaded, $GroupID) = $DB->next_record(); if (((intval($LoggedUser['ID']) !== $UserID && intval($LoggedUser['ID']) !== $FillerID) && !check_perms('site_moderate_requests')) || $FillerID === '0') { - error(403); + error(403); } // Unfill $DB->prepared_query(' - UPDATE requests - SET TorrentID = 0, - FillerID = 0, - TimeFilled = null, - Visible = 1 - WHERE ID = ?', $RequestID); + UPDATE requests + SET TorrentID = 0, + FillerID = 0, + TimeFilled = null, + Visible = 1 + WHERE ID = ?', $RequestID); $CategoryName = $CategoriesV2[$CategoryID - 1]; @@ -44,17 +44,17 @@ $RequestVotes = Requests::get_votes_array($RequestID); if ($RequestVotes['TotalBounty'] > $Uploaded) { - // If we can't take it all out of upload, zero that out and add whatever is left as download. - $DB->prepared_query(' - UPDATE users_main - SET Uploaded = 0, Downloaded = Downloaded + ? - WHERE ID = ?', - $RequestVotes['TotalBounty'] - $Uploaded, $FillerID); + // If we can't take it all out of upload, zero that out and add whatever is left as download. + $DB->prepared_query(' + UPDATE users_leech_stats + SET Uploaded = 0, Downloaded = Downloaded + ? + WHERE UserID = ?', + $RequestVotes['TotalBounty'] - $Uploaded, $FillerID); } else { - $DB->prepared_query(' - UPDATE users_main - SET Uploaded = Uploaded - ? - WHERE ID = ?', $RequestVotes['TotalBounty'], $FillerID); + $DB->prepared_query(' + UPDATE users_leech_stats + SET Uploaded = Uploaded - ? + WHERE UserID = ?', $RequestVotes['TotalBounty'], $FillerID); } Misc::send_pm($FillerID, 0, 'A request you filled has been unfilled', "The request \"[url=".site_url()."requests.php?action=view&id=$RequestID]$FullName"."[/url]\" was unfilled by [url=".site_url().'user.php?id='.$LoggedUser['ID'].']'.$LoggedUser['Username'].'[/url] for the reason: [quote]'.$_POST['reason']."[/quote]\nIf you feel like this request was unjustly unfilled, please [url=".site_url()."reports.php?action=report&type=request&id=$RequestID]report the request[/url] and explain why this request should not have been unfilled."); @@ -62,7 +62,7 @@ $Cache->delete_value("user_stats_$FillerID"); if ($UserID !== $LoggedUser['ID']) { - Misc::send_pm($UserID, 0, 'A request you created has been unfilled', "The request \"[url=".site_url()."requests.php?action=view&id=$RequestID]$FullName"."[/url]\" was unfilled by [url=".site_url().'user.php?id='.$LoggedUser['ID'].']'.$LoggedUser['Username']."[/url] for the reason: [quote]".$_POST['reason'].'[/quote]'); + Misc::send_pm($UserID, 0, 'A request you created has been unfilled', "The request \"[url=".site_url()."requests.php?action=view&id=$RequestID]$FullName"."[/url]\" was unfilled by [url=".site_url().'user.php?id='.$LoggedUser['ID'].']'.$LoggedUser['Username']."[/url] for the reason: [quote]".$_POST['reason'].'[/quote]'); } Misc::write_log("Request $RequestID ($FullName), with a ".Format::get_size($RequestVotes['TotalBounty']).' bounty, was unfilled by user '.$LoggedUser['ID'].' ('.$LoggedUser['Username'].') for the reason: '.$_POST['reason']); @@ -70,23 +70,23 @@ $Cache->delete_value("request_$RequestID"); $Cache->delete_value("request_artists_$RequestID"); if ($GroupID) { - $Cache->delete_value("requests_group_$GroupID"); + $Cache->delete_value("requests_group_$GroupID"); } Requests::update_sphinx_requests($RequestID); if (!empty($ArtistForm)) { - foreach ($ArtistForm as $ArtistType) { - foreach ($ArtistType as $Artist) { - $Cache->delete_value('artists_requests_'.$Artist['id']); - } - } + foreach ($ArtistForm as $ArtistType) { + foreach ($ArtistType as $Artist) { + $Cache->delete_value('artists_requests_'.$Artist['id']); + } + } } $SphQL = new SphinxqlQuery(); $SphQL->raw_query(" - UPDATE requests, requests_delta - SET torrentid = 0, fillerid = 0 - WHERE id = $RequestID", false); + UPDATE requests, requests_delta + SET torrentid = 0, fillerid = 0 + WHERE id = $RequestID", false); header("Location: requests.php?action=view&id=$RequestID"); diff --git a/sections/requests/take_vote.php b/sections/requests/take_vote.php index c1030a181..6f3309fad 100644 --- a/sections/requests/take_vote.php +++ b/sections/requests/take_vote.php @@ -1,77 +1,77 @@ -<? +<?php //******************************************************************************// //--------------- Vote on a request --------------------------------------------// //This page is ajax! if (!check_perms('site_vote')) { - error(403); + error(403); } authorize(); if (empty($_GET['id']) || !intval($_GET['id'])) { - error(0); + error(0); } $RequestID = $_GET['id']; $DB->prepared_query(' - SELECT TorrentID - FROM requests - WHERE ID = ?', $RequestID); + SELECT TorrentID + FROM requests + WHERE ID = ?', $RequestID); if (!$DB->has_results()) { - echo "missing"; - error(0); + echo "missing"; + error(0); } list($FilledTorrentID) = $DB->next_record(); if ($FilledTorrentId > 0) { - echo "filled"; - error(0); + echo "filled"; + error(0); } $Amount = (empty($_GET['amount']) || !intval($_GET['amount']) || $_GET['amount'] < $MinimumVote) - ? $MinimumVote - : $_GET['amount']; + ? $MinimumVote + : $_GET['amount']; $Bounty = $Amount * (1 - $RequestTax); if ($LoggedUser['BytesUploaded'] < $Amount) { - echo 'bankrupt'; - error(0); + echo 'bankrupt'; + error(0); } // Create vote! $DB->prepared_query(' - INSERT INTO requests_votes - (RequestID, UserID, Bounty) - VALUES - (?, ?, ?) - ON DUPLICATE KEY UPDATE Bounty = Bounty + ?', - $RequestID, $LoggedUser['ID'], $Bounty, $Bounty); + INSERT INTO requests_votes + (RequestID, UserID, Bounty) + VALUES + (?, ?, ?) + ON DUPLICATE KEY UPDATE Bounty = Bounty + ?', + $RequestID, $LoggedUser['ID'], $Bounty, $Bounty); $DB->prepared_query(' - UPDATE requests - SET LastVote = NOW() - WHERE ID = ?', $RequestID); + UPDATE requests + SET LastVote = NOW() + WHERE ID = ?', $RequestID); // Subtract amount from user $DB->prepared_query(' - UPDATE users_main - SET Uploaded = Uploaded - ? - WHERE ID = ?', $Amount, $LoggedUser['ID']); + UPDATE users_leech_stats + SET Uploaded = Uploaded - ? + WHERE UserID = ?', $Amount, $LoggedUser['ID']); $Cache->delete_value('user_stats_'.$LoggedUser['ID']); Requests::update_sphinx_requests($RequestID); $DB->prepared_query(' - SELECT UserID - FROM requests_votes - WHERE RequestID = ? - AND UserID != ?', $RequestID, $LoggedUser['ID']); + SELECT UserID + FROM requests_votes + WHERE RequestID = ? + AND UserID != ?', $RequestID, $LoggedUser['ID']); $UserIDs = []; while (list($UserID) = $DB->next_record()) { - $UserIDs[] = $UserID; + $UserIDs[] = $UserID; } NotificationsManager::notify_users($UserIDs, NotificationsManager::REQUESTALERTS, Format::get_size($Amount) . " of bounty has been added to a request you've voted on!", "requests.php?action=view&id=" . $RequestID); @@ -80,9 +80,9 @@ $ArtistForm = Requests::get_artists($RequestID); foreach ($ArtistForm as $Importance) { - foreach ($Importance as $Artist) { - $Cache->delete_value('artists_requests_'.$Artist['id']); - } + foreach ($Importance as $Artist) { + $Cache->delete_value('artists_requests_'.$Artist['id']); + } } echo 'success'; diff --git a/sections/rules/chat.php b/sections/rules/chat.php index 37b18fa88..d1ea7d3b6 100644 --- a/sections/rules/chat.php +++ b/sections/rules/chat.php @@ -1,27 +1,27 @@ -<? +<?php //Include the header View::show_header('Chat Rules'); ?> <div class="thin"> -<? include('jump.php'); ?> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <p>Anything not allowed on the forums is also not allowed on IRC and vice versa. They are separated for convenience only.</p> - </div> - <br /> +<?php include('jump.php'); ?> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <p>Anything not allowed on the forums is also not allowed on IRC and vice versa. They are separated for convenience only.</p> + </div> + <br /> <!-- Forum Rules --> - <h2 id="forums">Forum Rules</h2> - <div class="box pad rule_summary" style="padding: 10px 10px 10px 20px;"> -<? Rules::display_forum_rules() ?> - </div> + <h2 id="forums">Forum Rules</h2> + <div class="box pad rule_summary" style="padding: 10px 10px 10px 20px;"> +<?php Rules::display_forum_rules() ?> + </div> <!-- END Forum Rules --> <!-- IRC Rules --> - <h2 id="irc">IRC Rules</h2> - <div class="box pad rule_summary" style="padding: 10px 10px 10px 20px;"> -<? Rules::display_irc_chat_rules() ?> - </div> + <h2 id="irc">IRC Rules</h2> + <div class="box pad rule_summary" style="padding: 10px 10px 10px 20px;"> +<?php Rules::display_irc_chat_rules() ?> + </div> </div> -<? +<?php View::show_footer(); ?> diff --git a/sections/rules/clients.php b/sections/rules/clients.php index 005787b2e..70d0110f7 100644 --- a/sections/rules/clients.php +++ b/sections/rules/clients.php @@ -1,42 +1,46 @@ -<? +<?php View::show_header('Client Rules'); if (!$WhitelistedClients = $Cache->get_value('whitelisted_clients')) { - $DB->query(' - SELECT vstring, peer_id - FROM xbt_client_whitelist - WHERE vstring NOT LIKE \'//%\' - ORDER BY vstring ASC'); - $WhitelistedClients = $DB->to_array(false, MYSQLI_NUM, false); - $Cache->cache_value('whitelisted_clients', $WhitelistedClients, 604800); + $DB->query(' + SELECT vstring, peer_id + FROM xbt_client_whitelist + WHERE vstring NOT LIKE \'//%\' + ORDER BY vstring ASC'); + $WhitelistedClients = $DB->to_array(false, MYSQLI_NUM, false); + $Cache->cache_value('whitelisted_clients', $WhitelistedClients, 604800); } ?> <div class="thin"> -<? include('jump.php'); ?> - <div class="header"> - <h2 class="general">Client Whitelist</h2> - </div> - <div class="box pad"> - <p>Client rules are how we maintain the integrity of our swarms. This allows us to filter out disruptive and dishonest clients that may hurt the performance of either the tracker or individual peers.</p> - <table cellpadding="5" cellspacing="1" border="0" class="border" width="100%"> - <tr class="colhead"> - <td><strong>Allowed Client</strong></td> - <td style="width: 75px"><strong>Peer ID</strong></td> - <!-- td style="width: 400px;"><strong>Additional Notes</strong></td> --> - </tr> -<? - $Row = 'a'; - foreach ($WhitelistedClients as $Client) { - //list($ClientName, $Notes) = $Client; - list($ClientName, $PeerID) = $Client; - $Row = $Row === 'a' ? 'b' : 'a'; +<?php include('jump.php'); ?> + <div class="header"> + <h2 class="general">Client Whitelist</h2> + </div> + <div class="box pad"> + <p>Client rules are how we maintain the integrity of our swarms. This allows us to filter out disruptive and dishonest clients that may hurt the performance of either the tracker or individual peers.</p> + <table cellpadding="5" cellspacing="1" border="0" class="border" width="100%"> + <tr class="colhead"> + <td><strong>Allowed Client</strong></td> + <td style="width: 75px"><strong>Peer ID</strong></td> + <!-- td style="width: 400px;"><strong>Additional Notes</strong></td> --> + </tr> +<?php + $Row = 'a'; + foreach ($WhitelistedClients as $Client) { + //list($ClientName, $Notes) = $Client; + list($ClientName, $PeerID) = $Client; + if (strpos($ClientName, 'HIDDEN') !== false) { + continue; + } + $Row = $Row === 'a' ? 'b' : 'a'; ?> - <tr class="row<?=$Row?>"> - <td><?=$ClientName?></td> - <td><?=$PeerID?></td> - </tr> -<? } ?> - </table> - </div> + <tr class="row<?=$Row?>"> + <td><?=$ClientName?></td> + <td><?=$PeerID?></td> + </tr> +<?php + } ?> + </table> + </div> </div> -<? View::show_footer(); ?> +<?php View::show_footer(); ?> diff --git a/sections/rules/collages.php b/sections/rules/collages.php index a116a1d15..ab10895ba 100644 --- a/sections/rules/collages.php +++ b/sections/rules/collages.php @@ -1,59 +1,59 @@ -<? +<?php //Include the header View::show_header('Collages Rules'); ?> <div class="thin"> -<? include('jump.php'); ?> - <div class="header"> - <h2 class="general">Collages</h2> - </div> - <div class="box pad rule_summary" style="padding: 10px 10px 10px 20px;"> - <ul> - <li>Collages in the Discography, Staff Picks, Label, and Charts categories must be based on fact, and not opinion. If something is a published Best Of (for instance, "Pitchfork's Best Albums of the 1990's") then it should go in the Charts category.</li> +<?php include('jump.php'); ?> + <div class="header"> + <h2 class="general">Collages</h2> + </div> + <div class="box pad rule_summary" style="padding: 10px 10px 10px 20px;"> + <ul> + <li>Collages in the Discography, Staff Picks, Label, and Charts categories must be based on fact, and not opinion. If something is a published Best Of (for instance, "Pitchfork's Best Albums of the 1990's") then it should go in the Charts category.</li> - <li> - Collages in the Personal, Theme, and Genre Introductions categories may be based on opinion. You must respect others' opinions whilst creating and populating Theme and Genre Introduction collages. - </li> - <li> - Vandalizing of collages will be taken very seriously, resulting in collage editing privileges being removed (at a minimum). - </li> - <li> - Personal Best Of collages are only allowed in the new Personal Collages category. You must be a Power User+ or Donor to create one. - </li> - <li> - A well-defined group of people, for instance Torrent Masters, or Interviewers, may create a Group Picks Theme collage with one pick per person, after having gained permission for the collage from Staff. - </li> - <li> - There may only be one collage per Genre Introduction/Theme. Dupe collages will be deleted. - </li> - <li> - Theme/Genre Introduction collages must be sensible, and reasonably broad. Those that do not fit this description will be deleted. - </li> - <li> - Collages are not an alternative to the tagging system. A collage such as 'mathcore torrents' wouldn't be allowed, because it is far more appropriate to just tag the torrents as mathcore. Of course, an 'xysite.com worst 50 mathcore albums' collage would be looked upon differently. - </li> - <li> - Collages should not be used to create artist discographies, as the artist pages already exist for this purpose. However, for an artist who has a multitude of side projects, it is allowed to create a collage containing all of the projects, to be placed in the Discography category. - </li> - <li> - Power Users and Donors get one personal collage. Elites can have two, Torrent Masters can have up to three, Power TMs up to four and Elite TM+ up to five. Donors always receive one more than the class maximum. - </li> - <li> - Every collage must have at least 3 torrent groups in it, except for collages of type "Label", "Personal", and "Staff Picks". - </li> - <li> - Please check to see that a similar collage does not already exist. If a similar collage does exist, please contribute to the existing collage. - </li> - <li> - Please give your collage an appropriate title and a decent description explaining its purpose. - </li> - <li> - Please attempt to add album art to every torrent in your collage. - </li> + <li> + Collages in the Personal, Theme, and Genre Introductions categories may be based on opinion. You must respect others' opinions whilst creating and populating Theme and Genre Introduction collages. + </li> + <li> + Vandalizing of collages will be taken very seriously, resulting in collage editing privileges being removed (at a minimum). + </li> + <li> + Personal Best Of collages are only allowed in the new Personal Collages category. You must be a Power User+ or Donor to create one. + </li> + <li> + A well-defined group of people, for instance Torrent Masters, or Interviewers, may create a Group Picks Theme collage with one pick per person, after having gained permission for the collage from Staff. + </li> + <li> + There may only be one collage per Genre Introduction/Theme. Dupe collages will be deleted. + </li> + <li> + Theme/Genre Introduction collages must be sensible, and reasonably broad. Those that do not fit this description will be deleted. + </li> + <li> + Collages are not an alternative to the tagging system. A collage such as 'mathcore torrents' wouldn't be allowed, because it is far more appropriate to just tag the torrents as mathcore. Of course, an 'xysite.com worst 50 mathcore albums' collage would be looked upon differently. + </li> + <li> + Collages should not be used to create artist discographies, as the artist pages already exist for this purpose. However, for an artist who has a multitude of side projects, it is allowed to create a collage containing all of the projects, to be placed in the Discography category. + </li> + <li> + Power Users and Donors get one personal collage. Elites can have two, Torrent Masters can have up to three, Power TMs up to four and Elite TM+ up to five. Donors always receive one more than the class maximum. + </li> + <li> + Every collage must have at least 3 torrent groups in it, except for collages of type "Label", "Personal", and "Staff Picks". + </li> + <li> + Please check to see that a similar collage does not already exist. If a similar collage does exist, please contribute to the existing collage. + </li> + <li> + Please give your collage an appropriate title and a decent description explaining its purpose. + </li> + <li> + Please attempt to add album art to every torrent in your collage. + </li> - </ul> - </div> + </ul> + </div> </div> -<? +<?php View::show_footer(); ?> diff --git a/sections/rules/index.php b/sections/rules/index.php index 3dcd29e77..c92be1509 100644 --- a/sections/rules/index.php +++ b/sections/rules/index.php @@ -1,32 +1,32 @@ -<? +<?php //Include all the basic stuff... enforce_login(); if (!isset($_GET['p'])) { - require(SERVER_ROOT.'/sections/rules/rules.php'); + require(SERVER_ROOT.'/sections/rules/rules.php'); } else { - switch ($_GET['p']) { - case 'ratio': - require(SERVER_ROOT.'/sections/rules/ratio.php'); - break; - case 'clients': - require(SERVER_ROOT.'/sections/rules/clients.php'); - break; - case 'chat': - require(SERVER_ROOT.'/sections/rules/chat.php'); - break; - case 'upload': - require(SERVER_ROOT.'/sections/rules/upload.php'); - break; - case 'requests'; - require(SERVER_ROOT.'/sections/rules/requests.php'); - break; - case 'collages'; - require(SERVER_ROOT.'/sections/rules/collages.php'); - break; - case 'tag': - require(SERVER_ROOT.'/sections/rules/tag.php'); - break; - default: - error(0); - } + switch ($_GET['p']) { + case 'ratio': + require(SERVER_ROOT.'/sections/rules/ratio.php'); + break; + case 'clients': + require(SERVER_ROOT.'/sections/rules/clients.php'); + break; + case 'chat': + require(SERVER_ROOT.'/sections/rules/chat.php'); + break; + case 'upload': + require(SERVER_ROOT.'/sections/rules/upload.php'); + break; + case 'requests'; + require(SERVER_ROOT.'/sections/rules/requests.php'); + break; + case 'collages'; + require(SERVER_ROOT.'/sections/rules/collages.php'); + break; + case 'tag': + require(SERVER_ROOT.'/sections/rules/tag.php'); + break; + default: + error(0); + } } diff --git a/sections/rules/jump.php b/sections/rules/jump.php index 09dc83073..6e6025e6b 100644 --- a/sections/rules/jump.php +++ b/sections/rules/jump.php @@ -1,112 +1,112 @@ <!-- Other Sections --> <h3 id="jump">Rule Sections</h3> <div class="box pad rule_table" style="padding: 10px 10px 10px 20px;"> - <table class="m_table" width="100%"> - <tr class="colhead"> - <td class="m_th_left" style="width: 150px;">Category</td> - <td style="width: 400px;">Additional Information</td> - </tr> - <tr class="rowb"> - <td class="nobr"> - <a href="rules.php">Golden Rules</a> - </td> - <td class="nobr"> - These are the most important rules. Breaking these rules will result in the most serious consequences. - </td> - </tr> - <tr class="rowa"> - <td class="nobr"> - <a href="rules.php?p=ratio">Ratio</a> - </td> - <td class="nobr"> - These are the rules for seeding/leeching activity on this site. - </td> - </tr> - <tr class="rowb"> - <td class="nobr"> - <a href="rules.php?p=requests">Requests</a> - </td> - <td class="nobr"> - These are the rules that govern requests. - </td> - </tr> - <tr class="rowa"> - <td class="nobr"> - <a href="rules.php?p=collages">Collages</a> - </td> - <td class="nobr"> - These are the rules that govern collages. - </td> - </tr> - <tr class="rowb"> - <td class="nobr"> - <a href="rules.php?p=clients">Clients</a> - </td> - <td class="nobr"> - These are the clients we allow to connect to our tracker and rules specific to them. - </td> - </tr> - <tr class="rowa"> - <td class="nobr"> - <a href="rules.php?p=upload">Upload</a> - </td> - <td class="nobr"> - This is the section of the rules regarding any and all content which is allowed on this site. - </td> - </tr> - <tr class="rowb"> - <td class="nobr"> - <a href="rules.php?p=chat">Chat</a> - </td> - <td class="nobr"> - Read this before posting in our forums or talking on our IRC network. - </td> - </tr> - <tr class="rowa"> - <td class="nobr"> - <a href="rules.php?p=tag">Tagging</a> - </td> - <td class="nobr"> - These rules govern what tags can and cannot be added. - </td> - </tr> + <table class="m_table" width="100%"> + <tr class="colhead"> + <td class="m_th_left" style="width: 150px;">Category</td> + <td style="width: 400px;">Additional Information</td> + </tr> + <tr class="rowb"> + <td class="nobr"> + <a href="rules.php">Golden Rules</a> + </td> + <td class="nobr"> + These are the most important rules. Breaking these rules will result in the most serious consequences. + </td> + </tr> + <tr class="rowa"> + <td class="nobr"> + <a href="rules.php?p=ratio">Ratio</a> + </td> + <td class="nobr"> + These are the rules for seeding/leeching activity on this site. + </td> + </tr> + <tr class="rowb"> + <td class="nobr"> + <a href="rules.php?p=requests">Requests</a> + </td> + <td class="nobr"> + These are the rules that govern requests. + </td> + </tr> + <tr class="rowa"> + <td class="nobr"> + <a href="rules.php?p=collages">Collages</a> + </td> + <td class="nobr"> + These are the rules that govern collages. + </td> + </tr> + <tr class="rowb"> + <td class="nobr"> + <a href="rules.php?p=clients">Clients</a> + </td> + <td class="nobr"> + These are the clients we allow to connect to our tracker and rules specific to them. + </td> + </tr> + <tr class="rowa"> + <td class="nobr"> + <a href="rules.php?p=upload">Upload</a> + </td> + <td class="nobr"> + This is the section of the rules regarding any and all content which is allowed on this site. + </td> + </tr> + <tr class="rowb"> + <td class="nobr"> + <a href="rules.php?p=chat">Chat</a> + </td> + <td class="nobr"> + Read this before posting in our forums or talking on our IRC network. + </td> + </tr> + <tr class="rowa"> + <td class="nobr"> + <a href="rules.php?p=tag">Tagging</a> + </td> + <td class="nobr"> + These rules govern what tags can and cannot be added. + </td> + </tr> <!-- - <tr class="colhead"> - <td colspan="2">Other languages (Please note, these are community-maintained and are subject to error.)</td> - </tr> - <tr class="rowb"> - <td class="nobr"> - <a href="wiki.php?action=article&id=273">Français</a> - </td> - <td class="nobr"> - Les règles en français. - </td> - </tr> - <tr class="rowa"> - <td class="nobr"> - <a href="wiki.php?action=article&id=297">Svenska</a> - </td> - <td class="nobr"> - Svenska Regler - </td> - </tr> - <tr class="rowb"> - <td class="nobr"> - <a href="wiki.php?action=article&id=325">Deutsch</a> - </td> - <td class="nobr"> - Die Regeln auf Deutsch - </td> - </tr> - <tr class="rowa"> - <td class="nobr"> - <a href="wiki.php?action=article&id=388">Español</a> - </td> - <td class="nobr"> - Las reglas en español - </td> - </tr> + <tr class="colhead"> + <td colspan="2">Other languages (Please note, these are community-maintained and are subject to error.)</td> + </tr> + <tr class="rowb"> + <td class="nobr"> + <a href="wiki.php?action=article&id=273">Français</a> + </td> + <td class="nobr"> + Les règles en français. + </td> + </tr> + <tr class="rowa"> + <td class="nobr"> + <a href="wiki.php?action=article&id=297">Svenska</a> + </td> + <td class="nobr"> + Svenska Regler + </td> + </tr> + <tr class="rowb"> + <td class="nobr"> + <a href="wiki.php?action=article&id=325">Deutsch</a> + </td> + <td class="nobr"> + Die Regeln auf Deutsch + </td> + </tr> + <tr class="rowa"> + <td class="nobr"> + <a href="wiki.php?action=article&id=388">Español</a> + </td> + <td class="nobr"> + Las reglas en español + </td> + </tr> --> - </table> + </table> </div> <!-- END Other Sections --> diff --git a/sections/rules/ratio.php b/sections/rules/ratio.php index 0b3b5dc76..0a71d4883 100644 --- a/sections/rules/ratio.php +++ b/sections/rules/ratio.php @@ -1,204 +1,204 @@ -<? +<?php //Include the header View::show_header('Ratio Requirements'); ?> <div class="thin"> -<? include('jump.php'); ?> - <div class="header"> - <h2 class="general">Ratio Rules</h2> - </div> - <div class="box pad rule_summary"> - <br /> - <strong>Ratio System Overview:</strong> - <br /> - <ul> - <li>Your <strong>ratio</strong> is calculated by dividing the amount of data you've uploaded by the amount of data you've downloaded. You can view your ratio in the site header or in the "stats" section of your user profile. - </li> - <li>To maintain <strong>leeching privileges</strong>, your ratio must remain above a minimum value. This minimum value is your <strong>required ratio</strong>.</li> - <li>If your ratio falls below your required ratio, you will be given two weeks to raise your ratio back above your required ratio. During this period, you are on <strong>ratio watch</strong>. - </li> - <li>If you fail to raise your ratio above your required ratio in the allotted time, your leeching privileges will be revoked. You will be unable to download more data. Your account will remain enabled. - </li> - </ul> - <br /> - <br /> - <strong>Required Ratio Overview:</strong> - <br /> - <ul> - <li>Your required ratio represents the minimum ratio you must maintain to avoid ratio watch. You can view your required ratio in the site header after the word "required" or in the "stats" section of your user profile. - </li> - <li>Your required ratio is unique; each person's required ratio is calculated for their account specifically.</li> - <li>Your required ratio is calculated using (1) the total amount of data you've downloaded and (2) the total number of torrents you're seeding. The seeding total is not limited to snatched torrents (completed downloads) — the total includes, but is not limited to, your uploaded torrents. - </li> - <li>The required ratio system lowers your required ratio when you seed a greater number of torrents. The more torrents you seed, the lower your required ratio will be. The lower your required ratio is, the less likely it is that you'll enter ratio watch. - </li> - </ul> - <br /> - <br /> - <div style="text-align: center;"> - <strong>Required Ratio Table</strong> - <br /> - <br /> - <table class="ratio_table"> - <tr class="colhead"> - <td class="tooltip" title="These units are actually in base 2, not base 10. For example, there are 1,024 MB in 1 GB.">Amount Downloaded</td> - <td>Required Ratio (0% seeded)</td> - <td>Required Ratio (100% seeded)</td> - </tr> - <tr class="row<?=($LoggedUser['BytesDownloaded'] < 5 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> - <td>0–5 GB</td> - <td>0.00</td> - <td>0.00</td> - </tr> - <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 5 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 10 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> - <td>5–10 GB</td> - <td>0.15</td> - <td>0.00</td> - </tr> - <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 10 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 20 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> - <td>10–20 GB</td> - <td>0.20</td> - <td>0.00</td> - </tr> - <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 20 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 30 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> - <td>20–30 GB</td> - <td>0.30</td> - <td>0.05</td> - </tr> - <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 30 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 40 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> - <td>30–40 GB</td> - <td>0.40</td> - <td>0.10</td> - </tr> - <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 40 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 50 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> - <td>40–50 GB</td> - <td>0.50</td> - <td>0.20</td> - </tr> - <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 50 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 60 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> - <td>50–60 GB</td> - <td>0.60</td> - <td>0.30</td> - </tr> - <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 60 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 80 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> - <td>60–80 GB</td> - <td>0.60</td> - <td>0.40</td> - </tr> - <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 80 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 100 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> - <td>80–100 GB</td> - <td>0.60</td> - <td>0.50</td> - </tr> - <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 100 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> - <td>100+ GB</td> - <td>0.60</td> - <td>0.60</td> - </tr> - </table> - </div> - <br /> - <br /> - <strong>Required Ratio Calculation:</strong> - <br /> - <ul> - <li> - <strong>1: Determine the maximum and minimum possible values of your required ratio.</strong> Using the table above, determine your amount downloaded bracket from the first column. - Next, locate the values in the adjacent columns. The second column lists the maximum required ratio for each bracket, and the third column lists the minimum required ratio for each - bracket. The maximum and minimum required ratios are also referred to as the <strong>0% seeded</strong> and <strong>100% seeded</strong> required ratios, respectively. - </li> - <li> - <strong>2: Determine the actual required ratio.</strong> Your actual required ratio will be a number that falls between the maximum and minimum required ratio values determined in the - previous step. To determine your actual required ratio, the system first uses the maximum required ratio (0% seeded) and multiplies it by the value [1 − (<var>seeding</var> / <var>snatched</var>)]. Formatted - differently, the calculation performed by the system looks like this: - <br /> - <br /> - <div style="text-align: center;"> - <img style="vertical-align: middle;" src="static/blank.gif" alt="required ratio = (maximum required ratio) * (1 - (seeding / snatched))" - onload="if (this.src.substr(this.src.length - 9, this.src.length) == 'blank.gif') { this.src = 'https://chart.googleapis.com/chart?cht=tx&chf=bg,s,FFFFFF00&chl=%5Ctextrm%7B%28maximum+required+ratio%29+%2A+%281-%5Cfrac%7Bseeding%7D%7Bsnatched%7D%29%7D&chco=' + hexify(getComputedStyle(this.parentNode, null).color); }" /> - </div> - <br /> - <br /> - <ul> - <li>In this formula, <var>snatched</var> is the number of non-deleted unique snatches you have made. If you snatch a torrent twice, it only counts once. If a snatched torrent is - deleted from the site, it is not counted at all. - </li> - <li>In this formula, <var>seeding</var> is the average number of torrents you've seeded over a 72 hour period within the last week. If you've seeded a torrent for less than - 72 hours within the last week, it will not raise your seeding total. Please note that while it is possible to seed more torrents than you have snatched, the system effectively caps the - value at 100% of your snatched amount. - </li> - </ul> - </li> - <li><strong>3: Round, if necessary.</strong> The value determined in the previous step is rounded up to your minimum required ratio (100% seeded) if necessary. This step is required because - most amount downloaded brackets have a minimum required ratio (100% seeded) greater than zero, and the value returned by the above calculation is zero when seeding equals snatched. - </li> - </ul> - <br /> - <br /> - <strong>Required Ratio Details:</strong> - <br /> - <ul> - <li>If you stop seeding for one week, your required ratio will become the maximum required ratio (0% seeded) for your amount downloaded bracket. Once you have resumed seeding for a 72 hour - period, your required ratio will decrease according to the above calculations. - </li> - <li>If your download total is less than 5 GB, you won't be eligible for ratio watch, and you will not need a required ratio. In this circumstance, your required ratio will be zero - regardless of your seeding percentage. - </li> - <li>If your download total is less than 20 GB and you are seeding a number of torrents equal to 100% of your snatches, your required ratio will be zero.</li> - <li>As your download total increases, your minimum (100% seeded) and maximum (0% seeded) required ratios taper together. After you have downloaded 100 GB, those values become equal to each - other. This means that users with download totals greater than or equal to 100 GB have a minimum required ratio (100% seeded) of 0.60 from that point forward. - </li> - </ul> - <br /> - <br /> - <strong>Required Ratio Example:</strong> - <br /> - <ul> - <li>In this example, Rippy has downloaded 25 GB. Rippy falls into the 20–30 GB amount downloaded bracket in the table above. Rippy's maximum required ratio (0% seeded) is 0.30, and his minimum required ratio (100% seeded) is 0.05. - </li> - <li>In this example, Rippy has snatched 90 torrents, and is currently seeding 45 torrents.</li> - <li>To calculate Rippy's actual required ratio, we take his maximum required ratio (0% seeded), which is 0.30, and multiply it by [1 − (<var>seeding</var> / <var>snatched</var>)] (which is 0.50). Written out: - <samp>0.30 * [1 − (45 / 90)] = 0.15</samp> - </li> - <li>The resulting required ratio is 0.15, which falls between the maximum required ratio of 0.30 and the minimum required ratio of 0.05 for his amount downloaded bracket.</li> - <li>If Rippy's on-site required ratio was listed as a value greater than the calculated value, this would be because he hadn't seeded those 45 torrents for a 72 hour period in the - last week. In this case, the system would not be counting all 45 torrents as seeded. - </li> - </ul> - <br /> - <br /> - <strong>Ratio Watch Overview:</strong> - <br /> - <ul> - <li>Everyone gets to download their first 5 GB before ratio watch eligibility begins.</li> - <li>If you've downloaded more than 5 GB and your ratio does not meet or surpass your required ratio, you will be put on ratio watch and have <strong>two weeks</strong> to raise your - ratio above your required ratio. - </li> - <li>If you download 10 GB while on ratio watch, your leeching privileges will automatically be disabled.</li> - <li>If you fail to leave ratio watch within a two week period, you will lose leeching privileges. After losing leeching privileges, you will be unable to download more data. Your account - will remain enabled. - </li> - <li>The ratio watch system is automated and cannot be interrupted by staff.</li> - </ul> - <br /> - <br /> - <strong>Leaving Ratio Watch:</strong> - <br /> - <ul> - <li>To leave ratio watch, you must either raise your ratio by uploading more, or lower your required ratio by seeding more. Your ratio must be equal to or above your required ratio in - order for ratio watch to end. - </li> - <li>If you fail to improve your ratio by the time ratio watch expires and lose leeching privileges, your required ratio will be temporarily set to the maximum possible requirement (as if 0% of snatched torrents were being seeded). - </li> - <li>After losing leeching privileges, in order to adjust the required ratio so that it reflects the actual number of torrents being seeded, you must seed for a combined 72 hours within a weeklong period. After 72 - hours of seeding occur, the required ratio will update to reflect your current seeding total, just as it would for a leech-enabled user. - </li> - <li>Leeching privileges will be restored once your ratio has become greater than or equal to your required ratio.</li> - </ul> - <br /> - <br /> - </div> +<?php include('jump.php'); ?> + <div class="header"> + <h2 class="general">Ratio Rules</h2> + </div> + <div class="box pad rule_summary"> + <br /> + <strong>Ratio System Overview:</strong> + <br /> + <ul> + <li>Your <strong>ratio</strong> is calculated by dividing the amount of data you've uploaded by the amount of data you've downloaded. You can view your ratio in the site header or in the "stats" section of your user profile. + </li> + <li>To maintain <strong>leeching privileges</strong>, your ratio must remain above a minimum value. This minimum value is your <strong>required ratio</strong>.</li> + <li>If your ratio falls below your required ratio, you will be given two weeks to raise your ratio back above your required ratio. During this period, you are on <strong>ratio watch</strong>. + </li> + <li>If you fail to raise your ratio above your required ratio in the allotted time, your leeching privileges will be revoked. You will be unable to download more data. Your account will remain enabled. + </li> + </ul> + <br /> + <br /> + <strong>Required Ratio Overview:</strong> + <br /> + <ul> + <li>Your required ratio represents the minimum ratio you must maintain to avoid ratio watch. You can view your required ratio in the site header after the word "required" or in the "stats" section of your user profile. + </li> + <li>Your required ratio is unique; each person's required ratio is calculated for their account specifically.</li> + <li>Your required ratio is calculated using (1) the total amount of data you've downloaded and (2) the total number of torrents you're seeding. The seeding total is not limited to snatched torrents (completed downloads) — the total includes, but is not limited to, your uploaded torrents. + </li> + <li>The required ratio system lowers your required ratio when you seed a greater number of torrents. The more torrents you seed, the lower your required ratio will be. The lower your required ratio is, the less likely it is that you'll enter ratio watch. + </li> + </ul> + <br /> + <br /> + <div style="text-align: center;"> + <strong>Required Ratio Table</strong> + <br /> + <br /> + <table class="ratio_table"> + <tr class="colhead"> + <td class="tooltip" title="These units are actually in base 2, not base 10. For example, there are 1,024 MB in 1 GB.">Amount Downloaded</td> + <td>Required Ratio (0% seeded)</td> + <td>Required Ratio (100% seeded)</td> + </tr> + <tr class="row<?=($LoggedUser['BytesDownloaded'] < 5 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> + <td>0–5 GB</td> + <td>0.00</td> + <td>0.00</td> + </tr> + <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 5 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 10 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> + <td>5–10 GB</td> + <td>0.15</td> + <td>0.00</td> + </tr> + <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 10 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 20 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> + <td>10–20 GB</td> + <td>0.20</td> + <td>0.00</td> + </tr> + <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 20 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 30 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> + <td>20–30 GB</td> + <td>0.30</td> + <td>0.05</td> + </tr> + <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 30 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 40 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> + <td>30–40 GB</td> + <td>0.40</td> + <td>0.10</td> + </tr> + <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 40 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 50 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> + <td>40–50 GB</td> + <td>0.50</td> + <td>0.20</td> + </tr> + <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 50 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 60 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> + <td>50–60 GB</td> + <td>0.60</td> + <td>0.30</td> + </tr> + <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 60 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 80 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> + <td>60–80 GB</td> + <td>0.60</td> + <td>0.40</td> + </tr> + <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 80 * 1024 * 1024 * 1024 && $LoggedUser['BytesDownloaded'] < 100 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> + <td>80–100 GB</td> + <td>0.60</td> + <td>0.50</td> + </tr> + <tr class="row<?=($LoggedUser['BytesDownloaded'] >= 100 * 1024 * 1024 * 1024) ? 'a' : 'b' ?>"> + <td>100+ GB</td> + <td>0.60</td> + <td>0.60</td> + </tr> + </table> + </div> + <br /> + <br /> + <strong>Required Ratio Calculation:</strong> + <br /> + <ul> + <li> + <strong>1: Determine the maximum and minimum possible values of your required ratio.</strong> Using the table above, determine your amount downloaded bracket from the first column. + Next, locate the values in the adjacent columns. The second column lists the maximum required ratio for each bracket, and the third column lists the minimum required ratio for each + bracket. The maximum and minimum required ratios are also referred to as the <strong>0% seeded</strong> and <strong>100% seeded</strong> required ratios, respectively. + </li> + <li> + <strong>2: Determine the actual required ratio.</strong> Your actual required ratio will be a number that falls between the maximum and minimum required ratio values determined in the + previous step. To determine your actual required ratio, the system first uses the maximum required ratio (0% seeded) and multiplies it by the value [1 − (<var>seeding</var> / <var>snatched</var>)]. Formatted + differently, the calculation performed by the system looks like this: + <br /> + <br /> + <div style="text-align: center;"> + <img style="vertical-align: middle;" src="static/blank.gif" alt="required ratio = (maximum required ratio) * (1 - (seeding / snatched))" + onload="if (this.src.substr(this.src.length - 9, this.src.length) == 'blank.gif') { this.src = 'https://chart.googleapis.com/chart?cht=tx&chf=bg,s,FFFFFF00&chl=%5Ctextrm%7B%28maximum+required+ratio%29+%2A+%281-%5Cfrac%7Bseeding%7D%7Bsnatched%7D%29%7D&chco=' + hexify(getComputedStyle(this.parentNode, null).color); }" /> + </div> + <br /> + <br /> + <ul> + <li>In this formula, <var>snatched</var> is the number of non-deleted unique snatches you have made. If you snatch a torrent twice, it only counts once. If a snatched torrent is + deleted from the site, it is not counted at all. + </li> + <li>In this formula, <var>seeding</var> is the average number of torrents you've seeded over a 72 hour period within the last week. If you've seeded a torrent for less than + 72 hours within the last week, it will not raise your seeding total. Please note that while it is possible to seed more torrents than you have snatched, the system effectively caps the + value at 100% of your snatched amount. + </li> + </ul> + </li> + <li><strong>3: Round, if necessary.</strong> The value determined in the previous step is rounded up to your minimum required ratio (100% seeded) if necessary. This step is required because + most amount downloaded brackets have a minimum required ratio (100% seeded) greater than zero, and the value returned by the above calculation is zero when seeding equals snatched. + </li> + </ul> + <br /> + <br /> + <strong>Required Ratio Details:</strong> + <br /> + <ul> + <li>If you stop seeding for one week, your required ratio will become the maximum required ratio (0% seeded) for your amount downloaded bracket. Once you have resumed seeding for a 72 hour + period, your required ratio will decrease according to the above calculations. + </li> + <li>If your download total is less than 5 GB, you won't be eligible for ratio watch, and you will not need a required ratio. In this circumstance, your required ratio will be zero + regardless of your seeding percentage. + </li> + <li>If your download total is less than 20 GB and you are seeding a number of torrents equal to 100% of your snatches, your required ratio will be zero.</li> + <li>As your download total increases, your minimum (100% seeded) and maximum (0% seeded) required ratios taper together. After you have downloaded 100 GB, those values become equal to each + other. This means that users with download totals greater than or equal to 100 GB have a minimum required ratio (100% seeded) of 0.60 from that point forward. + </li> + </ul> + <br /> + <br /> + <strong>Required Ratio Example:</strong> + <br /> + <ul> + <li>In this example, Rippy has downloaded 25 GB. Rippy falls into the 20–30 GB amount downloaded bracket in the table above. Rippy's maximum required ratio (0% seeded) is 0.30, and his minimum required ratio (100% seeded) is 0.05. + </li> + <li>In this example, Rippy has snatched 90 torrents, and is currently seeding 45 torrents.</li> + <li>To calculate Rippy's actual required ratio, we take his maximum required ratio (0% seeded), which is 0.30, and multiply it by [1 − (<var>seeding</var> / <var>snatched</var>)] (which is 0.50). Written out: + <samp>0.30 * [1 − (45 / 90)] = 0.15</samp> + </li> + <li>The resulting required ratio is 0.15, which falls between the maximum required ratio of 0.30 and the minimum required ratio of 0.05 for his amount downloaded bracket.</li> + <li>If Rippy's on-site required ratio was listed as a value greater than the calculated value, this would be because he hadn't seeded those 45 torrents for a 72 hour period in the + last week. In this case, the system would not be counting all 45 torrents as seeded. + </li> + </ul> + <br /> + <br /> + <strong>Ratio Watch Overview:</strong> + <br /> + <ul> + <li>Everyone gets to download their first 5 GB before ratio watch eligibility begins.</li> + <li>If you've downloaded more than 5 GB and your ratio does not meet or surpass your required ratio, you will be put on ratio watch and have <strong>two weeks</strong> to raise your + ratio above your required ratio. + </li> + <li>If you download 10 GB while on ratio watch, your leeching privileges will automatically be disabled.</li> + <li>If you fail to leave ratio watch within a two week period, you will lose leeching privileges. After losing leeching privileges, you will be unable to download more data. Your account + will remain enabled. + </li> + <li>The ratio watch system is automated and cannot be interrupted by staff.</li> + </ul> + <br /> + <br /> + <strong>Leaving Ratio Watch:</strong> + <br /> + <ul> + <li>To leave ratio watch, you must either raise your ratio by uploading more, or lower your required ratio by seeding more. Your ratio must be equal to or above your required ratio in + order for ratio watch to end. + </li> + <li>If you fail to improve your ratio by the time ratio watch expires and lose leeching privileges, your required ratio will be temporarily set to the maximum possible requirement (as if 0% of snatched torrents were being seeded). + </li> + <li>After losing leeching privileges, in order to adjust the required ratio so that it reflects the actual number of torrents being seeded, you must seed for a combined 72 hours within a weeklong period. After 72 + hours of seeding occur, the required ratio will update to reflect your current seeding total, just as it would for a leech-enabled user. + </li> + <li>Leeching privileges will be restored once your ratio has become greater than or equal to your required ratio.</li> + </ul> + <br /> + <br /> + </div> </div> -<? - View::show_footer(); +<?php + View::show_footer(); ?> diff --git a/sections/rules/requests.php b/sections/rules/requests.php index d5bd8fb7c..7f31ad8e6 100644 --- a/sections/rules/requests.php +++ b/sections/rules/requests.php @@ -1,33 +1,33 @@ -<? +<?php //Include the header View::show_header('Request Rules'); ?> <div class="thin"> -<? include('jump.php'); ?> - <div class="header"> - <h2 class="general">Requests</h2> - </div> - <div class="box pad rule_summary" style="padding: 10px 10px 10px 20px;"> - <ul> - <li> - <strong>Do not make requests for torrents that break the rules.</strong> It is your responsibility that the request follows the rules. Your request will be deleted, and you will not get your bounty back. Requests cannot be more specific than the upload (and trumping) rules. For example, requesting an MP3 torrent with a log when the rules prohibit replacing an MP3 torrent without a log. Such a request asks for a duplicate to be uploaded. - </li> - <li> - <strong>Only one title (application, album, etc.) per request.</strong> No requests for multiple albums (e.g. discographies) or vague requirements. You may ask for multiple formats, but you cannot specify all of them. For example, you may ask for either a FLAC or V0 but not both formats. You may also make a list of albums by one artist that satisfies your request, but the request can be filled with only one album. Application requests can consist of only one application, but may span a range of different versions. However, such requests can be filled with only one version of that title. - </li> - <li> - <strong>Do not unfill requests for trivial reasons.</strong> If you did not specify in your request what you wanted (such as bitrates or a particular edition), do not unfill and later change the description. Do not unfill requests if you are unsure of what you are doing (e.g. the filled torrent may be a transcode, but you don't know how to tell). Ask for help from <a href="/staff.php">first-line support or staff</a> in that case. You may unfill the request if the torrent does not fit your specifications stated clearly in the request. - </li> - <li> - <strong>All users must have an equal chance to fill a request.</strong> Trading upload credit is not allowed. Abusing the request system to exchange favors for other users is not tolerated. That includes making specific requests for certain users (whether explicitly named or not). Making requests for releases, and then unfilling so that one particular user can fill the request is not allowed. If reported, both the requester and user filling the request will receive a warning and lose the request bounty. - </li> - <li> - <strong>No manipulation of the requester for bounty.</strong> The bounty is a reward for helping other users — it should not be a ransom. Any user who refuses to fill a request unless the bounty is increased will face harsh punishment. - </li> - </ul> - </div> +<?php include('jump.php'); ?> + <div class="header"> + <h2 class="general">Requests</h2> + </div> + <div class="box pad rule_summary" style="padding: 10px 10px 10px 20px;"> + <ul> + <li> + <strong>Do not make requests for torrents that break the rules.</strong> It is your responsibility that the request follows the rules. Your request will be deleted, and you will not get your bounty back. Requests cannot be more specific than the upload (and trumping) rules. For example, requesting an MP3 torrent with a log when the rules prohibit replacing an MP3 torrent without a log. Such a request asks for a duplicate to be uploaded. + </li> + <li> + <strong>Only one title (application, album, etc.) per request.</strong> No requests for multiple albums (e.g. discographies) or vague requirements. You may ask for multiple formats, but you cannot specify all of them. For example, you may ask for either a FLAC or V0 but not both formats. You may also make a list of albums by one artist that satisfies your request, but the request can be filled with only one album. Application requests can consist of only one application, but may span a range of different versions. However, such requests can be filled with only one version of that title. + </li> + <li> + <strong>Do not unfill requests for trivial reasons.</strong> If you did not specify in your request what you wanted (such as bitrates or a particular edition), do not unfill and later change the description. Do not unfill requests if you are unsure of what you are doing (e.g. the filled torrent may be a transcode, but you don't know how to tell). Ask for help from <a href="/staff.php">first-line support or staff</a> in that case. You may unfill the request if the torrent does not fit your specifications stated clearly in the request. + </li> + <li> + <strong>All users must have an equal chance to fill a request.</strong> Trading upload credit is not allowed. Abusing the request system to exchange favors for other users is not tolerated. That includes making specific requests for certain users (whether explicitly named or not). Making requests for releases, and then unfilling so that one particular user can fill the request is not allowed. If reported, both the requester and user filling the request will receive a warning and lose the request bounty. + </li> + <li> + <strong>No manipulation of the requester for bounty.</strong> The bounty is a reward for helping other users — it should not be a ransom. Any user who refuses to fill a request unless the bounty is increased will face harsh punishment. + </li> + </ul> + </div> </div> -<? +<?php View::show_footer(); ?> diff --git a/sections/rules/rules.php b/sections/rules/rules.php index 5a3b72b68..948c0ac6c 100644 --- a/sections/rules/rules.php +++ b/sections/rules/rules.php @@ -1,20 +1,20 @@ -<? +<?php //Include the header View::show_header('Rule Index'); ?> <!-- General Rules --> <div class="thin"> -<? include('jump.php'); ?> - <div class="header"> - <h2 id="general">Golden Rules</h2> - <p>The Golden Rules encompass all of <? echo SITE_NAME; ?> and our IRC Network. These rules are paramount; non-compliance will jeopardize your account.</p> - </div> - <div class="box pad rule_summary" style="padding: 10px 10px 10px 20px;"> -<? Rules::display_golden_rules(); ?> - </div> - <!-- END General Rules --> +<?php include('jump.php'); ?> + <div class="header"> + <h2 id="general">Golden Rules</h2> + <p>The Golden Rules encompass all of <?php echo SITE_NAME; ?> and our IRC Network. These rules are paramount; non-compliance will jeopardize your account.</p> + </div> + <div class="box pad rule_summary" style="padding: 10px 10px 10px 20px;"> +<?php Rules::display_golden_rules(); ?> + </div> + <!-- END General Rules --> </div> -<? +<?php View::show_footer(); ?> diff --git a/sections/rules/tag.php b/sections/rules/tag.php index 87882f7d0..a1a4fa352 100644 --- a/sections/rules/tag.php +++ b/sections/rules/tag.php @@ -1,18 +1,18 @@ -<? +<?php //Include the header View::show_header('Tagging rules'); ?> <!-- General Rules --> <div class="thin"> -<? include('jump.php'); ?> - <div class="header"> - <h2 id="general">Tagging rules</h2> - </div> - <div class="box pad rule_summary" style="padding: 10px 10px 10px 20px;"> -<? Rules::display_site_tag_rules(false) ?> - </div> - <!-- END General Rules --> +<?php include('jump.php'); ?> + <div class="header"> + <h2 id="general">Tagging rules</h2> + </div> + <div class="box pad rule_summary" style="padding: 10px 10px 10px 20px;"> +<?php Rules::display_site_tag_rules(false) ?> + </div> + <!-- END General Rules --> </div> -<? +<?php View::show_footer(); ?> diff --git a/sections/rules/upload.php b/sections/rules/upload.php index ddaeb841e..4e0f5a4cc 100644 --- a/sections/rules/upload.php +++ b/sections/rules/upload.php @@ -1,728 +1,728 @@ -<? +<?php //Include the header View::show_header('Uploading Rules', 'rules'); ?> <!-- Uploading Rules --> <div class="thin"> - <? include('jump.php'); ?> - <div class="header"> - <h2>Uploading Rules</h2> - </div> + <?php include('jump.php'); ?> + <div class="header"> + <h2>Uploading Rules</h2> + </div> <!-- Uploading Rules Index Links --> - <br /> - <form class="search_form" name="rules" onsubmit="return false" action=""> - <input type="text" id="search_string" value="Filter (empty to reset)" /> - <span id="Index">Example: The search term <strong>FLAC</strong> returns all rules containing <strong>FLAC</strong>. The search term <strong>FLAC+trump</strong> returns all rules containing both <strong>FLAC</strong> and <strong>trump</strong>.</span> - </form> - <br /> - <div class="before_rules"> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="Introk"><a href="#Intro"><strong>Introduction</strong></a></li> - <li id="h1k"><a href="#h1">1. <strong>Uploading Rules</strong></a> - <ul> - <li id="h1.1k"><a href="#h1.1">1.1. <strong>General</strong></a></li> - <li id="h1.2k"><a href="#h1.2">1.2. <strong>Specifically Banned</strong></a></li> - <li id="h1.3k"><a href="#h1.3">1.3. <strong>Scene Uploads</strong></a></li> - </ul> - </li> - <li id="h2k"><a href="#h2">2. <strong>Music</strong></a> - <ul> - <li id="h2.1k"><a href="#h2.1">2.1. <strong>General</strong></a></li> - <li id="h2.2k"><a href="#h2.2">2.2. <strong>Duplicates & Trumping</strong></a> - <ul> - <li id="r2.2.9k"><a href="#r2.2.9">2.2.9. <strong>Lossy rules</strong></a></li> - <li id="r2.2.10k"><a href="#r2.2.10">2.2.10. <strong>Lossless rules</strong></a></li> - <li id="r2.2.11k"><a href="#r2.2.11">2.2.11. <strong>Editions and Releases rules</strong></a></li> - </ul> - </li> - <li id="h2.3k"><a href="#h2.3">2.3. <strong>Formatting</strong></a></li> - <li id="h2.4k"><a href="#h2.4">2.4. <strong>Bonus Content</strong></a></li> - <li id="h2.5k"><a href="#h2.5">2.5. <strong>Vinyl</strong></a></li> - <li id="h2.6k"><a href="#h2.6">2.6. <strong>Live Music and Soundboards</strong></a> - <ul> - <li id="r2.6.9.1k"><a href="#r2.6.9.1">2.6.9.1. <strong>Allowed Live Music</strong></a></li> - <li id="r2.6.9.2k"><a href="#r2.6.9.2">2.6.9.2. <strong>Disallowed Live Music</strong></a></li> - </ul> - </li> - <li id="h2.7k"><a href="#h2.7">2.7. <strong>Multichannel</strong></a></li> - <li id="h2.8k"><a href="#h2.8">2.8. <strong>SACD</strong></a></li> - <li id="h2.9k"><a href="#h2.9">2.9. <strong>Blu-ray</strong></a></li> - <li id="h2.10k"><a href="#h2.10">2.10. <strong>Cassettes</strong></a></li> - </ul> - </li> - <li id="h3k"><a href="#h3">3. <strong>Comedy (Audio) & Audiobooks</strong></a></li> - <li id="h4k"><a href="#h4">4. <strong>Applications</strong></a> - <ul> - <li id="h4.1k"><a href="#h4.1">4.1. <strong>General</strong></a></li> - <li id="h4.2k"><a href="#h4.2">4.2. <strong>Duplicates & Trumping</strong></a></li> - </ul> - </li> - <li id="h5k"><a href="#h5">5. <strong>Comic Books</strong></a> - <ul> - <li id="h5.1k"><a href="#h5.1">5.1. <strong>General</strong></a></li> - <li id="h5.2k"><a href="#h5.2">5.2. <strong>Multi-comic</strong></a></li> - <li id="h5.3k"><a href="#h5.3">5.3. <strong>Duplicates & Trumping</strong></a></li> - <li id="h5.4k"><a href="#h5.4">5.4. <strong>Formatting</strong></a></li> - </ul> - </li> - <li id="h6k"><a href="#h6">6. <strong>ebooks, eLearning Books & Sheet Music</strong></a></li> - <li id="h7k"><a href="#h7">7. <strong>eLearning Videos</strong></a></li> - </ul> - </div> - </div> + <br /> + <form class="search_form" name="rules" onsubmit="return false" action=""> + <input type="text" id="search_string" value="Filter (empty to reset)" /> + <span id="Index">Example: The search term <strong>FLAC</strong> returns all rules containing <strong>FLAC</strong>. The search term <strong>FLAC+trump</strong> returns all rules containing both <strong>FLAC</strong> and <strong>trump</strong>.</span> + </form> + <br /> + <div class="before_rules"> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="Introk"><a href="#Intro"><strong>Introduction</strong></a></li> + <li id="h1k"><a href="#h1">1. <strong>Uploading Rules</strong></a> + <ul> + <li id="h1.1k"><a href="#h1.1">1.1. <strong>General</strong></a></li> + <li id="h1.2k"><a href="#h1.2">1.2. <strong>Specifically Banned</strong></a></li> + <li id="h1.3k"><a href="#h1.3">1.3. <strong>Scene Uploads</strong></a></li> + </ul> + </li> + <li id="h2k"><a href="#h2">2. <strong>Music</strong></a> + <ul> + <li id="h2.1k"><a href="#h2.1">2.1. <strong>General</strong></a></li> + <li id="h2.2k"><a href="#h2.2">2.2. <strong>Duplicates & Trumping</strong></a> + <ul> + <li id="r2.2.9k"><a href="#r2.2.9">2.2.9. <strong>Lossy rules</strong></a></li> + <li id="r2.2.10k"><a href="#r2.2.10">2.2.10. <strong>Lossless rules</strong></a></li> + <li id="r2.2.11k"><a href="#r2.2.11">2.2.11. <strong>Editions and Releases rules</strong></a></li> + </ul> + </li> + <li id="h2.3k"><a href="#h2.3">2.3. <strong>Formatting</strong></a></li> + <li id="h2.4k"><a href="#h2.4">2.4. <strong>Bonus Content</strong></a></li> + <li id="h2.5k"><a href="#h2.5">2.5. <strong>Vinyl</strong></a></li> + <li id="h2.6k"><a href="#h2.6">2.6. <strong>Live Music and Soundboards</strong></a> + <ul> + <li id="r2.6.9.1k"><a href="#r2.6.9.1">2.6.9.1. <strong>Allowed Live Music</strong></a></li> + <li id="r2.6.9.2k"><a href="#r2.6.9.2">2.6.9.2. <strong>Disallowed Live Music</strong></a></li> + </ul> + </li> + <li id="h2.7k"><a href="#h2.7">2.7. <strong>Multichannel</strong></a></li> + <li id="h2.8k"><a href="#h2.8">2.8. <strong>SACD</strong></a></li> + <li id="h2.9k"><a href="#h2.9">2.9. <strong>Blu-ray</strong></a></li> + <li id="h2.10k"><a href="#h2.10">2.10. <strong>Cassettes</strong></a></li> + </ul> + </li> + <li id="h3k"><a href="#h3">3. <strong>Comedy (Audio) & Audiobooks</strong></a></li> + <li id="h4k"><a href="#h4">4. <strong>Applications</strong></a> + <ul> + <li id="h4.1k"><a href="#h4.1">4.1. <strong>General</strong></a></li> + <li id="h4.2k"><a href="#h4.2">4.2. <strong>Duplicates & Trumping</strong></a></li> + </ul> + </li> + <li id="h5k"><a href="#h5">5. <strong>Comic Books</strong></a> + <ul> + <li id="h5.1k"><a href="#h5.1">5.1. <strong>General</strong></a></li> + <li id="h5.2k"><a href="#h5.2">5.2. <strong>Multi-comic</strong></a></li> + <li id="h5.3k"><a href="#h5.3">5.3. <strong>Duplicates & Trumping</strong></a></li> + <li id="h5.4k"><a href="#h5.4">5.4. <strong>Formatting</strong></a></li> + </ul> + </li> + <li id="h6k"><a href="#h6">6. <strong>ebooks, eLearning Books & Sheet Music</strong></a></li> + <li id="h7k"><a href="#h7">7. <strong>eLearning Videos</strong></a></li> + </ul> + </div> + </div> <!-- Actual Uploading Rules --> - <div id="actual_rules"> - <div class="before_rules"> - <h4 id="Intro"><a href="#Introk"><strong>↑</strong></a> Introduction</h4> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <p>The uploading rules below are overwhelmingly long and detailed for a reason. The length is necessary to explain the rules clearly and thoroughly. A summary of each rule is in <span style="font-weight: bold;">bold text</span> before the actual rule for easier reading. You may also find the corresponding rule sections in the <a href="#Index">Index</a>. The corresponding <a href="#">↑</a> (move one level up) and <a href="#Index">rule section links</a> (move down into the document) help provide quick navigation.</p> - <p>Before you upload anything, if you are still unsure of what a rule means, PLEASE ask your questions at any of the following points of site user support: <a href="staff.php">First-Line Support</a>, <a href="forums.php?action=viewforum&forumid=3">the Help Forum</a>, or <a href="wiki.php?action=article&name=IRC"><?=BOT_HELP_CHAN?> on IRC</a>. Send a <a href="staffpm.php">Staff PM</a> addressed to staff if other support has directed you to a moderator or if support has been unhelpful in your particular case. If you find any dead or broken links in the upload rules, send a <a href="staffpm.php">Staff PM</a> addressed to staff, and include in your message the upload rule number (e.g. <a href="#r2.4.3">2.4.3</a>) and preferably the correct link to replace the broken one.</p> - </div> - </div> - <h4 id="h1"><a href="#h1k"><strong>↑</strong></a> <a href="#h1">1.</a> Uploading Rules</h4> + <div id="actual_rules"> + <div class="before_rules"> + <h4 id="Intro"><a href="#Introk"><strong>↑</strong></a> Introduction</h4> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <p>The uploading rules below are overwhelmingly long and detailed for a reason. The length is necessary to explain the rules clearly and thoroughly. A summary of each rule is in <span style="font-weight: bold;">bold text</span> before the actual rule for easier reading. You may also find the corresponding rule sections in the <a href="#Index">Index</a>. The corresponding <a href="#">↑</a> (move one level up) and <a href="#Index">rule section links</a> (move down into the document) help provide quick navigation.</p> + <p>Before you upload anything, if you are still unsure of what a rule means, PLEASE ask your questions at any of the following points of site user support: <a href="staff.php">First-Line Support</a>, <a href="forums.php?action=viewforum&forumid=3">the Help Forum</a>, or <a href="wiki.php?action=article&name=IRC"><?=BOT_HELP_CHAN?> on IRC</a>. Send a <a href="staffpm.php">Staff PM</a> addressed to staff if other support has directed you to a moderator or if support has been unhelpful in your particular case. If you find any dead or broken links in the upload rules, send a <a href="staffpm.php">Staff PM</a> addressed to staff, and include in your message the upload rule number (e.g. <a href="#r2.4.3">2.4.3</a>) and preferably the correct link to replace the broken one.</p> + </div> + </div> + <h4 id="h1"><a href="#h1k"><strong>↑</strong></a> <a href="#h1">1.</a> Uploading Rules</h4> - <h5 id="h1.1"><a href="#h1.1k"><strong>↑</strong></a> <a href="#h1.1">1.1.</a> General</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r1.1.1"><a href="#h1.1"><strong>↑_</strong></a> <a href="#r1.1.1">1.1.1.</a> - <strong>Only music, applications, comic books, ebooks, comedy (audio), audiobooks, and music-related eLearning videos are allowed on the site.</strong> - </li> - <li id="r1.1.2"><a href="#h1.1"><strong>↑_</strong></a> <a href="#r1.1.2">1.1.2.</a> - <strong>Duplicate torrents in any category are not allowed.</strong> There are some exceptions to this rule, which are outlined in their relevant sections below. - </li> - <li id="r1.1.3"><a href="#h1.1"><strong>↑_</strong></a> <a href="#r1.1.3">1.1.3.</a> - <strong>No freely available content in non-music sections.</strong> If you could just download something from the web, so can everyone else. Each main section explains in greater detail what "freely available" means in the context of that section. See <a href="#r2.1.9">2.1.9</a> in regards to freely available music. See <a href="#r4.1.3">4.1.3</a> in regards to freely available applications. - </li> - <li id="r1.1.4"><a href="#h1.1"><strong>↑_</strong></a> <a href="#r1.1.4">1.1.4.</a> - <strong>Seed complete copies of your uploads.</strong> Do not upload a torrent unless you intend to seed until there are at least 1.0 distributed copies. Seeding past this minimum is strongly encouraged. - </li> - <li id="r1.1.5"><a href="#h1.1"><strong>↑_</strong></a> <a href="#r1.1.5">1.1.5.</a> - <strong>No advertising or personal credits.</strong> Providing artist, album, label, or retailer information is not considered advertising. - </li> - <ul> - <li id="r1.1.5.1"><a href="#h1.1.5"><strong>↑_</strong></a> <a href="#r1.1.5.1">1.1.5.1</a> - <strong>Do not advertise sites, groups, or persons in torrent contents (e.g., folder names, file names, or file tags).</strong> Exceptions: untouched scene uploads are exempt from this rule. For analog uploads, ripper credits may be included in a lineage text file, in a folder name, or in an optional file tag field (e.g. "comment" or "ripped by"), but never in a required tag field). - </li> - <li id="r1.1.5.2"><a href="#h1.1.5"><strong>↑_</strong></a> <a href="#r1.1.5.2">1.1.5.2</a> - <strong>Do not advertise sites, groups, or persons in torrent descriptions.</strong> Exception: torrent source information (e.g. ripper, scene group, or original uploader credit) is allowed in torrent descriptions. - </li> - </ul> - <li id="r1.1.6"><a href="#h1.1"><strong>↑_</strong></a> <a href="#r1.1.6">1.1.6.</a> - <strong>Archived files in uploads are not allowed.</strong> Exceptions: The sections that allow archived files (e.g., zip, rar, iso, etc.) are the following: - <ul> - <li>Comic Books (.cbr and .cbz).</li> - <li>Scene released torrents in non-music categories.</li> - <li>Sheet music may be individually archived.</li> - </ul> - </li> - </ul> - </div> - <h5 id="h1.2"><a href="#h1.2k"><strong>↑</strong></a> <a href="#h1.2">1.2.</a> Specifically Banned</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r1.2.1"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.1">1.2.1.</a> - <strong>Anything not specifically allowed below.</strong> If you have any doubts, ask before uploading. - </li> - <li id="r1.2.2"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.2">1.2.2.</a> - <strong>Any car parts and car data programs.</strong> This ban includes programs like AllData and vendor-specific diagnostic programs such as Carsoft. - </li> - <li id="r1.2.3"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.3">1.2.3.</a> - <strong>Videos of any kind (other than eLearning Videos).</strong> No movies, no TV shows, no concerts, and no data/video tracks from enhanced CDs. - </li> - <li id="r1.2.4"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.4">1.2.4.</a> - <strong>Pornography or nudity of any kind unless it is part of the official packaging for a commercial audio release.</strong> This ban includes pictures, erotic comic books or hentai, sex manuals, erotic magazines, etc. - </li> - <li id="r1.2.5"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.5">1.2.5.</a> - <strong>Games of any kind.</strong> No games of any kind for PC, Mac, Linux, mobile devices, or any other platform are allowed. - </li> - <li id="r1.2.6"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.6">1.2.6.</a> - <strong>Collections of pictures or wallpapers are not applications.</strong> You may not upload them to any category. Commercially available template and clip art packs are acceptable for the application category (see <a href="#r4.1.9">4.1.9</a>). - </li> - <li id="r1.2.7"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.7">1.2.7.</a> - <strong>User compilations in any category.</strong> All releases, packs, or collections must be reasonably official as specified in each category. - </li> - <li id="r1.2.8"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.8">1.2.8.</a> - <strong>DRM-restricted files.</strong> Files must not be encrypted or be in a restricted format that impedes sharing. You are also highly encouraged to remove personal information from any non-DRM protected files (such as iTunes Plus releases). - </li> - <li id="r1.2.9"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.9">1.2.9.</a> - <strong>MQA-encoded files.</strong> FLAC files encoded using the (proprietary) Master Quality Authenticated technology are not permitted. - </li> - </ul> - </div> - <h5 id="h1.3"><a href="#h1.3k"><strong>↑</strong></a> <a href="#h1.3">1.3.</a> Scene Uploads</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r1.3.1"><a href="#h1.3"><strong>↑_</strong></a> <a href="#r1.3.1">1.3.1.</a> - <strong>You may give credit to the release group (optional).</strong> If you really want to give credit to the release group, mention the full release name, including group name, in the Release description section only. Do not add this information to the Album description section. - </li> - <li id="r1.3.2"><a href="#h1.3"><strong>↑_</strong></a> <a href="#r1.3.2">1.3.2.</a> - <strong>No NFO art pasted in the album description.</strong> Unedited NFOs are allowed within the specific torrent Release description - not in the Album page description. If you must include some information from the NFO in the Album description or Release description, include only the tracklist, album notes, and other essential information. Specific encoding settings belong in the Release description. - </li> - <li id="r1.3.3"><a href="#h1.3"><strong>↑_</strong></a> <a href="#r1.3.3">1.3.3.</a> - <strong>Use the actual album title on the <a href="upload.php">upload page</a>; do not use the scene-given title.</strong> Naming your album_titles_like_this or your.albums.like.this is not allowed. Use the actual release title and artist. Do not use the title from the torrent folder or NFO for the scene release. - </li> - <li id="r1.3.4"><a href="#h1.3"><strong>↑_</strong></a> <a href="#r1.3.4">1.3.4.</a> - <strong>Scene releases must be complete (as released) to use the Scene label.</strong> If you have changed the tags, unpacked the archive, removed any files, split the tracks, or altered the track naming, then it is no longer a scene release and cannot be labeled as such. If any of these changes have taken place, file names should not include any reference to the release group (see <a href="#r1.1.5">1.1.5</a>, <a href="#r2.3.2">2.3.2</a>, and <a href="#r2.3.11">2.3.11</a>). - </li> - <li id="r1.3.5"><a href="#h1.3"><strong>↑_</strong></a> <a href="#r1.3.5">1.3.5.</a> - <strong>No protected archives.</strong> Archived releases must not be password protected. - </li> - <li id="r1.3.6"><a href="#h1.3"><strong>↑_</strong></a> <a href="#r1.3.6">1.3.6.</a> - <strong>Scene releases in each torrent category must conform to the rules specified for their respective sections.</strong> For example, music scene releases must adhere to the music quality and formatting rules no matter how the original files were released. If the scene archives were password protected, you cannot upload them to this site unmodified. Exceptions: You may upload scene releases that originally do not fit in the rules if you can make the necessary changes within the rules. However, these modified uploads must not be labeled as scene. - </li> - </ul> - </div> - <h4 id="h2"><a href="#h2k"><strong>↑</strong></a> <a href="#h2">2.</a> Music</h4> + <h5 id="h1.1"><a href="#h1.1k"><strong>↑</strong></a> <a href="#h1.1">1.1.</a> General</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r1.1.1"><a href="#h1.1"><strong>↑_</strong></a> <a href="#r1.1.1">1.1.1.</a> + <strong>Only music, applications, comic books, ebooks, comedy (audio), audiobooks, and music-related eLearning videos are allowed on the site.</strong> + </li> + <li id="r1.1.2"><a href="#h1.1"><strong>↑_</strong></a> <a href="#r1.1.2">1.1.2.</a> + <strong>Duplicate torrents in any category are not allowed.</strong> There are some exceptions to this rule, which are outlined in their relevant sections below. + </li> + <li id="r1.1.3"><a href="#h1.1"><strong>↑_</strong></a> <a href="#r1.1.3">1.1.3.</a> + <strong>No freely available content in non-music sections.</strong> If you could just download something from the web, so can everyone else. Each main section explains in greater detail what "freely available" means in the context of that section. See <a href="#r2.1.9">2.1.9</a> in regards to freely available music. See <a href="#r4.1.3">4.1.3</a> in regards to freely available applications. + </li> + <li id="r1.1.4"><a href="#h1.1"><strong>↑_</strong></a> <a href="#r1.1.4">1.1.4.</a> + <strong>Seed complete copies of your uploads.</strong> Do not upload a torrent unless you intend to seed until there are at least 1.0 distributed copies. Seeding past this minimum is strongly encouraged. + </li> + <li id="r1.1.5"><a href="#h1.1"><strong>↑_</strong></a> <a href="#r1.1.5">1.1.5.</a> + <strong>No advertising or personal credits.</strong> Providing artist, album, label, or retailer information is not considered advertising. + </li> + <ul> + <li id="r1.1.5.1"><a href="#h1.1.5"><strong>↑_</strong></a> <a href="#r1.1.5.1">1.1.5.1</a> + <strong>Do not advertise sites, groups, or persons in torrent contents (e.g., folder names, file names, or file tags).</strong> Exceptions: untouched scene uploads are exempt from this rule. For analog uploads, ripper credits may be included in a lineage text file, in a folder name, or in an optional file tag field (e.g. "comment" or "ripped by"), but never in a required tag field). + </li> + <li id="r1.1.5.2"><a href="#h1.1.5"><strong>↑_</strong></a> <a href="#r1.1.5.2">1.1.5.2</a> + <strong>Do not advertise sites, groups, or persons in torrent descriptions.</strong> Exception: torrent source information (e.g. ripper, scene group, or original uploader credit) is allowed in torrent descriptions. + </li> + </ul> + <li id="r1.1.6"><a href="#h1.1"><strong>↑_</strong></a> <a href="#r1.1.6">1.1.6.</a> + <strong>Archived files in uploads are not allowed.</strong> Exceptions: The sections that allow archived files (e.g., zip, rar, iso, etc.) are the following: + <ul> + <li>Comic Books (.cbr and .cbz).</li> + <li>Scene released torrents in non-music categories.</li> + <li>Sheet music may be individually archived.</li> + </ul> + </li> + </ul> + </div> + <h5 id="h1.2"><a href="#h1.2k"><strong>↑</strong></a> <a href="#h1.2">1.2.</a> Specifically Banned</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r1.2.1"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.1">1.2.1.</a> + <strong>Anything not specifically allowed below.</strong> If you have any doubts, ask before uploading. + </li> + <li id="r1.2.2"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.2">1.2.2.</a> + <strong>Any car parts and car data programs.</strong> This ban includes programs like AllData and vendor-specific diagnostic programs such as Carsoft. + </li> + <li id="r1.2.3"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.3">1.2.3.</a> + <strong>Videos of any kind (other than eLearning Videos).</strong> No movies, no TV shows, no concerts, and no data/video tracks from enhanced CDs. + </li> + <li id="r1.2.4"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.4">1.2.4.</a> + <strong>Pornography or nudity of any kind unless it is part of the official packaging for a commercial audio release.</strong> This ban includes pictures, erotic comic books or hentai, sex manuals, erotic magazines, etc. + </li> + <li id="r1.2.5"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.5">1.2.5.</a> + <strong>Games of any kind.</strong> No games of any kind for PC, Mac, Linux, mobile devices, or any other platform are allowed. + </li> + <li id="r1.2.6"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.6">1.2.6.</a> + <strong>Collections of pictures or wallpapers are not applications.</strong> You may not upload them to any category. Commercially available template and clip art packs are acceptable for the application category (see <a href="#r4.1.9">4.1.9</a>). + </li> + <li id="r1.2.7"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.7">1.2.7.</a> + <strong>User compilations in any category.</strong> All releases, packs, or collections must be reasonably official as specified in each category. + </li> + <li id="r1.2.8"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.8">1.2.8.</a> + <strong>DRM-restricted files.</strong> Files must not be encrypted or be in a restricted format that impedes sharing. You are also highly encouraged to remove personal information from any non-DRM protected files (such as iTunes Plus releases). + </li> + <li id="r1.2.9"><a href="#h1.2"><strong>↑_</strong></a> <a href="#r1.2.9">1.2.9.</a> + <strong>MQA-encoded files.</strong> FLAC files encoded using the (proprietary) Master Quality Authenticated technology are not permitted. + </li> + </ul> + </div> + <h5 id="h1.3"><a href="#h1.3k"><strong>↑</strong></a> <a href="#h1.3">1.3.</a> Scene Uploads</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r1.3.1"><a href="#h1.3"><strong>↑_</strong></a> <a href="#r1.3.1">1.3.1.</a> + <strong>You may give credit to the release group (optional).</strong> If you really want to give credit to the release group, mention the full release name, including group name, in the Release description section only. Do not add this information to the Album description section. + </li> + <li id="r1.3.2"><a href="#h1.3"><strong>↑_</strong></a> <a href="#r1.3.2">1.3.2.</a> + <strong>No NFO art pasted in the album description.</strong> Unedited NFOs are allowed within the specific torrent Release description - not in the Album page description. If you must include some information from the NFO in the Album description or Release description, include only the tracklist, album notes, and other essential information. Specific encoding settings belong in the Release description. + </li> + <li id="r1.3.3"><a href="#h1.3"><strong>↑_</strong></a> <a href="#r1.3.3">1.3.3.</a> + <strong>Use the actual album title on the <a href="upload.php">upload page</a>; do not use the scene-given title.</strong> Naming your album_titles_like_this or your.albums.like.this is not allowed. Use the actual release title and artist. Do not use the title from the torrent folder or NFO for the scene release. + </li> + <li id="r1.3.4"><a href="#h1.3"><strong>↑_</strong></a> <a href="#r1.3.4">1.3.4.</a> + <strong>Scene releases must be complete (as released) to use the Scene label.</strong> If you have changed the tags, unpacked the archive, removed any files, split the tracks, or altered the track naming, then it is no longer a scene release and cannot be labeled as such. If any of these changes have taken place, file names should not include any reference to the release group (see <a href="#r1.1.5">1.1.5</a>, <a href="#r2.3.2">2.3.2</a>, and <a href="#r2.3.11">2.3.11</a>). + </li> + <li id="r1.3.5"><a href="#h1.3"><strong>↑_</strong></a> <a href="#r1.3.5">1.3.5.</a> + <strong>No protected archives.</strong> Archived releases must not be password protected. + </li> + <li id="r1.3.6"><a href="#h1.3"><strong>↑_</strong></a> <a href="#r1.3.6">1.3.6.</a> + <strong>Scene releases in each torrent category must conform to the rules specified for their respective sections.</strong> For example, music scene releases must adhere to the music quality and formatting rules no matter how the original files were released. If the scene archives were password protected, you cannot upload them to this site unmodified. Exceptions: You may upload scene releases that originally do not fit in the rules if you can make the necessary changes within the rules. However, these modified uploads must not be labeled as scene. + </li> + </ul> + </div> + <h4 id="h2"><a href="#h2k"><strong>↑</strong></a> <a href="#h2">2.</a> Music</h4> - <h5 id="h2.1"><a href="#h2.1k"><strong>↑</strong></a> <a href="#h2.1">2.1.</a> General</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> + <h5 id="h2.1"><a href="#h2.1k"><strong>↑</strong></a> <a href="#h2.1">2.1.</a> General</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> - <li id="r2.1.1"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.1">2.1.1.</a> <strong>The only formats allowed for music are:</strong> - <ul> - <li><strong>Lossy:</strong> MP3, AAC, AC3, DTS</li> - <li><strong>Lossless:</strong> FLAC (maximum 24-bit depth, maximum 192 kHz sampling rate)</li> - </ul> - <span style="font-style: italic;">Only standard versions of each format are allowed. Hybrid formats that combine both lossless and lossy audio data in the same file, such as DTS-HD, mp3HD, and HD-AAC, are not allowed. AC3 and DTS are reserved for commercial media sources and only if they contain such tracks; transcoding from any other source, including lossless (e.g., PCM and MLP formats), is not allowed.</span> - </li> - <li id="r2.1.2"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.2">2.1.2.</a> <strong>No transcodes or re-encodes of lossy releases are acceptable here.</strong> - <ul> - <li id="r2.1.2.1"><a href="#r2.1.2"><strong>↑_</strong></a> <a href="#r2.1.2.1">2.1.2.1.</a> <strong>The only acceptable transcodes are releases that were transcoded from a lossless source (e.g., CD, SBD, DAT, Vinyl, SACD, or LPCM).</strong> Please refer to <a href="wiki.php?action=article&id=36">this wiki</a> for more information on transcodes and how to detect them.</li> - <li id="r2.1.2.2"><a href="#r2.1.2"><strong>↑_</strong></a> <a href="#r2.1.2.2">2.1.2.2.</a> <strong>Official lossy-mastered releases are not considered transcodes.</strong> They are allowed on the site. See <a href="wiki.php?action=article&id=65">this wiki</a> for further information.</li> - <li id="r2.1.2.3"><a href="#r2.1.2"><strong>↑_</strong></a> <a href="#r2.1.2.3">2.1.2.3.</a> <strong>Releases from Bandcamp, Beatport, and similar online retailers are considered official lossy-mastered releases when lossy mastered by the artist or label.</strong> A non-lossy mastered release from any source may trump a lossy mastered release from a WEB source. And if the same WEB retailer revises their release and subsequently supplies a non-lossy mastered release, the new source may trump the original upload.</li> - </ul> - </li> - <li id="r2.1.3"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.3">2.1.3.</a> <strong>Music releases must have an average bitrate of at least 192 kbps regardless of the format.</strong> Exceptions: The following VBR encodes may go under the 192 kbps limit: LAME V2 (VBR), V1 (VBR), V0 (VBR), APS (VBR), APX (VBR), MP3 192 (VBR), and AAC ~192 (VBR) to AAC ~256 (VBR) releases. See <a href="wiki.php?action=article&id=11">this wiki</a> for more information on encoding options.</li> - <li id="r2.1.4"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.4">2.1.4.</a> <strong>Bitrates must accurately reflect encoder presets or the average bitrate of the audio files.</strong> You are responsible for supplying correct format and bitrate information on the <a href="upload.php">upload page</a>. See <a href="wiki.php?action=article&id=98">this wiki</a> for further information.</li> - <li id="r2.1.5"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.5">2.1.5.</a> <strong>Albums must not be ripped or uploaded as a single track.</strong> - <ul> - <li id="r2.1.5.1"><a href="#r2.1.5"><strong>↑_</strong></a> <a href="#r2.1.5.1">2.1.5.1.</a> <strong>If the tracks on the original CD were separate, you must rip them to separate files.</strong> Unsplit rips may be present on the site but are NOT the preferred format for lossless rips. Any unsplit FLAC rips lacking a cue sheet will be deleted outright. Any unsplit FLAC rip that includes a cue sheet will be trumpable by a properly split FLAC torrent. CDs with single tracks can be uploaded without prior splitting. See <a href="wiki.php?action=article&id=70">this wiki</a> for information on unsplit rips. Information about splitting rips can be <a href="wiki.php?action=article&id=81">found here</a>. Users who upload unsplit rips will be warned.</li> - <li id="r2.1.5.2"><a href="#r2.1.5"><strong>↑_</strong></a> <a href="#r2.1.5.2">2.1.5.2.</a> <strong>Gapless DJ or professional mixes released as MP3+CUE images are allowed as unseparated album images on the site.</strong> This includes scene DJ mixes. You cannot take a previously split collection of tracks and upload it as an unsplit mix because the original form of those audio tracks was not a single unsplit file. Such torrents will be removed when reported. Split and unsplit versions do not count as duplicates of one another and may coexist.</li> - <li id="r2.1.5.3"><a href="#r2.1.5"><strong>↑_</strong></a> <a href="#r2.1.5.3">2.1.5.3.</a> <strong>For albums that contain separate tracks, unsplit MP3 albums are allowed for continuous albums and mixes only and must include a cue sheet.</strong> If there is no reason for the album to be uploaded unsplit, or if the torrent is lacking a cue sheet, it will be deleted. Exception: If the retail version of an unsplit MP3 album is sold without a cue sheet, you may upload the unsplit rip without a cue sheet.</li> - </ul> - </li> - <li id="r2.1.6"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.6">2.1.6.</a> <strong>All music torrents must be encoded with a single encoder using the same settings.</strong> - <ul> - <li id="r2.1.6.1"><a href="#r2.1.6"><strong>↑_</strong></a> <a href="#r2.1.6.1">2.1.6.1.</a> <strong>This means that you cannot create a torrent that contains both CBR and VBR files, nor can you upload torrents containing a mix of APS (VBR)/V2 (VBR) and APX (VBR)/V0 (VBR) files.</strong> Likewise, this means that you cannot upload torrents containing files of varying bit depths or sampling rates. Exceptions: If the source media was delivered in different bit depths (e.g., 16-bit and 24-bit) or sampling rates (e.g., 48 kHz and 96 kHz) you may upload a torrent containing audio files with different attributes.</li> - <li id="r2.1.6.2"><a href="#r2.1.6"><strong>↑_</strong></a> <a href="#r2.1.6.2">2.1.6.2.</a> <strong>Some WEB-sourced albums are only available in hybrid collections (e.g., albums consisting of both 24-bit files and 16-bit files, 96 kHz-sampled and 48 kHz-sampled files, or stereophonic and monophonic files).</strong> If the online retailer supplies the files with such inconsistency, they are acceptable on the site. When requested by the staff, you must be able to provide proof of the WEB-sourcing in the form of web store receipts and album information indicating the non-uniform nature of the audio files. Label them according to the highest bit depth or sampling rate and be sure to note the variation in audio quality in the torrent description (e.g., Tracks 1, 2, and 3 are encoded at 24 bits while Tracks 4, 5, 6, and 7 are encoded at 16 bits).</li> - <li id="r2.1.6.3"><a href="#r2.1.6"><strong>↑_</strong></a> <a href="#r2.1.6.3">2.1.6.3.</a> <strong>Including other kinds of audio quality differences in a torrent (e.g., duplicate lossy files in a lossless torrent) is prohibited.</strong> This kind of release is referred to as a "mutt rip".</li> - </ul> - </li> - <li id="r2.1.7"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.7">2.1.7.</a> <strong>Use only the allowed container formats for audio files.</strong> Use .m4a and .mp4 for AAC, and .flac for FLAC only. All other formats should not be encapsulated in random containers (e.g., FLAC must not be in an Ogg container, MP3 must not be in an .m4a container, and so forth). Exceptions: DTS CD-sourced audio rips, although contained in WAV, should have the .dts extension (see <a href="#h2.7">2.7</a>).</li> - <li id="r2.1.8"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.8">2.1.8.</a> <strong>Music not sourced from vinyl must not contain pops, clicks, or skips.</strong> They will be deleted for rip/encode errors if reported. Music sourced from vinyl must follow the rules <a href="#h2.5">found here</a>.</li> - <li id="r2.1.9"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.9">2.1.9.</a> <strong>Freely available music is allowed.</strong> Uploaded music may be freely available on the web (come from official sources such as record labels, band web sites, or the <a href="https://www.archive.org/index.php" target="_blank">Internet Archive</a>). Uploads can come from other torrent sites, but you are responsible for determining the audio quality of the music (e.g., that it is not transcoded, that it does not contain an edited log, that it is not a user compilation, etc.). Users are highly encouraged to provide a link to the source of their upload when uploading freely available music. However, this is not required and the lack of such a link to further information is not grounds for reporting a torrent. All freely available music must conform to both quality rules and formatting rules. This means it must be tagged correctly, not be a transcode, have separate tracks, and so forth. Freely available music uploads should have the "WEB" media format if no other source media (e.g., CD, DVD, etc.) can be established for the files.</li> - <li id="r2.1.10"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.10">2.1.10.</a> <strong>Clearly label water-marked or voice-over releases.</strong> Watermarks or voice-overs must be clearly indicated in the torrent description. The torrent will be deleted for quality misrepresentation if this information is not noted.</li> - <li id="r2.1.11"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.11">2.1.11.</a> <strong>Music ripped from the radio (Satellite or FM), television, the web, or podcasts are not allowed.</strong> See <a href="wiki.php?action=article&id=71">this wiki</a> for the difference between web rips and the WEB category.</li> - <li id="r2.1.12"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.12">2.1.12.</a> <strong>No unofficial audience recordings may be uploaded.</strong> These include but are not limited to AUD (Audience), IEM (In Ear Monitor), ALD (Assistive Listening Device), Mini-Disc, and Matrix-sourced recordings (see <a href="#r2.6.3">2.6.3</a>).</li> - <li id="r2.1.13"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.13">2.1.13.</a> <strong>Tape (VHS, Video-8, etc.) music sources are not allowed, except for cassette rips and certain soundboards.</strong> See <a href="#h2.6">2.6</a> for further information.</li> - <li id="r2.1.14"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.14">2.1.14.</a> <strong>The WEB media category is for digital downloads only.</strong> Digital downloads released only on the internet from internet sources cannot be given the CD media label on the <a href="upload.php">upload page</a>. This includes downloads from the iTunes Store, LiveDownloads, Beatport, Amazon.com, Rhapsody, and other web stores. Scene releases with no source information must be labeled as WEB. Freely available music with no source information must also be labeled as WEB. If possible, indicate the source of your files (e.g., the specific web store) in the torrent description. You are responsible for determining whether the downloaded files conform to <?=SITE_NAME?>'s rules for music quality. Note: Do not confuse WEB-sourced files with web rips (see <a href="wiki.php?action=article&id=71">this wiki</a>). WEB media torrents are not always web rips (meaning <a href="wiki.php?action=article&id=14">transcodes</a>). Please check the spectrals before assuming that they are web rips.</li> - <li id="r2.1.15"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.15">2.1.15.</a> <strong>WEB uploads must be uploaded at the bit depths and sampling rates they were sold at.</strong> WEB uploads of the same audio with different sampling rates or bit depths may coexist if that is how they are sold or distributed. This means that if, for example, an album is sold with a bit depth of 24 bits and a sampling rate of 88.2 kHz, it must be uploaded directly with no modifications to the audio stream. An existing WEB upload of the same material with a bit depth of 24 and a sampling rate of 96 kHz may coexist with the new 24/88.2 upload but does not trump it. See <a href="#r2.1.6.2">2.1.6.2</a> for more information.</li> - <li id="r2.1.16"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.16">2.1.16.</a> <strong>User-made compilations are not allowed.</strong> - <ul> - <li id="r2.1.16.1"><a href="#r2.1.16"><strong>↑_</strong></a> <a href="#r2.1.16.1">2.1.16.1.</a> <strong>These are defined as compilations made by the uploader or anyone else who does not officially represent the artist or the label. </strong> Compilations must be reasonably official. For example, "34 of my favourite Grateful Dead songs" is not a reasonably official collection. Compiling a release from a list, such as a Top 100 Billboard Chart, is not allowed. User-made and unofficial multichannel mixes are also not allowed. See <a href="wiki.php?action=article&id=72">this wiki</a> for more information.</li> - <li id="r2.1.16.2"><a href="#r2.1.16"><strong>↑_</strong></a> <a href="#r2.1.16.2">2.1.16.2.</a> <strong>Exceptions: Bootlegs are allowed if they meet quality standards for music and are proven to be retail releases in either a digital or physical form.</strong> Bootlegs/mixtapes assembled and available from internet-only sources (e.g., music blogs, message boards, etc.) are not considered official enough for this site.</li> - </ul> - </li> - <li id="r2.1.17"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.17">2.1.17.</a> <strong>No comedy, audiobook, or spoken word releases may be uploaded as music.</strong> These torrents must not be grouped with other formats from the same release (i.e., they need to have individual torrent pages). See <a href="#r3.2">3.2</a> and <a href="wiki.php?action=article&id=73">this wiki</a> for more information.</li> - <li id="r2.1.18"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.18">2.1.18.</a> <strong>Sound sample packs must be uploaded as applications.</strong> See <a href="#r4.1.9">4.1.9</a> for more information.</li> - <li id="r2.1.19"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.19">2.1.19.</a> <strong>All music torrents must represent a complete release, and may not be missing tracks (or discs in the case of a multi-disc release).</strong> - <ul> - <li id="r2.1.19.1"><a href="#r2.1.19"><strong>↑_</strong></a> <a href="#r2.1.19.1">2.1.19.1.</a> <strong>If an album is released as a multi-disc set (or box set) of CDs or vinyl discs, then it must be uploaded as a single torrent.</strong> Preferably, each individual CD rip in a multi-disc set should be organized in its own folder (see <a href="#r2.3.15">2.3.15</a>).</li> - <li id="r2.1.19.2"><a href="#r2.1.19"><strong>↑_</strong></a> <a href="#r2.1.19.2">2.1.19.2.</a> <strong>A single track (e.g., one MP3 file) cannot be uploaded on its own unless it is an officially released single.</strong> If a specific track can only be found on an album, the entire album must be uploaded in the torrent.</li> - <li id="r2.1.19.3"><a href="#r2.1.19"><strong>↑_</strong></a> <a href="#r2.1.19.3">2.1.19.3.</a> <strong>Bonus discs may be uploaded separately in accordance with <a href="#h2.4">2.4</a>.</strong> Please note that individual bonus tracks cannot be uploaded without the rest of the album. Bonus tracks are not bonus discs. Enhanced audio CDs with data or video tracks must be uploaded without the non-audio tracks. If you want to share the videos or data, you may host the files off-site with a file sharing service and include the link to that service in your torrent description.</li> - </ul> - </li> - <li id="r2.1.20"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.20">2.1.20.</a> <strong>User made discographies may not be uploaded.</strong> Multi-album torrents are not allowed on the site under any circumstances. That means no discographies, Pitchfork compilations, etc. If releases (e.g., CD singles) were never released as a bundled set, do not upload them together. Live soundboard material should be uploaded as one torrent per night, per show, or per venue. Including more than one show in a torrent results in a multi-album torrent. Exceptions: Only official boxsets and official compilation collections can contain multiple albums.</li> - <li id="r2.1.21"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.21">2.1.21.</a> <strong><a href="wiki.php?action=article&id=60">Pre-emphasis</a> is allowed in lossless torrents only.</strong> Lossless FLAC torrents with pre-emphasis are allowed on the site. They are allowed to coexist with lossless de-emphasized torrents (both in their separate album edition groups). In contrast, lossy formats may not have pre-emphasis and will be deleted if uploaded.</li> - <li id="r2.1.22"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.22">2.1.22.</a> <strong>Edition Information must be provided for digitally-sourced torrents.</strong> Digitally-sourced (including CD-sourced) rips of albums that were first released before the availability of their source medium must have accurate edition information. For example, if a CD rip is of an album whose original release date was 1957, predating the creation and distribution of CDs, then the uploader must make note of the correct year in which the CD was pressed, and preferably note the catalog identification as well. Rips for which Edition Information cannot be provided must be marked as an "Unknown Release". Under no circumstances may you guess or feign knowledge of the Edition Information. See <a href="wiki.php?action=article&id=18">this wiki</a> for more information on album editions.</li> - <li id="r2.1.23"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.23">2.1.23.</a> <strong>Audio can only be ripped from a video game CD under very specific circumstances. Audio ripped from a video game DVD is not allowed on site.</strong> - <ul> - <li id="r2.1.23.1"><a href="#r2.1.23"><strong>↑_</strong></a> <a href="#r2.1.23.1">2.1.23.1.</a> <strong>This audio layer must comply with the Red Book standard for audio data.</strong> If there is no Red Book audio on the game disc, you may not upload a rip of the disc. Be prepared to provide extensive information on any audio that is ripped from a gaming disc (see <a href="wiki.php?action=article&id=75">this wiki</a> for information about providing proof).</li> - <li id="r2.1.23.2"><a href="#r2.1.23"><strong>↑_</strong></a> <a href="#r2.1.23.2">2.1.23.2.</a> <strong>The audio must meet the minimum requirements for music on the site (see <a href="#r2.1.3">2.1.3</a> and <a href="#r2.1.6">2.1.6</a>).</strong> </li> - <li id="r2.1.23.3"><a href="#r2.1.23"><strong>↑_</strong></a> <a href="#r2.1.23.3">2.1.23.3.</a> <strong>Officially-released game soundtracks, which adhere to Red Book standards, are always allowed.</strong> </li> - </ul> - </li> - <li id="r2.1.24"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.24">2.1.24.</a> <strong>AAC torrents may only be uploaded if they represent editions unavailable on Orpheus in any other format sourced from the same medium and edition (e.g., an iTunes AAC WEB release can co-exist with a CD release of the same edition).</strong> See <a href="#r2.2.9.11">2.2.9.11</a> for information about trumping AAC torrents.</li> - <li id="r2.1.25"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.25">2.1.25.</a> <strong>No HDCD content may be uploaded to the site.</strong> There are inherent problems with ripping this format, and other forms of high-resolution audio are both more prevalent and preferred at this time (e.g., vinyl, SACD, Blu-ray, etc.). Any CD bearing the HDCD logo can still be ripped using the usual methods for ripping normal audio CDs. See <a href="wiki.php?action=article&id=76">this wiki</a> for more details on the media format.</li> - <li id="r2.1.26"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.26">2.1.26.</a> <strong> Downsampling of digital sources is allowed for certain media types.</strong> Downsampling of CD sources is strictly prohibited. Downsampling of WEB sources is allowed for audio files sampled at 88.2 kHz and above. If the WEB source is already sampled at a rate of either 44.1 kHz or 48 kHz you may not downsample the audio files any further. Downsampling of SACD, DVD, or Blu-ray sources is allowed in certain cases. See <a href="#r2.8.5">2.8.5</a>, <a href="#r2.7.3">2.7.3</a>, and <a href="#r2.9.5">2.9.5</a> for more information.</li> - </ul> - </div> - <h5 id="h2.2"><a href="#h2.2k"><strong>↑</strong></a> <a href="#h2.2">2.2.</a> Duplicates & Trumping</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r2.2.0"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.0">2.2.0.</a> <strong>Overview</strong> - <p style="text-align: center;"> - <img src="static/common/trumpchart.png" alt="Audio Dupe and Trump Chart" /><br /><br /> - <em>This chart is an overview of how the dupe and trump rules work.</em> - </p> - </li> - <li id="r2.2.1"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.1">2.2.1.</a> <strong>Upload an allowed format if it doesn't already exist on the site.</strong> If there is no existing torrent of the album in the format you have chosen, you can upload it in any bitrate that averages at least 192 kbps.</li> - <li id="r2.2.2"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.2">2.2.2.</a> <strong>Torrents that have the same bitrates, formats, and comparable or identical sampling rates for the same music release are duplicates.</strong> If a torrent is already present on the site in the format and bitrate you wanted to upload, you are not allowed to upload it - it's a duplicate (dupe). So if there's a 192 kbps CBR MP3 torrent on the site, you are not allowed to upload another 192 kbps CBR MP3 torrent for the same release. Similarly, if a torrent is already present on the site in the format and at the sampling rate you wanted to upload, you are not allowed to upload it - it's a duplicate (dupe). So if there's a 48 kHz WEB FLAC torrent on the site, you are not allowed to upload a 44.1 kHz WEB FLAC torrent for the same release. You cannot trump an existing torrent with another version solely because the new version includes album art. Exceptions: Different editions and source media do not count as dupes. See <a href="#r2.2.11">2.2.11</a> for more information.</li> - <li id="r2.2.3"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.3">2.2.3.</a> <strong>Report all trumped and duplicated torrents.</strong> If you trump a torrent or notice a duplicate torrent, please use the report link (RP) to notify staff for removal of the old or duplicate torrent. This process is particularly vital for lossless rips. If you are uploading a superior rip to the current one in the same format on the site, report the older torrent and include a link to your new torrent in the report. Your torrent will be deleted as a dupe if the older torrent is not reported. Note: Trump - This occurs when a new torrent is uploaded in a preferred bitrate or quality (as specified by the lossy-, lossless-, and edition-specific rules below) and replaces the older version that exists on the site. Dupe - This occurs when a new torrent is uploaded in a bitrate or quality that is equal to the existing older version on the site. Because the two torrents cannot coexist, the most recent upload is considered a duplicate.</li> - <li id="r2.2.4"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.4">2.2.4.</a> <strong>Dupe rules also apply to previous uploads.</strong> All of the dupe rules below concerning music formats may apply to torrents uploaded at an earlier time. For example, if you upload an album in 192 kbps MP3, someone can upload the same album in 320 kbps at a later date and trump your 192 kbps MP3.</li> - <li id="r2.2.5"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.5">2.2.5.</a> <strong>Lossless scene and non-scene torrents for the same release and bitrate are dupes.</strong> If a scene FLAC of an album from CD is already uploaded, it can be trumped by a another FLAC of the same album when accompanied by a checksummed log scoring 100%. Lossy scene torrents are trumped by non-scene torrents. For example, if a scene V2 (VBR) of an album from CD is already uploaded, it may be trumped by upload another V2 (VBR) of the same album that has been transcoded from a 100% checksum CD rip. The only exception is for Scene rips that include a 100% checksum log, in which case they cannot be trumped.</li> - <li id="r2.2.6"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.6">2.2.6.</a> <strong>All vinyl torrents must be ripped at the correct speed.</strong> You must rip all vinyl albums at the speed with which they were intended to be played (see <a href="#r2.5.7">2.5.7</a>).</li> - <li id="r2.2.7"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.7">2.2.7.</a> <strong>Complete or untouched releases replace incomplete or watermarked versions.</strong> Watermarked promos containing voice-overs and similar imperfections can be trumped by a non-watermarked release. Releases missing hidden or pre-gap tracks can be replaced by proper rips that include the entire range of tracks. These hidden or pre-gap tracks should be in their own file, not appended to the beginning of track one. Releases where the hidden or pre-gap track is appended to the first track can be trumped by one where the track is in a separate file.</li> - <li id="r2.2.8"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.8">2.2.8.</a> <strong>Torrents that have been inactive (e.g., not seeded) for two weeks may be trumped by the identical torrent (e.g., reseeded) or by a brand new rip or encode (see <a href="wiki.php?action=article&id=77">this wiki</a> for the torrent inactivity rules) of the album.</strong> If you have the original torrent files for the inactive torrent, it is preferable to reseed those original files instead of uploading a new torrent. Uploading a replacement torrent should be done only when the files from the original torrent cannot be recovered or are unavailable.</li> - <li id="r2.2.9"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.9">2.2.9.</a> <strong>Lossy rules</strong> - <ul> - <li id="r2.2.9.1"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.1">2.2.9.1.</a> <strong>Higher bitrate CBR (constant bitrate) and ABR (average bitrate) torrents replace lower ones.</strong> Once a CBR (constant bitrate) rip has been uploaded, no CBR rips of that bitrate or lower can be uploaded. In the same manner, once an ABR (average bitrate) rip has been uploaded, no ABR rips of that bitrate or lower can be uploaded. For example, if a 320 kbps CBR rip is already on the site, you are not allowed to upload a 256 kbps CBR. ABR and CBR may be interchangeably trumped. A CBR can trump a lower bitrate ABR and an ABR may trump a lower bitrate CBR. You may not upload a 192 kbps CBR if a 256 kbps ABR of the same release is already up. For more information, see the Wikipedia articles on <a href="https://en.wikipedia.org/wiki/Average_bitrate" target="_blank">average bitrate</a>, <a href="https://en.wikipedia.org/wiki/Constant_bitrate" target="_blank">constant bitrate</a>, and <a href="https://en.wikipedia.org/wiki/Variable_bitrate" target="_blank">variable bitrate</a>.</li> - <li id="r2.2.9.2"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.2">2.2.9.2.</a> <strong>Lossy format torrents with .log files do not replace equivalent existing torrents.</strong> If you want to upload a lossy format (e.g., MP3) torrent that contains a log, when that format already exists in the same medium and album edition (but without the log), your new torrent will be considered a dupe. For example, a V2 (VBR) MP3 torrent with a log cannot replace a V2 (VBR) MP3 without a log. The same standard applies for lossy audio torrents with .m3u, .cue, and other additions such as artwork. Exceptions: If an existing torrent contains encode/rip errors, you may upload another copy that does not have errors. The bad torrent needs to be reported (with clear information about which tracks and what time points are affected) or your torrent will be deleted as a dupe. Also, see <a href="#r2.3.8">2.3.8</a> through <a href="#r2.3.16">2.3.16</a> for replacing poorly named and tagged torrents.</li> - <li id="r2.2.9.3"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.3">2.2.9.3.</a> <strong>V0 (VBR), V2 (VBR), and 320 CBR MP3 are allowed at any time.</strong> You may upload a V0 (VBR), V2 (VBR) or 320 CBR MP3 as long as another rip with the same bitrate and format doesn't already exist. So if a V0 (VBR) is on the site, you may still upload a V2 (VBR) or 320 CBR MP3 of the same release.</li> - <li id="r2.2.9.4"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.4">2.2.9.4.</a> <strong>V2 (VBR) replaces APS (VBR), and both replace CBR rips under 256 kbps, 192 (ABR), and rips averaging 192 (VBR) to 210 (VBR).</strong> Once a rip with either the V2 (VBR) or APS (VBR) LAME encoding preset has been uploaded, you are not allowed to upload any CBR torrents under 256 kbps bitrate. Furthermore, a V2 (VBR) or APS (VBR) rip will trump all indiscriminate VBR rips of similar bitrate. A V2 (VBR) rip will replace a 192 (VBR) rip or 210 (VBR) rip, for example. Also, a V2 (VBR) will replace a 192 (ABR) torrent.</li> - <li id="r2.2.9.5"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.5">2.2.9.5.</a> <strong>V0 (VBR) replaces APX (VBR), and both replace CBR rips under 320 kbps, V1 (VBR), all (ABR) rips, and all other non-LAME preset (VBR) rips.</strong> Once a rip with either the V0 (VBR) or APX (VBR) LAME encoding preset has been uploaded, you are not allowed to upload any CBR torrents under 320 kbps bitrate. Furthermore, a V0 (VBR) or APX (VBR) rip will trump all indiscriminate VBR rips (of 192 kbps average bitrate or higher). A V0 (VBR) rip will replace a 256 (VBR) rip, for example. You may not upload a V1 (VBR) LAME encoding preset if either V0 (VBR) or APX (VBR) are already present.</li> - <li id="r2.2.9.6"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.6">2.2.9.6.</a> <strong>No constant bitrate encodes higher than 320 CBR are allowed on the site.</strong> You may upload MP3 files from 192 kbps to 320 kbps if no torrent exists in that format for a release. Any CBR bitrates under 192 kbps and bitrates higher than 320 kbps will be deleted. See <a href="#r2.2.9.1">2.2.9.1</a> for more information.</li> - <li id="r2.2.9.7"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.7">2.2.9.7.</a> <strong>Non-LAME preset VBR rips do not replace each other.</strong> Similar bitrate VBR encodes are considered dupes. Thus, a 192 (VBR) is allowed to coexist with a significantly higher VBR encode. You may not upload a 224 (VBR) if a 256 (VBR) is already present, nor vice versa.</li> - <li id="r2.2.9.8"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.8">2.2.9.8.</a> <strong>V2 (VBR) and V0 (VBR) LAME encodes trump APS (VBR) and APX (VBR) respectively.</strong> V2 (VBR) trumps an APS (VBR) encode of the same release. Once a V2 (VBR) is uploaded, you may not upload an APS (VBR) encode. V0 (VBR) trumps an APX (VBR) encode of the same release. Once a V0 (VBR) is uploaded, you may not upload an APX (VBR) encode.</li> - <li id="r2.2.9.9"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.9">2.2.9.9.</a> <strong>Encoding profiles/object types at the same bitrate are not unique uploads.</strong> A 256 (VBR) in LC profile is a dupe if a 256 (VBR) HE object type encode already exists on the site. Similarly, different extensions (.m4a and .mp4) count as dupes if available in the same bitrate.</li> - <li id="r2.2.9.10"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.10">2.2.9.10.</a> <strong>VBR AAC encodes can be trumped by 256 CBR encodes.</strong> Any VBR AAC encode, regardless of encoder used, may be deleted in favor of 256 CBR encodes. This includes 320 (VBR) encodes or encodes made with Nero presets.</li> - <li id="r2.2.9.11"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.11">2.2.9.11.</a> <strong>An AAC encode can be trumped by an allowed encode of the same medium and edition.</strong> Note: For WEB torrents, comparisons of audio quality and source reliability will determine whether or not an MP3 may trump an AAC (at the discretion of the moderator involved).</li> - <li id="r2.2.9.12"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.12">2.2.9.12.</a> <strong>Only one lossy vinyl rip in a specific bitrate is allowed per edition.</strong> See <a href="#r2.5.2">2.5.2</a>.</li> - <li id="r2.2.9.13"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.13">2.2.9.13.</a> <strong>Any lossy formats must be encoded at the same sampling rate as their lossless source.</strong> FLAC files from a CD rip (sampled at 44.1 kHz) can only be transcoded to 44.1 kHz lossy formats. Any 48 kHz lossy formats for the same release that are uploaded after the 44.1 kHz torrents are on the site are considered dupes. FLAC files from a WEB FLAC (sampled at 48 kHz) can only be transcoded to 48 kHz lossy formats. If the 48 kHz FLAC torrent is uploaded, and the lossy formats are populated for this album at 48 kHz, no 44.1 kHz torrents (lossless or lossy) may be uploaded because those later torrents will be considered dupes.</li> - </ul> - </li> - <li id="r2.2.10"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.10">2.2.10.</a> <strong>Lossless rules</strong> - <ul> - <li id="r2.2.10.1"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.1">2.2.10.1.</a> <strong>All FLAC CD rips must come from official CD sources.</strong> - <ul> - <li id="r2.2.10.1.1"><a href="#r2.2.10.1"><strong>↑_</strong></a> <a href="#r2.2.10.1.1">2.2.10.1.1.</a> <strong>Rips must be taken from commercially pressed or official (e.g., artist- or label-approved) CD sources.</strong> They may not come from CD-R copies of the same pressed CDs.</li> - <li id="r2.2.10.1.2"><a href="#r2.2.10.1"><strong>↑_</strong></a> <a href="#r2.2.10.1.2">2.2.10.1.2.</a> <strong>Exceptions: If the release is only distributed on CD-R by the label or artist, then that is acceptable.</strong> See <a href="wiki.php?action=article&id=78">this wiki</a> for more information on CD-R releases.</li> - </ul> - </li> - <li id="r2.2.10.2"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.2">2.2.10.2.</a> <strong>A FLAC torrent without a log (or with a log from a non-EAC, non-XLD, or non-whipper ripping tool like dBpoweramp or Rubyripper) may be trumped by a FLAC torrent with a log from an approved ripping tool that scores either 100% or <100% because of non-audio deductions.</strong> For example, an <a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a> log that scores 50% (because of CRC mismatches or other audio deductions) cannot trump a Rubyripper log. See <a href="wiki.php?action=article&id=97">this wiki</a> for more information on approved ripping tools. Also, see <a href="wiki.php?action=article&id=23">this wiki</a> for setting up and ripping with <a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a>. See <a href="wiki.php?action=article&id=22">this wiki</a> for ripping with <a href="http://tmkk.undo.jp/xld/index_e.html" target="_blank">XLD</a>. Please make sure the log file has the ".log" extension so that it can display properly on the torrent page.</li> - <li id="r2.2.10.3"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.3">2.2.10.3.</a> <strong>A FLAC upload with an <a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a>, <a href="http://tmkk.undo.jp/xld/index_e.html" target="_blank">XLD</a>, or <a href="https://github.com/whipper-team/whipper" target="_blank">whipper</a> rip log that scores 100% on the <a href="logchecker.php">log checker</a> replaces one with a lower score.</strong> No log scoring less than 100% can trump an already existing one that scores under 100%. For example, a FLAC+log rip that scores 50% in the log checker cannot be trumped by a FLAC+log rip that scores 80%. <?=SITE_NAME?> recommends <a href="wiki.php?action=article&id=23">this guide for EAC</a>, <a href="wiki.php?action=article&id=22">this guide for XLD</a>, and <a href="wiki.php?action=article&id=126">this guide for whipper</a>. See <a href="wiki.php?action=article&id=44">this wiki</a> for more information on the site log checker. Note: A FLAC upload with a log that scores 95% for not defeating the audio cache may be rescored to 100% following the procedure outlined in <a href="wiki.php?action=article&id=79">this wiki</a>.</li> - <li id="r2.2.10.4"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.4">2.2.10.4.</a> <strong>A FLAC upload with <span style="font-weight: bold;">only non-audio deductions</span> in the log (see <a href="wiki.php?action=article&id=80">this wiki</a>) may replace a FLAC upload with a log that scores 0% or contains audio deductions.</strong> For example, a range rip that has AccurateRip scores of two or higher and assorted non-audio deductions (e.g., burst mode, audio cache, pre-gaps, C2 pointers, etc.) can trump a log that has audio deductions for mismatching CRCs.</li> - <li id="r2.2.10.5"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.5">2.2.10.5.</a> <strong><a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a> logs in languages other than English are automatically parsed to English for the logging process, while <a href="https://github.com/whipper-team/whipper" target="_blank">whipper</a> logs should always be in English. <a href="http://tmkk.undo.jp/xld/index_e.html" target="_blank">XLD</a> logs in languages other than English require a manual log checker score adjustment by staff.</strong>. The current log checker cannot parse XLD logs in other languages. A special exception is made for foreign-language logs since the rip quality is equivalent to English logs. Please report your torrent with the "RP" button and select "Log Rescore Request" for the reason of the report so staff can score your torrent manually. <span style="font-weight: bold;">Do not translate the log yourself</span> (see <a href="#r2.2.10.9">2.2.10.9</a>).</li> - <li id="r2.2.10.6"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.6">2.2.10.6.</a> <strong>Range rips of hidden tracks or regular range rips (acceptable under strict conditions) require manual score adjustment.</strong> - <ul> - <li id="r2.2.10.6.1"><a href="#r2.2.10.6"><strong>↑_</strong></a> <a href="#r2.2.10.6.1">2.2.10.6.1.</a> <strong>The new log checker cannot accurately score range-ripped hidden tracks appended to proper rip logs.</strong> If you have created a 100% rip with a hidden track, but the log checker has decreased your score for the hidden track, report the torrent and the log score will be corrected by staff.</li> - <li id="r2.2.10.6.2"><a href="#r2.2.10.6"><strong>↑_</strong></a> <a href="#r2.2.10.6.2">2.2.10.6.2.</a> <strong>If you created a CD range rip that has matching CRCs for test and copy, and where every track has an AccurateRip score of 2 or more, then you may submit your torrent for manual score adjustment.</strong> It will be rescored, assuming no other problems, to 99%.</li> - <li id="r2.2.10.6.3"><a href="#r2.2.10.6"><strong>↑_</strong></a> <a href="#r2.2.10.6.3">2.2.10.6.3.</a> <strong>The CD image rip must be split with <a href="http://www.cuetools.net/wiki/CUETools" target="_blank">CUETools</a>, <a href="http://tmkk.undo.jp/xld/index_e.html" target="_blank">XLD</a>, or <a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a>.</strong> No other splitter is acceptable for a score adjustment. You will not receive a score adjustment for copy-only range rips approved with AccurateRip, nor for range rips done with test and copy without AccurateRip enabled. See <a href="wiki.php?action=article&id=82">this wiki</a> for information on splitting image rips with EAC.</li> - </ul> - </li> - <li id="r2.2.10.7"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.7">2.2.10.7.</a> <strong>A 100% log rip lacking a cue sheet can be replaced by another 100% log rip with a noncompliant cue sheet.</strong> See <a href="wiki.php?action=article&id=74">this wiki</a> for more information on cue sheets. - <ul> - <li id="r2.2.10.7.1"><a href="#r2.2.10.7"><strong>↑_</strong></a> <a href="#r2.2.10.7.1">2.2.10.7.1.</a> <strong>A 100% log rip without a cue sheet can be replaced by a 100% log rip with a noncompliant cue sheet ONLY when the included cue sheet is materially different from "a cue generated from the ripping log". Examples of a material difference include additional or correct indices, properly detected pre-gap lengths, and <a href="wiki.php?action=article&id=60">pre-emphasis</a> flags.</strong> If you upload a torrent with a cue sheet that provides nothing additional beyond what is contained in the rip log of the preexisting torrent, it will be deleted as a dupe.</li> - <li id="r2.2.10.7.2"><a href="#r2.2.10.7"><strong>↑_</strong></a> <a href="#r2.2.10.7.2">2.2.10.7.2.</a> <strong>Exceptions: An <a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a> 0.95 rip with a 100% log and no cue file, uploaded before September 14, 2010, may be trumped by a torrent that scores 100% under the current log checker requirements.</strong> </li> - </ul> - </li> - <li id="r2.2.10.8"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.8">2.2.10.8.</a> <strong>FLAC rips that contain ID3 tags or other non-compliant tags for FLAC may be trumped by rips with identical scores that have the faulty tags removed and replaced with the standard for each format.</strong> Enabling ID3 tags in <a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a> when ripping to FLAC may prevent some players from playing the files because of the inclusion of ID3 headers. If you wish to trump a FLAC rip that was ripped with ID3 tags enabled, upload the corrected torrent with the proper Vorbis comments and report the old torrent. Add information about your tag clean-up in the "Release description", or your torrent may be deleted as a dupe. Do not edit the log and change the ID3 tag setting to "No". Note: A simple way of getting rid of the ID3 header is to decompress the files to WAV, then compressing the files to FLAC, and adding the proper Vorbis comments.</li> - <li id="r2.2.10.9"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.9">2.2.10.9.</a> <strong>No log editing is permitted.</strong> - <ul> - <li id="r2.2.10.9.1"><a href="#r2.2.10.9"><strong>↑_</strong></a> <a href="#r2.2.10.9.1">2.2.10.9.1.</a> <strong>Forging log data is a serious misrepresentation of quality, and will result in a warning and the loss of your uploading privileges when the edited log is found.</strong> We recommend that you do not open the rip log file for any reason. However, if you must open the rip log, do not edit anything in the file for any reason. If you discover that one of your software settings is incorrect in the ripping software preferences, you must rip the CD again with the proper settings. Do not consolidate logs under any circumstances. If you must re-rip specific tracks or an entire disc and the rip results happen to have the new log appended to the original, leave them as is. Do not remove any part of either log, and never copy/paste parts of a new log over an old log.</li> - <li id="r2.2.10.9.2"><a href="#r2.2.10.9"><strong>↑_</strong></a> <a href="#r2.2.10.9.2">2.2.10.9.2.</a> <strong>If you find that an appended log has not been scored properly, please report the torrent and use the log rescore option.</strong> </li> - </ul> - </li> - <li id="r2.2.10.10"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.10">2.2.10.10.</a> <strong>FLAC files must be compressed.</strong> We strongly recommend that FLAC files be compressed to Level 8. Lack of compression can increase the torrent size by 5% or more. Any torrents that have uncompressed FLAC files may be reported and trumped.</li> - <li id="r2.2.10.11"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.11">2.2.10.11.</a> <strong>Any lossless formats must be encoded at the same sampling rate as their lossless source material.</strong> Lossless files from a Red Book-compliant CD rip (sampled at 44.1 kHz) can only be encoded to 44.1 kHz FLAC files for uploading in lossless torrents. Any 48 kHz lossless torrents for the same release are considered dupes. FLAC files from a WEB FLAC (sampled at 48 kHz) can only be uploaded as 48 kHz FLAC torrents. Any 44.1 kHz torrents for the same release are considered dupes. When albums are available in a number of different lossless formats (44.1 kHz, 48 kHz, 96 kHz, etc.) the first lossless torrent that is uploaded to the site may remain while all subsequent versions are considered dupes. See <a href="#r2.2.2">2.2.2</a> and <a href="#r2.2.9.13">2.2.9.13</a> for more information.</li> - </ul> - </li> - <li id="r2.2.11"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.11">2.2.11.</a> <strong>Editions and Releases rules</strong> - <ul> - <li id="r2.2.11.1"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.1">2.2.11.1.</a> <strong>Different editions and source media count as separate releases.</strong> - <ul> - <li id="r2.2.11.1.1"><a href="#r2.2.11.1"><strong>↑_</strong></a> <a href="#r2.2.11.1.1">2.2.11.1.1.</a> <strong>A rip from a different medium (e.g., vinyl) or release (e.g., a remaster) of an already existing torrent counts as a different edition.</strong> The dupe rules do not apply to the two different album editions, nor do they apply to two differently sourced torrents. So if a FLAC ripped from a CD is already up, you are still allowed to upload a FLAC ripped from vinyl. And if a 320 kbps CBR MP3 release for an original mastering of an album was uploaded, you are still allowed to upload a 320 kbps CBR MP3 remaster. See <a href="wiki.php?action=article&id=18">this wiki</a> for the procedure on how to enter album edition information for a torrent.</li> - <li id="r2.2.11.1.2"><a href="#r2.2.11.1"><strong>↑_</strong></a> <a href="#r2.2.11.1.2">2.2.11.1.2.</a> <strong>Only one edition of each unofficial live recording is allowed.</strong> See <a href="#r2.6.2">2.6.2</a>.</li> - </ul> - </li> - <li id="r2.2.11.2"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.2">2.2.11.2.</a> <strong>Rip log information (ToC, peak levels, and pre-gaps), tracklist, and running order determine distinct editions, not catalog information.</strong> Merely having different catalog numbers or CD packaging is not enough to justify a new, distinct edition, though differences in year, label (imprint), or catalog number determine distinct releases. Though different editions may coexist, distinct releases that are not also distinct editions may not, and are considered dupes. See <a href="wiki.php?action=article&id=18">this wiki</a> for more information.</li> - <li id="r2.2.11.3"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.3">2.2.11.3.</a> <strong>Multiple releases may be grouped into one edition.</strong> In such a case, torrents belonging to any of the releases determined to be in the same edition are considered dupes. Conversely, release groups may be split into more than one edition when it is determined that there is a material difference in audio content (different editions that have the same catalog information are known as "silent remasters"), in which case other formats may be uploaded for both editions.</li> - <li id="r2.2.11.4"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.4">2.2.11.4.</a> <strong>Lossless uploads with edition information trump lossless rips with nonexistent or insufficient logs and no release information.</strong> An insufficient log is a log that doesn't provide any information (e.g., ToC, peak levels, and pre-gaps) about the audio being ripped.</li> - <li id="r2.2.11.5"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.5">2.2.11.5.</a> <strong>If a lossless torrent has no release information (an "unknown release") but does have a log containing ToC, peak level, and pre-gap information, it cannot be trumped by a lossless torrent with release information unless the "lossless unknown release torrent" can be determined to be the same edition as a lossless torrent for which the release information is known.</strong> If such an identification can be made, the "lossless unknown release torrent" (and all lossy torrents that can be identified as transcodes of it) is moved into the same edition as the lossless torrent with edition information, and the merged torrents are then subject to dupe and trump rules as per <a href="#r2.2.11.2">2.2.11.2</a>. If the rip log is incomplete (missing ToC, peak level, or pre-gap information) and the information available matches a torrent in an edition whose release information is known, the torrent whose release information is known trumps the torrent whose release information is unknown.</li> - <li id="r2.2.11.6"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.6">2.2.11.6.</a> <strong>Lossy torrents with release information trump unknown release torrents if they share the same tracklist and running order, with one exception.</strong> If it can be established that a lossy Unknown Release torrent is a transcode of an Unknown Release FLAC, and the Unknown Release FLAC is moved into an edition whose release information is known as per <a href="#r2.2.11.5">2.2.11.5</a>, then all lossy transcodes of it are moved with it and are treated as any other lossy torrents already present in the edition as per <a href="#r2.2.11.2">2.2.11.2</a>.</li> - <li id="r2.2.11.7"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.7">2.2.11.7.</a> <strong>Bonus disc-only uploads can coexist with the complete set in accordance with the trumping rules.</strong> A bonus disc-only release can be trumped by an upload containing the "full" original album + bonus discs, in the same format, in accordance with the usual trumping rules (see <a href="#h2.4">2.4</a> for the rules specific to bonus materials).</li> - <li id="r2.2.11.8"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.8">2.2.11.8.</a> <strong>Unknown Release torrents may be trumped by seemingly identical torrents whose Edition Information can be verified.</strong> Torrents marked as "Unknown Release" are eligible to be trumped by rips sourced from the same medium, with the same track listing and running order, whose source Edition Information is provided and can be verified.</li> - <li id="r2.2.11.9"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.9">2.2.11.9.</a> <strong>Only one lossless and two 24-bit lossless vinyl rips are allowed per edition.</strong> A poor sounding lossless rip may be trumped by a better sounding lossless rip, regardless of lineage information. The same quality trump can occur for 24-bit lossless rips. To trump an older torrent for a better sounding version, <span style="font-weight: bold;">you need to report it with clear information about how your rip sounds better than the other one</span> (e.g., specific tracks where the audio is appreciably improved, specific time points that demonstrate the improvement, etc.). Rips of extremely poor quality may be deleted outright if reported. All quality trumps/deletions of this nature are at the discretion of the moderator involved. See <a href="#h2.5">2.5</a>.</li> - </ul> - </li> - </ul> - </div> - <h5 id="h2.3"><a href="#h2.3k"><strong>↑</strong></a> <a href="#h2.3">2.3.</a> Formatting</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r2.3.1"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.1">2.3.1.</a> <strong>Music releases must be in a directory that contains the music.</strong> This includes single track releases, which must be enclosed in a torrent folder even if there is only one file in the torrent. No music may be compressed in an archive (e.g., .rar, .zip, .tar, .iso). Scene archives of music must be unpacked and not labeled as "scene".</li> - <li id="r2.3.2"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.2">2.3.2.</a> <strong>Name your directories with meaningful titles, such as "Artist - Album (Year) - Format". The minimum acceptable is "Album" although it is preferable to include more information.</strong> If the directory name does not include this minimum then another user can rename the directory, re-upload, and report your torrent for deletion. In addition, torrent folders that are named using the scene convention will be trumpable if the Scene label is absent from the torrent.</li> - <li id="r2.3.3"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.3">2.3.3.</a> <strong>Avoid creating unnecessary nested folders (such as an extra folder for the actual album) inside your properly named directory.</strong> A torrent with unnecessary nested folders is trumpable by a torrent with such folders removed. For single disc albums, all audio files must be included in the main torrent folder. For multi-disc albums, the main torrent folder may include one sub-folder that holds the audio file contents for each disc in the box set, e.g., the main torrent folder is "<samp>Adele - 19 (2008) - FLAC</samp>" while appropriate sub-folders may include "<samp>19 (Disc 1of2)</samp>" or "<samp>19</samp>" and "<samp>Live From The Hotel Cafe (Disc 2of2)</samp>" or "<samp>Acoustic Set Live From The Hotel Cafe, Los Angeles</samp>". Additional folders are unnecessary because they do nothing to improve the organization of the torrent. If you are uncertain about what to do for other cases, PM a staff member for guidance.</li> - <li id="r2.3.4"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.4">2.3.4.</a> <strong>Label your torrents according to site standards.</strong> Follow the <a href="wiki.php?action=article&id=100">torrent naming guide</a> for help on how to name your uploaded torrents properly. Use the Edition Information box on the <a href="upload.php">upload page</a> to denote different editions or versions of an album (e.g., censored version versus an uncensored version). If you need help merging or editing your upload, please request help in <a href="forums.php?action=viewforum&forumid=34">this forum</a>. For the album category/release type, follow the <a href="wiki.php?action=article&id=58">guidelines here</a>.</li> - <li id="r2.3.5"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.5">2.3.5.</a> <strong>Torrents should never have [REQ] or [REQUEST] in the title or artist name.</strong> If you fill a request using the <a href="requests.php">Requests system</a>, everyone who voted for it will be automatically notified.</li> - <li id="r2.3.6"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.6">2.3.6.</a> <strong>Torrent album titles must accurately reflect the actual album titles.</strong> Use proper capitalization when naming your albums. Typing the album titles in all lowercase letters or all capital letters is unacceptable and makes the torrent trumpable. For detailed information on naming practices see <a href="wiki.php?action=article&id=42">this wiki</a>. Any descriptions like [Advance] or [CDM] (if you must use them) should be entered in the Edition Information box on the <a href="upload.php">upload page</a>, not in the title. Exceptions: If the album uses special capitalization, then you may follow that convention.</li> - <li id="r2.3.7"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.7">2.3.7.</a> <strong>The Artist field in the torrent name should contain only the artist name.</strong> Do not add additional information about the artist in the artist field unless the album credits the artist in that manner. For example, "Artist X (of Band Y)" or "Band X (feat. Artist Y)". It is recommended that you search existing torrents for the artist name so that you can be sure that you name the artist the exact same way. A torrent with a proper artist name will be grouped with the existing torrents for that artist on a common artist page, and thus will be easy to find. Capitalization problems will also make a torrent trumpable. Labeling the artist incorrectly prevents your torrent from being grouped with the other torrents for the same artist. See <a href="wiki.php?action=article&id=83">this wiki</a> for more information.</li> - <li id="r2.3.8"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.8">2.3.8.</a> <strong>The year of the original recording should be used for the "Year" field on the <a href="upload.php">upload page</a>.</strong> Use the recording year for "Year of the original release" (if you can establish it), and use the option to add the release year for the album or edition you are uploading in the Edition Information on the <a href="upload.php">upload page</a>. For example, all editions of The Beatles (White Album) would have 1968 in the main Year box. However, each of the various mono pressings, remasters, re-releases, expanded editions, reconstructions, etc. would have its respective release year in the Edition Information box.</li> - <li id="r2.3.9"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.9">2.3.9.</a> <strong>All lossless analog rips should include clear information about source lineage.</strong> All lossless SACD digital layer analog rips and vinyl rips must include clear information about recording equipment used (see <a href="#h2.8">2.8</a>). If you used a USB turntable for a vinyl rip, clearly indicate this in your lineage information. Also include all intermediate steps up to lossless encoding, such as the program used for mastering, sound card used, etc. Lossless analog rips missing rip information can be trumped by better documented lossless analog rips of equal or better quality. In order to trump a lossless analog rip without a lineage, this lineage must be included as a .txt or .log file within the new torrent.</li> - <li id="r2.3.10"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.10">2.3.10.</a> <strong>All lossless soundboard recordings must include clear information about source lineage.</strong> This information should be displayed in the torrent description. Optionally, the uploader may include the information in a .txt or .log file within the torrent. Lossless soundboard recordings missing lineage information will be deleted if reported (see <a href="#r2.6.7">2.6.7</a>).</li> - <li id="r2.3.11"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.11">2.3.11.</a> <strong>File names must accurately reflect the song titles.</strong> You may not have file names like <samp>01track.mp3</samp>, <samp>02track.mp3</samp>, etc. Torrents containing files that are named with incorrect song titles can be trumped by properly labeled torrents. Also, torrents that are sourced from the scene but do not have the Scene label must comply with site naming rules (no release group names in the file names, no advertisements in the file names, etc.). Note that these must be substantial improvements such as the removal of garbage characters. Small changes such as diacritical marks are insufficient grounds for trumping. English translations of song titles in file names are encouraged but not necessary for foreign language song titles. If all the letters in the track titles are capitalized, the torrent is trumpable. Exceptions: Rare albums featuring no track listing or untitled tracks may have file names like <samp>01track.mp3</samp>, <samp>02track.mp3</samp>, and so forth. Please note this track list in the "Album description". If foreign language characters create playback problems for some systems and cannot be coherently translated, file names such as "<samp>01track</samp>" are acceptable for those few cases.</li> - <li id="r2.3.12"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.12">2.3.12.</a> <strong>The maximum character length for files is 255 characters.</strong> Path length values must not be so long that they cause incompatibility problems with operating systems and media players. For example, "<samp>My Artist Name - My Album Name (2012) - FLAC/01 - Long Track Name for the First Track.flac</samp>" is a typical torrent folder that contains the audio files. This path name consists of 90 characters. This limit includes the number of characters in the main torrent folder (in this case, 46 characters), any sub-folders, and files within that torrent folder. In addition, unnecessarily nested folders will count towards this limit.</li> - <li id="r2.3.13"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.13">2.3.13.</a> <strong>Track numbers are required in file names (e.g., "<samp>01 - TrackName.mp3</samp>").</strong> If a torrent without track numbers in the file names is uploaded, then a torrent with the track numbers in the file names can take its place. When formatted properly, file names will sort in order by track number or playing order. Also see <a href="#r2.3.14">2.3.14</a>. Exception: Track numbers are not required for single-track torrents.</li> - <li id="r2.3.14"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.14">2.3.14.</a> <strong>When formatted properly, file names will alphabetically sort into the original playing order of the release.</strong> - <ul> - <li id="r2.3.14.1"><a href="#r2.3.14"><strong>↑_</strong></a> <a href="#r2.3.14.1">2.3.14.1.</a><strong>For albums with more than one artist, if the name of the artist is in the file name, it must come after the track number in order for the tracks to sort into the correct order.</strong> For example, "<samp>01 - U2 - Where the Streets Have No Name.flac</samp>" is a properly formatted file name for a multiple-artist album. "<samp>U2 - 01 - Where the Streets Have No Name.mp3</samp>" is not correct in the context of a compilation album because the resultant file list will sort in alphabetical order by artist rather than by numerical track number. <strong>Note:</strong> there is no requirement for artist names to be in the track file names, other than <a href="#r2.3.16.2">2.3.16.2.</a> Torrents that have improperly-formatted or incomplete file names will be trumpable.</li> - <li id="r2.3.14.2"><a href="#r2.3.14"><strong>↑_</strong></a> <a href="#r2.3.14.2">2.3.14.2.</a><strong>If a torrent has subfolders for multiple discs of a release, these subfolders must sort by disc order.</strong> If each disc has a title (<a href="torrents.php?id=8238">torrents.php?id=8238</a>, for example) and the discs are not numbered, this rule does not apply, and each subdirectory should have the disc name included.</li> - </ul> - </li> - <li id="r2.3.15"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.15">2.3.15.</a> <strong>Multiple-disc torrents cannot have tracks with the same numbers in one directory.</strong> You may place all the tracks for disc one in one directory and all the tracks for disc two in another directory. If you prefer to use one directory for all the audio files, you must use successive numbering. For example, disc one has 15 tracks and disc two has 20 tracks. You may either number tracks in disc one as #01-#15 and those of disc two as #16-#35 in the same directory, or you may add a disc number before the track numbers such that the numbers are #1 06 for Disc One Track 06, and #2 03 for Disc 2 Track 03, and so forth.</li> - <li id="r2.3.16"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.16">2.3.16.</a> <strong>Properly tag your music files.</strong> - <ul> - <li id="r2.3.16.1"><a href="#r2.3.16"><strong>↑_</strong></a> <a href="#r2.3.16.1">2.3.16.1.</a>Certain meta tags (e.g., ID3, Vorbis) are required on all music uploads. Make sure to use the proper format tags for your files (e.g., no ID3 tags for FLAC — see <a href="#r2.2.10.8">2.2.10.8</a>). ID3v2 tags for files are highly recommended over ID3v1.</li> - <li id="r2.3.16.2"><a href="#r2.3.16"><strong>↑_</strong></a> <a href="#r2.3.16.2">2.3.16.2.</a>ID3 tags are recommended for AC3 torrents, but are not mandatory because the format does not natively support file metadata tagging (for AC3, the file names become the vehicle for correctly labeling media files). Because of this lack of support, the Album and Artist information must be included in the torrent folder for AC3 and DTS files. In addition, the Track Number and Track Title information must be included in the file names for AC3 and DTS files; for various artists compilations, the Track Artist must be included in the file names as well, ensuring that they also satisfy <a href="#r2.3.14">2.3.14.</a></li> - <li id="r2.3.16.3"><a href="#r2.3.16"><strong>↑_</strong></a> <a href="#r2.3.16.3">2.3.16.3.</a>Torrents uploaded with both good ID3v1 tags and blank ID3v2 tags (a dual set of tags) are trumpable by torrents with either just good ID3v1 tags or good ID3v2 tags (a single set of tags). See <a href="wiki.php?action=article&id=84">this wiki</a> for more information on ID3 tags.</li> - <li id="r2.3.16.4"><a href="#r2.3.16"><strong>↑_</strong></a> <a href="#r2.3.16.4">2.3.16.4.</a>If you upload an album missing one or more of the required tags, then another user may add the tags, re-upload, and report your torrent for deletion. The required tags are: - <ul> - <li>Artist</li> - <li>Album</li> - <li>Title</li> - <li>Track Number</li> - </ul> - </li> - </ul> - <span style="font-style: italic;">Note: The "Year" tag is optional but strongly encouraged. However, if missing or incorrect, this is not grounds for trumping a torrent.</span> - </li> - <li id="r2.3.17"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.17">2.3.17.</a> <strong>The torrent artist for classical works should use the full composer name.</strong> Before uploading, see <a href="wiki.php?action=article&id=85">this wiki</a> for guidelines on uploading and tagging classical music torrents.</li> - <li id="r2.3.18"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.18">2.3.18.</a> <strong>Newly re-tagged torrents trumping badly tagged torrents must reflect a substantial improvement over the previous tags.</strong> Removing "(Remastered)" or "(CD1)" from an album or song title is not a substantial improvement. Small changes that include fixing slight misspellings, or missing an alternate spelling of an artist (e.g., excluding "The" before a band name) are insufficient grounds for replacing other torrents. Artist names that are misspelled in the tags are grounds for trumping; this includes character accents and characters that mean one letter in one language and a different letter in another language. Improper capitalization in the tags is grounds for trumping; this includes artist tags (or composer tags) that contain names that are all upper case or track titles that are all upper case. Tags with multiple entries in the same tag (e.g., track number and track title in the track title tags; or track number, artist, and track title in the artist tags) are subject to trumping. You may trump a release if the tags do not follow the data from a reputable music cataloguing service such as <a href="https://musicbrainz.org/" target="_blank">MusicBrainz</a> or <a href="http://www.discogs.com/" target="_blank">Discogs</a>. In the case of a conflict between reputable listings, either tagged version is equally preferred on the site and cannot trump the other. For example, an album is tagged differently in <a href="https://musicbrainz.org/" target="_blank">MusicBrainz</a> and in <a href="http://www.discogs.com/" target="_blank">Discogs</a>. Either style of tagging is permitted; neither is "better" than the other. In that case, any newly tagged torrents replacing an already properly tagged torrent, which follows good tagging convention, will result in a dupe. Note: For classical music, please follow these <a href="wiki.php?action=article&id=85">tagging guidelines</a>.</li> - <li id="r2.3.19"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.19">2.3.19.</a> <strong>Do not embed large images in file metadata.</strong> The combined size of embedded images and padding may not exceed <strong>1024 KiB</strong> per file. You may include the artwork separately if it is too big. When removing embedded images, make sure that the padding is removed or reduced to a few kilobytes. Refer to <a href="wiki.php?action=article&id=86">this wiki article</a> for more information.</li> - <li id="r2.3.20"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.20">2.3.20.</a> <strong>Leading spaces are not allowed in any file or folder names.</strong> Leading spaces cause usability and interoperability problems among various operating systems and programs. Torrents with file or folder names that contain leading space characters are trumpable.</li> - </ul> - </div> - <h5 id="h2.4"><a href="#h2.4k"><strong>↑</strong></a> <a href="#h2.4">2.4.</a> Bonus Content</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r2.4.1"><a href="#h2.4"><strong>↑_</strong></a> <a href="#r2.4.1">2.4.1.</a> <strong>All music torrents must represent a complete release, and may not be missing tracks or discs (see <a href="#r2.1.19">2.1.19</a>).</strong> Bonus tracks or bonus discs should be uploaded as a complete release with the main album. Do not cobble together main album and bonus content from different sources if you do not have the complete release. Such torrents will be regarded as mutt rips or user compilations, and a staff member may ask for proof of the torrent's authenticity.</li> - <li id="r2.4.2"><a href="#h2.4"><strong>↑_</strong></a> <a href="#r2.4.2">2.4.2.</a> <strong>A FLAC bonus disc-only release is not automatically deleted due to the presence of the complete set (Album + Bonus disc).</strong> An upload consisting solely of a bonus disc can only be trumped by the complete set when the trumping torrent's log score qualifies based on the usual lossless rules (see <a href="#r2.2.10">2.2.10</a>). No bonus disc-only upload can trump a complete set that has a total score of 100%.</li> - <li id="r2.4.3"><a href="#h2.4"><strong>↑_</strong></a> <a href="#r2.4.3">2.4.3.</a> <strong>When a "perfect" FLAC rip (with a log scoring 100%) of the complete release (including the bonus disc) is uploaded, all bonus disc-only torrents are considered dupes of the complete set.</strong> </li> - <li id="r2.4.4"><a href="#h2.4"><strong>↑_</strong></a> <a href="#r2.4.4">2.4.4.</a> <strong>A lossy bonus disc-only torrent can be trumped by the complete set in the same format and bitrate.</strong> </li> - <li id="r2.4.5"><a href="#h2.4"><strong>↑_</strong></a> <a href="#r2.4.5">2.4.5.</a> <strong>Exclusive bonus content from services such as iTunes or Amazon may be uploaded in a separate torrent but may be trumped by a complete torrent containing the full digital release and bonus material.</strong> For instance, an iTunes release that contains two iTunes bonus tracks may have a torrent uploaded of solely the two bonus tracks, though it remains trumpable by the complete iTunes release that includes the bonus material.</li> - <li id="r2.4.6"><a href="#h2.4"><strong>↑_</strong></a> <a href="#r2.4.6">2.4.6.</a> <strong>Lossy bonus content which is otherwise unavailable may be uploaded in a separate torrent and must not be included in the same torrent as the original album, as to avoid creating mutt rips in accordance with <a href="#r2.1.6.3">2.1.6.3</a>.</strong> This includes, but is not limited to: Bonus MP3 content provided in the data portion of a CD, "subscription" bonus material where content is released (gradually or wholly) to subscribers or fan club members, or downloadable exclusives for purchasers of a release. All lossy bonus content must meet the minimum bitrate requirements of <a href="#r2.1.3">2.1.3</a>.</li> - <li id="r2.4.7"><a href="#h2.4"><strong>↑_</strong></a> <a href="#r2.4.7">2.4.7.</a> <strong>Mixed media sets are to be uploaded as one torrent per medium.</strong> For example, a release which contains a CD, a vinyl, and a cassette in one package must be uploaded as three separate torrents, with one torrent containing the CD content, another torrent containing the vinyl content, and another torrent containing the cassette content (in compliance with the tape rip requirements in <a href="#h2.10">2.10</a>).</li> - </ul> - </div> - <h5 id="h2.5"><a href="#h2.5k"><strong>↑</strong></a> <a href="#h2.5">2.5.</a> Vinyl</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r2.5.1"><a href="#h2.5"><strong>↑_</strong></a> <a href="#r2.5.1">2.5.1.</a> <strong>Downsampling of analog rips is allowed.</strong> Analog rips that have been downsampled may be uploaded (e.g., a 24/96 vinyl rip downsampled to 16/44.1). Any downsampled torrents must include the specific programs and methods used to downsample in addition to the lineage for the original rip or it will be deleted.</li> - <li id="r2.5.2"><a href="#h2.5"><strong>↑_</strong></a> <a href="#r2.5.2">2.5.2.</a> <strong>Only one lossy vinyl rip in a specific bitrate is allowed per edition.</strong> Once someone has uploaded a lossy format vinyl rip (in MP3), you may not upload another copy in the same bitrate. It does not matter whether or not the lossy files are of differing sampling rates. For example, if a 44.1 kHz V2 (VBR) copy is already up, you may not upload the same album in V2 (VBR) at 48 kHz.</li> - <li id="r2.5.3"><a href="#h2.5"><strong>↑_</strong></a> <a href="#r2.5.3">2.5.3.</a> <strong>Only one 16-bit lossless vinyl rip is allowed per edition.</strong> Lossless 16-bit vinyl rips must have a sampling rate of either 44.1 kHz or 48 kHz. Upsampling of rips is strictly forbidden. See <a href="wiki.php?action=article&id=87">this wiki</a> for vinyl ripping information and <a href="wiki.php?action=article&id=88">this wiki</a> for general information on the vinyl medium.</li> - <li id="r2.5.4"><a href="#h2.5"><strong>↑_</strong></a> <a href="#r2.5.4">2.5.4.</a> <strong>A maximum of two 24-bit lossless vinyl rips are allowed per edition: A lossless 24-bit rip with sampling rate equal to 192 kHz and a lossless 24-bit rip with sampling rate equal to 44.1, 48, 88.2, or 96 kHz.</strong> Lossless 24/96 vinyl rips trump 24/44.1, 24/48, and 24/88.2 rips. Two or more lossless 24-bit vinyl rips with sampling rates equal to 44.1, 48, or 88.2 kHz are considered dupes. So a 24/88.2 rip and a 24/48 rip of the same material are considered dupes. Rips must sound as good as or better than the rips they are attempting to trump (see <a href="#r2.2.11.9">2.2.11.9</a> for examples of trumping proofs). Upsampling of rips is strictly forbidden. See <a href="wiki.php?action=article&id=87">this wiki</a> for more information on 24-bit vinyl ripping.</li> - <li id="r2.5.5"><a href="#h2.5"><strong>↑_</strong></a> <a href="#r2.5.5">2.5.5.</a> <strong>Vinyl rips may be trumped by better-sounding rips of the same bit depth, regardless of lineage information (see <a href="#r2.3.9">2.3.9</a>).</strong> - <ul> - <li id="r2.5.5.1"><a href="#r2.5.5"><strong>↑_</strong></a> <a href="#r2.5.5.1">2.5.5.1.</a> <strong>Lossless 16-bit rips may be trumped by better-sounding lossless 16/44.1 or 16/48 rips.</strong> </li> - <li id="r2.5.5.2"><a href="#r2.5.5"><strong>↑_</strong></a> <a href="#r2.5.5.2">2.5.5.2.</a> <strong>Lossless 24-bit rips with sampling rate equal to 44.1, 48, 88.2, or 96 kHz may be trumped by better-sounding lossless 24/96 rips.</strong> </li> - <li id="r2.5.5.3"><a href="#r2.5.5"><strong>↑_</strong></a> <a href="#r2.5.5.3">2.5.5.3.</a> <strong>Lossless 24-bit rips with sampling rate equal to 192 kHz may be trumped by better-sounding lossless 24/192 rips.</strong> </li> - <li id="r2.5.5.4"><a href="#r2.5.5"><strong>↑_</strong></a> <a href="#r2.5.5.4">2.5.5.4.</a> <strong>Lossy vinyl rips may be trumped by substantially better-sounding lossy vinyl rips when the difference in quality is obvious.</strong> To trump an old rip with a new torrent that may be a better sounding version, you need to report it with clear information about how your rip sounds better than the other one, with references to specific tracks and time positions to justify your report (see <a href="#r2.2.11.9">2.2.11.9</a>). The following are acceptable reasons to both report and/or remove a torrent. - <ul> - <li id="r2.5.5.4.1"><a href="#r2.5.5.4"><strong>↑_</strong></a> <a href="#r2.5.5.4.1">2.5.5.4.1.</a> <strong>The vinyl audio was captured at the wrong playback speed.</strong> </li> - <li id="r2.5.5.4.2"><a href="#r2.5.5.4"><strong>↑_</strong></a> <a href="#r2.5.5.4.2">2.5.5.4.2.</a> <strong>At least one track has a constant level of static.</strong> </li> - <li id="r2.5.5.4.3"><a href="#r2.5.5.4"><strong>↑_</strong></a> <a href="#r2.5.5.4.3">2.5.5.4.3.</a> <strong>At least one track has a noticeable pop or click.</strong> This pop must be visible in the frequency spectrals for the track and should last 1/20th of a second.</li> - <li id="r2.5.5.4.4"><a href="#r2.5.5.4"><strong>↑_</strong></a> <a href="#r2.5.5.4.4">2.5.5.4.4.</a> <strong>There is noticeable wobbling during playback.</strong> </li> - <li id="r2.5.5.4.5"><a href="#r2.5.5.4"><strong>↑_</strong></a> <a href="#r2.5.5.4.5">2.5.5.4.5.</a> <strong>The needle drop is audible.</strong> </li> - <li id="r2.5.5.4.6"><a href="#r2.5.5.4"><strong>↑_</strong></a> <a href="#r2.5.5.4.6">2.5.5.4.6.</a> <strong>At least one track is truncated or improperly split.</strong> </li> - </ul> - </li> - <li id="r2.5.5.5"><a href="#r2.5.5"><strong>↑_</strong></a> <a href="#r2.5.5.5">2.5.5.5.</a> <strong>Rips of extremely poor quality (lossy or lossless) may be deleted outright if reported.</strong> All quality trumps/deletions of this nature are at the discretion of the moderator involved.</li> - </ul> - </li> - <li id="r2.5.6"><a href="#h2.5"><strong>↑_</strong></a> <a href="#r2.5.6">2.5.6.</a> <strong>Lossy transcodes of lossless vinyl rips trumped under rule <a href="#r2.2.11.9">2.2.11.9</a> and the rules in this section are trumpable by lossy transcodes of the trumping vinyl rips.</strong> For example, lossy torrent A and lossless torrent A exist on the site. A new vinyl rip (lossless torrent B) is uploaded and trumps lossless torrent A (the old vinyl rip). All lossy transcodes from lossless torrent B will automatically trump the corresponding lossy torrents transcoded from lossless torrent A; only the torrent B formats will remain on the site. So that the correct source for the lossless rip can be easily identified, you are strongly encouraged to mention or link to the vinyl rip that you are using for the lossless-to-lossy transcode and to reproduce its lineage in the release description of transcodes that you upload.</li> - <li id="r2.5.7"><a href="#h2.5"><strong>↑_</strong></a> <a href="#r2.5.7">2.5.7.</a> <strong>All vinyl torrents must be ripped at the correct speed.</strong> You must rip all vinyl albums using the speed at which they were intended to be played. For example, you may not rip a 45 rpm vinyl at 33 rpm and upload it to the site. In addition, turntables that have been improperly calibrated, and therefore play back LPs at an incorrect speed, will cause pitch changes that are noticeable. Any such vinyl albums that were captured at the wrong playback speed will be deleted once reported.</li> - </ul> - </div> - <h5 id="h2.6"><a href="#h2.6k"><strong>↑</strong></a> <a href="#h2.6">2.6.</a> Live Music and Soundboards</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r2.6.1"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.1">2.6.1.</a> <strong>Live soundboard material should be uploaded as one torrent per show.</strong> </li> - <li id="r2.6.2"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.2">2.6.2.</a> <strong>Only one edition of each unofficial live recording is allowed.</strong> Such bootlegs and mixtapes can be unofficially remastered several times, and such constant remastering is of little consequence on a site where bootlegs are not the primary focus. Only one unofficial soundboard recording of each show is allowed, and it should be uploaded on the torrent page in the absence of any edition information. See <a href="wiki.php?action=article&id=89">this wiki</a> for more information on how live music is organized on the site.</li> - <li id="r2.6.3"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.3">2.6.3.</a> <strong>No unofficial audience recordings.</strong> - <ul> - <li id="r2.6.3.1"><a href="#r2.6.3"><strong>↑_</strong></a> <a href="#r2.6.3.1">2.6.3.1.</a> <strong>AUD (Audience), IEM (In Ear Monitor), ALD (Assistive Listening Device), Mini-Disc, and Matrix-sourced recordings cannot be uploaded.</strong> </li> - <li id="r2.6.3.2"><a href="#r2.6.3"><strong>↑_</strong></a> <a href="#r2.6.3.2">2.6.3.2.</a> <strong>Officially-remastered AUD/IEM/ALD/Mini-Disc/Matrix recordings are allowed.</strong> These may be re-appropriated recordings released with the artist's or their label's consent. Bonus tracks from such recording sources are also exempt. Be prepared to provide clear proofs of such re-releases to the staff when asked.</li> - </ul> - </li> - <li id="r2.6.4"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.4">2.6.4.</a> <strong>Soundboards must comprise complete shows.</strong> Incomplete shows will be deleted.</li> - <li id="r2.6.5"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.5">2.6.5.</a> <strong>Soundboards may not include soundchecks.</strong> Those uploaded with a soundcheck will be deleted.</li> - <li id="r2.6.6"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.6">2.6.6.</a> <strong>One tape generation and one CD-R generation are allowed for each soundboard upload.</strong> - <ul> - <li id="r2.6.6.1"><a href="#r2.6.6"><strong>↑_</strong></a> <a href="#r2.6.6.1">2.6.6.1.</a> <strong>The tape generation must be the first generation.</strong> For example, there can be a master tape and then a subsequent tape (with no other transfer in between). Any additional tape generations beyond the secondary tape will require staff approval.</li> - <li id="r2.6.6.2"><a href="#r2.6.6"><strong>↑_</strong></a> <a href="#r2.6.6.2">2.6.6.2.</a> <strong>If a limited number of tracks (at the discretion of the staff member involved) have been patched with a short amount of tape-sourced data you may still upload the recording but this information must be clearly stated in the lineage and the album description.</strong> </li> - </ul> - </li> - <li id="r2.6.7"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.7">2.6.7.</a> <strong>Include lineage information for each soundboard recording (see <a href="#r2.3.10">2.3.10</a>).</strong> - <ul> - <li id="r2.6.7.1"><a href="#r2.6.7"><strong>↑_</strong></a> <a href="#r2.6.7.1">2.6.7.1.</a> <strong>Lineage information for a soundboard recording is highly recommended.</strong> </li> - <li id="r2.6.7.2"><a href="#r2.6.7"><strong>↑_</strong></a> <a href="#r2.6.7.2">2.6.7.2.</a> <strong>If a soundboard is uploaded without a lineage, and a lineage is later found that contains more than one tape or CD-R generation, then the uploader will be warned and the torrent deleted.</strong></li> - <li id="r2.6.7.3"><a href="#r2.6.7"><strong>↑_</strong></a> <a href="#r2.6.7.3">2.6.7.3.</a> <strong>No lineage editing or misrepresentation will be tolerated.</strong> Doing so will result in the loss of upload privileges. If you are unsure of a lineage, then do not provide it. Do NOT guess.</li> - </ul> - </li> + <li id="r2.1.1"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.1">2.1.1.</a> <strong>The only formats allowed for music are:</strong> + <ul> + <li><strong>Lossy:</strong> MP3, AAC, AC3, DTS</li> + <li><strong>Lossless:</strong> FLAC (maximum 24-bit depth, maximum 192 kHz sampling rate)</li> + </ul> + <span style="font-style: italic;">Only standard versions of each format are allowed. Hybrid formats that combine both lossless and lossy audio data in the same file, such as DTS-HD, mp3HD, and HD-AAC, are not allowed. AC3 and DTS are reserved for commercial media sources and only if they contain such tracks; transcoding from any other source, including lossless (e.g., PCM and MLP formats), is not allowed.</span> + </li> + <li id="r2.1.2"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.2">2.1.2.</a> <strong>No transcodes or re-encodes of lossy releases are acceptable here.</strong> + <ul> + <li id="r2.1.2.1"><a href="#r2.1.2"><strong>↑_</strong></a> <a href="#r2.1.2.1">2.1.2.1.</a> <strong>The only acceptable transcodes are releases that were transcoded from a lossless source (e.g., CD, SBD, DAT, Vinyl, SACD, or LPCM).</strong> Please refer to <a href="wiki.php?action=article&id=36">this wiki</a> for more information on transcodes and how to detect them.</li> + <li id="r2.1.2.2"><a href="#r2.1.2"><strong>↑_</strong></a> <a href="#r2.1.2.2">2.1.2.2.</a> <strong>Official lossy-mastered releases are not considered transcodes.</strong> They are allowed on the site. See <a href="wiki.php?action=article&id=65">this wiki</a> for further information.</li> + <li id="r2.1.2.3"><a href="#r2.1.2"><strong>↑_</strong></a> <a href="#r2.1.2.3">2.1.2.3.</a> <strong>Releases from Bandcamp, Beatport, and similar online retailers are considered official lossy-mastered releases when lossy mastered by the artist or label.</strong> A non-lossy mastered release from any source may trump a lossy mastered release from a WEB source. And if the same WEB retailer revises their release and subsequently supplies a non-lossy mastered release, the new source may trump the original upload.</li> + </ul> + </li> + <li id="r2.1.3"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.3">2.1.3.</a> <strong>Music releases must have an average bitrate of at least 192 kbps regardless of the format.</strong> Exceptions: The following VBR encodes may go under the 192 kbps limit: LAME V2 (VBR), V1 (VBR), V0 (VBR), APS (VBR), APX (VBR), MP3 192 (VBR), and AAC ~192 (VBR) to AAC ~256 (VBR) releases. See <a href="wiki.php?action=article&id=11">this wiki</a> for more information on encoding options.</li> + <li id="r2.1.4"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.4">2.1.4.</a> <strong>Bitrates must accurately reflect encoder presets or the average bitrate of the audio files.</strong> You are responsible for supplying correct format and bitrate information on the <a href="upload.php">upload page</a>. See <a href="wiki.php?action=article&id=98">this wiki</a> for further information.</li> + <li id="r2.1.5"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.5">2.1.5.</a> <strong>Albums must not be ripped or uploaded as a single track.</strong> + <ul> + <li id="r2.1.5.1"><a href="#r2.1.5"><strong>↑_</strong></a> <a href="#r2.1.5.1">2.1.5.1.</a> <strong>If the tracks on the original CD were separate, you must rip them to separate files.</strong> Unsplit rips may be present on the site but are NOT the preferred format for lossless rips. Any unsplit FLAC rips lacking a cue sheet will be deleted outright. Any unsplit FLAC rip that includes a cue sheet will be trumpable by a properly split FLAC torrent. CDs with single tracks can be uploaded without prior splitting. See <a href="wiki.php?action=article&id=70">this wiki</a> for information on unsplit rips. Information about splitting rips can be <a href="wiki.php?action=article&id=81">found here</a>. Users who upload unsplit rips will be warned.</li> + <li id="r2.1.5.2"><a href="#r2.1.5"><strong>↑_</strong></a> <a href="#r2.1.5.2">2.1.5.2.</a> <strong>Gapless DJ or professional mixes released as MP3+CUE images are allowed as unseparated album images on the site.</strong> This includes scene DJ mixes. You cannot take a previously split collection of tracks and upload it as an unsplit mix because the original form of those audio tracks was not a single unsplit file. Such torrents will be removed when reported. Split and unsplit versions do not count as duplicates of one another and may coexist.</li> + <li id="r2.1.5.3"><a href="#r2.1.5"><strong>↑_</strong></a> <a href="#r2.1.5.3">2.1.5.3.</a> <strong>For albums that contain separate tracks, unsplit MP3 albums are allowed for continuous albums and mixes only and must include a cue sheet.</strong> If there is no reason for the album to be uploaded unsplit, or if the torrent is lacking a cue sheet, it will be deleted. Exception: If the retail version of an unsplit MP3 album is sold without a cue sheet, you may upload the unsplit rip without a cue sheet.</li> + </ul> + </li> + <li id="r2.1.6"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.6">2.1.6.</a> <strong>All music torrents must be encoded with a single encoder using the same settings.</strong> + <ul> + <li id="r2.1.6.1"><a href="#r2.1.6"><strong>↑_</strong></a> <a href="#r2.1.6.1">2.1.6.1.</a> <strong>This means that you cannot create a torrent that contains both CBR and VBR files, nor can you upload torrents containing a mix of APS (VBR)/V2 (VBR) and APX (VBR)/V0 (VBR) files.</strong> Likewise, this means that you cannot upload torrents containing files of varying bit depths or sampling rates. Exceptions: If the source media was delivered in different bit depths (e.g., 16-bit and 24-bit) or sampling rates (e.g., 48 kHz and 96 kHz) you may upload a torrent containing audio files with different attributes.</li> + <li id="r2.1.6.2"><a href="#r2.1.6"><strong>↑_</strong></a> <a href="#r2.1.6.2">2.1.6.2.</a> <strong>Some WEB-sourced albums are only available in hybrid collections (e.g., albums consisting of both 24-bit files and 16-bit files, 96 kHz-sampled and 48 kHz-sampled files, or stereophonic and monophonic files).</strong> If the online retailer supplies the files with such inconsistency, they are acceptable on the site. When requested by the staff, you must be able to provide proof of the WEB-sourcing in the form of web store receipts and album information indicating the non-uniform nature of the audio files. Label them according to the highest bit depth or sampling rate and be sure to note the variation in audio quality in the torrent description (e.g., Tracks 1, 2, and 3 are encoded at 24 bits while Tracks 4, 5, 6, and 7 are encoded at 16 bits).</li> + <li id="r2.1.6.3"><a href="#r2.1.6"><strong>↑_</strong></a> <a href="#r2.1.6.3">2.1.6.3.</a> <strong>Including other kinds of audio quality differences in a torrent (e.g., duplicate lossy files in a lossless torrent) is prohibited.</strong> This kind of release is referred to as a "mutt rip".</li> + </ul> + </li> + <li id="r2.1.7"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.7">2.1.7.</a> <strong>Use only the allowed container formats for audio files.</strong> Use .m4a and .mp4 for AAC, and .flac for FLAC only. All other formats should not be encapsulated in random containers (e.g., FLAC must not be in an Ogg container, MP3 must not be in an .m4a container, and so forth). Exceptions: DTS CD-sourced audio rips, although contained in WAV, should have the .dts extension (see <a href="#h2.7">2.7</a>).</li> + <li id="r2.1.8"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.8">2.1.8.</a> <strong>Music not sourced from vinyl must not contain pops, clicks, or skips.</strong> They will be deleted for rip/encode errors if reported. Music sourced from vinyl must follow the rules <a href="#h2.5">found here</a>.</li> + <li id="r2.1.9"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.9">2.1.9.</a> <strong>Freely available music is allowed.</strong> Uploaded music may be freely available on the web (come from official sources such as record labels, band web sites, or the <a href="https://www.archive.org/index.php" target="_blank">Internet Archive</a>). Uploads can come from other torrent sites, but you are responsible for determining the audio quality of the music (e.g., that it is not transcoded, that it does not contain an edited log, that it is not a user compilation, etc.). Users are highly encouraged to provide a link to the source of their upload when uploading freely available music. However, this is not required and the lack of such a link to further information is not grounds for reporting a torrent. All freely available music must conform to both quality rules and formatting rules. This means it must be tagged correctly, not be a transcode, have separate tracks, and so forth. Freely available music uploads should have the "WEB" media format if no other source media (e.g., CD, DVD, etc.) can be established for the files.</li> + <li id="r2.1.10"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.10">2.1.10.</a> <strong>Clearly label water-marked or voice-over releases.</strong> Watermarks or voice-overs must be clearly indicated in the torrent description. The torrent will be deleted for quality misrepresentation if this information is not noted.</li> + <li id="r2.1.11"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.11">2.1.11.</a> <strong>Music ripped from the radio (Satellite or FM), television, the web, or podcasts are not allowed.</strong> See <a href="wiki.php?action=article&id=71">this wiki</a> for the difference between web rips and the WEB category.</li> + <li id="r2.1.12"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.12">2.1.12.</a> <strong>No unofficial audience recordings may be uploaded.</strong> These include but are not limited to AUD (Audience), IEM (In Ear Monitor), ALD (Assistive Listening Device), Mini-Disc, and Matrix-sourced recordings (see <a href="#r2.6.3">2.6.3</a>).</li> + <li id="r2.1.13"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.13">2.1.13.</a> <strong>Tape (VHS, Video-8, etc.) music sources are not allowed, except for cassette rips and certain soundboards.</strong> See <a href="#h2.6">2.6</a> for further information.</li> + <li id="r2.1.14"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.14">2.1.14.</a> <strong>The WEB media category is for digital downloads only.</strong> Digital downloads released only on the internet from internet sources cannot be given the CD media label on the <a href="upload.php">upload page</a>. This includes downloads from the iTunes Store, LiveDownloads, Beatport, Amazon.com, Rhapsody, and other web stores. Scene releases with no source information must be labeled as WEB. Freely available music with no source information must also be labeled as WEB. If possible, indicate the source of your files (e.g., the specific web store) in the torrent description. You are responsible for determining whether the downloaded files conform to <?=SITE_NAME?>'s rules for music quality. Note: Do not confuse WEB-sourced files with web rips (see <a href="wiki.php?action=article&id=71">this wiki</a>). WEB media torrents are not always web rips (meaning <a href="wiki.php?action=article&id=14">transcodes</a>). Please check the spectrals before assuming that they are web rips.</li> + <li id="r2.1.15"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.15">2.1.15.</a> <strong>WEB uploads must be uploaded at the bit depths and sampling rates they were sold at.</strong> WEB uploads of the same audio with different sampling rates or bit depths may coexist if that is how they are sold or distributed. This means that if, for example, an album is sold with a bit depth of 24 bits and a sampling rate of 88.2 kHz, it must be uploaded directly with no modifications to the audio stream. An existing WEB upload of the same material with a bit depth of 24 and a sampling rate of 96 kHz may coexist with the new 24/88.2 upload but does not trump it. See <a href="#r2.1.6.2">2.1.6.2</a> for more information.</li> + <li id="r2.1.16"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.16">2.1.16.</a> <strong>User-made compilations are not allowed.</strong> + <ul> + <li id="r2.1.16.1"><a href="#r2.1.16"><strong>↑_</strong></a> <a href="#r2.1.16.1">2.1.16.1.</a> <strong>These are defined as compilations made by the uploader or anyone else who does not officially represent the artist or the label. </strong> Compilations must be reasonably official. For example, "34 of my favourite Grateful Dead songs" is not a reasonably official collection. Compiling a release from a list, such as a Top 100 Billboard Chart, is not allowed. User-made and unofficial multichannel mixes are also not allowed. See <a href="wiki.php?action=article&id=72">this wiki</a> for more information.</li> + <li id="r2.1.16.2"><a href="#r2.1.16"><strong>↑_</strong></a> <a href="#r2.1.16.2">2.1.16.2.</a> <strong>Exceptions: Bootlegs are allowed if they meet quality standards for music and are proven to be retail releases in either a digital or physical form.</strong> Bootlegs/mixtapes assembled and available from internet-only sources (e.g., music blogs, message boards, etc.) are not considered official enough for this site.</li> + </ul> + </li> + <li id="r2.1.17"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.17">2.1.17.</a> <strong>No comedy, audiobook, or spoken word releases may be uploaded as music.</strong> These torrents must not be grouped with other formats from the same release (i.e., they need to have individual torrent pages). See <a href="#r3.2">3.2</a> and <a href="wiki.php?action=article&id=73">this wiki</a> for more information.</li> + <li id="r2.1.18"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.18">2.1.18.</a> <strong>Sound sample packs must be uploaded as applications.</strong> See <a href="#r4.1.9">4.1.9</a> for more information.</li> + <li id="r2.1.19"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.19">2.1.19.</a> <strong>All music torrents must represent a complete release, and may not be missing tracks (or discs in the case of a multi-disc release).</strong> + <ul> + <li id="r2.1.19.1"><a href="#r2.1.19"><strong>↑_</strong></a> <a href="#r2.1.19.1">2.1.19.1.</a> <strong>If an album is released as a multi-disc set (or box set) of CDs or vinyl discs, then it must be uploaded as a single torrent.</strong> Preferably, each individual CD rip in a multi-disc set should be organized in its own folder (see <a href="#r2.3.15">2.3.15</a>).</li> + <li id="r2.1.19.2"><a href="#r2.1.19"><strong>↑_</strong></a> <a href="#r2.1.19.2">2.1.19.2.</a> <strong>A single track (e.g., one MP3 file) cannot be uploaded on its own unless it is an officially released single.</strong> If a specific track can only be found on an album, the entire album must be uploaded in the torrent.</li> + <li id="r2.1.19.3"><a href="#r2.1.19"><strong>↑_</strong></a> <a href="#r2.1.19.3">2.1.19.3.</a> <strong>Bonus discs may be uploaded separately in accordance with <a href="#h2.4">2.4</a>.</strong> Please note that individual bonus tracks cannot be uploaded without the rest of the album. Bonus tracks are not bonus discs. Enhanced audio CDs with data or video tracks must be uploaded without the non-audio tracks. If you want to share the videos or data, you may host the files off-site with a file sharing service and include the link to that service in your torrent description.</li> + </ul> + </li> + <li id="r2.1.20"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.20">2.1.20.</a> <strong>User made discographies may not be uploaded.</strong> Multi-album torrents are not allowed on the site under any circumstances. That means no discographies, Pitchfork compilations, etc. If releases (e.g., CD singles) were never released as a bundled set, do not upload them together. Live soundboard material should be uploaded as one torrent per night, per show, or per venue. Including more than one show in a torrent results in a multi-album torrent. Exceptions: Only official boxsets and official compilation collections can contain multiple albums.</li> + <li id="r2.1.21"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.21">2.1.21.</a> <strong><a href="wiki.php?action=article&id=60">Pre-emphasis</a> is allowed in lossless torrents only.</strong> Lossless FLAC torrents with pre-emphasis are allowed on the site. They are allowed to coexist with lossless de-emphasized torrents (both in their separate album edition groups). In contrast, lossy formats may not have pre-emphasis and will be deleted if uploaded.</li> + <li id="r2.1.22"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.22">2.1.22.</a> <strong>Edition Information must be provided for digitally-sourced torrents.</strong> Digitally-sourced (including CD-sourced) rips of albums that were first released before the availability of their source medium must have accurate edition information. For example, if a CD rip is of an album whose original release date was 1957, predating the creation and distribution of CDs, then the uploader must make note of the correct year in which the CD was pressed, and preferably note the catalog identification as well. Rips for which Edition Information cannot be provided must be marked as an "Unknown Release". Under no circumstances may you guess or feign knowledge of the Edition Information. See <a href="wiki.php?action=article&id=18">this wiki</a> for more information on album editions.</li> + <li id="r2.1.23"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.23">2.1.23.</a> <strong>Audio can only be ripped from a video game CD under very specific circumstances. Audio ripped from a video game DVD is not allowed on site.</strong> + <ul> + <li id="r2.1.23.1"><a href="#r2.1.23"><strong>↑_</strong></a> <a href="#r2.1.23.1">2.1.23.1.</a> <strong>This audio layer must comply with the Red Book standard for audio data.</strong> If there is no Red Book audio on the game disc, you may not upload a rip of the disc. Be prepared to provide extensive information on any audio that is ripped from a gaming disc (see <a href="wiki.php?action=article&id=75">this wiki</a> for information about providing proof).</li> + <li id="r2.1.23.2"><a href="#r2.1.23"><strong>↑_</strong></a> <a href="#r2.1.23.2">2.1.23.2.</a> <strong>The audio must meet the minimum requirements for music on the site (see <a href="#r2.1.3">2.1.3</a> and <a href="#r2.1.6">2.1.6</a>).</strong> </li> + <li id="r2.1.23.3"><a href="#r2.1.23"><strong>↑_</strong></a> <a href="#r2.1.23.3">2.1.23.3.</a> <strong>Officially-released game soundtracks, which adhere to Red Book standards, are always allowed.</strong> </li> + </ul> + </li> + <li id="r2.1.24"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.24">2.1.24.</a> <strong>AAC torrents may only be uploaded if they represent editions unavailable on Orpheus in any other format sourced from the same medium and edition (e.g., an iTunes AAC WEB release can co-exist with a CD release of the same edition).</strong> See <a href="#r2.2.9.11">2.2.9.11</a> for information about trumping AAC torrents.</li> + <li id="r2.1.25"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.25">2.1.25.</a> <strong>No HDCD content may be uploaded to the site.</strong> There are inherent problems with ripping this format, and other forms of high-resolution audio are both more prevalent and preferred at this time (e.g., vinyl, SACD, Blu-ray, etc.). Any CD bearing the HDCD logo can still be ripped using the usual methods for ripping normal audio CDs. See <a href="wiki.php?action=article&id=76">this wiki</a> for more details on the media format.</li> + <li id="r2.1.26"><a href="#h2.1"><strong>↑_</strong></a> <a href="#r2.1.26">2.1.26.</a> <strong> Downsampling of digital sources is allowed for certain media types.</strong> Downsampling of CD sources is strictly prohibited. Downsampling of WEB sources is allowed for audio files sampled at 88.2 kHz and above. If the WEB source is already sampled at a rate of either 44.1 kHz or 48 kHz you may not downsample the audio files any further. Downsampling of SACD, DVD, or Blu-ray sources is allowed in certain cases. See <a href="#r2.8.5">2.8.5</a>, <a href="#r2.7.3">2.7.3</a>, and <a href="#r2.9.5">2.9.5</a> for more information.</li> + </ul> + </div> + <h5 id="h2.2"><a href="#h2.2k"><strong>↑</strong></a> <a href="#h2.2">2.2.</a> Duplicates & Trumping</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r2.2.0"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.0">2.2.0.</a> <strong>Overview</strong> + <p style="text-align: center;"> + <img src="static/common/trumpchart.png" alt="Audio Dupe and Trump Chart" /><br /><br /> + <em>This chart is an overview of how the dupe and trump rules work.</em> + </p> + </li> + <li id="r2.2.1"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.1">2.2.1.</a> <strong>Upload an allowed format if it doesn't already exist on the site.</strong> If there is no existing torrent of the album in the format you have chosen, you can upload it in any bitrate that averages at least 192 kbps.</li> + <li id="r2.2.2"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.2">2.2.2.</a> <strong>Torrents that have the same bitrates, formats, and comparable or identical sampling rates for the same music release are duplicates.</strong> If a torrent is already present on the site in the format and bitrate you wanted to upload, you are not allowed to upload it - it's a duplicate (dupe). So if there's a 192 kbps CBR MP3 torrent on the site, you are not allowed to upload another 192 kbps CBR MP3 torrent for the same release. Similarly, if a torrent is already present on the site in the format and at the sampling rate you wanted to upload, you are not allowed to upload it - it's a duplicate (dupe). So if there's a 48 kHz WEB FLAC torrent on the site, you are not allowed to upload a 44.1 kHz WEB FLAC torrent for the same release. You cannot trump an existing torrent with another version solely because the new version includes album art. Exceptions: Different editions and source media do not count as dupes. See <a href="#r2.2.11">2.2.11</a> for more information.</li> + <li id="r2.2.3"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.3">2.2.3.</a> <strong>Report all trumped and duplicated torrents.</strong> If you trump a torrent or notice a duplicate torrent, please use the report link (RP) to notify staff for removal of the old or duplicate torrent. This process is particularly vital for lossless rips. If you are uploading a superior rip to the current one in the same format on the site, report the older torrent and include a link to your new torrent in the report. Your torrent will be deleted as a dupe if the older torrent is not reported. Note: Trump - This occurs when a new torrent is uploaded in a preferred bitrate or quality (as specified by the lossy-, lossless-, and edition-specific rules below) and replaces the older version that exists on the site. Dupe - This occurs when a new torrent is uploaded in a bitrate or quality that is equal to the existing older version on the site. Because the two torrents cannot coexist, the most recent upload is considered a duplicate.</li> + <li id="r2.2.4"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.4">2.2.4.</a> <strong>Dupe rules also apply to previous uploads.</strong> All of the dupe rules below concerning music formats may apply to torrents uploaded at an earlier time. For example, if you upload an album in 192 kbps MP3, someone can upload the same album in 320 kbps at a later date and trump your 192 kbps MP3.</li> + <li id="r2.2.5"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.5">2.2.5.</a> <strong>Lossless scene and non-scene torrents for the same release and bitrate are dupes.</strong> If a scene FLAC of an album from CD is already uploaded, it can be trumped by a another FLAC of the same album when accompanied by a checksummed log scoring 100%. Lossy scene torrents are trumped by non-scene torrents. For example, if a scene V2 (VBR) of an album from CD is already uploaded, it may be trumped by upload another V2 (VBR) of the same album that has been transcoded from a 100% checksum CD rip. The only exception is for Scene rips that include a 100% checksum log, in which case they cannot be trumped.</li> + <li id="r2.2.6"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.6">2.2.6.</a> <strong>All vinyl torrents must be ripped at the correct speed.</strong> You must rip all vinyl albums at the speed with which they were intended to be played (see <a href="#r2.5.7">2.5.7</a>).</li> + <li id="r2.2.7"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.7">2.2.7.</a> <strong>Complete or untouched releases replace incomplete or watermarked versions.</strong> Watermarked promos containing voice-overs and similar imperfections can be trumped by a non-watermarked release. Releases missing hidden or pre-gap tracks can be replaced by proper rips that include the entire range of tracks. These hidden or pre-gap tracks should be in their own file, not appended to the beginning of track one. Releases where the hidden or pre-gap track is appended to the first track can be trumped by one where the track is in a separate file.</li> + <li id="r2.2.8"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.8">2.2.8.</a> <strong>Torrents that have been inactive (e.g., not seeded) for two weeks may be trumped by the identical torrent (e.g., reseeded) or by a brand new rip or encode (see <a href="wiki.php?action=article&id=77">this wiki</a> for the torrent inactivity rules) of the album.</strong> If you have the original torrent files for the inactive torrent, it is preferable to reseed those original files instead of uploading a new torrent. Uploading a replacement torrent should be done only when the files from the original torrent cannot be recovered or are unavailable.</li> + <li id="r2.2.9"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.9">2.2.9.</a> <strong>Lossy rules</strong> + <ul> + <li id="r2.2.9.1"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.1">2.2.9.1.</a> <strong>Higher bitrate CBR (constant bitrate) and ABR (average bitrate) torrents replace lower ones.</strong> Once a CBR (constant bitrate) rip has been uploaded, no CBR rips of that bitrate or lower can be uploaded. In the same manner, once an ABR (average bitrate) rip has been uploaded, no ABR rips of that bitrate or lower can be uploaded. For example, if a 320 kbps CBR rip is already on the site, you are not allowed to upload a 256 kbps CBR. ABR and CBR may be interchangeably trumped. A CBR can trump a lower bitrate ABR and an ABR may trump a lower bitrate CBR. You may not upload a 192 kbps CBR if a 256 kbps ABR of the same release is already up. For more information, see the Wikipedia articles on <a href="https://en.wikipedia.org/wiki/Average_bitrate" target="_blank">average bitrate</a>, <a href="https://en.wikipedia.org/wiki/Constant_bitrate" target="_blank">constant bitrate</a>, and <a href="https://en.wikipedia.org/wiki/Variable_bitrate" target="_blank">variable bitrate</a>.</li> + <li id="r2.2.9.2"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.2">2.2.9.2.</a> <strong>Lossy format torrents with .log files do not replace equivalent existing torrents.</strong> If you want to upload a lossy format (e.g., MP3) torrent that contains a log, when that format already exists in the same medium and album edition (but without the log), your new torrent will be considered a dupe. For example, a V2 (VBR) MP3 torrent with a log cannot replace a V2 (VBR) MP3 without a log. The same standard applies for lossy audio torrents with .m3u, .cue, and other additions such as artwork. Exceptions: If an existing torrent contains encode/rip errors, you may upload another copy that does not have errors. The bad torrent needs to be reported (with clear information about which tracks and what time points are affected) or your torrent will be deleted as a dupe. Also, see <a href="#r2.3.8">2.3.8</a> through <a href="#r2.3.16">2.3.16</a> for replacing poorly named and tagged torrents.</li> + <li id="r2.2.9.3"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.3">2.2.9.3.</a> <strong>V0 (VBR), V2 (VBR), and 320 CBR MP3 are allowed at any time.</strong> You may upload a V0 (VBR), V2 (VBR) or 320 CBR MP3 as long as another rip with the same bitrate and format doesn't already exist. So if a V0 (VBR) is on the site, you may still upload a V2 (VBR) or 320 CBR MP3 of the same release.</li> + <li id="r2.2.9.4"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.4">2.2.9.4.</a> <strong>V2 (VBR) replaces APS (VBR), and both replace CBR rips under 256 kbps, 192 (ABR), and rips averaging 192 (VBR) to 210 (VBR).</strong> Once a rip with either the V2 (VBR) or APS (VBR) LAME encoding preset has been uploaded, you are not allowed to upload any CBR torrents under 256 kbps bitrate. Furthermore, a V2 (VBR) or APS (VBR) rip will trump all indiscriminate VBR rips of similar bitrate. A V2 (VBR) rip will replace a 192 (VBR) rip or 210 (VBR) rip, for example. Also, a V2 (VBR) will replace a 192 (ABR) torrent.</li> + <li id="r2.2.9.5"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.5">2.2.9.5.</a> <strong>V0 (VBR) replaces APX (VBR), and both replace CBR rips under 320 kbps, V1 (VBR), all (ABR) rips, and all other non-LAME preset (VBR) rips.</strong> Once a rip with either the V0 (VBR) or APX (VBR) LAME encoding preset has been uploaded, you are not allowed to upload any CBR torrents under 320 kbps bitrate. Furthermore, a V0 (VBR) or APX (VBR) rip will trump all indiscriminate VBR rips (of 192 kbps average bitrate or higher). A V0 (VBR) rip will replace a 256 (VBR) rip, for example. You may not upload a V1 (VBR) LAME encoding preset if either V0 (VBR) or APX (VBR) are already present.</li> + <li id="r2.2.9.6"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.6">2.2.9.6.</a> <strong>No constant bitrate encodes higher than 320 CBR are allowed on the site.</strong> You may upload MP3 files from 192 kbps to 320 kbps if no torrent exists in that format for a release. Any CBR bitrates under 192 kbps and bitrates higher than 320 kbps will be deleted. See <a href="#r2.2.9.1">2.2.9.1</a> for more information.</li> + <li id="r2.2.9.7"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.7">2.2.9.7.</a> <strong>Non-LAME preset VBR rips do not replace each other.</strong> Similar bitrate VBR encodes are considered dupes. Thus, a 192 (VBR) is allowed to coexist with a significantly higher VBR encode. You may not upload a 224 (VBR) if a 256 (VBR) is already present, nor vice versa.</li> + <li id="r2.2.9.8"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.8">2.2.9.8.</a> <strong>V2 (VBR) and V0 (VBR) LAME encodes trump APS (VBR) and APX (VBR) respectively.</strong> V2 (VBR) trumps an APS (VBR) encode of the same release. Once a V2 (VBR) is uploaded, you may not upload an APS (VBR) encode. V0 (VBR) trumps an APX (VBR) encode of the same release. Once a V0 (VBR) is uploaded, you may not upload an APX (VBR) encode.</li> + <li id="r2.2.9.9"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.9">2.2.9.9.</a> <strong>Encoding profiles/object types at the same bitrate are not unique uploads.</strong> A 256 (VBR) in LC profile is a dupe if a 256 (VBR) HE object type encode already exists on the site. Similarly, different extensions (.m4a and .mp4) count as dupes if available in the same bitrate.</li> + <li id="r2.2.9.10"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.10">2.2.9.10.</a> <strong>VBR AAC encodes can be trumped by 256 CBR encodes.</strong> Any VBR AAC encode, regardless of encoder used, may be deleted in favor of 256 CBR encodes. This includes 320 (VBR) encodes or encodes made with Nero presets.</li> + <li id="r2.2.9.11"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.11">2.2.9.11.</a> <strong>An AAC encode can be trumped by an allowed encode of the same medium and edition.</strong> Note: For WEB torrents, comparisons of audio quality and source reliability will determine whether or not an MP3 may trump an AAC (at the discretion of the moderator involved).</li> + <li id="r2.2.9.12"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.12">2.2.9.12.</a> <strong>Only one lossy vinyl rip in a specific bitrate is allowed per edition.</strong> See <a href="#r2.5.2">2.5.2</a>.</li> + <li id="r2.2.9.13"><a href="#r2.2.9"><strong>↑_</strong></a> <a href="#r2.2.9.13">2.2.9.13.</a> <strong>Any lossy formats must be encoded at the same sampling rate as their lossless source.</strong> FLAC files from a CD rip (sampled at 44.1 kHz) can only be transcoded to 44.1 kHz lossy formats. Any 48 kHz lossy formats for the same release that are uploaded after the 44.1 kHz torrents are on the site are considered dupes. FLAC files from a WEB FLAC (sampled at 48 kHz) can only be transcoded to 48 kHz lossy formats. If the 48 kHz FLAC torrent is uploaded, and the lossy formats are populated for this album at 48 kHz, no 44.1 kHz torrents (lossless or lossy) may be uploaded because those later torrents will be considered dupes.</li> + </ul> + </li> + <li id="r2.2.10"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.10">2.2.10.</a> <strong>Lossless rules</strong> + <ul> + <li id="r2.2.10.1"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.1">2.2.10.1.</a> <strong>All FLAC CD rips must come from official CD sources.</strong> + <ul> + <li id="r2.2.10.1.1"><a href="#r2.2.10.1"><strong>↑_</strong></a> <a href="#r2.2.10.1.1">2.2.10.1.1.</a> <strong>Rips must be taken from commercially pressed or official (e.g., artist- or label-approved) CD sources.</strong> They may not come from CD-R copies of the same pressed CDs.</li> + <li id="r2.2.10.1.2"><a href="#r2.2.10.1"><strong>↑_</strong></a> <a href="#r2.2.10.1.2">2.2.10.1.2.</a> <strong>Exceptions: If the release is only distributed on CD-R by the label or artist, then that is acceptable.</strong> See <a href="wiki.php?action=article&id=78">this wiki</a> for more information on CD-R releases.</li> + </ul> + </li> + <li id="r2.2.10.2"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.2">2.2.10.2.</a> <strong>A FLAC torrent without a log (or with a log from a non-EAC, non-XLD, or non-whipper ripping tool like dBpoweramp or Rubyripper) may be trumped by a FLAC torrent with a log from an approved ripping tool that scores either 100% or <100% because of non-audio deductions.</strong> For example, an <a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a> log that scores 50% (because of CRC mismatches or other audio deductions) cannot trump a Rubyripper log. See <a href="wiki.php?action=article&id=97">this wiki</a> for more information on approved ripping tools. Also, see <a href="wiki.php?action=article&id=23">this wiki</a> for setting up and ripping with <a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a>. See <a href="wiki.php?action=article&id=22">this wiki</a> for ripping with <a href="http://tmkk.undo.jp/xld/index_e.html" target="_blank">XLD</a>. Please make sure the log file has the ".log" extension so that it can display properly on the torrent page.</li> + <li id="r2.2.10.3"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.3">2.2.10.3.</a> <strong>A FLAC upload with an <a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a>, <a href="http://tmkk.undo.jp/xld/index_e.html" target="_blank">XLD</a>, or <a href="https://github.com/whipper-team/whipper" target="_blank">whipper</a> rip log that scores 100% on the <a href="logchecker.php">log checker</a> replaces one with a lower score.</strong> No log scoring less than 100% can trump an already existing one that scores under 100%. For example, a FLAC+log rip that scores 50% in the log checker cannot be trumped by a FLAC+log rip that scores 80%. <?=SITE_NAME?> recommends <a href="wiki.php?action=article&id=23">this guide for EAC</a>, <a href="wiki.php?action=article&id=22">this guide for XLD</a>, and <a href="wiki.php?action=article&id=126">this guide for whipper</a>. See <a href="wiki.php?action=article&id=44">this wiki</a> for more information on the site log checker. Note: A FLAC upload with a log that scores 95% for not defeating the audio cache may be rescored to 100% following the procedure outlined in <a href="wiki.php?action=article&id=79">this wiki</a>.</li> + <li id="r2.2.10.4"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.4">2.2.10.4.</a> <strong>A FLAC upload with <span style="font-weight: bold;">only non-audio deductions</span> in the log (see <a href="wiki.php?action=article&id=80">this wiki</a>) may replace a FLAC upload with a log that scores 0% or contains audio deductions.</strong> For example, a range rip that has AccurateRip scores of two or higher and assorted non-audio deductions (e.g., burst mode, audio cache, pre-gaps, C2 pointers, etc.) can trump a log that has audio deductions for mismatching CRCs.</li> + <li id="r2.2.10.5"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.5">2.2.10.5.</a> <strong><a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a> logs in languages other than English are automatically parsed to English for the logging process, while <a href="https://github.com/whipper-team/whipper" target="_blank">whipper</a> logs should always be in English. <a href="http://tmkk.undo.jp/xld/index_e.html" target="_blank">XLD</a> logs in languages other than English require a manual log checker score adjustment by staff.</strong>. The current log checker cannot parse XLD logs in other languages. A special exception is made for foreign-language logs since the rip quality is equivalent to English logs. Please report your torrent with the "RP" button and select "Log Rescore Request" for the reason of the report so staff can score your torrent manually. <span style="font-weight: bold;">Do not translate the log yourself</span> (see <a href="#r2.2.10.9">2.2.10.9</a>).</li> + <li id="r2.2.10.6"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.6">2.2.10.6.</a> <strong>Range rips of hidden tracks or regular range rips (acceptable under strict conditions) require manual score adjustment.</strong> + <ul> + <li id="r2.2.10.6.1"><a href="#r2.2.10.6"><strong>↑_</strong></a> <a href="#r2.2.10.6.1">2.2.10.6.1.</a> <strong>The new log checker cannot accurately score range-ripped hidden tracks appended to proper rip logs.</strong> If you have created a 100% rip with a hidden track, but the log checker has decreased your score for the hidden track, report the torrent and the log score will be corrected by staff.</li> + <li id="r2.2.10.6.2"><a href="#r2.2.10.6"><strong>↑_</strong></a> <a href="#r2.2.10.6.2">2.2.10.6.2.</a> <strong>If you created a CD range rip that has matching CRCs for test and copy, and where every track has an AccurateRip score of 2 or more, then you may submit your torrent for manual score adjustment.</strong> It will be rescored, assuming no other problems, to 99%.</li> + <li id="r2.2.10.6.3"><a href="#r2.2.10.6"><strong>↑_</strong></a> <a href="#r2.2.10.6.3">2.2.10.6.3.</a> <strong>The CD image rip must be split with <a href="http://www.cuetools.net/wiki/CUETools" target="_blank">CUETools</a>, <a href="http://tmkk.undo.jp/xld/index_e.html" target="_blank">XLD</a>, or <a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a>.</strong> No other splitter is acceptable for a score adjustment. You will not receive a score adjustment for copy-only range rips approved with AccurateRip, nor for range rips done with test and copy without AccurateRip enabled. See <a href="wiki.php?action=article&id=82">this wiki</a> for information on splitting image rips with EAC.</li> + </ul> + </li> + <li id="r2.2.10.7"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.7">2.2.10.7.</a> <strong>A 100% log rip lacking a cue sheet can be replaced by another 100% log rip with a noncompliant cue sheet.</strong> See <a href="wiki.php?action=article&id=74">this wiki</a> for more information on cue sheets. + <ul> + <li id="r2.2.10.7.1"><a href="#r2.2.10.7"><strong>↑_</strong></a> <a href="#r2.2.10.7.1">2.2.10.7.1.</a> <strong>A 100% log rip without a cue sheet can be replaced by a 100% log rip with a noncompliant cue sheet ONLY when the included cue sheet is materially different from "a cue generated from the ripping log". Examples of a material difference include additional or correct indices, properly detected pre-gap lengths, and <a href="wiki.php?action=article&id=60">pre-emphasis</a> flags.</strong> If you upload a torrent with a cue sheet that provides nothing additional beyond what is contained in the rip log of the preexisting torrent, it will be deleted as a dupe.</li> + <li id="r2.2.10.7.2"><a href="#r2.2.10.7"><strong>↑_</strong></a> <a href="#r2.2.10.7.2">2.2.10.7.2.</a> <strong>Exceptions: An <a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a> 0.95 rip with a 100% log and no cue file, uploaded before September 14, 2010, may be trumped by a torrent that scores 100% under the current log checker requirements.</strong> </li> + </ul> + </li> + <li id="r2.2.10.8"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.8">2.2.10.8.</a> <strong>FLAC rips that contain ID3 tags or other non-compliant tags for FLAC may be trumped by rips with identical scores that have the faulty tags removed and replaced with the standard for each format.</strong> Enabling ID3 tags in <a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a> when ripping to FLAC may prevent some players from playing the files because of the inclusion of ID3 headers. If you wish to trump a FLAC rip that was ripped with ID3 tags enabled, upload the corrected torrent with the proper Vorbis comments and report the old torrent. Add information about your tag clean-up in the "Release description", or your torrent may be deleted as a dupe. Do not edit the log and change the ID3 tag setting to "No". Note: A simple way of getting rid of the ID3 header is to decompress the files to WAV, then compressing the files to FLAC, and adding the proper Vorbis comments.</li> + <li id="r2.2.10.9"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.9">2.2.10.9.</a> <strong>No log editing is permitted.</strong> + <ul> + <li id="r2.2.10.9.1"><a href="#r2.2.10.9"><strong>↑_</strong></a> <a href="#r2.2.10.9.1">2.2.10.9.1.</a> <strong>Forging log data is a serious misrepresentation of quality, and will result in a warning and the loss of your uploading privileges when the edited log is found.</strong> We recommend that you do not open the rip log file for any reason. However, if you must open the rip log, do not edit anything in the file for any reason. If you discover that one of your software settings is incorrect in the ripping software preferences, you must rip the CD again with the proper settings. Do not consolidate logs under any circumstances. If you must re-rip specific tracks or an entire disc and the rip results happen to have the new log appended to the original, leave them as is. Do not remove any part of either log, and never copy/paste parts of a new log over an old log.</li> + <li id="r2.2.10.9.2"><a href="#r2.2.10.9"><strong>↑_</strong></a> <a href="#r2.2.10.9.2">2.2.10.9.2.</a> <strong>If you find that an appended log has not been scored properly, please report the torrent and use the log rescore option.</strong> </li> + </ul> + </li> + <li id="r2.2.10.10"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.10">2.2.10.10.</a> <strong>FLAC files must be compressed.</strong> We strongly recommend that FLAC files be compressed to Level 8. Lack of compression can increase the torrent size by 5% or more. Any torrents that have uncompressed FLAC files may be reported and trumped.</li> + <li id="r2.2.10.11"><a href="#r2.2.10"><strong>↑_</strong></a> <a href="#r2.2.10.11">2.2.10.11.</a> <strong>Any lossless formats must be encoded at the same sampling rate as their lossless source material.</strong> Lossless files from a Red Book-compliant CD rip (sampled at 44.1 kHz) can only be encoded to 44.1 kHz FLAC files for uploading in lossless torrents. Any 48 kHz lossless torrents for the same release are considered dupes. FLAC files from a WEB FLAC (sampled at 48 kHz) can only be uploaded as 48 kHz FLAC torrents. Any 44.1 kHz torrents for the same release are considered dupes. When albums are available in a number of different lossless formats (44.1 kHz, 48 kHz, 96 kHz, etc.) the first lossless torrent that is uploaded to the site may remain while all subsequent versions are considered dupes. See <a href="#r2.2.2">2.2.2</a> and <a href="#r2.2.9.13">2.2.9.13</a> for more information.</li> + </ul> + </li> + <li id="r2.2.11"><a href="#h2.2"><strong>↑_</strong></a> <a href="#r2.2.11">2.2.11.</a> <strong>Editions and Releases rules</strong> + <ul> + <li id="r2.2.11.1"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.1">2.2.11.1.</a> <strong>Different editions and source media count as separate releases.</strong> + <ul> + <li id="r2.2.11.1.1"><a href="#r2.2.11.1"><strong>↑_</strong></a> <a href="#r2.2.11.1.1">2.2.11.1.1.</a> <strong>A rip from a different medium (e.g., vinyl) or release (e.g., a remaster) of an already existing torrent counts as a different edition.</strong> The dupe rules do not apply to the two different album editions, nor do they apply to two differently sourced torrents. So if a FLAC ripped from a CD is already up, you are still allowed to upload a FLAC ripped from vinyl. And if a 320 kbps CBR MP3 release for an original mastering of an album was uploaded, you are still allowed to upload a 320 kbps CBR MP3 remaster. See <a href="wiki.php?action=article&id=18">this wiki</a> for the procedure on how to enter album edition information for a torrent.</li> + <li id="r2.2.11.1.2"><a href="#r2.2.11.1"><strong>↑_</strong></a> <a href="#r2.2.11.1.2">2.2.11.1.2.</a> <strong>Only one edition of each unofficial live recording is allowed.</strong> See <a href="#r2.6.2">2.6.2</a>.</li> + </ul> + </li> + <li id="r2.2.11.2"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.2">2.2.11.2.</a> <strong>Rip log information (ToC, peak levels, and pre-gaps), tracklist, and running order determine distinct editions, not catalog information.</strong> Merely having different catalog numbers or CD packaging is not enough to justify a new, distinct edition, though differences in year, label (imprint), or catalog number determine distinct releases. Though different editions may coexist, distinct releases that are not also distinct editions may not, and are considered dupes. See <a href="wiki.php?action=article&id=18">this wiki</a> for more information.</li> + <li id="r2.2.11.3"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.3">2.2.11.3.</a> <strong>Multiple releases may be grouped into one edition.</strong> In such a case, torrents belonging to any of the releases determined to be in the same edition are considered dupes. Conversely, release groups may be split into more than one edition when it is determined that there is a material difference in audio content (different editions that have the same catalog information are known as "silent remasters"), in which case other formats may be uploaded for both editions.</li> + <li id="r2.2.11.4"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.4">2.2.11.4.</a> <strong>Lossless uploads with edition information trump lossless rips with nonexistent or insufficient logs and no release information.</strong> An insufficient log is a log that doesn't provide any information (e.g., ToC, peak levels, and pre-gaps) about the audio being ripped.</li> + <li id="r2.2.11.5"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.5">2.2.11.5.</a> <strong>If a lossless torrent has no release information (an "unknown release") but does have a log containing ToC, peak level, and pre-gap information, it cannot be trumped by a lossless torrent with release information unless the "lossless unknown release torrent" can be determined to be the same edition as a lossless torrent for which the release information is known.</strong> If such an identification can be made, the "lossless unknown release torrent" (and all lossy torrents that can be identified as transcodes of it) is moved into the same edition as the lossless torrent with edition information, and the merged torrents are then subject to dupe and trump rules as per <a href="#r2.2.11.2">2.2.11.2</a>. If the rip log is incomplete (missing ToC, peak level, or pre-gap information) and the information available matches a torrent in an edition whose release information is known, the torrent whose release information is known trumps the torrent whose release information is unknown.</li> + <li id="r2.2.11.6"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.6">2.2.11.6.</a> <strong>Lossy torrents with release information trump unknown release torrents if they share the same tracklist and running order, with one exception.</strong> If it can be established that a lossy Unknown Release torrent is a transcode of an Unknown Release FLAC, and the Unknown Release FLAC is moved into an edition whose release information is known as per <a href="#r2.2.11.5">2.2.11.5</a>, then all lossy transcodes of it are moved with it and are treated as any other lossy torrents already present in the edition as per <a href="#r2.2.11.2">2.2.11.2</a>.</li> + <li id="r2.2.11.7"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.7">2.2.11.7.</a> <strong>Bonus disc-only uploads can coexist with the complete set in accordance with the trumping rules.</strong> A bonus disc-only release can be trumped by an upload containing the "full" original album + bonus discs, in the same format, in accordance with the usual trumping rules (see <a href="#h2.4">2.4</a> for the rules specific to bonus materials).</li> + <li id="r2.2.11.8"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.8">2.2.11.8.</a> <strong>Unknown Release torrents may be trumped by seemingly identical torrents whose Edition Information can be verified.</strong> Torrents marked as "Unknown Release" are eligible to be trumped by rips sourced from the same medium, with the same track listing and running order, whose source Edition Information is provided and can be verified.</li> + <li id="r2.2.11.9"><a href="#r2.2.11"><strong>↑_</strong></a> <a href="#r2.2.11.9">2.2.11.9.</a> <strong>Only one lossless and two 24-bit lossless vinyl rips are allowed per edition.</strong> A poor sounding lossless rip may be trumped by a better sounding lossless rip, regardless of lineage information. The same quality trump can occur for 24-bit lossless rips. To trump an older torrent for a better sounding version, <span style="font-weight: bold;">you need to report it with clear information about how your rip sounds better than the other one</span> (e.g., specific tracks where the audio is appreciably improved, specific time points that demonstrate the improvement, etc.). Rips of extremely poor quality may be deleted outright if reported. All quality trumps/deletions of this nature are at the discretion of the moderator involved. See <a href="#h2.5">2.5</a>.</li> + </ul> + </li> + </ul> + </div> + <h5 id="h2.3"><a href="#h2.3k"><strong>↑</strong></a> <a href="#h2.3">2.3.</a> Formatting</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r2.3.1"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.1">2.3.1.</a> <strong>Music releases must be in a directory that contains the music.</strong> This includes single track releases, which must be enclosed in a torrent folder even if there is only one file in the torrent. No music may be compressed in an archive (e.g., .rar, .zip, .tar, .iso). Scene archives of music must be unpacked and not labeled as "scene".</li> + <li id="r2.3.2"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.2">2.3.2.</a> <strong>Name your directories with meaningful titles, such as "Artist - Album (Year) - Format". The minimum acceptable is "Album" although it is preferable to include more information.</strong> If the directory name does not include this minimum then another user can rename the directory, re-upload, and report your torrent for deletion. In addition, torrent folders that are named using the scene convention will be trumpable if the Scene label is absent from the torrent.</li> + <li id="r2.3.3"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.3">2.3.3.</a> <strong>Avoid creating unnecessary nested folders (such as an extra folder for the actual album) inside your properly named directory.</strong> A torrent with unnecessary nested folders is trumpable by a torrent with such folders removed. For single disc albums, all audio files must be included in the main torrent folder. For multi-disc albums, the main torrent folder may include one sub-folder that holds the audio file contents for each disc in the box set, e.g., the main torrent folder is "<samp>Adele - 19 (2008) - FLAC</samp>" while appropriate sub-folders may include "<samp>19 (Disc 1of2)</samp>" or "<samp>19</samp>" and "<samp>Live From The Hotel Cafe (Disc 2of2)</samp>" or "<samp>Acoustic Set Live From The Hotel Cafe, Los Angeles</samp>". Additional folders are unnecessary because they do nothing to improve the organization of the torrent. If you are uncertain about what to do for other cases, PM a staff member for guidance.</li> + <li id="r2.3.4"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.4">2.3.4.</a> <strong>Label your torrents according to site standards.</strong> Follow the <a href="wiki.php?action=article&id=100">torrent naming guide</a> for help on how to name your uploaded torrents properly. Use the Edition Information box on the <a href="upload.php">upload page</a> to denote different editions or versions of an album (e.g., censored version versus an uncensored version). If you need help merging or editing your upload, please request help in <a href="forums.php?action=viewforum&forumid=34">this forum</a>. For the album category/release type, follow the <a href="wiki.php?action=article&id=58">guidelines here</a>.</li> + <li id="r2.3.5"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.5">2.3.5.</a> <strong>Torrents should never have [REQ] or [REQUEST] in the title or artist name.</strong> If you fill a request using the <a href="requests.php">Requests system</a>, everyone who voted for it will be automatically notified.</li> + <li id="r2.3.6"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.6">2.3.6.</a> <strong>Torrent album titles must accurately reflect the actual album titles.</strong> Use proper capitalization when naming your albums. Typing the album titles in all lowercase letters or all capital letters is unacceptable and makes the torrent trumpable. For detailed information on naming practices see <a href="wiki.php?action=article&id=42">this wiki</a>. Any descriptions like [Advance] or [CDM] (if you must use them) should be entered in the Edition Information box on the <a href="upload.php">upload page</a>, not in the title. Exceptions: If the album uses special capitalization, then you may follow that convention.</li> + <li id="r2.3.7"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.7">2.3.7.</a> <strong>The Artist field in the torrent name should contain only the artist name.</strong> Do not add additional information about the artist in the artist field unless the album credits the artist in that manner. For example, "Artist X (of Band Y)" or "Band X (feat. Artist Y)". It is recommended that you search existing torrents for the artist name so that you can be sure that you name the artist the exact same way. A torrent with a proper artist name will be grouped with the existing torrents for that artist on a common artist page, and thus will be easy to find. Capitalization problems will also make a torrent trumpable. Labeling the artist incorrectly prevents your torrent from being grouped with the other torrents for the same artist. See <a href="wiki.php?action=article&id=83">this wiki</a> for more information.</li> + <li id="r2.3.8"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.8">2.3.8.</a> <strong>The year of the original recording should be used for the "Year" field on the <a href="upload.php">upload page</a>.</strong> Use the recording year for "Year of the original release" (if you can establish it), and use the option to add the release year for the album or edition you are uploading in the Edition Information on the <a href="upload.php">upload page</a>. For example, all editions of The Beatles (White Album) would have 1968 in the main Year box. However, each of the various mono pressings, remasters, re-releases, expanded editions, reconstructions, etc. would have its respective release year in the Edition Information box.</li> + <li id="r2.3.9"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.9">2.3.9.</a> <strong>All lossless analog rips should include clear information about source lineage.</strong> All lossless SACD digital layer analog rips and vinyl rips must include clear information about recording equipment used (see <a href="#h2.8">2.8</a>). If you used a USB turntable for a vinyl rip, clearly indicate this in your lineage information. Also include all intermediate steps up to lossless encoding, such as the program used for mastering, sound card used, etc. Lossless analog rips missing rip information can be trumped by better documented lossless analog rips of equal or better quality. In order to trump a lossless analog rip without a lineage, this lineage must be included as a .txt or .log file within the new torrent.</li> + <li id="r2.3.10"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.10">2.3.10.</a> <strong>All lossless soundboard recordings must include clear information about source lineage.</strong> This information should be displayed in the torrent description. Optionally, the uploader may include the information in a .txt or .log file within the torrent. Lossless soundboard recordings missing lineage information will be deleted if reported (see <a href="#r2.6.7">2.6.7</a>).</li> + <li id="r2.3.11"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.11">2.3.11.</a> <strong>File names must accurately reflect the song titles.</strong> You may not have file names like <samp>01track.mp3</samp>, <samp>02track.mp3</samp>, etc. Torrents containing files that are named with incorrect song titles can be trumped by properly labeled torrents. Also, torrents that are sourced from the scene but do not have the Scene label must comply with site naming rules (no release group names in the file names, no advertisements in the file names, etc.). Note that these must be substantial improvements such as the removal of garbage characters. Small changes such as diacritical marks are insufficient grounds for trumping. English translations of song titles in file names are encouraged but not necessary for foreign language song titles. If all the letters in the track titles are capitalized, the torrent is trumpable. Exceptions: Rare albums featuring no track listing or untitled tracks may have file names like <samp>01track.mp3</samp>, <samp>02track.mp3</samp>, and so forth. Please note this track list in the "Album description". If foreign language characters create playback problems for some systems and cannot be coherently translated, file names such as "<samp>01track</samp>" are acceptable for those few cases.</li> + <li id="r2.3.12"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.12">2.3.12.</a> <strong>The maximum character length for files is 255 characters.</strong> Path length values must not be so long that they cause incompatibility problems with operating systems and media players. For example, "<samp>My Artist Name - My Album Name (2012) - FLAC/01 - Long Track Name for the First Track.flac</samp>" is a typical torrent folder that contains the audio files. This path name consists of 90 characters. This limit includes the number of characters in the main torrent folder (in this case, 46 characters), any sub-folders, and files within that torrent folder. In addition, unnecessarily nested folders will count towards this limit.</li> + <li id="r2.3.13"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.13">2.3.13.</a> <strong>Track numbers are required in file names (e.g., "<samp>01 - TrackName.mp3</samp>").</strong> If a torrent without track numbers in the file names is uploaded, then a torrent with the track numbers in the file names can take its place. When formatted properly, file names will sort in order by track number or playing order. Also see <a href="#r2.3.14">2.3.14</a>. Exception: Track numbers are not required for single-track torrents.</li> + <li id="r2.3.14"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.14">2.3.14.</a> <strong>When formatted properly, file names will alphabetically sort into the original playing order of the release.</strong> + <ul> + <li id="r2.3.14.1"><a href="#r2.3.14"><strong>↑_</strong></a> <a href="#r2.3.14.1">2.3.14.1.</a><strong>For albums with more than one artist, if the name of the artist is in the file name, it must come after the track number in order for the tracks to sort into the correct order.</strong> For example, "<samp>01 - U2 - Where the Streets Have No Name.flac</samp>" is a properly formatted file name for a multiple-artist album. "<samp>U2 - 01 - Where the Streets Have No Name.mp3</samp>" is not correct in the context of a compilation album because the resultant file list will sort in alphabetical order by artist rather than by numerical track number. <strong>Note:</strong> there is no requirement for artist names to be in the track file names, other than <a href="#r2.3.16.2">2.3.16.2.</a> Torrents that have improperly-formatted or incomplete file names will be trumpable.</li> + <li id="r2.3.14.2"><a href="#r2.3.14"><strong>↑_</strong></a> <a href="#r2.3.14.2">2.3.14.2.</a><strong>If a torrent has subfolders for multiple discs of a release, these subfolders must sort by disc order.</strong> If each disc has a title (<a href="torrents.php?id=8238">torrents.php?id=8238</a>, for example) and the discs are not numbered, this rule does not apply, and each subdirectory should have the disc name included.</li> + </ul> + </li> + <li id="r2.3.15"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.15">2.3.15.</a> <strong>Multiple-disc torrents cannot have tracks with the same numbers in one directory.</strong> You may place all the tracks for disc one in one directory and all the tracks for disc two in another directory. If you prefer to use one directory for all the audio files, you must use successive numbering. For example, disc one has 15 tracks and disc two has 20 tracks. You may either number tracks in disc one as #01-#15 and those of disc two as #16-#35 in the same directory, or you may add a disc number before the track numbers such that the numbers are #1 06 for Disc One Track 06, and #2 03 for Disc 2 Track 03, and so forth.</li> + <li id="r2.3.16"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.16">2.3.16.</a> <strong>Properly tag your music files.</strong> + <ul> + <li id="r2.3.16.1"><a href="#r2.3.16"><strong>↑_</strong></a> <a href="#r2.3.16.1">2.3.16.1.</a>Certain meta tags (e.g., ID3, Vorbis) are required on all music uploads. Make sure to use the proper format tags for your files (e.g., no ID3 tags for FLAC — see <a href="#r2.2.10.8">2.2.10.8</a>). ID3v2 tags for files are highly recommended over ID3v1.</li> + <li id="r2.3.16.2"><a href="#r2.3.16"><strong>↑_</strong></a> <a href="#r2.3.16.2">2.3.16.2.</a>ID3 tags are recommended for AC3 torrents, but are not mandatory because the format does not natively support file metadata tagging (for AC3, the file names become the vehicle for correctly labeling media files). Because of this lack of support, the Album and Artist information must be included in the torrent folder for AC3 and DTS files. In addition, the Track Number and Track Title information must be included in the file names for AC3 and DTS files; for various artists compilations, the Track Artist must be included in the file names as well, ensuring that they also satisfy <a href="#r2.3.14">2.3.14.</a></li> + <li id="r2.3.16.3"><a href="#r2.3.16"><strong>↑_</strong></a> <a href="#r2.3.16.3">2.3.16.3.</a>Torrents uploaded with both good ID3v1 tags and blank ID3v2 tags (a dual set of tags) are trumpable by torrents with either just good ID3v1 tags or good ID3v2 tags (a single set of tags). See <a href="wiki.php?action=article&id=84">this wiki</a> for more information on ID3 tags.</li> + <li id="r2.3.16.4"><a href="#r2.3.16"><strong>↑_</strong></a> <a href="#r2.3.16.4">2.3.16.4.</a>If you upload an album missing one or more of the required tags, then another user may add the tags, re-upload, and report your torrent for deletion. The required tags are: + <ul> + <li>Artist</li> + <li>Album</li> + <li>Title</li> + <li>Track Number</li> + </ul> + </li> + </ul> + <span style="font-style: italic;">Note: The "Year" tag is optional but strongly encouraged. However, if missing or incorrect, this is not grounds for trumping a torrent.</span> + </li> + <li id="r2.3.17"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.17">2.3.17.</a> <strong>The torrent artist for classical works should use the full composer name.</strong> Before uploading, see <a href="wiki.php?action=article&id=85">this wiki</a> for guidelines on uploading and tagging classical music torrents.</li> + <li id="r2.3.18"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.18">2.3.18.</a> <strong>Newly re-tagged torrents trumping badly tagged torrents must reflect a substantial improvement over the previous tags.</strong> Removing "(Remastered)" or "(CD1)" from an album or song title is not a substantial improvement. Small changes that include fixing slight misspellings, or missing an alternate spelling of an artist (e.g., excluding "The" before a band name) are insufficient grounds for replacing other torrents. Artist names that are misspelled in the tags are grounds for trumping; this includes character accents and characters that mean one letter in one language and a different letter in another language. Improper capitalization in the tags is grounds for trumping; this includes artist tags (or composer tags) that contain names that are all upper case or track titles that are all upper case. Tags with multiple entries in the same tag (e.g., track number and track title in the track title tags; or track number, artist, and track title in the artist tags) are subject to trumping. You may trump a release if the tags do not follow the data from a reputable music cataloguing service such as <a href="https://musicbrainz.org/" target="_blank">MusicBrainz</a> or <a href="http://www.discogs.com/" target="_blank">Discogs</a>. In the case of a conflict between reputable listings, either tagged version is equally preferred on the site and cannot trump the other. For example, an album is tagged differently in <a href="https://musicbrainz.org/" target="_blank">MusicBrainz</a> and in <a href="http://www.discogs.com/" target="_blank">Discogs</a>. Either style of tagging is permitted; neither is "better" than the other. In that case, any newly tagged torrents replacing an already properly tagged torrent, which follows good tagging convention, will result in a dupe. Note: For classical music, please follow these <a href="wiki.php?action=article&id=85">tagging guidelines</a>.</li> + <li id="r2.3.19"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.19">2.3.19.</a> <strong>Do not embed large images in file metadata.</strong> The combined size of embedded images and padding may not exceed <strong>1024 KiB</strong> per file. You may include the artwork separately if it is too big. When removing embedded images, make sure that the padding is removed or reduced to a few kilobytes. Refer to <a href="wiki.php?action=article&id=86">this wiki article</a> for more information.</li> + <li id="r2.3.20"><a href="#h2.3"><strong>↑_</strong></a> <a href="#r2.3.20">2.3.20.</a> <strong>Leading spaces are not allowed in any file or folder names.</strong> Leading spaces cause usability and interoperability problems among various operating systems and programs. Torrents with file or folder names that contain leading space characters are trumpable.</li> + </ul> + </div> + <h5 id="h2.4"><a href="#h2.4k"><strong>↑</strong></a> <a href="#h2.4">2.4.</a> Bonus Content</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r2.4.1"><a href="#h2.4"><strong>↑_</strong></a> <a href="#r2.4.1">2.4.1.</a> <strong>All music torrents must represent a complete release, and may not be missing tracks or discs (see <a href="#r2.1.19">2.1.19</a>).</strong> Bonus tracks or bonus discs should be uploaded as a complete release with the main album. Do not cobble together main album and bonus content from different sources if you do not have the complete release. Such torrents will be regarded as mutt rips or user compilations, and a staff member may ask for proof of the torrent's authenticity.</li> + <li id="r2.4.2"><a href="#h2.4"><strong>↑_</strong></a> <a href="#r2.4.2">2.4.2.</a> <strong>A FLAC bonus disc-only release is not automatically deleted due to the presence of the complete set (Album + Bonus disc).</strong> An upload consisting solely of a bonus disc can only be trumped by the complete set when the trumping torrent's log score qualifies based on the usual lossless rules (see <a href="#r2.2.10">2.2.10</a>). No bonus disc-only upload can trump a complete set that has a total score of 100%.</li> + <li id="r2.4.3"><a href="#h2.4"><strong>↑_</strong></a> <a href="#r2.4.3">2.4.3.</a> <strong>When a "perfect" FLAC rip (with a log scoring 100%) of the complete release (including the bonus disc) is uploaded, all bonus disc-only torrents are considered dupes of the complete set.</strong> </li> + <li id="r2.4.4"><a href="#h2.4"><strong>↑_</strong></a> <a href="#r2.4.4">2.4.4.</a> <strong>A lossy bonus disc-only torrent can be trumped by the complete set in the same format and bitrate.</strong> </li> + <li id="r2.4.5"><a href="#h2.4"><strong>↑_</strong></a> <a href="#r2.4.5">2.4.5.</a> <strong>Exclusive bonus content from services such as iTunes or Amazon may be uploaded in a separate torrent but may be trumped by a complete torrent containing the full digital release and bonus material.</strong> For instance, an iTunes release that contains two iTunes bonus tracks may have a torrent uploaded of solely the two bonus tracks, though it remains trumpable by the complete iTunes release that includes the bonus material.</li> + <li id="r2.4.6"><a href="#h2.4"><strong>↑_</strong></a> <a href="#r2.4.6">2.4.6.</a> <strong>Lossy bonus content which is otherwise unavailable may be uploaded in a separate torrent and must not be included in the same torrent as the original album, as to avoid creating mutt rips in accordance with <a href="#r2.1.6.3">2.1.6.3</a>.</strong> This includes, but is not limited to: Bonus MP3 content provided in the data portion of a CD, "subscription" bonus material where content is released (gradually or wholly) to subscribers or fan club members, or downloadable exclusives for purchasers of a release. All lossy bonus content must meet the minimum bitrate requirements of <a href="#r2.1.3">2.1.3</a>.</li> + <li id="r2.4.7"><a href="#h2.4"><strong>↑_</strong></a> <a href="#r2.4.7">2.4.7.</a> <strong>Mixed media sets are to be uploaded as one torrent per medium.</strong> For example, a release which contains a CD, a vinyl, and a cassette in one package must be uploaded as three separate torrents, with one torrent containing the CD content, another torrent containing the vinyl content, and another torrent containing the cassette content (in compliance with the tape rip requirements in <a href="#h2.10">2.10</a>).</li> + </ul> + </div> + <h5 id="h2.5"><a href="#h2.5k"><strong>↑</strong></a> <a href="#h2.5">2.5.</a> Vinyl</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r2.5.1"><a href="#h2.5"><strong>↑_</strong></a> <a href="#r2.5.1">2.5.1.</a> <strong>Downsampling of analog rips is allowed.</strong> Analog rips that have been downsampled may be uploaded (e.g., a 24/96 vinyl rip downsampled to 16/44.1). Any downsampled torrents must include the specific programs and methods used to downsample in addition to the lineage for the original rip or it will be deleted.</li> + <li id="r2.5.2"><a href="#h2.5"><strong>↑_</strong></a> <a href="#r2.5.2">2.5.2.</a> <strong>Only one lossy vinyl rip in a specific bitrate is allowed per edition.</strong> Once someone has uploaded a lossy format vinyl rip (in MP3), you may not upload another copy in the same bitrate. It does not matter whether or not the lossy files are of differing sampling rates. For example, if a 44.1 kHz V2 (VBR) copy is already up, you may not upload the same album in V2 (VBR) at 48 kHz.</li> + <li id="r2.5.3"><a href="#h2.5"><strong>↑_</strong></a> <a href="#r2.5.3">2.5.3.</a> <strong>Only one 16-bit lossless vinyl rip is allowed per edition.</strong> Lossless 16-bit vinyl rips must have a sampling rate of either 44.1 kHz or 48 kHz. Upsampling of rips is strictly forbidden. See <a href="wiki.php?action=article&id=87">this wiki</a> for vinyl ripping information and <a href="wiki.php?action=article&id=88">this wiki</a> for general information on the vinyl medium.</li> + <li id="r2.5.4"><a href="#h2.5"><strong>↑_</strong></a> <a href="#r2.5.4">2.5.4.</a> <strong>A maximum of two 24-bit lossless vinyl rips are allowed per edition: A lossless 24-bit rip with sampling rate equal to 192 kHz and a lossless 24-bit rip with sampling rate equal to 44.1, 48, 88.2, or 96 kHz.</strong> Lossless 24/96 vinyl rips trump 24/44.1, 24/48, and 24/88.2 rips. Two or more lossless 24-bit vinyl rips with sampling rates equal to 44.1, 48, or 88.2 kHz are considered dupes. So a 24/88.2 rip and a 24/48 rip of the same material are considered dupes. Rips must sound as good as or better than the rips they are attempting to trump (see <a href="#r2.2.11.9">2.2.11.9</a> for examples of trumping proofs). Upsampling of rips is strictly forbidden. See <a href="wiki.php?action=article&id=87">this wiki</a> for more information on 24-bit vinyl ripping.</li> + <li id="r2.5.5"><a href="#h2.5"><strong>↑_</strong></a> <a href="#r2.5.5">2.5.5.</a> <strong>Vinyl rips may be trumped by better-sounding rips of the same bit depth, regardless of lineage information (see <a href="#r2.3.9">2.3.9</a>).</strong> + <ul> + <li id="r2.5.5.1"><a href="#r2.5.5"><strong>↑_</strong></a> <a href="#r2.5.5.1">2.5.5.1.</a> <strong>Lossless 16-bit rips may be trumped by better-sounding lossless 16/44.1 or 16/48 rips.</strong> </li> + <li id="r2.5.5.2"><a href="#r2.5.5"><strong>↑_</strong></a> <a href="#r2.5.5.2">2.5.5.2.</a> <strong>Lossless 24-bit rips with sampling rate equal to 44.1, 48, 88.2, or 96 kHz may be trumped by better-sounding lossless 24/96 rips.</strong> </li> + <li id="r2.5.5.3"><a href="#r2.5.5"><strong>↑_</strong></a> <a href="#r2.5.5.3">2.5.5.3.</a> <strong>Lossless 24-bit rips with sampling rate equal to 192 kHz may be trumped by better-sounding lossless 24/192 rips.</strong> </li> + <li id="r2.5.5.4"><a href="#r2.5.5"><strong>↑_</strong></a> <a href="#r2.5.5.4">2.5.5.4.</a> <strong>Lossy vinyl rips may be trumped by substantially better-sounding lossy vinyl rips when the difference in quality is obvious.</strong> To trump an old rip with a new torrent that may be a better sounding version, you need to report it with clear information about how your rip sounds better than the other one, with references to specific tracks and time positions to justify your report (see <a href="#r2.2.11.9">2.2.11.9</a>). The following are acceptable reasons to both report and/or remove a torrent. + <ul> + <li id="r2.5.5.4.1"><a href="#r2.5.5.4"><strong>↑_</strong></a> <a href="#r2.5.5.4.1">2.5.5.4.1.</a> <strong>The vinyl audio was captured at the wrong playback speed.</strong> </li> + <li id="r2.5.5.4.2"><a href="#r2.5.5.4"><strong>↑_</strong></a> <a href="#r2.5.5.4.2">2.5.5.4.2.</a> <strong>At least one track has a constant level of static.</strong> </li> + <li id="r2.5.5.4.3"><a href="#r2.5.5.4"><strong>↑_</strong></a> <a href="#r2.5.5.4.3">2.5.5.4.3.</a> <strong>At least one track has a noticeable pop or click.</strong> This pop must be visible in the frequency spectrals for the track and should last 1/20th of a second.</li> + <li id="r2.5.5.4.4"><a href="#r2.5.5.4"><strong>↑_</strong></a> <a href="#r2.5.5.4.4">2.5.5.4.4.</a> <strong>There is noticeable wobbling during playback.</strong> </li> + <li id="r2.5.5.4.5"><a href="#r2.5.5.4"><strong>↑_</strong></a> <a href="#r2.5.5.4.5">2.5.5.4.5.</a> <strong>The needle drop is audible.</strong> </li> + <li id="r2.5.5.4.6"><a href="#r2.5.5.4"><strong>↑_</strong></a> <a href="#r2.5.5.4.6">2.5.5.4.6.</a> <strong>At least one track is truncated or improperly split.</strong> </li> + </ul> + </li> + <li id="r2.5.5.5"><a href="#r2.5.5"><strong>↑_</strong></a> <a href="#r2.5.5.5">2.5.5.5.</a> <strong>Rips of extremely poor quality (lossy or lossless) may be deleted outright if reported.</strong> All quality trumps/deletions of this nature are at the discretion of the moderator involved.</li> + </ul> + </li> + <li id="r2.5.6"><a href="#h2.5"><strong>↑_</strong></a> <a href="#r2.5.6">2.5.6.</a> <strong>Lossy transcodes of lossless vinyl rips trumped under rule <a href="#r2.2.11.9">2.2.11.9</a> and the rules in this section are trumpable by lossy transcodes of the trumping vinyl rips.</strong> For example, lossy torrent A and lossless torrent A exist on the site. A new vinyl rip (lossless torrent B) is uploaded and trumps lossless torrent A (the old vinyl rip). All lossy transcodes from lossless torrent B will automatically trump the corresponding lossy torrents transcoded from lossless torrent A; only the torrent B formats will remain on the site. So that the correct source for the lossless rip can be easily identified, you are strongly encouraged to mention or link to the vinyl rip that you are using for the lossless-to-lossy transcode and to reproduce its lineage in the release description of transcodes that you upload.</li> + <li id="r2.5.7"><a href="#h2.5"><strong>↑_</strong></a> <a href="#r2.5.7">2.5.7.</a> <strong>All vinyl torrents must be ripped at the correct speed.</strong> You must rip all vinyl albums using the speed at which they were intended to be played. For example, you may not rip a 45 rpm vinyl at 33 rpm and upload it to the site. In addition, turntables that have been improperly calibrated, and therefore play back LPs at an incorrect speed, will cause pitch changes that are noticeable. Any such vinyl albums that were captured at the wrong playback speed will be deleted once reported.</li> + </ul> + </div> + <h5 id="h2.6"><a href="#h2.6k"><strong>↑</strong></a> <a href="#h2.6">2.6.</a> Live Music and Soundboards</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r2.6.1"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.1">2.6.1.</a> <strong>Live soundboard material should be uploaded as one torrent per show.</strong> </li> + <li id="r2.6.2"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.2">2.6.2.</a> <strong>Only one edition of each unofficial live recording is allowed.</strong> Such bootlegs and mixtapes can be unofficially remastered several times, and such constant remastering is of little consequence on a site where bootlegs are not the primary focus. Only one unofficial soundboard recording of each show is allowed, and it should be uploaded on the torrent page in the absence of any edition information. See <a href="wiki.php?action=article&id=89">this wiki</a> for more information on how live music is organized on the site.</li> + <li id="r2.6.3"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.3">2.6.3.</a> <strong>No unofficial audience recordings.</strong> + <ul> + <li id="r2.6.3.1"><a href="#r2.6.3"><strong>↑_</strong></a> <a href="#r2.6.3.1">2.6.3.1.</a> <strong>AUD (Audience), IEM (In Ear Monitor), ALD (Assistive Listening Device), Mini-Disc, and Matrix-sourced recordings cannot be uploaded.</strong> </li> + <li id="r2.6.3.2"><a href="#r2.6.3"><strong>↑_</strong></a> <a href="#r2.6.3.2">2.6.3.2.</a> <strong>Officially-remastered AUD/IEM/ALD/Mini-Disc/Matrix recordings are allowed.</strong> These may be re-appropriated recordings released with the artist's or their label's consent. Bonus tracks from such recording sources are also exempt. Be prepared to provide clear proofs of such re-releases to the staff when asked.</li> + </ul> + </li> + <li id="r2.6.4"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.4">2.6.4.</a> <strong>Soundboards must comprise complete shows.</strong> Incomplete shows will be deleted.</li> + <li id="r2.6.5"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.5">2.6.5.</a> <strong>Soundboards may not include soundchecks.</strong> Those uploaded with a soundcheck will be deleted.</li> + <li id="r2.6.6"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.6">2.6.6.</a> <strong>One tape generation and one CD-R generation are allowed for each soundboard upload.</strong> + <ul> + <li id="r2.6.6.1"><a href="#r2.6.6"><strong>↑_</strong></a> <a href="#r2.6.6.1">2.6.6.1.</a> <strong>The tape generation must be the first generation.</strong> For example, there can be a master tape and then a subsequent tape (with no other transfer in between). Any additional tape generations beyond the secondary tape will require staff approval.</li> + <li id="r2.6.6.2"><a href="#r2.6.6"><strong>↑_</strong></a> <a href="#r2.6.6.2">2.6.6.2.</a> <strong>If a limited number of tracks (at the discretion of the staff member involved) have been patched with a short amount of tape-sourced data you may still upload the recording but this information must be clearly stated in the lineage and the album description.</strong> </li> + </ul> + </li> + <li id="r2.6.7"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.7">2.6.7.</a> <strong>Include lineage information for each soundboard recording (see <a href="#r2.3.10">2.3.10</a>).</strong> + <ul> + <li id="r2.6.7.1"><a href="#r2.6.7"><strong>↑_</strong></a> <a href="#r2.6.7.1">2.6.7.1.</a> <strong>Lineage information for a soundboard recording is highly recommended.</strong> </li> + <li id="r2.6.7.2"><a href="#r2.6.7"><strong>↑_</strong></a> <a href="#r2.6.7.2">2.6.7.2.</a> <strong>If a soundboard is uploaded without a lineage, and a lineage is later found that contains more than one tape or CD-R generation, then the uploader will be warned and the torrent deleted.</strong></li> + <li id="r2.6.7.3"><a href="#r2.6.7"><strong>↑_</strong></a> <a href="#r2.6.7.3">2.6.7.3.</a> <strong>No lineage editing or misrepresentation will be tolerated.</strong> Doing so will result in the loss of upload privileges. If you are unsure of a lineage, then do not provide it. Do NOT guess.</li> + </ul> + </li> <li id="r2.6.8"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.8">2.6.8.</a> <strong>Soundboards must be uploaded and organized according to <a href="wiki.php?action=article&id=90">this wiki</a>.</strong> </li> - <li id="r2.6.9"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.9">2.6.9.</a> <strong>The following lists should illustrate when Live music may or may not be uploaded.</strong> - <ul> - <li id="r2.6.9.1"><a href="#r2.6.9"><strong>↑_</strong></a> <a href="#r2.6.9.1">2.6.9.1.</a> <strong>Allowed Live Music</strong> - <ul> - <li>Live album bought from an online retailer</li> - <li>CD bought from a brick & mortar store</li> - <li>Soundboard recording that follows the Live Music rules</li> - <li>Soundboard recording that has been adopted by the artist</li> - <li>WEB-sourced live bootleg (released by the artist or a record label)</li> - <li>CD-sourced live bootleg (released by the artist or a record label)</li> - <li>Bootleg album that was later sanctioned by the record label</li> - <li>Soundboard recordings containing a tape generation in the lineage; this will require staff approval unless the tape generation is the first generation</li> - <li>Soundboard recordings that are slightly incomplete due to circumstances beyond control of the recording engineer (i.e. Reel flips, cassette flips or hitting the record button after the band starts playing). Soundboard recordings containing audience/FM/Broadcast/IEM patches containing this missing material may be uploaded. No more than three minutes may be missing. Shows uploaded according to this rule must clearly indicate what is missing and the timing of the missing material. Recordings missing this information may be trumped by recordings in compliance with this rule.</li> - </ul> - </li> - <li id="r2.6.9.2"><a href="#r2.6.9"><strong>↑_</strong></a> <a href="#r2.6.9.2">2.6.9.2.</a> <strong>Disallowed Live Music</strong> - <ul> - <li>Unofficial live mix of a studio album (no artist or record label involvement)</li> - <li>Vanity House mix</li> - <li>Soundboard recordings missing more than three minutes of material</li> - <li>Soundboard recordings containing sound checks or other non-performance portions</li> - <li>Collection of random live tracks from a single artist</li> - <li>Collection of random live tracks from multiple artists</li> - <li>Collection of remixed live tracks</li> - <li>Radio broadcast of a live performance</li> - </ul> - </li> - </ul> - </li> - </ul> - </div> - <h5 id="h2.7"><a href="#h2.7k"><strong>↑</strong></a> <a href="#h2.7">2.7.</a> Multichannel</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r2.7.1"><a href="#h2.7"><strong>↑_</strong></a> <a href="#r2.7.1">2.7.1.</a> <strong>The only lossy multichannel audio formats allowed are AC3 and DTS.</strong> If the source is DTS or AC3, do not transcode to other formats. Exceptions: Transcoding is allowed if the source is lossless (LPCM or MLP). Those should be compressed with multichannel FLAC. See <a href="wiki.php?action=article&id=91">this wiki</a> for more information.</li> - <li id="r2.7.2"><a href="#h2.7"><strong>↑_</strong></a> <a href="#r2.7.2">2.7.2.</a> <strong>DTS-CD rips cannot be re-compressed to a lossless codec and they must be kept as WAV files with the .dts extension.</strong> </li> - <li id="r2.7.3"><a href="#h2.7"><strong>↑_</strong></a> <a href="#r2.7.3">2.7.3.</a> <strong>A multichannel lossless rip of multichannel source media must have the same number of and arrangement of channels as the source media.</strong> In addition, the rip must be encoded at the same bit depth and sampling rate as the original stream. Downsampled versions of the original source material may be trumped by a torrent containing audio files encoded at the original bit depth and sampling rate as the files on disc.</li> - <li id="r2.7.4"><a href="#h2.7"><strong>↑_</strong></a> <a href="#r2.7.4">2.7.4.</a> <strong>A multichannel rip of source media that includes both multichannel and stereo streams may coexist with a rip of the stereo stream off the same media.</strong> A rip of the stereo stream trumps a downmix of the multichannel rip. However, if there is no rip of a stereo stream uploaded, a stereo downmix of a rip from the multichannel stream may coexist with a multichannel rip of the same stream. For example, a DVD-Audio disc may include both stereo LPCM (Linear Pulse-Code Modulation) audio and 5.1 MLP (Meridian Lossless Packing) audio. In this case, a stereo rip of the LPCM stream could coexist with a 5.1 rip of the MLP stream, and the stereo LPCM rip would trump a stereo downmix of a multichannel MLP stream rip. If no stereo LPCM rip of this source media was uploaded, a stereo downmix of the MLP stream could coexist with a 5.1 multichannel rip of the MLP stream.</li> - <li id="r2.7.5"><a href="#h2.7"><strong>↑_</strong></a> <a href="#r2.7.5">2.7.5.</a> <strong>Be aware of the special tagging rules for AC3 torrents (see <a href="#r2.3.16">2.3.16</a>).</strong> </li> - </ul> - </div> - <h5 id="h2.8"><a href="#h2.8k"><strong>↑</strong></a> <a href="#h2.8">2.8.</a> SACD</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r2.8.1"><a href="#h2.8"><strong>↑_</strong></a> <a href="#r2.8.1">2.8.1.</a> <strong>Digital rips produced by the PlayStation 3 (PS3) - SACD ripper process (see <a href="wiki.php?action=article&id=10">this wiki</a>) trump any other SACD digital or analog rips (e.g., Oppo method, analog output to ADC, DSD stream from a SACD player, etc.).</strong> Non-PS3 digital SACD rips trump analog SACD rips. Torrents containing a 88.2 kHz digitally-sourced PCM stream compressed with FLAC are trumpable by new digital rips made directly from the SACD disc using the PS3-SACD ripper method. See <a href="wiki.php?action=article&id=92">this wiki</a> for more information on the SACD format. Note: This means that you cannot download another user's uncompressed DSD source files and manipulate those.</li> - <li id="r2.8.2"><a href="#h2.8"><strong>↑_</strong></a> <a href="#r2.8.2">2.8.2.</a> <strong>Analog SACD rips may have a sampling rate equal to 88.2 or 96 kHz and may be trumped at any time by a digital 24/88.2 SACD rip.</strong> If a digital SACD rip has frequencies higher than 44.1 kHz, contact a moderator privately for approval to upload a 24-bit rip with a sampling rate greater than 88.2 kHz.</li> - <li id="r2.8.3"><a href="#h2.8"><strong>↑_</strong></a> <a href="#r2.8.3">2.8.3.</a> <strong>SACD hybrid rip sources must be properly labeled.</strong> - <ul> - <li id="r2.8.3.1"><a href="#r2.8.3"><strong>↑_</strong></a> <a href="#r2.8.3.1">2.8.3.1.</a> <strong>SACD hybrid discs ripped from the CD layer should be labeled as CD and not SACD.</strong> If you used your CD/DVD drive and a CD ripper (such as <a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a>) to extract the audio, then label it as "CD" sourced because it is not true 24-bit audio.</li> - <li id="r2.8.3.2"><a href="#r2.8.3"><strong>↑_</strong></a> <a href="#r2.8.3.2">2.8.3.2.</a> <strong>If the rip came from the genuine digital SACD layer via a PS3 player or through a SACD player mod or line out, it should be 24-bit quality SACD.</strong> You may include "SACD Hybrid" in the Edition Information box on the <a href="upload.php">upload page</a>.</li> - </ul> - </li> - <li id="r2.8.4"><a href="#h2.8"><strong>↑_</strong></a> <a href="#r2.8.4">2.8.4.</a> <strong>Lineage information for an SACD capture is required and should be included in the torrent folder.</strong> - <ul> - <li id="r2.8.4.1"><a href="#r2.8.4"><strong>↑_</strong></a> <a href="#r2.8.4.1">2.8.4.1.</a> <strong>Both digital and analog SACD rips require lineage information that includes the SACD ripping process, equipment used, applicable conversion software (with the conversion settings), and any post-capture software mastering that was performed.</strong> Any torrents that lack this lineage information are trumpable.</li> - <li id="r2.8.4.2"><a href="#r2.8.4"><strong>↑_</strong></a> <a href="#r2.8.4.2">2.8.4.2.</a> <strong>Digital rips that use the Saracon or Philips converters will trump foobar rips that use the SACD_input converter; all three of these rips would trump a digital rip that uses the Audiogate converter.</strong> </li> - <li id="r2.8.4.3"><a href="#r2.8.4"><strong>↑_</strong></a> <a href="#r2.8.4.3">2.8.4.3.</a> <strong>Digital rips created from the Saracon and Philips converters must be "de-clicked" (PM a moderator for details on the process, which consists of trimming 53 samples from the DFF file and then prepending and appending 12 samples back onto the file during the conversion process).</strong> </li> - <li id="r2.8.4.4"><a href="#r2.8.4"><strong>↑_</strong></a> <a href="#r2.8.4.4">2.8.4.4.</a> <strong>No lineage editing or misrepresentation will be tolerated.</strong> Doing so will result in the loss of upload privileges. If you are unsure of a lineage, then do not provide it. Do NOT guess.</li> - </ul> - </li> - <li id="r2.8.5"><a href="#h2.8"><strong>↑_</strong></a> <a href="#r2.8.5">2.8.5.</a> <strong> Downsampling of SACD sources is allowed in certain cases.</strong> - <ul> - <li id="r2.8.5.1"><a href="#r2.8.5"><strong>↑_</strong></a> <a href="#r2.8.5.1">2.8.5.1.</a> <strong> A multichannel lossless rip must have the same number of and arrangement of channels as the source media.</strong> In addition, the rip must be encoded at the same bit depth and sampling rate as the original stream. Downsampled versions of the original source material may be trumped by a torrent containing audio files ripped directly from the disc at the bit depth and sampling rate originally found on disc. For example, there is a 192 kHz track and a 44.1 kHz track on the SACD. A user uploads only the 192 kHz track and that source is subsequently downsampled to 44.1 kHz. Another user who rips the actual 44.1 kHz track from the original disc may trump the downsampled torrent.</li> - <li id="r2.8.5.2"><a href="#r2.8.5"><strong>↑_</strong></a> <a href="#r2.8.5.2">2.8.5.2.</a> <strong>Downmixing of SACD sources is prohibited.</strong> Do not downmix a 5.1 (or higher) channel mix to stereo and upload the resulting audio files. You may still downconvert lossless audio sources from 24-bit to 16-bit.</li> - <li id="r2.8.5.3"><a href="#r2.8.5"><strong>↑_</strong></a> <a href="#r2.8.5.3">2.8.5.3.</a> <strong> All lossy SACD torrents must be uploaded in a Red Book-compliant form.</strong> Any stereo lossless sources must be downconverted to 16 bits and downsampled to 44.1 kHz before they are transcoded to lossy MP3 formats. No other bit depth or sampling rate is allowed for lossy SACD torrents.</li> - </ul> - </li> - </ul> - </div> - <h5 id="h2.9"><a href="#h2.9k"><strong>↑</strong></a> <a href="#h2.9">2.9.</a> Blu-ray</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r2.9.1"><a href="#h2.9"><strong>↑_</strong></a> <a href="#r2.9.1">2.9.1.</a> <strong>Only digital rips of the lossless audio track from a Blu-ray disc may be uploaded (see <a href="wiki.php?action=article&id=93">this wiki</a> for more information).</strong> Analog Blu-ray rips are forbidden. Blu-ray discs may contain all of the following formats: Dolby Digital (AC3), DTS, Dolby Digital Plus (AC3), DTS-HD, PCM, Dolby TrueHD, DTS-HD Master Audio. Only PCM, Dolby TrueHD, and DTS-HD Master Audio contain truly lossless audio material on the Blu-ray disc. See <a href="#r2.1.1">2.1.1</a> and <a href="#r2.7.1">2.7.1 </a> for more information on multichannel audio material on the site.</li> - <li id="r2.9.2"><a href="#h2.9"><strong>↑_</strong></a> <a href="#r2.9.2">2.9.2.</a> <strong> Uploading movie audio tracks is prohibited.</strong> For example, the Inception Blu-ray disc set features a DTS-HD Master Audio 5.1 surround sound track for the entire movie, which cannot be uploaded, and a DTS-HD Master Audio 5.1 surround sound mix of the movie soundtrack, which can be uploaded. Uploading movie audio tracks will result in a warning and the loss of your uploading privileges.</li> - <li id="r2.9.3"><a href="#h2.9"><strong>↑_</strong></a> <a href="#r2.9.3">2.9.3.</a> <strong> Blu-ray rip sources must be properly labeled.</strong> - <ul> - <li id="r2.9.3.1"><a href="#r2.9.3"><strong>↑_</strong></a> <a href="#r2.9.3.1">2.9.3.1.</a> <strong> If the source was lossy (e.g., AC3 or DTS) do not transcode to other formats.</strong> </li> - <li id="r2.9.3.2"><a href="#r2.9.3"><strong>↑_</strong></a> <a href="#r2.9.3.2">2.9.3.2.</a> <strong> If the source was a genuine lossless track, identify the track in the album edition field, e.g., "16-bit PCM" or "24-bit DTS-HD MA 5.1".</strong> Sampling rate information may also be included (e.g., "16/48 LPCM") in the album edition field. </li> - </ul> - </li> - <li id="r2.9.4"><a href="#h2.9"><strong>↑_</strong></a> <a href="#r2.9.4">2.9.4.</a> <strong>Lineage information for a Blu-ray transfer is required and should be included in the torrent folder.</strong> - <ul> - <li id="r2.9.4.1"><a href="#r2.9.4"><strong>↑_</strong></a> <a href="#r2.9.4.1">2.9.4.1.</a> <strong> All Blu-ray rips require lineage information that includes the identity of the Blu-ray source, any software used for ripping and encoding, disc drive and other equipment used, and details of the transfer process.</strong> Any torrents that lack this lineage information are trumpable.</li> - <li id="r2.9.4.2"><a href="#r2.9.4"><strong>↑_</strong></a> <a href="#r2.9.4.2">2.9.4.2.</a> <strong>No lineage editing or misrepresentation will be tolerated.</strong> Doing so will result in the loss of upload privileges. If you are unsure of a lineage, then do not provide it. Do NOT guess.</li> - </ul> - </li> - <li id="r2.9.5"><a href="#h2.9"><strong>↑_</strong></a> <a href="#r2.9.5">2.9.5.</a> <strong> Downsampling of Blu-ray sources is allowed in certain cases.</strong> - <ul> - <li id="r2.9.5.1"><a href="#r2.9.5"><strong>↑_</strong></a> <a href="#r2.9.5.1">2.9.5.1.</a> <strong> A multichannel lossless rip must have the same number of and arrangement of channels as the source media.</strong> In addition, the rip must be encoded at the same bit depth and sampling rate as the original stream. Downsampled versions of the original source material may be trumped by a torrent containing audio files ripped directly from the disc at the bit depth and sampling rate originally found on disc. For example, there is a 96 kHz track and a 44.1 kHz track on the Blu-ray disc. A user uploads only the 96 kHz track and that source is subsequently downsampled to 44.1 kHz. Another user who rips the actual 44.1 kHz track from the original disc may trump the downsampled torrent.</li> - <li id="r2.9.5.2"><a href="#r2.9.5"><strong>↑_</strong></a> <a href="#r2.9.5.2">2.9.5.2.</a> <strong> Downmixing of Blu-ray sources is prohibited.</strong> Do not downmix a 5.1 (or higher) channel mix to stereo and upload the resulting audio files. You may still downconvert lossless audio sources from 24-bit to 16-bit.</li> - <li id="r2.9.5.3"><a href="#r2.9.5"><strong>↑_</strong></a> <a href="#r2.9.5.3">2.9.5.3.</a> <strong> All lossy Blu-ray torrents must be uploaded in a Red Book-compliant form.</strong> Any stereo lossless sources must be downconverted to 16 bits and downsampled to 44.1 kHz before they are transcoded to lossy MP3 formats. No other bit depth or sampling rate is allowed for lossy Blu-ray torrents.</li> - <li id="r2.9.5.4"><a href="#r2.9.5"><strong>↑_</strong></a> <a href="#r2.9.5.4">2.9.5.4.</a> <strong> Do not downsample, downmix, transcode or otherwise manipulate any lossy Blu-ray format (e.g., Dolby Digital (AC3), DTS, Dolby Digital Plus (AC3), and DTS-HD).</strong> </li> - </ul> - </li> - </ul> - </div> - <h5 id="h2.10"><a href="#h2.10k"><strong>↑</strong></a> <a href="#h2.10">2.10.</a> Cassettes</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r2.10.1"><a href="#h2.10"><strong>↑_</strong></a> <a href="#r2.10.1">2.10.1.</a> <strong>Cassettes are allowed under strict conditions.</strong> "Cassettes" include compact cassettes, 8-track tape cartridges, and non-cassette consumer reel-to-reel audio tape recordings. - <ul> - <li id="r2.10.1.1"><a href="#r2.10.1"><strong>↑_</strong></a> <a href="#r2.10.1.1">2.10.1.1.</a> <strong>Cassette releases may not be uploaded if the release exists on the site in any other format.</strong> A non-cassette release may trump a cassette release. A bonus track which only appears on the cassette release is considered exclusive content for that release, and the full cassette release may be uploaded. A different mastering or a different track order is not sufficient to justify the upload of a cassette rip.</li> - <li id="r2.10.1.2"><a href="#r2.10.1"><strong>↑_</strong></a> <a href="#r2.10.1.2">2.10.1.2.</a> <strong>Rips must be made from official (e.g., artist- or label-approved) cassette sources;</strong> secondhand copies are prohibited. Bootlegs are often secondhand copies and may be deleted outright if they do not meet high quality audio standards. Please see rule <a href="#r2.10.8">2.10.8</a>.</li> - </ul> - </li> - <li id="r2.10.2"><a href="#r2.10"><strong>↑_</strong></a> <a href="#r2.10.2">2.10.2.</a> <strong>All cassette rips should include clear information about lineage.</strong> Include a description of the ripping hardware if this is a personal rip or if a rip downloaded elsewhere includes this information. It is strongly recommended that uploaders include a photo of the cassette and artwork in the torrent description. Any torrents that lack lineage information are trumpable by those that have lineage information.</li> - <li id="r2.10.3"><a href="#r2.10"><strong>↑_</strong></a> <a href="#r2.10.3">2.10.3.</a> <strong>Cassette uploads must be ripped at a sample rate of 44.1 kHz or 48 kHz and 88.2 kHz or 96 kHz.</strong> Cassette uploads with sample rates above 96 kHz may be deleted at moderator discretion. Lossy transcodes of cassette FLACs must retain the sample rate of the original FLAC if it is 44.1 kHz or 48 kHz. 44.1 kHz and 48 kHz rips may not exist concurrently; 88.2 kHz and 96 kHz may not exist concurrently; if one is already available, the other is considered a dupe unless a substantial improvement in audio quality is evident (see rule <a href="#r2.10.6">2.10.6</a>).</li> - <li id="r2.10.4"><a href="#r2.10"><strong>↑_</strong></a> <a href="#r2.10.4">2.10.4.</a> <strong>Cassette rips will be trumped by an upload of the same release in any other medium.</strong> A cassette edition with bonus tracks not available in another edition is not subject to trumping.</li> - <li id="r2.10.5"><a href="#r2.10"><strong>↑_</strong></a> <a href="#r2.10.5">2.10.5.</a> <strong>Cassette rips are subject to the duplicates and trumping rules outlined in rule <a href="#h2.2">2.2</a>.</strong></li> - <li id="r2.10.6"><a href="#r2.10"><strong>↑_</strong></a> <a href="#r2.10.6">2.10.6.</a> <strong>Cassette rips can be trumped if a substantially better-sounding version is uploaded.</strong> To trump an old rip with a new torrent that may be a better-sounding version, you need to report it with clear information about how your rip sounds better than the other one, with references to specific tracks and time positions to justify your report. All quality trumps/deletions of this nature are at the discretion of the moderator involved.</li> - <li id="r2.10.7"><a href="#r2.10"><strong>↑_</strong></a> <a href="#r2.10.7">2.10.7.</a> <strong>Cassette rips of extremely poor quality (lossy or lossless) may be deleted outright if reported.</strong> All quality trumps/deletions of this nature are at the discretion of the moderator involved.</li> - <li id="r2.10.8"><a href="#r2.10"><strong>↑_</strong></a> <a href="#r2.10.8">2.10.8.</a> <strong>Exceptions to any of the above rules may be made at a moderator's discretion.</strong> Please contact staff <em>before uploading</em> with detailed reasons why such an exception should be considered. Examples: a higher sample rate for a high quality reel-to-reel recording, a cassette release that was subsequently released on brickwalled CD master, a lower sample rate for a good rip of a Type I cassette, a good rip of a second generation tape with rare content. This is not an exhaustive list of possible reasons to grant an exception.</li> - </ul> - </div> - <h4 id="h3"><a href="#h3k"><strong>↑</strong></a> <a href="#h3">3.</a> Comedy (Audio) & Audiobooks</h4> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r3.1"><a href="#r3.1k"><strong>↑_</strong></a> <a href="#r3.1">3.1.</a> The only formats allowed for comedy and audiobooks are those listed below: - <ul> - <li>MP3, AAC, FLAC</li> - <li id="r3.1.1"><a href="#r3.1"><strong>↑_</strong></a> <a href="#r3.1.1">3.1.1.</a> <strong>Lossless audio files are allowed at all times, in accordance with the dupe rules (see <a href="#r3.9">3.9</a>).</strong> </li> - <li id="r3.1.2"><a href="#r3.1"><strong>↑_</strong></a> <a href="#r3.1.2">3.1.2.</a> <strong>Lossy comedy and audiobook torrents must have a minimum average bitrate of 32 kbps (for MP3 and AAC).</strong> </li> - </ul> - </li> - <li id="r3.2"><a href="#r3.2k"><strong>↑_</strong></a> <a href="#r3.2">3.2.</a> <strong>No music is permitted in these two categories. They are for spoken word only. See <a href="wiki.php?action=article&id=94">this wiki</a> for more information.</strong> For example, if a comedy artist performs a stand-up routine that is recorded on CD, this would be classified as a Comedy album. If this same artist sings some funny songs on an album, this would be classified as a Comedy album. If this same artist produces a music CD with no hint of comedic material (e.g., spoken word tracks), this would be classified as a music album (see <a href="#h2.1">2.1</a>). </li> - <li id="r3.3"><a href="#r3.3k"><strong>↑_</strong></a> <a href="#r3.3">3.3.</a> <strong>No radio talk shows or podcasts are allowed.</strong> <?=SITE_NAME?> is primarily a music site, and those recordings do not belong in any torrent category. </li> - <li id="r3.4"><a href="#r3.4k"><strong>↑_</strong></a> <a href="#r3.4">3.4.</a> <strong>Comedy and audiobooks must not be freely available.</strong> Free audiobooks and comedy releases from official sources may not be uploaded. Exceptions: Uploads that are at a different bitrate from those freely available are allowed. If a comedy album in 96 kbps CBR MP3 is freely available, you may still upload a V8 (VBR) or higher bitrate torrent of the freely available torrent if it does not already exist on the site. </li> - <li id="r3.5"><a href="#r3.5k"><strong>↑_</strong></a> <a href="#r3.5">3.5.</a> <strong>Releases must be unarchived and consist of a single release.</strong> Comedy and audiobook releases should not be archived in a file (e.g., .zip or .rar). The torrent must consist of a directory containing the audio files. Only one release per torrent is allowed. You may not bundle multiple audiobooks or comedy releases into one torrent. See <a href="#r1.2.7">1.2.7</a> and <a href="#r2.1.20">2.1.20</a>. </li> - <li id="r3.6"><a href="#r3.6k"><strong>↑_</strong></a> <a href="#r3.6">3.6.</a> <strong>All comedy and audiobook releases must have an average bitrate of at least 32 kbps</strong>. However, higher bitrates are preferred over this minimum requirement. - <ul> - <li id="r3.6.1"><a href="#r3.6"><strong>↑_</strong></a> <a href="#r3.6.1">3.6.1.</a> <strong>V0 (VBR), V2 (VBR), and V8 (VBR) MP3 torrents for the same album are allowed at all times.</strong> </li> - <li id="r3.6.2"><a href="#r3.6"><strong>↑_</strong></a> <a href="#r3.6.2">3.6.2.</a> <strong>V8 (VBR) replaces V3 (VBR) through V7 (VBR) and replaces CBR rips between 32 kbps and 191 kbps.</strong> Once a rip with a V8 (VBR) encoding preset has been uploaded, you are not allowed to upload any V3 (VBR) through V7 (VBR) torrents or any CBR torrents under 192 kbps.</li> - <li id="r3.6.3"><a href="#r3.6"><strong>↑_</strong></a> <a href="#r3.6.3">3.6.3.</a> <strong>V2 (VBR) replaces CBR rips between 192 kbps and 320 kbps.</strong> Once a rip with a V2 (VBR) encoding preset has been uploaded, you are not allowed to upload any CBR torrents.</li> - <li id="r3.6.4"><a href="#r3.6"><strong>↑_</strong></a> <a href="#r3.6.4">3.6.4.</a> <strong>V0 (VBR) replaces V1 (VBR) and replaces CBR rips at 320 kbps and above.</strong> Once a rip with a V0 (VBR) encoding preset has been uploaded, you are not allowed to upload a V1 (VBR) torrent or any CBR torrents at 320 kbps and above.</li> - <li id="r3.6.5"><a href="#r3.6"><strong>↑_</strong></a> <a href="#r3.6.5">3.6.5.</a> <strong>The preferred AAC bitrate is 64 kbps.</strong> All other AAC bitrates may be trumped by a 64 kbps upload. Once a 64 kbps AAC torrent has been uploaded, no further AAC uploads are allowed for that album.</li> - </ul> - </li> - <li id="r3.7"><a href="#r3.7k"><strong>↑_</strong></a> <a href="#r3.7">3.7.</a> <strong>Lossy sources, including lossy transcodes, are allowed in this section only.</strong> The source may come from cassette, VHS (audio), radio, or a higher bitrate lossy file. While the sharing of transcoded material is strongly discouraged for music, the audio quality is less important for spoken word material. Note: You may not transcode a lower bitrate file to a higher bitrate file and upload it here. For example, if you find a 32 kbps CBR WMA you cannot transcode it to 64 kbps CBR MP3 and share this on the site.</li> - <li id="r3.8"><a href="#r3.8k"><strong>↑_</strong></a> <a href="#r3.8">3.8.</a> <strong>Online audiobook sources may carry only low bitrate audio files.</strong> Audiobook sources such as <a href="http://www.audible.com/">audible.com</a> have standard audio quality levels that may be at or below the standards for the site (see <a href="#r3.1.2">3.1.2</a>). Please make sure that you know the audio quality of your source material (e.g., many audiobook retailers only supply 64 kbps files) before you transcode to V8 (VBR), V2 (VBR), or V0 (VBR). Transcoding from a lower bitrate file to a higher bitrate file is forbidden.</li> - <li id="r3.9"><a href="#r3.9k"><strong>↑_</strong></a> <a href="#r3.9">3.9.</a> <strong>Duplicate torrents of the same bitrate and format are not allowed.</strong> No uploads of the same bitrate and format are allowed to coexist on the site. See <a href="#r3.6">3.6</a>.</li> - <li id="r3.10"><a href="#r3.10k"><strong>↑_</strong></a> <a href="#r3.10">3.10.</a> <strong>Scene and non-scene versions of the same release, bitrate, and format are dupes.</strong> Non-scene releases trump Scene releases. If a Scene and non-scene release coexist, the non-scene release trumps the Scene release, regardless of which release was uploaded first.</li> - <li id="r3.11"><a href="#r3.11k"><strong>↑_</strong></a> <a href="#r3.11">3.11.</a> <strong>Releases must follow the usual formatting guidelines for file names and tags.</strong> Audiobooks and comedy must follow the formatting rules outlined in section <a href="#h2.3">2.3</a>. This means that file names must include track numbers, audio files must have metatags, etc.</li> - <li id="r3.12"><a href="#r3.12k"><strong>↑_</strong></a> <a href="#r3.12">3.12.</a> <strong>Do not upload multiple formats for a Comedy or audiobook album onto the same album page.</strong> Each format should have its own torrent page. Likewise, do not group existing single-torrent Comedy and audiobook releases onto a single common album page with multiple formats. Grouping torrents in these non-music categories as if they were music torrents causes a number of database and organizational problems on the site.</li> - </ul> - </div> + <li id="r2.6.9"><a href="#h2.6"><strong>↑_</strong></a> <a href="#r2.6.9">2.6.9.</a> <strong>The following lists should illustrate when Live music may or may not be uploaded.</strong> + <ul> + <li id="r2.6.9.1"><a href="#r2.6.9"><strong>↑_</strong></a> <a href="#r2.6.9.1">2.6.9.1.</a> <strong>Allowed Live Music</strong> + <ul> + <li>Live album bought from an online retailer</li> + <li>CD bought from a brick & mortar store</li> + <li>Soundboard recording that follows the Live Music rules</li> + <li>Soundboard recording that has been adopted by the artist</li> + <li>WEB-sourced live bootleg (released by the artist or a record label)</li> + <li>CD-sourced live bootleg (released by the artist or a record label)</li> + <li>Bootleg album that was later sanctioned by the record label</li> + <li>Soundboard recordings containing a tape generation in the lineage; this will require staff approval unless the tape generation is the first generation</li> + <li>Soundboard recordings that are slightly incomplete due to circumstances beyond control of the recording engineer (i.e. Reel flips, cassette flips or hitting the record button after the band starts playing). Soundboard recordings containing audience/FM/Broadcast/IEM patches containing this missing material may be uploaded. No more than three minutes may be missing. Shows uploaded according to this rule must clearly indicate what is missing and the timing of the missing material. Recordings missing this information may be trumped by recordings in compliance with this rule.</li> + </ul> + </li> + <li id="r2.6.9.2"><a href="#r2.6.9"><strong>↑_</strong></a> <a href="#r2.6.9.2">2.6.9.2.</a> <strong>Disallowed Live Music</strong> + <ul> + <li>Unofficial live mix of a studio album (no artist or record label involvement)</li> + <li>Vanity House mix</li> + <li>Soundboard recordings missing more than three minutes of material</li> + <li>Soundboard recordings containing sound checks or other non-performance portions</li> + <li>Collection of random live tracks from a single artist</li> + <li>Collection of random live tracks from multiple artists</li> + <li>Collection of remixed live tracks</li> + <li>Radio broadcast of a live performance</li> + </ul> + </li> + </ul> + </li> + </ul> + </div> + <h5 id="h2.7"><a href="#h2.7k"><strong>↑</strong></a> <a href="#h2.7">2.7.</a> Multichannel</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r2.7.1"><a href="#h2.7"><strong>↑_</strong></a> <a href="#r2.7.1">2.7.1.</a> <strong>The only lossy multichannel audio formats allowed are AC3 and DTS.</strong> If the source is DTS or AC3, do not transcode to other formats. Exceptions: Transcoding is allowed if the source is lossless (LPCM or MLP). Those should be compressed with multichannel FLAC. See <a href="wiki.php?action=article&id=91">this wiki</a> for more information.</li> + <li id="r2.7.2"><a href="#h2.7"><strong>↑_</strong></a> <a href="#r2.7.2">2.7.2.</a> <strong>DTS-CD rips cannot be re-compressed to a lossless codec and they must be kept as WAV files with the .dts extension.</strong> </li> + <li id="r2.7.3"><a href="#h2.7"><strong>↑_</strong></a> <a href="#r2.7.3">2.7.3.</a> <strong>A multichannel lossless rip of multichannel source media must have the same number of and arrangement of channels as the source media.</strong> In addition, the rip must be encoded at the same bit depth and sampling rate as the original stream. Downsampled versions of the original source material may be trumped by a torrent containing audio files encoded at the original bit depth and sampling rate as the files on disc.</li> + <li id="r2.7.4"><a href="#h2.7"><strong>↑_</strong></a> <a href="#r2.7.4">2.7.4.</a> <strong>A multichannel rip of source media that includes both multichannel and stereo streams may coexist with a rip of the stereo stream off the same media.</strong> A rip of the stereo stream trumps a downmix of the multichannel rip. However, if there is no rip of a stereo stream uploaded, a stereo downmix of a rip from the multichannel stream may coexist with a multichannel rip of the same stream. For example, a DVD-Audio disc may include both stereo LPCM (Linear Pulse-Code Modulation) audio and 5.1 MLP (Meridian Lossless Packing) audio. In this case, a stereo rip of the LPCM stream could coexist with a 5.1 rip of the MLP stream, and the stereo LPCM rip would trump a stereo downmix of a multichannel MLP stream rip. If no stereo LPCM rip of this source media was uploaded, a stereo downmix of the MLP stream could coexist with a 5.1 multichannel rip of the MLP stream.</li> + <li id="r2.7.5"><a href="#h2.7"><strong>↑_</strong></a> <a href="#r2.7.5">2.7.5.</a> <strong>Be aware of the special tagging rules for AC3 torrents (see <a href="#r2.3.16">2.3.16</a>).</strong> </li> + </ul> + </div> + <h5 id="h2.8"><a href="#h2.8k"><strong>↑</strong></a> <a href="#h2.8">2.8.</a> SACD</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r2.8.1"><a href="#h2.8"><strong>↑_</strong></a> <a href="#r2.8.1">2.8.1.</a> <strong>Digital rips produced by the PlayStation 3 (PS3) - SACD ripper process (see <a href="wiki.php?action=article&id=10">this wiki</a>) trump any other SACD digital or analog rips (e.g., Oppo method, analog output to ADC, DSD stream from a SACD player, etc.).</strong> Non-PS3 digital SACD rips trump analog SACD rips. Torrents containing a 88.2 kHz digitally-sourced PCM stream compressed with FLAC are trumpable by new digital rips made directly from the SACD disc using the PS3-SACD ripper method. See <a href="wiki.php?action=article&id=92">this wiki</a> for more information on the SACD format. Note: This means that you cannot download another user's uncompressed DSD source files and manipulate those.</li> + <li id="r2.8.2"><a href="#h2.8"><strong>↑_</strong></a> <a href="#r2.8.2">2.8.2.</a> <strong>Analog SACD rips may have a sampling rate equal to 88.2 or 96 kHz and may be trumped at any time by a digital 24/88.2 SACD rip.</strong> If a digital SACD rip has frequencies higher than 44.1 kHz, contact a moderator privately for approval to upload a 24-bit rip with a sampling rate greater than 88.2 kHz.</li> + <li id="r2.8.3"><a href="#h2.8"><strong>↑_</strong></a> <a href="#r2.8.3">2.8.3.</a> <strong>SACD hybrid rip sources must be properly labeled.</strong> + <ul> + <li id="r2.8.3.1"><a href="#r2.8.3"><strong>↑_</strong></a> <a href="#r2.8.3.1">2.8.3.1.</a> <strong>SACD hybrid discs ripped from the CD layer should be labeled as CD and not SACD.</strong> If you used your CD/DVD drive and a CD ripper (such as <a href="http://www.exactaudiocopy.de/" target="_blank">EAC</a>) to extract the audio, then label it as "CD" sourced because it is not true 24-bit audio.</li> + <li id="r2.8.3.2"><a href="#r2.8.3"><strong>↑_</strong></a> <a href="#r2.8.3.2">2.8.3.2.</a> <strong>If the rip came from the genuine digital SACD layer via a PS3 player or through a SACD player mod or line out, it should be 24-bit quality SACD.</strong> You may include "SACD Hybrid" in the Edition Information box on the <a href="upload.php">upload page</a>.</li> + </ul> + </li> + <li id="r2.8.4"><a href="#h2.8"><strong>↑_</strong></a> <a href="#r2.8.4">2.8.4.</a> <strong>Lineage information for an SACD capture is required and should be included in the torrent folder.</strong> + <ul> + <li id="r2.8.4.1"><a href="#r2.8.4"><strong>↑_</strong></a> <a href="#r2.8.4.1">2.8.4.1.</a> <strong>Both digital and analog SACD rips require lineage information that includes the SACD ripping process, equipment used, applicable conversion software (with the conversion settings), and any post-capture software mastering that was performed.</strong> Any torrents that lack this lineage information are trumpable.</li> + <li id="r2.8.4.2"><a href="#r2.8.4"><strong>↑_</strong></a> <a href="#r2.8.4.2">2.8.4.2.</a> <strong>Digital rips that use the Saracon or Philips converters will trump foobar rips that use the SACD_input converter; all three of these rips would trump a digital rip that uses the Audiogate converter.</strong> </li> + <li id="r2.8.4.3"><a href="#r2.8.4"><strong>↑_</strong></a> <a href="#r2.8.4.3">2.8.4.3.</a> <strong>Digital rips created from the Saracon and Philips converters must be "de-clicked" (PM a moderator for details on the process, which consists of trimming 53 samples from the DFF file and then prepending and appending 12 samples back onto the file during the conversion process).</strong> </li> + <li id="r2.8.4.4"><a href="#r2.8.4"><strong>↑_</strong></a> <a href="#r2.8.4.4">2.8.4.4.</a> <strong>No lineage editing or misrepresentation will be tolerated.</strong> Doing so will result in the loss of upload privileges. If you are unsure of a lineage, then do not provide it. Do NOT guess.</li> + </ul> + </li> + <li id="r2.8.5"><a href="#h2.8"><strong>↑_</strong></a> <a href="#r2.8.5">2.8.5.</a> <strong> Downsampling of SACD sources is allowed in certain cases.</strong> + <ul> + <li id="r2.8.5.1"><a href="#r2.8.5"><strong>↑_</strong></a> <a href="#r2.8.5.1">2.8.5.1.</a> <strong> A multichannel lossless rip must have the same number of and arrangement of channels as the source media.</strong> In addition, the rip must be encoded at the same bit depth and sampling rate as the original stream. Downsampled versions of the original source material may be trumped by a torrent containing audio files ripped directly from the disc at the bit depth and sampling rate originally found on disc. For example, there is a 192 kHz track and a 44.1 kHz track on the SACD. A user uploads only the 192 kHz track and that source is subsequently downsampled to 44.1 kHz. Another user who rips the actual 44.1 kHz track from the original disc may trump the downsampled torrent.</li> + <li id="r2.8.5.2"><a href="#r2.8.5"><strong>↑_</strong></a> <a href="#r2.8.5.2">2.8.5.2.</a> <strong>Downmixing of SACD sources is prohibited.</strong> Do not downmix a 5.1 (or higher) channel mix to stereo and upload the resulting audio files. You may still downconvert lossless audio sources from 24-bit to 16-bit.</li> + <li id="r2.8.5.3"><a href="#r2.8.5"><strong>↑_</strong></a> <a href="#r2.8.5.3">2.8.5.3.</a> <strong> All lossy SACD torrents must be uploaded in a Red Book-compliant form.</strong> Any stereo lossless sources must be downconverted to 16 bits and downsampled to 44.1 kHz before they are transcoded to lossy MP3 formats. No other bit depth or sampling rate is allowed for lossy SACD torrents.</li> + </ul> + </li> + </ul> + </div> + <h5 id="h2.9"><a href="#h2.9k"><strong>↑</strong></a> <a href="#h2.9">2.9.</a> Blu-ray</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r2.9.1"><a href="#h2.9"><strong>↑_</strong></a> <a href="#r2.9.1">2.9.1.</a> <strong>Only digital rips of the lossless audio track from a Blu-ray disc may be uploaded (see <a href="wiki.php?action=article&id=93">this wiki</a> for more information).</strong> Analog Blu-ray rips are forbidden. Blu-ray discs may contain all of the following formats: Dolby Digital (AC3), DTS, Dolby Digital Plus (AC3), DTS-HD, PCM, Dolby TrueHD, DTS-HD Master Audio. Only PCM, Dolby TrueHD, and DTS-HD Master Audio contain truly lossless audio material on the Blu-ray disc. See <a href="#r2.1.1">2.1.1</a> and <a href="#r2.7.1">2.7.1 </a> for more information on multichannel audio material on the site.</li> + <li id="r2.9.2"><a href="#h2.9"><strong>↑_</strong></a> <a href="#r2.9.2">2.9.2.</a> <strong> Uploading movie audio tracks is prohibited.</strong> For example, the Inception Blu-ray disc set features a DTS-HD Master Audio 5.1 surround sound track for the entire movie, which cannot be uploaded, and a DTS-HD Master Audio 5.1 surround sound mix of the movie soundtrack, which can be uploaded. Uploading movie audio tracks will result in a warning and the loss of your uploading privileges.</li> + <li id="r2.9.3"><a href="#h2.9"><strong>↑_</strong></a> <a href="#r2.9.3">2.9.3.</a> <strong> Blu-ray rip sources must be properly labeled.</strong> + <ul> + <li id="r2.9.3.1"><a href="#r2.9.3"><strong>↑_</strong></a> <a href="#r2.9.3.1">2.9.3.1.</a> <strong> If the source was lossy (e.g., AC3 or DTS) do not transcode to other formats.</strong> </li> + <li id="r2.9.3.2"><a href="#r2.9.3"><strong>↑_</strong></a> <a href="#r2.9.3.2">2.9.3.2.</a> <strong> If the source was a genuine lossless track, identify the track in the album edition field, e.g., "16-bit PCM" or "24-bit DTS-HD MA 5.1".</strong> Sampling rate information may also be included (e.g., "16/48 LPCM") in the album edition field. </li> + </ul> + </li> + <li id="r2.9.4"><a href="#h2.9"><strong>↑_</strong></a> <a href="#r2.9.4">2.9.4.</a> <strong>Lineage information for a Blu-ray transfer is required and should be included in the torrent folder.</strong> + <ul> + <li id="r2.9.4.1"><a href="#r2.9.4"><strong>↑_</strong></a> <a href="#r2.9.4.1">2.9.4.1.</a> <strong> All Blu-ray rips require lineage information that includes the identity of the Blu-ray source, any software used for ripping and encoding, disc drive and other equipment used, and details of the transfer process.</strong> Any torrents that lack this lineage information are trumpable.</li> + <li id="r2.9.4.2"><a href="#r2.9.4"><strong>↑_</strong></a> <a href="#r2.9.4.2">2.9.4.2.</a> <strong>No lineage editing or misrepresentation will be tolerated.</strong> Doing so will result in the loss of upload privileges. If you are unsure of a lineage, then do not provide it. Do NOT guess.</li> + </ul> + </li> + <li id="r2.9.5"><a href="#h2.9"><strong>↑_</strong></a> <a href="#r2.9.5">2.9.5.</a> <strong> Downsampling of Blu-ray sources is allowed in certain cases.</strong> + <ul> + <li id="r2.9.5.1"><a href="#r2.9.5"><strong>↑_</strong></a> <a href="#r2.9.5.1">2.9.5.1.</a> <strong> A multichannel lossless rip must have the same number of and arrangement of channels as the source media.</strong> In addition, the rip must be encoded at the same bit depth and sampling rate as the original stream. Downsampled versions of the original source material may be trumped by a torrent containing audio files ripped directly from the disc at the bit depth and sampling rate originally found on disc. For example, there is a 96 kHz track and a 44.1 kHz track on the Blu-ray disc. A user uploads only the 96 kHz track and that source is subsequently downsampled to 44.1 kHz. Another user who rips the actual 44.1 kHz track from the original disc may trump the downsampled torrent.</li> + <li id="r2.9.5.2"><a href="#r2.9.5"><strong>↑_</strong></a> <a href="#r2.9.5.2">2.9.5.2.</a> <strong> Downmixing of Blu-ray sources is prohibited.</strong> Do not downmix a 5.1 (or higher) channel mix to stereo and upload the resulting audio files. You may still downconvert lossless audio sources from 24-bit to 16-bit.</li> + <li id="r2.9.5.3"><a href="#r2.9.5"><strong>↑_</strong></a> <a href="#r2.9.5.3">2.9.5.3.</a> <strong> All lossy Blu-ray torrents must be uploaded in a Red Book-compliant form.</strong> Any stereo lossless sources must be downconverted to 16 bits and downsampled to 44.1 kHz before they are transcoded to lossy MP3 formats. No other bit depth or sampling rate is allowed for lossy Blu-ray torrents.</li> + <li id="r2.9.5.4"><a href="#r2.9.5"><strong>↑_</strong></a> <a href="#r2.9.5.4">2.9.5.4.</a> <strong> Do not downsample, downmix, transcode or otherwise manipulate any lossy Blu-ray format (e.g., Dolby Digital (AC3), DTS, Dolby Digital Plus (AC3), and DTS-HD).</strong> </li> + </ul> + </li> + </ul> + </div> + <h5 id="h2.10"><a href="#h2.10k"><strong>↑</strong></a> <a href="#h2.10">2.10.</a> Cassettes</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r2.10.1"><a href="#h2.10"><strong>↑_</strong></a> <a href="#r2.10.1">2.10.1.</a> <strong>Cassettes are allowed under strict conditions.</strong> "Cassettes" include compact cassettes, 8-track tape cartridges, and non-cassette consumer reel-to-reel audio tape recordings. + <ul> + <li id="r2.10.1.1"><a href="#r2.10.1"><strong>↑_</strong></a> <a href="#r2.10.1.1">2.10.1.1.</a> <strong>Cassette releases may not be uploaded if the release exists on the site in any other format.</strong> A non-cassette release may trump a cassette release. A bonus track which only appears on the cassette release is considered exclusive content for that release, and the full cassette release may be uploaded. A different mastering or a different track order is not sufficient to justify the upload of a cassette rip.</li> + <li id="r2.10.1.2"><a href="#r2.10.1"><strong>↑_</strong></a> <a href="#r2.10.1.2">2.10.1.2.</a> <strong>Rips must be made from official (e.g., artist- or label-approved) cassette sources;</strong> secondhand copies are prohibited. Bootlegs are often secondhand copies and may be deleted outright if they do not meet high quality audio standards. Please see rule <a href="#r2.10.8">2.10.8</a>.</li> + </ul> + </li> + <li id="r2.10.2"><a href="#r2.10"><strong>↑_</strong></a> <a href="#r2.10.2">2.10.2.</a> <strong>All cassette rips should include clear information about lineage.</strong> Include a description of the ripping hardware if this is a personal rip or if a rip downloaded elsewhere includes this information. It is strongly recommended that uploaders include a photo of the cassette and artwork in the torrent description. Any torrents that lack lineage information are trumpable by those that have lineage information.</li> + <li id="r2.10.3"><a href="#r2.10"><strong>↑_</strong></a> <a href="#r2.10.3">2.10.3.</a> <strong>Cassette uploads must be ripped at a sample rate of 44.1 kHz or 48 kHz and 88.2 kHz or 96 kHz.</strong> Cassette uploads with sample rates above 96 kHz may be deleted at moderator discretion. Lossy transcodes of cassette FLACs must retain the sample rate of the original FLAC if it is 44.1 kHz or 48 kHz. 44.1 kHz and 48 kHz rips may not exist concurrently; 88.2 kHz and 96 kHz may not exist concurrently; if one is already available, the other is considered a dupe unless a substantial improvement in audio quality is evident (see rule <a href="#r2.10.6">2.10.6</a>).</li> + <li id="r2.10.4"><a href="#r2.10"><strong>↑_</strong></a> <a href="#r2.10.4">2.10.4.</a> <strong>Cassette rips will be trumped by an upload of the same release in any other medium.</strong> A cassette edition with bonus tracks not available in another edition is not subject to trumping.</li> + <li id="r2.10.5"><a href="#r2.10"><strong>↑_</strong></a> <a href="#r2.10.5">2.10.5.</a> <strong>Cassette rips are subject to the duplicates and trumping rules outlined in rule <a href="#h2.2">2.2</a>.</strong></li> + <li id="r2.10.6"><a href="#r2.10"><strong>↑_</strong></a> <a href="#r2.10.6">2.10.6.</a> <strong>Cassette rips can be trumped if a substantially better-sounding version is uploaded.</strong> To trump an old rip with a new torrent that may be a better-sounding version, you need to report it with clear information about how your rip sounds better than the other one, with references to specific tracks and time positions to justify your report. All quality trumps/deletions of this nature are at the discretion of the moderator involved.</li> + <li id="r2.10.7"><a href="#r2.10"><strong>↑_</strong></a> <a href="#r2.10.7">2.10.7.</a> <strong>Cassette rips of extremely poor quality (lossy or lossless) may be deleted outright if reported.</strong> All quality trumps/deletions of this nature are at the discretion of the moderator involved.</li> + <li id="r2.10.8"><a href="#r2.10"><strong>↑_</strong></a> <a href="#r2.10.8">2.10.8.</a> <strong>Exceptions to any of the above rules may be made at a moderator's discretion.</strong> Please contact staff <em>before uploading</em> with detailed reasons why such an exception should be considered. Examples: a higher sample rate for a high quality reel-to-reel recording, a cassette release that was subsequently released on brickwalled CD master, a lower sample rate for a good rip of a Type I cassette, a good rip of a second generation tape with rare content. This is not an exhaustive list of possible reasons to grant an exception.</li> + </ul> + </div> + <h4 id="h3"><a href="#h3k"><strong>↑</strong></a> <a href="#h3">3.</a> Comedy (Audio) & Audiobooks</h4> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r3.1"><a href="#r3.1k"><strong>↑_</strong></a> <a href="#r3.1">3.1.</a> The only formats allowed for comedy and audiobooks are those listed below: + <ul> + <li>MP3, AAC, FLAC</li> + <li id="r3.1.1"><a href="#r3.1"><strong>↑_</strong></a> <a href="#r3.1.1">3.1.1.</a> <strong>Lossless audio files are allowed at all times, in accordance with the dupe rules (see <a href="#r3.9">3.9</a>).</strong> </li> + <li id="r3.1.2"><a href="#r3.1"><strong>↑_</strong></a> <a href="#r3.1.2">3.1.2.</a> <strong>Lossy comedy and audiobook torrents must have a minimum average bitrate of 32 kbps (for MP3 and AAC).</strong> </li> + </ul> + </li> + <li id="r3.2"><a href="#r3.2k"><strong>↑_</strong></a> <a href="#r3.2">3.2.</a> <strong>No music is permitted in these two categories. They are for spoken word only. See <a href="wiki.php?action=article&id=94">this wiki</a> for more information.</strong> For example, if a comedy artist performs a stand-up routine that is recorded on CD, this would be classified as a Comedy album. If this same artist sings some funny songs on an album, this would be classified as a Comedy album. If this same artist produces a music CD with no hint of comedic material (e.g., spoken word tracks), this would be classified as a music album (see <a href="#h2.1">2.1</a>). </li> + <li id="r3.3"><a href="#r3.3k"><strong>↑_</strong></a> <a href="#r3.3">3.3.</a> <strong>No radio talk shows or podcasts are allowed.</strong> <?=SITE_NAME?> is primarily a music site, and those recordings do not belong in any torrent category. </li> + <li id="r3.4"><a href="#r3.4k"><strong>↑_</strong></a> <a href="#r3.4">3.4.</a> <strong>Comedy and audiobooks must not be freely available.</strong> Free audiobooks and comedy releases from official sources may not be uploaded. Exceptions: Uploads that are at a different bitrate from those freely available are allowed. If a comedy album in 96 kbps CBR MP3 is freely available, you may still upload a V8 (VBR) or higher bitrate torrent of the freely available torrent if it does not already exist on the site. </li> + <li id="r3.5"><a href="#r3.5k"><strong>↑_</strong></a> <a href="#r3.5">3.5.</a> <strong>Releases must be unarchived and consist of a single release.</strong> Comedy and audiobook releases should not be archived in a file (e.g., .zip or .rar). The torrent must consist of a directory containing the audio files. Only one release per torrent is allowed. You may not bundle multiple audiobooks or comedy releases into one torrent. See <a href="#r1.2.7">1.2.7</a> and <a href="#r2.1.20">2.1.20</a>. </li> + <li id="r3.6"><a href="#r3.6k"><strong>↑_</strong></a> <a href="#r3.6">3.6.</a> <strong>All comedy and audiobook releases must have an average bitrate of at least 32 kbps</strong>. However, higher bitrates are preferred over this minimum requirement. + <ul> + <li id="r3.6.1"><a href="#r3.6"><strong>↑_</strong></a> <a href="#r3.6.1">3.6.1.</a> <strong>V0 (VBR), V2 (VBR), and V8 (VBR) MP3 torrents for the same album are allowed at all times.</strong> </li> + <li id="r3.6.2"><a href="#r3.6"><strong>↑_</strong></a> <a href="#r3.6.2">3.6.2.</a> <strong>V8 (VBR) replaces V3 (VBR) through V7 (VBR) and replaces CBR rips between 32 kbps and 191 kbps.</strong> Once a rip with a V8 (VBR) encoding preset has been uploaded, you are not allowed to upload any V3 (VBR) through V7 (VBR) torrents or any CBR torrents under 192 kbps.</li> + <li id="r3.6.3"><a href="#r3.6"><strong>↑_</strong></a> <a href="#r3.6.3">3.6.3.</a> <strong>V2 (VBR) replaces CBR rips between 192 kbps and 320 kbps.</strong> Once a rip with a V2 (VBR) encoding preset has been uploaded, you are not allowed to upload any CBR torrents.</li> + <li id="r3.6.4"><a href="#r3.6"><strong>↑_</strong></a> <a href="#r3.6.4">3.6.4.</a> <strong>V0 (VBR) replaces V1 (VBR) and replaces CBR rips at 320 kbps and above.</strong> Once a rip with a V0 (VBR) encoding preset has been uploaded, you are not allowed to upload a V1 (VBR) torrent or any CBR torrents at 320 kbps and above.</li> + <li id="r3.6.5"><a href="#r3.6"><strong>↑_</strong></a> <a href="#r3.6.5">3.6.5.</a> <strong>The preferred AAC bitrate is 64 kbps.</strong> All other AAC bitrates may be trumped by a 64 kbps upload. Once a 64 kbps AAC torrent has been uploaded, no further AAC uploads are allowed for that album.</li> + </ul> + </li> + <li id="r3.7"><a href="#r3.7k"><strong>↑_</strong></a> <a href="#r3.7">3.7.</a> <strong>Lossy sources, including lossy transcodes, are allowed in this section only.</strong> The source may come from cassette, VHS (audio), radio, or a higher bitrate lossy file. While the sharing of transcoded material is strongly discouraged for music, the audio quality is less important for spoken word material. Note: You may not transcode a lower bitrate file to a higher bitrate file and upload it here. For example, if you find a 32 kbps CBR WMA you cannot transcode it to 64 kbps CBR MP3 and share this on the site.</li> + <li id="r3.8"><a href="#r3.8k"><strong>↑_</strong></a> <a href="#r3.8">3.8.</a> <strong>Online audiobook sources may carry only low bitrate audio files.</strong> Audiobook sources such as <a href="http://www.audible.com/">audible.com</a> have standard audio quality levels that may be at or below the standards for the site (see <a href="#r3.1.2">3.1.2</a>). Please make sure that you know the audio quality of your source material (e.g., many audiobook retailers only supply 64 kbps files) before you transcode to V8 (VBR), V2 (VBR), or V0 (VBR). Transcoding from a lower bitrate file to a higher bitrate file is forbidden.</li> + <li id="r3.9"><a href="#r3.9k"><strong>↑_</strong></a> <a href="#r3.9">3.9.</a> <strong>Duplicate torrents of the same bitrate and format are not allowed.</strong> No uploads of the same bitrate and format are allowed to coexist on the site. See <a href="#r3.6">3.6</a>.</li> + <li id="r3.10"><a href="#r3.10k"><strong>↑_</strong></a> <a href="#r3.10">3.10.</a> <strong>Scene and non-scene versions of the same release, bitrate, and format are dupes.</strong> Non-scene releases trump Scene releases. If a Scene and non-scene release coexist, the non-scene release trumps the Scene release, regardless of which release was uploaded first.</li> + <li id="r3.11"><a href="#r3.11k"><strong>↑_</strong></a> <a href="#r3.11">3.11.</a> <strong>Releases must follow the usual formatting guidelines for file names and tags.</strong> Audiobooks and comedy must follow the formatting rules outlined in section <a href="#h2.3">2.3</a>. This means that file names must include track numbers, audio files must have metatags, etc.</li> + <li id="r3.12"><a href="#r3.12k"><strong>↑_</strong></a> <a href="#r3.12">3.12.</a> <strong>Do not upload multiple formats for a Comedy or audiobook album onto the same album page.</strong> Each format should have its own torrent page. Likewise, do not group existing single-torrent Comedy and audiobook releases onto a single common album page with multiple formats. Grouping torrents in these non-music categories as if they were music torrents causes a number of database and organizational problems on the site.</li> + </ul> + </div> - <h4 id="h4"><a href="#h4k"><strong>↑</strong></a> <a href="#h4">4.</a> Applications</h4> - <h5 id="h4.1"><a href="#h4.1k"><strong>↑</strong></a> <a href="#h4.1">4.1.</a> General</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r4.1.1"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.1">4.1.1.</a> <strong>App releases can be either a torrent of a directory or a single archive.</strong> - <ul> - <li id="r4.1.1.1"><a href="#r4.1.1"><strong>↑_</strong></a> <a href="#r4.1.1.1">4.1.1.1.</a> <strong>Scene released applications may be archived but must not be password protected.</strong> If archives were originally password protected and had the protection removed, they cannot be represented as official scene releases and should not be labeled as scene torrents. The file hashes between protected and unprotected archives are different, resulting in a modification of the original files. Some packs and collections cannot be archived (see <a href="#r4.1.9">4.1.9</a>).</li> - <li id="r4.1.1.2"><a href="#r4.1.1"><strong>↑_</strong></a> <a href="#r4.1.1.2">4.1.1.2.</a> <strong>Mac applications must be archived for them to function.</strong> Mac applications uploaded as .app directories will require a CHMOD operation, which is unnecessary when the application is properly archived (e.g., .dmg, .zip, etc.).</li> - </ul> - </li> - <li id="r4.1.2"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.2">4.1.2.</a> <strong>All applications must come with a crack, keygen, or other method of ensuring that downloaders can install them easily.</strong> App torrents with keygens, cracks, or patches that do not work or torrents missing clear installation instructions will be deleted if reported. No exceptions.</li> - <li id="r4.1.3"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.3">4.1.3.</a> <strong>App releases must not be freely available tools.</strong> Application releases cannot be freely downloaded anywhere from any official source. Nor may you upload open source applications where the source code is available for free. Closed or shareware installers like Crossover Office are allowed. Note: If all official sources stop hosting and remove a freely available application and its source code from their site(s) due to varying reasons (e.g., legal concerns, dead development, etc.), the application ceases to be freely available. You may then upload it in that case.</li> - <li id="r4.1.4"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.4">4.1.4.</a> <strong>Release descriptions for applications must contain good information about the application.</strong> You should either have a small description of the program (either taken from its web site or from an NFO file) or a link to the information — but ideally both. Torrents missing this information will be deleted when reported.</li> - <li id="r4.1.5"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.5">4.1.5.</a> <strong>The torrent title must have a descriptive name.</strong> The torrent title should at least include the application name and release version. Optionally, you may include additional labels for operating system and method of circumvention (e.g., crack, patch, keygen, or serial). For example, "AcrylicApps Wallet v3.0.1.493 MacOSX Cracked".</li> - <li id="r4.1.6"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.6">4.1.6.</a> <strong>Use relevant tags for your torrent.</strong> Add all applicable default <a href="?p=tag">Gazelle tags</a> to help downloaders find your torrent. The default tags are apps.mac for Mac applications, apps.windows for Windows applications, and apps.sound for audio applications. You may add additional tags if the default ones do not apply (such as apps.linux).</li> - <li id="r4.1.7"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.7">4.1.7.</a> <strong>Games of any kind are prohibited (see <a href="#r1.2.5">1.2.5</a>).</strong> </li> - <li id="r4.1.8"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.8">4.1.8.</a> <strong>Application "packs" are not allowed.</strong> That means no 0-day packs or "video utilities" compilations. Also, packaging multiple versions of the same application into a single torrent is not allowed (e.g., Adobe Acrobat 8 and Adobe Acrobat 9 in a single torrent). Exceptions: The applications are from the same company and an official release. For example, Adobe CS and Macromedia Studio packaged in one set.</li> - <li id="r4.1.9"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.9">4.1.9.</a> <strong>Sound sample packs, template collections, and font collections are allowed if they are official releases, not freely available, and unarchived.</strong> Sound sample packs, template collections, and font collections must be official compilations and they must not be uploaded as an archive. The files contained inside the torrent must not be archived so that users can see what the pack contains. That means if sound sample packs are in WAV format, they must be uploaded as WAV. If the font collection, template collection, or sound sample pack was originally released as an archive, you must unpack the files before uploading them in a torrent. None of the contents in these packs and collections may be freely available.</li> - <li id="r4.1.10"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.10">4.1.10.</a> <strong>Application components such as plug-ins, add-ons, expansions, filters, and so forth may be uploaded in a collection if they correspond to a particular application.</strong> You may upload plug-ins, expansions, add-ons, filters, and other application components as collections provided they are compatible to a particular application and version. For example, you may not upload a megapack of all filters for Adobe Photoshop CS2, CS3, and CS4. But you may upload a pack of Adobe Photoshop CS4 filters.</li> - <li id="r4.1.11"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.11">4.1.11.</a> <strong>Collections of cracks, keygens or serials are not allowed.</strong> The crack, keygen, or serial for an application must be in a torrent with its corresponding application. It cannot be uploaded separately from the application.</li> - <li id="r4.1.12"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.12">4.1.12.</a> <strong>Torrents containing hacking or cracking tools are prohibited.</strong> </li> - <li id="r4.1.13"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.13">4.1.13.</a> <strong>Never post serial numbers in torrent descriptions.</strong> Serial numbers should be in a text file contained within the torrent. If a serial number is posted in the torrent description and not included as a text file in the torrent folder, the torrent will be removed when reported. No exceptions.</li> - <li id="r4.1.14"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.14">4.1.14.</a> <strong>All applications must be complete.</strong> If an application consists of multiple CDs or DVDs, these should all be uploaded as one torrent and not as separate torrents. This also applies to scene uploads where multiple CDs or DVDs were released separately.</li> - </ul> - </div> - <h5 id="h4.2"><a href="#h4.2k"><strong>↑</strong></a> <a href="#h4.2">4.2.</a> Duplicates & Trumping</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r4.2.1"><a href="#h4.2"><strong>↑_</strong></a> <a href="#r4.2.1">4.2.1.</a> <strong>Applications having the same version number are dupes.</strong> An application may have older versions than those already uploaded. Those are not dupes. Only identical versions are duplicates. Note: Not everyone has updated to the latest operating system. In such cases, older versions of applications may still be useful for a number of users.</li> - <li id="r4.2.2"><a href="#h4.2"><strong>↑_</strong></a> <a href="#r4.2.2">4.2.2.</a> <strong>A scene archived torrent may coexist with an unarchived torrent of the same application version.</strong> If both a scene archive and an unarchived copy are uploaded of the same application and version, both may stay on the site. Any subsequent uploads of the same torrent in either form (with identical install methods) are dupes.</li> - <li id="r4.2.3"><a href="#h4.2"><strong>↑_</strong></a> <a href="#r4.2.3">4.2.3.</a> <strong>Different language editions of the same application and version are unique.</strong> Multi-language versions and single language versions of different languages are not considered dupes.</li> - <li id="r4.2.4"><a href="#h4.2"><strong>↑_</strong></a> <a href="#r4.2.4">4.2.4.</a> <strong>Existing application torrents can be trumped by other torrents with better install methods.</strong> Applications with serial keys may be trumped by crack/patch versions or torrents with keygens. Once an application with either a crack/patch or keygen is uploaded to the site, no other identical application with a different installation method is allowed. Report the older torrent when you are trumping it with a torrent of the same application containing an improved method of installation.</li> - <li id="r4.2.5"><a href="#h4.2"><strong>↑_</strong></a> <a href="#r4.2.5">4.2.5.</a> <strong>Applications that no longer work will be deleted when reported.</strong> If the install method for an application no longer works (e.g., the developer has coded a patch, the install method is no longer supported, etc.) the torrent will be removed.</li> - </ul> - </div> + <h4 id="h4"><a href="#h4k"><strong>↑</strong></a> <a href="#h4">4.</a> Applications</h4> + <h5 id="h4.1"><a href="#h4.1k"><strong>↑</strong></a> <a href="#h4.1">4.1.</a> General</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r4.1.1"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.1">4.1.1.</a> <strong>App releases can be either a torrent of a directory or a single archive.</strong> + <ul> + <li id="r4.1.1.1"><a href="#r4.1.1"><strong>↑_</strong></a> <a href="#r4.1.1.1">4.1.1.1.</a> <strong>Scene released applications may be archived but must not be password protected.</strong> If archives were originally password protected and had the protection removed, they cannot be represented as official scene releases and should not be labeled as scene torrents. The file hashes between protected and unprotected archives are different, resulting in a modification of the original files. Some packs and collections cannot be archived (see <a href="#r4.1.9">4.1.9</a>).</li> + <li id="r4.1.1.2"><a href="#r4.1.1"><strong>↑_</strong></a> <a href="#r4.1.1.2">4.1.1.2.</a> <strong>Mac applications must be archived for them to function.</strong> Mac applications uploaded as .app directories will require a CHMOD operation, which is unnecessary when the application is properly archived (e.g., .dmg, .zip, etc.).</li> + </ul> + </li> + <li id="r4.1.2"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.2">4.1.2.</a> <strong>All applications must come with a crack, keygen, or other method of ensuring that downloaders can install them easily.</strong> App torrents with keygens, cracks, or patches that do not work or torrents missing clear installation instructions will be deleted if reported. No exceptions.</li> + <li id="r4.1.3"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.3">4.1.3.</a> <strong>App releases must not be freely available tools.</strong> Application releases cannot be freely downloaded anywhere from any official source. Nor may you upload open source applications where the source code is available for free. Closed or shareware installers like Crossover Office are allowed. Note: If all official sources stop hosting and remove a freely available application and its source code from their site(s) due to varying reasons (e.g., legal concerns, dead development, etc.), the application ceases to be freely available. You may then upload it in that case.</li> + <li id="r4.1.4"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.4">4.1.4.</a> <strong>Release descriptions for applications must contain good information about the application.</strong> You should either have a small description of the program (either taken from its web site or from an NFO file) or a link to the information — but ideally both. Torrents missing this information will be deleted when reported.</li> + <li id="r4.1.5"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.5">4.1.5.</a> <strong>The torrent title must have a descriptive name.</strong> The torrent title should at least include the application name and release version. Optionally, you may include additional labels for operating system and method of circumvention (e.g., crack, patch, keygen, or serial). For example, "AcrylicApps Wallet v3.0.1.493 MacOSX Cracked".</li> + <li id="r4.1.6"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.6">4.1.6.</a> <strong>Use relevant tags for your torrent.</strong> Add all applicable default <a href="?p=tag">Gazelle tags</a> to help downloaders find your torrent. The default tags are apps.mac for Mac applications, apps.windows for Windows applications, and apps.sound for audio applications. You may add additional tags if the default ones do not apply (such as apps.linux).</li> + <li id="r4.1.7"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.7">4.1.7.</a> <strong>Games of any kind are prohibited (see <a href="#r1.2.5">1.2.5</a>).</strong> </li> + <li id="r4.1.8"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.8">4.1.8.</a> <strong>Application "packs" are not allowed.</strong> That means no 0-day packs or "video utilities" compilations. Also, packaging multiple versions of the same application into a single torrent is not allowed (e.g., Adobe Acrobat 8 and Adobe Acrobat 9 in a single torrent). Exceptions: The applications are from the same company and an official release. For example, Adobe CS and Macromedia Studio packaged in one set.</li> + <li id="r4.1.9"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.9">4.1.9.</a> <strong>Sound sample packs, template collections, and font collections are allowed if they are official releases, not freely available, and unarchived.</strong> Sound sample packs, template collections, and font collections must be official compilations and they must not be uploaded as an archive. The files contained inside the torrent must not be archived so that users can see what the pack contains. That means if sound sample packs are in WAV format, they must be uploaded as WAV. If the font collection, template collection, or sound sample pack was originally released as an archive, you must unpack the files before uploading them in a torrent. None of the contents in these packs and collections may be freely available.</li> + <li id="r4.1.10"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.10">4.1.10.</a> <strong>Application components such as plug-ins, add-ons, expansions, filters, and so forth may be uploaded in a collection if they correspond to a particular application.</strong> You may upload plug-ins, expansions, add-ons, filters, and other application components as collections provided they are compatible to a particular application and version. For example, you may not upload a megapack of all filters for Adobe Photoshop CS2, CS3, and CS4. But you may upload a pack of Adobe Photoshop CS4 filters.</li> + <li id="r4.1.11"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.11">4.1.11.</a> <strong>Collections of cracks, keygens or serials are not allowed.</strong> The crack, keygen, or serial for an application must be in a torrent with its corresponding application. It cannot be uploaded separately from the application.</li> + <li id="r4.1.12"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.12">4.1.12.</a> <strong>Torrents containing hacking or cracking tools are prohibited.</strong> </li> + <li id="r4.1.13"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.13">4.1.13.</a> <strong>Never post serial numbers in torrent descriptions.</strong> Serial numbers should be in a text file contained within the torrent. If a serial number is posted in the torrent description and not included as a text file in the torrent folder, the torrent will be removed when reported. No exceptions.</li> + <li id="r4.1.14"><a href="#h4.1"><strong>↑_</strong></a> <a href="#r4.1.14">4.1.14.</a> <strong>All applications must be complete.</strong> If an application consists of multiple CDs or DVDs, these should all be uploaded as one torrent and not as separate torrents. This also applies to scene uploads where multiple CDs or DVDs were released separately.</li> + </ul> + </div> + <h5 id="h4.2"><a href="#h4.2k"><strong>↑</strong></a> <a href="#h4.2">4.2.</a> Duplicates & Trumping</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r4.2.1"><a href="#h4.2"><strong>↑_</strong></a> <a href="#r4.2.1">4.2.1.</a> <strong>Applications having the same version number are dupes.</strong> An application may have older versions than those already uploaded. Those are not dupes. Only identical versions are duplicates. Note: Not everyone has updated to the latest operating system. In such cases, older versions of applications may still be useful for a number of users.</li> + <li id="r4.2.2"><a href="#h4.2"><strong>↑_</strong></a> <a href="#r4.2.2">4.2.2.</a> <strong>A scene archived torrent may coexist with an unarchived torrent of the same application version.</strong> If both a scene archive and an unarchived copy are uploaded of the same application and version, both may stay on the site. Any subsequent uploads of the same torrent in either form (with identical install methods) are dupes.</li> + <li id="r4.2.3"><a href="#h4.2"><strong>↑_</strong></a> <a href="#r4.2.3">4.2.3.</a> <strong>Different language editions of the same application and version are unique.</strong> Multi-language versions and single language versions of different languages are not considered dupes.</li> + <li id="r4.2.4"><a href="#h4.2"><strong>↑_</strong></a> <a href="#r4.2.4">4.2.4.</a> <strong>Existing application torrents can be trumped by other torrents with better install methods.</strong> Applications with serial keys may be trumped by crack/patch versions or torrents with keygens. Once an application with either a crack/patch or keygen is uploaded to the site, no other identical application with a different installation method is allowed. Report the older torrent when you are trumping it with a torrent of the same application containing an improved method of installation.</li> + <li id="r4.2.5"><a href="#h4.2"><strong>↑_</strong></a> <a href="#r4.2.5">4.2.5.</a> <strong>Applications that no longer work will be deleted when reported.</strong> If the install method for an application no longer works (e.g., the developer has coded a patch, the install method is no longer supported, etc.) the torrent will be removed.</li> + </ul> + </div> - <h4 id="h5"><a href="#h5k"><strong>↑</strong></a> <a href="#h5">5.</a> Comic Books</h4> - <h5 id="h5.1"><a href="#h5.1k"><strong>↑</strong></a> <a href="#h5.1">5.1.</a> General</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r5.1.1"><a href="#h5.1"><strong>↑_</strong></a> <a href="#r5.1.1">5.1.1.</a> <strong>Comic books must be uploaded in the following specified formats, according to descending preference:</strong> - <ul> - <li>A rar archive (Preferably with the .cbr extension)</li> - <li>A zip archive (Preferably with the .cbz extension)</li> - <li>A PDF file</li> - <li>A directory containing only the images themselves</li> - </ul> - </li> - <li id="r5.1.2"><a href="#h5.1"><strong>↑_</strong></a> <a href="#r5.1.2">5.1.2.</a> <strong>Pages must be scanned cleanly and be of good quality.</strong> Scans of poor quality will be deleted, especially if the quality is so poor as to render the image difficult to read. Poorer quality scans may be acceptable for very old or rare comics with staff approval.</li> - <li id="r5.1.3"><a href="#h5.1"><strong>↑_</strong></a> <a href="#r5.1.3">5.1.3.</a> <strong>Comic books must not be freely available.</strong> Comics must be official publications and cannot be taken for free from official sources. You may upload comics from other torrent and unofficial distribution sites, but it is your responsibility to make sure that they conform to our quality and formatting rules for comic books. Note: If all official sources stop hosting a comic and remove the official freely available release from their site(s), the release ceases to be freely available. You may only upload it at that time.</li> - <li id="r5.1.4"><a href="#h5.1"><strong>↑_</strong></a> <a href="#r5.1.4">5.1.4.</a> <strong>0-Day comic uploads are allowed and encouraged.</strong> </li> - </ul> - </div> + <h4 id="h5"><a href="#h5k"><strong>↑</strong></a> <a href="#h5">5.</a> Comic Books</h4> + <h5 id="h5.1"><a href="#h5.1k"><strong>↑</strong></a> <a href="#h5.1">5.1.</a> General</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r5.1.1"><a href="#h5.1"><strong>↑_</strong></a> <a href="#r5.1.1">5.1.1.</a> <strong>Comic books must be uploaded in the following specified formats, according to descending preference:</strong> + <ul> + <li>A rar archive (Preferably with the .cbr extension)</li> + <li>A zip archive (Preferably with the .cbz extension)</li> + <li>A PDF file</li> + <li>A directory containing only the images themselves</li> + </ul> + </li> + <li id="r5.1.2"><a href="#h5.1"><strong>↑_</strong></a> <a href="#r5.1.2">5.1.2.</a> <strong>Pages must be scanned cleanly and be of good quality.</strong> Scans of poor quality will be deleted, especially if the quality is so poor as to render the image difficult to read. Poorer quality scans may be acceptable for very old or rare comics with staff approval.</li> + <li id="r5.1.3"><a href="#h5.1"><strong>↑_</strong></a> <a href="#r5.1.3">5.1.3.</a> <strong>Comic books must not be freely available.</strong> Comics must be official publications and cannot be taken for free from official sources. You may upload comics from other torrent and unofficial distribution sites, but it is your responsibility to make sure that they conform to our quality and formatting rules for comic books. Note: If all official sources stop hosting a comic and remove the official freely available release from their site(s), the release ceases to be freely available. You may only upload it at that time.</li> + <li id="r5.1.4"><a href="#h5.1"><strong>↑_</strong></a> <a href="#r5.1.4">5.1.4.</a> <strong>0-Day comic uploads are allowed and encouraged.</strong> </li> + </ul> + </div> - <h5 id="h5.2"><a href="#h5.2k"><strong>↑</strong></a> <a href="#h5.2">5.2.</a> Multi-comic</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r5.2.1"><a href="#h5.2"><strong>↑_</strong></a> <a href="#r5.2.1">5.2.1.</a> <strong>Multi-comic and series packs must follow site formatting requirements.</strong> Multi-comic and series packs are both accepted and encouraged but care must be taken to make a valid compilation. The rules in this section outline the requirements for multi-comic torrents.</li> - <li id="r5.2.2"><a href="#h5.2"><strong>↑_</strong></a> <a href="#r5.2.2">5.2.2.</a> <strong>0-Day comic packs are allowed.</strong> Make sure such uploads are synchronized with any previous packs. 0-Day comic uploads must not be missing any of their corresponding DCP or Minutemen scans for that time period.</li> - <li id="r5.2.3"><a href="#h5.2"><strong>↑_</strong></a> <a href="#r5.2.3">5.2.3.</a> <strong>Collections may not span more than one comic title.</strong> You may not include multiple, different comic titles in a single collection, e.g., "The Amazing Spider-Man #1" and "The Incredible Hulk #1". Exceptions: Titles may contain more than one comic title if either of the following is true: it's a recognized comic crossover/event or it's a DCP weekly release.</li> - <li id="r5.2.4"><a href="#h5.2"><strong>↑_</strong></a> <a href="#r5.2.4">5.2.4.</a> <strong>Any "multi-part" comic enveloping the whole event is allowed as a single torrent.</strong> Whole events may be uploaded together. For example, the comics "<samp>Buffy the Vampire Slayer Season Eight - 2007 - part 1.cbr</samp>" and "<samp>Buffy the Vampire Slayer Season Eight - 2007 - The Long Way Home Part 2.cbr</samp>" can be uploaded as a single torrent provided there are only 2 parts to "The Long Way Home".</li> - <li id="r5.2.5"><a href="#h5.2"><strong>↑_</strong></a> <a href="#r5.2.5">5.2.5.</a> <strong>Torrents containing complete volumes of comics may be uploaded.</strong> For example, "The Amazing Spider-Man Vol. 1 #1-#441" can be uploaded.</li> - <li id="r5.2.6"><a href="#h5.2"><strong>↑_</strong></a> <a href="#r5.2.6">5.2.6.</a> <strong>Torrents spanning multiple volumes are too large and must be uploaded as separate volumes.</strong> </li> - <li id="r5.2.7"><a href="#h5.2"><strong>↑_</strong></a> <a href="#r5.2.7">5.2.7.</a> <strong>Torrents containing specific #Number-#CurrentDay issues are allowed only if the comics appear in no other pack.</strong> For example, if #1-#35 are already on the site and the current issue is #50, #1-#50 is NOT allowed to be uploaded, but #36-#50 is allowed.</li> - </ul> - </div> + <h5 id="h5.2"><a href="#h5.2k"><strong>↑</strong></a> <a href="#h5.2">5.2.</a> Multi-comic</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r5.2.1"><a href="#h5.2"><strong>↑_</strong></a> <a href="#r5.2.1">5.2.1.</a> <strong>Multi-comic and series packs must follow site formatting requirements.</strong> Multi-comic and series packs are both accepted and encouraged but care must be taken to make a valid compilation. The rules in this section outline the requirements for multi-comic torrents.</li> + <li id="r5.2.2"><a href="#h5.2"><strong>↑_</strong></a> <a href="#r5.2.2">5.2.2.</a> <strong>0-Day comic packs are allowed.</strong> Make sure such uploads are synchronized with any previous packs. 0-Day comic uploads must not be missing any of their corresponding DCP or Minutemen scans for that time period.</li> + <li id="r5.2.3"><a href="#h5.2"><strong>↑_</strong></a> <a href="#r5.2.3">5.2.3.</a> <strong>Collections may not span more than one comic title.</strong> You may not include multiple, different comic titles in a single collection, e.g., "The Amazing Spider-Man #1" and "The Incredible Hulk #1". Exceptions: Titles may contain more than one comic title if either of the following is true: it's a recognized comic crossover/event or it's a DCP weekly release.</li> + <li id="r5.2.4"><a href="#h5.2"><strong>↑_</strong></a> <a href="#r5.2.4">5.2.4.</a> <strong>Any "multi-part" comic enveloping the whole event is allowed as a single torrent.</strong> Whole events may be uploaded together. For example, the comics "<samp>Buffy the Vampire Slayer Season Eight - 2007 - part 1.cbr</samp>" and "<samp>Buffy the Vampire Slayer Season Eight - 2007 - The Long Way Home Part 2.cbr</samp>" can be uploaded as a single torrent provided there are only 2 parts to "The Long Way Home".</li> + <li id="r5.2.5"><a href="#h5.2"><strong>↑_</strong></a> <a href="#r5.2.5">5.2.5.</a> <strong>Torrents containing complete volumes of comics may be uploaded.</strong> For example, "The Amazing Spider-Man Vol. 1 #1-#441" can be uploaded.</li> + <li id="r5.2.6"><a href="#h5.2"><strong>↑_</strong></a> <a href="#r5.2.6">5.2.6.</a> <strong>Torrents spanning multiple volumes are too large and must be uploaded as separate volumes.</strong> </li> + <li id="r5.2.7"><a href="#h5.2"><strong>↑_</strong></a> <a href="#r5.2.7">5.2.7.</a> <strong>Torrents containing specific #Number-#CurrentDay issues are allowed only if the comics appear in no other pack.</strong> For example, if #1-#35 are already on the site and the current issue is #50, #1-#50 is NOT allowed to be uploaded, but #36-#50 is allowed.</li> + </ul> + </div> - <h5 id="h5.3"><a href="#h5.3k"><strong>↑</strong></a> <a href="#h5.3">5.3.</a> Duplicates & Trumping</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r5.3.1"><a href="#h5.3"><strong>↑_</strong></a> <a href="#r5.3.1">5.3.1.</a> <strong>A dupe of a single comic is defined as two scans of the same book by the same scanner, where the same pages have been scanned.</strong> The scanner may be an individual, a release group, or a scanning device. Exceptions: The following examples are NOT dupes: - <ul> - <li>Two copies of the same book by the same scanner, but one is a c2c copy and the other is a "no ads" copy</li> - <li>Two scans of the same book by different scanners</li> - <li>Two scans of the same book by the same scanner, when the copy you're uploading contains fixes</li> - </ul> - </li> - <li id="r5.3.2"><a href="#h5.3"><strong>↑_</strong></a> <a href="#r5.3.2">5.3.2.</a> <strong>Releases in .cbz and .cbr are always allowed, with preference given to the earliest upload.</strong> In the event of a dupe occurring between a .cbr and a .cbz, the earliest upload remains. In the event of any other dupe, the order listed in <a href="#r5.1.1">5.1.1</a> determines which torrent is kept. For example, if a PDF is uploaded followed by a .cbr, the .cbr remains on the site and the PDF is deleted as a trump. If a .cbz is uploaded followed by a PDF, the .cbz remains on the site and the PDF is deleted as a dupe.</li> - <li id="r5.3.3"><a href="#h5.3"><strong>↑_</strong></a> <a href="#r5.3.3">5.3.3.</a> <strong>Multi-comic collections and packs must conform to either of these collection types in order of preference.</strong> - <ul> - <li id="r5.3.3.1"><a href="#r5.3.3"><strong>↑_</strong></a> <a href="#r5.3.3.1">5.3.3.1.</a> <strong>A torrent consisting of issues #1-50 is currently on the site.</strong> The only packs that are allowed to contain any of the comics before #50 are a #1-100 pack or a complete volume pack.</li> - <li id="r5.3.3.2"><a href="#r5.3.3"><strong>↑_</strong></a> <a href="#r5.3.3.2">5.3.3.2.</a> <strong>Packs consisting of #1-#10, #11-#20, #21- #40, etc.</strong> are allowed only if the comics appear in no other pack. Each pack must contain at least 10 new comics.</li> - <li id="r5.3.3.3"><a href="#r5.3.3"><strong>↑_</strong></a> <a href="#r5.3.3.3">5.3.3.3.</a> <strong>Packs consisting of #1-#100, #101-#200, etc.</strong> are allowed only if they are not a complete volume pack.</li> - <li id="r5.3.3.4"><a href="#r5.3.3"><strong>↑_</strong></a> <a href="#r5.3.3.4">5.3.3.4.</a> <strong>A pack composed of #1-#EndOfVolume is allowed at any time.</strong> </li> - </ul> - </li> - <li id="r5.3.4"><a href="#h5.3"><strong>↑_</strong></a> <a href="#r5.3.4">5.3.4.</a> <strong>Complete volume or series packs will trump any torrent consisting of the same comic issues.</strong> See <a href="#r5.3.1">5.3.1</a> for details on what constitutes a duplicate comic. For example, the complete volume "The Amazing Spider-Man Vol. 1 #1-#441" will trump any torrents containing those issues, e.g., "The Amazing Spider-Man #1-#50" or "The Amazing Spider-Man #150-#200" or "The Amazing Spider-Man #300-#310," etc. Exception: If the image quality of the comic issue(s) in a torrent is higher than the quality of the corresponding comic(s) in a complete volume or series pack, the higher quality version may coexist on the site with the complete collection.</li> - </ul> - </div> + <h5 id="h5.3"><a href="#h5.3k"><strong>↑</strong></a> <a href="#h5.3">5.3.</a> Duplicates & Trumping</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r5.3.1"><a href="#h5.3"><strong>↑_</strong></a> <a href="#r5.3.1">5.3.1.</a> <strong>A dupe of a single comic is defined as two scans of the same book by the same scanner, where the same pages have been scanned.</strong> The scanner may be an individual, a release group, or a scanning device. Exceptions: The following examples are NOT dupes: + <ul> + <li>Two copies of the same book by the same scanner, but one is a c2c copy and the other is a "no ads" copy</li> + <li>Two scans of the same book by different scanners</li> + <li>Two scans of the same book by the same scanner, when the copy you're uploading contains fixes</li> + </ul> + </li> + <li id="r5.3.2"><a href="#h5.3"><strong>↑_</strong></a> <a href="#r5.3.2">5.3.2.</a> <strong>Releases in .cbz and .cbr are always allowed, with preference given to the earliest upload.</strong> In the event of a dupe occurring between a .cbr and a .cbz, the earliest upload remains. In the event of any other dupe, the order listed in <a href="#r5.1.1">5.1.1</a> determines which torrent is kept. For example, if a PDF is uploaded followed by a .cbr, the .cbr remains on the site and the PDF is deleted as a trump. If a .cbz is uploaded followed by a PDF, the .cbz remains on the site and the PDF is deleted as a dupe.</li> + <li id="r5.3.3"><a href="#h5.3"><strong>↑_</strong></a> <a href="#r5.3.3">5.3.3.</a> <strong>Multi-comic collections and packs must conform to either of these collection types in order of preference.</strong> + <ul> + <li id="r5.3.3.1"><a href="#r5.3.3"><strong>↑_</strong></a> <a href="#r5.3.3.1">5.3.3.1.</a> <strong>A torrent consisting of issues #1-50 is currently on the site.</strong> The only packs that are allowed to contain any of the comics before #50 are a #1-100 pack or a complete volume pack.</li> + <li id="r5.3.3.2"><a href="#r5.3.3"><strong>↑_</strong></a> <a href="#r5.3.3.2">5.3.3.2.</a> <strong>Packs consisting of #1-#10, #11-#20, #21- #40, etc.</strong> are allowed only if the comics appear in no other pack. Each pack must contain at least 10 new comics.</li> + <li id="r5.3.3.3"><a href="#r5.3.3"><strong>↑_</strong></a> <a href="#r5.3.3.3">5.3.3.3.</a> <strong>Packs consisting of #1-#100, #101-#200, etc.</strong> are allowed only if they are not a complete volume pack.</li> + <li id="r5.3.3.4"><a href="#r5.3.3"><strong>↑_</strong></a> <a href="#r5.3.3.4">5.3.3.4.</a> <strong>A pack composed of #1-#EndOfVolume is allowed at any time.</strong> </li> + </ul> + </li> + <li id="r5.3.4"><a href="#h5.3"><strong>↑_</strong></a> <a href="#r5.3.4">5.3.4.</a> <strong>Complete volume or series packs will trump any torrent consisting of the same comic issues.</strong> See <a href="#r5.3.1">5.3.1</a> for details on what constitutes a duplicate comic. For example, the complete volume "The Amazing Spider-Man Vol. 1 #1-#441" will trump any torrents containing those issues, e.g., "The Amazing Spider-Man #1-#50" or "The Amazing Spider-Man #150-#200" or "The Amazing Spider-Man #300-#310," etc. Exception: If the image quality of the comic issue(s) in a torrent is higher than the quality of the corresponding comic(s) in a complete volume or series pack, the higher quality version may coexist on the site with the complete collection.</li> + </ul> + </div> - <h5 id="h5.4"><a href="#h5.4k"><strong>↑</strong></a> <a href="#h5.4">5.4.</a> Formatting</h5> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r5.4.1"><a href="#h5.4"><strong>↑_</strong></a> <a href="#r5.4.1">5.4.1.</a> <strong>All comic page scans must have zero-padded numbers and may be archived in .pdf, .rar (.cbr) or .zip (.cbz) files.</strong> The contents of the archive or directory must be image files (either JPEG or PNG), which are named sequentially so that they display in the correct order by <a href="https://en.wikipedia.org/wiki/Comparison_of_image_viewers" target="_blank">comic reading software</a> such as <a href="http://www.cdisplay.me/" target="_blank">CDisplay</a> and <a href="http://www.feedface.com/software/ffview.html" target="_blank">FFView</a>. The page numbers and books must be zero-padded for this same reason. For example, this constitutes good numbering: <samp>file01.jpg</samp>, <samp>file02.jpg</samp>,... <samp>file30.jpg</samp>; and this constitutes bad numbering: <samp>file1.jpg</samp>, <samp>file2.jpg</samp>, <samp>file3.jpg</samp>,... <samp>file30.jpg</samp>.</li> - <li id="r5.4.2"><a href="#h5.4"><strong>↑_</strong></a> <a href="#r5.4.2">5.4.2.</a> <strong>Comic book archive file names must be informative.</strong> The archive names should include at least the book's name (e.g., "Uncanny X-Men"), the volume (if there's more than one volume for that book), and the issue number. Including the cover year, scanner information (to differentiate between different scans of the same book), and the issue's title (e.g., "Days of Future Past") is strongly recommended. For example: <samp>Buffy the Vampire Slayer Season Eight - #01 - 2007 - The Long Way Home Part 1.cbr</samp> and <samp>Amazing Spiderman - Volume 1 - #10 - 1964.cbz</samp>.</li> - <li id="r5.4.3"><a href="#h5.4"><strong>↑_</strong></a> <a href="#r5.4.3">5.4.3.</a> <strong>The directory name should uniquely identify its contents.</strong> You should include the title, as well as the issue numbers included (if applicable). The title, volume, cover year, and story name can often be found in small type at the bottom of the page opposite the inside cover. Directories should be named with the title of the series and the issue numbers. For example: <samp>../Buffy the Vampire Slayer Season Eight - #01-#08/</samp> and <samp>../Amazing Spiderman - Volume 1 - #10-#20/</samp>.</li> - </ul> - </div> + <h5 id="h5.4"><a href="#h5.4k"><strong>↑</strong></a> <a href="#h5.4">5.4.</a> Formatting</h5> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r5.4.1"><a href="#h5.4"><strong>↑_</strong></a> <a href="#r5.4.1">5.4.1.</a> <strong>All comic page scans must have zero-padded numbers and may be archived in .pdf, .rar (.cbr) or .zip (.cbz) files.</strong> The contents of the archive or directory must be image files (either JPEG or PNG), which are named sequentially so that they display in the correct order by <a href="https://en.wikipedia.org/wiki/Comparison_of_image_viewers" target="_blank">comic reading software</a> such as <a href="http://www.cdisplay.me/" target="_blank">CDisplay</a> and <a href="http://www.feedface.com/software/ffview.html" target="_blank">FFView</a>. The page numbers and books must be zero-padded for this same reason. For example, this constitutes good numbering: <samp>file01.jpg</samp>, <samp>file02.jpg</samp>,... <samp>file30.jpg</samp>; and this constitutes bad numbering: <samp>file1.jpg</samp>, <samp>file2.jpg</samp>, <samp>file3.jpg</samp>,... <samp>file30.jpg</samp>.</li> + <li id="r5.4.2"><a href="#h5.4"><strong>↑_</strong></a> <a href="#r5.4.2">5.4.2.</a> <strong>Comic book archive file names must be informative.</strong> The archive names should include at least the book's name (e.g., "Uncanny X-Men"), the volume (if there's more than one volume for that book), and the issue number. Including the cover year, scanner information (to differentiate between different scans of the same book), and the issue's title (e.g., "Days of Future Past") is strongly recommended. For example: <samp>Buffy the Vampire Slayer Season Eight - #01 - 2007 - The Long Way Home Part 1.cbr</samp> and <samp>Amazing Spiderman - Volume 1 - #10 - 1964.cbz</samp>.</li> + <li id="r5.4.3"><a href="#h5.4"><strong>↑_</strong></a> <a href="#r5.4.3">5.4.3.</a> <strong>The directory name should uniquely identify its contents.</strong> You should include the title, as well as the issue numbers included (if applicable). The title, volume, cover year, and story name can often be found in small type at the bottom of the page opposite the inside cover. Directories should be named with the title of the series and the issue numbers. For example: <samp>../Buffy the Vampire Slayer Season Eight - #01-#08/</samp> and <samp>../Amazing Spiderman - Volume 1 - #10-#20/</samp>.</li> + </ul> + </div> - <h4 id="h6"><a href="#h6k"><strong>↑</strong></a> <a href="#h6">6.</a> ebooks, eLearning Books & Sheet Music</h4> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r6.1"><a href="#r6.1k"><strong>↑_</strong></a> <a href="#r6.1">6.1.</a> <strong>Individual releases can be either a torrent of a directory, an archive, or the original format (e.g., .epub, .pdf, .chm, .txt, etc.).</strong> Neither the individual release or archive can be password protected. Note: ebooks cannot be uploaded in an archive. Users must be able to see the ebook format that is in the torrent.</li> - <li id="r6.2"><a href="#r6.2k"><strong>↑_</strong></a> <a href="#r6.2">6.2.</a> <strong>Uploading a pack of ebooks in one archive (e.g., .tar, .rar, .zip) is prohibited.</strong> ebook collections cannot be uploaded. Exception: ebooks that come in a retail set may be uploaded in a single torrent.</li> - <li id="r6.3"><a href="#r6.3k"><strong>↑_</strong></a> <a href="#r6.3">6.3.</a> <strong>ebook pages must be of good quality (in other words, legible with little or no artifacting) and must be scanned cleanly (if the pages were hand-scanned).</strong> Poor quality scans will be deleted, especially if the quality is so poor that the resulting image is difficult to read. Judgments on quality are at the discretion of the moderator involved.</li> - <li id="r6.4"><a href="#r6.4k"><strong>↑_</strong></a> <a href="#r6.4">6.4.</a> <strong>Only published ebooks are allowed.</strong> Freely available ebooks are not allowed. This rule also covers recipes and cookbooks. Only official publications are allowed.</li> - <li id="r6.5"><a href="#r6.5k"><strong>↑_</strong></a> <a href="#r6.5">6.5.</a> <strong>Collections/packs of ebooks are prohibited, even if each title is somehow related to other ebook titles in some way.</strong> All ebooks must be uploaded individually and cannot be archived (users must be able to see the ebook format in the torrent).</li> - <li id="r6.6"><a href="#r6.6k"><strong>↑_</strong></a> <a href="#r6.6">6.6.</a> <strong>Two identical ebooks or two identical sheet music uploads are dupes of one another.</strong> The same ebook and sheet music titles in the same format (e.g., .epub, .pdf, .chm, .txt, etc.) are dupes.</li> - <li id="r6.7"><a href="#r6.7k"><strong>↑_</strong></a> <a href="#r6.7">6.7.</a> <strong>Manually-scanned, scanned and OCR'ed (optical character recognized), and retail ebooks are all allowed on the site.</strong> OCR'ed ebooks may trump manually-scanned versions, while untouched retail ebooks will trump both hand-scanned and OCR'ed versions.</li> - <li id="r6.8"><a href="#r6.8k"><strong>↑_</strong></a> <a href="#r6.8">6.8.</a> <strong>Include proper <a href="?p=tag">Gazelle tags</a> for your ebook and sheet music uploads.</strong> You are strongly encouraged to use the appropriate default tags with your uploads. That way other users can find your uploads easily through the tag search system. Sheet music should use the sheet.music tag. ebooks should at least contain the fiction tag or the non.fiction tag depending on the content. You may add additional tags if the defaults do not apply or are not enough to describe the torrent contents.</li> - </ul> - </div> + <h4 id="h6"><a href="#h6k"><strong>↑</strong></a> <a href="#h6">6.</a> ebooks, eLearning Books & Sheet Music</h4> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r6.1"><a href="#r6.1k"><strong>↑_</strong></a> <a href="#r6.1">6.1.</a> <strong>Individual releases can be either a torrent of a directory, an archive, or the original format (e.g., .epub, .pdf, .chm, .txt, etc.).</strong> Neither the individual release or archive can be password protected. Note: ebooks cannot be uploaded in an archive. Users must be able to see the ebook format that is in the torrent.</li> + <li id="r6.2"><a href="#r6.2k"><strong>↑_</strong></a> <a href="#r6.2">6.2.</a> <strong>Uploading a pack of ebooks in one archive (e.g., .tar, .rar, .zip) is prohibited.</strong> ebook collections cannot be uploaded. Exception: ebooks that come in a retail set may be uploaded in a single torrent.</li> + <li id="r6.3"><a href="#r6.3k"><strong>↑_</strong></a> <a href="#r6.3">6.3.</a> <strong>ebook pages must be of good quality (in other words, legible with little or no artifacting) and must be scanned cleanly (if the pages were hand-scanned).</strong> Poor quality scans will be deleted, especially if the quality is so poor that the resulting image is difficult to read. Judgments on quality are at the discretion of the moderator involved.</li> + <li id="r6.4"><a href="#r6.4k"><strong>↑_</strong></a> <a href="#r6.4">6.4.</a> <strong>Only published ebooks are allowed.</strong> Freely available ebooks are not allowed. This rule also covers recipes and cookbooks. Only official publications are allowed.</li> + <li id="r6.5"><a href="#r6.5k"><strong>↑_</strong></a> <a href="#r6.5">6.5.</a> <strong>Collections/packs of ebooks are prohibited, even if each title is somehow related to other ebook titles in some way.</strong> All ebooks must be uploaded individually and cannot be archived (users must be able to see the ebook format in the torrent).</li> + <li id="r6.6"><a href="#r6.6k"><strong>↑_</strong></a> <a href="#r6.6">6.6.</a> <strong>Two identical ebooks or two identical sheet music uploads are dupes of one another.</strong> The same ebook and sheet music titles in the same format (e.g., .epub, .pdf, .chm, .txt, etc.) are dupes.</li> + <li id="r6.7"><a href="#r6.7k"><strong>↑_</strong></a> <a href="#r6.7">6.7.</a> <strong>Manually-scanned, scanned and OCR'ed (optical character recognized), and retail ebooks are all allowed on the site.</strong> OCR'ed ebooks may trump manually-scanned versions, while untouched retail ebooks will trump both hand-scanned and OCR'ed versions.</li> + <li id="r6.8"><a href="#r6.8k"><strong>↑_</strong></a> <a href="#r6.8">6.8.</a> <strong>Include proper <a href="?p=tag">Gazelle tags</a> for your ebook and sheet music uploads.</strong> You are strongly encouraged to use the appropriate default tags with your uploads. That way other users can find your uploads easily through the tag search system. Sheet music should use the sheet.music tag. ebooks should at least contain the fiction tag or the non.fiction tag depending on the content. You may add additional tags if the defaults do not apply or are not enough to describe the torrent contents.</li> + </ul> + </div> - <h4 id="h7"><a href="#h7k"><strong>↑</strong></a> <a href="#h7">7.</a> eLearning Videos</h4> - <div class="box pad" style="padding: 10px 10px 10px 20px;"> - <ul> - <li id="r7.1"><a href="#h7"><strong>↑_</strong></a> <a href="#r7.1">7.1.</a> <strong>The eLearning Videos category is only for tutorial videos on specific topics pertaining to music (see <a href="#r7.3">7.3</a> in this section).</strong> Any video clips mentioned in <a href="#h1.2">Section 1.2</a> cannot be uploaded to the eLearning Videos category.</li> - <li id="r7.2"><a href="#h7"><strong>↑_</strong></a> <a href="#r7.2">7.2.</a> <strong>No freely available eLearning videos are allowed.</strong> You may not upload videos that are officially hosted from university sites, the author's site, the Internet Archive, or the publisher's site. You may upload videos from other torrent sites as long as they conform to the rules in this section.</li> - <li id="r7.3"><a href="#h7"><strong>↑_</strong></a> <a href="#r7.3">7.3.</a> <strong>Tutorials on how to use musical instruments, vocal training, producing music, or otherwise learning the theory and practice of music are the only allowed topics.</strong> No material outside of these topics is allowed. For example, instruction videos about Kung Fu training, dance lessons, beer brewing, or photography are not permitted here. What is considered allowable under these topics is ultimately at the discretion of the staff.</li> - <li id="r7.4"><a href="#h7"><strong>↑_</strong></a> <a href="#r7.4">7.4.</a> <strong>eLearning Videos must be either in a video file format (.mkv, .avi, .mov, .mp4, etc.) or a disk image (.iso, .bin/.cue, etc.).</strong> Only one video file format and one disk image format are allowed per video. The same video in another file format or within another disk image type is a dupe. Torrents should not be uploaded in compressed archives. Such torrents will be removed when reported.</li> - <li id="r7.5"><a href="#h7"><strong>↑_</strong></a> <a href="#r7.5">7.5.</a> <strong>eLearning video uploads must contain an informative description and use proper <a href="?p=tag">Gazelle tags</a>.</strong> Uploads should include a proper description and/or a link to further information. Using the elearning.videos <a href="?p=tag">Gazelle tags</a> is strongly encouraged.</li> - </ul> - </div> - </div> + <h4 id="h7"><a href="#h7k"><strong>↑</strong></a> <a href="#h7">7.</a> eLearning Videos</h4> + <div class="box pad" style="padding: 10px 10px 10px 20px;"> + <ul> + <li id="r7.1"><a href="#h7"><strong>↑_</strong></a> <a href="#r7.1">7.1.</a> <strong>The eLearning Videos category is only for tutorial videos on specific topics pertaining to music (see <a href="#r7.3">7.3</a> in this section).</strong> Any video clips mentioned in <a href="#h1.2">Section 1.2</a> cannot be uploaded to the eLearning Videos category.</li> + <li id="r7.2"><a href="#h7"><strong>↑_</strong></a> <a href="#r7.2">7.2.</a> <strong>No freely available eLearning videos are allowed.</strong> You may not upload videos that are officially hosted from university sites, the author's site, the Internet Archive, or the publisher's site. You may upload videos from other torrent sites as long as they conform to the rules in this section.</li> + <li id="r7.3"><a href="#h7"><strong>↑_</strong></a> <a href="#r7.3">7.3.</a> <strong>Tutorials on how to use musical instruments, vocal training, producing music, or otherwise learning the theory and practice of music are the only allowed topics.</strong> No material outside of these topics is allowed. For example, instruction videos about Kung Fu training, dance lessons, beer brewing, or photography are not permitted here. What is considered allowable under these topics is ultimately at the discretion of the staff.</li> + <li id="r7.4"><a href="#h7"><strong>↑_</strong></a> <a href="#r7.4">7.4.</a> <strong>eLearning Videos must be either in a video file format (.mkv, .avi, .mov, .mp4, etc.) or a disk image (.iso, .bin/.cue, etc.).</strong> Only one video file format and one disk image format are allowed per video. The same video in another file format or within another disk image type is a dupe. Torrents should not be uploaded in compressed archives. Such torrents will be removed when reported.</li> + <li id="r7.5"><a href="#h7"><strong>↑_</strong></a> <a href="#r7.5">7.5.</a> <strong>eLearning video uploads must contain an informative description and use proper <a href="?p=tag">Gazelle tags</a>.</strong> Uploads should include a proper description and/or a link to further information. Using the elearning.videos <a href="?p=tag">Gazelle tags</a> is strongly encouraged.</li> + </ul> + </div> + </div> <!-- END Other Sections --> </div> -<? +<?php View::show_footer(); ?> diff --git a/sections/schedule/biweekly/cycle_auth_keys.php b/sections/schedule/biweekly/cycle_auth_keys.php index dae8a27c2..73eb0481b 100644 --- a/sections/schedule/biweekly/cycle_auth_keys.php +++ b/sections/schedule/biweekly/cycle_auth_keys.php @@ -3,16 +3,16 @@ //------------- Cycle auth keys -----------------------------------------// $DB->query(" - UPDATE users_info - SET AuthKey = - MD5( - CONCAT( - AuthKey, RAND(), '".db_string(Users::make_secret())."', - SHA1( - CONCAT( - RAND(), RAND(), '".db_string(Users::make_secret())."' - ) - ) - ) - );" -); \ No newline at end of file + UPDATE users_info + SET AuthKey = + MD5( + CONCAT( + AuthKey, RAND(), '".db_string(Users::make_secret())."', + SHA1( + CONCAT( + RAND(), RAND(), '".db_string(Users::make_secret())."' + ) + ) + ) + );" +); diff --git a/sections/schedule/daily/delete_dead_torrents.php b/sections/schedule/daily/delete_dead_torrents.php index d6ef4fa3f..6ae0dfbc2 100644 --- a/sections/schedule/daily/delete_dead_torrents.php +++ b/sections/schedule/daily/delete_dead_torrents.php @@ -2,92 +2,100 @@ //------------- Delete dead torrents ------------------------------------// -sleep(10); +$DB->prepared_query(" + SELECT + t.ID, + t.GroupID, + tg.Name, + t.Format, + t.Encoding, + t.UserID, + t.Media, + HEX(t.info_hash) AS InfoHash + FROM torrents AS t + INNER JOIN torrents_leech_stats AS tls ON (tls.TorrentID = t.ID) + INNER JOIN torrents_group AS tg ON (tg.ID = t.GroupID) + WHERE + (tls.last_action IS NOT NULL AND tls.last_action < now() - INTERVAL 28 DAY) + OR + (tls.last_action IS NULL AND t.Time < now() - INTERVAL 2 DAY) + LIMIT 8000 +"); +$torrents = $DB->to_array(false, MYSQLI_NUM, false); +echo('Found '.count($torrents)." inactive torrents to be deleted.\n"); -$DB->query(" - SELECT - t.ID, - t.GroupID, - tg.Name, - t.Format, - t.Encoding, - t.UserID, - t.Media, - HEX(t.info_hash) AS InfoHash - FROM torrents AS t - JOIN torrents_group AS tg ON tg.ID = t.GroupID - WHERE - (t.last_action < '".time_minus(3600 * 24 * 28)."' AND t.last_action != 0) - OR - (t.Time < '".time_minus(3600 * 24 * 2)."' AND t.last_action = 0)"); -$Torrents = $DB->to_array(false, MYSQLI_NUM, false); -echo 'Found '.count($Torrents)." inactive torrents to be deleted.\n"; - -$LogEntries = $DeleteNotes = []; +$logEntries = $deleteNotes = []; // Exceptions for inactivity deletion -$InactivityExceptionsMade = [ - //UserID => expiry time of exception +$inactivityExceptionsMade = [ + //UserID => expiry time of exception ]; $i = 0; -foreach ($Torrents as $Torrent) { - list($ID, $GroupID, $Name, $Format, $Encoding, $UserID, $Media, $InfoHash) = $Torrent; - if (array_key_exists($UserID, $InactivityExceptionsMade) && (time() < $InactivityExceptionsMade[$UserID])) { - // don't delete the torrent! - continue; - } - $ArtistName = Artists::display_artists(Artists::get_artist($GroupID), false, false, false); - if ($ArtistName) { - $Name = "$ArtistName - $Name"; - } - if ($Format && $Encoding) { - $Name .= ' ['.(empty($Media) ? '' : "$Media / ") . "$Format / $Encoding]"; - } - Torrents::delete_torrent($ID, $GroupID); - $LogEntries[] = db_string("Torrent $ID ($Name) (".strtoupper($InfoHash).") was deleted for inactivity (unseeded)"); +foreach ($torrents as $torrent) { + list($id, $groupID, $name, $format, $encoding, $userID, $media, $infoHash) = $torrent; + if (array_key_exists($userID, $inactivityExceptionsMade) && (time() < $inactivityExceptionsMade[$userID])) { + // don't delete the torrent! + continue; + } + $artistName = Artists::display_artists(Artists::get_artist($groupID), false, false, false); + if ($artistName) { + $name = "$artistName - $name"; + } + if ($format && $encoding) { + $name .= ' ['.(empty($media) ? '' : "$media / ") . "$format / $encoding]"; + } + Torrents::delete_torrent($id, $groupID); + $logEntries[] = "Torrent $id ($name) (".strtoupper($infoHash).") was deleted for inactivity (unseeded)"; - if (!array_key_exists($UserID, $DeleteNotes)) { - $DeleteNotes[$UserID] = array('Count' => 0, 'Msg' => ''); - } + if (!array_key_exists($userID, $deleteNotes)) { + $deleteNotes[$userID] = ['Count' => 0, 'Msg' => '']; + } - $DeleteNotes[$UserID]['Msg'] .= "\n$Name"; - $DeleteNotes[$UserID]['Count']++; + $deleteNotes[$userID]['Msg'] .= sprintf("\n[url=torrents.php?id=%s]%s[/url]", $groupID, $name); + $deleteNotes[$userID]['Count']++; - ++$i; - if ($i % 500 == 0) { - echo "$i inactive torrents removed.\n"; - } + ++$i; + if ($i % 500 == 0) { + echo("$i inactive torrents removed.\n"); + } } -echo "$i torrents deleted for inactivity.\n"; +echo("$i torrents deleted for inactivity.\n"); -foreach ($DeleteNotes as $UserID => $MessageInfo) { - $Singular = (($MessageInfo['Count'] == 1) ? true : false); - Misc::send_pm($UserID, 0, $MessageInfo['Count'].' of your torrents '.($Singular ? 'has' : 'have').' been deleted for inactivity', ($Singular ? 'One' : 'Some').' of your uploads '.($Singular ? 'has' : 'have').' been deleted for being unseeded. Since '.($Singular ? 'it' : 'they').' didn\'t break any rules (we hope), please feel free to re-upload '.($Singular ? 'it' : 'them').".\n\nThe following torrent".($Singular ? ' was' : 's were').' deleted:'.$MessageInfo['Msg']); +foreach ($deleteNotes as $userID => $messageInfo) { + $singular = (($messageInfo['Count'] == 1) ? true : false); + Misc::send_pm($userID, 0, $messageInfo['Count'].' of your torrents '.($singular ? 'has' : 'have').' been deleted for inactivity', ($singular ? 'One' : 'Some').' of your uploads '.($singular ? 'has' : 'have').' been deleted for being unseeded. Since '.($singular ? 'it' : 'they').' didn\'t break any rules (we hope), please feel free to re-upload '.($singular ? 'it' : 'them').".\n\nThe following torrent".($singular ? ' was' : 's were').' deleted:'.$messageInfo['Msg']); } -unset($DeleteNotes); +unset($deleteNotes); -if (count($LogEntries) > 0) { - $Values = "('".implode("', '$sqltime'), ('", $LogEntries) . "', '$sqltime')"; - $DB->query(" - INSERT INTO log (Message, Time) - VALUES $Values"); - echo "\nDeleted $i torrents for inactivity\n"; +if (count($logEntries) > 0) { + $chunks = array_chunk($logEntries, 100); + foreach ($chunks as $messages) { + $placeholders = array_fill(0, count($messages), '(?, now())'); + $DB->prepared_query(" + INSERT INTO log (Message, Time) + VALUES " . implode(', ', $placeholders), ...$messages); + echo("\nDeleted $i torrents for inactivity\n"); + } } -$DB->query(" - SELECT SimilarID - FROM artists_similar_scores - WHERE Score <= 0"); -$SimilarIDs = implode(',', $DB->collect('SimilarID')); +$DB->prepared_query(" + SELECT SimilarID + FROM artists_similar_scores + WHERE Score <= 0"); +$similarIDs = $DB->collect('SimilarID'); -if ($SimilarIDs) { - $DB->query(" - DELETE FROM artists_similar - WHERE SimilarID IN($SimilarIDs)"); - $DB->query(" - DELETE FROM artists_similar_scores - WHERE SimilarID IN($SimilarIDs)"); - $DB->query(" - DELETE FROM artists_similar_votes - WHERE SimilarID IN($SimilarIDs)"); -} \ No newline at end of file +if ($similarIDs) { + $placeholders = implode(', ', array_fill(0, count($similarIDs), '(?)')); + $DB->prepared_query(" + DELETE FROM artists_similar + WHERE SimilarID IN ($placeholders)", + $similarIDs); + $DB->prepared_query(" + DELETE FROM artists_similar_scores + WHERE SimilarID IN ($placeholders)", + $similarIDs); + $DB->prepared_query(" + DELETE FROM artists_similar_votes + WHERE SimilarID IN ($placeholders)", + $similarIDs); +} diff --git a/sections/schedule/daily/demote_users.php b/sections/schedule/daily/demote_users.php index d2bf71f60..12000ef0b 100644 --- a/sections/schedule/daily/demote_users.php +++ b/sections/schedule/daily/demote_users.php @@ -5,60 +5,64 @@ // Demote to Member when the ratio falls below 0.95 or they have less than 25 GB upload $DemoteClasses = [POWER, ELITE, TORRENT_MASTER, POWER_TM, ELITE_TM]; $Query = $DB->query(' - SELECT ID - FROM users_main - WHERE PermissionID IN(' . implode(', ', $DemoteClasses) . ') - AND ( - (Downloaded > 0 AND Uploaded / Downloaded < 0.95) - OR Uploaded < 25 * 1024 * 1024 * 1024 - )'); + SELECT ID + FROM users_main um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + WHERE um.PermissionID IN(' . implode(', ', $DemoteClasses) . ') + AND ( + (uls.Downloaded > 0 AND uls.Uploaded / uls.Downloaded < 0.95) + OR uls.Uploaded < 25 * 1024 * 1024 * 1024 + )'); echo "demoted 1\n"; $DB->query(' - UPDATE users_info AS ui - JOIN users_main AS um ON um.ID = ui.UserID - SET - um.PermissionID = ' . MEMBER . ", - ui.AdminComment = CONCAT('" . sqltime() . ' - Class changed to ' . Users::make_class_string(MEMBER) . " by System\n\n', ui.AdminComment) - WHERE um.PermissionID IN (" . implode(', ', $DemoteClasses) . ') - AND ( - (um.Downloaded > 0 AND um.Uploaded / um.Downloaded < 0.95) - OR um.Uploaded < 25 * 1024 * 1024 * 1024 - )'); + UPDATE users_info AS ui + INNER JOIN users_main AS um ON (um.ID = ui.UserID) + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + SET + um.PermissionID = ' . MEMBER . ", + ui.AdminComment = CONCAT('" . sqltime() . ' - Class changed to ' . Users::make_class_string(MEMBER) . " by System\n\n', ui.AdminComment) + WHERE um.PermissionID IN (" . implode(', ', $DemoteClasses) . ') + AND ( + (uls.Downloaded > 0 AND uls.Uploaded / uls.Downloaded < 0.95) + OR uls.Uploaded < 25 * 1024 * 1024 * 1024 + )'); $DB->set_query_id($Query); while (list($UserID) = $DB->next_record()) { - /*$Cache->begin_transaction("user_info_$UserID"); - $Cache->update_row(false, array('PermissionID' => MEMBER)); - $Cache->commit_transaction(2592000);*/ - $Cache->delete_value("user_info_$UserID"); - $Cache->delete_value("user_info_heavy_$UserID"); - Misc::send_pm($UserID, 0, 'You have been demoted to '.Users::make_class_string(MEMBER), "You now only meet the requirements for the \"".Users::make_class_string(MEMBER)."\" user class.\n\nTo read more about ".SITE_NAME."'s user classes, read [url=".site_url()."wiki.php?action=article&name=userclasses]this wiki article[/url]."); + /*$Cache->begin_transaction("user_info_$UserID"); + $Cache->update_row(false, array('PermissionID' => MEMBER)); + $Cache->commit_transaction(2592000);*/ + $Cache->delete_value("user_info_$UserID"); + $Cache->delete_value("user_info_heavy_$UserID"); + Misc::send_pm($UserID, 0, 'You have been demoted to '.Users::make_class_string(MEMBER), "You now only meet the requirements for the \"".Users::make_class_string(MEMBER)."\" user class.\n\nTo read more about ".SITE_NAME."'s user classes, read [url=".site_url()."wiki.php?action=article&name=userclasses]this wiki article[/url]."); } echo "demoted 2\n"; // Demote to User when the ratio drops below 0.65 $DemoteClasses = [MEMBER, POWER, ELITE, TORRENT_MASTER, POWER_TM, ELITE_TM]; $Query = $DB->query(' - SELECT ID - FROM users_main - WHERE PermissionID IN(' . implode(', ', $DemoteClasses) . ') - AND Uploaded / Downloaded < 0.65'); + SELECT ID + FROM users_main um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + WHERE um.PermissionID IN(' . implode(', ', $DemoteClasses) . ') + AND uls.Uploaded / uls.Downloaded < 0.65'); echo "demoted 3\n"; $DB->query(' - UPDATE users_info AS ui - JOIN users_main AS um ON um.ID = ui.UserID - SET - um.PermissionID = ' . USER . ", - ui.AdminComment = CONCAT('" . sqltime() . ' - Class changed to ' . Users::make_class_string(USER) . " by System\n\n', ui.AdminComment) - WHERE um.PermissionID IN (" . implode(', ', $DemoteClasses) . ') - AND (um.Downloaded > 0 AND um.Uploaded / um.Downloaded < 0.65)'); + UPDATE users_info AS ui + INNER JOIN users_main AS um ON (um.ID = ui.UserID) + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + SET + um.PermissionID = ' . USER . ", + ui.AdminComment = CONCAT('" . sqltime() . ' - Class changed to ' . Users::make_class_string(USER) . " by System\n\n', ui.AdminComment) + WHERE um.PermissionID IN (" . implode(', ', $DemoteClasses) . ') + AND (uls.Downloaded > 0 AND uls.Uploaded / uls.Downloaded < 0.65)'); $DB->set_query_id($Query); while (list($UserID) = $DB->next_record()) { - /*$Cache->begin_transaction("user_info_$UserID"); - $Cache->update_row(false, array('PermissionID' => USER)); - $Cache->commit_transaction(2592000);*/ - $Cache->delete_value("user_info_$UserID"); - $Cache->delete_value("user_info_heavy_$UserID"); - Misc::send_pm($UserID, 0, 'You have been demoted to '.Users::make_class_string(USER), "You now only meet the requirements for the \"".Users::make_class_string(USER)."\" user class.\n\nTo read more about ".SITE_NAME."'s user classes, read [url=".site_url()."wiki.php?action=article&name=userclasses]this wiki article[/url]."); + /*$Cache->begin_transaction("user_info_$UserID"); + $Cache->update_row(false, array('PermissionID' => USER)); + $Cache->commit_transaction(2592000);*/ + $Cache->delete_value("user_info_$UserID"); + $Cache->delete_value("user_info_heavy_$UserID"); + Misc::send_pm($UserID, 0, 'You have been demoted to '.Users::make_class_string(USER), "You now only meet the requirements for the \"".Users::make_class_string(USER)."\" user class.\n\nTo read more about ".SITE_NAME."'s user classes, read [url=".site_url()."wiki.php?action=article&name=userclasses]this wiki article[/url]."); } echo "demoted 4\n"; diff --git a/sections/schedule/daily/disable_downloading_ratio_watch.php b/sections/schedule/daily/disable_downloading_ratio_watch.php index 9a454a508..9d6b6c879 100644 --- a/sections/schedule/daily/disable_downloading_ratio_watch.php +++ b/sections/schedule/daily/disable_downloading_ratio_watch.php @@ -2,40 +2,40 @@ //------------- Disable downloading ability of users on ratio watch $UserQuery = $DB->query(" - SELECT ID, torrent_pass - FROM users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - WHERE i.RatioWatchEnds != '0000-00-00 00:00:00' - AND i.RatioWatchEnds < '$sqltime' - AND m.Enabled = '1' - AND m.can_leech != '0'"); + SELECT ID, torrent_pass + FROM users_info AS i + JOIN users_main AS m ON m.ID = i.UserID + WHERE i.RatioWatchEnds != '0000-00-00 00:00:00' + AND i.RatioWatchEnds < '$sqltime' + AND m.Enabled = '1' + AND m.can_leech != '0'"); $UserIDs = $DB->collect('ID'); if (count($UserIDs) > 0) { - $DB->query(" - UPDATE users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - SET m.can_leech = '0', - i.AdminComment = CONCAT('$sqltime - Leeching ability disabled by ratio watch system - required ratio: ', m.RequiredRatio, '\n\n', i.AdminComment) - WHERE m.ID IN(".implode(',', $UserIDs).')'); + $DB->query(" + UPDATE users_info AS i + JOIN users_main AS m ON m.ID = i.UserID + SET m.can_leech = '0', + i.AdminComment = CONCAT('$sqltime - Leeching ability disabled by ratio watch system - required ratio: ', m.RequiredRatio, '\n\n', i.AdminComment) + WHERE m.ID IN(".implode(',', $UserIDs).')'); - $DB->query(" - DELETE FROM users_torrent_history - WHERE UserID IN (".implode(',', $UserIDs).')'); + $DB->query(" + DELETE FROM users_torrent_history + WHERE UserID IN (".implode(',', $UserIDs).')'); } foreach ($UserIDs as $UserID) { - $Cache->begin_transaction("user_info_heavy_$UserID"); - $Cache->update_row(false, array('RatioWatchDownload' => 0, 'CanLeech' => 0)); - $Cache->commit_transaction(0); - Misc::send_pm($UserID, 0, 'Your downloading privileges have been disabled', "As you did not raise your ratio in time, your downloading privileges have been revoked. You will not be able to download any torrents until your ratio is above your new required ratio."); - echo "Ratio watch disabled: $UserID\n"; + $Cache->begin_transaction("user_info_heavy_$UserID"); + $Cache->update_row(false, array('RatioWatchDownload' => 0, 'CanLeech' => 0)); + $Cache->commit_transaction(0); + Misc::send_pm($UserID, 0, 'Your downloading privileges have been disabled', "As you did not raise your ratio in time, your downloading privileges have been revoked. You will not be able to download any torrents until your ratio is above your new required ratio."); + echo "Ratio watch disabled: $UserID\n"; } $DB->set_query_id($UserQuery); $Passkeys = $DB->collect('torrent_pass'); foreach ($Passkeys as $Passkey) { - Tracker::update_tracker('update_user', array('passkey' => $Passkey, 'can_leech' => '0')); -} \ No newline at end of file + Tracker::update_tracker('update_user', array('passkey' => $Passkey, 'can_leech' => '0')); +} diff --git a/sections/schedule/daily/disable_inactive_users.php b/sections/schedule/daily/disable_inactive_users.php index 9a0372468..6537e68a4 100644 --- a/sections/schedule/daily/disable_inactive_users.php +++ b/sections/schedule/daily/disable_inactive_users.php @@ -4,35 +4,35 @@ sleep(5); // Send email $DB->query(" - SELECT um.Username, um.Email - FROM users_info AS ui - JOIN users_main AS um ON um.ID = ui.UserID - LEFT JOIN users_levels AS ul ON ul.UserID = um.ID AND ul.PermissionID = '".CELEB."' - WHERE um.PermissionID IN ('".USER."', '".MEMBER ."') - AND um.LastAccess < '".time_minus(3600 * 24 * 110, true)."' - AND um.LastAccess > '".time_minus(3600 * 24 * 111, true)."' - AND um.LastAccess != '0000-00-00 00:00:00' - AND ui.Donor = '0' - AND um.Enabled != '2' - AND ul.UserID IS NULL - GROUP BY um.ID"); + SELECT um.Username, um.Email + FROM users_info AS ui + JOIN users_main AS um ON um.ID = ui.UserID + LEFT JOIN users_levels AS ul ON ul.UserID = um.ID AND ul.PermissionID = '".CELEB."' + WHERE um.PermissionID IN ('".USER."', '".MEMBER ."') + AND um.LastAccess < '".time_minus(3600 * 24 * 110, true)."' + AND um.LastAccess > '".time_minus(3600 * 24 * 111, true)."' + AND um.LastAccess != '0000-00-00 00:00:00' + AND ui.Donor = '0' + AND um.Enabled != '2' + AND ul.UserID IS NULL + GROUP BY um.ID"); while (list($Username, $Email) = $DB->next_record()) { - $Body = "Hi $Username,\n\nIt has been almost 4 months since you used your account at ".site_url().". This is an automated email to inform you that your account will be disabled in 10 days if you do not sign in."; - Misc::send_email($Email, 'Your '.SITE_NAME.' account is about to be disabled', $Body, 'noreply'); + $Body = "Hi $Username,\n\nIt has been almost 4 months since you used your account at ".site_url().". This is an automated email to inform you that your account will be disabled in 10 days if you do not sign in."; + Misc::send_email($Email, 'Your '.SITE_NAME.' account is about to be disabled', $Body, 'noreply'); } $DB->query(" - SELECT um.ID - FROM users_info AS ui - JOIN users_main AS um ON um.ID = ui.UserID - LEFT JOIN users_levels AS ul ON ul.UserID = um.ID AND ul.PermissionID = '".CELEB."' - WHERE um.PermissionID IN ('".USER."', '".MEMBER ."') - AND um.LastAccess < '".time_minus(3600 * 24 * 30 * 4)."' - AND um.LastAccess != '0000-00-00 00:00:00' - AND ui.Donor = '0' - AND um.Enabled != '2' - AND ul.UserID IS NULL - GROUP BY um.ID"); + SELECT um.ID + FROM users_info AS ui + JOIN users_main AS um ON um.ID = ui.UserID + LEFT JOIN users_levels AS ul ON ul.UserID = um.ID AND ul.PermissionID = '".CELEB."' + WHERE um.PermissionID IN ('".USER."', '".MEMBER ."') + AND um.LastAccess < '".time_minus(3600 * 24 * 30 * 4)."' + AND um.LastAccess != '0000-00-00 00:00:00' + AND ui.Donor = '0' + AND um.Enabled != '2' + AND ul.UserID IS NULL + GROUP BY um.ID"); if ($DB->has_results()) { - Tools::disable_users($DB->collect('ID'), 'Disabled for inactivity.', 3); -} \ No newline at end of file + Tools::disable_users($DB->collect('ID'), 'Disabled for inactivity.', 3); +} diff --git a/sections/schedule/daily/disable_unconfirmed_users.php b/sections/schedule/daily/disable_unconfirmed_users.php index 8a8da4c7b..45ad1c933 100644 --- a/sections/schedule/daily/disable_unconfirmed_users.php +++ b/sections/schedule/daily/disable_unconfirmed_users.php @@ -4,30 +4,30 @@ sleep(10); // get a list of user IDs for clearing cache keys $DB->query(" - SELECT UserID - FROM users_info AS ui - JOIN users_main AS um ON um.ID = ui.UserID - WHERE um.LastAccess = '0000-00-00 00:00:00' - AND ui.JoinDate < '".time_minus(60 * 60 * 24 * 7)."' - AND um.Enabled != '2'"); + SELECT UserID + FROM users_info AS ui + JOIN users_main AS um ON um.ID = ui.UserID + WHERE um.LastAccess = '0000-00-00 00:00:00' + AND ui.JoinDate < '".time_minus(60 * 60 * 24 * 7)."' + AND um.Enabled != '2'"); $UserIDs = $DB->collect('UserID'); // disable the users $DB->query(" - UPDATE users_info AS ui - JOIN users_main AS um ON um.ID = ui.UserID - SET um.Enabled = '2', - ui.BanDate = '$sqltime', - ui.BanReason = '3', - ui.AdminComment = CONCAT('$sqltime - Disabled for inactivity (never logged in)\n\n', ui.AdminComment) - WHERE um.LastAccess = '0000-00-00 00:00:00' - AND ui.JoinDate < '".time_minus(60 * 60 * 24 * 7)."' - AND um.Enabled != '2'"); + UPDATE users_info AS ui + JOIN users_main AS um ON um.ID = ui.UserID + SET um.Enabled = '2', + ui.BanDate = '$sqltime', + ui.BanReason = '3', + ui.AdminComment = CONCAT('$sqltime - Disabled for inactivity (never logged in)\n\n', ui.AdminComment) + WHERE um.LastAccess = '0000-00-00 00:00:00' + AND ui.JoinDate < '".time_minus(60 * 60 * 24 * 7)."' + AND um.Enabled != '2'"); $Cache->decrement('stats_user_count', $DB->affected_rows()); // clear the appropriate cache keys foreach ($UserIDs as $UserID) { - $Cache->delete_value("user_info_$UserID"); + $Cache->delete_value("user_info_$UserID"); } -echo "disabled unconfirmed\n"; \ No newline at end of file +echo "disabled unconfirmed\n"; diff --git a/sections/schedule/daily/lock_old_threads.php b/sections/schedule/daily/lock_old_threads.php index d3358ff84..30451c6f9 100644 --- a/sections/schedule/daily/lock_old_threads.php +++ b/sections/schedule/daily/lock_old_threads.php @@ -3,38 +3,38 @@ //------------- Lock old threads ----------------------------------------// sleep(10); $DB->query(" - SELECT t.ID, t.ForumID - FROM forums_topics AS t - JOIN forums AS f ON t.ForumID = f.ID - WHERE t.IsLocked = '0' - AND t.IsSticky = '0' - AND DATEDIFF(CURDATE(), DATE(t.LastPostTime)) / 7 > f.AutoLockWeeks - AND f.AutoLock = '1'"); + SELECT t.ID, t.ForumID + FROM forums_topics AS t + JOIN forums AS f ON t.ForumID = f.ID + WHERE t.IsLocked = '0' + AND t.IsSticky = '0' + AND DATEDIFF(CURDATE(), DATE(t.LastPostTime)) / 7 > f.AutoLockWeeks + AND f.AutoLock = '1'"); $IDs = $DB->collect('ID'); $ForumIDs = $DB->collect('ForumID'); if (count($IDs) > 0) { - $LockIDs = implode(',', $IDs); - $DB->query(" - UPDATE forums_topics - SET IsLocked = '1' - WHERE ID IN($LockIDs)"); - sleep(2); - $DB->query(" - DELETE FROM forums_last_read_topics - WHERE TopicID IN($LockIDs)"); + $LockIDs = implode(',', $IDs); + $DB->query(" + UPDATE forums_topics + SET IsLocked = '1' + WHERE ID IN($LockIDs)"); + sleep(2); + $DB->query(" + DELETE FROM forums_last_read_topics + WHERE TopicID IN($LockIDs)"); - foreach ($IDs as $ID) { - $Cache->begin_transaction("thread_$ID".'_info'); - $Cache->update_row(false, array('IsLocked' => '1')); - $Cache->commit_transaction(3600 * 24 * 30); - $Cache->expire_value("thread_$ID".'_catalogue_0', 3600 * 24 * 30); - $Cache->expire_value("thread_$ID".'_info', 3600 * 24 * 30); - Forums::add_topic_note($ID, 'Locked automatically by schedule', 0); - } + foreach ($IDs as $ID) { + $Cache->begin_transaction("thread_$ID".'_info'); + $Cache->update_row(false, array('IsLocked' => '1')); + $Cache->commit_transaction(3600 * 24 * 30); + $Cache->expire_value("thread_$ID".'_catalogue_0', 3600 * 24 * 30); + $Cache->expire_value("thread_$ID".'_info', 3600 * 24 * 30); + Forums::add_topic_note($ID, 'Locked automatically by schedule', 0); + } - $ForumIDs = array_flip(array_flip($ForumIDs)); - foreach ($ForumIDs as $ForumID) { - $Cache->delete_value("forums_$ForumID"); - } -} \ No newline at end of file + $ForumIDs = array_flip(array_flip($ForumIDs)); + foreach ($ForumIDs as $ForumID) { + $Cache->delete_value("forums_$ForumID"); + } +} diff --git a/sections/schedule/daily/ratio_requirements.php b/sections/schedule/daily/ratio_requirements.php index 139764dc7..7bdfa9d63 100644 --- a/sections/schedule/daily/ratio_requirements.php +++ b/sections/schedule/daily/ratio_requirements.php @@ -4,111 +4,114 @@ // Clear old seed time history $DB->query(" - DELETE FROM users_torrent_history - WHERE Date < DATE('".sqltime()."' - INTERVAL 7 DAY) + 0"); + DELETE FROM users_torrent_history + WHERE Date < DATE('".sqltime()."' - INTERVAL 7 DAY) + 0"); // Store total seeded time for each user in a temp table $DB->query("TRUNCATE TABLE users_torrent_history_temp"); $DB->query(" - INSERT INTO users_torrent_history_temp - (UserID, SumTime) - SELECT UserID, SUM(Time) - FROM users_torrent_history - GROUP BY UserID"); + INSERT INTO users_torrent_history_temp + (UserID, SumTime) + SELECT UserID, SUM(Time) + FROM users_torrent_history + GROUP BY UserID"); // Insert new row with <NumTorrents> = 0 with <Time> being number of seconds short of 72 hours. // This is where we penalize torrents seeded for less than 72 hours $DB->query(" - INSERT INTO users_torrent_history - (UserID, NumTorrents, Date, Time) - SELECT UserID, 0, UTC_DATE() + 0, 259200 - SumTime - FROM users_torrent_history_temp - WHERE SumTime < 259200"); + INSERT INTO users_torrent_history + (UserID, NumTorrents, Date, Time) + SELECT UserID, 0, UTC_DATE() + 0, 259200 - SumTime + FROM users_torrent_history_temp + WHERE SumTime < 259200"); // Set <Weight> to the time seeding <NumTorrents> torrents $DB->query(" - UPDATE users_torrent_history - SET Weight = NumTorrents * Time"); + UPDATE users_torrent_history + SET Weight = NumTorrents * Time"); // Calculate average time spent seeding each of the currently active torrents. // This rounds the results to the nearest integer because SeedingAvg is an int column. $DB->query("TRUNCATE TABLE users_torrent_history_temp"); $DB->query(" - INSERT INTO users_torrent_history_temp - (UserID, SeedingAvg) - SELECT UserID, SUM(Weight) / SUM(Time) - FROM users_torrent_history - GROUP BY UserID"); + INSERT INTO users_torrent_history_temp + (UserID, SeedingAvg) + SELECT UserID, SUM(Weight) / SUM(Time) + FROM users_torrent_history + GROUP BY UserID"); // Remove dummy entry for torrents seeded less than 72 hours $DB->query(" - DELETE FROM users_torrent_history - WHERE NumTorrents = '0'"); + DELETE FROM users_torrent_history + WHERE NumTorrents = '0'"); // Get each user's amount of snatches of existing torrents $DB->query("TRUNCATE TABLE users_torrent_history_snatch"); $DB->query(" - INSERT INTO users_torrent_history_snatch (UserID, NumSnatches) - SELECT xs.uid, COUNT(DISTINCT xs.fid) - FROM xbt_snatched AS xs - JOIN torrents AS t ON t.ID = xs.fid - GROUP BY xs.uid"); + INSERT INTO users_torrent_history_snatch (UserID, NumSnatches) + SELECT xs.uid, COUNT(DISTINCT xs.fid) + FROM xbt_snatched AS xs + JOIN torrents AS t ON t.ID = xs.fid + GROUP BY xs.uid"); // Get the fraction of snatched torrents seeded for at least 72 hours this week // Essentially take the total number of hours seeded this week and divide that by 72 hours * <NumSnatches> $DB->query(" - UPDATE users_main AS um - JOIN users_torrent_history_temp AS t ON t.UserID = um.ID - JOIN users_torrent_history_snatch AS s ON s.UserID = um.ID - SET um.RequiredRatioWork = (1 - (t.SeedingAvg / s.NumSnatches)) - WHERE s.NumSnatches > 0"); + UPDATE users_main AS um + JOIN users_torrent_history_temp AS t ON t.UserID = um.ID + JOIN users_torrent_history_snatch AS s ON s.UserID = um.ID + SET um.RequiredRatioWork = (1 - (t.SeedingAvg / s.NumSnatches)) + WHERE s.NumSnatches > 0"); $RatioRequirements = array( - array(80 * 1024 * 1024 * 1024, 0.60, 0.50), - array(60 * 1024 * 1024 * 1024, 0.60, 0.40), - array(50 * 1024 * 1024 * 1024, 0.60, 0.30), - array(40 * 1024 * 1024 * 1024, 0.50, 0.20), - array(30 * 1024 * 1024 * 1024, 0.40, 0.10), - array(20 * 1024 * 1024 * 1024, 0.30, 0.05), - array(10 * 1024 * 1024 * 1024, 0.20, 0.0), - array(5 * 1024 * 1024 * 1024, 0.15, 0.0) + array(80 * 1024 * 1024 * 1024, 0.60, 0.50), + array(60 * 1024 * 1024 * 1024, 0.60, 0.40), + array(50 * 1024 * 1024 * 1024, 0.60, 0.30), + array(40 * 1024 * 1024 * 1024, 0.50, 0.20), + array(30 * 1024 * 1024 * 1024, 0.40, 0.10), + array(20 * 1024 * 1024 * 1024, 0.30, 0.05), + array(10 * 1024 * 1024 * 1024, 0.20, 0.0), + array(5 * 1024 * 1024 * 1024, 0.15, 0.0) ); $DownloadBarrier = 100 * 1024 * 1024 * 1024; -$DB->query(" - UPDATE users_main - SET RequiredRatio = 0.60 - WHERE Downloaded > $DownloadBarrier"); +$DB->prepared_query(" + UPDATE users_main AS um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + SET um.RequiredRatio = 0.60 + WHERE uls.Downloaded > ?", $DownloadBarrier); foreach ($RatioRequirements as $Requirement) { - list($Download, $Ratio, $MinRatio) = $Requirement; - - $DB->query(" - UPDATE users_main - SET RequiredRatio = RequiredRatioWork * $Ratio - WHERE Downloaded >= '$Download' - AND Downloaded < '$DownloadBarrier'"); - - $DB->query(" - UPDATE users_main - SET RequiredRatio = $MinRatio - WHERE Downloaded >= '$Download' - AND Downloaded < '$DownloadBarrier' - AND RequiredRatio < $MinRatio"); - - /*$DB->query(" - UPDATE users_main - SET RequiredRatio = $Ratio - WHERE Downloaded >= '$Download' - AND Downloaded < '$DownloadBarrier' - AND can_leech = '0' - AND Enabled = '1'"); - */ - $DownloadBarrier = $Download; + list($Download, $Ratio, $MinRatio) = $Requirement; + + $DB->prepared_query(" + UPDATE users_main AS um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + SET um.RequiredRatio = um.RequiredRatioWork * ? + WHERE uls.Downloaded >= ? + AND uls.Downloaded < ?", $Ratio, $Download, $DownloadBarrier); + + $DB->prepared_query(" + UPDATE users_main AS um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + SET um.RequiredRatio = ? + WHERE uls.Downloaded >= ? + AND uls.Downloaded < ? + AND um.RequiredRatio < ?", $MinRatio, $Download, $DownloadBarrier, $MinRatio); + + /*$DB->query(" + UPDATE users_main + SET RequiredRatio = $Ratio + WHERE Downloaded >= '$Download' + AND Downloaded < '$DownloadBarrier' + AND can_leech = '0' + AND Enabled = '1'"); + */ + $DownloadBarrier = $Download; } $DB->query(" - UPDATE users_main - SET RequiredRatio = 0.00 - WHERE Downloaded < 5 * 1024 * 1024 * 1024"); \ No newline at end of file + UPDATE users_main + SET RequiredRatio = 0.00 + WHERE Downloaded < 5 * 1024 * 1024 * 1024"); diff --git a/sections/schedule/daily/ratio_watch.php b/sections/schedule/daily/ratio_watch.php index 6c00bef82..9a0119d0f 100644 --- a/sections/schedule/daily/ratio_watch.php +++ b/sections/schedule/daily/ratio_watch.php @@ -2,108 +2,112 @@ // Here is where we manage ratio watch -$OffRatioWatch = array(); -$OnRatioWatch = array(); +$OffRatioWatch = []; +$OnRatioWatch = []; // Take users off ratio watch and enable leeching $UserQuery = $DB->query(" - SELECT - m.ID, - torrent_pass - FROM users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - WHERE m.Downloaded > 0 - AND m.Uploaded / m.Downloaded >= m.RequiredRatio - AND i.RatioWatchEnds != '0000-00-00 00:00:00' - AND m.can_leech = '0' - AND m.Enabled = '1'"); + SELECT + um.ID, + um.torrent_pass + FROM users_info AS i + INNER JOIN users_main AS um ON (um.ID = i.UserID) + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + WHERE uls.Downloaded > 0 + AND uls.Uploaded / uls.Downloaded >= um.RequiredRatio + AND i.RatioWatchEnds != '0000-00-00 00:00:00' + AND um.can_leech = '0' + AND um.Enabled = '1'"); $OffRatioWatch = $DB->collect('ID'); if (count($OffRatioWatch) > 0) { - $DB->query(" - UPDATE users_info AS ui - JOIN users_main AS um ON um.ID = ui.UserID - SET ui.RatioWatchEnds = '0000-00-00 00:00:00', - ui.RatioWatchDownload = '0', - um.can_leech = '1', - ui.AdminComment = CONCAT('$sqltime - Leeching re-enabled by adequate ratio.\n\n', ui.AdminComment) - WHERE ui.UserID IN(".implode(',', $OffRatioWatch).')'); + $DB->query(" + UPDATE users_info AS ui + JOIN users_main AS um ON um.ID = ui.UserID + SET ui.RatioWatchEnds = '0000-00-00 00:00:00', + ui.RatioWatchDownload = '0', + um.can_leech = '1', + ui.AdminComment = CONCAT('$sqltime - Leeching re-enabled by adequate ratio.\n\n', ui.AdminComment) + WHERE ui.UserID IN(".implode(',', $OffRatioWatch).')'); } foreach ($OffRatioWatch as $UserID) { - $Cache->begin_transaction("user_info_heavy_$UserID"); - $Cache->update_row(false, array('RatioWatchEnds' => '0000-00-00 00:00:00', 'RatioWatchDownload' => '0', 'CanLeech' => 1)); - $Cache->commit_transaction(0); - Misc::send_pm($UserID, 0, 'You have been taken off Ratio Watch', "Congratulations! Feel free to begin downloading again.\n To ensure that you do not get put on ratio watch again, please read the rules located [url=".site_url()."rules.php?p=ratio]here[/url].\n"); - echo "Ratio watch off: $UserID\n"; + $Cache->begin_transaction("user_info_heavy_$UserID"); + $Cache->update_row(false, array('RatioWatchEnds' => '0000-00-00 00:00:00', 'RatioWatchDownload' => '0', 'CanLeech' => 1)); + $Cache->commit_transaction(0); + Misc::send_pm($UserID, 0, 'You have been taken off Ratio Watch', "Congratulations! Feel free to begin downloading again.\n To ensure that you do not get put on ratio watch again, please read the rules located [url=".site_url()."rules.php?p=ratio]here[/url].\n"); + echo "Ratio watch off: $UserID\n"; } $DB->set_query_id($UserQuery); $Passkeys = $DB->collect('torrent_pass'); foreach ($Passkeys as $Passkey) { - Tracker::update_tracker('update_user', array('passkey' => $Passkey, 'can_leech' => '1')); + Tracker::update_tracker('update_user', array('passkey' => $Passkey, 'can_leech' => '1')); } // Take users off ratio watch $UserQuery = $DB->query(" - SELECT m.ID, torrent_pass - FROM users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - WHERE m.Downloaded > 0 - AND m.Uploaded / m.Downloaded >= m.RequiredRatio - AND i.RatioWatchEnds != '0000-00-00 00:00:00' - AND m.Enabled = '1'"); + SELECT um.ID, um.torrent_pass + FROM users_info AS i + INNER JOIN users_main AS um ON (um.ID = i.UserID) + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + WHERE uls.Downloaded > 0 + AND uls.Uploaded / uls.Downloaded >= um.RequiredRatio + AND i.RatioWatchEnds != '0000-00-00 00:00:00' + AND um.Enabled = '1'"); $OffRatioWatch = $DB->collect('ID'); if (count($OffRatioWatch) > 0) { - $DB->query(" - UPDATE users_info AS ui - JOIN users_main AS um ON um.ID = ui.UserID - SET ui.RatioWatchEnds = '0000-00-00 00:00:00', - ui.RatioWatchDownload = '0', - um.can_leech = '1' - WHERE ui.UserID IN(".implode(',', $OffRatioWatch).')'); + $DB->query(" + UPDATE users_info AS ui + JOIN users_main AS um ON um.ID = ui.UserID + SET ui.RatioWatchEnds = '0000-00-00 00:00:00', + ui.RatioWatchDownload = '0', + um.can_leech = '1' + WHERE ui.UserID IN(".implode(',', $OffRatioWatch).')'); } foreach ($OffRatioWatch as $UserID) { - $Cache->begin_transaction("user_info_heavy_$UserID"); - $Cache->update_row(false, array('RatioWatchEnds' => '0000-00-00 00:00:00', 'RatioWatchDownload' => '0', 'CanLeech' => 1)); - $Cache->commit_transaction(0); - Misc::send_pm($UserID, 0, "You have been taken off Ratio Watch", "Congratulations! Feel free to begin downloading again.\n To ensure that you do not get put on ratio watch again, please read the rules located [url=".site_url()."rules.php?p=ratio]here[/url].\n"); - echo "Ratio watch off: $UserID\n"; + $Cache->begin_transaction("user_info_heavy_$UserID"); + $Cache->update_row(false, array('RatioWatchEnds' => '0000-00-00 00:00:00', 'RatioWatchDownload' => '0', 'CanLeech' => 1)); + $Cache->commit_transaction(0); + Misc::send_pm($UserID, 0, "You have been taken off Ratio Watch", "Congratulations! Feel free to begin downloading again.\n To ensure that you do not get put on ratio watch again, please read the rules located [url=".site_url()."rules.php?p=ratio]here[/url].\n"); + echo "Ratio watch off: $UserID\n"; } $DB->set_query_id($UserQuery); $Passkeys = $DB->collect('torrent_pass'); foreach ($Passkeys as $Passkey) { - Tracker::update_tracker('update_user', array('passkey' => $Passkey, 'can_leech' => '1')); + Tracker::update_tracker('update_user', array('passkey' => $Passkey, 'can_leech' => '1')); } // Put user on ratio watch if he doesn't meet the standards sleep(10); $DB->query(" - SELECT m.ID, m.Downloaded - FROM users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - WHERE m.Downloaded > 0 - AND m.Uploaded / m.Downloaded < m.RequiredRatio - AND i.RatioWatchEnds = '0000-00-00 00:00:00' - AND m.Enabled = '1' - AND m.can_leech = '1'"); + SELECT um.ID, um.Downloaded + FROM users_info AS i + INNER JOIN users_main AS um ON (um.ID = i.UserID) + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + WHERE uls.Downloaded > 0 + AND uls.Uploaded / uls.Downloaded < um.RequiredRatio + AND i.RatioWatchEnds = '0000-00-00 00:00:00' + AND um.Enabled = '1' + AND um.can_leech = '1'"); $OnRatioWatch = $DB->collect('ID'); if (count($OnRatioWatch) > 0) { - $DB->query(" - UPDATE users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - SET i.RatioWatchEnds = '".time_plus(60 * 60 * 24 * 14)."', - i.RatioWatchTimes = i.RatioWatchTimes + 1, - i.RatioWatchDownload = m.Downloaded - WHERE m.ID IN(".implode(',', $OnRatioWatch).')'); + $DB->query(" + UPDATE users_info AS i + INNER JOIN users_main AS um ON (um.ID = i.UserID) + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + SET i.RatioWatchEnds = '".time_plus(60 * 60 * 24 * 14)."', + i.RatioWatchTimes = i.RatioWatchTimes + 1, + i.RatioWatchDownload = uls.Downloaded + WHERE um.ID IN(".implode(',', $OnRatioWatch).')'); } foreach ($OnRatioWatch as $UserID) { - $Cache->begin_transaction("user_info_heavy_$UserID"); - $Cache->update_row(false, array('RatioWatchEnds' => time_plus(60 * 60 * 24 * 14), 'RatioWatchDownload' => 0)); - $Cache->commit_transaction(0); - Misc::send_pm($UserID, 0, 'You have been put on Ratio Watch', "This happens when your ratio falls below the requirements we have outlined in the rules located [url=".site_url()."rules.php?p=ratio]here[/url].\n For information about ratio watch, click the link above."); - echo "Ratio watch on: $UserID\n"; + $Cache->begin_transaction("user_info_heavy_$UserID"); + $Cache->update_row(false, array('RatioWatchEnds' => time_plus(60 * 60 * 24 * 14), 'RatioWatchDownload' => 0)); + $Cache->commit_transaction(0); + Misc::send_pm($UserID, 0, 'You have been put on Ratio Watch', "This happens when your ratio falls below the requirements outlined in the rules located [url=".site_url()."rules.php?p=ratio]here[/url].\n For information about ratio watch, click the link above."); + echo "Ratio watch on: $UserID\n"; } -sleep(5); \ No newline at end of file +sleep(5); diff --git a/sections/schedule/daily/update_daily_top10.php b/sections/schedule/daily/update_daily_top10.php index 7e536a4fb..da0a3e70e 100644 --- a/sections/schedule/daily/update_daily_top10.php +++ b/sections/schedule/daily/update_daily_top10.php @@ -2,112 +2,113 @@ // Daily top 10 history. $DB->query(" - INSERT INTO top10_history (Date, Type) - VALUES ('$sqltime', 'Daily')"); + INSERT INTO top10_history (Date, Type) + VALUES ('$sqltime', 'Daily')"); $HistoryID = $DB->inserted_id(); $Top10 = $Cache->get_value('top10tor_day_10'); if ($Top10 === false) { - $DB->query(" - SELECT - t.ID, - g.ID, - g.Name, - g.CategoryID, - g.TagList, - t.Format, - t.Encoding, - t.Media, - t.Scene, - t.HasLog, - t.HasCue, - t.HasLogDB, - t.LogScore, - t.LogChecksum, - t.RemasterYear, - g.Year, - t.RemasterTitle, - t.Snatched, - t.Seeders, - t.Leechers, - ((t.Size * t.Snatched) + (t.Size * 0.5 * t.Leechers)) AS Data - FROM torrents AS t - LEFT JOIN torrents_group AS g ON g.ID = t.GroupID - WHERE t.Seeders > 0 - AND t.Time > ('$sqltime' - INTERVAL 1 DAY) - ORDER BY (t.Seeders + t.Leechers) DESC - LIMIT 10;"); + $DB->query(" + SELECT + t.ID, + g.ID, + g.Name, + g.CategoryID, + g.TagList, + t.Format, + t.Encoding, + t.Media, + t.Scene, + t.HasLog, + t.HasCue, + t.HasLogDB, + t.LogScore, + t.LogChecksum, + t.RemasterYear, + g.Year, + t.RemasterTitle, + tls.Snatched, + tls.Seeders, + tls.Leechers, + ((t.Size * tls.Snatched) + (t.Size * 0.5 * tls.Leechers)) AS Data + FROM torrents AS t + INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID) + INNER JOIN torrents_group AS g ON (g.ID = t.GroupID) + WHERE tls.Seeders > 0 + AND t.Time > (now() - INTERVAL 1 DAY) + ORDER BY (tls.Seeders + tls.Leechers) DESC + LIMIT 10;"); - $Top10 = $DB->to_array(); + $Top10 = $DB->to_array(); } $i = 1; foreach ($Top10 as $Torrent) { - list($TorrentID, $GroupID, $GroupName, $GroupCategoryID, $TorrentTags, - $Format, $Encoding, $Media, $Scene, $HasLog, $HasCue, $HasLogDB, $LogScore, $LogChecksum, - $Year, $GroupYear, $RemasterTitle, $Snatched, $Seeders, $Leechers, $Data) = $Torrent; + list($TorrentID, $GroupID, $GroupName, $GroupCategoryID, $TorrentTags, + $Format, $Encoding, $Media, $Scene, $HasLog, $HasCue, $HasLogDB, $LogScore, $LogChecksum, + $Year, $GroupYear, $RemasterTitle, $Snatched, $Seeders, $Leechers, $Data) = $Torrent; - $DisplayName = ''; + $DisplayName = ''; - $Artists = Artists::get_artist($GroupID); + $Artists = Artists::get_artist($GroupID); - if (!empty($Artists)) { - $DisplayName = Artists::display_artists($Artists, false, true); - } + if (!empty($Artists)) { + $DisplayName = Artists::display_artists($Artists, false, true); + } - $DisplayName .= $GroupName; + $DisplayName .= $GroupName; - if ($GroupCategoryID == 1 && $GroupYear > 0) { - $DisplayName .= " [$GroupYear]"; - } + if ($GroupCategoryID == 1 && $GroupYear > 0) { + $DisplayName .= " [$GroupYear]"; + } - // append extra info to torrent title - $ExtraInfo = ''; - $AddExtra = ''; - if ($Format) { - $ExtraInfo .= $Format; - $AddExtra = ' / '; - } - if ($Encoding) { - $ExtraInfo .= $AddExtra.$Encoding; - $AddExtra = ' / '; - } - // "FLAC / Lossless / Log (100%) / Cue / CD"; - if ($HasLog) { - $ExtraInfo .= "{$AddExtra}Log".($HasLogDB ? " ($LogScore%)" : ""); - $AddExtra = ' / '; - } - if ($HasCue) { - $ExtraInfo .= "{$AddExtra}Cue"; - $AddExtra = ' / '; - } - if ($Media) { - $ExtraInfo .= $AddExtra.$Media; - $AddExtra = ' / '; - } - if ($Scene) { - $ExtraInfo .= "{$AddExtra}Scene"; - $AddExtra = ' / '; - } - if ($Year > 0) { - $ExtraInfo .= $AddExtra.$Year; - $AddExtra = ' '; - } - if ($RemasterTitle) { - $ExtraInfo .= $AddExtra.$RemasterTitle; - } - if ($ExtraInfo != '') { - $ExtraInfo = "- [$ExtraInfo]"; - } + // append extra info to torrent title + $ExtraInfo = ''; + $AddExtra = ''; + if ($Format) { + $ExtraInfo .= $Format; + $AddExtra = ' / '; + } + if ($Encoding) { + $ExtraInfo .= $AddExtra.$Encoding; + $AddExtra = ' / '; + } + // "FLAC / Lossless / Log (100%) / Cue / CD"; + if ($HasLog) { + $ExtraInfo .= "{$AddExtra}Log".($HasLogDB ? " ($LogScore%)" : ""); + $AddExtra = ' / '; + } + if ($HasCue) { + $ExtraInfo .= "{$AddExtra}Cue"; + $AddExtra = ' / '; + } + if ($Media) { + $ExtraInfo .= $AddExtra.$Media; + $AddExtra = ' / '; + } + if ($Scene) { + $ExtraInfo .= "{$AddExtra}Scene"; + $AddExtra = ' / '; + } + if ($Year > 0) { + $ExtraInfo .= $AddExtra.$Year; + $AddExtra = ' '; + } + if ($RemasterTitle) { + $ExtraInfo .= $AddExtra.$RemasterTitle; + } + if ($ExtraInfo != '') { + $ExtraInfo = "- [$ExtraInfo]"; + } - $TitleString = "$DisplayName $ExtraInfo"; + $TitleString = "$DisplayName $ExtraInfo"; - $TagString = str_replace('|', ' ', $TorrentTags); + $TagString = str_replace('|', ' ', $TorrentTags); - $DB->query(" - INSERT INTO top10_history_torrents - (HistoryID, Rank, TorrentID, TitleString, TagString) - VALUES - ($HistoryID, $i, $TorrentID, '".db_string($TitleString)."', '".db_string($TagString)."')"); - $i++; -} \ No newline at end of file + $DB->query(" + INSERT INTO top10_history_torrents + (HistoryID, Rank, TorrentID, TitleString, TagString) + VALUES + ($HistoryID, $i, $TorrentID, '".db_string($TitleString)."', '".db_string($TagString)."')"); + $i++; +} diff --git a/sections/schedule/daily/user_stats_monthly.php b/sections/schedule/daily/user_stats_monthly.php index 3c9300ae6..c057f3ed4 100644 --- a/sections/schedule/daily/user_stats_monthly.php +++ b/sections/schedule/daily/user_stats_monthly.php @@ -2,19 +2,20 @@ $DB->prepared_query(" INSERT INTO users_stats_monthly (UserID, Uploaded, Downloaded, BonusPoints, Torrents, PerfectFLACs) -SELECT um.ID, um.Uploaded, um.Downloaded, um.BonusPoints, COUNT(t.ID) AS Torrents, COALESCE(p.Perfects, 0) AS PerfectFLACs +SELECT um.ID, uls.Uploaded, uls.Downloaded, um.BonusPoints, COUNT(t.ID) AS Torrents, COALESCE(p.Perfects, 0) AS PerfectFLACs FROM users_main um +INNER JOIN users_leech_stats uls ON uls.UserID = um.ID LEFT JOIN torrents t ON t.UserID = um.ID LEFT JOIN ( - SELECT UserID, COUNT(ID) AS Perfects - FROM torrents - WHERE( Format = 'FLAC' - AND ( - Media IN ('Vinyl', 'WEB', 'DVD', 'Soundboard', 'Cassette', 'SACD', 'BD', 'DAT') - OR - (LogScore = 100 AND Media = 'CD'))) - GROUP BY UserID + SELECT UserID, COUNT(ID) AS Perfects + FROM torrents + WHERE( Format = 'FLAC' + AND ( + Media IN ('Vinyl', 'WEB', 'DVD', 'Soundboard', 'Cassette', 'SACD', 'BD', 'DAT') + OR + (LogScore = 100 AND Media = 'CD'))) + GROUP BY UserID ) p ON p.UserID = um.ID GROUP BY um.ID;"); diff --git a/sections/schedule/disabled/freeleech.php b/sections/schedule/disabled/freeleech.php index 6c4662086..0e75f37bf 100644 --- a/sections/schedule/disabled/freeleech.php +++ b/sections/schedule/disabled/freeleech.php @@ -6,21 +6,21 @@ $TimeMinus = time_minus(3600 * 7); $DB->query(" - SELECT DISTINCT GroupID - FROM torrents - WHERE FreeTorrent = '1' - AND FreeLeechType = '3' - AND Time < '$TimeMinus'"); + SELECT DISTINCT GroupID + FROM torrents + WHERE FreeTorrent = '1' + AND FreeLeechType = '3' + AND Time < '$TimeMinus'"); while (list($GroupID) = $DB->next_record()) { - $Cache->delete_value("torrents_details_$GroupID"); - $Cache->delete_value("torrent_group_$GroupID"); + $Cache->delete_value("torrents_details_$GroupID"); + $Cache->delete_value("torrent_group_$GroupID"); } $DB->query(" - UPDATE torrents - SET FreeTorrent = '0', - FreeLeechType = '0' - WHERE FreeTorrent = '1' - AND FreeLeechType = '3' - AND Time < '$TimeMinus'"); + UPDATE torrents + SET FreeTorrent = '0', + FreeLeechType = '0' + WHERE FreeTorrent = '1' + AND FreeLeechType = '3' + AND Time < '$TimeMinus'"); -sleep(5); \ No newline at end of file +sleep(5); diff --git a/sections/schedule/disabled/rescore_eac_95.php b/sections/schedule/disabled/rescore_eac_95.php index 5e17796e5..7e7796b3f 100644 --- a/sections/schedule/disabled/rescore_eac_95.php +++ b/sections/schedule/disabled/rescore_eac_95.php @@ -3,26 +3,26 @@ //------------- Rescore 0.95 logs of disabled users $LogQuery = $DB->query(" - SELECT DISTINCT t.ID - FROM torrents AS t - JOIN users_main AS um ON t.UserID = um.ID - JOIN torrents_logs_new AS tl ON tl.TorrentID = t.ID - WHERE um.Enabled = '2' - AND t.HasLog = '1' - AND LogScore = 100 - AND Log LIKE 'EAC extraction logfile from%'"); -$Details = array(); + SELECT DISTINCT t.ID + FROM torrents AS t + JOIN users_main AS um ON t.UserID = um.ID + JOIN torrents_logs_new AS tl ON tl.TorrentID = t.ID + WHERE um.Enabled = '2' + AND t.HasLog = '1' + AND LogScore = 100 + AND Log LIKE 'EAC extraction logfile from%'"); +$Details = []; $Details[] = "Ripped with EAC v0.95, -1 point [1]"; $Details = serialize($Details); while (list($TorrentID) = $DB->next_record()) { - $DB->query(" - UPDATE torrents - SET LogScore = 99 - WHERE ID = $TorrentID"); - $DB->query(" - UPDATE torrents_logs_new - SET Score = 99, Details = '$Details' - WHERE TorrentID = $TorrentID"); + $DB->query(" + UPDATE torrents + SET LogScore = 99 + WHERE ID = $TorrentID"); + $DB->query(" + UPDATE torrents_logs_new + SET Score = 99, Details = '$Details' + WHERE TorrentID = $TorrentID"); } -sleep(5); \ No newline at end of file +sleep(5); diff --git a/sections/schedule/disabled/reward_perfect_flac_uploads.php b/sections/schedule/disabled/reward_perfect_flac_uploads.php index f935cb043..0734d43f0 100644 --- a/sections/schedule/disabled/reward_perfect_flac_uploads.php +++ b/sections/schedule/disabled/reward_perfect_flac_uploads.php @@ -11,17 +11,17 @@ GROUP BY t.UserID ORDER BY c DESC"); if ($DB->has_results()) { - while (list($UserID, $Username, $Count, $FLT_Given, $Invites_Given, $CurInvites, $CurFLTokens) = $DB->next_record()) { - $FLTokens = max(floor($Count / 5) - $FLT_Given, 0); - $Invites = max(floor($Count / 20)-$Invites_Given, 0); - $SQL = "UPDATE users_main SET FLTokens = FLTokens + $FLTokens, Invites = Invites + $Invites, FLT_Given = FLT_Given + $FLTokens, Invites_Given = Invites_Given + $Invites WHERE ID = $UserID"; - if ($FLTokens != 0) { - G::$DB->query_unb($SQL); - $Invites = $Invites + $CurInvites; - $FLTokens = $FLTokens + $CurFLTokens; - G::$Cache->begin_transaction('user_info_heavy_'.$UserID); - G::$Cache->update_row(false, array('Invites' => $Invites, 'FLTokens' => $FLTokens)); - G::$Cache->commit_transaction(0); - } - } + while (list($UserID, $Username, $Count, $FLT_Given, $Invites_Given, $CurInvites, $CurFLTokens) = $DB->next_record()) { + $FLTokens = max(floor($Count / 5) - $FLT_Given, 0); + $Invites = max(floor($Count / 20)-$Invites_Given, 0); + $SQL = "UPDATE users_main SET FLTokens = FLTokens + $FLTokens, Invites = Invites + $Invites, FLT_Given = FLT_Given + $FLTokens, Invites_Given = Invites_Given + $Invites WHERE ID = $UserID"; + if ($FLTokens != 0) { + G::$DB->query_unb($SQL); + $Invites = $Invites + $CurInvites; + $FLTokens = $FLTokens + $CurFLTokens; + G::$Cache->begin_transaction('user_info_heavy_'.$UserID); + G::$Cache->update_row(false, array('Invites' => $Invites, 'FLTokens' => $FLTokens)); + G::$Cache->commit_transaction(0); + } + } } diff --git a/sections/schedule/disabled/update_geoip.php b/sections/schedule/disabled/update_geoip.php index d295c52b8..8efee24b2 100644 --- a/sections/schedule/disabled/update_geoip.php +++ b/sections/schedule/disabled/update_geoip.php @@ -1,10 +1,10 @@ <?php $DB->query("INSERT INTO users_geodistribution - (Code, Users) + (Code, Users) SELECT g.Code, COUNT(u.ID) AS Users FROM geoip_country AS g - JOIN users_main AS u ON INET_ATON(u.IP) BETWEEN g.StartIP AND g.EndIP + JOIN users_main AS u ON INET_ATON(u.IP) BETWEEN g.StartIP AND g.EndIP WHERE u.Enabled = '1' GROUP BY g.Code -ORDER BY Users DESC"); \ No newline at end of file +ORDER BY Users DESC"); diff --git a/sections/schedule/every/calculate_contest_leaderboard.php b/sections/schedule/every/calculate_contest_leaderboard.php index 2d3cb4b27..6035fbb63 100644 --- a/sections/schedule/every/calculate_contest_leaderboard.php +++ b/sections/schedule/every/calculate_contest_leaderboard.php @@ -1,4 +1,11 @@ <?php -Contest::calculate_leaderboard(); -Contest::calculate_request_pairs(); +$ContestMgr = new \Gazelle\Contest(G::$DB, G::$Cache); + +$ContestMgr->calculate_leaderboard(); +$ContestMgr->calculate_request_pairs(); + +$total = $ContestMgr->schedule_payout(); +if ($total) { + echo "$total messages sent\n"; +} diff --git a/sections/schedule/every/delete_tags.php b/sections/schedule/every/delete_tags.php index 68990c0c3..52f1dd9f3 100644 --- a/sections/schedule/every/delete_tags.php +++ b/sections/schedule/every/delete_tags.php @@ -2,6 +2,6 @@ //------------- Delete unpopular tags -----------------------------------// $DB->query(" - DELETE FROM torrents_tags - WHERE NegativeVotes > 1 - AND NegativeVotes > PositiveVotes"); \ No newline at end of file + DELETE FROM torrents_tags + WHERE NegativeVotes > 1 + AND NegativeVotes > PositiveVotes"); diff --git a/sections/schedule/every/expire_fl_tokens.php b/sections/schedule/every/expire_fl_tokens.php index 44f922a7e..a090cf523 100644 --- a/sections/schedule/every/expire_fl_tokens.php +++ b/sections/schedule/every/expire_fl_tokens.php @@ -3,28 +3,28 @@ //------------- Expire old FL Tokens and clear cache where needed ------// $sqltime = sqltime(); $DB->query(" - SELECT DISTINCT UserID - FROM users_freeleeches - WHERE Expired = FALSE - AND Time < '$sqltime' - INTERVAL 4 DAY"); + SELECT DISTINCT UserID + FROM users_freeleeches + WHERE Expired = FALSE + AND Time < '$sqltime' - INTERVAL 4 DAY"); if ($DB->has_results()) { - while (list($UserID) = $DB->next_record()) { - $Cache->delete_value('users_tokens_'.$UserID[0]); - } + while (list($UserID) = $DB->next_record()) { + $Cache->delete_value('users_tokens_'.$UserID[0]); + } - $DB->query(" - SELECT uf.UserID, t.info_hash - FROM users_freeleeches AS uf - JOIN torrents AS t ON uf.TorrentID = t.ID - WHERE uf.Expired = FALSE - AND uf.Time < '$sqltime' - INTERVAL 4 DAY"); - while (list($UserID, $InfoHash) = $DB->next_record(MYSQLI_NUM, false)) { - Tracker::update_tracker('remove_token', array('info_hash' => rawurlencode($InfoHash), 'userid' => $UserID)); - } - $DB->query(" - UPDATE users_freeleeches - SET Expired = TRUE - WHERE Time < '$sqltime' - INTERVAL 4 DAY - AND Expired = FALSE"); -} \ No newline at end of file + $DB->query(" + SELECT uf.UserID, t.info_hash + FROM users_freeleeches AS uf + JOIN torrents AS t ON uf.TorrentID = t.ID + WHERE uf.Expired = FALSE + AND uf.Time < '$sqltime' - INTERVAL 4 DAY"); + while (list($UserID, $InfoHash) = $DB->next_record(MYSQLI_NUM, false)) { + Tracker::update_tracker('remove_token', array('info_hash' => rawurlencode($InfoHash), 'userid' => $UserID)); + } + $DB->query(" + UPDATE users_freeleeches + SET Expired = TRUE + WHERE Time < '$sqltime' - INTERVAL 4 DAY + AND Expired = FALSE"); +} diff --git a/sections/schedule/every/recovery.php b/sections/schedule/every/recovery.php new file mode 100644 index 000000000..859c4f18d --- /dev/null +++ b/sections/schedule/every/recovery.php @@ -0,0 +1,7 @@ +<?php + +if (defined('RECOVERY_AUTOVALIDATE') && RECOVERY_AUTOVALIDATE) { + \Gazelle\Recovery::validate_pending(G::$DB); +} + +\Gazelle\Recovery::boost_upload(G::$DB, G::$Cache); diff --git a/sections/schedule/hourly/community_stats.php b/sections/schedule/hourly/community_stats.php index 13f53ad97..72bcf4c50 100644 --- a/sections/schedule/hourly/community_stats.php +++ b/sections/schedule/hourly/community_stats.php @@ -1,25 +1,25 @@ <?php $DB->prepared_query(" - INSERT INTO users_summary (UserID, Groups) - SELECT UserID, COUNT(DISTINCT GroupID) - FROM torrents t - INNER JOIN users_main u ON u.ID = t.UserID - GROUP BY UserID - ON DUPLICATE KEY UPDATE Groups = VALUES(Groups)"); + INSERT INTO users_summary (UserID, Groups) + SELECT UserID, COUNT(DISTINCT GroupID) + FROM torrents t + INNER JOIN users_main u ON u.ID = t.UserID + GROUP BY UserID + ON DUPLICATE KEY UPDATE Groups = VALUES(Groups)"); $DB->prepared_query(" INSERT INTO users_summary (UserID, PerfectFlacs) - SELECT t.UserID, COUNT(t.ID) - FROM torrents t - INNER JOIN users_main u ON u.ID = t.UserID - WHERE ( t.Format = 'FLAC' - AND ( - (t.LogScore = 100 AND t.Media = 'CD') + SELECT t.UserID, COUNT(t.ID) + FROM torrents t + INNER JOIN users_main u ON u.ID = t.UserID + WHERE ( t.Format = 'FLAC' + AND ( + (t.LogScore = 100 AND t.Media = 'CD') OR t.Media IN ('Vinyl', 'WEB', 'DVD', 'Soundboard', 'Cassette', 'SACD', 'BD', 'DAT') - ) - ) - GROUP BY t.UserID + ) + ) + GROUP BY t.UserID ON DUPLICATE KEY UPDATE PerfectFlacs = VALUES(PerfectFlacs)"); diff --git a/sections/schedule/hourly/disable_leeching_ratio_watch.php b/sections/schedule/hourly/disable_leeching_ratio_watch.php index 262ca8682..1d5d35342 100644 --- a/sections/schedule/hourly/disable_leeching_ratio_watch.php +++ b/sections/schedule/hourly/disable_leeching_ratio_watch.php @@ -4,27 +4,27 @@ // disable leeching privileges, and send the user a message $DB->query(" - SELECT ID, torrent_pass - FROM users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - WHERE i.RatioWatchEnds != '0000-00-00 00:00:00' - AND i.RatioWatchDownload + 10 * 1024 * 1024 * 1024 < m.Downloaded - AND m.Enabled = '1' - AND m.can_leech = '1'"); + SELECT ID, torrent_pass + FROM users_info AS i + JOIN users_main AS m ON m.ID = i.UserID + WHERE i.RatioWatchEnds != '0000-00-00 00:00:00' + AND i.RatioWatchDownload + 10 * 1024 * 1024 * 1024 < m.Downloaded + AND m.Enabled = '1' + AND m.can_leech = '1'"); $Users = $DB->to_pair('torrent_pass', 'ID'); if (count($Users) > 0) { - $Subject = 'Leeching Disabled'; - $Message = 'You have downloaded more than 10 GB while on Ratio Watch. Your leeching privileges have been disabled. Please reread the rules and refer to this guide on how to improve your ratio ' . site_url() . 'wiki.php?action=article&id=115'; - foreach ($Users as $TorrentPass => $UserID) { - Misc::send_pm($UserID, 0, $Subject, $Message); - Tracker::update_tracker('update_user', array('passkey' => $TorrentPass, 'can_leech' => '0')); - } + $Subject = 'Leeching Disabled'; + $Message = 'You have downloaded more than 10 GB while on Ratio Watch. Your leeching privileges have been disabled. Please reread the rules and refer to this guide on how to improve your ratio ' . site_url() . 'wiki.php?action=article&id=115'; + foreach ($Users as $TorrentPass => $UserID) { + Misc::send_pm($UserID, 0, $Subject, $Message); + Tracker::update_tracker('update_user', array('passkey' => $TorrentPass, 'can_leech' => '0')); + } - $DB->query(" - UPDATE users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - SET m.can_leech = '0', - i.AdminComment = CONCAT('$sqltime - Leeching privileges disabled by ratio watch system for downloading more than 10 GBs on ratio watch. - required ratio: ', m.RequiredRatio, '\n\n', i.AdminComment) - WHERE m.ID IN(" . implode(',', $Users) . ')'); -} \ No newline at end of file + $DB->query(" + UPDATE users_info AS i + JOIN users_main AS m ON m.ID = i.UserID + SET m.can_leech = '0', + i.AdminComment = CONCAT('$sqltime - Leeching privileges disabled by ratio watch system for downloading more than 10 GBs on ratio watch. - required ratio: ', m.RequiredRatio, '\n\n', i.AdminComment) + WHERE m.ID IN(" . implode(',', $Users) . ')'); +} diff --git a/sections/schedule/hourly/expire_invites.php b/sections/schedule/hourly/expire_invites.php index 652998876..5538da3e9 100644 --- a/sections/schedule/hourly/expire_invites.php +++ b/sections/schedule/hourly/expire_invites.php @@ -5,10 +5,10 @@ $DB->query("SELECT InviterID FROM invites WHERE Expires < '$sqltime'"); $Users = $DB->to_array(); foreach ($Users as $User) { - list($UserID) = $User; - $DB->prepared_query("UPDATE users_main SET Invites = Invites + 1 WHERE ID = ?", $UserID); - $Cache->begin_transaction("user_info_heavy_$UserID"); - $Cache->update_row(false, array('Invites' => '+1')); - $Cache->commit_transaction(0); + list($UserID) = $User; + $DB->prepared_query("UPDATE users_main SET Invites = Invites + 1 WHERE ID = ?", $UserID); + $Cache->begin_transaction("user_info_heavy_$UserID"); + $Cache->update_row(false, array('Invites' => '+1')); + $Cache->commit_transaction(0); } $DB->query("DELETE FROM invites WHERE Expires < '$sqltime'"); diff --git a/sections/schedule/hourly/front_page_stats.php b/sections/schedule/hourly/front_page_stats.php index c27f0d9a9..a164855f8 100644 --- a/sections/schedule/hourly/front_page_stats.php +++ b/sections/schedule/hourly/front_page_stats.php @@ -5,43 +5,43 @@ //Love or hate, this makes things a hell of a lot faster if ($Hour % 2 == 0) { - $DB->query(" - SELECT COUNT(uid) AS Snatches - FROM xbt_snatched"); - list($SnatchStats) = $DB->next_record(); - $Cache->cache_value('stats_snatches', $SnatchStats, 0); + $DB->query(" + SELECT COUNT(uid) AS Snatches + FROM xbt_snatched"); + list($SnatchStats) = $DB->next_record(); + $Cache->cache_value('stats_snatches', $SnatchStats, 0); } $DB->query(" - SELECT IF(remaining = 0, 'Seeding', 'Leeching') AS Type, - COUNT(uid) - FROM xbt_files_users - WHERE active = 1 - GROUP BY Type"); + SELECT IF(remaining = 0, 'Seeding', 'Leeching') AS Type, + COUNT(uid) + FROM xbt_files_users + WHERE active = 1 + GROUP BY Type"); $PeerCount = $DB->to_array(0, MYSQLI_NUM, false); $SeederCount = isset($PeerCount['Seeding'][1]) ? $PeerCount['Seeding'][1] : 0; $LeecherCount = isset($PeerCount['Leeching'][1]) ? $PeerCount['Leeching'][1] : 0; $Cache->cache_value('stats_peers', array($LeecherCount, $SeederCount), 0); $DB->query(" - SELECT COUNT(ID) - FROM users_main - WHERE Enabled = '1' - AND LastAccess > '".time_minus(3600 * 24)."'"); + SELECT COUNT(ID) + FROM users_main + WHERE Enabled = '1' + AND LastAccess > now() - INTERVAL 1 DAY"); list($UserStats['Day']) = $DB->next_record(); $DB->query(" - SELECT COUNT(ID) - FROM users_main - WHERE Enabled = '1' - AND LastAccess > '".time_minus(3600 * 24 * 7)."'"); + SELECT COUNT(ID) + FROM users_main + WHERE Enabled = '1' + AND LastAccess > now() - INTERVAL 1 WEEK"); list($UserStats['Week']) = $DB->next_record(); $DB->query(" - SELECT COUNT(ID) - FROM users_main - WHERE Enabled = '1' - AND LastAccess > '".time_minus(3600 * 24 * 30)."'"); + SELECT COUNT(ID) + FROM users_main + WHERE Enabled = '1' + AND LastAccess > now() - INTERVAL 1 MONTH"); list($UserStats['Month']) = $DB->next_record(); -$Cache->cache_value('stats_users', $UserStats, 0); \ No newline at end of file +$Cache->cache_value('stats_users', $UserStats, 0); diff --git a/sections/schedule/hourly/hide_old_requests.php b/sections/schedule/hourly/hide_old_requests.php index 0fd8db9ee..28eb2d9ed 100644 --- a/sections/schedule/hourly/hide_old_requests.php +++ b/sections/schedule/hourly/hide_old_requests.php @@ -3,7 +3,7 @@ //------------- Hide old requests ---------------------------------------// sleep(3); $DB->query(" - UPDATE requests - SET Visible = 0 - WHERE TimeFilled < (NOW() - INTERVAL 7 DAY) - AND TimeFilled != '0000-00-00 00:00:00'"); \ No newline at end of file + UPDATE requests + SET Visible = 0 + WHERE TimeFilled < (NOW() - INTERVAL 7 DAY) + AND TimeFilled != '0000-00-00 00:00:00'"); diff --git a/sections/schedule/hourly/lower_login_attempts.php b/sections/schedule/hourly/lower_login_attempts.php index 536275e95..74bf3f390 100644 --- a/sections/schedule/hourly/lower_login_attempts.php +++ b/sections/schedule/hourly/lower_login_attempts.php @@ -2,9 +2,9 @@ //------------- Lower Login Attempts ------------------------------------// $DB->query(" - UPDATE login_attempts - SET Attempts = Attempts - 1 - WHERE Attempts > 0"); + UPDATE login_attempts + SET Attempts = Attempts - 1 + WHERE Attempts > 0"); $DB->query(" - DELETE FROM login_attempts - WHERE LastAttempt < '".time_minus(3600 * 24 * 90)."'"); \ No newline at end of file + DELETE FROM login_attempts + WHERE LastAttempt < '".time_minus(3600 * 24 * 90)."'"); diff --git a/sections/schedule/hourly/promote_users.php b/sections/schedule/hourly/promote_users.php index 0e2c152ed..2b465f88e 100644 --- a/sections/schedule/hourly/promote_users.php +++ b/sections/schedule/hourly/promote_users.php @@ -2,134 +2,136 @@ //------------- Promote users -------------------------------------------// sleep(5); -$Criteria = array(); +$Criteria = []; $Criteria[] = array('From' => USER, 'To' => MEMBER, 'MinUpload' => 10 * 1024 * 1024 * 1024, 'MinRatio' => 0.7, 'MinUploads' => 0, 'MaxTime' => time_minus(3600 * 24 * 7)); $Criteria[] = array('From' => MEMBER, 'To' => POWER, 'MinUpload' => 25 * 1024 * 1024 * 1024, 'MinRatio' => 1.05, 'MinUploads' => 5, 'MaxTime' => time_minus(3600 * 24 * 7 * 2)); $Criteria[] = array('From' => POWER, 'To' => ELITE, 'MinUpload' => 100 * 1024 * 1024 * 1024, 'MinRatio' => 1.05, 'MinUploads' => 50, 'MaxTime' => time_minus(3600 * 24 * 7 * 4)); $Criteria[] = array('From' => ELITE, 'To' => TORRENT_MASTER, 'MinUpload' => 500 * 1024 * 1024 * 1024, 'MinRatio' => 1.05, 'MinUploads' => 500, 'MaxTime' => time_minus(3600 * 24 * 7 * 8)); $Criteria[] = array( - 'From' => TORRENT_MASTER, - 'To' => POWER_TM, - 'MinUpload' => 500 * 1024 * 1024 * 1024, - 'MinRatio' => 1.05, - 'MinUploads' => 500, - 'MaxTime' => time_minus(3600 * 24 * 7 * 8), - 'Extra' => ' - ( - SELECT COUNT(DISTINCT GroupID) - FROM torrents - WHERE UserID = users_main.ID - ) >= 500'); + 'From' => TORRENT_MASTER, + 'To' => POWER_TM, + 'MinUpload' => 500 * 1024 * 1024 * 1024, + 'MinRatio' => 1.05, + 'MinUploads' => 500, + 'MaxTime' => time_minus(3600 * 24 * 7 * 8), + 'Extra' => ' + ( + SELECT COUNT(DISTINCT GroupID) + FROM torrents + WHERE UserID = um.ID + ) >= 500'); $Criteria[] = array( - 'From' => POWER_TM, - 'To' => ELITE_TM, - 'MinUpload' => 500 * 1024 * 1024 * 1024, - 'MinRatio' => 1.05, - 'MinUploads' => 500, - 'MaxTime' => time_minus(3600 * 24 * 7 * 8), - 'Extra' => " - ( - SELECT COUNT(ID) - FROM torrents - WHERE ((LogScore = 100 AND Format = 'FLAC') - OR (Media = 'Vinyl' AND Format = 'FLAC') - OR (Media = 'WEB' AND Format = 'FLAC') - OR (Media = 'DVD' AND Format = 'FLAC') - OR (Media = 'Soundboard' AND Format = 'FLAC') - OR (Media = 'Cassette' AND Format = 'FLAC') - OR (Media = 'SACD' AND Format = 'FLAC') - OR (Media = 'Blu-ray' AND Format = 'FLAC') - OR (Media = 'DAT' AND Format = 'FLAC') - ) - AND UserID = users_main.ID - ) >= 500"); + 'From' => POWER_TM, + 'To' => ELITE_TM, + 'MinUpload' => 500 * 1024 * 1024 * 1024, + 'MinRatio' => 1.05, + 'MinUploads' => 500, + 'MaxTime' => time_minus(3600 * 24 * 7 * 8), + 'Extra' => " + ( + SELECT COUNT(ID) + FROM torrents + WHERE ((LogScore = 100 AND Format = 'FLAC') + OR (Media = 'Vinyl' AND Format = 'FLAC') + OR (Media = 'WEB' AND Format = 'FLAC') + OR (Media = 'DVD' AND Format = 'FLAC') + OR (Media = 'Soundboard' AND Format = 'FLAC') + OR (Media = 'Cassette' AND Format = 'FLAC') + OR (Media = 'SACD' AND Format = 'FLAC') + OR (Media = 'Blu-ray' AND Format = 'FLAC') + OR (Media = 'DAT' AND Format = 'FLAC') + ) + AND UserID = um.ID + ) >= 500"); foreach ($Criteria as $L) { // $L = Level - $Query = " - SELECT ID - FROM users_main - JOIN users_info ON users_main.ID = users_info.UserID - WHERE PermissionID = ".$L['From']." - AND Warned = '0000-00-00 00:00:00' - AND Uploaded >= '$L[MinUpload]' - AND (Uploaded / Downloaded >= '$L[MinRatio]' OR (Uploaded / Downloaded IS NULL)) - AND JoinDate < '$L[MaxTime]' - AND ( - SELECT COUNT(ID) - FROM torrents - WHERE UserID = users_main.ID - ) >= '$L[MinUploads]' - AND Enabled = '1'"; - if (!empty($L['Extra'])) { - $Query .= ' AND '.$L['Extra']; - } + $Query = " + SELECT um.ID + FROM users_main AS um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + INNER JOIN users_info AS ui ON (ui.UserID = um.ID) + WHERE um.PermissionID = ".$L['From']." + AND ui.Warned = '0000-00-00 00:00:00' + AND uls.Uploaded >= '$L[MinUpload]' + AND (uls.Uploaded / uls.Downloaded >= '$L[MinRatio]' OR (uls.Uploaded / uls.Downloaded IS NULL)) + AND ui.JoinDate < '$L[MaxTime]' + AND ( + SELECT count(*) + FROM torrents + WHERE UserID = um.ID + ) >= '$L[MinUploads]' + AND um.Enabled = '1'"; + if (!empty($L['Extra'])) { + $Query .= ' AND '.$L['Extra']; + } - $DB->query($Query); + $DB->query($Query); - $UserIDs = $DB->collect('ID'); + $UserIDs = $DB->collect('ID'); - if (count($UserIDs) > 0) { - $DB->query(" - UPDATE users_main - SET PermissionID = ".$L['To']." - WHERE ID IN(".implode(',', $UserIDs).')'); - foreach ($UserIDs as $UserID) { - /*$Cache->begin_transaction("user_info_$UserID"); - $Cache->update_row(false, array('PermissionID' => $L['To'])); - $Cache->commit_transaction(0);*/ - $Cache->delete_value("user_info_$UserID"); - $Cache->delete_value("user_info_heavy_$UserID"); - $Cache->delete_value("user_stats_$UserID"); - $Cache->delete_value("enabled_$UserID"); - $DB->query(" - UPDATE users_info - SET AdminComment = CONCAT('".sqltime()." - Class changed to ".Users::make_class_string($L['To'])." by System\n\n', AdminComment) - WHERE UserID = $UserID"); - Misc::send_pm($UserID, 0, 'You have been promoted to '.Users::make_class_string($L['To']), 'Congratulations on your promotion to '.Users::make_class_string($L['To'])."!\n\nTo read more about ".SITE_NAME."'s user classes, read [url=".site_url()."wiki.php?action=article&name=userclasses]this wiki article[/url]."); - } - } + if (count($UserIDs) > 0) { + $DB->query(" + UPDATE users_main + SET PermissionID = ".$L['To']." + WHERE ID IN(".implode(',', $UserIDs).')'); + foreach ($UserIDs as $UserID) { + /*$Cache->begin_transaction("user_info_$UserID"); + $Cache->update_row(false, array('PermissionID' => $L['To'])); + $Cache->commit_transaction(0);*/ + $Cache->delete_value("user_info_$UserID"); + $Cache->delete_value("user_info_heavy_$UserID"); + $Cache->delete_value("user_stats_$UserID"); + $Cache->delete_value("enabled_$UserID"); + $DB->query(" + UPDATE users_info + SET AdminComment = CONCAT('".sqltime()." - Class changed to ".Users::make_class_string($L['To'])." by System\n\n', AdminComment) + WHERE UserID = $UserID"); + Misc::send_pm($UserID, 0, 'You have been promoted to '.Users::make_class_string($L['To']), 'Congratulations on your promotion to '.Users::make_class_string($L['To'])."!\n\nTo read more about ".SITE_NAME."'s user classes, read [url=".site_url()."wiki.php?action=article&name=userclasses]this wiki article[/url]."); + } + } - // Demote users with less than the required uploads + // Demote users with less than the required uploads - $Query = " - SELECT ID - FROM users_main - JOIN users_info ON users_main.ID = users_info.UserID - WHERE PermissionID = '$L[To]' - AND ( Uploaded < '$L[MinUpload]' - OR ( - SELECT COUNT(ID) - FROM torrents - WHERE UserID = users_main.ID - ) < '$L[MinUploads]'"; - if (!empty($L['Extra'])) { - $Query .= ' OR NOT '.$L['Extra']; - } - $Query .= " - ) - AND Enabled = '1'"; + $Query = " + SELECT ID + FROM users_main um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + INNER JOIN users_info ui ON (ui.UserID = um.ID) + WHERE um.PermissionID = '$L[To]' + AND ( uls.Uploaded < '$L[MinUpload]' + OR ( + SELECT count(*) + FROM torrents + WHERE UserID = um.ID + ) < '$L[MinUploads]'"; + if (!empty($L['Extra'])) { + $Query .= ' OR NOT '.$L['Extra']; + } + $Query .= " + ) + AND um.Enabled = '1'"; - $DB->query($Query); - $UserIDs = $DB->collect('ID'); + $DB->query($Query); + $UserIDs = $DB->collect('ID'); - if (count($UserIDs) > 0) { - $DB->query(" - UPDATE users_main - SET PermissionID = ".$L['From']." - WHERE ID IN(".implode(',', $UserIDs).')'); - foreach ($UserIDs as $UserID) { - /*$Cache->begin_transaction("user_info_$UserID"); - $Cache->update_row(false, array('PermissionID' => $L['From'])); - $Cache->commit_transaction(0);*/ - $Cache->delete_value("user_info_$UserID"); - $Cache->delete_value("user_info_heavy_$UserID"); - $Cache->delete_value("user_stats_$UserID"); - $Cache->delete_value("enabled_$UserID"); - $DB->query(" - UPDATE users_info - SET AdminComment = CONCAT('".sqltime()." - Class changed to ".Users::make_class_string($L['From'])." by System\n\n', AdminComment) - WHERE UserID = $UserID"); - Misc::send_pm($UserID, 0, 'You have been demoted to '.Users::make_class_string($L['From']), "You now only qualify for the \"".Users::make_class_string($L['From'])."\" user class.\n\nTo read more about ".SITE_NAME."'s user classes, read [url=".site_url()."wiki.php?action=article&name=userclasses]this wiki article[/url]."); - } - } -} \ No newline at end of file + if (count($UserIDs) > 0) { + $DB->query(" + UPDATE users_main + SET PermissionID = ".$L['From']." + WHERE ID IN(".implode(',', $UserIDs).')'); + foreach ($UserIDs as $UserID) { + /*$Cache->begin_transaction("user_info_$UserID"); + $Cache->update_row(false, array('PermissionID' => $L['From'])); + $Cache->commit_transaction(0);*/ + $Cache->delete_value("user_info_$UserID"); + $Cache->delete_value("user_info_heavy_$UserID"); + $Cache->delete_value("user_stats_$UserID"); + $Cache->delete_value("enabled_$UserID"); + $DB->query(" + UPDATE users_info + SET AdminComment = CONCAT('".sqltime()." - Class changed to ".Users::make_class_string($L['From'])." by System\n\n', AdminComment) + WHERE UserID = $UserID"); + Misc::send_pm($UserID, 0, 'You have been demoted to '.Users::make_class_string($L['From']), "You now only qualify for the \"".Users::make_class_string($L['From'])."\" user class.\n\nTo read more about ".SITE_NAME."'s user classes, read [url=".site_url()."wiki.php?action=article&name=userclasses]this wiki article[/url]."); + } + } +} diff --git a/sections/schedule/hourly/remove_dead_peers.php b/sections/schedule/hourly/remove_dead_peers.php index bd8ba5e34..acc22dee5 100644 --- a/sections/schedule/hourly/remove_dead_peers.php +++ b/sections/schedule/hourly/remove_dead_peers.php @@ -2,5 +2,5 @@ //------------- Remove dead peers ---------------------------------------// $DB->query(" - DELETE FROM xbt_files_users - WHERE mtime < unix_timestamp(NOW() - INTERVAL 6 HOUR)"); \ No newline at end of file + DELETE FROM xbt_files_users + WHERE mtime < unix_timestamp(NOW() - INTERVAL 6 HOUR)"); diff --git a/sections/schedule/hourly/remove_dead_sessions.php b/sections/schedule/hourly/remove_dead_sessions.php index a55a2246c..7f64d5e1c 100644 --- a/sections/schedule/hourly/remove_dead_sessions.php +++ b/sections/schedule/hourly/remove_dead_sessions.php @@ -7,18 +7,18 @@ $AgoDays = time_minus(3600 * 24 * 30); $SessionQuery = $DB->query(" - SELECT UserID, SessionID - FROM users_sessions - WHERE (LastUpdate < '$AgoDays' AND KeepLogged = '1') - OR (LastUpdate < '$AgoMins' AND KeepLogged = '0')"); + SELECT UserID, SessionID + FROM users_sessions + WHERE (LastUpdate < '$AgoDays' AND KeepLogged = '1') + OR (LastUpdate < '$AgoMins' AND KeepLogged = '0')"); $DB->query(" - DELETE FROM users_sessions - WHERE (LastUpdate < '$AgoDays' AND KeepLogged = '1') - OR (LastUpdate < '$AgoMins' AND KeepLogged = '0')"); + DELETE FROM users_sessions + WHERE (LastUpdate < '$AgoDays' AND KeepLogged = '1') + OR (LastUpdate < '$AgoMins' AND KeepLogged = '0')"); $DB->set_query_id($SessionQuery); while (list($UserID, $SessionID) = $DB->next_record()) { - $Cache->begin_transaction("users_sessions_$UserID"); - $Cache->delete_row($SessionID); - $Cache->commit_transaction(0); -} \ No newline at end of file + $Cache->begin_transaction("users_sessions_$UserID"); + $Cache->delete_row($SessionID); + $Cache->commit_transaction(0); +} diff --git a/sections/schedule/hourly/remove_expired_warnings.php b/sections/schedule/hourly/remove_expired_warnings.php index 743a56266..fae30a8df 100644 --- a/sections/schedule/hourly/remove_expired_warnings.php +++ b/sections/schedule/hourly/remove_expired_warnings.php @@ -2,16 +2,16 @@ //------------- Remove expired warnings ---------------------------------// $DB->query(" - SELECT UserID - FROM users_info - WHERE Warned < '$sqltime'"); + SELECT UserID + FROM users_info + WHERE Warned < '$sqltime'"); while (list($UserID) = $DB->next_record()) { - $Cache->begin_transaction("user_info_$UserID"); - $Cache->update_row(false, array('Warned' => '0000-00-00 00:00:00')); - $Cache->commit_transaction(2592000); + $Cache->begin_transaction("user_info_$UserID"); + $Cache->update_row(false, array('Warned' => '0000-00-00 00:00:00')); + $Cache->commit_transaction(2592000); } $DB->query(" - UPDATE users_info - SET Warned = '0000-00-00 00:00:00' - WHERE Warned < '$sqltime'"); + UPDATE users_info + SET Warned = '0000-00-00 00:00:00' + WHERE Warned < '$sqltime'"); diff --git a/sections/schedule/hourly/update_user_bonus_points.php b/sections/schedule/hourly/update_user_bonus_points.php index b0d187aa6..1df0b6245 100644 --- a/sections/schedule/hourly/update_user_bonus_points.php +++ b/sections/schedule/hourly/update_user_bonus_points.php @@ -10,30 +10,32 @@ $DB->query(" UPDATE users_main AS um LEFT JOIN ( - SELECT - xfu.uid AS ID, - SUM(IFNULL((t.Size / (1024 * 1024 * 1024)) * ( - 0.0433 + ( - (0.07 * LN(1 + (xfh.seedtime / (24)))) / (POW(GREATEST(t.Seeders, 1), 0.35)) - ) - ), 0)) AS NewPoints - FROM - (SELECT DISTINCT uid,fid FROM xbt_files_users WHERE active='1' AND remaining=0 AND mtime > unix_timestamp(NOW() - INTERVAL 1 HOUR)) AS xfu - JOIN xbt_files_history AS xfh ON xfh.uid = xfu.uid AND xfh.fid = xfu.fid - JOIN users_main AS um ON um.ID = xfu.uid - JOIN users_info AS ui ON ui.UserID = xfu.uid - JOIN torrents AS t ON t.ID = xfu.fid - WHERE - um.Enabled = '1' - AND ui.DisablePoints = '0' - GROUP BY - xfu.uid + SELECT + xfu.uid AS ID, + SUM(IFNULL((t.Size / (1024 * 1024 * 1024)) * ( + 0.0433 + ( + (0.07 * LN(1 + (xfh.seedtime / (24)))) / (POW(GREATEST(tls.Seeders, 1), 0.35)) + ) + ), 0)) AS NewPoints + FROM ( + SELECT DISTINCT uid,fid FROM xbt_files_users WHERE active='1' AND remaining=0 AND mtime > unix_timestamp(NOW() - INTERVAL 1 HOUR) + ) AS xfu + INNER JOIN xbt_files_history AS xfh ON (xfh.uid = xfu.uid AND xfh.fid = xfu.fid) + INNER JOIN users_main AS um ON (um.ID = xfu.uid) + INNER JOIN users_info AS ui ON (ui.UserID = xfu.uid) + INNER JOIN torrents AS t ON (t.ID = xfu.fid) + INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID) + WHERE + um.Enabled = '1' + AND ui.DisablePoints = '0' + GROUP BY + xfu.uid ) AS p ON um.ID = p.ID SET um.BonusPoints=um.BonusPoints + CASE WHEN p.NewPoints IS NULL THEN 0 ELSE ROUND(p.NewPoints, 5) END"); $DB->query("SELECT UserID FROM users_info WHERE DisablePoints = '0'"); if ($DB->has_results()) { - while(list($UserID) = $DB->next_record()) { - $Cache->delete_value('user_stats_'.$UserID); - } + while(list($UserID) = $DB->next_record()) { + $Cache->delete_value('user_stats_'.$UserID); + } } diff --git a/sections/schedule/hourly/update_user_torrent_history.php b/sections/schedule/hourly/update_user_torrent_history.php index 57b06cfd7..55ca87805 100644 --- a/sections/schedule/hourly/update_user_torrent_history.php +++ b/sections/schedule/hourly/update_user_torrent_history.php @@ -6,35 +6,35 @@ // Find seeders that have announced within the last hour $DB->query(" - INSERT INTO users_torrent_history_temp - (UserID, NumTorrents) - SELECT uid, COUNT(DISTINCT fid) - FROM xbt_files_users - WHERE mtime > unix_timestamp(NOW() - INTERVAL 1 HOUR) - AND Remaining = 0 - GROUP BY uid"); + INSERT INTO users_torrent_history_temp + (UserID, NumTorrents) + SELECT uid, COUNT(DISTINCT fid) + FROM xbt_files_users + WHERE mtime > unix_timestamp(NOW() - INTERVAL 1 HOUR) + AND Remaining = 0 + GROUP BY uid"); // Mark new records as "checked" and set the current time as the time // the user started seeding <NumTorrents> seeded. // Finished = 1 means that the user hasn't been seeding exactly <NumTorrents> earlier today. // This query will only do something if the next one inserted new rows last hour. $DB->query(" - UPDATE users_torrent_history AS h - JOIN users_torrent_history_temp AS t ON t.UserID = h.UserID - AND t.NumTorrents = h.NumTorrents - SET h.Finished = '0', - h.LastTime = UNIX_TIMESTAMP(NOW()) - WHERE h.Finished = '1' - AND h.Date = UTC_DATE() + 0"); + UPDATE users_torrent_history AS h + JOIN users_torrent_history_temp AS t ON t.UserID = h.UserID + AND t.NumTorrents = h.NumTorrents + SET h.Finished = '0', + h.LastTime = UNIX_TIMESTAMP(NOW()) + WHERE h.Finished = '1' + AND h.Date = UTC_DATE() + 0"); // Insert new rows for users who haven't been seeding exactly <NumTorrents> torrents earlier today // and update the time spent seeding <NumTorrents> torrents for the others. // Primary table index: (UserID, NumTorrents, Date). $DB->query(" - INSERT INTO users_torrent_history - (UserID, NumTorrents, Date) - SELECT UserID, NumTorrents, UTC_DATE() + 0 - FROM users_torrent_history_temp - ON DUPLICATE KEY UPDATE - Time = Time + UNIX_TIMESTAMP(NOW()) - LastTime, - LastTime = UNIX_TIMESTAMP(NOW())"); \ No newline at end of file + INSERT INTO users_torrent_history + (UserID, NumTorrents, Date) + SELECT UserID, NumTorrents, UTC_DATE() + 0 + FROM users_torrent_history_temp + ON DUPLICATE KEY UPDATE + Time = Time + UNIX_TIMESTAMP(NOW()) - LastTime, + LastTime = UNIX_TIMESTAMP(NOW())"); diff --git a/sections/schedule/hourly/user_stats_daily.php b/sections/schedule/hourly/user_stats_daily.php index 08f376a29..c009fc9bb 100644 --- a/sections/schedule/hourly/user_stats_daily.php +++ b/sections/schedule/hourly/user_stats_daily.php @@ -2,19 +2,20 @@ $DB->prepared_query(" INSERT INTO users_stats_daily (UserID, Uploaded, Downloaded, BonusPoints, Torrents, PerfectFLACs) -SELECT um.ID, um.Uploaded, um.Downloaded, um.BonusPoints, COUNT(t.ID) AS Torrents, COALESCE(p.Perfects, 0) AS PerfectFLACs +SELECT um.ID, uls.Uploaded, uls.Downloaded, um.BonusPoints, COUNT(t.ID) AS Torrents, COALESCE(p.Perfects, 0) AS PerfectFLACs FROM users_main um +INNER JOIN users_leech_stats uls ON uls.UserID = um.ID LEFT JOIN torrents t ON t.UserID = um.ID LEFT JOIN ( - SELECT UserID, COUNT(ID) AS Perfects - FROM torrents - WHERE( Format = 'FLAC' - AND ( - Media IN ('Vinyl', 'WEB', 'DVD', 'Soundboard', 'Cassette', 'SACD', 'BD', 'DAT') - OR - (LogScore = 100 AND Media = 'CD'))) - GROUP BY UserID + SELECT UserID, COUNT(ID) AS Perfects + FROM torrents + WHERE( Format = 'FLAC' + AND ( + Media IN ('Vinyl', 'WEB', 'DVD', 'Soundboard', 'Cassette', 'SACD', 'BD', 'DAT') + OR + (LogScore = 100 AND Media = 'CD'))) + GROUP BY UserID ) p ON p.UserID = um.ID GROUP BY um.ID;"); diff --git a/sections/schedule/index.php b/sections/schedule/index.php index db0234e04..29d095c8d 100644 --- a/sections/schedule/index.php +++ b/sections/schedule/index.php @@ -20,8 +20,8 @@ */ $PCount = chop(shell_exec("/usr/bin/pgrep -cf schedule.php")); if ($PCount > 3) { - // 3 because the cron job starts two processes and pgrep finds itself - die("schedule.php is already running. Exiting ($PCount){$LineEnd}"); + // 3 because the cron job starts two processes and pgrep finds itself + die("schedule.php is already running. Exiting ($PCount){$LineEnd}"); } $RunEvery = false; @@ -41,66 +41,66 @@ * @param string $Dir which dir to load all files from to run */ function run_tasks($Dir) { - global $RunTasks, $LineEnd; - $Tasks = array_diff(scandir(SERVER_ROOT.'/sections/schedule/'.$Dir, 1), array('.', '..')); - sort($Tasks); - extract($GLOBALS); - foreach ($Tasks as $Task) { - $TimeStart = microtime(true); - $Task = str_replace('.php', '', $Task); - if (!empty($RunTasks) && !in_array($Task, $RunTasks)) { - continue; - } - print("Running {$Task}..."); - /** @noinspection PhpIncludeInspection */ - require_once SERVER_ROOT."/sections/schedule/{$Dir}/{$Task}.php"; - print("DONE! (".number_format(microtime(true) - $TimeStart, 3).")".$LineEnd); - } + global $RunTasks, $LineEnd; + $Tasks = array_diff(scandir(SERVER_ROOT.'/sections/schedule/'.$Dir, 1), array('.', '..')); + sort($Tasks); + extract($GLOBALS); + foreach ($Tasks as $Task) { + $TimeStart = microtime(true); + $Task = str_replace('.php', '', $Task); + if (!empty($RunTasks) && !in_array($Task, $RunTasks)) { + continue; + } + print("Running {$Task}..."); + /** @noinspection PhpIncludeInspection */ + require_once SERVER_ROOT."/sections/schedule/{$Dir}/{$Task}.php"; + print("DONE! (".number_format(microtime(true) - $TimeStart, 3).")".$LineEnd); + } } if (PHP_SAPI === 'cli') { - if (!isset($argv[1]) || $argv[1] != SCHEDULE_KEY) { - error(403); - } - for ($i = 2; $i < count($argv); $i++) { - if ($argv[$i] === 'run_tasks') { - if ($i < count($argv) - 1) { - $RunTasks = array(); - for (++$i; $i < count($argv); $i++) { - $RunTasks[] = $argv[$i]; - } - foreach (array('RunEvery', 'RunHourly', 'RunDaily', 'RunWeekly', 'RunBiweekly', 'RunManual') as $Var) { - $$Var = true; - } - } - } - elseif (substr($argv[$i], 0, 4) === 'run_') { - $Var = str_replace('_', '', ucwords($argv[$i], '_')); - ${$Var} = true; - } - } + if (!isset($argv[1]) || $argv[1] != SCHEDULE_KEY) { + error(403); + } + for ($i = 2; $i < count($argv); $i++) { + if ($argv[$i] === 'run_tasks') { + if ($i < count($argv) - 1) { + $RunTasks = []; + for (++$i; $i < count($argv); $i++) { + $RunTasks[] = $argv[$i]; + } + foreach (array('RunEvery', 'RunHourly', 'RunDaily', 'RunWeekly', 'RunBiweekly', 'RunManual') as $Var) { + $$Var = true; + } + } + } + elseif (substr($argv[$i], 0, 4) === 'run_') { + $Var = str_replace('_', '', ucwords($argv[$i], '_')); + ${$Var} = true; + } + } } else { - foreach($_GET as $Key => $Value) { - if (substr($Key, 0, 4) === 'run_') { - $Key = str_replace('_', '', ucwords($argv[$i])); - $$Key = true; - } - } - if (!check_perms('admin_schedule')) { - error(403); - } + foreach($_GET as $Key => $Value) { + if (substr($Key, 0, 4) === 'run_') { + $Key = str_replace('_', '', ucwords($argv[$i])); + $$Key = true; + } + } + if (!check_perms('admin_schedule')) { + error(403); + } } if (check_perms('admin_schedule')) { - authorize(); - View::show_header(); - echo '<pre>'; + authorize(); + View::show_header(); + echo '<pre>'; } $DB->query(" - SELECT NextHour, NextDay, NextBiWeekly - FROM schedule"); + SELECT NextHour, NextDay, NextBiWeekly + FROM schedule"); list($Hour, $Day, $BiWeek) = $DB->next_record(); $CurrentHour = date('H'); $CurrentDay = date('d'); @@ -109,18 +109,18 @@ function run_tasks($Dir) { $ChooseRun = false; if (empty($RunTasks) && !$RunManual && !$RunEvery && !$RunHourly && !$RunDaily && !$RunWeekly && !$RunBiweekly) { - // We set this true here just so we run the tasks as we're (trying) to run all sections and - // not just an individual one (or some collection of tasks) - $RunEvery = true; - $DB->query(" - UPDATE schedule - SET - NextHour = $CurrentHour, - NextDay = $CurrentDay, - NextBiWeekly = $CurrentBiWeek"); + // We set this true here just so we run the tasks as we're (trying) to run all sections and + // not just an individual one (or some collection of tasks) + $RunEvery = true; + $DB->query(" + UPDATE schedule + SET + NextHour = $CurrentHour, + NextDay = $CurrentDay, + NextBiWeekly = $CurrentBiWeek"); } else { - $ChooseRun = true; + $ChooseRun = true; } $sqltime = sqltime(); @@ -136,9 +136,9 @@ function run_tasks($Dir) { \*************************************************************************/ if ($RunEvery) { - echo "Running every run tasks...{$LineEnd}"; - run_tasks('every'); - echo "{$LineEnd}"; + echo "Running every run tasks...{$LineEnd}"; + run_tasks('every'); + echo "{$LineEnd}"; } /*************************************************************************\ @@ -149,9 +149,9 @@ function run_tasks($Dir) { \*************************************************************************/ if ((!$ChooseRun && $Hour != $CurrentHour) || $RunHourly) { - echo "Running hourly tasks...{$LineEnd}"; - run_tasks('hourly'); - echo "{$LineEnd}"; + echo "Running hourly tasks...{$LineEnd}"; + run_tasks('hourly'); + echo "{$LineEnd}"; } /*************************************************************************\ @@ -162,9 +162,9 @@ function run_tasks($Dir) { \*************************************************************************/ if ((!$ChooseRun && $Day != $CurrentDay) || $RunDaily) { - echo "Running daily tasks...{$LineEnd}"; - run_tasks('daily'); - echo "{$LineEnd}"; + echo "Running daily tasks...{$LineEnd}"; + run_tasks('daily'); + echo "{$LineEnd}"; } /*************************************************************************\ @@ -175,9 +175,9 @@ function run_tasks($Dir) { \*************************************************************************/ if ((!$ChooseRun && $Day != $CurrentDay && date('w') == 0) || $RunWeekly) { - echo "Running weekly tasks...{$LineEnd}"; - run_tasks('weekly'); - echo "{$LineEnd}"; + echo "Running weekly tasks...{$LineEnd}"; + run_tasks('weekly'); + echo "{$LineEnd}"; } /*************************************************************************\ @@ -188,9 +188,9 @@ function run_tasks($Dir) { \*************************************************************************/ if ((!$ChooseRun && $BiWeek != $CurrentBiWeek) || $RunBiweekly) { - echo "Running bi-weekly tasks...{$LineEnd}"; - run_tasks('biweekly'); - echo "{$LineEnd}"; + echo "Running bi-weekly tasks...{$LineEnd}"; + run_tasks('biweekly'); + echo "{$LineEnd}"; } /*************************************************************************\ @@ -200,14 +200,14 @@ function run_tasks($Dir) { \*************************************************************************/ if ($RunManual) { - echo "Running manual tasks...{$LineEnd}"; - run_tasks('manually'); + echo "Running manual tasks...{$LineEnd}"; + run_tasks('manually'); } // Done echo "-------------------------{$LineEnd}{$LineEnd}"; if (check_perms('admin_schedule')) { - echo '<pre>'; - View::show_footer(); -} \ No newline at end of file + echo '<pre>'; + View::show_footer(); +} diff --git a/sections/schedule/weekly/donations.php b/sections/schedule/weekly/donations.php index 1583d6209..508a00774 100644 --- a/sections/schedule/weekly/donations.php +++ b/sections/schedule/weekly/donations.php @@ -1,3 +1,3 @@ <?php -Donations::schedule(); \ No newline at end of file +Donations::schedule(); diff --git a/sections/schedule/weekly/resolve_staff_pms.php b/sections/schedule/weekly/resolve_staff_pms.php index 82b57b9eb..4b3691330 100644 --- a/sections/schedule/weekly/resolve_staff_pms.php +++ b/sections/schedule/weekly/resolve_staff_pms.php @@ -1,8 +1,8 @@ <?php $DB->query(" - UPDATE staff_pm_conversations - SET Status = 'Resolved', ResolverID = '0' - WHERE Date < NOW() - INTERVAL 1 MONTH - AND Status = 'Open' - AND AssignedToUser IS NULL"); \ No newline at end of file + UPDATE staff_pm_conversations + SET Status = 'Resolved', ResolverID = '0' + WHERE Date < NOW() - INTERVAL 1 MONTH + AND Status = 'Open' + AND AssignedToUser IS NULL"); diff --git a/sections/schedule/weekly/update_weekly_top10.php b/sections/schedule/weekly/update_weekly_top10.php index f5806cc5c..e043cf5f1 100644 --- a/sections/schedule/weekly/update_weekly_top10.php +++ b/sections/schedule/weekly/update_weekly_top10.php @@ -1,112 +1,113 @@ <?php $DB->query(" - INSERT INTO top10_history (Date, Type) - VALUES ('$sqltime', 'Weekly')"); + INSERT INTO top10_history (Date, Type) + VALUES ('$sqltime', 'Weekly')"); $HistoryID = $DB->inserted_id(); $Top10 = $Cache->get_value('top10tor_week_10'); if ($Top10 === false) { - $DB->query(" - SELECT - t.ID, - g.ID, - g.Name, - g.CategoryID, - g.TagList, - t.Format, - t.Encoding, - t.Media, - t.Scene, - t.HasLog, - t.HasCue, - t.HasLogDB, - t.LogScore, - t.LogChecksum, - t.RemasterYear, - g.Year, - t.RemasterTitle, - t.Snatched, - t.Seeders, - t.Leechers, - ((t.Size * t.Snatched) + (t.Size * 0.5 * t.Leechers)) AS Data - FROM torrents AS t - LEFT JOIN torrents_group AS g ON g.ID = t.GroupID - WHERE t.Seeders > 0 - AND t.Time > ('$sqltime' - INTERVAL 1 WEEK) - ORDER BY (t.Seeders + t.Leechers) DESC - LIMIT 10;"); + $DB->query(" + SELECT + t.ID, + g.ID, + g.Name, + g.CategoryID, + g.TagList, + t.Format, + t.Encoding, + t.Media, + t.Scene, + t.HasLog, + t.HasCue, + t.HasLogDB, + t.LogScore, + t.LogChecksum, + t.RemasterYear, + g.Year, + t.RemasterTitle, + tls.Snatched, + tls.Seeders, + tls.Leechers, + ((t.Size * tls.Snatched) + (t.Size * 0.5 * tls.Leechers)) AS Data + FROM torrents AS t + INNER JOIN torrents_leech_stats tls ON (tls.TorrentID = t.ID) + INNER JOIN torrents_group AS g ON (g.ID = t.GroupID) + WHERE tls.Seeders > 0 + AND t.Time > (now() - INTERVAL 1 WEEK) + ORDER BY (tls.Seeders + tls.Leechers) DESC + LIMIT 10;"); - $Top10 = $DB->to_array(); + $Top10 = $DB->to_array(); } $i = 1; foreach ($Top10 as $Torrent) { - list($TorrentID, $GroupID, $GroupName, $GroupCategoryID, $TorrentTags, - $Format, $Encoding, $Media, $Scene, $HasLog, $HasCue, $HasLogDB, $LogScore, $LogChecksum, - $Year, $GroupYear, $RemasterTitle, $Snatched, $Seeders, $Leechers, $Data) = $Torrent; + list($TorrentID, $GroupID, $GroupName, $GroupCategoryID, $TorrentTags, + $Format, $Encoding, $Media, $Scene, $HasLog, $HasCue, $HasLogDB, $LogScore, $LogChecksum, + $Year, $GroupYear, $RemasterTitle, $Snatched, $Seeders, $Leechers, $Data) = $Torrent; - $DisplayName = ''; + $DisplayName = ''; - $Artists = Artists::get_artist($GroupID); + $Artists = Artists::get_artist($GroupID); - if (!empty($Artists)) { - $DisplayName = Artists::display_artists($Artists, false, true); - } + if (!empty($Artists)) { + $DisplayName = Artists::display_artists($Artists, false, true); + } - $DisplayName .= $GroupName; + $DisplayName .= $GroupName; - if ($GroupCategoryID == 1 && $GroupYear > 0) { - $DisplayName .= " [$GroupYear]"; - } + if ($GroupCategoryID == 1 && $GroupYear > 0) { + $DisplayName .= " [$GroupYear]"; + } - // append extra info to torrent title - $ExtraInfo = ''; - $AddExtra = ''; - if ($Format) { - $ExtraInfo .= $Format; - $AddExtra = ' / '; - } - if ($Encoding) { - $ExtraInfo .= $AddExtra.$Encoding; - $AddExtra = ' / '; - } - // "FLAC / Lossless / Log (100%) / Cue / CD"; - if ($HasLog) { - $ExtraInfo .= "{$AddExtra}Log".($HasLogDB ? " ($LogScore%)" : ""); - $AddExtra = ' / '; - } - if ($HasCue) { - $ExtraInfo .= "{$AddExtra}Cue"; - $AddExtra = ' / '; - } - if ($Media) { - $ExtraInfo .= $AddExtra.$Media; - $AddExtra = ' / '; - } - if ($Scene) { - $ExtraInfo .= "{$AddExtra}Scene"; - $AddExtra = ' / '; - } - if ($Year > 0) { - $ExtraInfo .= $AddExtra.$Year; - $AddExtra = ' '; - } - if ($RemasterTitle) { - $ExtraInfo .= $AddExtra.$RemasterTitle; - } - if ($ExtraInfo != '') { - $ExtraInfo = "- [$ExtraInfo]"; - } + // append extra info to torrent title + $ExtraInfo = ''; + $AddExtra = ''; + if ($Format) { + $ExtraInfo .= $Format; + $AddExtra = ' / '; + } + if ($Encoding) { + $ExtraInfo .= $AddExtra.$Encoding; + $AddExtra = ' / '; + } + // "FLAC / Lossless / Log (100%) / Cue / CD"; + if ($HasLog) { + $ExtraInfo .= "{$AddExtra}Log".($HasLogDB ? " ($LogScore%)" : ""); + $AddExtra = ' / '; + } + if ($HasCue) { + $ExtraInfo .= "{$AddExtra}Cue"; + $AddExtra = ' / '; + } + if ($Media) { + $ExtraInfo .= $AddExtra.$Media; + $AddExtra = ' / '; + } + if ($Scene) { + $ExtraInfo .= "{$AddExtra}Scene"; + $AddExtra = ' / '; + } + if ($Year > 0) { + $ExtraInfo .= $AddExtra.$Year; + $AddExtra = ' '; + } + if ($RemasterTitle) { + $ExtraInfo .= $AddExtra.$RemasterTitle; + } + if ($ExtraInfo != '') { + $ExtraInfo = "- [$ExtraInfo]"; + } - $TitleString = "$DisplayName $ExtraInfo"; + $TitleString = "$DisplayName $ExtraInfo"; - $TagString = str_replace('|', ' ', $TorrentTags); + $TagString = str_replace('|', ' ', $TorrentTags); - $DB->query(" - INSERT INTO top10_history_torrents - (HistoryID, Rank, TorrentID, TitleString, TagString) - VALUES - ($HistoryID, $i, $TorrentID, '" . db_string($TitleString) . "', '" . db_string($TagString) . "')"); - $i++; -} //foreach ($Top10 as $Torrent) \ No newline at end of file + $DB->query(" + INSERT INTO top10_history_torrents + (HistoryID, Rank, TorrentID, TitleString, TagString) + VALUES + ($HistoryID, $i, $TorrentID, '" . db_string($TitleString) . "', '" . db_string($TagString) . "')"); + $i++; +} //foreach ($Top10 as $Torrent) diff --git a/sections/schedule/weekly/user_stats_yearly.php b/sections/schedule/weekly/user_stats_yearly.php index a8a1d6513..5683ec740 100644 --- a/sections/schedule/weekly/user_stats_yearly.php +++ b/sections/schedule/weekly/user_stats_yearly.php @@ -2,19 +2,20 @@ $DB->prepared_query(" INSERT INTO users_stats_yearly (UserID, Uploaded, Downloaded, BonusPoints, Torrents, PerfectFLACs) -SELECT um.ID, um.Uploaded, um.Downloaded, um.BonusPoints, COUNT(t.ID) AS Torrents, COALESCE(p.Perfects, 0) AS PerfectFLACs +SELECT um.ID, uls.Uploaded, uls.Downloaded, um.BonusPoints, COUNT(t.ID) AS Torrents, COALESCE(p.Perfects, 0) AS PerfectFLACs FROM users_main um +INNER JOIN users_leech_stats uls ON uls.UserID = um.ID LEFT JOIN torrents t ON t.UserID = um.ID LEFT JOIN ( - SELECT UserID, COUNT(ID) AS Perfects - FROM torrents - WHERE( Format = 'FLAC' - AND ( - Media IN ('Vinyl', 'WEB', 'DVD', 'Soundboard', 'Cassette', 'SACD', 'BD', 'DAT') - OR - (LogScore = 100 AND Media = 'CD'))) - GROUP BY UserID + SELECT UserID, COUNT(ID) AS Perfects + FROM torrents + WHERE( Format = 'FLAC' + AND ( + Media IN ('Vinyl', 'WEB', 'DVD', 'Soundboard', 'Cassette', 'SACD', 'BD', 'DAT') + OR + (LogScore = 100 AND Media = 'CD'))) + GROUP BY UserID ) p ON p.UserID = um.ID GROUP BY um.ID;"); diff --git a/sections/schedule/weekly/warn_uploaders.php b/sections/schedule/weekly/warn_uploaders.php index 02e693f42..4754b322c 100644 --- a/sections/schedule/weekly/warn_uploaders.php +++ b/sections/schedule/weekly/warn_uploaders.php @@ -1,43 +1,46 @@ <?php +// disabled from weekly + // Send warnings to uploaders of torrents that will be deleted this week $DB->query(" - SELECT - t.ID, - t.GroupID, - tg.Name, - t.Format, - t.Encoding, - t.UserID - FROM torrents AS t - JOIN torrents_group AS tg ON tg.ID = t.GroupID - JOIN users_info AS u ON u.UserID = t.UserID - WHERE t.last_action < NOW() - INTERVAL 20 DAY - AND t.last_action != 0 - AND u.UnseededAlerts = '1' - ORDER BY t.last_action ASC"); + SELECT + t.ID, + t.GroupID, + tg.Name, + t.Format, + t.Encoding, + t.UserID + FROM torrents AS t + INNER JOIN torrents_leech_stats AS tls ON tls.TorrentID = t.ID + JOIN torrents_group AS tg ON tg.ID = t.GroupID + JOIN users_info AS u ON u.UserID = t.UserID + WHERE tls.last_action < NOW() - INTERVAL 20 DAY + AND tls.last_action != 0 + AND u.UnseededAlerts = '1' + ORDER BY tls.last_action ASC"); $TorrentIDs = $DB->to_array(); -$TorrentAlerts = array(); +$TorrentAlerts = []; foreach ($TorrentIDs as $TorrentID) { - list($ID, $GroupID, $Name, $Format, $Encoding, $UserID) = $TorrentID; + list($ID, $GroupID, $Name, $Format, $Encoding, $UserID) = $TorrentID; - if (array_key_exists($UserID, $InactivityExceptionsMade) && (time() < $InactivityExceptionsMade[$UserID])) { - // don't notify exceptions - continue; - } + if (array_key_exists($UserID, $InactivityExceptionsMade) && (time() < $InactivityExceptionsMade[$UserID])) { + // don't notify exceptions + continue; + } - if (!array_key_exists($UserID, $TorrentAlerts)) - $TorrentAlerts[$UserID] = array('Count' => 0, 'Msg' => ''); - $ArtistName = Artists::display_artists(Artists::get_artist($GroupID), false, false, false); - if ($ArtistName) { - $Name = "$ArtistName - $Name"; - } - if ($Format && $Encoding) { - $Name .= " [$Format / $Encoding]"; - } - $TorrentAlerts[$UserID]['Msg'] .= "\n[url=".site_url()."torrents.php?torrentid=$ID]".$Name."[/url]"; - $TorrentAlerts[$UserID]['Count']++; + if (!array_key_exists($UserID, $TorrentAlerts)) + $TorrentAlerts[$UserID] = array('Count' => 0, 'Msg' => ''); + $ArtistName = Artists::display_artists(Artists::get_artist($GroupID), false, false, false); + if ($ArtistName) { + $Name = "$ArtistName - $Name"; + } + if ($Format && $Encoding) { + $Name .= " [$Format / $Encoding]"; + } + $TorrentAlerts[$UserID]['Msg'] .= "\n[url=".site_url()."torrents.php?torrentid=$ID]".$Name."[/url]"; + $TorrentAlerts[$UserID]['Count']++; } foreach ($TorrentAlerts as $UserID => $MessageInfo) { - Misc::send_pm($UserID, 0, 'Unseeded torrent notification', $MessageInfo['Count']." of your uploads will be deleted for inactivity soon. Unseeded torrents are deleted after 4 weeks. If you still have the files, you can seed your uploads by ensuring the torrents are in your client and that they aren't stopped. You can view the time that a torrent has been unseeded by clicking on the torrent description line and looking for the \"Last active\" time. For more information, please go [url=".site_url()."wiki.php?action=article&id=77]here[/url].\n\nThe following torrent".($MessageInfo['Count'] > 1 ? 's' : '').' will be removed for inactivity:'.$MessageInfo['Msg']."\n\nIf you no longer wish to receive these notifications, please disable them in your profile settings."); -} \ No newline at end of file + Misc::send_pm($UserID, 0, 'Unseeded torrent notification', $MessageInfo['Count']." of your uploads will be deleted for inactivity soon. Unseeded torrents are deleted after 4 weeks. If you still have the files, you can seed your uploads by ensuring the torrents are in your client and that they aren't stopped. You can view the time that a torrent has been unseeded by clicking on the torrent description line and looking for the \"Last active\" time. For more information, please go [url=".site_url()."wiki.php?action=article&id=77]here[/url].\n\nThe following torrent".($MessageInfo['Count'] > 1 ? 's' : '').' will be removed for inactivity:'.$MessageInfo['Msg']."\n\nIf you no longer wish to receive these notifications, please disable them in your profile settings."); +} diff --git a/sections/sitehistory/edit.php b/sections/sitehistory/edit.php index 6b338f721..4b336d5eb 100644 --- a/sections/sitehistory/edit.php +++ b/sections/sitehistory/edit.php @@ -1,30 +1,33 @@ -<? +<?php if (!check_perms('users_mod') ) { - error(403); + error(403); } if (is_number($_GET['id'])) { - $ID = $_GET['id']; - $Event = SiteHistory::get_event($ID); + $ID = $_GET['id']; + $Event = SiteHistory::get_event($ID); } if ($ID) { - $Title = "Edit"; + $Title = "Edit"; } else { - $Title = "Create"; + $Title = "Create"; } View::show_header($Title, "jquery.validate,form_validate,site_history"); ?> <div class="header"> -<? if ($ID) { ?> - <h2>Edit event</h2> -<? } else { ?> - <h2>Create new event</h2> -<? } ?> +<?php +if ($ID) { ?> + <h2>Edit event</h2> +<?php +} else { ?> + <h2>Create new event</h2> +<?php +} ?> </div> -<? +<?php SiteHistoryView::render_edit_form($Event); -View::show_footer(); \ No newline at end of file +View::show_footer(); diff --git a/sections/sitehistory/history.php b/sections/sitehistory/history.php index 4b017e62f..4ccd4230a 100644 --- a/sections/sitehistory/history.php +++ b/sections/sitehistory/history.php @@ -1,52 +1,52 @@ -<? +<?php define('DEFAULT_LIMIT', 10); $Limit = DEFAULT_LIMIT; if (is_number($_GET['month'])) { - $Month = $_GET['month']; - $Limit = null; + $Month = $_GET['month']; + $Limit = null; } if (is_number($_GET['year'])) { - $Year = $_GET['year']; - $Limit = null; + $Year = $_GET['year']; + $Limit = null; } if (!empty($_GET['title'])) { - $Title = $_GET['title']; - $Limit = null; + $Title = $_GET['title']; + $Limit = null; } if (!empty($_GET['category'])) { - $Category = $_GET['category']; - $Limit = null; + $Category = $_GET['category']; + $Limit = null; } if (!empty($_GET['subcategory'])) { - $SubCategory = $_GET['subcategory']; - $Limit = null; + $SubCategory = $_GET['subcategory']; + $Limit = null; } if (!empty($_GET['tags'])) { - $Tags = $_GET['tags']; - $Limit = null; + $Tags = $_GET['tags']; + $Limit = null; } $Events = SiteHistory::get_events($Month, $Year, $Title, $Category, $SubCategory, $Tags, $Limit); $Months = SiteHistory::get_months(); View::show_header("Site History"); ?> <div class="header"> - <h2><a href="sitehistory.php">Site History</a> <?=$Month && $Year ? date("- F, Y", mktime(0, 0, 0, $Month, 1, $Year)) : '' ?></h2> -<? - SiteHistoryView::render_linkbox(); + <h2><a href="sitehistory.php">Site History</a> <?=$Month && $Year ? date("- F, Y", mktime(0, 0, 0, $Month, 1, $Year)) : '' ?></h2> +<?php + SiteHistoryView::render_linkbox(); ?> </div> <div class="sidebar"> -<? - SiteHistoryView::render_search(); - SiteHistoryView::render_months($Months); +<?php + SiteHistoryView::render_search(); + SiteHistoryView::render_months($Months); ?> </div> <div class="main_column"> -<? - SiteHistoryView::render_events($Events); +<?php + SiteHistoryView::render_events($Events); ?> </div> -<? +<?php View::show_footer(); diff --git a/sections/sitehistory/index.php b/sections/sitehistory/index.php index a4ba6fef6..dc296b381 100644 --- a/sections/sitehistory/index.php +++ b/sections/sitehistory/index.php @@ -1,33 +1,33 @@ -<? +<?php enforce_login(); if (!check_perms('users_mod')) { - error(403); + error(403); } if (!empty($_POST['action'])) { - switch ($_POST['action']) { - case 'take_create': - include(SERVER_ROOT . '/sections/sitehistory/take_create.php'); - break; - case 'take_edit': - include(SERVER_ROOT . '/sections/sitehistory/take_edit.php'); - break; - default: - error(404); - break; - } + switch ($_POST['action']) { + case 'take_create': + include(SERVER_ROOT . '/sections/sitehistory/take_create.php'); + break; + case 'take_edit': + include(SERVER_ROOT . '/sections/sitehistory/take_edit.php'); + break; + default: + error(404); + break; + } } elseif (!empty($_GET['action'])) { - switch ($_GET['action']) { - case 'search': - include(SERVER_ROOT . '/sections/sitehistory/history.php'); - break; - case 'edit': - include(SERVER_ROOT . '/sections/sitehistory/edit.php'); - break; - default: - error(404); - break; - } + switch ($_GET['action']) { + case 'search': + include(SERVER_ROOT . '/sections/sitehistory/history.php'); + break; + case 'edit': + include(SERVER_ROOT . '/sections/sitehistory/edit.php'); + break; + default: + error(404); + break; + } } else { - include(SERVER_ROOT . '/sections/sitehistory/history.php'); + include(SERVER_ROOT . '/sections/sitehistory/history.php'); } diff --git a/sections/sitehistory/take_create.php b/sections/sitehistory/take_create.php index 717d62715..47f4cff43 100644 --- a/sections/sitehistory/take_create.php +++ b/sections/sitehistory/take_create.php @@ -1,9 +1,9 @@ -<? +<?php authorize(); if (!check_perms('users_mod') ) { - error(403); + error(403); } SiteHistory::add_event($_POST['date'], $_POST['title'], $_POST['url'], $_POST['category'], $_POST['sub_category'], $_POST['tags'], $_POST['body'], $LoggedUser['ID']); -header("Location: sitehistory.php"); \ No newline at end of file +header("Location: sitehistory.php"); diff --git a/sections/sitehistory/take_edit.php b/sections/sitehistory/take_edit.php index 39e576d46..1d60be47e 100644 --- a/sections/sitehistory/take_edit.php +++ b/sections/sitehistory/take_edit.php @@ -1,12 +1,12 @@ -<? +<?php authorize(); if (!check_perms('users_mod') ) { - error(403); + error(403); } if ($_POST['submit']) { - SiteHistory::update_event($_POST['id'], $_POST['date'], $_POST['title'], $_POST['url'], $_POST['category'], $_POST['sub_category'], $_POST['tags'], $_POST['body'], $LoggedUser['ID']); + SiteHistory::update_event($_POST['id'], $_POST['date'], $_POST['title'], $_POST['url'], $_POST['category'], $_POST['sub_category'], $_POST['tags'], $_POST['body'], $LoggedUser['ID']); } elseif ($_POST['delete']) { - SiteHistory::delete_event($_POST['id']); + SiteHistory::delete_event($_POST['id']); } header("Location: sitehistory.php"); diff --git a/sections/staff/functions.php b/sections/staff/functions.php index 2a3f71ba8..c7d33f83e 100644 --- a/sections/staff/functions.php +++ b/sections/staff/functions.php @@ -1,4 +1,4 @@ -<? +<?php /** * Generate a table row for a staff member on staff.php * @@ -9,114 +9,114 @@ * @param $LastAccess datetime the user last browsed the site * @param String $Remark the "Staff remark" or FLS' "Support for" text * @param String $HiddenBy the text that is displayed when a staff member's - * paranoia hides their LastAccess time + * paranoia hides their LastAccess time * @return string $Row */ function make_staff_row($Row, $ID, $Paranoia, $Class, $LastAccess, $Remark = '', $HiddenBy = 'Hidden by user') { - $Row = $Row === 'a' ? 'b' : 'a'; + $Row = $Row === 'a' ? 'b' : 'a'; - echo "\t\t\t<tr class=\"row$Row\"> - <td class=\"nobr\"> - " . Users::format_username($ID, false, false, false) . " - </td> - <td class=\"nobr\"> - "; //used for proper indentation of HTML - if (check_paranoia('lastseen', $Paranoia, $Class)) { - echo time_diff($LastAccess); - } else { - echo "$HiddenBy"; - } - echo "\n\t\t\t\t</td> - <td class=\"nobr\">" - . Text::full_format($Remark) . - "</td> - </tr>\n"; // the "\n" is needed for pretty HTML - // the foreach loop that calls this function needs to know the new value of $Row - return $Row; + echo "\t\t\t<tr class=\"row$Row\"> + <td class=\"nobr\"> + " . Users::format_username($ID, false, false, false) . " + </td> + <td class=\"nobr\"> + "; //used for proper indentation of HTML + if (check_paranoia('lastseen', $Paranoia, $Class)) { + echo time_diff($LastAccess); + } else { + echo "$HiddenBy"; + } + echo "\n\t\t\t\t</td> + <td class=\"nobr\">" + . Text::full_format($Remark) . + "</td> + </tr>\n"; // the "\n" is needed for pretty HTML + // the foreach loop that calls this function needs to know the new value of $Row + return $Row; } function get_fls() { - global $Cache, $DB; - static $FLS; - if (is_array($FLS)) { - return $FLS; - } - if (($FLS = $Cache->get_value('fls')) === false) { - $DB->prepared_query(' - SELECT - m.ID, - p.Level, - m.Username, - m.Paranoia, - m.LastAccess, - i.SupportFor - FROM users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - JOIN permissions AS p ON p.ID = m.PermissionID - JOIN users_levels AS l ON l.UserID = i.UserID - WHERE l.PermissionID = ? - ORDER BY m.Username', FLS_TEAM); - $FLS = $DB->to_array(false, MYSQLI_BOTH, array(3, 'Paranoia')); - $Cache->cache_value('fls', $FLS, 180); - } - return $FLS; + global $Cache, $DB; + static $FLS; + if (is_array($FLS)) { + return $FLS; + } + if (($FLS = $Cache->get_value('fls')) === false) { + $DB->prepared_query(' + SELECT + m.ID, + p.Level, + m.Username, + m.Paranoia, + m.LastAccess, + i.SupportFor + FROM users_info AS i + JOIN users_main AS m ON m.ID = i.UserID + JOIN permissions AS p ON p.ID = m.PermissionID + JOIN users_levels AS l ON l.UserID = i.UserID + WHERE l.PermissionID = ? + ORDER BY m.Username', FLS_TEAM); + $FLS = $DB->to_array(false, MYSQLI_BOTH, array(3, 'Paranoia')); + $Cache->cache_value('fls', $FLS, 180); + } + return $FLS; } function get_staff() { - global $Cache, $DB; - static $Staff; - if (is_array($Staff)) { - return $Staff; - } + global $Cache, $DB; + static $Staff; + if (is_array($Staff)) { + return $Staff; + } - if (($Staff = $Cache->get_value('staff')) === false) { - $DB->prepared_query(" - SELECT - m.ID, - p.ID as LevelID, - p.Level, - p.Name, - IFNULL(sg.Name, '') AS StaffGroup, - m.Username, - m.Paranoia, - m.LastAccess, - i.SupportFor - FROM users_main AS m - JOIN users_info AS i ON m.ID = i.UserID - JOIN permissions AS p ON p.ID = m.PermissionID - INNER JOIN staff_groups AS sg ON sg.ID = p.StaffGroup - WHERE p.DisplayStaff = '1' AND Secondary = 0 - ORDER BY p.Level, m.Username"); - $TmpStaff = $DB->to_array(false, MYSQLI_BOTH, array(6, 'Paranoia')); - $DB->prepared_query(" - SELECT Name - FROM staff_groups - ORDER BY Sort"); - $Groups = $DB->collect('Name'); - array_unshift($Groups, 'Staff'); - $Staff = []; - foreach ($Groups as $g) { - $Staff[$g] = []; - } - foreach ($TmpStaff as $Class) { - $Staff[$Class['StaffGroup']][] = $Class; - } - $Cache->cache_value('staff', $Staff, 180); - } - return $Staff; + if (($Staff = $Cache->get_value('staff')) === false) { + $DB->prepared_query(" + SELECT + m.ID, + p.ID as LevelID, + p.Level, + p.Name, + IFNULL(sg.Name, '') AS StaffGroup, + m.Username, + m.Paranoia, + m.LastAccess, + i.SupportFor + FROM users_main AS m + JOIN users_info AS i ON m.ID = i.UserID + JOIN permissions AS p ON p.ID = m.PermissionID + INNER JOIN staff_groups AS sg ON sg.ID = p.StaffGroup + WHERE p.DisplayStaff = '1' AND Secondary = 0 + ORDER BY p.Level, m.Username"); + $TmpStaff = $DB->to_array(false, MYSQLI_BOTH, array(6, 'Paranoia')); + $DB->prepared_query(" + SELECT Name + FROM staff_groups + ORDER BY Sort"); + $Groups = $DB->collect('Name'); + array_unshift($Groups, 'Staff'); + $Staff = []; + foreach ($Groups as $g) { + $Staff[$g] = []; + } + foreach ($TmpStaff as $Class) { + $Staff[$Class['StaffGroup']][] = $Class; + } + $Cache->cache_value('staff', $Staff, 180); + } + return $Staff; } function get_support() { - return array( - get_fls(), - get_staff() - ); + return array( + get_fls(), + get_staff() + ); } function printSectionDiv($ClassName) { ?> - </div><br /> - <div class='box pad' style='padding: 10px 10px 10px 10px;'> - <h2 style='text-align: left;'><?=$ClassName?></h2> -<? + </div><br /> + <div class='box pad' style='padding: 10px 10px 10px 10px;'> + <h2 style='text-align: left;'><?=$ClassName?></h2> +<?php } diff --git a/sections/staff/index.php b/sections/staff/index.php index 74639f33d..46c02e005 100644 --- a/sections/staff/index.php +++ b/sections/staff/index.php @@ -1,4 +1,4 @@ -<? +<?php enforce_login(); View::show_header('Staff'); @@ -9,97 +9,99 @@ list($FrontLineSupport, $Staff) = $SupportStaff; ?> -<? if (check_perms('admin_manage_applicants')) { ?> +<?php +if (check_perms('admin_manage_applicants')) { ?> <div class="linkbox"> - <a href="apply.php">Role applications</a> + <a href="apply.php">Role applications</a> </div> -<? } ?> +<?php +} ?> <div class="thin"> - <div class="header"> - <h2><?=SITE_NAME?> Staff</h2> - </div> - <div class="box pad" style="padding: 0px 10px 10px 10px;"> - <br /> - <h3>Contact Staff</h3> - <div id="below_box"> - <p>If you are looking for help with a general question, we appreciate it if you would only message through the staff inbox, where we can all help you.</p> - <p>You can do that by <strong><a href="#" onclick="$('#compose').gtoggle(); return false;">sending a message to the Staff Inbox</a></strong>.</p> - <p>If you'd like to join our staff, please feel free to <strong><a href="apply.php">apply</a></strong>!</p> - <p>If you are looking to ask staff a question which isn't fit for a Staff PM or support area, <a href='questions.php'>you can use our questions feature</a>.</p> - </div> - </div> - <div class="box pad" style="padding: 0px 10px 10px 10px;"> - <? View::parse('generic/reply/staffpm.php', array('Hidden' => true)); ?> - <br /> - <h2 style="text-align: left;">Community Help</h2> - <h3 style="font-size: 17px;" id="fls"><i>First-Line Support</i></h3> - <p><strong>These users are not official staff members.</strong> They are users who have volunteered their time to help people in need. Please treat them with respect, and read <a href="wiki.php?action=article&id=52">this</a> before contacting them.</p><br /> - <table class="staff" width="100%"> - <tr class="colhead"> - <td style="width: 130px;">Username</td> - <td style="width: 130px;">Last seen</td> - <td><strong>Support for</strong></td> - </tr> -<? - $Row = 'a'; - foreach ($FrontLineSupport as $Support) { - list($ID, $Class, $Username, $Paranoia, $LastAccess, $SupportFor) = $Support; + <div class="header"> + <h2><?=SITE_NAME?> Staff</h2> + </div> + <div class="box pad" style="padding: 0px 10px 10px 10px;"> + <br /> + <h3>Contact Staff</h3> + <div id="below_box"> + <p>If you are looking for help with a general question, we appreciate it if you would only message through the staff inbox, where we can all help you.</p> + <p>You can do that by <strong><a href="#" onclick="$('#compose').gtoggle(); return false;">sending a message to the Staff Inbox</a></strong>.</p> + <p>If you'd like to join our staff, please feel free to <strong><a href="apply.php">apply</a></strong>!</p> + <p>If you are looking to ask staff a question which isn't fit for a Staff PM or support area, <a href='questions.php'>you can use our questions feature</a>.</p> + </div> + </div> + <div class="box pad" style="padding: 0px 10px 10px 10px;"> + <?php View::parse('generic/reply/staffpm.php', array('Hidden' => true)); ?> + <br /> + <h2 style="text-align: left;">Community Help</h2> + <h3 style="font-size: 17px;" id="fls"><i>First-Line Support</i></h3> + <p><strong>These users are not official staff members.</strong> They are users who have volunteered their time to help people in need. Please treat them with respect, and read <a href="wiki.php?action=article&id=52">this</a> before contacting them.</p><br /> + <table class="staff" width="100%"> + <tr class="colhead"> + <td style="width: 130px;">Username</td> + <td style="width: 130px;">Last seen</td> + <td><strong>Support for</strong></td> + </tr> +<?php + $Row = 'a'; + foreach ($FrontLineSupport as $Support) { + list($ID, $Class, $Username, $Paranoia, $LastAccess, $SupportFor) = $Support; - $Row = make_staff_row($Row, $ID, $Paranoia, $Class, $LastAccess, $SupportFor); + $Row = make_staff_row($Row, $ID, $Paranoia, $Class, $LastAccess, $SupportFor); - } ?> - </table> - </div> - <br /> + } ?> + </table> + </div> + <br /> <?php - foreach ($Staff as $SectionName => $StaffSection) { - if (count($StaffSection) === 0) { - continue; - } + foreach ($Staff as $SectionName => $StaffSection) { + if (count($StaffSection) === 0) { + continue; + } ?> - <div class="box pad" style="padding: 0px 10px 10px 10px;"> - <h2 style='text-align: left;'><?=$SectionName?></h2> -<? - $CurClass = 0; - $CloseTable = false; - foreach ($StaffSection as $StaffMember) { - list($ID, $ClassID, $Class, $ClassName, $StaffGroup, $Username, $Paranoia, $LastAccess, $Remark) = $StaffMember; - if ($Class != $CurClass) { // Start new class of staff members - $Row = 'a'; - if ($CloseTable) { - $CloseTable = false; - // the "\t" and "\n" are used here to make the HTML look pretty - echo "\t\t</table>\n\t\t<br />\n"; - } - $CurClass = $Class; - $CloseTable = true; + <div class="box pad" style="padding: 0px 10px 10px 10px;"> + <h2 style='text-align: left;'><?=$SectionName?></h2> +<?php + $CurClass = 0; + $CloseTable = false; + foreach ($StaffSection as $StaffMember) { + list($ID, $ClassID, $Class, $ClassName, $StaffGroup, $Username, $Paranoia, $LastAccess, $Remark) = $StaffMember; + if ($Class != $CurClass) { // Start new class of staff members + $Row = 'a'; + if ($CloseTable) { + $CloseTable = false; + // the "\t" and "\n" are used here to make the HTML look pretty + echo "\t\t</table>\n\t\t<br />\n"; + } + $CurClass = $Class; + $CloseTable = true; - $HTMLID = str_replace(' ', '_', strtolower($ClassName)); - echo "\t\t<h3 style=\"font-size: 17px;\" id=\"$HTMLID\"><i>".$ClassName."s</i></h3>\n"; + $HTMLID = str_replace(' ', '_', strtolower($ClassName)); + echo "\t\t<h3 style=\"font-size: 17px;\" id=\"$HTMLID\"><i>".$ClassName."s</i></h3>\n"; ?> - <table class="staff" width="100%"> - <tr class="colhead"> - <td style="width: 130px;">Username</td> - <td style="width: 130px;">Last seen</td> - <td><strong>Remark</strong></td> - </tr> -<? - } // End new class header + <table class="staff" width="100%"> + <tr class="colhead"> + <td style="width: 130px;">Username</td> + <td style="width: 130px;">Last seen</td> + <td><strong>Remark</strong></td> + </tr> +<?php + } // End new class header - $HiddenBy = 'Hidden by staff member'; + $HiddenBy = 'Hidden by staff member'; - // Display staff members for this class - $Row = make_staff_row($Row, $ID, $Paranoia, $Class, $LastAccess, $Remark, $HiddenBy); + // Display staff members for this class + $Row = make_staff_row($Row, $ID, $Paranoia, $Class, $LastAccess, $Remark, $HiddenBy); - } ?> - </table> + } ?> + </table> - </div> - <br /> - <?php } ?> + </div> + <br /> + <?php } ?> </div> -<? +<?php View::show_footer(); ?> diff --git a/sections/staffblog/index.php b/sections/staffblog/index.php index eaa1815c5..1674d0fde 100644 --- a/sections/staffblog/index.php +++ b/sections/staffblog/index.php @@ -1,155 +1,155 @@ -<? +<?php enforce_login(); if (!check_perms('users_mod')) { - error(403); + error(403); } $DB->query(" - INSERT INTO staff_blog_visits - (UserID, Time) - VALUES - (".$LoggedUser['ID'].", NOW()) - ON DUPLICATE KEY UPDATE - Time = NOW()"); + INSERT INTO staff_blog_visits + (UserID, Time) + VALUES + (".$LoggedUser['ID'].", NOW()) + ON DUPLICATE KEY UPDATE + Time = NOW()"); $Cache->delete_value('staff_blog_read_'.$LoggedUser['ID']); define('ANNOUNCEMENT_FORUM_ID', 5); if (check_perms('admin_manage_blog')) { - if (!empty($_REQUEST['action'])) { - switch ($_REQUEST['action']) { - case 'takeeditblog': - authorize(); - if (empty($_POST['title'])) { - error("Please enter a title."); - } - if (is_number($_POST['blogid'])) { - $DB->query(" - UPDATE staff_blog - SET Title = '".db_string($_POST['title'])."', Body = '".db_string($_POST['body'])."' - WHERE ID = '".db_string($_POST['blogid'])."'"); - $Cache->delete_value('staff_blog'); - $Cache->delete_value('staff_feed_blog'); - } - header('Location: staffblog.php'); - break; - case 'editblog': - if (is_number($_GET['id'])) { - $BlogID = $_GET['id']; - $DB->query(" - SELECT Title, Body - FROM staff_blog - WHERE ID = $BlogID"); - list($Title, $Body, $ThreadID) = $DB->next_record(); - } - break; - case 'deleteblog': - if (is_number($_GET['id'])) { - authorize(); - $DB->query(" - DELETE FROM staff_blog - WHERE ID = '".db_string($_GET['id'])."'"); - $Cache->delete_value('staff_blog'); - $Cache->delete_value('staff_feed_blog'); - } - header('Location: staffblog.php'); - break; + if (!empty($_REQUEST['action'])) { + switch ($_REQUEST['action']) { + case 'takeeditblog': + authorize(); + if (empty($_POST['title'])) { + error("Please enter a title."); + } + if (is_number($_POST['blogid'])) { + $DB->query(" + UPDATE staff_blog + SET Title = '".db_string($_POST['title'])."', Body = '".db_string($_POST['body'])."' + WHERE ID = '".db_string($_POST['blogid'])."'"); + $Cache->delete_value('staff_blog'); + $Cache->delete_value('staff_feed_blog'); + } + header('Location: staffblog.php'); + break; + case 'editblog': + if (is_number($_GET['id'])) { + $BlogID = $_GET['id']; + $DB->query(" + SELECT Title, Body + FROM staff_blog + WHERE ID = $BlogID"); + list($Title, $Body, $ThreadID) = $DB->next_record(); + } + break; + case 'deleteblog': + if (is_number($_GET['id'])) { + authorize(); + $DB->query(" + DELETE FROM staff_blog + WHERE ID = '".db_string($_GET['id'])."'"); + $Cache->delete_value('staff_blog'); + $Cache->delete_value('staff_feed_blog'); + } + header('Location: staffblog.php'); + break; - case 'takenewblog': - authorize(); - if (empty($_POST['title'])) { - error("Please enter a title."); - } - $Title = db_string($_POST['title']); - $Body = db_string($_POST['body']); + case 'takenewblog': + authorize(); + if (empty($_POST['title'])) { + error("Please enter a title."); + } + $Title = db_string($_POST['title']); + $Body = db_string($_POST['body']); - $DB->query(" - INSERT INTO staff_blog - (UserID, Title, Body, Time) - VALUES - ('$LoggedUser[ID]', '".db_string($_POST['title'])."', '".db_string($_POST['body'])."', NOW())"); - $Cache->delete_value('staff_blog'); - $Cache->delete_value('staff_blog_latest_time'); + $DB->query(" + INSERT INTO staff_blog + (UserID, Title, Body, Time) + VALUES + ('$LoggedUser[ID]', '".db_string($_POST['title'])."', '".db_string($_POST['body'])."', NOW())"); + $Cache->delete_value('staff_blog'); + $Cache->delete_value('staff_blog_latest_time'); - send_irc("PRIVMSG ".ADMIN_CHAN." :!mod New staff blog: " . $_POST['title'] . " - https://".SSL_SITE_URL."/staffblog.php#blog" . $DB->inserted_id()); + send_irc("PRIVMSG ".MOD_CHAN." :!mod New staff blog: " . $_POST['title'] . " - https://".SSL_SITE_URL."/staffblog.php#blog" . $DB->inserted_id()); - header('Location: staffblog.php'); - break; - } - } - View::show_header('Staff Blog','bbcode'); - ?> - <div class="box box2 thin"> - <div class="head"> - <?=((empty($_GET['action'])) ? 'Create a staff blog post' : 'Edit staff blog post')?> - <span style="float: right;"> - <a href="#" onclick="$('#postform').gtoggle(); this.innerHTML = (this.innerHTML == 'Hide' ? 'Show' : 'Hide'); return false;" class="brackets"><?=((!isset($_REQUEST['action']) || $_REQUEST['action'] != 'editblog') ? 'Show' : 'Hide')?></a> - </span> - </div> - <form class="<?=((empty($_GET['action'])) ? 'create_form' : 'edit_form')?>" name="blog_post" action="staffblog.php" method="post"> - <div id="postform" class="pad<?=(!isset($_REQUEST['action']) || $_REQUEST['action'] != 'editblog') ? ' hidden' : '' ?>"> - <input type="hidden" name="action" value="<?=((empty($_GET['action'])) ? 'takenewblog' : 'takeeditblog')?>" /> - <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> -<? if (!empty($_GET['action']) && $_GET['action'] == 'editblog') { ?> - <input type="hidden" name="blogid" value="<?=$BlogID; ?>" /> -<? } ?> - <div class="field_div"> - <h3>Title</h3> - <input type="text" name="title" size="95"<? if (!empty($Title)) { echo ' value="'.display_str($Title).'"'; } ?> /> - </div> - <div class="field_div"> - <h3>Body</h3> - <textarea name="body" cols="95" rows="15"><? if (!empty($Body)) { echo display_str($Body); } ?></textarea> <br /> - </div> - <div class="submit_div center"> - <input type="submit" value="<?=((!isset($_GET['action'])) ? 'Create blog post' : 'Edit blog post') ?>" /> - </div> - </div> - </form> - </div> -<? + header('Location: staffblog.php'); + break; + } + } + View::show_header('Staff Blog','bbcode'); + ?> + <div class="box box2 thin"> + <div class="head"> + <?=((empty($_GET['action'])) ? 'Create a staff blog post' : 'Edit staff blog post')?> + <span style="float: right;"> + <a href="#" onclick="$('#postform').gtoggle(); this.innerHTML = (this.innerHTML == 'Hide' ? 'Show' : 'Hide'); return false;" class="brackets"><?=((!isset($_REQUEST['action']) || $_REQUEST['action'] != 'editblog') ? 'Show' : 'Hide')?></a> + </span> + </div> + <form class="<?=((empty($_GET['action'])) ? 'create_form' : 'edit_form')?>" name="blog_post" action="staffblog.php" method="post"> + <div id="postform" class="pad<?=(!isset($_REQUEST['action']) || $_REQUEST['action'] != 'editblog') ? ' hidden' : '' ?>"> + <input type="hidden" name="action" value="<?=((empty($_GET['action'])) ? 'takenewblog' : 'takeeditblog')?>" /> + <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> +<?php if (!empty($_GET['action']) && $_GET['action'] == 'editblog') { ?> + <input type="hidden" name="blogid" value="<?=$BlogID; ?>" /> +<?php } ?> + <div class="field_div"> + <h3>Title</h3> + <input type="text" name="title" size="95"<?php if (!empty($Title)) { echo ' value="'.display_str($Title).'"'; } ?> /> + </div> + <div class="field_div"> + <h3>Body</h3> + <textarea name="body" cols="95" rows="15"><?php if (!empty($Body)) { echo display_str($Body); } ?></textarea> <br /> + </div> + <div class="submit_div center"> + <input type="submit" value="<?=((!isset($_GET['action'])) ? 'Create blog post' : 'Edit blog post') ?>" /> + </div> + </div> + </form> + </div> +<?php } else { - View::show_header('Staff Blog','bbcode'); + View::show_header('Staff Blog','bbcode'); } ?> <div class="thin"> -<? +<?php if (($Blog = $Cache->get_value('staff_blog')) === false) { - $DB->query(" - SELECT - b.ID, - um.Username, - b.Title, - b.Body, - b.Time - FROM staff_blog AS b - LEFT JOIN users_main AS um ON b.UserID = um.ID - ORDER BY Time DESC"); - $Blog = $DB->to_array(false, MYSQLI_NUM); - $Cache->cache_value('staff_blog', $Blog, 1209600); + $DB->query(" + SELECT + b.ID, + um.Username, + b.Title, + b.Body, + b.Time + FROM staff_blog AS b + LEFT JOIN users_main AS um ON b.UserID = um.ID + ORDER BY Time DESC"); + $Blog = $DB->to_array(false, MYSQLI_NUM); + $Cache->cache_value('staff_blog', $Blog, 1209600); } foreach ($Blog as $BlogItem) { - list($BlogID, $Author, $Title, $Body, $BlogTime) = $BlogItem; - $BlogTime = strtotime($BlogTime); + list($BlogID, $Author, $Title, $Body, $BlogTime) = $BlogItem; + $BlogTime = strtotime($BlogTime); ?> - <div id="blog<?=$BlogID?>" class="box box2 blog_post"> - <div class="head"> - <strong><?=$Title?></strong> - posted <?=time_diff($BlogTime);?> by <?=$Author?> -<? if (check_perms('admin_manage_blog')) { ?> - - <a href="staffblog.php?action=editblog&id=<?=$BlogID?>" class="brackets">Edit</a> - <a href="staffblog.php?action=deleteblog&id=<?=$BlogID?>&auth=<?=$LoggedUser['AuthKey']?>" onclick="return confirm('Do you want to delete this?');" class="brackets">Delete</a> -<? } ?> - </div> - <div class="pad"> - <?=Text::full_format($Body)?> - </div> - </div> -<? + <div id="blog<?=$BlogID?>" class="box box2 blog_post"> + <div class="head"> + <strong><?=$Title?></strong> - posted <?=time_diff($BlogTime);?> by <?=$Author?> +<?php if (check_perms('admin_manage_blog')) { ?> + - <a href="staffblog.php?action=editblog&id=<?=$BlogID?>" class="brackets">Edit</a> + <a href="staffblog.php?action=deleteblog&id=<?=$BlogID?>&auth=<?=$LoggedUser['AuthKey']?>" onclick="return confirm('Do you want to delete this?');" class="brackets">Delete</a> +<?php } ?> + </div> + <div class="pad"> + <?=Text::full_format($Body)?> + </div> + </div> +<?php } ?> </div> -<? +<?php View::show_footer(); ?> diff --git a/sections/staffpm/ajax_delete_response.php b/sections/staffpm/ajax_delete_response.php index de824663a..3bf737441 100644 --- a/sections/staffpm/ajax_delete_response.php +++ b/sections/staffpm/ajax_delete_response.php @@ -1,31 +1,31 @@ -<? +<?php enforce_login(); // Get user level $DB->query(" - SELECT - i.SupportFor, - p.DisplayStaff - FROM users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - JOIN permissions AS p ON p.ID = m.PermissionID - WHERE i.UserID = ".$LoggedUser['ID'] + SELECT + i.SupportFor, + p.DisplayStaff + FROM users_info AS i + JOIN users_main AS m ON m.ID = i.UserID + JOIN permissions AS p ON p.ID = m.PermissionID + WHERE i.UserID = ".$LoggedUser['ID'] ); list($SupportFor, $DisplayStaff) = $DB->next_record(); if (!($SupportFor != '' || $DisplayStaff == '1')) { - // Logged in user is not FLS or Staff - error(403); + // Logged in user is not FLS or Staff + error(403); } if ($ID = (int)$_POST['id']) { - $DB->query(" - DELETE FROM staff_pm_responses - WHERE ID = $ID"); - echo '1'; + $DB->query(" + DELETE FROM staff_pm_responses + WHERE ID = $ID"); + echo '1'; } else { - // No ID - echo '-1'; + // No ID + echo '-1'; } ?> diff --git a/sections/staffpm/ajax_edit_response.php b/sections/staffpm/ajax_edit_response.php index 8e19c282a..a6c9e7ea8 100644 --- a/sections/staffpm/ajax_edit_response.php +++ b/sections/staffpm/ajax_edit_response.php @@ -1,58 +1,58 @@ -<? +<?php enforce_login(); // Get user level $DB->query(' - SELECT - i.SupportFor, - p.DisplayStaff - FROM users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - JOIN permissions AS p ON p.ID = m.PermissionID - WHERE i.UserID = '.$LoggedUser['ID'] + SELECT + i.SupportFor, + p.DisplayStaff + FROM users_info AS i + JOIN users_main AS m ON m.ID = i.UserID + JOIN permissions AS p ON p.ID = m.PermissionID + WHERE i.UserID = '.$LoggedUser['ID'] ); list($SupportFor, $DisplayStaff) = $DB->next_record(); if (!($SupportFor != '' || $DisplayStaff == '1')) { - // Logged in user is not FLS or Staff - error(403); + // Logged in user is not FLS or Staff + error(403); } if (($Message = db_string($_POST['message'])) && ($Name = db_string($_POST['name']))) { - $ID = (int)$_POST['id']; - if (is_numeric($ID)) { - if ($ID == 0) { - // Create new response - $DB->query(" - INSERT INTO staff_pm_responses (Message, Name) - VALUES ('$Message', '$Name')"); - echo '1'; - } else { - $DB->query(" - SELECT * - FROM staff_pm_responses - WHERE ID = $ID"); - if ($DB->has_results()) { - // Edit response - $DB->query(" - UPDATE staff_pm_responses - SET Message = '$Message', Name = '$Name' - WHERE ID = $ID"); - echo '2'; - } else { - // Create new response - $DB->query(" - INSERT INTO staff_pm_responses (Message, Name) - VALUES ('$Message', '$Name')"); - echo '1'; - } - } - } else { - // No ID - echo '-2'; - } + $ID = (int)$_POST['id']; + if (is_numeric($ID)) { + if ($ID == 0) { + // Create new response + $DB->query(" + INSERT INTO staff_pm_responses (Message, Name) + VALUES ('$Message', '$Name')"); + echo '1'; + } else { + $DB->query(" + SELECT * + FROM staff_pm_responses + WHERE ID = $ID"); + if ($DB->has_results()) { + // Edit response + $DB->query(" + UPDATE staff_pm_responses + SET Message = '$Message', Name = '$Name' + WHERE ID = $ID"); + echo '2'; + } else { + // Create new response + $DB->query(" + INSERT INTO staff_pm_responses (Message, Name) + VALUES ('$Message', '$Name')"); + echo '1'; + } + } + } else { + // No ID + echo '-2'; + } } else { - // No message/name - echo '-1'; + // No message/name + echo '-1'; } ?> diff --git a/sections/staffpm/ajax_get_response.php b/sections/staffpm/ajax_get_response.php index 386cfda64..818ec7a50 100644 --- a/sections/staffpm/ajax_get_response.php +++ b/sections/staffpm/ajax_get_response.php @@ -1,37 +1,37 @@ -<? +<?php enforce_login(); // Get user level $DB->query(" - SELECT - i.SupportFor, - p.DisplayStaff - FROM users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - JOIN permissions AS p ON p.ID = m.PermissionID - WHERE i.UserID = ".$LoggedUser['ID'] + SELECT + i.SupportFor, + p.DisplayStaff + FROM users_info AS i + JOIN users_main AS m ON m.ID = i.UserID + JOIN permissions AS p ON p.ID = m.PermissionID + WHERE i.UserID = ".$LoggedUser['ID'] ); list($SupportFor, $DisplayStaff) = $DB->next_record(); if (!$IsFLS) { - // Logged in user is not FLS or Staff - error(403); + // Logged in user is not FLS or Staff + error(403); } if ($ID = (int)$_GET['id']) { - $DB->query(" - SELECT Message - FROM staff_pm_responses - WHERE ID = $ID"); - list($Message) = $DB->next_record(); - if ($_GET['plain'] == 1) { - echo $Message; - } else { - echo Text::full_format($Message); - } + $DB->query(" + SELECT Message + FROM staff_pm_responses + WHERE ID = $ID"); + list($Message) = $DB->next_record(); + if ($_GET['plain'] == 1) { + echo $Message; + } else { + echo Text::full_format($Message); + } } else { - // No ID - echo '-1'; + // No ID + echo '-1'; } ?> diff --git a/sections/staffpm/ajax_preview_response.php b/sections/staffpm/ajax_preview_response.php index 224a72f93..613787b6a 100644 --- a/sections/staffpm/ajax_preview_response.php +++ b/sections/staffpm/ajax_preview_response.php @@ -1,6 +1,6 @@ -<? +<?php /* AJAX Previews, simple stuff. */ if (!empty($_POST['message'])) { - echo Text::full_format($_POST['message']); + echo Text::full_format($_POST['message']); } diff --git a/sections/staffpm/assign.php b/sections/staffpm/assign.php index d70db7813..48d2b0e88 100644 --- a/sections/staffpm/assign.php +++ b/sections/staffpm/assign.php @@ -1,97 +1,97 @@ -<? +<?php if (!($IsFLS)) { - // Logged in user is not FLS or Staff - error(403); + // Logged in user is not FLS or Staff + error(403); } if ($ConvID = (int)$_GET['convid']) { - // FLS, check level of conversation - $DB->prepared_query(" - SELECT Level - FROM staff_pm_conversations - WHERE ID = ?", $ConvID); - list($Level) = $DB->next_record(); + // FLS, check level of conversation + $DB->prepared_query(" + SELECT Level + FROM staff_pm_conversations + WHERE ID = ?", $ConvID); + list($Level) = $DB->next_record(); - if ($Level == 0) { - // FLS conversation, assign to staff (moderator) - if (!empty($_GET['to'])) { - $Level = 0; - switch ($_GET['to']) { - case 'forum': - $Level = 650; - break; - case 'staff': - $Level = 700; - break; - default: - error(404); - break; - } + if ($Level == 0) { + // FLS conversation, assign to staff (moderator) + if (!empty($_GET['to'])) { + $Level = 0; + switch ($_GET['to']) { + case 'forum': + $Level = $Classes[FORUM_MOD]['Level']; + break; + case 'staff': + $Level = $Classes[MOD]['Level']; + break; + default: + error(404); + break; + } - $DB->prepared_query(" - UPDATE staff_pm_conversations - SET Status = 'Unanswered', - Level = ? - WHERE ID = ?", $Level, $ConvID); - $Cache->delete_value("num_staff_pms_$LoggedUser[ID]"); - header('Location: staffpm.php'); - } else { - error(404); - } - } else { - // FLS trying to assign non-FLS conversation - error(403); - } + $DB->prepared_query(" + UPDATE staff_pm_conversations + SET Status = 'Unanswered', + Level = ? + WHERE ID = ?", $Level, $ConvID); + $Cache->delete_value("num_staff_pms_$LoggedUser[ID]"); + header('Location: staffpm.php'); + } else { + error(404); + } + } else { + // FLS trying to assign non-FLS conversation + error(403); + } } elseif ($ConvID = (int)$_POST['convid']) { - // Staff (via AJAX), get current assign of conversation - $DB->prepared_query(" - SELECT Level, AssignedToUser - FROM staff_pm_conversations - WHERE ID = ?", $ConvID); - list($Level, $AssignedToUser) = $DB->next_record(); - - $LevelCap = 1000; - + // Staff (via AJAX), get current assign of conversation + $DB->prepared_query(" + SELECT Level, AssignedToUser + FROM staff_pm_conversations + WHERE ID = ?", $ConvID); + list($Level, $AssignedToUser) = $DB->next_record(); + + $LevelCap = 1000; + - if ($LoggedUser['EffectiveClass'] >= min($Level, $LevelCap) || $AssignedToUser == $LoggedUser['ID']) { - // Staff member is allowed to assign conversation, assign - list($LevelType, $NewLevel) = explode('_', db_string($_POST['assign'])); + if ($LoggedUser['EffectiveClass'] >= min($Level, $LevelCap) || $AssignedToUser == $LoggedUser['ID']) { + // Staff member is allowed to assign conversation, assign + list($LevelType, $NewLevel) = explode('_', $_POST['assign']); - if ($LevelType == 'class') { - // Assign to class - $DB->prepared_query(" - UPDATE staff_pm_conversations - SET Status = 'Unanswered', - Level = ?, - AssignedToUser = NULL - WHERE ID = ?", $NewLevel, $ConvID); - $Cache->delete_value("num_staff_pms_$LoggedUser[ID]"); - } else { - $UserInfo = Users::user_info($NewLevel); - $Level = $Classes[$UserInfo['PermissionID']]['Level']; - if (!$Level) { - error('Assign to user not found.'); - } + if ($LevelType == 'class') { + // Assign to class + $DB->prepared_query(" + UPDATE staff_pm_conversations + SET Status = 'Unanswered', + Level = ?, + AssignedToUser = NULL + WHERE ID = ?", $NewLevel, $ConvID); + $Cache->delete_value("num_staff_pms_$LoggedUser[ID]"); + } else { + $UserInfo = Users::user_info($NewLevel); + $Level = $Classes[$UserInfo['PermissionID']]['Level']; + if (!$Level) { + error('Assign to user not found.'); + } - // Assign to user - $DB->prepared_query(" - UPDATE staff_pm_conversations - SET Status = 'Unanswered', - AssignedToUser = ?, - Level = ? - WHERE ID = ?", $NewLevel, $Level, $ConvID); - $Cache->delete_value("num_staff_pms_$LoggedUser[ID]"); - } - echo '1'; + // Assign to user + $DB->prepared_query(" + UPDATE staff_pm_conversations + SET Status = 'Unanswered', + AssignedToUser = ?, + Level = ? + WHERE ID = ?", $NewLevel, $Level, $ConvID); + $Cache->delete_value("num_staff_pms_$LoggedUser[ID]"); + } + echo '1'; - } else { - // Staff member is not allowed to assign conversation - echo '-1'; - } + } else { + // Staff member is not allowed to assign conversation + echo '-1'; + } } else { - // No ID - header('Location: staffpm.php'); + // No ID + header('Location: staffpm.php'); } ?> diff --git a/sections/staffpm/common_responses.php b/sections/staffpm/common_responses.php index 9fcae09f7..6764a86ad 100644 --- a/sections/staffpm/common_responses.php +++ b/sections/staffpm/common_responses.php @@ -1,95 +1,99 @@ -<? +<?php if (!($IsFLS)) { - // Logged in user is not FLS or Staff - error(403); + // Logged in user is not FLS or Staff + error(403); } View::show_header('Staff PMs', 'staffpm'); ?> <div class="thin"> - <div class="header"> - <h2>Staff PMs - Manage common responses</h2> - <div class="linkbox"> -<? if ($IsStaff) { ?> - <a href="staffpm.php" class="brackets">View your unanswered</a> -<? } ?> - <a href="staffpm.php?view=unanswered" class="brackets">View all unanswered</a> - <a href="staffpm.php?view=open" class="brackets">View unresolved</a> - <a href="staffpm.php?view=resolved" class="brackets">View resolved</a> -<? if ($ConvID = (int)$_GET['convid']) { ?> - <a href="staffpm.php?action=viewconv&id=<?=$ConvID?>" class="brackets">Back to conversation</a> -<? } ?> - </div> - </div> - <br /> - <br /> - <div id="commonresponses" class="center"> - <br /> - <div id="ajax_message_0" class="hidden center alertbar"></div> - <br /> - <div class="center"> - <h3>Create new response:</h3> - </div> - <div id="response_new" class="box"> - <form class="send_form" name="response" id="response_form_0" action=""> - <div class="head"> - <strong>Name:</strong> - <input onfocus="if (this.value == 'New name') { this.value = ''; }" - onblur="if (this.value == '') { this.value = 'New name'; }" - type="text" id="response_name_0" size="87" value="New name" - /> - </div> - <div class="pad"> - <textarea onfocus="if (this.value == 'New message') { this.value = ''; }" - onblur="if (this.value == '') { this.value = 'New message'; }" - rows="10" cols="87" - id="response_message_0">New message</textarea> - <br /> - <input type="button" value="Save" id="save_0" onclick="SaveMessage(0);" /> - </div> - </form> - </div> - <br /> - <br /> - <div class="center"> - <h3>Edit old responses:</h3> - </div> -<? + <div class="header"> + <h2>Staff PMs - Manage common responses</h2> + <div class="linkbox"> +<?php + if ($IsStaff) { ?> + <a href="staffpm.php" class="brackets">View your unanswered</a> +<?php + } ?> + <a href="staffpm.php?view=unanswered" class="brackets">View all unanswered</a> + <a href="staffpm.php?view=open" class="brackets">View unresolved</a> + <a href="staffpm.php?view=resolved" class="brackets">View resolved</a> +<?php + if ($ConvID = (int)$_GET['convid']) { ?> + <a href="staffpm.php?action=viewconv&id=<?=$ConvID?>" class="brackets">Back to conversation</a> +<?php + } ?> + </div> + </div> + <br /> + <br /> + <div id="commonresponses" class="center"> + <br /> + <div id="ajax_message_0" class="hidden center alertbar"></div> + <br /> + <div class="center"> + <h3>Create new response:</h3> + </div> + <div id="response_new" class="box"> + <form class="send_form" name="response" id="response_form_0" action=""> + <div class="head"> + <strong>Name:</strong> + <input onfocus="if (this.value == 'New name') { this.value = ''; }" + onblur="if (this.value == '') { this.value = 'New name'; }" + type="text" id="response_name_0" size="87" value="New name" + /> + </div> + <div class="pad"> + <textarea onfocus="if (this.value == 'New message') { this.value = ''; }" + onblur="if (this.value == '') { this.value = 'New message'; }" + rows="10" cols="87" + id="response_message_0">New message</textarea> + <br /> + <input type="button" value="Save" id="save_0" onclick="SaveMessage(0);" /> + </div> + </form> + </div> + <br /> + <br /> + <div class="center"> + <h3>Edit old responses:</h3> + </div> +<?php // List common responses $DB->query(" - SELECT ID, Message, Name - FROM staff_pm_responses - ORDER BY Name ASC"); + SELECT ID, Message, Name + FROM staff_pm_responses + ORDER BY Name ASC"); while (list($ID, $Message, $Name) = $DB->next_record()) { ?> - <br /> - <div id="ajax_message_<?=$ID?>" class="hidden center alertbar"></div> - <br /> - <div id="response_<?=$ID?>" class="box"> - <form class="send_form" name="response" id="response_form_<?=$ID?>" action=""> - <div class="head"> - <strong>Name:</strong> - <input type="hidden" name="id" value="<?=$ID?>" /> - <input type="text" name="name" id="response_name_<?=$ID?>" size="87" value="<?=display_str($Name)?>" /> - </div> - <div class="pad"> - <div class="box pad hidden" style="text-align: left;" id="response_div_<?=$ID?>"> - <?=Text::full_format($Message)?> - </div> - <textarea rows="10" cols="87" id="response_message_<?=$ID?>" name="message"><?=display_str($Message)?></textarea> - <br /> - <input type="button" value="Toggle preview" onclick="PreviewResponse(<?=$ID?>);" /> - <input type="button" value="Delete" onclick="DeleteMessage(<?=$ID?>);" /> - <input type="button" value="Save" id="save_<?=$ID?>" onclick="SaveMessage(<?=$ID?>);" /> - </div> - </form> - </div> -<? + <br /> + <div id="ajax_message_<?=$ID?>" class="hidden center alertbar"></div> + <br /> + <div id="response_<?=$ID?>" class="box"> + <form class="send_form" name="response" id="response_form_<?=$ID?>" action=""> + <div class="head"> + <strong>Name:</strong> + <input type="hidden" name="id" value="<?=$ID?>" /> + <input type="text" name="name" id="response_name_<?=$ID?>" size="87" value="<?=display_str($Name)?>" /> + </div> + <div class="pad"> + <div class="box pad hidden" style="text-align: left;" id="response_div_<?=$ID?>"> + <?=Text::full_format($Message)?> + </div> + <textarea rows="10" cols="87" id="response_message_<?=$ID?>" name="message"><?=display_str($Message)?></textarea> + <br /> + <input type="button" value="Toggle preview" onclick="PreviewResponse(<?=$ID?>);" /> + <input type="button" value="Delete" onclick="DeleteMessage(<?=$ID?>);" /> + <input type="button" value="Save" id="save_<?=$ID?>" onclick="SaveMessage(<?=$ID?>);" /> + </div> + </form> + </div> +<?php } ?> - </div> + </div> </div> -<? View::show_footer(); ?> +<?php View::show_footer(); ?> diff --git a/sections/staffpm/get_post.php b/sections/staffpm/get_post.php index 44060aa55..8564bb309 100644 --- a/sections/staffpm/get_post.php +++ b/sections/staffpm/get_post.php @@ -1,4 +1,4 @@ -<? +<?php //TODO: make this use the cache version of the thread, save the db query /*********************************************************************\ //--------------Get Post--------------------------------------------// @@ -13,7 +13,7 @@ // Quick SQL injection check if (!$_GET['post'] || !is_number($_GET['post'])) { - error(0); + error(0); } // Variables for database input @@ -22,17 +22,17 @@ // Message is selected providing the user quoting is the guy who opened the PM or has // the right level $DB->query(" - SELECT m.Message, c.Level, c.UserID - FROM staff_pm_messages AS m - JOIN staff_pm_conversations AS c ON m.ConvID = c.ID - WHERE m.ID = '$PostID'"); + SELECT m.Message, c.Level, c.UserID + FROM staff_pm_messages AS m + JOIN staff_pm_conversations AS c ON m.ConvID = c.ID + WHERE m.ID = '$PostID'"); list($Message, $Level, $UserID) = $DB->next_record(MYSQLI_NUM); if (($LoggedUser['ID'] == $UserID) || ($IsFLS && $LoggedUser['Class'] >= $Level)) { - // This gets sent to the browser, which echoes it wherever - echo trim($Message); + // This gets sent to the browser, which echoes it wherever + echo trim($Message); } else { - error(403); + error(403); } ?> diff --git a/sections/staffpm/index.php b/sections/staffpm/index.php index 72f4bf6ea..8f0b94dd0 100644 --- a/sections/staffpm/index.php +++ b/sections/staffpm/index.php @@ -1,19 +1,19 @@ -<? +<?php enforce_login(); if (!isset($_REQUEST['action'])) { - $_REQUEST['action'] = ''; + $_REQUEST['action'] = ''; } // Get user level $DB->query(" - SELECT - i.SupportFor, - p.DisplayStaff - FROM users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - JOIN permissions AS p ON p.ID = m.PermissionID - WHERE i.UserID = ".$LoggedUser['ID'] + SELECT + i.SupportFor, + p.DisplayStaff + FROM users_info AS i + JOIN users_main AS m ON m.ID = i.UserID + JOIN permissions AS p ON p.ID = m.PermissionID + WHERE i.UserID = ".$LoggedUser['ID'] ); list($SupportFor, $DisplayStaff) = $DB->next_record(); // Logged in user is staff @@ -22,58 +22,58 @@ $IsFLS = ($IsStaff || isset($LoggedUser['ExtraClasses'][FLS_TEAM])); switch ($_REQUEST['action']) { - case 'viewconv': - require('viewconv.php'); - break; - case 'takepost': - require('takepost.php'); - break; - case 'resolve': - require('resolve.php'); - break; - case 'unresolve': - require('unresolve.php'); - break; - case 'multiresolve': - require('multiresolve.php'); - break; - case 'assign': - require('assign.php'); - break; - case 'make_donor': - require('makedonor.php'); - break; - case 'responses': - require('common_responses.php'); - break; - case 'get_response': - require('ajax_get_response.php'); - break; - case 'delete_response': - require('ajax_delete_response.php'); - break; - case 'edit_response': - require('ajax_edit_response.php'); - break; - case 'preview': - require('ajax_preview_response.php'); - break; - case 'get_post': - require('get_post.php'); - break; - case 'scoreboard': - require('scoreboard.php'); - break; - case 'userinbox': - require('user_inbox.php'); - break; - default: - if ($IsStaff || $IsFLS) { - require('staff_inbox.php'); - } else { - require('user_inbox.php'); - } - break; + case 'viewconv': + require('viewconv.php'); + break; + case 'takepost': + require('takepost.php'); + break; + case 'resolve': + require('resolve.php'); + break; + case 'unresolve': + require('unresolve.php'); + break; + case 'multiresolve': + require('multiresolve.php'); + break; + case 'assign': + require('assign.php'); + break; + case 'make_donor': + require('makedonor.php'); + break; + case 'responses': + require('common_responses.php'); + break; + case 'get_response': + require('ajax_get_response.php'); + break; + case 'delete_response': + require('ajax_delete_response.php'); + break; + case 'edit_response': + require('ajax_edit_response.php'); + break; + case 'preview': + require('ajax_preview_response.php'); + break; + case 'get_post': + require('get_post.php'); + break; + case 'scoreboard': + require('scoreboard.php'); + break; + case 'userinbox': + require('user_inbox.php'); + break; + default: + if ($IsStaff || $IsFLS) { + require('staff_inbox.php'); + } else { + require('user_inbox.php'); + } + break; } ?> diff --git a/sections/staffpm/makedonor.php b/sections/staffpm/makedonor.php index b1c061ffe..cde36695d 100644 --- a/sections/staffpm/makedonor.php +++ b/sections/staffpm/makedonor.php @@ -1,51 +1,51 @@ -<? +<?php authorize(); if (!check_perms('users_give_donor')) { - error(403); + error(403); } if (!is_number($_POST['id']) || !is_numeric($_POST['donation_amount']) || empty($_POST['donation_currency'])) { - error(404); + error(404); } $ConvID = (int)$_POST['id']; $DB->query(" - SELECT c.Subject, c.UserID, c.Level, c.AssignedToUser, c.Unread, c.Status, u.Donor - FROM staff_pm_conversations AS c - JOIN users_info AS u ON u.UserID = c.UserID - WHERE ID = $ConvID"); + SELECT c.Subject, c.UserID, c.Level, c.AssignedToUser, c.Unread, c.Status, u.Donor + FROM staff_pm_conversations AS c + JOIN users_info AS u ON u.UserID = c.UserID + WHERE ID = $ConvID"); list($Subject, $UserID, $Level, $AssignedToUser, $Unread, $Status, $Donor) = $DB->next_record(); if ($DB->record_count() == 0) { - error(404); + error(404); } $Message = "Thank for for helping to support the site. It's users like you who make all of this possible."; if ((int)$Donor === 0) { - $Message .= ' Enjoy your new love from us!'; + $Message .= ' Enjoy your new love from us!'; } else { - $Message .= ' '; + $Message .= ' '; } /* $DB->query(" - INSERT INTO staff_pm_messages - (UserID, SentDate, Message, ConvID) - VALUES - (".$LoggedUser['ID'].", '".sqltime()."', '".db_string($Message)."', $ConvID)"); + INSERT INTO staff_pm_messages + (UserID, SentDate, Message, ConvID) + VALUES + (".$LoggedUser['ID'].", '".sqltime()."', '".db_string($Message)."', $ConvID)"); */ $DB->query(" - UPDATE staff_pm_conversations - SET Date = '".sqltime()."', - Unread = true, - Status = 'Resolved', - ResolverID = ".$LoggedUser['ID']." - WHERE ID = $ConvID"); + UPDATE staff_pm_conversations + SET Date = '".sqltime()."', + Unread = true, + Status = 'Resolved', + ResolverID = ".$LoggedUser['ID']." + WHERE ID = $ConvID"); Donations::donate($UserID, array( - "Source" => "Staff PM", - "Price" => $_POST['donation_amount'], - "Currency" => $_POST['donation_currency'], - "Reason" => $_POST['donation_reason'], - "SendPM" => true)); + "Source" => "Staff PM", + "Price" => $_POST['donation_amount'], + "Currency" => $_POST['donation_currency'], + "Reason" => $_POST['donation_reason'], + "SendPM" => true)); header('Location: staffpm.php'); diff --git a/sections/staffpm/multiresolve.php b/sections/staffpm/multiresolve.php index f115e4172..d070b708b 100644 --- a/sections/staffpm/multiresolve.php +++ b/sections/staffpm/multiresolve.php @@ -1,40 +1,40 @@ -<? +<?php if ($IDs = $_POST['id']) { - $Queries = array(); - foreach ($IDs as &$ID) { - $ID = (int)$ID; + $Queries = []; + foreach ($IDs as &$ID) { + $ID = (int)$ID; - // Check if conversation belongs to user - $DB->query(" - SELECT UserID, AssignedToUser - FROM staff_pm_conversations - WHERE ID = $ID"); - list($UserID, $AssignedToUser) = $DB->next_record(); + // Check if conversation belongs to user + $DB->query(" + SELECT UserID, AssignedToUser + FROM staff_pm_conversations + WHERE ID = $ID"); + list($UserID, $AssignedToUser) = $DB->next_record(); - if ($UserID == $LoggedUser['ID'] || $DisplayStaff == '1' || $UserID == $AssignedToUser) { - // Conversation belongs to user or user is staff, queue query - $Queries[] = " - UPDATE staff_pm_conversations - SET Status = 'Resolved', ResolverID = ".$LoggedUser['ID']." - WHERE ID = $ID"; - } else { - // Trying to run disallowed query - error(403); - } - } + if ($UserID == $LoggedUser['ID'] || $DisplayStaff == '1' || $UserID == $AssignedToUser) { + // Conversation belongs to user or user is staff, queue query + $Queries[] = " + UPDATE staff_pm_conversations + SET Status = 'Resolved', ResolverID = ".$LoggedUser['ID']." + WHERE ID = $ID"; + } else { + // Trying to run disallowed query + error(403); + } + } - // Run queries - foreach ($Queries as $Query) { - $DB->query($Query); - } - // Clear cache for user - $Cache->delete_value("staff_pm_new_$LoggedUser[ID]"); - $Cache->delete_value("num_staff_pms_$LoggedUser[ID]"); + // Run queries + foreach ($Queries as $Query) { + $DB->query($Query); + } + // Clear cache for user + $Cache->delete_value("staff_pm_new_$LoggedUser[ID]"); + $Cache->delete_value("num_staff_pms_$LoggedUser[ID]"); - // Done! Return to inbox - header("Location: staffpm.php"); + // Done! Return to inbox + header("Location: staffpm.php"); } else { - // No ID - header("Location: staffpm.php"); + // No ID + header("Location: staffpm.php"); } ?> diff --git a/sections/staffpm/resolve.php b/sections/staffpm/resolve.php index 4af66bba1..a1b43d1c7 100644 --- a/sections/staffpm/resolve.php +++ b/sections/staffpm/resolve.php @@ -1,28 +1,28 @@ -<? +<?php if ($ID = (int)($_GET['id'])) { - // Check if conversation belongs to user - $DB->query(" - SELECT UserID, AssignedToUser - FROM staff_pm_conversations - WHERE ID = $ID"); - list($UserID, $AssignedToUser) = $DB->next_record(); + // Check if conversation belongs to user + $DB->query(" + SELECT UserID, AssignedToUser + FROM staff_pm_conversations + WHERE ID = $ID"); + list($UserID, $AssignedToUser) = $DB->next_record(); - if ($UserID == $LoggedUser['ID'] || $IsFLS || $AssignedToUser == $LoggedUser['ID']) { - // Conversation belongs to user or user is staff, resolve it - $DB->query(" - UPDATE staff_pm_conversations - SET Status = 'Resolved', ResolverID = $LoggedUser[ID] - WHERE ID = $ID"); - $Cache->delete_value("staff_pm_new_$LoggedUser[ID]"); - $Cache->delete_value("num_staff_pms_$LoggedUser[ID]"); + if ($UserID == $LoggedUser['ID'] || $IsFLS || $AssignedToUser == $LoggedUser['ID']) { + // Conversation belongs to user or user is staff, resolve it + $DB->query(" + UPDATE staff_pm_conversations + SET Status = 'Resolved', ResolverID = $LoggedUser[ID] + WHERE ID = $ID"); + $Cache->delete_value("staff_pm_new_$LoggedUser[ID]"); + $Cache->delete_value("num_staff_pms_$LoggedUser[ID]"); - header('Location: staffpm.php'); - } else { - // Conversation does not belong to user - error(403); - } + header('Location: staffpm.php'); + } else { + // Conversation does not belong to user + error(403); + } } else { - // No ID - header('Location: staffpm.php'); + // No ID + header('Location: staffpm.php'); } ?> diff --git a/sections/staffpm/scoreboard.php b/sections/staffpm/scoreboard.php index c9b8b699b..3f949c44d 100644 --- a/sections/staffpm/scoreboard.php +++ b/sections/staffpm/scoreboard.php @@ -8,25 +8,29 @@ $View = isset($_REQUEST['view']) ? $_REQUEST['view'] : 'staff'; $Action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 'stats'; ?> - <div class="thin"> - <div class="linkbox"> -<? if ($IsStaff) { ?> - <a href="staffpm.php" class="brackets">View your unanswered</a> -<? } ?> - <a href="staffpm.php?view=unanswered" class="brackets">View all unanswered</a> - <a href="staffpm.php?view=open" class="brackets">View unresolved</a> - <a href="staffpm.php?view=resolved" class="brackets">View resolved</a> - <a href="staffpm.php?action=scoreboard&view=user" class="brackets">View user scoreboard</a> - <a href="staffpm.php?action=scoreboard&view=staff" class="brackets">View staff scoreboard</a> -<? if ($IsFLS && !$IsStaff) { ?> - <span class="tooltip" title="This is the inbox where replies to Staff PMs you have sent are."><a href="staffpm.php?action=userinbox" class="brackets">Personal Staff Inbox</a></span> -<? } ?> - </div> - <div class="head">Statistics</div> - <div class="box pad"> - <table> - <tr> - <td style="width: 50%;"> + <div class="thin"> + <div class="linkbox"> +<?php +if ($IsStaff) { ?> + <a href="staffpm.php" class="brackets">View your unanswered</a> +<?php +} ?> + <a href="staffpm.php?view=unanswered" class="brackets">View all unanswered</a> + <a href="staffpm.php?view=open" class="brackets">View unresolved</a> + <a href="staffpm.php?view=resolved" class="brackets">View resolved</a> + <a href="staffpm.php?action=scoreboard&view=user" class="brackets">View user scoreboard</a> + <a href="staffpm.php?action=scoreboard&view=staff" class="brackets">View staff scoreboard</a> +<?php +if ($IsFLS && !$IsStaff) { ?> + <span class="tooltip" title="This is the inbox where replies to Staff PMs you have sent are."><a href="staffpm.php?action=userinbox" class="brackets">Personal Staff Inbox</a></span> +<?php +} ?> + </div> + <div class="head">Statistics</div> + <div class="box pad"> + <table> + <tr> + <td style="width: 50%;"> <?php $SupportStaff = get_support(); @@ -35,135 +39,143 @@ $SupportStaff = array_column($SupportStaff, 'ID'); if ($View != 'staff') { - $IN = "NOT IN"; - $COL = "PMs"; - $EXTRA = "( SELECT COUNT(spc.ID) - FROM staff_pm_conversations AS spc - WHERE spc.UserID=um.ID - AND spc.Date > ?)"; + $IN = "NOT IN"; + $COL = "PMs"; + $EXTRA = "( SELECT COUNT(spc.ID) + FROM staff_pm_conversations AS spc + WHERE spc.UserID=um.ID + AND spc.Date > ?)"; } else { - $IN = "IN"; - $COL = "Resolved"; - $EXTRA = "( SELECT COUNT(spc.ID) - FROM staff_pm_conversations AS spc - WHERE spc.ResolverID=um.ID - AND spc.Status = 'Resolved' - AND spc.Date > ?)"; + $IN = "IN"; + $COL = "Resolved"; + $EXTRA = "( SELECT COUNT(spc.ID) + FROM staff_pm_conversations AS spc + WHERE spc.ResolverID=um.ID + AND spc.Status = 'Resolved' + AND spc.Date > ?)"; } $BaseSQL = sprintf("SELECT - um.ID, - um.Username, - COUNT(spm.ID) AS Num, - %s AS Extra - FROM staff_pm_messages AS spm - INNER JOIN users_main AS um ON um.ID=spm.UserID - INNER JOIN permissions p ON p.ID = um.PermissionID - WHERE spm.SentDate > ? AND p.Level <= ? AND um.ID %s (%s) - GROUP BY spm.UserID - ORDER BY Num DESC - LIMIT 50", $EXTRA, $IN, - implode(', ', array_fill(0, count($SupportStaff), '?'))); + um.ID, + um.Username, + COUNT(spm.ID) AS Num, + %s AS Extra + FROM staff_pm_messages AS spm + INNER JOIN users_main AS um ON um.ID=spm.UserID + INNER JOIN permissions p ON p.ID = um.PermissionID + WHERE spm.SentDate > ? AND p.Level <= ? AND um.ID %s (%s) + GROUP BY spm.UserID + ORDER BY Num DESC + LIMIT 50", $EXTRA, $IN, + implode(', ', array_fill(0, count($SupportStaff), '?'))); $DB->prepared_query($BaseSQL, \Gazelle\Util\Time::timeOffset(-3600 * 24), \Gazelle\Util\Time::timeOffset(-3600 * 24), $LoggedUser['Class'], ...$SupportStaff); $Results = $DB->to_array(); ?> - <strong>Inbox actions in the last 24 hours</strong> - <table class="noborder"> - <tr class="colhead"> - <td>Username</td> - <td>Replies</td> - <td><?=$COL?></td> - </tr> -<?php foreach ($Results as $Result) { - list($UserID, $Username, $Num, $Extra) = $Result; + <strong>Inbox actions in the last 24 hours</strong> + <table class="noborder"> + <tr class="colhead"> + <td>Username</td> + <td>Replies</td> + <td><?=$COL?></td> + </tr> +<?php +foreach ($Results as $Result) { + list($UserID, $Username, $Num, $Extra) = $Result; ?> - <tr> - <td><a href="/reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> - <td><?=$Num?></td> - <td><?=$Extra?></td> - </tr> -<?php } ?> - </table> - <br /><br /> + <tr> + <td><a href="/reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> + <td><?=$Num?></td> + <td><?=$Extra?></td> + </tr> +<?php +} ?> + </table> + <br /><br /> <?php $DB->prepared_query($BaseSQL, \Gazelle\Util\Time::timeOffset(-3600 * 24 * 7), \Gazelle\Util\Time::timeOffset(-3600 * 24 * 7), $LoggedUser['Class'], ...$SupportStaff); $Results = $DB->to_array(); ?> - <strong>Inbox actions in the last week</strong> - <table class="noborder"> - <tr class="colhead"> - <td>Username</td> - <td>Replies</td> - <td><?=$COL?></td> - </tr> -<?php foreach ($Results as $Result) { - list($UserID, $Username, $Num, $Extra) = $Result; + <strong>Inbox actions in the last week</strong> + <table class="noborder"> + <tr class="colhead"> + <td>Username</td> + <td>Replies</td> + <td><?=$COL?></td> + </tr> +<?php +foreach ($Results as $Result) { + list($UserID, $Username, $Num, $Extra) = $Result; ?> - <tr> - <td><a href="/reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> - <td><?=$Num?></td> - <td><?=$Extra?></td> - </tr> -<?php } ?> - </table> - </td> - <td> + <tr> + <td><a href="/reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> + <td><?=$Num?></td> + <td><?=$Extra?></td> + </tr> +<?php +} ?> + </table> + </td> + <td> <?php $DB->prepared_query($BaseSQL, \Gazelle\Util\Time::timeOffset(-3600 * 24 * 30), \Gazelle\Util\Time::timeOffset(-3600 * 24 * 30), $LoggedUser['Class'], ...$SupportStaff); $Results = $DB->to_array(); ?> - <strong>Inbox actions in the last month</strong> - <table class="noborder"> - <tr class="colhead"> - <td>Username</td> - <td>Replies</td> - <td><?=$COL?></td> - </tr> -<?php foreach ($Results as $Result) { - list($UserID, $Username, $Num, $Extra) = $Result; + <strong>Inbox actions in the last month</strong> + <table class="noborder"> + <tr class="colhead"> + <td>Username</td> + <td>Replies</td> + <td><?=$COL?></td> + </tr> +<?php +foreach ($Results as $Result) { + list($UserID, $Username, $Num, $Extra) = $Result; ?> - <tr> - <td><a href="/reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> - <td><?=$Num?></td> - <td><?=$Extra?></td> - </tr> -<?php } ?> - </table> - <br /><br /> + <tr> + <td><a href="/reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> + <td><?=$Num?></td> + <td><?=$Extra?></td> + </tr> +<?php +} ?> + </table> + <br /><br /> <?php $DB->prepared_query($BaseSQL, '0000-00-00 00:00:00', '0000-00-00 00:00:00', $LoggedUser['Class'], ...$SupportStaff); $Results = $DB->to_array(); ?> - <strong>Inbox actions total</strong> - <table class="noborder"> - <tr class="colhead"> - <td>Username</td> - <td>Replies</td> - <td><?=$COL?></td> - </tr> -<?php foreach ($Results as $Result) { - list($UserID, $Username, $Num, $Extra) = $Result; + <strong>Inbox actions total</strong> + <table class="noborder"> + <tr class="colhead"> + <td>Username</td> + <td>Replies</td> + <td><?=$COL?></td> + </tr> +<?php +foreach ($Results as $Result) { + list($UserID, $Username, $Num, $Extra) = $Result; ?> - <tr> - <td><a href="/reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> - <td><?=$Num?></td> - <td><?=$Extra?></td> - </tr> -<?php } ?> - </table> - </td></tr> - </table> - </div> - <br/> - </div> + <tr> + <td><a href="/reportsv2.php?view=resolver&id=<?=$UserID?>"><?=$Username?></a></td> + <td><?=$Num?></td> + <td><?=$Extra?></td> + </tr> +<?php +} ?> + </table> + </td></tr> + </table> + </div> + <br/> + </div> <?php View::show_footer(); diff --git a/sections/staffpm/staff_inbox.php b/sections/staffpm/staff_inbox.php index 7b668d937..066af9b81 100644 --- a/sections/staffpm/staff_inbox.php +++ b/sections/staffpm/staff_inbox.php @@ -12,68 +12,68 @@ // Setup for current view mode $SortStr = 'IF(AssignedToUser = '.$LoggedUser['ID'].', 0, 1) ASC, '; switch ($View) { - case 'unanswered': - $ViewString = 'Unanswered'; - $Status = "Unanswered"; - break; - case 'open': - $ViewString = 'Unresolved'; - $Status = "Open', 'Unanswered"; - $SortStr = ''; - break; - case 'resolved': - $ViewString = 'Resolved'; - $Status = "Resolved"; - $SortStr = ''; - break; - case 'my': - $ViewString = 'Your Unanswered'; - $Status = "Unanswered"; - break; - default: - $Status = "Unanswered"; - if ($UserLevel >= $Classes[MOD]['Level'] || $UserLevel == $Classes[FORUM_MOD]['Level']) { - $ViewString = 'Your Unanswered'; - } else { - // FLS - $ViewString = 'Unanswered'; - } - break; + case 'unanswered': + $ViewString = 'Unanswered'; + $Status = "Unanswered"; + break; + case 'open': + $ViewString = 'Unresolved'; + $Status = "Open', 'Unanswered"; + $SortStr = ''; + break; + case 'resolved': + $ViewString = 'Resolved'; + $Status = "Resolved"; + $SortStr = ''; + break; + case 'my': + $ViewString = 'Your Unanswered'; + $Status = "Unanswered"; + break; + default: + $Status = "Unanswered"; + if ($UserLevel >= $Classes[MOD]['Level'] || $UserLevel == $Classes[FORUM_MOD]['Level']) { + $ViewString = 'Your Unanswered'; + } else { + // FLS + $ViewString = 'Unanswered'; + } + break; } $WhereCondition = " - WHERE (LEAST($LevelCap, spc.Level) <= $UserLevel OR spc.AssignedToUser = '".$LoggedUser['ID']."') - AND spc.Status IN ('$Status')"; + WHERE (LEAST($LevelCap, spc.Level) <= $UserLevel OR spc.AssignedToUser = '".$LoggedUser['ID']."') + AND spc.Status IN ('$Status')"; if ($ViewString == 'Your Unanswered') { - if ($UserLevel >= $Classes[MOD]['Level']) { - $WhereCondition .= " AND spc.Level >= " . $Classes[MOD]['Level']; - } else if ($UserLevel == $Classes[FORUM_MOD]['Level']) { - $WhereCondition .= " AND spc.Level >= " . $Classes[FORUM_MOD]['Level']; - } + if ($UserLevel >= $Classes[MOD]['Level']) { + $WhereCondition .= " AND spc.Level >= " . $Classes[MOD]['Level']; + } else if ($UserLevel == $Classes[FORUM_MOD]['Level']) { + $WhereCondition .= " AND spc.Level >= " . $Classes[FORUM_MOD]['Level']; + } } list($Page, $Limit) = Format::page_limit(MESSAGES_PER_PAGE); // Get messages $StaffPMs = $DB->query(" - SELECT - SQL_CALC_FOUND_ROWS - spc.ID, - spc.Subject, - spc.UserID, - spc.Status, - spc.Level, - spc.AssignedToUser, - spc.Date, - spc.Unread, - COUNT(spm.ID) AS NumReplies, - spc.ResolverID - FROM staff_pm_conversations AS spc - JOIN staff_pm_messages spm ON spm.ConvID = spc.ID - $WhereCondition - GROUP BY spc.ID - ORDER BY $SortStr spc.Level DESC, spc.Date DESC - LIMIT $Limit + SELECT + SQL_CALC_FOUND_ROWS + spc.ID, + spc.Subject, + spc.UserID, + spc.Status, + spc.Level, + spc.AssignedToUser, + spc.Date, + spc.Unread, + COUNT(spm.ID) AS NumReplies, + spc.ResolverID + FROM staff_pm_conversations AS spc + JOIN staff_pm_messages spm ON spm.ConvID = spc.ID + $WhereCondition + GROUP BY spc.ID + ORDER BY $SortStr spc.Level DESC, spc.Date DESC + LIMIT $Limit "); $DB->query('SELECT FOUND_ROWS()'); @@ -82,9 +82,9 @@ $CurURL = Format::get_url(); if (empty($CurURL)) { - $CurURL = 'staffpm.php?'; + $CurURL = 'staffpm.php?'; } else { - $CurURL = "staffpm.php?$CurURL&"; + $CurURL = "staffpm.php?$CurURL&"; } $Pages = Format::get_pages($Page, $NumResults, MESSAGES_PER_PAGE, 9); @@ -93,136 +93,136 @@ // Start page ?> <div class="thin"> - <div class="header"> - <h2><?=$ViewString?> Staff PMs</h2> - <div class="linkbox"> -<? if ($IsStaff) { ?> - <a href="staffpm.php" class="brackets">View your unanswered</a> -<? } ?> - <a href="staffpm.php?view=unanswered" class="brackets">View all unanswered</a> - <a href="staffpm.php?view=open" class="brackets">View unresolved</a> - <a href="staffpm.php?view=resolved" class="brackets">View resolved</a> -<? if (check_perms('admin_staffpm_stats')) { ?> - <a href="staffpm.php?action=scoreboard&view=user" class="brackets">View user scoreboard</a> - <a href="staffpm.php?action=scoreboard&view=staff" class="brackets">View staff scoreboard</a> -<? } - - if ($IsFLS && !$IsStaff) { ?> - <span class="tooltip" title="This is the inbox where replies to Staff PMs you have sent are."><a href="staffpm.php?action=userinbox" class="brackets">Personal Staff Inbox</a></span> -<? } ?> - </div> - </div> - <br /> - <br /> - <div class="linkbox"> - <?=$Pages?> - </div> - <div class="box pad" id="inbox"> -<? + <div class="header"> + <h2><?=$ViewString?> Staff PMs</h2> + <div class="linkbox"> +<?php if ($IsStaff) { ?> + <a href="staffpm.php" class="brackets">View your unanswered</a> +<?php } ?> + <a href="staffpm.php?view=unanswered" class="brackets">View all unanswered</a> + <a href="staffpm.php?view=open" class="brackets">View unresolved</a> + <a href="staffpm.php?view=resolved" class="brackets">View resolved</a> +<?php if (check_perms('admin_staffpm_stats')) { ?> + <a href="staffpm.php?action=scoreboard&view=user" class="brackets">View user scoreboard</a> + <a href="staffpm.php?action=scoreboard&view=staff" class="brackets">View staff scoreboard</a> +<?php } + + if ($IsFLS && !$IsStaff) { ?> + <span class="tooltip" title="This is the inbox where replies to Staff PMs you have sent are."><a href="staffpm.php?action=userinbox" class="brackets">Personal Staff Inbox</a></span> +<?php } ?> + </div> + </div> + <br /> + <br /> + <div class="linkbox"> + <?=$Pages?> + </div> + <div class="box pad" id="inbox"> +<?php if (!$DB->has_results()) { - // No messages + // No messages ?> - <h2>No messages</h2> -<? + <h2>No messages</h2> +<?php } else { - // Messages, draw table - if ($ViewString != 'Resolved' && $IsStaff) { - // Open multiresolve form + // Messages, draw table + if ($ViewString != 'Resolved' && $IsStaff) { + // Open multiresolve form ?> - <form class="manage_form" name="staff_messages" method="post" action="staffpm.php" id="messageform"> - <input type="hidden" name="action" value="multiresolve" /> - <input type="hidden" name="view" value="<?=strtolower($View)?>" /> -<? - } + <form class="manage_form" name="staff_messages" method="post" action="staffpm.php" id="messageform"> + <input type="hidden" name="action" value="multiresolve" /> + <input type="hidden" name="view" value="<?=strtolower($View)?>" /> +<?php + } - // Table head + // Table head ?> - <table class="message_table<?=($ViewString != 'Resolved' && $IsStaff) ? ' checkboxes' : '' ?>"> - <tr class="colhead"> -<? if ($ViewString != 'Resolved' && $IsStaff) { ?> - <td width="10"><input type="checkbox" onclick="toggleChecks('messageform', this);" /></td> -<? } ?> - <td width="50%">Subject</td> - <td>Sender</td> - <td>Date</td> - <td>Assigned to</td> - <td>Replies</td> -<? if ($ViewString == 'Resolved') { ?> - <td>Resolved by</td> -<? } ?> - </tr> -<? - - // List messages - while (list($ID, $Subject, $UserID, $Status, $Level, $AssignedToUser, $Date, $Unread, $NumReplies, $ResolverID) = $DB->next_record()) { - $Row = $Row === 'a' ? 'b' : 'a'; - $RowClass = "row$Row"; - - //$UserInfo = Users::user_info($UserID); - $UserStr = Users::format_username($UserID, true, true, true, true); - - // Get assigned - if ($AssignedToUser == '') { - // Assigned to class - $Assigned = ($Level == 0) ? 'First Line Support' : $ClassLevels[$Level]['Name']; - // No + on Sysops - if ($Assigned != 'Sysop') { - $Assigned .= '+'; - } - - } else { - // Assigned to user - // $UserInfo = Users::user_info($AssignedToUser); - $Assigned = Users::format_username($AssignedToUser, true, true, true, true); - - } - - // Get resolver - if ($ViewString == 'Resolved') { - //$UserInfo = Users::user_info($ResolverID); - $ResolverStr = Users::format_username($ResolverID, true, true, true, true); - } - - // Table row + <table class="message_table<?=($ViewString != 'Resolved' && $IsStaff) ? ' checkboxes' : '' ?>"> + <tr class="colhead"> +<?php if ($ViewString != 'Resolved' && $IsStaff) { ?> + <td width="10"><input type="checkbox" onclick="toggleChecks('messageform', this);" /></td> +<?php } ?> + <td width="50%">Subject</td> + <td>Sender</td> + <td>Date</td> + <td>Assigned to</td> + <td>Replies</td> +<?php if ($ViewString == 'Resolved') { ?> + <td>Resolved by</td> +<?php } ?> + </tr> +<?php + + // List messages + while (list($ID, $Subject, $UserID, $Status, $Level, $AssignedToUser, $Date, $Unread, $NumReplies, $ResolverID) = $DB->next_record()) { + $Row = $Row === 'a' ? 'b' : 'a'; + $RowClass = "row$Row"; + + //$UserInfo = Users::user_info($UserID); + $UserStr = Users::format_username($UserID, true, true, true, true); + + // Get assigned + if ($AssignedToUser == '') { + // Assigned to class + $Assigned = ($Level == 0) ? 'First Line Support' : $ClassLevels[$Level]['Name']; + // No + on Sysops + if ($Assigned != 'Sysop') { + $Assigned .= '+'; + } + + } else { + // Assigned to user + // $UserInfo = Users::user_info($AssignedToUser); + $Assigned = Users::format_username($AssignedToUser, true, true, true, true); + + } + + // Get resolver + if ($ViewString == 'Resolved') { + //$UserInfo = Users::user_info($ResolverID); + $ResolverStr = Users::format_username($ResolverID, true, true, true, true); + } + + // Table row ?> - <tr class="<?=$RowClass?>"> -<? if ($ViewString != 'Resolved' && $IsStaff) { ?> - <td class="center"><input type="checkbox" name="id[]" value="<?=$ID?>" /></td> -<? } ?> - <td><a href="staffpm.php?action=viewconv&id=<?=$ID?>"><?=display_str($Subject)?></a></td> - <td><?=$UserStr?></td> - <td><?=time_diff($Date, 2, true)?></td> - <td><?=$Assigned?></td> - <td><?=$NumReplies - 1?></td> -<? if ($ViewString == 'Resolved') { ?> - <td><?=$ResolverStr?></td> -<? } ?> - </tr> -<? - - $DB->set_query_id($StaffPMs); - } //while - - // Close table and multiresolve form + <tr class="<?=$RowClass?>"> +<?php if ($ViewString != 'Resolved' && $IsStaff) { ?> + <td class="center"><input type="checkbox" name="id[]" value="<?=$ID?>" /></td> +<?php } ?> + <td><a href="staffpm.php?action=viewconv&id=<?=$ID?>"><?=display_str($Subject)?></a></td> + <td><?=$UserStr?></td> + <td><?=time_diff($Date, 2, true)?></td> + <td><?=$Assigned?></td> + <td><?=$NumReplies - 1?></td> +<?php if ($ViewString == 'Resolved') { ?> + <td><?=$ResolverStr?></td> +<?php } ?> + </tr> +<?php + + $DB->set_query_id($StaffPMs); + } //while + + // Close table and multiresolve form ?> - </table> -<? if ($ViewString != 'Resolved' && $IsStaff) { ?> - <div class="submit_div"> - <input type="submit" value="Resolve selected" /> - </div> - </form> -<? - } + </table> +<?php if ($ViewString != 'Resolved' && $IsStaff) { ?> + <div class="submit_div"> + <input type="submit" value="Resolve selected" /> + </div> + </form> +<?php + } } //if (!$DB->has_results()) ?> - </div> - <div class="linkbox"> - <?=$Pages?> - </div> + </div> + <div class="linkbox"> + <?=$Pages?> + </div> </div> -<? +<?php View::show_footer(); diff --git a/sections/staffpm/takepost.php b/sections/staffpm/takepost.php index 201d78ae0..c12465f03 100644 --- a/sections/staffpm/takepost.php +++ b/sections/staffpm/takepost.php @@ -1,84 +1,84 @@ -<? +<?php if ($Message = db_string($_POST['message'])) { if ($Subject = db_string($_POST['subject'])) { - // New staff PM conversation - assert_numbers($_POST, array('level'), 'Invalid recipient'); - $DB->query(" - INSERT INTO staff_pm_conversations - (Subject, Status, Level, UserID, Date) - VALUES - ('$Subject', 'Unanswered', $_POST[level], $LoggedUser[ID], '".sqltime()."')" - ); + // New staff PM conversation + assert_numbers($_POST, array('level'), 'Invalid recipient'); + $DB->query(" + INSERT INTO staff_pm_conversations + (Subject, Status, Level, UserID, Date) + VALUES + ('$Subject', 'Unanswered', $_POST[level], $LoggedUser[ID], '".sqltime()."')" + ); - // New message - $ConvID = $DB->inserted_id(); - $DB->query(" - INSERT INTO staff_pm_messages - (UserID, SentDate, Message, ConvID) - VALUES - ($LoggedUser[ID], '".sqltime()."', '$Message', $ConvID)" - ); + // New message + $ConvID = $DB->inserted_id(); + $DB->query(" + INSERT INTO staff_pm_messages + (UserID, SentDate, Message, ConvID) + VALUES + ($LoggedUser[ID], '".sqltime()."', '$Message', $ConvID)" + ); - header('Location: staffpm.php'); + header('Location: staffpm.php'); - } elseif ($ConvID = (int)$_POST['convid']) { - // Check if conversation belongs to user - $DB->query(" - SELECT UserID, AssignedToUser, Level - FROM staff_pm_conversations - WHERE ID = $ConvID"); - list($UserID, $AssignedToUser, $Level) = $DB->next_record(); + } elseif ($ConvID = (int)$_POST['convid']) { + // Check if conversation belongs to user + $DB->query(" + SELECT UserID, AssignedToUser, Level + FROM staff_pm_conversations + WHERE ID = $ConvID"); + list($UserID, $AssignedToUser, $Level) = $DB->next_record(); - $LevelCap = 1000; + $LevelCap = 1000; - $Level = min($Level, $LevelCap); - if ($UserID == $LoggedUser['ID'] || ($IsFLS && $LoggedUser['EffectiveClass'] >= $Level) || $UserID == $AssignedToUser) { - // Response to existing conversation - $DB->query(" - INSERT INTO staff_pm_messages - (UserID, SentDate, Message, ConvID) - VALUES - (".$LoggedUser['ID'].", '".sqltime()."', '$Message', $ConvID)" - ); + $Level = min($Level, $LevelCap); + if ($UserID == $LoggedUser['ID'] || ($IsFLS && $LoggedUser['EffectiveClass'] >= $Level) || $UserID == $AssignedToUser) { + // Response to existing conversation + $DB->query(" + INSERT INTO staff_pm_messages + (UserID, SentDate, Message, ConvID) + VALUES + (".$LoggedUser['ID'].", '".sqltime()."', '$Message', $ConvID)" + ); - // Update conversation - if ($IsFLS) { - // FLS/Staff - $DB->query(" - UPDATE staff_pm_conversations - SET Date = '".sqltime()."', - Unread = true, - Status = 'Open' - WHERE ID = $ConvID"); - $Cache->delete_value("num_staff_pms_$LoggedUser[ID]"); - } else { - // User - $DB->query(" - UPDATE staff_pm_conversations - SET Date = '".sqltime()."', - Unread = true, - Status = 'Unanswered' - WHERE ID = $ConvID"); - } + // Update conversation + if ($IsFLS) { + // FLS/Staff + $DB->query(" + UPDATE staff_pm_conversations + SET Date = '".sqltime()."', + Unread = true, + Status = 'Open' + WHERE ID = $ConvID"); + $Cache->delete_value("num_staff_pms_$LoggedUser[ID]"); + } else { + // User + $DB->query(" + UPDATE staff_pm_conversations + SET Date = '".sqltime()."', + Unread = true, + Status = 'Unanswered' + WHERE ID = $ConvID"); + } - // Clear cache for user - $Cache->delete_value("staff_pm_new_$UserID"); - $Cache->delete_value("staff_pm_new_$LoggedUser[ID]"); + // Clear cache for user + $Cache->delete_value("staff_pm_new_$UserID"); + $Cache->delete_value("staff_pm_new_$LoggedUser[ID]"); - header("Location: staffpm.php?action=viewconv&id=$ConvID"); - } else { - // User is trying to respond to conversation that does no belong to them - error(403); - } - } else { - // Message but no subject or conversation ID - header("Location: staffpm.php?action=viewconv&id=$ConvID"); - } + header("Location: staffpm.php?action=viewconv&id=$ConvID"); + } else { + // User is trying to respond to conversation that does no belong to them + error(403); + } + } else { + // Message but no subject or conversation ID + header("Location: staffpm.php?action=viewconv&id=$ConvID"); + } } elseif ($ConvID = (int)$_POST['convid']) { - // No message, but conversation ID - header("Location: staffpm.php?action=viewconv&id=$ConvID"); + // No message, but conversation ID + header("Location: staffpm.php?action=viewconv&id=$ConvID"); } else { - // No message or conversation ID - error('You have not entered a message for your StaffPM. Please go back and do so.'); + // No message or conversation ID + error('You have not entered a message for your StaffPM. Please go back and do so.'); } ?> diff --git a/sections/staffpm/unresolve.php b/sections/staffpm/unresolve.php index 64ddbf508..f340950e0 100644 --- a/sections/staffpm/unresolve.php +++ b/sections/staffpm/unresolve.php @@ -1,36 +1,36 @@ -<? +<?php if ($ID = (int)($_GET['id'])) { - // Check if conversation belongs to user - $DB->query(" - SELECT UserID, Level, AssignedToUser - FROM staff_pm_conversations - WHERE ID = $ID"); - list($UserID, $Level, $AssignedToUser) = $DB->next_record(); + // Check if conversation belongs to user + $DB->query(" + SELECT UserID, Level, AssignedToUser + FROM staff_pm_conversations + WHERE ID = $ID"); + list($UserID, $Level, $AssignedToUser) = $DB->next_record(); - if ($UserID == $LoggedUser['ID'] - || ($IsFLS && $Level == 0) - || $AssignedToUser == $LoggedUser['ID'] - || ($IsStaff && $Level <= $LoggedUser['EffectiveClass']) - ) { - /*if ($Level != 0 && $IsStaff == false) { - error(403); - }*/ + if ($UserID == $LoggedUser['ID'] + || ($IsFLS && $Level == 0) + || $AssignedToUser == $LoggedUser['ID'] + || ($IsStaff && $Level <= $LoggedUser['EffectiveClass']) + ) { + /*if ($Level != 0 && $IsStaff == false) { + error(403); + }*/ - // Conversation belongs to user or user is staff, unresolve it - $DB->query(" - UPDATE staff_pm_conversations - SET Status = 'Unanswered' - WHERE ID = $ID"); - // Clear cache for user - $Cache->delete_value("num_staff_pms_$LoggedUser[ID]"); + // Conversation belongs to user or user is staff, unresolve it + $DB->query(" + UPDATE staff_pm_conversations + SET Status = 'Unanswered' + WHERE ID = $ID"); + // Clear cache for user + $Cache->delete_value("num_staff_pms_$LoggedUser[ID]"); - header('Location: staffpm.php'); - } else { - // Conversation does not belong to user - error(403); - } + header('Location: staffpm.php'); + } else { + // Conversation does not belong to user + error(403); + } } else { - // No ID - header('Location: staffpm.php'); + // No ID + header('Location: staffpm.php'); } ?> diff --git a/sections/staffpm/user_inbox.php b/sections/staffpm/user_inbox.php index c18314124..7b6c24704 100644 --- a/sections/staffpm/user_inbox.php +++ b/sections/staffpm/user_inbox.php @@ -1,116 +1,116 @@ -<? +<?php View::show_header('Staff PMs', 'staffpm'); // Get messages $StaffPMs = $DB->query(" - SELECT - ID, - Subject, - UserID, - Status, - Level, - AssignedToUser, - Date, - Unread - FROM staff_pm_conversations - WHERE UserID = ".$LoggedUser['ID']." - ORDER BY Status, Date DESC" + SELECT + ID, + Subject, + UserID, + Status, + Level, + AssignedToUser, + Date, + Unread + FROM staff_pm_conversations + WHERE UserID = ".$LoggedUser['ID']." + ORDER BY Status, Date DESC" ); // Start page ?> <div class="thin"> - <div class="header"> - <h2>Staff PMs</h2> - <div class="linkbox"> - <a href="#" onclick="$('#compose').gtoggle();" class="brackets">Compose new</a> - </div> - </div> - <br /> - <br /> - <? View::parse('generic/reply/staffpm.php', array('Hidden' => true)); ?> - <div class="box pad" id="inbox"> -<? + <div class="header"> + <h2>Staff PMs</h2> + <div class="linkbox"> + <a href="#" onclick="$('#compose').gtoggle();" class="brackets">Compose new</a> + </div> + </div> + <br /> + <br /> + <?php View::parse('generic/reply/staffpm.php', array('Hidden' => true)); ?> + <div class="box pad" id="inbox"> +<?php if (!$DB->has_results()) { - // No messages + // No messages ?> - <h2>No messages</h2> -<? + <h2>No messages</h2> +<?php } else { - // Messages, draw table + // Messages, draw table ?> - <form class="manage_form" name="staff_messages" method="post" action="staffpm.php" id="messageform"> - <input type="hidden" name="action" value="multiresolve" /> - <h3>Open messages</h3> - <table class="message_table checkboxes"> - <tr class="colhead"> - <td width="10"><input type="checkbox" onclick="toggleChecks('messageform', this);" /></td> - <td width="50%">Subject</td> - <td>Date</td> - <td>Assigned to</td> - </tr> -<? - // List messages - $Row = 'a'; - $ShowBox = 1; - while (list($ID, $Subject, $UserID, $Status, $Level, $AssignedToUser, $Date, $Unread) = $DB->next_record()) { - if ($Unread === '1') { - $RowClass = 'unreadpm'; - } else { - $Row = $Row === 'a' ? 'b' : 'a'; - $RowClass = "row$Row"; - } + <form class="manage_form" name="staff_messages" method="post" action="staffpm.php" id="messageform"> + <input type="hidden" name="action" value="multiresolve" /> + <h3>Open messages</h3> + <table class="message_table checkboxes"> + <tr class="colhead"> + <td width="10"><input type="checkbox" onclick="toggleChecks('messageform', this);" /></td> + <td width="50%">Subject</td> + <td>Date</td> + <td>Assigned to</td> + </tr> +<?php + // List messages + $Row = 'a'; + $ShowBox = 1; + while (list($ID, $Subject, $UserID, $Status, $Level, $AssignedToUser, $Date, $Unread) = $DB->next_record()) { + if ($Unread === '1') { + $RowClass = 'unreadpm'; + } else { + $Row = $Row === 'a' ? 'b' : 'a'; + $RowClass = "row$Row"; + } - if ($Status == 'Resolved') { - $ShowBox++; - } - if ($ShowBox == 2) { - // First resolved PM + if ($Status == 'Resolved') { + $ShowBox++; + } + if ($ShowBox == 2) { + // First resolved PM ?> - </table> - <br /> - <h3>Resolved messages</h3> - <table class="message_table checkboxes"> - <tr class="colhead"> - <td width="10"><input type="checkbox" onclick="toggleChecks('messageform',this)" /></td> - <td width="50%">Subject</td> - <td>Date</td> - <td>Assigned to</td> - </tr> -<? - } + </table> + <br /> + <h3>Resolved messages</h3> + <table class="message_table checkboxes"> + <tr class="colhead"> + <td width="10"><input type="checkbox" onclick="toggleChecks('messageform',this)" /></td> + <td width="50%">Subject</td> + <td>Date</td> + <td>Assigned to</td> + </tr> +<?php + } - // Get assigned - $Assigned = ($Level == 0) ? 'First Line Support' : $ClassLevels[$Level]['Name']; - // No + on Sysops - if ($Assigned != 'Sysop') { - $Assigned .= '+'; - } + // Get assigned + $Assigned = ($Level == 0) ? 'First Line Support' : $ClassLevels[$Level]['Name']; + // No + on Sysops + if ($Assigned != 'Sysop') { + $Assigned .= '+'; + } - // Table row + // Table row ?> - <tr class="<?=$RowClass?>"> - <td class="center"><input type="checkbox" name="id[]" value="<?=$ID?>" /></td> - <td><a href="staffpm.php?action=viewconv&id=<?=$ID?>"><?=display_str($Subject)?></a></td> - <td><?=time_diff($Date, 2, true)?></td> - <td><?=$Assigned?></td> - </tr> -<? - $DB->set_query_id($StaffPMs); - } + <tr class="<?=$RowClass?>"> + <td class="center"><input type="checkbox" name="id[]" value="<?=$ID?>" /></td> + <td><a href="staffpm.php?action=viewconv&id=<?=$ID?>"><?=display_str($Subject)?></a></td> + <td><?=time_diff($Date, 2, true)?></td> + <td><?=$Assigned?></td> + </tr> +<?php + $DB->set_query_id($StaffPMs); + } - // Close table and multiresolve form + // Close table and multiresolve form ?> - </table> - <div class="submit_div"> - <input type="submit" value="Resolve selected" /> - </div> - </form> -<? + </table> + <div class="submit_div"> + <input type="submit" value="Resolve selected" /> + </div> + </form> +<?php } ?> - </div> + </div> </div> -<? View::show_footer(); ?> +<?php View::show_footer(); ?> diff --git a/sections/staffpm/viewconv.php b/sections/staffpm/viewconv.php index 4baa70095..aaa57ad09 100644 --- a/sections/staffpm/viewconv.php +++ b/sections/staffpm/viewconv.php @@ -1,285 +1,290 @@ -<? +<?php if ($ConvID = (int)$_GET['id']) { - // Get conversation info - $DB->query(" - SELECT Subject, UserID, Level, AssignedToUser, Unread, Status - FROM staff_pm_conversations - WHERE ID = $ConvID"); - list($Subject, $UserID, $Level, $AssignedToUser, $Unread, $Status) = $DB->next_record(); + // Get conversation info + $DB->query(" + SELECT Subject, UserID, Level, AssignedToUser, Unread, Status + FROM staff_pm_conversations + WHERE ID = $ConvID"); + list($Subject, $UserID, $Level, $AssignedToUser, $Unread, $Status) = $DB->next_record(); - $LevelCap = 1000; + $LevelCap = 1000; - $PMLevel = $Level; - $Level = min($Level, $LevelCap); + $PMLevel = $Level; + $Level = min($Level, $LevelCap); - if (!(($UserID == $LoggedUser['ID']) - || ($AssignedToUser == $LoggedUser['ID']) - || (($Level > 0 && $Level <= $LoggedUser['EffectiveClass']) || ($Level == 0 && $IsFLS)) - )) { - // User is trying to view someone else's conversation - error(403); - } - // User is trying to view their own unread conversation, set it to read - if ($UserID == $LoggedUser['ID'] && $Unread) { - $DB->query(" - UPDATE staff_pm_conversations - SET Unread = false - WHERE ID = $ConvID"); - // Clear cache for user - $Cache->delete_value("staff_pm_new_$LoggedUser[ID]"); - } + if (!(($UserID == $LoggedUser['ID']) + || ($AssignedToUser == $LoggedUser['ID']) + || (($Level > 0 && $Level <= $LoggedUser['EffectiveClass']) || ($Level == 0 && $IsFLS)) + )) { + // User is trying to view someone else's conversation + error(403); + } + // User is trying to view their own unread conversation, set it to read + if ($UserID == $LoggedUser['ID'] && $Unread) { + $DB->query(" + UPDATE staff_pm_conversations + SET Unread = false + WHERE ID = $ConvID"); + // Clear cache for user + $Cache->delete_value("staff_pm_new_$LoggedUser[ID]"); + } - View::show_header('Staff PM', 'staffpm,bbcode'); + View::show_header('Staff PM', 'staffpm,bbcode'); - $UserInfo = Users::user_info($UserID); - $UserStr = Users::format_username($UserID, true, true, true, true); + $UserInfo = Users::user_info($UserID); + $UserStr = Users::format_username($UserID, true, true, true, true); - $OwnerID = $UserID; - $OwnerName = $UserInfo['Username']; + $OwnerID = $UserID; + $OwnerName = $UserInfo['Username']; ?> <div class="thin"> - <div class="header"> - <h2>Staff PM - <?=display_str($Subject)?></h2> - <div class="linkbox"> + <div class="header"> + <h2>Staff PM - <?=display_str($Subject)?></h2> + <div class="linkbox"> -<? +<?php if ($IsStaff) { ?> - <a href="staffpm.php" class="brackets">View your unanswered</a> -<? + <a href="staffpm.php" class="brackets">View your unanswered</a> +<?php } if ($IsFLS) { ?> <a href="staffpm.php?view=unanswered" class="brackets">View all unanswered</a> <a href="staffpm.php?view=open" class="brackets">View unresolved</a> <a href="staffpm.php?view=resolved" class="brackets">View resolved</a> -<? +<?php } if ($IsStaff) { ?> - <a href="staffpm.php?action=scoreboard" class="brackets">View scoreboard</a> -<? + <a href="staffpm.php?action=scoreboard" class="brackets">View scoreboard</a> +<?php } if (!$IsStaff && !$IsFLS) { ?> - <a href="staffpm.php" class="brackets">Back to inbox</a> -<? + <a href="staffpm.php" class="brackets">Back to inbox</a> +<?php } -?> </div> - </div> - <br /> - <br /> - <div id="inbox"> -<? - // Get messages - $StaffPMs = $DB->query(" - SELECT UserID, SentDate, Message, ID - FROM staff_pm_messages - WHERE ConvID = $ConvID"); +?> </div> + </div> + <br /> + <br /> + <div id="inbox"> +<?php + // Get messages + $StaffPMs = $DB->query(" + SELECT UserID, SentDate, Message, ID + FROM staff_pm_messages + WHERE ConvID = $ConvID"); - while (list($UserID, $SentDate, $Message, $MessageID) = $DB->next_record()) { - // Set user string - if ($UserID == $OwnerID) { - // User, use prepared string - $UserString = $UserStr; - $Username = $OwnerName; - } else { - // Staff/FLS - $UserInfo = Users::user_info($UserID); - $UserString = Users::format_username($UserID, true, true, true, true); - $Username = $UserInfo['Username']; - } + while (list($UserID, $SentDate, $Message, $MessageID) = $DB->next_record()) { + // Set user string + if ($UserID == $OwnerID) { + // User, use prepared string + $UserString = $UserStr; + $Username = $OwnerName; + } else { + // Staff/FLS + $UserInfo = Users::user_info($UserID); + $UserString = Users::format_username($UserID, true, true, true, true); + $Username = $UserInfo['Username']; + } ?> - <div class="box vertical_space" id="post<?=$MessageID?>"> - <div class="head"> -<? /* TODO: the inline style in the <a> tag is an ugly hack. get rid of it. */ ?> - <a class="postid" href="staffpm.php?action=viewconv&id=<?=$ConvID?>#post<?=$MessageID?>" style="font-weight: normal;">#<?=$MessageID?></a> - <strong> - <?=$UserString?> - </strong> - <?=time_diff($SentDate, 2, true)?> -<? if ($Status != 'Resolved') { ?> - - <a href="#quickpost" onclick="Quote('<?=$MessageID?>', '<?=$Username?>');" class="brackets">Quote</a> -<? } ?> - </div> - <div class="body"><?=Text::full_format($Message)?></div> - </div> - <div align="center" style="display: none;"></div> -<? - $DB->set_query_id($StaffPMs); - } + <div class="box vertical_space" id="post<?=$MessageID?>"> + <div class="head"> +<?php /* TODO: the inline style in the <a> tag is an ugly hack. get rid of it. */ ?> + <a class="postid" href="staffpm.php?action=viewconv&id=<?=$ConvID?>#post<?=$MessageID?>" style="font-weight: normal;">#<?=$MessageID?></a> + <strong> + <?=$UserString?> + </strong> + <?=time_diff($SentDate, 2, true)?> +<?php if ($Status != 'Resolved') { ?> + - <a href="#quickpost" onclick="Quote('<?=$MessageID?>', '<?=$Username?>');" class="brackets">Quote</a> +<?php } ?> + </div> + <div class="body"><?=Text::full_format($Message)?></div> + </div> + <div align="center" style="display: none;"></div> +<?php + $DB->set_query_id($StaffPMs); + } - // Common responses - if ($IsFLS && $Status != 'Resolved') { + // Common responses + if ($IsFLS && $Status != 'Resolved') { ?> - <div id="common_answers" class="hidden"> - <div class="box vertical_space"> - <div class="head"> - <strong>Preview</strong> - </div> - <div id="common_answers_body" class="body">Select an answer from the drop-down to view it.</div> - </div> - <br /> - <div class="center"> - <select id="common_answers_select" onchange="UpdateMessage();"> - <option id="first_common_response">Select a message</option> -<? - // List common responses - $DB->query(" - SELECT ID, Name - FROM staff_pm_responses - ORDER BY Name ASC"); - while (list($ID, $Name) = $DB->next_record()) { + <div id="common_answers" class="hidden"> + <div class="box vertical_space"> + <div class="head"> + <strong>Preview</strong> + </div> + <div id="common_answers_body" class="body">Select an answer from the drop-down to view it.</div> + </div> + <br /> + <div class="center"> + <select id="common_answers_select" onchange="UpdateMessage();"> + <option id="first_common_response">Select a message</option> +<?php + // List common responses + $DB->query(" + SELECT ID, Name + FROM staff_pm_responses + ORDER BY Name ASC"); + while (list($ID, $Name) = $DB->next_record()) { ?> - <option value="<?=$ID?>"><?=$Name?></option> -<? } ?> - </select> - <input type="button" value="Set message" onclick="SetMessage();" /> - <input type="button" value="Create new / Edit" onclick="location.href='staffpm.php?action=responses&convid=<?=$ConvID?>';" /> - </div> - </div> -<? - } + <option value="<?=$ID?>"><?=$Name?></option> +<?php } ?> + </select> + <input type="button" value="Set message" onclick="SetMessage();" /> + <input type="button" value="Create new / Edit" onclick="location.href='staffpm.php?action=responses&convid=<?=$ConvID?>';" /> + </div> + </div> +<?php + } - // Ajax assign response div - if ($IsStaff) { + // Ajax assign response div + if ($IsStaff) { ?> - <div id="ajax_message" class="hidden center alertbar"></div> -<? - } + <div id="ajax_message" class="hidden center alertbar"></div> +<?php + } - // Reply box and buttons + // Reply box and buttons ?> - <h3>Reply</h3> - <div class="box pad" id="reply_box"> - <div id="buttons" class="center"> - <form class="manage_form" name="staff_messages" action="staffpm.php" method="post" id="messageform"> - <input type="hidden" name="action" value="takepost" /> - <input type="hidden" name="convid" value="<?=$ConvID?>" id="convid" /> -<? - if ($Status != 'Resolved') { - $TextPrev = new TEXTAREA_PREVIEW('message', 'quickpost', '', 90, 10, true, false, false, array(), true); - } + <h3>Reply</h3> + <div class="box pad" id="reply_box"> + <div id="buttons" class="center"> + <form class="manage_form" name="staff_messages" action="staffpm.php" method="post" id="messageform"> + <input type="hidden" name="action" value="takepost" /> + <input type="hidden" name="convid" value="<?=$ConvID?>" id="convid" /> +<?php + if ($Status != 'Resolved') { + $TextPrev = new TEXTAREA_PREVIEW('message', 'quickpost', '', 90, 10, true, false, false, [], true); + } ?> - <br /> -<? - // Assign to - if ($IsStaff) { - // Staff assign dropdown + <br /> +<?php + // Assign to + if ($IsStaff) { + // Staff assign dropdown ?> - <select id="assign_to" name="assign"> - <optgroup label="User classes"> -<? // FLS "class" - $Selected = ((!$AssignedToUser && $PMLevel == 0) ? ' selected="selected"' : ''); + <select id="assign_to" name="assign"> + <optgroup label="User classes"> +<?php // FLS "class" + $Selected = ((!$AssignedToUser && $PMLevel == 0) ? ' selected="selected"' : ''); ?> - <option value="class_0"<?=$Selected?>>First Line Support</option> -<? // Staff classes - foreach ($ClassLevels as $Class) { - // Create one <option> for each staff user class - if ($Class['Level'] >= 650) { - $Selected = ((!$AssignedToUser && ($PMLevel == $Class['Level'])) ? ' selected="selected"' : ''); + <option value="class_0"<?=$Selected?>>First Line Support</option> +<?php // Staff classes + foreach ($ClassLevels as $Class) { + // Create one <option> for each staff user class + if ($Class['Level'] >= 650) { + $Selected = ((!$AssignedToUser && ($PMLevel == $Class['Level'])) ? ' selected="selected"' : ''); ?> - <option value="class_<?=$Class['Level']?>"<?=$Selected?>><?=$Class['Name']?></option> -<? - } - } + <option value="class_<?=$Class['Level']?>"<?=$Selected?>><?=$Class['Name']?></option> +<?php + } + } ?> - </optgroup> - <optgroup label="Staff"> -<? // Staff members - $DB->query(" - SELECT - m.ID, - m.Username - FROM permissions AS p - JOIN users_main AS m ON m.PermissionID = p.ID - WHERE p.DisplayStaff = '1' - ORDER BY p.Level DESC, m.Username ASC" - ); - while (list($ID, $Name) = $DB->next_record()) { - // Create one <option> for each staff member - $Selected = (($AssignedToUser == $ID) ? ' selected="selected"' : ''); + </optgroup> + <optgroup label="Staff"> +<?php // Staff members + $DB->query(" + SELECT + m.ID, + m.Username + FROM permissions AS p + JOIN users_main AS m ON m.PermissionID = p.ID + WHERE p.DisplayStaff = '1' + ORDER BY p.Level DESC, m.Username ASC" + ); + while (list($ID, $Name) = $DB->next_record()) { + // Create one <option> for each staff member + $Selected = (($AssignedToUser == $ID) ? ' selected="selected"' : ''); ?> - <option value="user_<?=$ID?>"<?=$Selected?>><?=$Name?></option> -<? } ?> - </optgroup> - <optgroup label="First Line Support"> -<? - // FLS users - $DB->query(" - SELECT - m.ID, - m.Username - FROM users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - JOIN permissions AS p ON p.ID = m.PermissionID - WHERE p.DisplayStaff != '1' - AND i.SupportFor != '' - ORDER BY m.Username ASC - "); - while (list($ID, $Name) = $DB->next_record()) { - // Create one <option> for each FLS user - $Selected = (($AssignedToUser == $ID) ? ' selected="selected"' : ''); + <option value="user_<?=$ID?>"<?=$Selected?>><?=$Name?></option> +<?php } ?> + </optgroup> + <optgroup label="First Line Support"> +<?php + // FLS users + $DB->query(" + SELECT + m.ID, + m.Username + FROM users_info AS i + JOIN users_main AS m ON m.ID = i.UserID + JOIN permissions AS p ON p.ID = m.PermissionID + WHERE p.DisplayStaff != '1' + AND i.SupportFor != '' + ORDER BY m.Username ASC + "); + while (list($ID, $Name) = $DB->next_record()) { + // Create one <option> for each FLS user + $Selected = (($AssignedToUser == $ID) ? ' selected="selected"' : ''); ?> - <option value="user_<?=$ID?>"<?=$Selected?>><?=$Name?></option> -<? } ?> - </optgroup> - </select> - <input type="button" onclick="Assign();" value="Assign" /> -<? } elseif ($IsFLS) { /* FLS assign button */ ?> - <input type="button" value="Assign to staff" onclick="location.href='staffpm.php?action=assign&to=staff&convid=<?=$ConvID?>';" /> - <input type="button" value="Assign to forum staff" onclick="location.href='staffpm.php?action=assign&to=forum&convid=<?=$ConvID?>';" /> -<? - } + <option value="user_<?=$ID?>"<?=$Selected?>><?=$Name?></option> +<?php } ?> + </optgroup> + </select> + <input type="button" onclick="Assign();" value="Assign" /> +<?php + } elseif ($IsFLS) { /* FLS assign button */ ?> + <input type="button" value="Assign to staff" onclick="location.href='staffpm.php?action=assign&to=staff&convid=<?=$ConvID?>';" /> + <input type="button" value="Assign to forum staff" onclick="location.href='staffpm.php?action=assign&to=forum&convid=<?=$ConvID?>';" /> +<?php + } - if ($Status != 'Resolved') { ?> - <input type="button" value="Resolve" onclick="location.href='staffpm.php?action=resolve&id=<?=$ConvID?>';" /> -<? if ($IsFLS) { /* Moved by request */ ?> - <input type="button" value="Common answers" onclick="$('#common_answers').gtoggle();" /> -<? } ?> - <input type="button" id="previewbtn" value="Preview" class="hidden button_preview_<?=$TextPrev->getID()?>" /> - <input type="submit" value="Send message" /> -<? } else { ?> - <input type="button" value="Unresolve" onclick="location.href='staffpm.php?action=unresolve&id=<?=$ConvID?>';" /> -<? - } - if (check_perms('users_give_donor')) { ?> - <br /> - <input type="button" value="Make Donor" onclick="$('#make_donor_form').gtoggle(); return false;" /> -<? } ?> - </form> -<? if (check_perms('users_give_donor')) { ?> - <div id="make_donor_form" class="hidden"> - <form action="staffpm.php" method="post"> - <input type="hidden" name="action" value="make_donor" /> - <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> - <input type="hidden" name="id" value="<?=$ConvID?>" /> - <strong>Amount: </strong> - <input type="text" name="donation_amount" onkeypress="return isNumberKey(event);" /> - <br /> - <strong>Reason: </strong> - <input type="text" name="donation_reason" /> - <br /> - <select name="donation_source"> - <option value="Flattr">Flattr</option> - </select> - <select name="donation_currency"> - <option value="EUR">EUR</option> - </select> - <input type="submit" value="Submit" /> - </form> - </div> -<? } ?> - </div> - </div> - </div> + if ($Status != 'Resolved') { ?> + <input type="button" value="Resolve" onclick="location.href='staffpm.php?action=resolve&id=<?=$ConvID?>';" /> +<?php if ($IsFLS) { /* Moved by request */ ?> + <input type="button" value="Common answers" onclick="$('#common_answers').gtoggle();" /> +<?php } ?> + <input type="button" id="previewbtn" value="Preview" class="hidden button_preview_<?=$TextPrev->getID()?>" /> + <input type="submit" value="Send message" /> +<?php + } else { ?> + <input type="button" value="Unresolve" onclick="location.href='staffpm.php?action=unresolve&id=<?=$ConvID?>';" /> +<?php + } + if (check_perms('users_give_donor')) { ?> + <br /> + <input type="button" value="Make Donor" onclick="$('#make_donor_form').gtoggle(); return false;" /> +<?php + } ?> + </form> +<?php + if (check_perms('users_give_donor')) { ?> + <div id="make_donor_form" class="hidden"> + <form action="staffpm.php" method="post"> + <input type="hidden" name="action" value="make_donor" /> + <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> + <input type="hidden" name="id" value="<?=$ConvID?>" /> + <strong>Amount: </strong> + <input type="text" name="donation_amount" onkeypress="return isNumberKey(event);" /> + <br /> + <strong>Reason: </strong> + <input type="text" name="donation_reason" /> + <br /> + <select name="donation_source"> + <option value="Flattr">Flattr</option> + </select> + <select name="donation_currency"> + <option value="EUR">EUR</option> + </select> + <input type="submit" value="Submit" /> + </form> + </div> +<?php + } ?> + </div> + </div> + </div> </div> -<? +<?php - View::show_footer(); + View::show_footer(); } else { - // No ID - header('Location: staffpm.php'); + // No ID + header('Location: staffpm.php'); } diff --git a/sections/stats/index.php b/sections/stats/index.php index 4fcc3c203..3ed27a1a4 100644 --- a/sections/stats/index.php +++ b/sections/stats/index.php @@ -1,18 +1,18 @@ -<? +<?php enforce_login(); if (!isset($_REQUEST['action'])) { - include(SERVER_ROOT . '/sections/stats/list.php'); + include(SERVER_ROOT . '/sections/stats/list.php'); } else { - switch ($_REQUEST['action']) { - case 'users': - include(SERVER_ROOT.'/sections/stats/users.php'); - break; - case 'torrents': - include(SERVER_ROOT.'/sections/stats/torrents.php'); - break; - default: - include(SERVER_ROOT.'/sections/stats/list.php'); - break; - } + switch ($_REQUEST['action']) { + case 'users': + include(SERVER_ROOT.'/sections/stats/users.php'); + break; + case 'torrents': + include(SERVER_ROOT.'/sections/stats/torrents.php'); + break; + default: + include(SERVER_ROOT.'/sections/stats/list.php'); + break; + } } diff --git a/sections/stats/list.php b/sections/stats/list.php index e1f7bce2c..f90ae8843 100644 --- a/sections/stats/list.php +++ b/sections/stats/list.php @@ -1,11 +1,11 @@ <?php -View::show_header('Stats'); +View::show_header('Inbox'); ?> <div class="thin"> - <h3 id="general">Site Statistics</h3> + <h3 id="general">Pursuit of Perfection</h3> <div class="box pad" style="padding: 10px 10px 10px 20px;"> <ul> <li><a href='stats.php?action=users'>User Stats</a> diff --git a/sections/stats/torrents.php b/sections/stats/torrents.php index d10ce2d6b..67ab7cea6 100644 --- a/sections/stats/torrents.php +++ b/sections/stats/torrents.php @@ -1,76 +1,76 @@ -<? +<?php if (!list($Labels, $InFlow, $OutFlow, $NetFlow, $Max) = $Cache->get_value('torrents_timeline')) { - $DB->query(" - SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, COUNT(ID) - FROM log - WHERE Message LIKE 'Torrent % was uploaded by %' - GROUP BY Month - ORDER BY Time DESC - LIMIT 1, 12"); - $TimelineIn = array_reverse($DB->to_array()); - $DB->query(" - SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, COUNT(ID) - FROM log - WHERE Message LIKE 'Torrent % was deleted %' - GROUP BY Month - ORDER BY Time DESC - LIMIT 1, 12"); - $TimelineOut = array_reverse($DB->to_array()); - $DB->query(" - SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, COUNT(ID) - FROM torrents - GROUP BY Month - ORDER BY Time DESC - LIMIT 1, 12"); - $TimelineNet = array_reverse($DB->to_array()); + $DB->query(" + SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, COUNT(ID) + FROM log + WHERE Message LIKE 'Torrent % was uploaded by %' + GROUP BY Month + ORDER BY Time DESC + LIMIT 1, 12"); + $TimelineIn = array_reverse($DB->to_array()); + $DB->query(" + SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, COUNT(ID) + FROM log + WHERE Message LIKE 'Torrent % was deleted %' + GROUP BY Month + ORDER BY Time DESC + LIMIT 1, 12"); + $TimelineOut = array_reverse($DB->to_array()); + $DB->query(" + SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, COUNT(ID) + FROM torrents + GROUP BY Month + ORDER BY Time DESC + LIMIT 1, 12"); + $TimelineNet = array_reverse($DB->to_array()); - foreach ($TimelineIn as $Month) { - list($Label, $Amount) = $Month; - if ($Amount > $Max) { - $Max = $Amount; - } - } - foreach ($TimelineOut as $Month) { - list($Label, $Amount) = $Month; - if ($Amount > $Max) { - $Max = $Amount; - } - } - foreach ($TimelineNet as $Month) { - list($Label, $Amount) = $Month; - if ($Amount > $Max) { - $Max = $Amount; - } - } - foreach ($TimelineIn as $Month) { - list($Label, $Amount) = $Month; - $Labels[] = $Label; - $InFlow[] = number_format(($Amount / $Max) * 100, 4); - } - foreach ($TimelineOut as $Month) { - list($Label, $Amount) = $Month; - $OutFlow[] = number_format(($Amount / $Max) * 100, 4); - } - foreach ($TimelineNet as $Month) { - list($Label, $Amount) = $Month; - $NetFlow[] = number_format(($Amount / $Max) * 100, 4); - } - $Cache->cache_value('torrents_timeline', array($Labels, $InFlow, $OutFlow, $NetFlow, $Max), mktime(0, 0, 0, date('n') + 1, 2)); //Tested: fine for dec -> jan + foreach ($TimelineIn as $Month) { + list($Label, $Amount) = $Month; + if ($Amount > $Max) { + $Max = $Amount; + } + } + foreach ($TimelineOut as $Month) { + list($Label, $Amount) = $Month; + if ($Amount > $Max) { + $Max = $Amount; + } + } + foreach ($TimelineNet as $Month) { + list($Label, $Amount) = $Month; + if ($Amount > $Max) { + $Max = $Amount; + } + } + foreach ($TimelineIn as $Month) { + list($Label, $Amount) = $Month; + $Labels[] = $Label; + $InFlow[] = number_format(($Amount / $Max) * 100, 4); + } + foreach ($TimelineOut as $Month) { + list($Label, $Amount) = $Month; + $OutFlow[] = number_format(($Amount / $Max) * 100, 4); + } + foreach ($TimelineNet as $Month) { + list($Label, $Amount) = $Month; + $NetFlow[] = number_format(($Amount / $Max) * 100, 4); + } + $Cache->cache_value('torrents_timeline', array($Labels, $InFlow, $OutFlow, $NetFlow, $Max), mktime(0, 0, 0, date('n') + 1, 2)); //Tested: fine for dec -> jan } include_once(SERVER_ROOT.'/classes/charts.class.php'); $DB->query(" - SELECT tg.CategoryID, COUNT(t.ID) AS Torrents - FROM torrents AS t - JOIN torrents_group AS tg ON tg.ID = t.GroupID - GROUP BY tg.CategoryID - ORDER BY Torrents DESC"); + SELECT tg.CategoryID, COUNT(t.ID) AS Torrents + FROM torrents AS t + JOIN torrents_group AS tg ON tg.ID = t.GroupID + GROUP BY tg.CategoryID + ORDER BY Torrents DESC"); $Groups = $DB->to_array(); $Pie = new PIE_CHART(750, 400, array('Other' => 1, 'Percentage' => 1)); foreach ($Groups as $Group) { - list($CategoryID, $Torrents) = $Group; - $CategoryName = $Categories[$CategoryID - 1]; - $Pie->add($CategoryName, $Torrents); + list($CategoryID, $Torrents) = $Group; + $CategoryName = $Categories[$CategoryID - 1]; + $Pie->add($CategoryName, $Torrents); } $Pie->transparent(); $Pie->color('FF33CC'); @@ -80,15 +80,15 @@ View::show_header('Detailed torrent statistics'); ?> <div class="linkbox"> - <a href="stats.php?action=users" class="brackets">User Stats</a> + <a href="stats.php?action=users" class="brackets">User Stats</a> </div> <h1 id="Torrent_Upload"><a href="#Torrent_Upload">Uploads by month</a></h1> <div class="box pad center"> - <img src="https://chart.googleapis.com/chart?cht=lc&chs=880x160&chco=000D99,99000D,00990D&chg=0,-1,1,1&chxt=y,x&chxs=0,h&chxl=1:|<?=implode('|', $Labels)?>&chxr=0,0,<?=$Max?>&chd=t:<?=implode(',', $InFlow)?>|<?=implode(',', $OutFlow)?>|<?=implode(',', $NetFlow)?>&chls=2,4,0&chdl=Uploads|Deletions|Remaining&chf=bg,s,FFFFFF00" alt="User Flow Chart" /> + <img src="https://chart.googleapis.com/chart?cht=lc&chs=880x160&chco=000D99,99000D,00990D&chg=0,-1,1,1&chxt=y,x&chxs=0,h&chxl=1:|<?=implode('|', $Labels)?>&chxr=0,0,<?=$Max?>&chd=t:<?=implode(',', $InFlow)?>|<?=implode(',', $OutFlow)?>|<?=implode(',', $NetFlow)?>&chls=2,4,0&chdl=Uploads|Deletions|Remaining&chf=bg,s,FFFFFF00" alt="User Flow Chart" /> </div> <h1 id="Torrent_Category"><a href="#Torrent_Category">Torrents by category</a></h1> <div class="box pad center"> - <img src="<?=$Categories?>" alt="" /> + <img src="<?=$Categories?>" alt="" /> </div> -<? +<?php View::show_footer(); diff --git a/sections/stats/users.php b/sections/stats/users.php index 018008a0c..0abd86b11 100644 --- a/sections/stats/users.php +++ b/sections/stats/users.php @@ -1,189 +1,189 @@ -<? +<?php if (!list($Countries, $Rank, $CountryUsers, $CountryMax, $CountryMin, $LogIncrements) = $Cache->get_value('geodistribution')) { - include_once(SERVER_ROOT.'/classes/charts.class.php'); - $DB->query(' - SELECT Code, Users - FROM users_geodistribution'); - $Data = $DB->to_array(); - $Count = $DB->record_count() - 1; + include_once(SERVER_ROOT.'/classes/charts.class.php'); + $DB->query(' + SELECT Code, Users + FROM users_geodistribution'); + $Data = $DB->to_array(); + $Count = $DB->record_count() - 1; - if ($Count < 30) { - $CountryMinThreshold = $Count; - } else { - $CountryMinThreshold = 30; - } + if ($Count < 30) { + $CountryMinThreshold = $Count; + } else { + $CountryMinThreshold = 30; + } - $CountryMax = ceil(log(Max(1, $Data[0][1])) / log(2)) + 1; - $CountryMin = floor(log(Max(1, $Data[$CountryMinThreshold][1])) / log(2)); + $CountryMax = ceil(log(Max(1, $Data[0][1])) / log(2)) + 1; + $CountryMin = floor(log(Max(1, $Data[$CountryMinThreshold][1])) / log(2)); - $CountryRegions = array('RS' => array('RS-KM')); // Count Kosovo as Serbia as it doesn't have a TLD - foreach ($Data as $Key => $Item) { - list($Country, $UserCount) = $Item; - $Countries[] = $Country; - $CountryUsers[] = number_format((((log($UserCount) / log(2)) - $CountryMin) / ($CountryMax - $CountryMin)) * 100, 2); - $Rank[] = round((1 - ($Key / $Count)) * 100); + $CountryRegions = array('RS' => array('RS-KM')); // Count Kosovo as Serbia as it doesn't have a TLD + foreach ($Data as $Key => $Item) { + list($Country, $UserCount) = $Item; + $Countries[] = $Country; + $CountryUsers[] = number_format((((log($UserCount) / log(2)) - $CountryMin) / ($CountryMax - $CountryMin)) * 100, 2); + $Rank[] = round((1 - ($Key / $Count)) * 100); - if (isset($CountryRegions[$Country])) { - foreach ($CountryRegions[$Country] as $Region) { - $Countries[] = $Region; - $Rank[] = end($Rank); - } - } - } - reset($Rank); + if (isset($CountryRegions[$Country])) { + foreach ($CountryRegions[$Country] as $Region) { + $Countries[] = $Region; + $Rank[] = end($Rank); + } + } + } + reset($Rank); - for ($i = $CountryMin; $i <= $CountryMax; $i++) { - $LogIncrements[] = Format::human_format(pow(2, $i)); - } - $Cache->cache_value('geodistribution', array($Countries, $Rank, $CountryUsers, $CountryMax, $CountryMin, $LogIncrements), 0); + for ($i = $CountryMin; $i <= $CountryMax; $i++) { + $LogIncrements[] = Format::human_format(pow(2, $i)); + } + $Cache->cache_value('geodistribution', array($Countries, $Rank, $CountryUsers, $CountryMax, $CountryMin, $LogIncrements), 0); } if (!$ClassDistribution = $Cache->get_value('class_distribution')) { - include_once(SERVER_ROOT.'/classes/charts.class.php'); - $DB->query(" - SELECT p.Name, COUNT(m.ID) AS Users - FROM users_main AS m - JOIN permissions AS p ON m.PermissionID = p.ID - WHERE m.Enabled = '1' - GROUP BY p.Name - ORDER BY Users DESC"); - $ClassSizes = $DB->to_array(); - $Pie = new PIE_CHART(750, 400, array('Other' => 1, 'Percentage' => 1)); - foreach ($ClassSizes as $ClassSize) { - list($Label, $Users) = $ClassSize; - $Pie->add($Label, $Users); - } - $Pie->transparent(); - $Pie->color('FF33CC'); - $Pie->generate(); - $ClassDistribution = $Pie->url(); - $Cache->cache_value('class_distribution', $ClassDistribution, 3600 * 24 * 14); + include_once(SERVER_ROOT.'/classes/charts.class.php'); + $DB->query(" + SELECT p.Name, COUNT(m.ID) AS Users + FROM users_main AS m + JOIN permissions AS p ON m.PermissionID = p.ID + WHERE m.Enabled = '1' + GROUP BY p.Name + ORDER BY Users DESC"); + $ClassSizes = $DB->to_array(); + $Pie = new PIE_CHART(750, 400, array('Other' => 1, 'Percentage' => 1)); + foreach ($ClassSizes as $ClassSize) { + list($Label, $Users) = $ClassSize; + $Pie->add($Label, $Users); + } + $Pie->transparent(); + $Pie->color('FF33CC'); + $Pie->generate(); + $ClassDistribution = $Pie->url(); + $Cache->cache_value('class_distribution', $ClassDistribution, 3600 * 24 * 14); } if (!$PlatformDistribution = $Cache->get_value('platform_distribution')) { - include_once(SERVER_ROOT.'/classes/charts.class.php'); + include_once(SERVER_ROOT.'/classes/charts.class.php'); - $DB->query(" - SELECT OperatingSystem, COUNT(UserID) AS Users - FROM users_sessions - GROUP BY OperatingSystem - ORDER BY Users DESC"); + $DB->query(" + SELECT OperatingSystem, COUNT(UserID) AS Users + FROM users_sessions + GROUP BY OperatingSystem + ORDER BY Users DESC"); - $Platforms = $DB->to_array(); - $Pie = new PIE_CHART(750, 400, array('Other' => 1, 'Percentage' => 1)); - foreach ($Platforms as $Platform) { - list($Label, $Users) = $Platform; - $Pie->add($Label, $Users); - } - $Pie->transparent(); - $Pie->color('8A00B8'); - $Pie->generate(); - $PlatformDistribution = $Pie->url(); - $Cache->cache_value('platform_distribution', $PlatformDistribution, 3600 * 24 * 14); + $Platforms = $DB->to_array(); + $Pie = new PIE_CHART(750, 400, array('Other' => 1, 'Percentage' => 1)); + foreach ($Platforms as $Platform) { + list($Label, $Users) = $Platform; + $Pie->add($Label, $Users); + } + $Pie->transparent(); + $Pie->color('8A00B8'); + $Pie->generate(); + $PlatformDistribution = $Pie->url(); + $Cache->cache_value('platform_distribution', $PlatformDistribution, 3600 * 24 * 14); } if (!$BrowserDistribution = $Cache->get_value('browser_distribution')) { - include_once(SERVER_ROOT.'/classes/charts.class.php'); + include_once(SERVER_ROOT.'/classes/charts.class.php'); - $DB->query(" - SELECT Browser, COUNT(UserID) AS Users - FROM users_sessions - GROUP BY Browser - ORDER BY Users DESC"); + $DB->query(" + SELECT Browser, COUNT(UserID) AS Users + FROM users_sessions + GROUP BY Browser + ORDER BY Users DESC"); - $Browsers = $DB->to_array(); - $Pie = new PIE_CHART(750, 400, array('Other' => 1, 'Percentage' => 1)); - foreach ($Browsers as $Browser) { - list($Label, $Users) = $Browser; - $Pie->add($Label, $Users); - } - $Pie->transparent(); - $Pie->color('008AB8'); - $Pie->generate(); - $BrowserDistribution = $Pie->url(); - $Cache->cache_value('browser_distribution', $BrowserDistribution, 3600 * 24 * 14); + $Browsers = $DB->to_array(); + $Pie = new PIE_CHART(750, 400, array('Other' => 1, 'Percentage' => 1)); + foreach ($Browsers as $Browser) { + list($Label, $Users) = $Browser; + $Pie->add($Label, $Users); + } + $Pie->transparent(); + $Pie->color('008AB8'); + $Pie->generate(); + $BrowserDistribution = $Pie->url(); + $Cache->cache_value('browser_distribution', $BrowserDistribution, 3600 * 24 * 14); } //Timeline generation if (!list($Labels, $InFlow, $OutFlow, $Max) = $Cache->get_value('users_timeline')) { - $DB->query(" - SELECT DATE_FORMAT(JoinDate,'%b \\'%y') AS Month, COUNT(UserID) - FROM users_info - GROUP BY Month - ORDER BY JoinDate DESC - LIMIT 1, 12"); - $TimelineIn = array_reverse($DB->to_array()); - $DB->query(" - SELECT DATE_FORMAT(BanDate,'%b \\'%y') AS Month, COUNT(UserID) - FROM users_info - GROUP BY Month - ORDER BY BanDate DESC - LIMIT 1, 12"); - $TimelineOut = array_reverse($DB->to_array()); - foreach ($TimelineIn as $Month) { - list($Label, $Amount) = $Month; - if ($Amount > $Max) { - $Max = $Amount; - } - } - foreach ($TimelineOut as $Month) { - list($Label, $Amount) = $Month; - if ($Amount > $Max) { - $Max = $Amount; - } - } + $DB->query(" + SELECT DATE_FORMAT(JoinDate,'%b \\'%y') AS Month, COUNT(UserID) + FROM users_info + GROUP BY Month + ORDER BY JoinDate DESC + LIMIT 1, 12"); + $TimelineIn = array_reverse($DB->to_array()); + $DB->query(" + SELECT DATE_FORMAT(BanDate,'%b \\'%y') AS Month, COUNT(UserID) + FROM users_info + GROUP BY Month + ORDER BY BanDate DESC + LIMIT 1, 12"); + $TimelineOut = array_reverse($DB->to_array()); + foreach ($TimelineIn as $Month) { + list($Label, $Amount) = $Month; + if ($Amount > $Max) { + $Max = $Amount; + } + } + foreach ($TimelineOut as $Month) { + list($Label, $Amount) = $Month; + if ($Amount > $Max) { + $Max = $Amount; + } + } - $Labels = array(); - foreach ($TimelineIn as $Month) { - list($Label, $Amount) = $Month; - $Labels[] = $Label; - $InFlow[] = number_format(($Amount / $Max) * 100, 4); - } - foreach ($TimelineOut as $Month) { - list($Label, $Amount) = $Month; - $OutFlow[] = number_format(($Amount / $Max) * 100, 4); - } - $Cache->cache_value('users_timeline', array($Labels, $InFlow, $OutFlow, $Max), mktime(0, 0, 0, date('n') + 1, 2)); //Tested: fine for Dec -> Jan + $Labels = []; + foreach ($TimelineIn as $Month) { + list($Label, $Amount) = $Month; + $Labels[] = $Label; + $InFlow[] = number_format(($Amount / $Max) * 100, 4); + } + foreach ($TimelineOut as $Month) { + list($Label, $Amount) = $Month; + $OutFlow[] = number_format(($Amount / $Max) * 100, 4); + } + $Cache->cache_value('users_timeline', array($Labels, $InFlow, $OutFlow, $Max), mktime(0, 0, 0, date('n') + 1, 2)); //Tested: fine for Dec -> Jan } //End timeline generation View::show_header('Detailed User Statistics'); ?> <div class="linkbox"> - <a href="stats.php?action=torrents" class="brackets">Torrent Stats</a> + <a href="stats.php?action=torrents" class="brackets">Torrent Stats</a> </div> <h1 id="User_Flow"><a href="#User_Flow">User Flow</a></h1> <div class="box pad center"> - <img src="https://chart.googleapis.com/chart?cht=lc&chs=880x160&chco=000D99,99000D&chg=0,-1,1,1&chxt=y,x&chxs=0,h&chxl=1:|<?=implode('|', $Labels)?>&chxr=0,0,<?=$Max?>&chd=t:<?=implode(',', $InFlow)?>|<?=implode(',', $OutFlow)?>&chls=2,4,0&chdl=New+Registrations|Disabled+Users&chf=bg,s,FFFFFF00" alt="User Flow Chart" /> + <img src="https://chart.googleapis.com/chart?cht=lc&chs=880x160&chco=000D99,99000D&chg=0,-1,1,1&chxt=y,x&chxs=0,h&chxl=1:|<?=implode('|', $Labels)?>&chxr=0,0,<?=$Max?>&chd=t:<?=implode(',', $InFlow)?>|<?=implode(',', $OutFlow)?>&chls=2,4,0&chdl=New+Registrations|Disabled+Users&chf=bg,s,FFFFFF00" alt="User Flow Chart" /> </div> <br /> <h1 id="User_Classes"><a href="#User_Classes">User Classes</a></h1> <div class="box pad center"> - <img src="<?=$ClassDistribution?>" alt="User Class Distribution" /> + <img src="<?=$ClassDistribution?>" alt="User Class Distribution" /> </div> <br /> <h1 id="User_Platforms"><a href="#User_Platforms">User Platforms</a></h1> <div class="box pad center"> - <img src="<?=$PlatformDistribution?>" alt="User Platform Distribution" /> + <img src="<?=$PlatformDistribution?>" alt="User Platform Distribution" /> </div> <br /> <h1 id="User_Browsers"><a href="#User_Browsers">User Browsers</a></h1> <div class="box pad center"> - <img src="<?=$BrowserDistribution?>" alt="User Browsers Market Share" /> + <img src="<?=$BrowserDistribution?>" alt="User Browsers Market Share" /> </div> <br /> <h1 id="Geo_Dist_Map"><a href="#Geo_Dist_Map">Geographical Distribution Map</a></h1> <div class="box center"> - <img src="https://chart.googleapis.com/chart?cht=map:fixed=-55,-180,73,180&chs=440x220&chd=t:<?=implode(',', $Rank)?>&chco=FFFFFF,EDEDED,1F0066&chld=<?=implode('|', $Countries)?>&chf=bg,s,CCD6FF" alt="Geographical Distribution Map - Worldwide" /> - <img src="https://chart.googleapis.com/chart?cht=map:fixed=37,-26,65,67&chs=440x220&chs=440x220&chd=t:<?=implode(',', $Rank)?>&chco=FFFFFF,EDEDED,1F0066&chld=<?=implode('|', $Countries)?>&chf=bg,s,CCD6FF" alt="Geographical Distribution Map - Europe" /> - <br /> - <img src="https://chart.googleapis.com/chart?cht=map:fixed=-46,-132,24,21.5&chs=440x220&chd=t:<?=implode(',', $Rank)?>&chco=FFFFFF,EDEDED,1F0066&chld=<?=implode('|', $Countries)?>&chf=bg,s,CCD6FF" alt="Geographical Distribution Map - South America" /> - <img src="https://chart.googleapis.com/chart?cht=map:fixed=-11,22,50,160&chs=440x220&chd=t:<?=implode(',', $Rank)?>&chco=FFFFFF,EDEDED,1F0066&chld=<?=implode('|', $Countries)?>&chf=bg,s,CCD6FF" alt="Geographical Distribution Map - Asia" /> - <br /> - <img src="https://chart.googleapis.com/chart?cht=map:fixed=-36,-57,37,100&chs=440x220&chd=t:<?=implode(',', $Rank)?>&chco=FFFFFF,EDEDED,1F0066&chld=<?=implode('|', $Countries)?>&chf=bg,s,CCD6FF" alt="Geographical Distribution Map - Africa" /> - <img src="https://chart.googleapis.com/chart?cht=map:fixed=14.8,15,45,86&chs=440x220&chd=t:<?=implode(',', $Rank)?>&chco=FFFFFF,EDEDED,1F0066&chld=<?=implode('|', $Countries)?>&chf=bg,s,CCD6FF" alt="Geographical Distribution Map - Middle East" /> - <br /> - <img src="https://chart.googleapis.com/chart?chxt=y,x&chg=0,-1,1,1&chxs=0,h&cht=bvs&chco=76A4FB&chs=880x300&chd=t:<?=implode(',', array_slice($CountryUsers, 0, 31))?>&chxl=1:|<?=implode('|', array_slice($Countries, 0, 31))?>|0:|<?=implode('|', $LogIncrements)?>&chf=bg,s,FFFFFF00" alt="Number of users by country" /> + <img src="https://chart.googleapis.com/chart?cht=map:fixed=-55,-180,73,180&chs=440x220&chd=t:<?=implode(',', $Rank)?>&chco=FFFFFF,EDEDED,1F0066&chld=<?=implode('|', $Countries)?>&chf=bg,s,CCD6FF" alt="Geographical Distribution Map - Worldwide" /> + <img src="https://chart.googleapis.com/chart?cht=map:fixed=37,-26,65,67&chs=440x220&chs=440x220&chd=t:<?=implode(',', $Rank)?>&chco=FFFFFF,EDEDED,1F0066&chld=<?=implode('|', $Countries)?>&chf=bg,s,CCD6FF" alt="Geographical Distribution Map - Europe" /> + <br /> + <img src="https://chart.googleapis.com/chart?cht=map:fixed=-46,-132,24,21.5&chs=440x220&chd=t:<?=implode(',', $Rank)?>&chco=FFFFFF,EDEDED,1F0066&chld=<?=implode('|', $Countries)?>&chf=bg,s,CCD6FF" alt="Geographical Distribution Map - South America" /> + <img src="https://chart.googleapis.com/chart?cht=map:fixed=-11,22,50,160&chs=440x220&chd=t:<?=implode(',', $Rank)?>&chco=FFFFFF,EDEDED,1F0066&chld=<?=implode('|', $Countries)?>&chf=bg,s,CCD6FF" alt="Geographical Distribution Map - Asia" /> + <br /> + <img src="https://chart.googleapis.com/chart?cht=map:fixed=-36,-57,37,100&chs=440x220&chd=t:<?=implode(',', $Rank)?>&chco=FFFFFF,EDEDED,1F0066&chld=<?=implode('|', $Countries)?>&chf=bg,s,CCD6FF" alt="Geographical Distribution Map - Africa" /> + <img src="https://chart.googleapis.com/chart?cht=map:fixed=14.8,15,45,86&chs=440x220&chd=t:<?=implode(',', $Rank)?>&chco=FFFFFF,EDEDED,1F0066&chld=<?=implode('|', $Countries)?>&chf=bg,s,CCD6FF" alt="Geographical Distribution Map - Middle East" /> + <br /> + <img src="https://chart.googleapis.com/chart?chxt=y,x&chg=0,-1,1,1&chxs=0,h&cht=bvs&chco=76A4FB&chs=880x300&chd=t:<?=implode(',', array_slice($CountryUsers, 0, 31))?>&chxl=1:|<?=implode('|', array_slice($Countries, 0, 31))?>|0:|<?=implode('|', $LogIncrements)?>&chf=bg,s,FFFFFF00" alt="Number of users by country" /> </div> -<? +<?php View::show_footer(); diff --git a/sections/tools/data/database_specifics.php b/sections/tools/data/database_specifics.php index c81bdf9df..719dadf8f 100644 --- a/sections/tools/data/database_specifics.php +++ b/sections/tools/data/database_specifics.php @@ -1,83 +1,83 @@ -<? +<?php if (!check_perms('site_database_specifics')) { - error(403); + error(403); } //View schemas if (!empty($_GET['table'])) { - $DB->query('SHOW TABLES'); - $Tables =$DB->collect('Tables_in_'.SQLDB); - if (!in_array($_GET['table'], $Tables)) { - error(0); - } - $DB->query('SHOW CREATE TABLE '.db_string($_GET['table'])); - list(,$Schema) = $DB->next_record(MYSQLI_NUM, false); - header('Content-type: text/plain'); - die($Schema); + $DB->query('SHOW TABLES'); + $Tables =$DB->collect('Tables_in_'.SQLDB); + if (!in_array($_GET['table'], $Tables)) { + error(0); + } + $DB->query('SHOW CREATE TABLE '.db_string($_GET['table'])); + list(,$Schema) = $DB->next_record(MYSQLI_NUM, false); + header('Content-type: text/plain'); + die($Schema); } //Cache the tables for 4 hours, makes sorting faster if (!$Tables = $Cache->get_value('database_table_stats')) { - $DB->query('SHOW TABLE STATUS'); - $Tables =$DB->to_array(); - $Cache->cache_value('database_table_stats', $Tables, 3600 * 4); + $DB->query('SHOW TABLE STATUS'); + $Tables =$DB->to_array(); + $Cache->cache_value('database_table_stats', $Tables, 3600 * 4); } require(SERVER_ROOT.'/classes/charts.class.php'); $Pie = new PIE_CHART(750,400,array('Other'=>1,'Percentage'=>1,'Sort'=>1)); //Begin sorting -$Sort = array(); +$Sort = []; switch (empty($_GET['order_by']) ? '' : $_GET['order_by']) { - case 'name': - foreach ($Tables as $Key => $Value) { - $Pie->add($Value[0], $Value[6] + $Value[8]); - $Sort[$Key] = $Value[0]; - } - break; - case 'engine': - foreach ($Tables as $Key => $Value) { - $Pie->add($Value[0], $Value[6] + $Value[8]); - $Sort[$Key] = $Value[1]; - } - break; - case 'rows': - foreach ($Tables as $Key => $Value) { - $Pie->add($Value[0], $Value[4]); - $Sort[$Key] = $Value[4]; - } - break; - case 'rowsize': - foreach ($Tables as $Key => $Value) { - $Pie->add($Value[0], $Value[5]); - $Sort[$Key] = $Value[5]; - } - break; - case 'datasize': - foreach ($Tables as $Key => $Value) { - $Pie->add($Value[0], $Value[6]); - $Sort[$Key] = $Value[6]; - } - break; - case 'indexsize': - foreach ($Tables as $Key => $Value) { - $Pie->add($Value[0], $Value[8]); - $Sort[$Key] = $Value[8]; - } - break; - case 'totalsize': - default: - foreach ($Tables as $Key => $Value) { - $Pie->add($Value[0], $Value[6] + $Value[8]); - $Sort[$Key] = $Value[6] + $Value[8]; - } + case 'name': + foreach ($Tables as $Key => $Value) { + $Pie->add($Value[0], $Value[6] + $Value[8]); + $Sort[$Key] = $Value[0]; + } + break; + case 'engine': + foreach ($Tables as $Key => $Value) { + $Pie->add($Value[0], $Value[6] + $Value[8]); + $Sort[$Key] = $Value[1]; + } + break; + case 'rows': + foreach ($Tables as $Key => $Value) { + $Pie->add($Value[0], $Value[4]); + $Sort[$Key] = $Value[4]; + } + break; + case 'rowsize': + foreach ($Tables as $Key => $Value) { + $Pie->add($Value[0], $Value[5]); + $Sort[$Key] = $Value[5]; + } + break; + case 'datasize': + foreach ($Tables as $Key => $Value) { + $Pie->add($Value[0], $Value[6]); + $Sort[$Key] = $Value[6]; + } + break; + case 'indexsize': + foreach ($Tables as $Key => $Value) { + $Pie->add($Value[0], $Value[8]); + $Sort[$Key] = $Value[8]; + } + break; + case 'totalsize': + default: + foreach ($Tables as $Key => $Value) { + $Pie->add($Value[0], $Value[6] + $Value[8]); + $Sort[$Key] = $Value[6] + $Value[8]; + } } $Pie->generate(); if (!empty ($_GET['order_way']) && $_GET['order_way'] == 'asc') { - $SortWay = SORT_ASC; + $SortWay = SORT_ASC; } else { - $SortWay = SORT_DESC; + $SortWay = SORT_DESC; } array_multisort($Sort, $SortWay, $Tables); @@ -87,56 +87,56 @@ ?> <h3>Breakdown</h3> <div class="box pad center"> - <img src="<?=$Pie->url()?>" /> + <img src="<?=$Pie->url()?>" /> </div> <br /> <table> - <tr class="colhead"> - <td><a href="tools.php?action=database_specifics&order_by=name&order_way=<?=(!empty($_GET['order_by']) && $_GET['order_by'] == 'name' && !empty($_GET['order_way']) && $_GET['order_way'] == 'desc') ? 'asc' : 'desc'?>">Name</a></td> - <td><a href="tools.php?action=database_specifics&order_by=engine&order_way=<?=(!empty($_GET['order_by']) && $_GET['order_by'] == 'engine' && !empty($_GET['order_way']) && $_GET['order_way'] == 'desc') ? 'asc' : 'desc'?>">Engine</a></td> - <td><a href="tools.php?action=database_specifics&order_by=rows&order_way=<?=(!empty($_GET['order_by']) && $_GET['order_by'] == 'rows' && !empty($_GET['order_way']) && $_GET['order_way'] == 'desc') ? 'asc' : 'desc'?>">Rows</td> - <td><a href="tools.php?action=database_specifics&order_by=rowsize&order_way=<?=(!empty($_GET['order_by']) && $_GET['order_by'] == 'rowsize' && !empty($_GET['order_way']) && $_GET['order_way'] == 'desc') ? 'asc' : 'desc'?>">Row Size</a></td> - <td><a href="tools.php?action=database_specifics&order_by=datasize&order_way=<?=(!empty($_GET['order_by']) && $_GET['order_by'] == 'datasize' && !empty($_GET['order_way']) && $_GET['order_way'] == 'desc') ? 'asc' : 'desc'?>">Data Size</a></td> - <td><a href="tools.php?action=database_specifics&order_by=indexsize&order_way=<?=(!empty($_GET['order_by']) && $_GET['order_by'] == 'indexsize' && !empty($_GET['order_way']) && $_GET['order_way'] == 'desc') ? 'asc' : 'desc'?>">Index Size</a></td> - <td><a href="tools.php?action=database_specifics&order_by=totalsize&order_way=<?=(!empty($_GET['order_by']) && $_GET['order_by'] == 'totalsize' && !empty($_GET['order_way']) && $_GET['order_way'] == 'desc') ? 'asc' : 'desc'?>">Total Size</td> - <td>Tools</td> - </tr> -<? + <tr class="colhead"> + <td><a href="tools.php?action=database_specifics&order_by=name&order_way=<?=(!empty($_GET['order_by']) && $_GET['order_by'] == 'name' && !empty($_GET['order_way']) && $_GET['order_way'] == 'desc') ? 'asc' : 'desc'?>">Name</a></td> + <td><a href="tools.php?action=database_specifics&order_by=engine&order_way=<?=(!empty($_GET['order_by']) && $_GET['order_by'] == 'engine' && !empty($_GET['order_way']) && $_GET['order_way'] == 'desc') ? 'asc' : 'desc'?>">Engine</a></td> + <td><a href="tools.php?action=database_specifics&order_by=rows&order_way=<?=(!empty($_GET['order_by']) && $_GET['order_by'] == 'rows' && !empty($_GET['order_way']) && $_GET['order_way'] == 'desc') ? 'asc' : 'desc'?>">Rows</td> + <td><a href="tools.php?action=database_specifics&order_by=rowsize&order_way=<?=(!empty($_GET['order_by']) && $_GET['order_by'] == 'rowsize' && !empty($_GET['order_way']) && $_GET['order_way'] == 'desc') ? 'asc' : 'desc'?>">Row Size</a></td> + <td><a href="tools.php?action=database_specifics&order_by=datasize&order_way=<?=(!empty($_GET['order_by']) && $_GET['order_by'] == 'datasize' && !empty($_GET['order_way']) && $_GET['order_way'] == 'desc') ? 'asc' : 'desc'?>">Data Size</a></td> + <td><a href="tools.php?action=database_specifics&order_by=indexsize&order_way=<?=(!empty($_GET['order_by']) && $_GET['order_by'] == 'indexsize' && !empty($_GET['order_way']) && $_GET['order_way'] == 'desc') ? 'asc' : 'desc'?>">Index Size</a></td> + <td><a href="tools.php?action=database_specifics&order_by=totalsize&order_way=<?=(!empty($_GET['order_by']) && $_GET['order_by'] == 'totalsize' && !empty($_GET['order_way']) && $_GET['order_way'] == 'desc') ? 'asc' : 'desc'?>">Total Size</td> + <td>Tools</td> + </tr> +<?php $TotalRows = 0; $TotalDataSize = 0; $TotalIndexSize = 0; $Row = 'a'; foreach ($Tables as $Table) { - list($Name,$Engine,,,$Rows,$RowSize,$DataSize,,$IndexSize) = $Table; - $Row = $Row === 'a' ? 'b' : 'a'; + list($Name,$Engine,,,$Rows,$RowSize,$DataSize,,$IndexSize) = $Table; + $Row = $Row === 'a' ? 'b' : 'a'; - $TotalRows += $Rows; - $TotalDataSize += $DataSize; - $TotalIndexSize += $IndexSize; + $TotalRows += $Rows; + $TotalDataSize += $DataSize; + $TotalIndexSize += $IndexSize; ?> - <tr class="row<?=$Row?>"> - <td><?=display_str($Name)?></td> - <td><?=display_str($Engine)?></td> - <td><?=number_format($Rows)?></td> - <td><?=Format::get_size($RowSize)?></td> - <td><?=Format::get_size($DataSize)?></td> - <td><?=Format::get_size($IndexSize)?></td> - <td><?=Format::get_size($DataSize + $IndexSize)?></td> - <td><a href="tools.php?action=database_specifics&table=<?=display_str($Name)?>" class="brackets">Schema</a></td> - </tr> -<? + <tr class="row<?=$Row?>"> + <td><?=display_str($Name)?></td> + <td><?=display_str($Engine)?></td> + <td><?=number_format($Rows)?></td> + <td><?=Format::get_size($RowSize)?></td> + <td><?=Format::get_size($DataSize)?></td> + <td><?=Format::get_size($IndexSize)?></td> + <td><?=Format::get_size($DataSize + $IndexSize)?></td> + <td><a href="tools.php?action=database_specifics&table=<?=display_str($Name)?>" class="brackets">Schema</a></td> + </tr> +<?php } ?> - <tr> - <td></td> - <td></td> - <td><?=number_format($TotalRows)?></td> - <td></td> - <td><?=Format::get_size($TotalDataSize)?></td> - <td><?=Format::get_size($TotalIndexSize)?></td> - <td><?=Format::get_size($TotalDataSize + $TotalIndexSize)?></td> - <td></td> - </tr> + <tr> + <td></td> + <td></td> + <td><?=number_format($TotalRows)?></td> + <td></td> + <td><?=Format::get_size($TotalDataSize)?></td> + <td><?=Format::get_size($TotalIndexSize)?></td> + <td><?=Format::get_size($TotalDataSize + $TotalIndexSize)?></td> + <td></td> + </tr> </table> -<? +<?php View::show_footer(); diff --git a/sections/tools/data/economic_stats.php b/sections/tools/data/economic_stats.php index 21aeebde2..b8856227a 100644 --- a/sections/tools/data/economic_stats.php +++ b/sections/tools/data/economic_stats.php @@ -1,147 +1,149 @@ -<? +<?php /* Tools necessary for economic management 1. Current overall stats (!economy) 2. Statistical traffic trends in a graph - a. All time / 1 year (whichever is smaller) - b. 1 month - c. 1 week - d. 1 day + a. All time / 1 year (whichever is smaller) + b. 1 month + c. 1 week + d. 1 day 3. Freeleech analysis - a. total download average during freeleech vs. normal conditions - b. total stats of a freeleech - uploaded torrents, upload amount, download amount, snatches, etc. + a. total download average during freeleech vs. normal conditions + b. total stats of a freeleech - uploaded torrents, upload amount, download amount, snatches, etc. 4. Traffic trends over an account's life, on average - a. at one week, one month, whatever (selectable range in weeks) - averages (up/down/ratio) - b. given a selected timespan, average ratio (users who are 4-5 months old have X ratio) - c. average date at which >50% of accounts with ratios >1 reach 1.0 and never dip below, stockpiling buffer + a. at one week, one month, whatever (selectable range in weeks) - averages (up/down/ratio) + b. given a selected timespan, average ratio (users who are 4-5 months old have X ratio) + c. average date at which >50% of accounts with ratios >1 reach 1.0 and never dip below, stockpiling buffer 5. Raw numbers - a. total torrents, seeders, leechers - b. average seeds/leechs per torrent - c. average snatches/user - d. average seeding torrents/user - e. users on ratio watch + a. total torrents, seeders, leechers + b. average seeds/leechs per torrent + c. average snatches/user + d. average seeding torrents/user + e. users on ratio watch 6. Distribution graph of seedership vs. torrent percentage - a. graph showing that the top 1% of torrents has 50% of seeders or whatever the numbers might be + a. graph showing that the top 1% of torrents has 50% of seeders or whatever the numbers might be 7. Effects of economic changes - a. number of users changed by ratio being changed - b. project effects with intelligent mathematical analysis of a 24, 48 or 72 hour freeleech + a. number of users changed by ratio being changed + b. project effects with intelligent mathematical analysis of a 24, 48 or 72 hour freeleech */ if (!check_perms('site_view_flow')) { - error(403); + error(403); } View::show_header('Economy'); if (!$EconomicStats = $Cache->get_value('new_economic_stats')) { - $DB->query(" - SELECT SUM(Uploaded), SUM(Downloaded), COUNT(ID) - FROM users_main - WHERE Enabled = '1'"); - list($TotalUpload, $TotalDownload, $NumUsers) = $DB->next_record(); - $DB->query(" - SELECT SUM(Bounty) - FROM requests_votes"); - list($TotalBounty) = $DB->next_record(); - $DB->query(" - SELECT SUM(rv.Bounty) - FROM requests_votes AS rv - JOIN requests AS r ON r.ID = rv.RequestID - WHERE TorrentID > 0"); - list($AvailableBounty) = $DB->next_record(); - $DB->query(" - SELECT SUM(Snatched), COUNT(ID) - FROM torrents"); - list($TotalSnatches, $TotalTorrents) = $DB->next_record(); // This is the total number of snatches for torrents that still exist + $DB->query(" + SELECT sum(uls.Uploaded), sum(uls.Downloaded), count(*) + FROM users_main um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + WHERE um.Enabled = '1'"); + list($TotalUpload, $TotalDownload, $NumUsers) = $DB->next_record(); + $DB->query(" + SELECT SUM(Bounty) + FROM requests_votes"); + list($TotalBounty) = $DB->next_record(); + $DB->query(" + SELECT SUM(rv.Bounty) + FROM requests_votes AS rv + JOIN requests AS r ON r.ID = rv.RequestID + WHERE TorrentID > 0"); + list($AvailableBounty) = $DB->next_record(); + $DB->query(" + SELECT sum(tls.Snatched), count(*) + FROM torrents_leech_stats tls + "); + list($TotalSnatches, $TotalTorrents) = $DB->next_record(); // This is the total number of snatches for torrents that still exist - $DB->query(" - SELECT COUNT(uid) - FROM xbt_snatched"); - list($TotalOverallSnatches) = $DB->next_record(); + $DB->query(" + SELECT count(*) + FROM xbt_snatched"); + list($TotalOverallSnatches) = $DB->next_record(); - if (($PeerStats = $Cache->get_value('stats_peers')) === false) { - $DB->query(" - SELECT COUNT(fid) - FROM xbt_files_users - WHERE remaining = 0"); - list($TotalSeeders) = $DB->next_record(); - $DB->query(" - SELECT COUNT(fid) - FROM xbt_files_users - WHERE remaining > 0"); - list($TotalLeechers) = $DB->next_record(); - } else { - list($TotalLeechers,$TotalSeeders) = $PeerStats; - } - $TotalPeers = $TotalLeechers + $TotalSeeders; - $DB->query(" - SELECT COUNT(ID) - FROM users_main - WHERE ( - SELECT COUNT(uid) - FROM xbt_files_users - WHERE uid = users_main.ID - ) > 0"); - list($TotalPeerUsers) = $DB->next_record(); - $Cache->cache_value('new_economic_stats', - array($TotalUpload, $TotalDownload, $NumUsers, $TotalBounty, - $AvailableBounty, $TotalSnatches, $TotalTorrents, - $TotalOverallSnatches, $TotalSeeders, $TotalPeers, - $TotalPeerUsers), 3600); + if (($PeerStats = $Cache->get_value('stats_peers')) === false) { + $DB->query(" + SELECT count(*) + FROM xbt_files_users + WHERE remaining = 0"); + list($TotalSeeders) = $DB->next_record(); + $DB->query(" + SELECT count(*) + FROM xbt_files_users + WHERE remaining > 0"); + list($TotalLeechers) = $DB->next_record(); + } else { + list($TotalLeechers,$TotalSeeders) = $PeerStats; + } + $TotalPeers = $TotalLeechers + $TotalSeeders; + $DB->query(" + SELECT COUNT(ID) + FROM users_main + WHERE ( + SELECT count(*) + FROM xbt_files_users + WHERE uid = users_main.ID + ) > 0"); + list($TotalPeerUsers) = $DB->next_record(); + $Cache->cache_value('new_economic_stats', + array($TotalUpload, $TotalDownload, $NumUsers, $TotalBounty, + $AvailableBounty, $TotalSnatches, $TotalTorrents, + $TotalOverallSnatches, $TotalSeeders, $TotalPeers, + $TotalPeerUsers), 3600); } else { - list($TotalUpload, $TotalDownload, $NumUsers, $TotalBounty, $AvailableBounty, - $TotalSnatches, $TotalTorrents, $TotalOverallSnatches, $TotalSeeders, - $TotalPeers, $TotalPeerUsers) = $EconomicStats; + list($TotalUpload, $TotalDownload, $NumUsers, $TotalBounty, $AvailableBounty, + $TotalSnatches, $TotalTorrents, $TotalOverallSnatches, $TotalSeeders, + $TotalPeers, $TotalPeerUsers) = $EconomicStats; } $TotalLeechers = $TotalPeers - $TotalSeeders; ?> <div class="thin"> - <div class="box"> - <div class="head">Overall stats</div> - <div class="pad"> - <ul class="stats nobullet"> - <li><strong>Total upload: </strong><?=Format::get_size($TotalUpload)?></li> - <li><strong>Total download: </strong><?=Format::get_size($TotalDownload)?></li> - <li><strong>Total buffer: </strong><?=Format::get_size($TotalUpload - $TotalDownload)?></li> - <br /> - <li><strong>Mean ratio: </strong><?=Format::get_ratio_html($TotalUpload, $TotalDownload)?></li> - <li><strong>Mean upload: </strong><?=Format::get_size($TotalUpload / $NumUsers)?></li> - <li><strong>Mean download: </strong><?=Format::get_size($TotalDownload / $NumUsers)?></li> - <li><strong>Mean buffer: </strong><?=Format::get_size(($TotalUpload - $TotalDownload) / $NumUsers)?></li> - <br /> - <li><strong>Total request bounty: </strong><?=Format::get_size($TotalBounty)?></li> - <li><strong>Available request bounty: </strong><?=Format::get_size($AvailableBounty)?></li> - </ul> - </div> - </div> - <br /> - <div class="box"> - <div class="head">Swarms and snatches</div> - <div class="pad"> - <ul class="stats nobullet"> - <li><strong>Total seeders: </strong><?=number_format($TotalSeeders)?></li> - <li><strong>Total leechers: </strong><?=number_format($TotalLeechers)?></li> - <li><strong>Total peers: </strong><?=number_format($TotalSeeders + $TotalLeechers)?></li> - <li><strong>Total snatches: </strong><?=number_format($TotalOverallSnatches)?></li> - <li><strong>Seeder/leecher ratio: </strong><?=Format::get_ratio_html($TotalSeeders, $TotalLeechers)?></li> - <li><strong>Seeder/snatch ratio: </strong><?=Format::get_ratio_html($TotalSeeders, $TotalOverallSnatches)?></li> - <br /> - <li><strong>Mean seeders per torrent: </strong><?=number_format($TotalSeeders / $TotalTorrents, 2)?></li> - <li><strong>Mean leechers per torrent: </strong><?=number_format($TotalLeechers / $TotalTorrents, 2)?></li> - <li><strong>Mean snatches per torrent: </strong><?=number_format($TotalSnatches / $TotalTorrents, 2)?></li> - <br /> - <li><strong>Mean seeding per user: </strong><?=number_format($TotalSeeders / $NumUsers, 2)?></li> - <li><strong>Mean leeching per user: </strong><?=number_format($TotalLeechers / $NumUsers, 2)?></li> - <li><strong>Mean snatches per user: </strong><?=number_format($TotalOverallSnatches / $NumUsers, 2)?></li> - <br /> - <li><strong>Total users in at least 1 swarm: </strong><?=number_format($TotalPeerUsers)?></li> - <li><strong>Mean seeding per user in at least 1 swarm: </strong><?=number_format($TotalSeeders / $TotalPeerUsers, 2)?></li> - <li><strong>Mean leeching per user in at least 1 swarm: </strong><?=number_format($TotalLeechers / $TotalPeerUsers, 2)?></li> - <li><strong>Mean snatches per user in at least 1 swarm: </strong><?=number_format($TotalSnatches / $TotalPeerUsers, 2)?></li> - </ul> - </div> - </div> + <div class="box"> + <div class="head">Overall stats</div> + <div class="pad"> + <ul class="stats nobullet"> + <li><strong>Total upload: </strong><?=Format::get_size($TotalUpload)?></li> + <li><strong>Total download: </strong><?=Format::get_size($TotalDownload)?></li> + <li><strong>Total buffer: </strong><?=Format::get_size($TotalUpload - $TotalDownload)?></li> + <br /> + <li><strong>Mean ratio: </strong><?=Format::get_ratio_html($TotalUpload, $TotalDownload)?></li> + <li><strong>Mean upload: </strong><?=Format::get_size($TotalUpload / $NumUsers)?></li> + <li><strong>Mean download: </strong><?=Format::get_size($TotalDownload / $NumUsers)?></li> + <li><strong>Mean buffer: </strong><?=Format::get_size(($TotalUpload - $TotalDownload) / $NumUsers)?></li> + <br /> + <li><strong>Total request bounty: </strong><?=Format::get_size($TotalBounty)?></li> + <li><strong>Available request bounty: </strong><?=Format::get_size($AvailableBounty)?></li> + </ul> + </div> + </div> + <br /> + <div class="box"> + <div class="head">Swarms and snatches</div> + <div class="pad"> + <ul class="stats nobullet"> + <li><strong>Total seeders: </strong><?=number_format($TotalSeeders)?></li> + <li><strong>Total leechers: </strong><?=number_format($TotalLeechers)?></li> + <li><strong>Total peers: </strong><?=number_format($TotalSeeders + $TotalLeechers)?></li> + <li><strong>Total snatches: </strong><?=number_format($TotalOverallSnatches)?></li> + <li><strong>Seeder/leecher ratio: </strong><?=Format::get_ratio_html($TotalSeeders, $TotalLeechers)?></li> + <li><strong>Seeder/snatch ratio: </strong><?=Format::get_ratio_html($TotalSeeders, $TotalOverallSnatches)?></li> + <br /> + <li><strong>Mean seeders per torrent: </strong><?=number_format($TotalSeeders / $TotalTorrents, 2)?></li> + <li><strong>Mean leechers per torrent: </strong><?=number_format($TotalLeechers / $TotalTorrents, 2)?></li> + <li><strong>Mean snatches per torrent: </strong><?=number_format($TotalSnatches / $TotalTorrents, 2)?></li> + <br /> + <li><strong>Mean seeding per user: </strong><?=number_format($TotalSeeders / $NumUsers, 2)?></li> + <li><strong>Mean leeching per user: </strong><?=number_format($TotalLeechers / $NumUsers, 2)?></li> + <li><strong>Mean snatches per user: </strong><?=number_format($TotalOverallSnatches / $NumUsers, 2)?></li> + <br /> + <li><strong>Total users in at least 1 swarm: </strong><?=number_format($TotalPeerUsers)?></li> + <li><strong>Mean seeding per user in at least 1 swarm: </strong><?=number_format($TotalSeeders / $TotalPeerUsers, 2)?></li> + <li><strong>Mean leeching per user in at least 1 swarm: </strong><?=number_format($TotalLeechers / $TotalPeerUsers, 2)?></li> + <li><strong>Mean snatches per user in at least 1 swarm: </strong><?=number_format($TotalSnatches / $TotalPeerUsers, 2)?></li> + </ul> + </div> + </div> </div> -<? +<?php View::show_footer(); ?> diff --git a/sections/tools/data/invite_pool.php b/sections/tools/data/invite_pool.php index a639277cb..6998ae2c9 100644 --- a/sections/tools/data/invite_pool.php +++ b/sections/tools/data/invite_pool.php @@ -1,6 +1,6 @@ -<? +<?php if (!check_perms('users_view_invites')) { - error(403); + error(403); } $Title = 'Invite Pool'; View::show_header($Title); @@ -8,36 +8,36 @@ list($Page, $Limit) = Format::page_limit(INVITES_PER_PAGE); if (!empty($_POST['invitekey']) && check_perms('users_edit_invites')) { - authorize(); + authorize(); - $DB->query(" - DELETE FROM invites - WHERE InviteKey = '".db_string($_POST['invitekey'])."'"); + $DB->query(" + DELETE FROM invites + WHERE InviteKey = '".db_string($_POST['invitekey'])."'"); } if (!empty($_GET['search'])) { - $Search = db_string($_GET['search']); + $Search = db_string($_GET['search']); } else { - $Search = ''; + $Search = ''; } $sql = " - SELECT - SQL_CALC_FOUND_ROWS - um.ID, - um.IP, - i.InviteKey, - i.Expires, - i.Email - FROM invites AS i - JOIN users_main AS um ON um.ID = i.InviterID "; + SELECT + SQL_CALC_FOUND_ROWS + um.ID, + um.IP, + i.InviteKey, + i.Expires, + i.Email + FROM invites AS i + JOIN users_main AS um ON um.ID = i.InviterID "; if ($Search) { - $sql .= " - WHERE i.Email LIKE '%$Search%' "; + $sql .= " + WHERE i.Email LIKE '%$Search%' "; } $sql .= " - ORDER BY i.Expires DESC - LIMIT $Limit"; + ORDER BY i.Expires DESC + LIMIT $Limit"; $RS = $DB->query($sql); $DB->query('SELECT FOUND_ROWS()'); @@ -45,70 +45,75 @@ $DB->set_query_id($RS); ?> - <div class="header"> - <h2><?=$Title?></h2> - </div> - <div class="box pad"> - <p><?=number_format($Results)?> unused invites have been sent.</p> - </div> - <br /> - <div> - <form class="search_form" name="invites" action="" method="get"> - <table cellpadding="6" cellspacing="1" border="0" class="layout border" width="100%"> - <tr> - <td class="label"><strong>Email address:</strong></td> - <td> - <input type="hidden" name="action" value="invite_pool" /> - <input type="email" name="search" size="60" value="<?=display_str($Search)?>" /> -   - <input type="submit" value="Search log" /> - </td> - </tr> - </table> - </form> - </div> - <div class="linkbox"> -<? - $Pages = Format::get_pages($Page, $Results, INVITES_PER_PAGE, 11) ; - echo $Pages; + <div class="header"> + <h2><?=$Title?></h2> + </div> + <div class="box pad"> + <p><?=number_format($Results)?> unused invites have been sent.</p> + </div> + <br /> + <div> + <form class="search_form" name="invites" action="" method="get"> + <table cellpadding="6" cellspacing="1" border="0" class="layout border" width="100%"> + <tr> + <td class="label"><strong>Email address:</strong></td> + <td> + <input type="hidden" name="action" value="invite_pool" /> + <input type="email" name="search" size="60" value="<?=display_str($Search)?>" /> +   + <input type="submit" value="Search log" /> + </td> + </tr> + </table> + </form> + </div> + <div class="linkbox"> +<?php + $Pages = Format::get_pages($Page, $Results, INVITES_PER_PAGE, 11) ; + echo $Pages; ?> - </div> - <table width="100%"> - <tr class="colhead"> - <td>Inviter</td> - <td>Email address</td> - <td>IP address</td> - <td>InviteCode</td> - <td>Expires</td> -<? if (check_perms('users_edit_invites')) { ?> - <td>Controls</td> -<? } ?> - </tr> -<? - $Row = 'b'; - while (list($UserID, $IP, $InviteKey, $Expires, $Email) = $DB->next_record()) { - $Row = $Row === 'b' ? 'a' : 'b'; + </div> + <table width="100%"> + <tr class="colhead"> + <td>Inviter</td> + <td>Email address</td> + <td>IP address</td> + <td>InviteCode</td> + <td>Expires</td> +<?php + if (check_perms('users_edit_invites')) { ?> + <td>Controls</td> +<?php + } ?> + </tr> +<?php + $Row = 'b'; + while (list($UserID, $IP, $InviteKey, $Expires, $Email) = $DB->next_record()) { + $Row = $Row === 'b' ? 'a' : 'b'; ?> - <tr class="row<?=$Row?>"> - <td><?=Users::format_username($UserID, true, true, true, true)?></td> - <td><?=display_str($Email)?></td> - <td><?=Tools::display_ip($IP)?></td> - <td><?=display_str($InviteKey)?></td> - <td><?=time_diff($Expires)?></td> -<? if (check_perms('users_edit_invites')) { ?> - <td> - <form class="delete_form" name="invite" action="" method="post"> - <input type="hidden" name="action" value="invite_pool" /> - <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> - <input type="hidden" name="invitekey" value="<?=display_str($InviteKey)?>" /> - <input type="submit" value="Delete" /> - </form> - </td> -<? } ?> - </tr> -<? } ?> - </table> -<? if ($Pages) { ?> - <div class="linkbox pager"><?=($Pages)?></div> -<? } + <tr class="row<?=$Row?>"> + <td><?=Users::format_username($UserID, true, true, true, true)?></td> + <td><?=display_str($Email)?></td> + <td><?=Tools::display_ip($IP)?></td> + <td><?=display_str($InviteKey)?></td> + <td><?=time_diff($Expires)?></td> +<?php if (check_perms('users_edit_invites')) { ?> + <td> + <form class="delete_form" name="invite" action="" method="post"> + <input type="hidden" name="action" value="invite_pool" /> + <input type="hidden" name="auth" value="<?=$LoggedUser['AuthKey']?>" /> + <input type="hidden" name="invitekey" value="<?=display_str($InviteKey)?>" /> + <input type="submit" value="Delete" /> + </form> + </td> +<?php } ?> + </tr> +<?php + } ?> + </table> +<?php + if ($Pages) { ?> + <div class="linkbox pager"><?=($Pages)?></div> +<?php + } View::show_footer(); ?> diff --git a/sections/tools/data/ocelot_info.php b/sections/tools/data/ocelot_info.php index 938526707..5f4e6ca04 100644 --- a/sections/tools/data/ocelot_info.php +++ b/sections/tools/data/ocelot_info.php @@ -1,92 +1,92 @@ -<? +<?php if (!check_perms('users_mod')) { - error(403); + error(403); } if (isset($_GET['userid']) && is_number($_GET['userid'])) { - $UserHeavyInfo = Users::user_heavy_info($_GET['userid']); - if (isset($UserHeavyInfo['torrent_pass'])) { - $TorrentPass = $UserHeavyInfo['torrent_pass']; - $UserPeerStats = Tracker::user_peer_count($TorrentPass); - $UserInfo = Users::user_info($_GET['userid']); - $UserLevel = $Classes[$UserInfo['PermissionID']]['Level']; - if (!check_paranoia('leeching+', $UserInfo['Paranoia'], $UserLevel, $_GET['userid'])) { - $UserPeerStats[0] = false; - } - if (!check_paranoia('seeding+', $UserInfo['Paranoia'], $UserLevel, $_GET['userid'])) { - $UserPeerStats[1] = false; - } - } else { - $UserPeerStats = false; - } + $UserHeavyInfo = Users::user_heavy_info($_GET['userid']); + if (isset($UserHeavyInfo['torrent_pass'])) { + $TorrentPass = $UserHeavyInfo['torrent_pass']; + $UserPeerStats = Tracker::user_peer_count($TorrentPass); + $UserInfo = Users::user_info($_GET['userid']); + $UserLevel = $Classes[$UserInfo['PermissionID']]['Level']; + if (!check_paranoia('leeching+', $UserInfo['Paranoia'], $UserLevel, $_GET['userid'])) { + $UserPeerStats[0] = false; + } + if (!check_paranoia('seeding+', $UserInfo['Paranoia'], $UserLevel, $_GET['userid'])) { + $UserPeerStats[1] = false; + } + } else { + $UserPeerStats = false; + } } else { - $MainStats = Tracker::info(); + $MainStats = Tracker::info(); } View::show_header('Tracker info'); ?> <div class="thin"> - <div class="header"> - <h2>Tracker info</h2> - </div> - <div class="linkbox"> - <a href="?action=<?=$_REQUEST['action']?>" class="brackets" />Main stats</a> - </div> - <div class="sidebar"> - <div class="box box2"> - <div class="head"><strong>User stats</strong></div> - <div class="pad"> - <form method="get" action=""> - <input type="hidden" name="action" value="ocelot_info" /> - <span class="label">Get stats for user</span><br /> - <input type="text" name="userid" placeholder="User ID" value="<?Format::form('userid')?>" /> - <input type="submit" value="Go" /> - </form> - </div> - </div> - </div> - <div class="main_column"> - <div class="box box2"> - <div class="head"><strong>Numbers and such</strong></div> - <div class="pad"> -<? + <div class="header"> + <h2>Tracker info</h2> + </div> + <div class="linkbox"> + <a href="?action=<?=$_REQUEST['action']?>" class="brackets" />Main stats</a> + </div> + <div class="sidebar"> + <div class="box box2"> + <div class="head"><strong>User stats</strong></div> + <div class="pad"> + <form method="get" action=""> + <input type="hidden" name="action" value="ocelot_info" /> + <span class="label">Get stats for user</span><br /> + <input type="text" name="userid" placeholder="User ID" value="<?php Format::form('userid'); ?>" /> + <input type="submit" value="Go" /> + </form> + </div> + </div> + </div> + <div class="main_column"> + <div class="box box2"> + <div class="head"><strong>Numbers and such</strong></div> + <div class="pad"> +<?php if (!empty($UserPeerStats)) { ?> - User ID: <?=$_GET['userid']?><br /> - Leeching: <?=$UserPeerStats[0] === false ? "hidden" : number_format($UserPeerStats[0])?><br /> - Seeding: <?=$UserPeerStats[1] === false ? "hidden" : number_format($UserPeerStats[1])?><br /> -<? + User ID: <?=$_GET['userid']?><br /> + Leeching: <?=$UserPeerStats[0] === false ? "hidden" : number_format($UserPeerStats[0])?><br /> + Seeding: <?=$UserPeerStats[1] === false ? "hidden" : number_format($UserPeerStats[1])?><br /> +<?php } elseif (!empty($MainStats)) { - foreach ($MainStats as $Key => $Value) { - if (is_numeric($Value)) { - if (substr($Key, 0, 6) === "bytes ") { - $Value = Format::get_size($Value); - $Key = substr($Key, 6); - } else { - $Value = number_format($Value); - } - } + foreach ($MainStats as $Key => $Value) { + if (is_numeric($Value)) { + if (substr($Key, 0, 6) === "bytes ") { + $Value = Format::get_size($Value); + $Key = substr($Key, 6); + } else { + $Value = number_format($Value); + } + } ?> - <?="$Value $Key<br />\n"?> -<? - } + <?="$Value $Key<br />\n"?> +<?php + } } elseif (isset($TorrentPass)) { ?> - Failed to get stats for user <?=$_GET['userid']?> -<? + Failed to get stats for user <?=$_GET['userid']?> +<?php } elseif (isset($_GET['userid'])) { ?> - User <?=display_str($_GET['userid'])?> doesn't exist -<? + User <?=display_str($_GET['userid'])?> doesn't exist +<?php } else { ?> - Failed to get tracker info -<? + Failed to get tracker info +<?php } ?> - </div> - </div> - </div> + </div> + </div> + </div> </div> -<? +<?php View::show_footer(); diff --git a/sections/tools/data/platform_usage.php b/sections/tools/data/platform_usage.php index 5fb2ce82f..7c2716ae0 100644 --- a/sections/tools/data/platform_usage.php +++ b/sections/tools/data/platform_usage.php @@ -1,51 +1,51 @@ <?php if (!check_perms('site_view_flow')) { - error(403); + error(403); } View::show_header('OS and Browser Usage'); ?> <div class="header"> - <h2>OS Usage</h2> + <h2>OS Usage</h2> </div> <table width="100%"> - <tr class="colhead"> - <td>OS</td> - <td>Count</td> - </tr> + <tr class="colhead"> + <td>OS</td> + <td>Count</td> + </tr> <?php G::$DB->prepared_query("SELECT OperatingSystem, OperatingSystemVersion, COUNT(*) FROM users_sessions GROUP BY OperatingSystem, OperatingSystemVersion ORDER BY COUNT(*) DESC"); while (list($OperatingSystem, $OperatingSystemVersion, $Count) = G::$DB->fetch_record(0, 'OperatingSystem', 1, 'OperatingSystemVersion')) { - ?> - <tr> - <td><?=$OperatingSystem?> <?=$OperatingSystemVersion?></td> - <td><?=$Count?></td> - </tr> - <?php + ?> + <tr> + <td><?=$OperatingSystem?> <?=$OperatingSystemVersion?></td> + <td><?=$Count?></td> + </tr> + <?php } ?> </table> <div class="header"> - <h2>Browser Usage</h2> + <h2>Browser Usage</h2> </div> <table width="100%"> - <tr class="colhead"> - <td>Browser</td> - <td>Count</td> - </tr> + <tr class="colhead"> + <td>Browser</td> + <td>Count</td> + </tr> <?php G::$DB->prepared_query("SELECT Browser, BrowserVersion, COUNT(*) FROM users_sessions GROUP BY Browser, BrowserVersion ORDER BY COUNT(*) DESC"); while (list($Browser, $BrowserVersion, $Count) = G::$DB->fetch_record(0, 'Browser', 1, 'BrowserVersion')) { - ?> - <tr> - <td><?=$Browser?> <?=$BrowserVersion?></td> - <td><?=$Count?></td> - </tr> - <?php + ?> + <tr> + <td><?=$Browser?> <?=$BrowserVersion?></td> + <td><?=$Count?></td> + </tr> + <?php } ?> </table> diff --git a/sections/tools/data/registration_log.php b/sections/tools/data/registration_log.php index 86b4e216f..8c2504fcc 100644 --- a/sections/tools/data/registration_log.php +++ b/sections/tools/data/registration_log.php @@ -1,6 +1,6 @@ <?php if (!check_perms('users_view_ips') || !check_perms('users_view_email')) { - error(403); + error(403); } View::show_header('Registration log'); define('USERS_PER_PAGE', 50); @@ -10,69 +10,71 @@ $BeforeDate = $_POST['before_date']; $DateSearch = false; if (!empty($AfterDate) && !empty($BeforeDate)) { - list($Y, $M, $D) = explode('-', $AfterDate); - if (!checkdate($M, $D, $Y)) { - error('Incorrect "after" date format'); - } - list($Y, $M, $D) = explode('-', $BeforeDate); - if (!checkdate($M, $D, $Y)) { - error('Incorrect "before" date format'); - } - $AfterDate = db_string($AfterDate); - $BeforeDate = db_string($BeforeDate); - $DateSearch = true; + list($Y, $M, $D) = explode('-', $AfterDate); + if (!checkdate($M, $D, $Y)) { + error('Incorrect "after" date format'); + } + list($Y, $M, $D) = explode('-', $BeforeDate); + if (!checkdate($M, $D, $Y)) { + error('Incorrect "before" date format'); + } + $AfterDate = db_string($AfterDate); + $BeforeDate = db_string($BeforeDate); + $DateSearch = true; } $RS = " - SELECT - SQL_CALC_FOUND_ROWS - m.ID, - m.IP, - m.ipcc, - m.Email, - m.Username, - m.PermissionID, - m.Uploaded, - m.Downloaded, - m.Enabled, - i.Donor, - i.Warned, - i.JoinDate, - ( - SELECT COUNT(h1.UserID) - FROM users_history_ips AS h1 - WHERE h1.IP = m.IP - ) AS Uses, - im.ID, - im.IP, - im.ipcc, - im.Email, - im.Username, - im.PermissionID, - im.Uploaded, - im.Downloaded, - im.Enabled, - ii.Donor, - ii.Warned, - ii.JoinDate, - ( - SELECT COUNT(h2.UserID) - FROM users_history_ips AS h2 - WHERE h2.IP = im.IP - ) AS InviterUses - FROM users_main AS m - LEFT JOIN users_info AS i ON i.UserID = m.ID - LEFT JOIN users_main AS im ON i.Inviter = im.ID - LEFT JOIN users_info AS ii ON i.Inviter = ii.UserID - WHERE"; + SELECT + SQL_CALC_FOUND_ROWS + um.ID, + um.IP, + um.ipcc, + um.Email, + um.Username, + um.PermissionID, + uls.Uploaded, + uls.Downloaded, + um.Enabled, + i.Donor, + i.Warned, + i.JoinDate, + ( + SELECT COUNT(h1.UserID) + FROM users_history_ips AS h1 + WHERE h1.IP = um.IP + ) AS Uses, + im.ID, + im.IP, + im.ipcc, + im.Email, + im.Username, + im.PermissionID, + ils.Uploaded, + ils.Downloaded, + im.Enabled, + ii.Donor, + ii.Warned, + ii.JoinDate, + ( + SELECT COUNT(h2.UserID) + FROM users_history_ips AS h2 + WHERE h2.IP = im.IP + ) AS InviterUses + FROM users_main AS um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + INNER JOIN users_info AS i ON (i.UserID = um.ID) + LEFT JOIN users_main AS im ON (i.Inviter = im.ID) + LEFT JOIN users_leech_stats AS ils ON (ils.UserID = im.ID) + LEFT JOIN users_info AS ii ON (i.Inviter = ii.UserID) + WHERE"; if ($DateSearch) { - $RS .= " i.JoinDate BETWEEN '$AfterDate' AND '$BeforeDate' "; + $RS .= " i.JoinDate BETWEEN '$AfterDate' AND '$BeforeDate' "; } else { - $RS .= " i.JoinDate > '".time_minus(3600 * 24 * 3)."'"; + $RS .= " i.JoinDate > '".time_minus(3600 * 24 * 3)."'"; } $RS .= " - ORDER BY i.Joindate DESC - LIMIT $Limit"; + ORDER BY i.Joindate DESC + LIMIT $Limit"; $QueryID = $DB->query($RS); $DB->query('SELECT FOUND_ROWS()'); list($Results) = $DB->next_record(); @@ -80,73 +82,74 @@ ?> <form action="" method="post" acclass="thin box pad"> - <input type="hidden" name="action" value="registration_log" /> - Joined after: <input type="date" name="after_date" /> - Joined before: <input type="date" name="before_date" /> - <input type="submit" /> + <input type="hidden" name="action" value="registration_log" /> + Joined after: <input type="date" name="after_date" /> + Joined before: <input type="date" name="before_date" /> + <input type="submit" /> </form> -<? +<?php if ($DB->has_results()) { ?> - <div class="linkbox"> -<? - $Pages = Format::get_pages($Page, $Results, USERS_PER_PAGE, 11) ; - echo $Pages; + <div class="linkbox"> +<?php + $Pages = Format::get_pages($Page, $Results, USERS_PER_PAGE, 11) ; + echo $Pages; ?> - </div> + </div> - <table width="100%"> - <tr class="colhead"> - <td>User</td> - <td>Ratio</td> - <td>Email</td> - <td>IP address</td> - <td>Country</td> - <td>Host</td> - <td>Registered</td> - </tr> -<? - while (list($UserID, $IP, $IPCC, $Email, $Username, $PermissionID, $Uploaded, $Downloaded, $Enabled, $Donor, $Warned, $Joined, $Uses, $InviterID, $InviterIP, $InviterIPCC, $InviterEmail, $InviterUsername, $InviterPermissionID, $InviterUploaded, $InviterDownloaded, $InviterEnabled, $InviterDonor, $InviterWarned, $InviterJoined, $InviterUses) = $DB->next_record()) { - $Row = $IP === $InviterIP ? 'a' : 'b'; + <table width="100%"> + <tr class="colhead"> + <td>User</td> + <td>Ratio</td> + <td>Email</td> + <td>IP address</td> + <td>Country</td> + <td>Host</td> + <td>Registered</td> + </tr> +<?php + while (list($UserID, $IP, $IPCC, $Email, $Username, $PermissionID, $Uploaded, $Downloaded, $Enabled, $Donor, $Warned, $Joined, $Uses, $InviterID, $InviterIP, $InviterIPCC, $InviterEmail, $InviterUsername, $InviterPermissionID, $InviterUploaded, $InviterDownloaded, $InviterEnabled, $InviterDonor, $InviterWarned, $InviterJoined, $InviterUses) = $DB->next_record()) { + $Row = $IP === $InviterIP ? 'a' : 'b'; ?> - <tr class="row<?=$Row?>"> - <td><?=Users::format_username($UserID, true, true, true, true)?><br /><?=Users::format_username($InviterID, true, true, true, true)?></td> - <td><?=Format::get_ratio_html($Uploaded, $Downloaded)?><br /><?=Format::get_ratio_html($InviterUploaded, $InviterDownloaded)?></td> - <td> - <span style="float: left;"><?=display_str($Email)?></span> - <span style="float: right;"><a href="userhistory.php?action=email&userid=<?=$UserID?>" title="History" class="brackets tooltip">H</a> <a href="/user.php?action=search&email_history=on&email=<?=display_str($Email)?>" title="Search" class="brackets tooltip">S</a></span><br /> - <span style="float: left;"><?=display_str($InviterEmail)?></span> - <span style="float: right;"><a href="userhistory.php?action=email&userid=<?=$InviterID?>" title="History" class="brackets tooltip">H</a> <a href="/user.php?action=search&email_history=on&email=<?=display_str($InviterEmail)?>" title="Search" class="brackets tooltip">S</a></span><br /> - </td> - <td> - <span style="float: left;"><?=display_str($IP)?></span> - <span style="float: right;"><?=display_str($Uses)?> <a href="userhistory.php?action=ips&userid=<?=$UserID?>" title="History" class="brackets tooltip">H</a> <a href="/user.php?action=search&ip_history=on&ip=<?=display_str($IP)?>" title="Search" class="brackets tooltip">S</a> <a href="http://whatismyipaddress.com/ip/<?=display_str($IP)?>" title="WI" class="brackets tooltip">WI</a></span><br /> - <span style="float: left;"><?=display_str($InviterIP)?></span> - <span style="float: right;"><?=display_str($InviterUses)?> <a href="userhistory.php?action=ips&userid=<?=$InviterID?>" title="History" class="brackets tooltip">H</a> <a href="/user.php?action=search&ip_history=on&ip=<?=display_str($InviterIP)?>" title="Search" class="brackets tooltip">S</a> <a href="http://whatismyipaddress.com/ip/<?=display_str($InviterIP)?>" title="WI" class="brackets tooltip">WI</a></span><br /> - </td> - <td> - <?=$IPCC?> <br /> - <?=$InviterIPCC?> - </td> - <td> - <?=Tools::get_host_by_ajax($IP)?><br /> - <?=Tools::get_host_by_ajax($InviterIP)?> - </td> - <td> - <?=time_diff($Joined)?><br /> - <?=time_diff($InviterJoined)?> - </td> - </tr> -<? } ?> - </table> - <div class="linkbox"> -<? echo $Pages; ?> - </div> -<? + <tr class="row<?=$Row?>"> + <td><?=Users::format_username($UserID, true, true, true, true)?><br /><?=Users::format_username($InviterID, true, true, true, true)?></td> + <td><?=Format::get_ratio_html($Uploaded, $Downloaded)?><br /><?=Format::get_ratio_html($InviterUploaded, $InviterDownloaded)?></td> + <td> + <span style="float: left;"><?=display_str($Email)?></span> + <span style="float: right;"><a href="userhistory.php?action=email&userid=<?=$UserID?>" title="History" class="brackets tooltip">H</a> <a href="/user.php?action=search&email_history=on&email=<?=display_str($Email)?>" title="Search" class="brackets tooltip">S</a></span><br /> + <span style="float: left;"><?=display_str($InviterEmail)?></span> + <span style="float: right;"><a href="userhistory.php?action=email&userid=<?=$InviterID?>" title="History" class="brackets tooltip">H</a> <a href="/user.php?action=search&email_history=on&email=<?=display_str($InviterEmail)?>" title="Search" class="brackets tooltip">S</a></span><br /> + </td> + <td> + <span style="float: left;"><?=display_str($IP)?></span> + <span style="float: right;"><?=display_str($Uses)?> <a href="userhistory.php?action=ips&userid=<?=$UserID?>" title="History" class="brackets tooltip">H</a> <a href="/user.php?action=search&ip_history=on&ip=<?=display_str($IP)?>" title="Search" class="brackets tooltip">S</a> <a href="http://whatismyipaddress.com/ip/<?=display_str($IP)?>" title="WI" class="brackets tooltip">WI</a></span><br /> + <span style="float: left;"><?=display_str($InviterIP)?></span> + <span style="float: right;"><?=display_str($InviterUses)?> <a href="userhistory.php?action=ips&userid=<?=$InviterID?>" title="History" class="brackets tooltip">H</a> <a href="/user.php?action=search&ip_history=on&ip=<?=display_str($InviterIP)?>" title="Search" class="brackets tooltip">S</a> <a href="http://whatismyipaddress.com/ip/<?=display_str($InviterIP)?>" title="WI" class="brackets tooltip">WI</a></span><br /> + </td> + <td> + <?=$IPCC?> <br /> + <?=$InviterIPCC?> + </td> + <td> + <?=Tools::get_host_by_ajax($IP)?><br /> + <?=Tools::get_host_by_ajax($InviterIP)?> + </td> + <td> + <?=time_diff($Joined)?><br /> + <?=time_diff($InviterJoined)?> + </td> + </tr> +<?php + } ?> + </table> + <div class="linkbox"> +<?php echo $Pages; ?> + </div> +<?php } else { ?> - <h2 align="center">There have been no new registrations in the past 72 hours.</h2> -<? + <h2 align="center">There have been no new registrations in the past 72 hours.</h2> +<?php } View::show_footer(); ?> diff --git a/sections/tools/data/special_users.php b/sections/tools/data/special_users.php index eb2927ae8..e71661e37 100644 --- a/sections/tools/data/special_users.php +++ b/sections/tools/data/special_users.php @@ -1,36 +1,37 @@ -<? +<?php if (!check_perms('admin_manage_permissions')) { - error(403); + error(403); } View::show_header('Special Users List'); ?> <div class="thin"> -<? +<?php $DB->query(" - SELECT ID - FROM users_main - WHERE CustomPermissions != '' - AND CustomPermissions != 'a:0:{}'"); + SELECT ID + FROM users_main + WHERE CustomPermissions != '' + AND CustomPermissions != 'a:0:{}'"); if ($DB->has_results()) { ?> - <table width="100%"> - <tr class="colhead"> - <td>User</td> - <td>Access</td> - </tr> -<? - while (list($UserID)=$DB->next_record()) { + <table width="100%"> + <tr class="colhead"> + <td>User</td> + <td>Access</td> + </tr> +<?php + while (list($UserID)=$DB->next_record()) { ?> - <tr> - <td><?=Users::format_username($UserID, true, true, true, true)?></td> - <td><a href="user.php?action=permissions&userid=<?=$UserID?>">Manage</a></td> - </tr> -<? } ?> - </table> -<? + <tr> + <td><?=Users::format_username($UserID, true, true, true, true)?></td> + <td><a href="user.php?action=permissions&userid=<?=$UserID?>">Manage</a></td> + </tr> +<?php + } ?> + </table> +<?php } else { ?> - <h2 align="center">There are no special users.</h2> -<? + <h2 align="center">There are no special users.</h2> +<?php } ?> </div> -<? View::show_footer(); ?> +<?php View::show_footer(); ?> diff --git a/sections/tools/data/torrent_stats.php b/sections/tools/data/torrent_stats.php index ee57537a7..29841db60 100644 --- a/sections/tools/data/torrent_stats.php +++ b/sections/tools/data/torrent_stats.php @@ -1,79 +1,73 @@ -<? +<?php if (!check_perms('site_view_flow')) { - error(403); + error(403); } View::show_header('Torrents'); if (!$TorrentStats = $Cache->get_value('new_torrent_stats')) { - $DB->query(" - SELECT COUNT(ID), SUM(Size), SUM(FileCount) - FROM torrents"); - list($TorrentCount, $TotalSize, $TotalFiles) = $DB->next_record(); - $DB->query(" - SELECT COUNT(ID) - FROM users_main - WHERE Enabled = '1'"); - list($NumUsers) = $DB->next_record(); - $DB->query("SELECT COUNT(ID), SUM(Size), SUM(FileCount) FROM torrents WHERE Time > SUBDATE('".sqltime()."', INTERVAL 1 DAY)"); - list($DayNum, $DaySize, $DayFiles) = $DB->next_record(); - $DB->query("SELECT COUNT(ID), SUM(Size), SUM(FileCount) FROM torrents WHERE Time > SUBDATE('".sqltime()."', INTERVAL 7 DAY)"); - list($WeekNum, $WeekSize, $WeekFiles) = $DB->next_record(); - $DB->query("SELECT COUNT(ID), SUM(Size), SUM(FileCount) FROM torrents WHERE Time > SUBDATE('".sqltime()."', INTERVAL 30 DAY)"); - list($MonthNum, $MonthSize, $MonthFiles) = $DB->next_record(); - $Cache->cache_value('new_torrent_stats', array($TorrentCount, $TotalSize, $TotalFiles, - $NumUsers, $DayNum, $DaySize, $DayFiles, - $WeekNum, $WeekSize, $WeekFiles, $MonthNum, - $MonthSize, $MonthFiles), 3600); + $DB->query(" SELECT COUNT(*), SUM(Size), SUM(FileCount) FROM torrents"); + list($TorrentCount, $TotalSize, $TotalFiles) = $DB->next_record(); + $NumUsers = Users::get_enabled_users_count(); + $DB->query("SELECT COUNT(*), SUM(Size), SUM(FileCount) FROM torrents WHERE Time > now() - INTERVAL 1 DAY)"); + list($DayNum, $DaySize, $DayFiles) = $DB->next_record(); + $DB->query("SELECT COUNT(*), SUM(Size), SUM(FileCount) FROM torrents WHERE Time > now() - INTERVAL 1 WEEK)"); + list($WeekNum, $WeekSize, $WeekFiles) = $DB->next_record(); + $DB->query("SELECT COUNT(*), SUM(Size), SUM(FileCount) FROM torrents WHERE Time > now() - INTERVAL 1 MONTH)"); + list($MonthNum, $MonthSize, $MonthFiles) = $DB->next_record(); + $Cache->cache_value('new_torrent_stats', array($TorrentCount, $TotalSize, $TotalFiles, + $NumUsers, $DayNum, $DaySize, $DayFiles, + $WeekNum, $WeekSize, $WeekFiles, $MonthNum, + $MonthSize, $MonthFiles), 3600); } else { - list($TorrentCount, $TotalSize, $TotalFiles, $NumUsers, $DayNum, $DaySize, $DayFiles, - $WeekNum, $WeekSize, $WeekFiles, $MonthNum, $MonthSize, $MonthFiles) = $TorrentStats; + list($TorrentCount, $TotalSize, $TotalFiles, $NumUsers, $DayNum, $DaySize, $DayFiles, + $WeekNum, $WeekSize, $WeekFiles, $MonthNum, $MonthSize, $MonthFiles) = $TorrentStats; } ?> <div class="thin"> - <div class="box"> - <div class="head">Overall stats</div> - <div class="pad"> - <ul class="stats nobullet"> - <li><strong>Total torrents: </strong><?=number_format($TorrentCount)?></li> - <li><strong>Total size: </strong><?=Format::get_size($TotalSize)?></li> - <li><strong>Total files: </strong><?=number_format($TotalFiles)?></li> - <br /> - <li><strong>Mean torrents per user: </strong><?=number_format($TorrentCount / $NumUsers)?></li> - <li><strong>Mean torrent size: </strong><?=Format::get_size($TotalSize / $TorrentCount)?></li> - <li><strong>Mean files per torrent: </strong><?=number_format($TotalFiles / $TorrentCount)?></li> - <li><strong>Mean filesize: </strong><?=Format::get_size($TotalSize / $TotalFiles)?></li> - </ul> - </div> - </div> - <br /> - <div class="box"> - <div class="head">Upload frequency</div> - <div class="pad"> - <ul class="stats nobullet"> - <li><strong>Torrents today: </strong><?=number_format($DayNum)?></li> - <li><strong>Size today: </strong><?=Format::get_size($DaySize)?></li> - <li><strong>Files today: </strong><?=number_format($DayFiles)?></li> - <br /> - <li><strong>Torrents this week: </strong><?=number_format($WeekNum)?></li> - <li><strong>Size this week: </strong><?=Format::get_size($WeekSize)?></li> - <li><strong>Files this week: </strong><?=number_format($WeekFiles)?></li> - <br /> - <li><strong>Torrents per day this week: </strong><?=number_format($WeekNum / 7)?></li> - <li><strong>Size per day this week: </strong><?=Format::get_size($WeekSize / 7)?></li> - <li><strong>Files per day this week: </strong><?=number_format($WeekFiles / 7)?></li> - <br /> - <li><strong>Torrents this month: </strong><?=number_format($MonthNum)?></li> - <li><strong>Size this month: </strong><?=Format::get_size($MonthSize)?></li> - <li><strong>Files this month: </strong><?=number_format($MonthFiles)?></li> - <br /> - <li><strong>Torrents per day this month: </strong><?=number_format($MonthNum / 30)?></li> - <li><strong>Size per day this month: </strong><?=Format::get_size($MonthSize / 30)?></li> - <li><strong>Files per day this month: </strong><?=number_format($MonthFiles / 30)?></li> - </ul> - </div> - </div> + <div class="box"> + <div class="head">Overall stats</div> + <div class="pad"> + <ul class="stats nobullet"> + <li><strong>Total torrents: </strong><?=number_format($TorrentCount)?></li> + <li><strong>Total size: </strong><?=Format::get_size($TotalSize)?></li> + <li><strong>Total files: </strong><?=number_format($TotalFiles)?></li> + <br /> + <li><strong>Mean torrents per user: </strong><?=number_format($TorrentCount / $NumUsers)?></li> + <li><strong>Mean torrent size: </strong><?=Format::get_size($TotalSize / $TorrentCount)?></li> + <li><strong>Mean files per torrent: </strong><?=number_format($TotalFiles / $TorrentCount)?></li> + <li><strong>Mean filesize: </strong><?=Format::get_size($TotalSize / $TotalFiles)?></li> + </ul> + </div> + </div> + <br /> + <div class="box"> + <div class="head">Upload frequency</div> + <div class="pad"> + <ul class="stats nobullet"> + <li><strong>Torrents today: </strong><?=number_format($DayNum)?></li> + <li><strong>Size today: </strong><?=Format::get_size($DaySize)?></li> + <li><strong>Files today: </strong><?=number_format($DayFiles)?></li> + <br /> + <li><strong>Torrents this week: </strong><?=number_format($WeekNum)?></li> + <li><strong>Size this week: </strong><?=Format::get_size($WeekSize)?></li> + <li><strong>Files this week: </strong><?=number_format($WeekFiles)?></li> + <br /> + <li><strong>Torrents per day this week: </strong><?=number_format($WeekNum / 7)?></li> + <li><strong>Size per day this week: </strong><?=Format::get_size($WeekSize / 7)?></li> + <li><strong>Files per day this week: </strong><?=number_format($WeekFiles / 7)?></li> + <br /> + <li><strong>Torrents this month: </strong><?=number_format($MonthNum)?></li> + <li><strong>Size this month: </strong><?=Format::get_size($MonthSize)?></li> + <li><strong>Files this month: </strong><?=number_format($MonthFiles)?></li> + <br /> + <li><strong>Torrents per day this month: </strong><?=number_format($MonthNum / 30)?></li> + <li><strong>Size per day this month: </strong><?=Format::get_size($MonthSize / 30)?></li> + <li><strong>Files per day this month: </strong><?=number_format($MonthFiles / 30)?></li> + </ul> + </div> + </div> </div> -<? +<?php View::show_footer(); ?> diff --git a/sections/tools/data/upscale_pool.php b/sections/tools/data/upscale_pool.php index 0a4cbb94d..32567d487 100644 --- a/sections/tools/data/upscale_pool.php +++ b/sections/tools/data/upscale_pool.php @@ -1,96 +1,98 @@ <?php if (!check_perms('site_view_flow')) { - error(403); + error(403); } View::show_header('Upscale Pool'); define('USERS_PER_PAGE', 50); list($Page, $Limit) = Format::page_limit(USERS_PER_PAGE); $RS = $DB->query(" - SELECT - SQL_CALC_FOUND_ROWS - m.ID, - m.Username, - m.Uploaded, - m.Downloaded, - m.PermissionID, - m.Enabled, - i.Donor, - i.Warned, - i.JoinDate, - i.RatioWatchEnds, - i.RatioWatchDownload, - m.RequiredRatio - FROM users_main AS m - LEFT JOIN users_info AS i ON i.UserID = m.ID - WHERE i.RatioWatchEnds != '0000-00-00 00:00:00' - AND m.Enabled = '1' - ORDER BY i.RatioWatchEnds ASC - LIMIT $Limit"); + SELECT + SQL_CALC_FOUND_ROWS + um.ID, + um.Username, + uls.Uploaded, + uls.Downloaded, + um.PermissionID, + um.Enabled, + i.Donor, + i.Warned, + i.JoinDate, + i.RatioWatchEnds, + i.RatioWatchDownload, + um.RequiredRatio + FROM users_main AS um + INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) + INNER JOIN users_info AS i ON (i.UserID = um.ID) + WHERE i.RatioWatchEnds != '0000-00-00 00:00:00' + AND um.Enabled = '1' + ORDER BY i.RatioWatchEnds ASC + LIMIT $Limit"); $DB->query('SELECT FOUND_ROWS()'); list($Results) = $DB->next_record(); $DB->query(" - SELECT COUNT(UserID) - FROM users_info - WHERE BanDate != '0000-00-00 00:00:00' - AND BanReason = '2'"); + SELECT COUNT(UserID) + FROM users_info + WHERE BanDate != '0000-00-00 00:00:00' + AND BanReason = '2'"); list($TotalDisabled) = $DB->next_record(); $DB->set_query_id($RS); ?> - <div class="header"> - <h2>Upscale Pool</h2> - </div> -<? + <div class="header"> + <h2>Upscale Pool</h2> + </div> +<?php if ($DB->has_results()) { ?> - <div class="box pad thin"> - <p>There are currently <?=number_format($Results)?> enabled users on Ratio Watch and <?=number_format($TotalDisabled)?> already disabled.</p> - </div> - <div class="linkbox"> -<? - $Pages = Format::get_pages($Page, $Results, USERS_PER_PAGE, 11); - echo $Pages; + <div class="box pad thin"> + <p>There are currently <?=number_format($Results)?> enabled users on Ratio Watch and <?=number_format($TotalDisabled)?> already disabled.</p> + </div> + <div class="linkbox"> +<?php + $Pages = Format::get_pages($Page, $Results, USERS_PER_PAGE, 11); + echo $Pages; ?> - </div> - <table width="100%"> - <tr class="colhead"> - <td>User</td> - <td class="number_column">Uploaded</td> - <td class="number_column">Downloaded</td> - <td class="number_column">Ratio</td> - <td class="number_column">Required Ratio</td> - <td class="number_column tooltip" title="How much the user needs to upload to meet his or her required ratio">Deficit</td> - <td class="number_column tooltip" title="How much the user has downloaded on Ratio Watch">Gamble</td> - <td>Registration Date</td> - <td class="tooltip" title="If the time shown here ends in "ago", then this is how long the user has been on ratio watch and/or below his or her required ratio. If the time shown here does not end in "ago", then this is the time until the two week Ratio Watch period expires.">Ratio Watch Ended/Ends</td> - <td>Lifespan</td> - </tr> -<? - while (list($UserID, $Username, $Uploaded, $Downloaded, $PermissionID, $Enabled, $Donor, $Warned, $Joined, $RatioWatchEnds, $RatioWatchDownload, $RequiredRatio) = $DB->next_record()) { - $Row = $Row === 'b' ? 'a' : 'b'; + </div> + <table width="100%"> + <tr class="colhead"> + <td>User</td> + <td class="number_column">Uploaded</td> + <td class="number_column">Downloaded</td> + <td class="number_column">Ratio</td> + <td class="number_column">Required Ratio</td> + <td class="number_column tooltip" title="How much the user needs to upload to meet his or her required ratio">Deficit</td> + <td class="number_column tooltip" title="How much the user has downloaded on Ratio Watch">Gamble</td> + <td>Registration Date</td> + <td class="tooltip" title="If the time shown here ends in "ago", then this is how long the user has been on ratio watch and/or below his or her required ratio. If the time shown here does not end in "ago", then this is the time until the two week Ratio Watch period expires.">Ratio Watch Ended/Ends</td> + <td>Lifespan</td> + </tr> +<?php + while (list($UserID, $Username, $Uploaded, $Downloaded, $PermissionID, $Enabled, $Donor, $Warned, $Joined, $RatioWatchEnds, $RatioWatchDownload, $RequiredRatio) = $DB->next_record()) { + $Row = $Row === 'b' ? 'a' : 'b'; ?> - <tr class="row<?=$Row?>"> - <td><?=Users::format_username($UserID, true, true, true, true)?></td> - <td class="number_column"><?=Format::get_size($Uploaded)?></td> - <td class="number_column"><?=Format::get_size($Downloaded)?></td> - <td class="number_column"><?=Format::get_ratio_html($Uploaded, $Downloaded)?></td> - <td class="number_column"><?=number_format($RequiredRatio, 2)?></td> - <td class="number_column"><? if (($Downloaded * $RequiredRatio) > $Uploaded) { echo Format::get_size(($Downloaded * $RequiredRatio) - $Uploaded);} ?></td> - <td class="number_column"><?=Format::get_size($Downloaded - $RatioWatchDownload)?></td> - <td><?=time_diff($Joined, 2)?></td> - <td><?=time_diff($RatioWatchEnds)?></td> - <td><?/*time_diff(strtotime($Joined), strtotime($RatioWatchEnds))*/?></td> - </tr> -<? } ?> - </table> - <div class="linkbox"> -<? echo $Pages; ?> - </div> -<? + <tr class="row<?=$Row?>"> + <td><?=Users::format_username($UserID, true, true, true, true)?></td> + <td class="number_column"><?=Format::get_size($Uploaded)?></td> + <td class="number_column"><?=Format::get_size($Downloaded)?></td> + <td class="number_column"><?=Format::get_ratio_html($Uploaded, $Downloaded)?></td> + <td class="number_column"><?=number_format($RequiredRatio, 2)?></td> + <td class="number_column"><?php if (($Downloaded * $RequiredRatio) > $Uploaded) { echo Format::get_size(($Downloaded * $RequiredRatio) - $Uploaded);} ?></td> + <td class="number_column"><?=Format::get_size($Downloaded - $RatioWatchDownload)?></td> + <td><?=time_diff($Joined, 2)?></td> + <td><?=time_diff($RatioWatchEnds)?></td> + <td><?php /*time_diff(strtotime($Joined), strtotime($RatioWatchEnds))*/?></td> + </tr> +<?php + } ?> + </table> + <div class="linkbox"> +<?php echo $Pages; ?> + </div> +<?php } else { ?> - <h2 align="center">There are currently no users on ratio watch.</h2> -<? + <h2 align="center">There are currently no users on ratio watch.</h2> +<?php } View::show_footer(); diff --git a/sections/tools/data/user_flow.php b/sections/tools/data/user_flow.php index 3ee325cf6..94611386b 100644 --- a/sections/tools/data/user_flow.php +++ b/sections/tools/data/user_flow.php @@ -1,51 +1,51 @@ <?php if (!check_perms('site_view_flow')) { - error(403); + error(403); } //Timeline generation if (!isset($_GET['page'])) { - if (!list($Labels, $InFlow, $OutFlow, $Max) = $Cache->get_value('users_timeline')) { - $Labels = []; - $InFlow = []; - $OutFlow = []; - $DB->query(" - SELECT DATE_FORMAT(JoinDate, '%b \'%y') AS Month, COUNT(UserID) - FROM users_info - GROUP BY Month - ORDER BY JoinDate DESC - LIMIT 1, 12"); - $TimelineIn = array_reverse($DB->to_array()); - $DB->query(" - SELECT DATE_FORMAT(BanDate, '%b \'%y') AS Month, COUNT(UserID) - FROM users_info - GROUP BY Month - ORDER BY BanDate DESC - LIMIT 1, 12"); - $TimelineOut = array_reverse($DB->to_array()); - foreach ($TimelineIn as $Month) { - list($Label, $Amount) = $Month; - if ($Amount > $Max) { - $Max = $Amount; - } - } - foreach ($TimelineOut as $Month) { - list($Label, $Amount) = $Month; - if ($Amount > $Max) { - $Max = $Amount; - } - } - foreach ($TimelineIn as $Month) { - list($Label, $Amount) = $Month; - $Labels[] = $Label; - $InFlow[] = number_format(($Amount / $Max) * 100, 4); - } - foreach ($TimelineOut as $Month) { - list($Label, $Amount) = $Month; - $OutFlow[] = number_format(($Amount / $Max) * 100, 4); - } - $Cache->cache_value('users_timeline', array($Labels, $InFlow, $OutFlow, $Max), mktime(0, 0, 0, date('n') + 1, 2)); - } + if (!list($Labels, $InFlow, $OutFlow, $Max) = $Cache->get_value('users_timeline')) { + $Labels = []; + $InFlow = []; + $OutFlow = []; + $DB->query(" + SELECT DATE_FORMAT(JoinDate, '%b \'%y') AS Month, COUNT(UserID) + FROM users_info + GROUP BY Month + ORDER BY JoinDate DESC + LIMIT 1, 12"); + $TimelineIn = array_reverse($DB->to_array()); + $DB->query(" + SELECT DATE_FORMAT(BanDate, '%b \'%y') AS Month, COUNT(UserID) + FROM users_info + GROUP BY Month + ORDER BY BanDate DESC + LIMIT 1, 12"); + $TimelineOut = array_reverse($DB->to_array()); + foreach ($TimelineIn as $Month) { + list($Label, $Amount) = $Month; + if ($Amount > $Max) { + $Max = $Amount; + } + } + foreach ($TimelineOut as $Month) { + list($Label, $Amount) = $Month; + if ($Amount > $Max) { + $Max = $Amount; + } + } + foreach ($TimelineIn as $Month) { + list($Label, $Amount) = $Month; + $Labels[] = $Label; + $InFlow[] = number_format(($Amount / $Max) * 100, 4); + } + foreach ($TimelineOut as $Month) { + list($Label, $Amount) = $Month; + $OutFlow[] = number_format(($Amount / $Max) * 100, 4); + } + $Cache->cache_value('users_timeline', array($Labels, $InFlow, $OutFlow, $Max), mktime(0, 0, 0, date('n') + 1, 2)); + } } //End timeline generation @@ -54,63 +54,63 @@ list($Page, $Limit) = Format::page_limit(DAYS_PER_PAGE); $RS = $DB->query(" - SELECT - SQL_CALC_FOUND_ROWS - j.Date, - DATE_FORMAT(j.Date, '%Y-%m') AS Month, - CASE ISNULL(j.Flow) - WHEN 0 THEN j.Flow - ELSE '0' - END AS Joined, - CASE ISNULL(m.Flow) - WHEN 0 THEN m.Flow - ELSE '0' - END AS Manual, - CASE ISNULL(r.Flow) - WHEN 0 THEN r.Flow - ELSE '0' - END AS Ratio, - CASE ISNULL(i.Flow) - WHEN 0 THEN i.Flow - ELSE '0' - END AS Inactivity - FROM ( - SELECT - DATE_FORMAT(JoinDate, '%Y-%m-%d') AS Date, - COUNT(UserID) AS Flow - FROM users_info - WHERE JoinDate != '0000-00-00 00:00:00' - GROUP BY Date - ) AS j - LEFT JOIN ( - SELECT - DATE_FORMAT(BanDate, '%Y-%m-%d') AS Date, - COUNT(UserID) AS Flow - FROM users_info - WHERE BanDate != '0000-00-00 00:00:00' - AND BanReason = '1' - GROUP BY Date - ) AS m ON j.Date = m.Date - LEFT JOIN ( - SELECT - DATE_FORMAT(BanDate, '%Y-%m-%d') AS Date, - COUNT(UserID) AS Flow - FROM users_info - WHERE BanDate != '0000-00-00 00:00:00' - AND BanReason = '2' - GROUP BY Date - ) AS r ON j.Date = r.Date - LEFT JOIN ( - SELECT - DATE_FORMAT(BanDate, '%Y-%m-%d') AS Date, - COUNT(UserID) AS Flow - FROM users_info - WHERE BanDate != '0000-00-00 00:00:00' - AND BanReason = '3' - GROUP BY Date - ) AS i ON j.Date = i.Date - ORDER BY j.Date DESC - LIMIT $Limit"); + SELECT + SQL_CALC_FOUND_ROWS + j.Date, + DATE_FORMAT(j.Date, '%Y-%m') AS Month, + CASE ISNULL(j.Flow) + WHEN 0 THEN j.Flow + ELSE '0' + END AS Joined, + CASE ISNULL(m.Flow) + WHEN 0 THEN m.Flow + ELSE '0' + END AS Manual, + CASE ISNULL(r.Flow) + WHEN 0 THEN r.Flow + ELSE '0' + END AS Ratio, + CASE ISNULL(i.Flow) + WHEN 0 THEN i.Flow + ELSE '0' + END AS Inactivity + FROM ( + SELECT + DATE_FORMAT(JoinDate, '%Y-%m-%d') AS Date, + COUNT(UserID) AS Flow + FROM users_info + WHERE JoinDate != '0000-00-00 00:00:00' + GROUP BY Date + ) AS j + LEFT JOIN ( + SELECT + DATE_FORMAT(BanDate, '%Y-%m-%d') AS Date, + COUNT(UserID) AS Flow + FROM users_info + WHERE BanDate != '0000-00-00 00:00:00' + AND BanReason = '1' + GROUP BY Date + ) AS m ON j.Date = m.Date + LEFT JOIN ( + SELECT + DATE_FORMAT(BanDate, '%Y-%m-%d') AS Date, + COUNT(UserID) AS Flow + FROM users_info + WHERE BanDate != '0000-00-00 00:00:00' + AND BanReason = '2' + GROUP BY Date + ) AS r ON j.Date = r.Date + LEFT JOIN ( + SELECT + DATE_FORMAT(BanDate, '%Y-%m-%d') AS Date, + COUNT(UserID) AS Flow + FROM users_info + WHERE BanDate != '0000-00-00 00:00:00' + AND BanReason = '3' + GROUP BY Date + ) AS i ON j.Date = i.Date + ORDER BY j.Date DESC + LIMIT $Limit"); $DB->query('SELECT FOUND_ROWS()'); list($Results) = $DB->next_record(); @@ -118,45 +118,48 @@ $DB->set_query_id($RS); ?> <div class="thin"> -<? if (!isset($_GET['page'])) { ?> - <div class="box pad"> - <img src="https://chart.googleapis.com/chart?cht=lc&chs=820x160&chco=000D99,99000D&chg=0,-1,1,1&chxt=y,x&chxs=0,h&chxl=1:|<?=implode('|', $Labels)?>&chxr=0,0,<?=$Max?>&chd=t:<?=implode(',', $InFlow)?>|<?=implode(',', $OutFlow)?>&chls=2,4,0&chdl=New+Registrations|Disabled+Users&chf=bg,s,FFFFFF00" alt="User Flow vs. Time" /> - </div> -<? } ?> - <div class="linkbox"> -<? +<?php + if (!isset($_GET['page'])) { ?> + <div class="box pad"> + <img src="https://chart.googleapis.com/chart?cht=lc&chs=820x160&chco=000D99,99000D&chg=0,-1,1,1&chxt=y,x&chxs=0,h&chxl=1:|<?=implode('|', $Labels)?>&chxr=0,0,<?=$Max?>&chd=t:<?=implode(',', $InFlow)?>|<?=implode(',', $OutFlow)?>&chls=2,4,0&chdl=New+Registrations|Disabled+Users&chf=bg,s,FFFFFF00" alt="User Flow vs. Time" /> + </div> +<?php + } ?> + <div class="linkbox"> +<?php $Pages = Format::get_pages($Page, $Results, DAYS_PER_PAGE, 11); echo $Pages; ?> - </div> - <table width="100%"> - <tr class="colhead"> - <td>Date</td> - <td>(+) Joined</td> - <td>(-) Manual</td> - <td>(-) Ratio</td> - <td>(-) Inactivity</td> - <td>(-) Total</td> - <td>Net Growth</td> - </tr> -<? - while (list($Date, $Month, $Joined, $Manual, $Ratio, $Inactivity) = $DB->next_record()) { - $TotalOut = $Ratio + $Inactivity + $Manual; - $TotalGrowth = $Joined - $TotalOut; + </div> + <table width="100%"> + <tr class="colhead"> + <td>Date</td> + <td>(+) Joined</td> + <td>(-) Manual</td> + <td>(-) Ratio</td> + <td>(-) Inactivity</td> + <td>(-) Total</td> + <td>Net Growth</td> + </tr> +<?php + while (list($Date, $Month, $Joined, $Manual, $Ratio, $Inactivity) = $DB->next_record()) { + $TotalOut = $Ratio + $Inactivity + $Manual; + $TotalGrowth = $Joined - $TotalOut; ?> - <tr class="rowb"> - <td><?=$Date?></td> - <td><?=number_format($Joined)?></td> - <td><?=number_format($Manual)?></td> - <td><?=number_format((float)$Ratio)?></td> - <td><?=number_format($Inactivity)?></td> - <td><?=number_format($TotalOut)?></td> - <td><?=number_format($TotalGrowth)?></td> - </tr> -<? } ?> - </table> - <div class="linkbox"> - <?=$Pages?> - </div> + <tr class="rowb"> + <td><?=$Date?></td> + <td><?=number_format($Joined)?></td> + <td><?=number_format($Manual)?></td> + <td><?=number_format((float)$Ratio)?></td> + <td><?=number_format($Inactivity)?></td> + <td><?=number_format($TotalOut)?></td> + <td><?=number_format($TotalGrowth)?></td> + </tr> +<?php + } ?> + </table> + <div class="linkbox"> + <?=$Pages?> + </div> </div> -<? View::show_footer(); ?> +<?php View::show_footer(); ?> diff --git a/sections/tools/development/clear_cache.php b/sections/tools/development/clear_cache.php index 5287ccc8d..a0c002b3f 100644 --- a/sections/tools/development/clear_cache.php +++ b/sections/tools/development/clear_cache.php @@ -1,109 +1,122 @@ -<? +<?php if (!check_perms('users_mod') || !check_perms('admin_clear_cache')) { - error(403); + error(403); +} + +if (!empty($_GET['key'])) { + if ($_GET['submit'] == 'Multi') { + $Keys = array_map('trim', preg_split('/\s+/', $_GET['key'])); + } else { + $Keys = [trim($_GET['key'])]; + } +} + +if (isset($Keys) && $_GET['type'] == 'view') { + foreach ($Keys as $Key) { + foreach ($CachePermissions as $k => $v) { + if (strpos($Key, $k) !== false && !check_perms($v)) { + error(403); + } + } + } } View::show_header('Clear a cache key'); //Make sure the form was sent if (isset($_GET['cache'])) { - if ($_GET['cache'] === 'users') { - $DB->query("SELECT count(*) as count FROM users_main"); - list($Count) = $DB->next_record(); + if ($_GET['cache'] === 'users') { + $DB->query("SELECT count(*) as count FROM users_main"); + list($Count) = $DB->next_record(); - for ($i = 1; $i <= $Count; $i++) { - $Cache->delete_value('user_stats_' . $i); - $Cache->delete_value('user_info_' . $i); - $Cache->delete_value('user_info_heavy_' . $i); - } - echo "<div class='save_message'>{$Count} users' caches cleared!</div>"; - } - elseif ($_GET['cache'] === 'torrent_groups') { - $DB->query("SELECT count(*) as count FROM torrents_group"); - list($Count) = $DB->next_record(); - for ($i = 1; $i <= $Count; $i++) { - $Cache->delete_value('torrent_group_' . $i); - $Cache->delete_value('groups_artists_' . $i); - } - } -} -if (!empty($_GET['key'])) { - if ($_GET['submit'] == 'Multi') { - $Keys = array_map('trim', preg_split('/\s+/', $_GET['key'])); - } else { - $Keys = [trim($_GET['key'])]; - } + for ($i = 1; $i <= $Count; $i++) { + $Cache->delete_value('user_stats_' . $i); + $Cache->delete_value('user_info_' . $i); + $Cache->delete_value('user_info_heavy_' . $i); + } + echo "<div class='save_message'>{$Count} users' caches cleared!</div>"; + } + elseif ($_GET['cache'] === 'torrent_groups') { + $DB->query("SELECT count(*) as count FROM torrents_group"); + list($Count) = $DB->next_record(); + for ($i = 1; $i <= $Count; $i++) { + $Cache->delete_value('torrent_group_' . $i); + $Cache->delete_value('groups_artists_' . $i); + } + } } + if (isset($Keys) && $_GET['type'] == 'clear') { - foreach ($Keys as $Key) { - if (preg_match('/(.*?)(\d+)\.\.(\d+)$/', $Key, $Matches) && is_number($Matches[2]) && is_number($Matches[3])) { - for ($i = $Matches[2]; $i <= $Matches[3]; $i++) { - $Cache->delete_value($Matches[1].$i); - } - } else { - $Cache->delete_value($Key); - } - } - echo '<div class="save_message">Key(s) ' . implode(', ', array_map('display_str', $Keys)) . ' cleared!</div>'; + foreach ($Keys as $Key) { + if (preg_match('/(.*?)(\d+)\.\.(\d+)$/', $Key, $Matches) && is_number($Matches[2]) && is_number($Matches[3])) { + for ($i = $Matches[2]; $i <= $Matches[3]; $i++) { + $Cache->delete_value($Matches[1].$i); + } + } else { + $Cache->delete_value($Key); + } + } + echo '<div class="save_message">Key(s) ' . implode(', ', array_map('display_str', $Keys)) . ' cleared!</div>'; } + $MultiKeyTooltip = 'Enter cache keys delimited by any amount of whitespace.'; ?> - <div class="header"> - <h2>Clear a cache key</h2> - </div> - <table class="layout" cellpadding="2" cellspacing="1" border="0" align="center"> - <tr> - <td>Key</td> - <td> - <form class="manage_form" name="cache" method="get" action=""> - <input type="hidden" name="action" value="clear_cache" /> - <select name="type"> - <option value="view">View</option> - <option value="clear">Clear</option> - </select> - <input type="text" name="key" id="key" class="inputtext" value="<?=(isset($_GET['key']) && $_GET['submit'] != 'Multi' ? display_str($_GET['key']) : '')?>" /> - <input type="submit" name="submit" value="Single" class="submit" /> - </form> - </td> - </tr> - <tr class="tooltip" title="<?=$MultiKeyTooltip?>"> - <td>Multi-key</td> - <td> - <form class="manage_form" name="cache" method="get" action=""> - <input type="hidden" name="action" value="clear_cache" /> - <select name="type"> - <option value="view">View</option> - <option value="clear">Clear</option> - </select> - <textarea type="text" name="key" id="key" class="inputtext"><?=(isset($_GET['key']) && $_GET['submit'] == 'Multi' ? display_str($_GET['key']) : '')?></textarea> - <input type="submit" name="submit" value="Multi" class="submit" /> - </form> - </td> - </tr> - <tr> - <td rowspan="2">Clear Common Caches:</td> - <td><a href="tools.php?action=clear_cache&cache=users">Users</a> (clears out user_stats_*, user_info_*, and user_info_heavy_*)</td> - </tr> - <tr> - <td><a href="tools.php?action=clear_cache&cache=torrent_groups">Torrent Groups</a> (clears out torrent_group_* and groups_artists_*)</td> - </tr> - </table> -<? + <div class="header"> + <h2>Clear a cache key</h2> + </div> + <table class="layout" cellpadding="2" cellspacing="1" border="0" align="center"> + <tr> + <td>Key</td> + <td> + <form class="manage_form" name="cache" method="get" action=""> + <input type="hidden" name="action" value="clear_cache" /> + <select name="type"> + <option value="view">View</option> + <option value="clear">Clear</option> + </select> + <input type="text" name="key" id="key" class="inputtext" value="<?=(isset($_GET['key']) && $_GET['submit'] != 'Multi' ? display_str($_GET['key']) : '')?>" /> + <input type="submit" name="submit" value="Single" class="submit" /> + </form> + </td> + </tr> + <tr class="tooltip" title="<?=$MultiKeyTooltip?>"> + <td>Multi-key</td> + <td> + <form class="manage_form" name="cache" method="get" action=""> + <input type="hidden" name="action" value="clear_cache" /> + <select name="type"> + <option value="view">View</option> + <option value="clear">Clear</option> + </select> + <textarea type="text" name="key" id="key" class="inputtext"><?=(isset($_GET['key']) && $_GET['submit'] == 'Multi' ? display_str($_GET['key']) : '')?></textarea> + <input type="submit" name="submit" value="Multi" class="submit" /> + </form> + </td> + </tr> + <tr> + <td rowspan="2">Clear Common Caches:</td> + <td><a href="tools.php?action=clear_cache&cache=users">Users</a> (clears out user_stats_*, user_info_*, and user_info_heavy_*)</td> + </tr> + <tr> + <td><a href="tools.php?action=clear_cache&cache=torrent_groups">Torrent Groups</a> (clears out torrent_group_* and groups_artists_*)</td> + </tr> + </table> +<?php if (isset($Keys) && $_GET['type'] == 'view') { - ?> - <table class="layout" cellpadding="2" cellspacing="1" border="0" align="center" style="margin-top: 1em;"> - <? - foreach ($Keys as $Key) { - ?> - <tr> - <td><?=display_str($Key)?></td> - <td> - <pre><? var_dump($Cache->get_value($Key)); ?></pre> - </td> - </tr> - <? } ?> - </table> - <? + ?> + <table class="layout" cellpadding="2" cellspacing="1" border="0" align="center" style="margin-top: 1em;"> + <?php + foreach ($Keys as $Key) { + ?> + <tr> + <td><?=display_str($Key)?></td> + <td> + <pre><?php var_dump($Cache->get_value($Key)); ?></pre> + </td> + </tr> + <?php } ?> + </table> + <?php } View::show_footer(); diff --git a/sections/tools/development/process_info.php b/sections/tools/development/process_info.php index 82c1d009f..ab412991e 100644 --- a/sections/tools/development/process_info.php +++ b/sections/tools/development/process_info.php @@ -1,6 +1,6 @@ -<? +<?php if (!check_perms('site_debug')) { - error(403); + error(403); } View::show_header('PHP Processes'); $PIDList = trim(`ps -C php-fpm -o pid --no-header`); @@ -9,33 +9,34 @@ $Debug->log_var($PIDs, 'PIDs'); ?> <div class="thin"> - <table class="process_info"> - <colgroup> - <col class="process_info_pid" /> - <col class="process_info_data" /> - </colgroup> - <tr class="colhead_dark"> - <td colspan="2"> - <?=count($PIDs) . ' processes'?> - </td> - </tr> -<? + <table class="process_info"> + <colgroup> + <col class="process_info_pid" /> + <col class="process_info_data" /> + </colgroup> + <tr class="colhead_dark"> + <td colspan="2"> + <?=count($PIDs) . ' processes'?> + </td> + </tr> +<?php foreach ($PIDs as $PID) { - $PID = trim($PID); - if (!$ProcessInfo = $Cache->get_value("php_$PID")) { - continue; - } + $PID = trim($PID); + if (!$ProcessInfo = $Cache->get_value("php_$PID")) { + continue; + } ?> - <tr> - <td> - <?=$PID?> - </td> - <td> - <pre><?print_r($ProcessInfo)?></pre> - </td> - </tr> -<? } ?> - </table> + <tr> + <td> + <?=$PID?> + </td> + <td> + <pre><?php print_r($ProcessInfo); ?></pre> + </td> + </tr> +<?php +} ?> + </table> </div> -<? +<?php View::show_footer(); diff --git a/sections/tools/development/render_base.html b/sections/tools/development/render_base.html index 241057229..bd0e1bfa5 100644 --- a/sections/tools/development/render_base.html +++ b/sections/tools/development/render_base.html @@ -1,328 +1,328 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" style="overflow: hidden !important; margin: 0 !important; padding: 0 !important;"> - <head> - <title>Stylesheet Gallery - - - - - - - -
    -

    Gazelle

    - -
    -
    -

    Forums

    -
    -

    Site

    - - - - - - - - - - - - - - - - - - - - - - - - -
    ForumLast postTopicsPosts
    -

    - Announcements -

    -
    - - New Site Announcements - - - - - by Ananke Just now - 38595,197
    -

    - What.CD -

    -
    - - Dear Mortals, you have violated the rule... - - by Drone 3 mins ago - 2,624110,432
    -

    Community

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ForumLast postTopicsPosts
    -

    - The Lounge -

    -
    - - Last stand against Drone? - - - - - by Ajax Just now - 37,5311,545,089
    -

    - The Lounge +1 -

    -
    - - No fun allowed - - by Drone 10 mins ago - 424490,163
    -

    - The Library -

    -
    - - List of forbidden literature - - by Drone 7 hours ago - 424490,163
    -

    - Concerts, Events & Meets -

    -
    - - [Region] The Void - - by Drone 25 mins ago - 30515,231
    -

    - Technology -

    -
    - - How did Drone take over the site? - - - - - by Etheryte 5 mins ago - 25,031386,278
    -

    Music

    - - - - - - - - - - - - - - - - - - - - - - - - -
    ForumLast postTopicsPosts
    -

    - Music -

    -
    - - Where did all the non-drone threads go? - - - - - by tuutiki 1 min ago - 22,564608,253
    -

    - Vanity House -

    -
    - - Drone - Drone [EP] (drone, noise) - - by Drone Just now - 3,94824,269
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - + + + + +
    +
      +
    • + +
      + +
      +
    • +
    • + +
      + + + +
      +
    • +
    • + +
      + +
      +
    • +
    • + +
      + + +
      +
    • +
    • + +
      + +
      +
    • +
    • + +
      + + +
      +
    • +
    +
    + +
    +
    +

    Forums

    +
    +

    Site

    + + + + + + + + + + + + + + + + + + + + + + + + +
    ForumLast postTopicsPosts
    +

    + Announcements +

    +
    + + New Site Announcements + + + + + by Ananke Just now + 38595,197
    +

    + What.CD +

    +
    + + Dear Mortals, you have violated the rule... + + by Drone 3 mins ago + 2,624110,432
    +

    Community

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ForumLast postTopicsPosts
    +

    + The Lounge +

    +
    + + Last stand against Drone? + + + + + by Ajax Just now + 37,5311,545,089
    +

    + The Lounge +1 +

    +
    + + No fun allowed + + by Drone 10 mins ago + 424490,163
    +

    + The Library +

    +
    + + List of forbidden literature + + by Drone 7 hours ago + 424490,163
    +

    + Concerts, Events & Meets +

    +
    + + [Region] The Void + + by Drone 25 mins ago + 30515,231
    +

    + Technology +

    +
    + + How did Drone take over the site? + + + + + by Etheryte 5 mins ago + 25,031386,278
    +

    Music

    + + + + + + + + + + + + + + + + + + + + + + + + +
    ForumLast postTopicsPosts
    +

    + Music +

    +
    + + Where did all the non-drone threads go? + + + + + by tuutiki 1 min ago + 22,564608,253
    +

    + Vanity House +

    +
    + + Drone - Drone [EP] (drone, noise) + + by Drone Just now + 3,94824,269
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + diff --git a/sections/tools/development/render_build_preview.js b/sections/tools/development/render_build_preview.js index ba26962f4..4a503ac46 100644 --- a/sections/tools/development/render_build_preview.js +++ b/sections/tools/development/render_build_preview.js @@ -5,8 +5,8 @@ var style = process.argv[4]; var toolsMiscPath = process.argv[5]; if (!fs.existsSync(rootPath + '/' + staticPath + 'styles/' + style)) { - console.log('Style folder does not exist'); - process.exit(-1); + console.log('Style folder does not exist'); + process.exit(-1); } var preview = rootPath + '/' + staticPath + 'styles/' + style + '/preview.html'; var output = rootPath + '/' + staticPath + 'stylespreview/full_' + style + '.png'; @@ -16,12 +16,12 @@ fs.createReadStream(toolsMiscPath + '/render_base.html').pipe(fs.createWriteStre const puppeteer = require('puppeteer'); (async () => { - const browser = await puppeteer.launch({args: ['--no-sandbox']}); - const page = await browser.newPage(); - await page.setViewport({width: 1200, height: 1000}); - await page.goto('file://' + preview); - await page.screenshot({path: output}); - await browser.close(); - fs.unlink(preview); + const browser = await puppeteer.launch({args: ['--no-sandbox']}); + const page = await browser.newPage(); + await page.setViewport({width: 1200, height: 1000}); + await page.goto('file://' + preview); + await page.screenshot({path: output}); + await browser.close(); + fs.unlink(preview); })(); diff --git a/sections/tools/development/rerender_gallery.php b/sections/tools/development/rerender_gallery.php index 7cd6f66d6..5840d5653 100644 --- a/sections/tools/development/rerender_gallery.php +++ b/sections/tools/development/rerender_gallery.php @@ -1,4 +1,4 @@ -query(' - SELECT - ID, - LOWER(REPLACE(Name," ","_")) AS Name, - Name AS ProperName - FROM stylesheets'); + SELECT + ID, + LOWER(REPLACE(Name," ","_")) AS Name, + Name AS ProperName + FROM stylesheets'); $Styles = $DB->to_array('ID', MYSQLI_BOTH); $ImagePath = SERVER_ROOT . '/' . STATIC_SERVER . 'stylespreview'; ?>
    -

    Rerender stylesheet gallery images

    - -
    -
    -
    About rendering
    -
    -

    You are now rendering stylesheet gallery images.

    -

    The used parameters can be seen on the right, returned statuses are displayed below.

    -
    -
    -
    -
    Rendering status
    -
    -Rerender stylesheet gallery images + +
    +
    +
    About rendering
    +
    +

    You are now rendering stylesheet gallery images.

    +

    The used parameters can be seen on the right, returned statuses are displayed below.

    +
    +
    +
    +
    Rendering status
    +
    + -
    -
    -

    Build preview:
    -"; - echo '     '; - $BuildResult = trim(shell_exec(escapeshellcmd($CmdLine))); - echo (empty($BuildResult)) ? 'Success.' : "Error occured: {$BuildResult}."; +

    +
    +

    Build preview:
    +"; + echo '     '; + $BuildResult = trim(shell_exec(escapeshellcmd($CmdLine))); + echo (empty($BuildResult)) ? 'Success.' : "Error occured: {$BuildResult}."; ?> -

    - + -

    Converting Image: -Converting Image: + -

    - -
    - -
    -
    -
    +

    + +
    + +
    +
    +
    -flush(); + authorize(); + $Cache->flush(); } $DB->query('SHOW GLOBAL STATUS'); $DBStats = $DB->to_array('Variable_name'); @@ -14,270 +14,271 @@ if (check_perms('site_database_specifics')) { ?> - +
    -
    - - - - - - - - - - - 0.7) { echo ' class="invalid" '; } ?>>Database: - - - - - - - - - - - - - - - - - - - - - - - - 0.85) { echo ' class="tooltip invalid" title="Evictions begin when storage exceeds 85%" '; } ?>>Cache Storage: - - - - - - - - -
    Service
    Threads (Active)
    Cache: (100.000%)
    (%)
    Connections
    Cache:
    Database:
    Special
    Cache Current Index:
    Cache Total Index:
    (%)
    Utilities
    Cache: -
    - - - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - >Cache: - - - - - - - - - - - - - - - - - - >Cache Increment: - - - - >Cache Decrement: - - - - - 0 && $MemStats['cas_hits'] / ($MemStats['cas_hits'] + $MemStats['cas_misses']) < 0.7) { echo ' class="tooltip invalid" title="More than 30% of the issued CAS commands were unnecessarily wasting time and resources." '; } elseif ($MemStats['cas_hits'] == 0) { echo ' class="tooltip notice" title="Disable CAS with the -C parameter and save resources since it is not used." '; } ?>>Cache: - - - - - - - - - >Cache: - - - - - - - - - - $MemStats['uptime'] / 7 * 24 * 3600) { echo ' class="tooltip invalid" title="Flushing the cache on a regular basis defeats the benefits of it, look into using cache transactions, or deletes instead of global flushing where possible." '; } ?>>Cache Flushes: - - - - 0) { echo ' class="invalid" '; } ?>>Cache Evicted: - - - - $DBStats['Questions']['Value'] / 7500) { echo ' class="tooltip invalid" title="1/7500 queries is allowed to be slow to minimize performance impact." '; } ?>>Database Slow: - - - - - - - - - - - - - - - - - - - - - -
    Activity
    Total Reads
    Cache:
    Database:
    Total Writes
    Cache:
    Database:
    Get/Select (Success)
    (%)
    Database: (100.000%)
    Set/Insert (Success)
    Cache: (100.000%)
    Database: (100.000%)
    Increment/Decrement (Success)
    (%)
    (%)
    CAS/Update (Success)
    ( 0) { echo number_format(($MemStats['cas_hits'] / ($MemStats['cas_hits'] + $MemStats['cas_misses'])) * 100, 3); } else { echo '0.000'; } ?>%)
    Database: (100.000%)
    Deletes (Success)
    (%)
    Database: (100.000%)
    Special
    Data Read
    Cache:
    Database:
    Data Write
    Cache:
    Database:
    -
    -
    - - - - - - - >Cache: - - - - - - - - - >Cache: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Concurrency
    Total Reads
    /s
    Database:/s
    Total Writes
    /s
    Database:/s
    Get/Select
    Cache:/s
    Database:/s
    Set/Insert
    Cache:/s
    Database:/s
    Increment/Decrement
    Cache Increment:/s
    Cache Decrement:/s
    CAS/Updates
    Cache:/s
    Database:/s
    Deletes
    Cache:/s
    Database:/s
    Special
    Cache Flushes:/s
    Cache Evicted:/s
    Database Slow:/s
    Data Read
    Cache:/s
    Database:/s
    Data Write
    Cache:/s
    Database:/s
    -
    +
    + + + + + + + + + + + 0.7) { echo ' class="invalid" '; } ?>>Database: + + + + + + + + + + + + + + + + + + + + + + + + 0.85) { echo ' class="tooltip invalid" title="Evictions begin when storage exceeds 85%" '; } ?>>Cache Storage: + + + + + + + + +
    Service
    Threads (Active)
    Cache: (100.000%)
    (%)
    Connections
    Cache:
    Database:
    Special
    Cache Current Index:
    Cache Total Index:
    (%)
    Utilities
    Cache: +
    + + + + +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + >Cache: + + + + + + + + + + + + + + + + + + >Cache Increment: + + + + >Cache Decrement: + + + + + 0 && $MemStats['cas_hits'] / ($MemStats['cas_hits'] + $MemStats['cas_misses']) < 0.7) { echo ' class="tooltip invalid" title="More than 30% of the issued CAS commands were unnecessarily wasting time and resources." '; } elseif ($MemStats['cas_hits'] == 0) { echo ' class="tooltip notice" title="Disable CAS with the -C parameter and save resources since it is not used." '; } ?>>Cache: + + + + + + + + + >Cache: + + + + + + + + + + $MemStats['uptime'] / 7 * 24 * 3600) { echo ' class="tooltip invalid" title="Flushing the cache on a regular basis defeats the benefits of it, look into using cache transactions, or deletes instead of global flushing where possible." '; } ?>>Cache Flushes: + + + + 0) { echo ' class="invalid" '; } ?>>Cache Evicted: + + + + $DBStats['Questions']['Value'] / 7500) { echo ' class="tooltip invalid" title="1/7500 queries is allowed to be slow to minimize performance impact." '; } ?>>Database Slow: + + + + + + + + + + + + + + + + + + + + + +
    Activity
    Total Reads
    Cache:
    Database:
    Total Writes
    Cache:
    Database:
    Get/Select (Success)
    (%)
    Database: (100.000%)
    Set/Insert (Success)
    Cache: (100.000%)
    Database: (100.000%)
    Increment/Decrement (Success)
    (%)
    (%)
    CAS/Update (Success)
    ( 0) { echo number_format(($MemStats['cas_hits'] / ($MemStats['cas_hits'] + $MemStats['cas_misses'])) * 100, 3); } else { echo '0.000'; } ?>%)
    Database: (100.000%)
    Deletes (Success)
    (%)
    Database: (100.000%)
    Special
    Data Read
    Cache:
    Database:
    Data Write
    Cache:
    Database:
    +
    +
    + + + + + + + >Cache: + + + + + + + + + >Cache: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Concurrency
    Total Reads
    /s
    Database:/s
    Total Writes
    /s
    Database:/s
    Get/Select
    Cache:/s
    Database:/s
    Set/Insert
    Cache:/s
    Database:/s
    Increment/Decrement
    Cache Increment:/s
    Cache Decrement:/s
    CAS/Updates
    Cache:/s
    Database:/s
    Deletes
    Cache:/s
    Database:/s
    Special
    Cache Flushes:/s
    Cache Evicted:/s
    Database Slow:/s
    Data Read
    Cache:/s
    Database:/s
    Data Write
    Cache:/s
    Database:/s
    +
    - + diff --git a/sections/tools/development/site_info.php b/sections/tools/development/site_info.php index 7bf5bbb12..eab88dc21 100644 --- a/sections/tools/development/site_info.php +++ b/sections/tools/development/site_info.php @@ -86,76 +86,76 @@ function composer_exec($CMD) { div#phpinfo hr {width: 934px; background-color: #ccc; border: 0; height: 1px;}
    -

    Timestamps

    -
    - PHP:
    - DB: -
    - -

    PHP

    -
    - PHP Version:
    - Toggle PHP Info
    -
    -
    - -

    Git

    -
    - Branch:
    - Local Hash:
    - Remote Hash: -
    - -

    Composer

    -
    - Composer Version:
    - - - - - - - - Timestamps +
    + PHP:
    + DB: +
    + +

    PHP

    +
    + PHP Version:
    + Toggle PHP Info
    +
    +
    + +

    Git

    +
    + Branch:
    + Local Hash:
    + Remote Hash: +
    + +

    Composer

    +
    + Composer Version:
    +
    PackageVersionInstalledLocked
    + + + + + + + - - - - - - - + + + + + + -
    PackageVersionInstalledLocked
    -
    - -

    Phinx

    -
    -
    - - - - - - - + + +

    Phinx

    +
    +
    +
    StatusMigration IDMigration Name
    + + + + + + - - - - - - + + + + + -
    StatusMigration IDMigration Name
    -
    + +
    Value Comment -next_record()) { $Row = $Row === 'a' ? 'b' : 'a'; @@ -26,11 +26,11 @@ - -query("DELETE FROM site_options WHERE Name = '" . $Name . "'"); + $DB->prepared_query('DELETE FROM site_options WHERE Name = ?', $Name); $Cache->delete_value('site_option_' . $Name); } else { - $Val->SetFields('name', '1', 'regex', 'The name must be separated by underscores. No spaces are allowed.', array('regex' => '/^[a-z][_a-z0-9]{0,63}$/i')); + $Val->SetFields('name', '1', 'regex', 'The name must be alphanumeric and may contain dashes or underscores. No spaces are allowed.', array('regex' => '/^[a-z][-_a-z0-9]{0,63}$/i')); $Val->SetFields('value', '1', 'string', 'You must specify a value for the option.'); $Val->SetFields('comment', '1', 'string', 'You must specify a comment for the option.'); @@ -52,42 +55,38 @@ error($Error); } - $Name = db_string($_POST['name']); - $Value = db_string($_POST['value']); - $Comment = db_string($_POST['comment']); - if ($_POST['submit'] == 'Edit') { - $DB->query("SELECT Name FROM site_options WHERE ID = '" . db_string($_POST['id']) . "'"); + $DB->prepared_query('SELECT Name FROM site_options WHERE ID = ?', $_POST['id']); list($OldName) = $DB->next_record(); - $DB->query(" + $DB->prepared_query(' UPDATE site_options SET - Name = '$Name', - Value = '$Value', - Comment = '$Comment' - WHERE ID = '" . db_string($_POST['id']) . "' - "); + Name = ?, Value = ?, Comment = ? + WHERE ID = ? + ', $Name, $Value, $Comment, $_POST['id'] + ); $Cache->delete_value('site_option_' . $OldName); + $Cache->cache_value('site_option_' . $Name, $Value); } else { - $DB->query(" + $DB->prepared_query(' INSERT INTO site_options (Name, Value, Comment) - VALUES ('$Name', '$Value', '$Comment') - "); + VALUES (?, ?, ?) + ', $Name, $Value, $Comment + ); + $Cache->cache_value('site_option_' . $Name, $Value); } - - $Cache->delete_value('site_option_' . $Name); } } -$DB->query(" +$DB->query(' SELECT ID, Name, Value, Comment FROM site_options - ORDER BY LOWER(Name) DESC -"); + ORDER BY LOWER(Name) +'); View::show_header('Site Options'); ?> @@ -98,7 +97,7 @@ @@ -122,7 +121,7 @@ -next_record()) { $Row = $Row === 'a' ? 'b' : 'a'; @@ -147,8 +146,9 @@ -
    - Name + Name Value Comment
    - +prepared_query(" CREATE TEMPORARY TABLE temp_geoip_locations ( - `ID` int(10) NOT NULL PRIMARY KEY, - `Country` varchar(2) NOT NULL + `ID` int(10) NOT NULL PRIMARY KEY, + `Country` varchar(2) NOT NULL )"); // Note: you cannot use a prepared query here for this @@ -47,9 +47,9 @@ $DB->prepared_query(" CREATE TEMPORARY TABLE temp_geoip_blocks ( - `StartIP` INT(11) UNSIGNED NOT NULL, - `EndIP` INT(11) UNSIGNED NOT NULL, - `LocID` INT(10) NOT NULL + `StartIP` INT(11) UNSIGNED NOT NULL, + `EndIP` INT(11) UNSIGNED NOT NULL, + `LocID` INT(10) NOT NULL )"); // Note: you cannot use a prepared query here for this @@ -63,18 +63,18 @@ $DB->prepared_query(" INSERT INTO geoip_country (StartIP, EndIP, Code) - SELECT StartIP, EndIP, Country - FROM temp_geoip_blocks AS tgb - LEFT JOIN temp_geoip_locations AS tgl ON tgb.LocID = tgl.ID + SELECT StartIP, EndIP, Country + FROM temp_geoip_blocks AS tgb + LEFT JOIN temp_geoip_locations AS tgl ON tgb.LocID = tgl.ID "); print "{$DB->affected_rows()} locations inserted"; $DB->query("INSERT INTO users_geodistribution - (Code, Users) + (Code, Users) SELECT g.Code, COUNT(u.ID) AS Users FROM geoip_country AS g - JOIN users_main AS u ON INET_ATON(u.IP) BETWEEN g.StartIP AND g.EndIP + JOIN users_main AS u ON INET_ATON(u.IP) BETWEEN g.StartIP AND g.EndIP WHERE u.Enabled = '1' GROUP BY g.Code ORDER BY Users DESC"); diff --git a/sections/tools/development/update_offsets.php b/sections/tools/development/update_offsets.php index f19ae6785..9f5964713 100644 --- a/sections/tools/development/update_offsets.php +++ b/sections/tools/development/update_offsets.php @@ -2,67 +2,67 @@ $Inserted = false; if (isset($_REQUEST['update']) && $_REQUEST['update'] === '1') { - $CH = curl_init(); + $CH = curl_init(); - curl_setopt($CH, CURLOPT_URL, 'http://www.accuraterip.com/driveoffsets.htm'); - curl_setopt($CH, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($CH, CURLOPT_CONNECTTIMEOUT, 5); - $Doc = new DOMDocument(); - $Doc->loadHTML(curl_exec($CH), LIBXML_NOWARNING | LIBXML_NOERROR); - curl_close($CH); + curl_setopt($CH, CURLOPT_URL, 'http://www.accuraterip.com/driveoffsets.htm'); + curl_setopt($CH, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($CH, CURLOPT_CONNECTTIMEOUT, 5); + $Doc = new DOMDocument(); + $Doc->loadHTML(curl_exec($CH), LIBXML_NOWARNING | LIBXML_NOERROR); + curl_close($CH); - $Rows = $Doc->getElementsByTagName('table')->item(1)->getElementsByTagName('tr'); - $Offsets = []; - $Prepared = []; - for ($I = 1; $I < $Rows->length; $I++) { - $Row = $Rows->item($I); - if ($Row->childNodes->length > 4 && $Row->childNodes->item(3)->nodeValue !== '[Purged]') { - $Offsets[] = trim($Row->childNodes->item(1)->nodeValue, '- '); - $Offsets[] = trim($Row->childNodes->item(3)->nodeValue); - $Prepared[] = "(?, ?)"; - } - } + $Rows = $Doc->getElementsByTagName('table')->item(1)->getElementsByTagName('tr'); + $Offsets = []; + $Prepared = []; + for ($I = 1; $I < $Rows->length; $I++) { + $Row = $Rows->item($I); + if ($Row->childNodes->length > 4 && $Row->childNodes->item(3)->nodeValue !== '[Purged]') { + $Offsets[] = trim($Row->childNodes->item(1)->nodeValue, '- '); + $Offsets[] = trim($Row->childNodes->item(3)->nodeValue); + $Prepared[] = "(?, ?)"; + } + } - G::$DB->prepared_query('TRUNCATE drives'); - G::$DB->prepared_query('INSERT INTO drives (Name, Offset) VALUES '.implode(', ', $Prepared), ...$Offsets); - $Inserted = G::$DB->affected_rows(); + G::$DB->prepared_query('TRUNCATE drives'); + G::$DB->prepared_query('INSERT INTO drives (Name, Offset) VALUES '.implode(', ', $Prepared), ...$Offsets); + $Inserted = G::$DB->affected_rows(); } View::show_header('Update Drive Offsets'); ?>
    -

    Drive Offsets

    +

    Drive Offsets

    -
    -

    This page lists all of the stored Drive Offsets on Orpheus. We use these offsets to check - the offset listed in a rip log file. This information comes from - Accuraterip. - {$Inserted} offsets inserted." : ""?> -

    -

    - Update Offsets -

    -
    - - - - - +
    +

    This page lists all of the stored Drive Offsets on Orpheus. We use these offsets to check + the offset listed in a rip log file. This information comes from + Accuraterip. + {$Inserted} offsets inserted." : ""?> +

    +

    + Update Offsets +

    +
    +
    DriveOffset
    + + + + prepared_query('SELECT Name, Offset FROM drives ORDER BY DriveID'); while (list($Name, $Offset) = G::$DB->fetch_record()) { - ?> - - - - - + + + + + -
    DriveOffset
    +
    -

    +

    -
    -

    -
    - +

    +
    + - Show donor list -&list=1" class="brackets">Show donor list +query(" - SELECT i.UserID, i.BitcoinAddress - FROM users_info AS i - JOIN users_main AS m ON m.ID = i.UserID - WHERE BitcoinAddress != '' - ORDER BY m.Username ASC"); + $BitcoinAddresses = DonationsBitcoin::get_received(); + $DB->query(" + SELECT i.UserID, i.BitcoinAddress + FROM users_info AS i + JOIN users_main AS m ON m.ID = i.UserID + WHERE BitcoinAddress != '' + ORDER BY m.Username ASC"); ?> - - - - - - -next_record(MYSQLI_NUM, false)) { - if (!isset($BitcoinAddresses[$BitcoinAddress])) { - continue; - } +
    UsernameReceiving Bitcoin AddressAmount
    + + + + + +next_record(MYSQLI_NUM, false)) { + if (!isset($BitcoinAddresses[$BitcoinAddress])) { + continue; + } ?> - - - - - - + + + + + -
    UsernameReceiving Bitcoin AddressAmount
    BTC
    BTC
    - + - + diff --git a/sections/tools/finances/bitcoin_unproc.php b/sections/tools/finances/bitcoin_unproc.php index 172cbc7d3..710a8b123 100644 --- a/sections/tools/finances/bitcoin_unproc.php +++ b/sections/tools/finances/bitcoin_unproc.php @@ -1,6 +1,6 @@ -query(" - SELECT BitcoinAddress, SUM(Amount) - FROM donations_bitcoin - GROUP BY BitcoinAddress"); + SELECT BitcoinAddress, SUM(Amount) + FROM donations_bitcoin + GROUP BY BitcoinAddress"); $OldDonations = G::$DB->to_pair(0, 1, false); ?>
    -
    -

    -
    -
    -
    Do not process these donations manually! The Bitcoin parser will get them sooner or later (poke a developer if something seems broken).
    -
    - +

    +
    +
    +
    Do not process these donations manually! The Bitcoin parser will get them sooner or later (poke a developer if something seems broken).
    +
    + $Amount) { - if (isset($OldDonations[$Address])) { - if ($Amount == $OldDonations[$Address]) { // Direct comparison should be fine as everything comes from bitcoind - continue; - } - $Debug->log_var(array('old' => $OldDonations[$Address], 'new' => $Amount), "New donations from $Address"); - // PHP doesn't do fixed-point math, and json_decode has already botched the precision - // so let's just round this off to satoshis and pray that we're on a 64 bit system - $Amount = round($Amount - $OldDonations[$Address], 8); - } - $TotalUnproc += $Amount; - $NewDonations[$Address] = $Amount; + if (isset($OldDonations[$Address])) { + if ($Amount == $OldDonations[$Address]) { // Direct comparison should be fine as everything comes from bitcoind + continue; + } + $Debug->log_var(array('old' => $OldDonations[$Address], 'new' => $Amount), "New donations from $Address"); + // PHP doesn't do fixed-point math, and json_decode has already botched the precision + // so let's just round this off to satoshis and pray that we're on a 64 bit system + $Amount = round($Amount - $OldDonations[$Address], 8); + } + $TotalUnproc += $Amount; + $NewDonations[$Address] = $Amount; } ?> - - - - - - - - - - + + + + + + + + + $UserID) { - $DonationEUR = Donations::currency_exchange($NewDonations[$Address], 'BTC'); + foreach (DonationsBitcoin::get_userids(array_keys($NewDonations)) as $Address => $UserID) { + $DonationEUR = Donations::currency_exchange($NewDonations[$Address], 'BTC'); ?> - - - - - - - - - + + + + + + + + - - - - -
    Bitcoin AddressUserUnprocessed Amount (Total: )Total AmountDonor RankSpecial Rank
    Bitcoin AddressUserUnprocessed Amount (Total: )Total AmountDonor RankSpecial Rank
    ()
    ()
    No unprocessed Bitcoin donations
    + + No unprocessed Bitcoin donations + + + -query($SQL); $Donations = $DB->to_array(); @@ -69,110 +69,112 @@ list($Total) = $DB->next_record(); if (empty($_GET['email']) && empty($_GET['username']) && empty($_GET['source']) && !isset($_GET['page']) && !$DonationTimeline = $Cache->get_value('donation_timeline')) { - include(SERVER_ROOT.'/classes/charts.class.php'); - $DB->query(" - SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, SUM(Amount) - FROM donations - GROUP BY Month - ORDER BY Time DESC - LIMIT 1, 18"); - $Timeline = array_reverse($DB->to_array()); - $Area = new AREA_GRAPH(880, 160, array('Break' => 1)); - foreach ($Timeline as $Entry) { - list($Label, $Amount) = $Entry; - $Area->add($Label, $Amount); - } - $Area->transparent(); - $Area->grid_lines(); - $Area->color('3d7930'); - $Area->lines(2); - $Area->generate(); - $DonationTimeline = $Area->url(); - $Cache->cache_value('donation_timeline', $DonationTimeline, mktime(0, 0, 0, date('n') + 1, 2)); + include(SERVER_ROOT.'/classes/charts.class.php'); + $DB->query(" + SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, SUM(Amount) + FROM donations + GROUP BY Month + ORDER BY Time DESC + LIMIT 1, 18"); + $Timeline = array_reverse($DB->to_array()); + $Area = new AREA_GRAPH(880, 160, array('Break' => 1)); + foreach ($Timeline as $Entry) { + list($Label, $Amount) = $Entry; + $Area->add($Label, $Amount); + } + $Area->transparent(); + $Area->grid_lines(); + $Area->color('3d7930'); + $Area->lines(2); + $Area->generate(); + $DonationTimeline = $Area->url(); + $Cache->cache_value('donation_timeline', $DonationTimeline, mktime(0, 0, 0, date('n') + 1, 2)); } View::show_header('Donation log'); if (empty($_GET['email']) && empty($_GET['source']) && empty($_GET['username']) && !isset($_GET['page'])) { ?>
    - Donation timeline. The "y" axis is donation amount. + Donation timeline. The "y" axis is donation amount.

    - +
    -
    - - - - - - - - - - - - - - - - - - - - - -
    Username: - -
    Email: - -
    Source: - -
    Date Range: - - -
    - -
    -
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Username: + +
    Email: + +
    Source: + +
    Date Range: + + +
    + +
    +

    - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - + + + +
    UserAmountEmailSourceReasonTime
    ()
    UserAmountEmailSourceReasonTime
    ()
    Page TotalTotalPage TotalTotal
    - + diff --git a/sections/tools/finances/donation_log.php b/sections/tools/finances/donation_log.php index f8ccaafae..fdc0492fc 100644 --- a/sections/tools/finances/donation_log.php +++ b/sections/tools/finances/donation_log.php @@ -1,7 +1,7 @@ -query($SQL); $Donations = $DB->to_array(); @@ -69,110 +69,112 @@ list($Total) = $DB->next_record(); if (empty($_GET['email']) && empty($_GET['username']) && empty($_GET['source']) && !isset($_GET['page']) && !$DonationTimeline = $Cache->get_value('donation_timeline')) { - include(SERVER_ROOT.'/classes/charts.class.php'); - $DB->query(" - SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, SUM(Amount) - FROM donations - GROUP BY Month - ORDER BY Time DESC - LIMIT 1, 18"); - $Timeline = array_reverse($DB->to_array()); - $Area = new AREA_GRAPH(880, 160, array('Break' => 1)); - foreach ($Timeline as $Entry) { - list($Label, $Amount) = $Entry; - $Area->add($Label, $Amount); - } - $Area->transparent(); - $Area->grid_lines(); - $Area->color('3d7930'); - $Area->lines(2); - $Area->generate(); - $DonationTimeline = $Area->url(); - $Cache->cache_value('donation_timeline', $DonationTimeline, mktime(0, 0, 0, date('n') + 1, 2)); + include(SERVER_ROOT.'/classes/charts.class.php'); + $DB->query(" + SELECT DATE_FORMAT(Time,'%b \'%y') AS Month, SUM(Amount) + FROM donations + GROUP BY Month + ORDER BY Time DESC + LIMIT 1, 18"); + $Timeline = array_reverse($DB->to_array()); + $Area = new AREA_GRAPH(880, 160, array('Break' => 1)); + foreach ($Timeline as $Entry) { + list($Label, $Amount) = $Entry; + $Area->add($Label, $Amount); + } + $Area->transparent(); + $Area->grid_lines(); + $Area->color('3d7930'); + $Area->lines(2); + $Area->generate(); + $DonationTimeline = $Area->url(); + $Cache->cache_value('donation_timeline', $DonationTimeline, mktime(0, 0, 0, date('n') + 1, 2)); } View::show_header('Donation log'); if (empty($_GET['email']) && empty($_GET['source']) && empty($_GET['username']) && !isset($_GET['page'])) { ?>
    - Donation timeline. The "y" axis is donation amount. + Donation timeline. The "y" axis is donation amount.

    - +
    -
    - - - - - - - - - - - - - - - - - - - - - -
    Username: - -
    Email: - -
    Source: - -
    Date Range: - - -
    - -
    -
    +
    + + + + + + + + + + + + + + + + + + + + + +
    Username: + +
    Email: + +
    Source: + +
    Date Range: + + +
    + +
    +

    - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - + + + +
    UserAmountEmailSourceReasonTime
    ()
    UserAmountEmailSourceReasonTime
    ()
    Page TotalTotalPage TotalTotal
    - + diff --git a/sections/tools/finances/donor_rewards.php b/sections/tools/finances/donor_rewards.php index fbafe52f6..da51acdaf 100644 --- a/sections/tools/finances/donor_rewards.php +++ b/sections/tools/finances/donor_rewards.php @@ -1,6 +1,6 @@ -query(" - SELECT - SQL_CALC_FOUND_ROWS - u.Username, - d.UserID, - d.Rank, - d.Hidden, - d.DonationTime, - r.IconMouseOverText, - r.AvatarMouseOverText, - r.CustomIcon, - r.SecondAvatar, - r.CustomIconLink - FROM users_donor_ranks AS d - LEFT JOIN users_main AS u ON u.ID = d.UserID - LEFT JOIN donor_rewards AS r ON r.UserID = d.UserID - $SearchQuery - ORDER BY d.Rank DESC - LIMIT $Limit"); + SELECT + SQL_CALC_FOUND_ROWS + u.Username, + d.UserID, + d.Rank, + d.Hidden, + d.DonationTime, + r.IconMouseOverText, + r.AvatarMouseOverText, + r.CustomIcon, + r.SecondAvatar, + r.CustomIconLink + FROM users_donor_ranks AS d + LEFT JOIN users_main AS u ON u.ID = d.UserID + LEFT JOIN donor_rewards AS r ON r.UserID = d.UserID + $SearchQuery + ORDER BY d.Rank DESC + LIMIT $Limit"); $Users = $DB->to_array(); $DB->query('SELECT FOUND_ROWS()'); @@ -42,63 +42,63 @@ View::show_header($Title); ?>
    -

    - +

    +
    - - Username Search: - + + Username Search: +
    - - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - -"> + + + + + + + + + + + -
    UsernameRankHiddenLast DonatedIcon TextIconIcon LinkAvatar TextSecond Avatar
    UsernameRankHiddenLast DonatedIcon TextIconIcon LinkAvatar TextSecond Avatar
    - - - - - - - - - - - -
    + + + + + + + + + + + +
    - - + +query(" - UPDATE news - SET Title = '".db_string($_POST['title'])."', - Body = '".db_string($_POST['body'])."' - WHERE ID = '".db_string($_POST['newsid'])."'"); - $Cache->delete_value('news'); - $Cache->delete_value('feed_news'); - } - header('Location: index.php'); - break; - - case 'deletenews': - if (!check_perms('admin_manage_news')) { - error(403); - } - if (is_number($_GET['id'])) { - authorize(); - $DB->query(" - DELETE FROM news - WHERE ID = '".db_string($_GET['id'])."'"); - $Cache->delete_value('news'); - $Cache->delete_value('feed_news'); - - // Deleting latest news - $LatestNews = $Cache->get_value('news_latest_id'); - if ($LatestNews !== false && $LatestNews == $_GET['id']) { - $Cache->delete_value('news_latest_id'); - $Cache->delete_value('news_latest_title'); - } - } - header('Location: index.php'); - break; - - case 'takenewnews': - if (!check_perms('admin_manage_news')) { - error(403); - } - - $DB->query(" - INSERT INTO news (UserID, Title, Body, Time) - VALUES ('$LoggedUser[ID]', '".db_string($_POST['title'])."', '".db_string($_POST['body'])."', '".sqltime()."')"); - $Cache->delete_value('news_latest_id'); - $Cache->delete_value('news_latest_title'); - $Cache->delete_value('news'); - - - - NotificationsManager::send_push(NotificationsManager::get_push_enabled_users(), $_POST['title'], $_POST['body'], site_url() . 'index.php', NotificationsManager::NEWS); - - header('Location: index.php'); - break; - - case 'bonus_points': - include(SERVER_ROOT.'/sections/tools/managers/bonus_points.php'); - break; - case 'tokens': - include(SERVER_ROOT.'/sections/tools/managers/tokens.php'); - break; - case 'multiple_freeleech': - include(SERVER_ROOT.'/sections/tools/managers/multiple_freeleech.php'); - break; - case 'ocelot': - include(SERVER_ROOT.'/sections/tools/managers/ocelot.php'); - break; - case 'ocelot_info': - include(SERVER_ROOT.'/sections/tools/data/ocelot_info.php'); - break; - case 'official_tags': - include(SERVER_ROOT.'/sections/tools/managers/official_tags.php'); - break; - case 'edit_tags': - include(SERVER_ROOT.'/sections/tools/misc/tags.php'); - break; - case 'tag_aliases': - include(SERVER_ROOT.'/sections/tools/managers/tag_aliases.php'); - break; - case 'label_aliases': - include(SERVER_ROOT.'/sections/tools/managers/label_aliases.php'); - break; - case 'change_log': - include(SERVER_ROOT.'/sections/tools/managers/change_log.php'); - break; - case 'global_notification': - include(SERVER_ROOT.'/sections/tools/managers/global_notification.php'); - break; - case 'take_global_notification': - include(SERVER_ROOT.'/sections/tools/managers/take_global_notification.php'); - break; - case 'permissions': - if (!check_perms('admin_manage_permissions')) { - error(403); - } - - if (!empty($_REQUEST['id'])) { - $Val->SetFields('name', true, 'string', 'You did not enter a valid name for this permission set.'); - $Val->SetFields('level', true, 'number', 'You did not enter a valid level for this permission set.'); - $_POST['maxcollages'] = (empty($_POST['maxcollages'])) ? 0 : $_POST['maxcollages']; - $Val->SetFields('maxcollages', true, 'number', 'You did not enter a valid number of personal collages.'); - - if (is_numeric($_REQUEST['id'])) { - $DB->prepared_query(" - SELECT p.ID, p.Name, p.Level, p.Secondary, p.PermittedForums, p.Values, p.DisplayStaff, p.StaffGroup, COUNT(u.ID) - FROM permissions AS p - LEFT JOIN users_main AS u ON u.PermissionID = p.ID - WHERE p.ID = ? - GROUP BY p.ID", $_REQUEST['id']); - list($ID, $Name, $Level, $Secondary, $Forums, $Values, $DisplayStaff, $StaffGroup, $UserCount) = $DB->next_record(MYSQLI_NUM, array(5)); - - if (!check_perms('admin_manage_permissions', $Level)) { - error(403); - } - $Values = unserialize($Values); - } - - if (!empty($_POST['submit'])) { - $Err = $Val->ValidateForm($_POST); - - if (!is_numeric($_REQUEST['id'])) { - $DB->prepared_query(" - SELECT ID - FROM permissions - WHERE Level = ?", $_REQUEST['level']); - list($DupeCheck)=$DB->next_record(); - - if ($DupeCheck) { - $Err = 'There is already a permission class with that level.'; - } - } - - $Values = array(); - foreach ($_REQUEST as $Key => $Perms) { - if (substr($Key, 0, 5) == 'perm_') { - $Values[substr($Key, 5)] = (int)$Perms; - } - } - - $Name = $_REQUEST['name']; - $Level = $_REQUEST['level']; - $Secondary = empty($_REQUEST['secondary']) ? 0 : 1; - $Forums = $_REQUEST['forums']; - $DisplayStaff = $_REQUEST['displaystaff']; - $StaffGroup = $_REQUEST['staffgroup']; - if (!$StaffGroup) { - $StaffGroup = null; - } - $Values['MaxCollages'] = $_REQUEST['maxcollages']; - - if (!$Err) { - if (!is_numeric($_REQUEST['id'])) { - $DB->prepared_query(" - INSERT INTO permissions (Level, Name, Secondary, PermittedForums, `Values`, DisplayStaff, StaffGroup) - VALUES (?, ?, ?, ?, ?, ?, ?)", - $Level, $Name, $Secondary, $Forums, serialize($Values), $DisplayStaff, $StaffGroup); - } else { - $DB->prepared_query(" - UPDATE permissions - SET Level = ?, - Name = ?, - Secondary = ?, - PermittedForums = ?, - `Values` = ?, - DisplayStaff = ?, - StaffGroup = ? - WHERE ID = ?", - $Level, $Name, $Secondary, $Forums, serialize($Values), $DisplayStaff, $StaffGroup, $_REQUEST['id']); - $Cache->delete_value('perm_'.$_REQUEST['id']); - if ($Secondary) { - $DB->prepared_query(" - SELECT DISTINCT UserID - FROM users_levels - WHERE PermissionID = ?", $_REQUEST['id']); - while ($UserID = $DB->next_record()) { - $Cache->delete_value("user_info_heavy_$UserID"); - } - } - } - $Cache->delete_value('classes'); - $Cache->delete_value('staff'); - } else { - error($Err); - } - } - - include(SERVER_ROOT.'/sections/tools/managers/permissions_alter.php'); - - } else { - if (!empty($_REQUEST['removeid'])) { - $DB->prepared_query(" - DELETE FROM permissions - WHERE ID = ?", $_REQUEST['removeid']); - $DB->prepared_query(" - SELECT UserID - FROM users_levels - WHERE PermissionID = ?", $_REQUEST['removeid']); - while (list($UserID) = $DB->next_record()) { - $Cache->delete_value("user_info_$UserID"); - $Cache->delete_value("user_info_heavy_$UserID"); - } - $DB->prepared_query(" - DELETE FROM users_levels - WHERE PermissionID = ?", $_REQUEST['removeid']); - $DB->prepared_query(" - SELECT ID - FROM users_main - WHERE PermissionID = ?", $_REQUEST['removeid']); - while (list($UserID) = $DB->next_record()) { - $Cache->delete_value("user_info_$UserID"); - $Cache->delete_value("user_info_heavy_$UserID"); - } - $DB->prepared_query(" - UPDATE users_main - SET PermissionID = ? - WHERE PermissionID = ?", USER, $_REQUEST['removeid']); - - $Cache->delete_value('classes'); - } - - include(SERVER_ROOT.'/sections/tools/managers/permissions_list.php'); - } - break; - case 'staff_groups_alter': - include(SERVER_ROOT.'/sections/tools/managers/staff_groups_alter.php'); - break; - case 'staff_groups': - include(SERVER_ROOT.'/sections/tools/managers/staff_groups_list.php'); - break; - case 'ip_ban': - //TODO: Clean up DB table ip_bans. - include(SERVER_ROOT.'/sections/tools/managers/bans.php'); - break; - case 'quick_ban': - include(SERVER_ROOT.'/sections/tools/misc/quick_ban.php'); - break; - //Data - case 'registration_log': - include(SERVER_ROOT.'/sections/tools/data/registration_log.php'); - break; - - case 'prvlog': - include(SERVER_ROOT.'/sections/tools/finances/btc_log.php'); - break; - - case 'bitcoin_unproc': - include(SERVER_ROOT.'/sections/tools/finances/bitcoin_unproc.php'); - break; - - case 'bitcoin_balance': - include(SERVER_ROOT.'/sections/tools/finances/bitcoin_balance.php'); - break; - - case 'donor_rewards': - include(SERVER_ROOT.'/sections/tools/finances/donor_rewards.php'); - break; - case 'upscale_pool': - include(SERVER_ROOT.'/sections/tools/data/upscale_pool.php'); - break; - - case 'invite_pool': - include(SERVER_ROOT.'/sections/tools/data/invite_pool.php'); - break; - - case 'torrent_stats': - include(SERVER_ROOT.'/sections/tools/data/torrent_stats.php'); - break; - - case 'user_flow': - include(SERVER_ROOT.'/sections/tools/data/user_flow.php'); - break; - - case 'economic_stats': - include(SERVER_ROOT.'/sections/tools/data/economic_stats.php'); - break; - - case 'service_stats': - include(SERVER_ROOT.'/sections/tools/development/service_stats.php'); - break; - - case 'database_specifics': - include(SERVER_ROOT.'/sections/tools/data/database_specifics.php'); - break; - - case 'special_users': - include(SERVER_ROOT.'/sections/tools/data/special_users.php'); - break; - - case 'platform_usage': - include(SERVER_ROOT.'/sections/tools/data/platform_usage.php'); - break; - //END Data - - //Misc - case 'update_geoip': - include(SERVER_ROOT.'/sections/tools/development/update_geoip.php'); - break; - - case 'update_offsets': - include(SERVER_ROOT.'/sections/tools/development/update_offsets.php'); - break; - - case 'dupe_ips': - include(SERVER_ROOT.'/sections/tools/misc/dupe_ip.php'); - break; - - case 'clear_cache': - include(SERVER_ROOT.'/sections/tools/development/clear_cache.php'); - break; - - case 'create_user': - include(SERVER_ROOT.'/sections/tools/misc/create_user.php'); - break; - - case 'manipulate_tree': - include(SERVER_ROOT.'/sections/tools/misc/manipulate_tree.php'); - break; - - case 'site_info': - include(SERVER_ROOT.'/sections/tools/development/site_info.php'); - break; - - case 'site_options': - include(SERVER_ROOT.'/sections/tools/development/site_options.php'); - break; - - case 'recommendations': - include(SERVER_ROOT.'/sections/tools/misc/recommendations.php'); - break; - - case 'analysis': - include(SERVER_ROOT.'/sections/tools/misc/analysis.php'); - break; - - case 'process_info': - include(SERVER_ROOT.'/sections/tools/development/process_info.php'); - break; - - case 'rerender_gallery': - include(SERVER_ROOT.'/sections/tools/development/rerender_gallery.php'); - break; - - case 'public_sandbox': - include(SERVER_ROOT.'/sections/tools/sandboxes/public_sandbox.php'); - break; - - case 'mod_sandbox': - if (check_perms('users_mod')) { - include(SERVER_ROOT.'/sections/tools/sandboxes/mod_sandbox.php'); - } else { - error(403); - } - break; - case 'bbcode_sandbox': - include(SERVER_ROOT.'/sections/tools/sandboxes/bbcode_sandbox.php'); - break; - case 'calendar': - include(SERVER_ROOT.'/sections/tools/managers/calendar.php'); - break; - case 'get_calendar_event': - include(SERVER_ROOT.'/sections/tools/managers/ajax_get_calendar_event.php'); - break; - case 'take_calendar_event': - include(SERVER_ROOT.'/sections/tools/managers/ajax_take_calendar_event.php'); - break; - case 'stylesheets': - include(SERVER_ROOT.'/sections/tools/managers/stylesheets_list.php'); - break; - case 'mass_pm': - include(SERVER_ROOT.'/sections/tools/managers/mass_pm.php'); - break; - case 'take_mass_pm': - include(SERVER_ROOT.'/sections/tools/managers/take_mass_pm.php'); - break; - case 'monthalbum': - include(SERVER_ROOT.'/sections/tools/misc/album_of_month.php'); - break; - case 'vanityhouse': - include(SERVER_ROOT.'/sections/tools/misc/vanity_house.php'); - break; - case 'dbkey': - include(SERVER_ROOT.'/sections/tools/managers/db_key.php'); - break; - case 'navigation_alter': - include(SERVER_ROOT.'/sections/tools/managers/navigation_alter.php'); - break; - case 'navigation': - include(SERVER_ROOT.'/sections/tools/managers/navigation_list.php'); - break; - default: - include(SERVER_ROOT.'/sections/tools/tools.php'); + case 'phpinfo': + if (!check_perms('site_debug')) { + error(403); + } + phpinfo(); + break; + //Services + case 'get_host': + include(SERVER_ROOT.'/sections/tools/services/get_host.php'); + break; + case 'get_cc': + include(SERVER_ROOT.'/sections/tools/services/get_cc.php'); + break; + //Managers + case 'categories': + include(SERVER_ROOT . '/sections/tools/managers/categories_list.php'); + break; + + case 'categories_alter': + include(SERVER_ROOT . '/sections/tools/managers/categories_alter.php'); + break; + + case 'forum': + include(SERVER_ROOT.'/sections/tools/managers/forum_list.php'); + break; + + case 'forum_alter': + include(SERVER_ROOT.'/sections/tools/managers/forum_alter.php'); + break; + + case 'irc': + include(SERVER_ROOT . '/sections/tools/managers/irc_list.php'); + break; + + case 'irc_alter': + include(SERVER_ROOT . '/sections/tools/managers/irc_alter.php'); + break; + + case 'whitelist': + include(SERVER_ROOT.'/sections/tools/managers/whitelist_list.php'); + break; + + case 'whitelist_alter': + include(SERVER_ROOT.'/sections/tools/managers/whitelist_alter.php'); + break; + + case 'referral_accounts': + include(SERVER_ROOT.'/sections/tools/managers/referral_accounts.php'); + break; + + case 'referral_alter': + include(SERVER_ROOT.'/sections/tools/managers/referral_alter.php'); + break; + + case 'referral_users': + include(SERVER_ROOT.'/sections/tools/managers/referral_users.php'); + break; + + case 'payment_alter': + include(SERVER_ROOT.'/sections/tools/managers/payment_alter.php'); + break; + + case 'payment_list': + include(SERVER_ROOT.'/sections/tools/managers/payment_list.php'); + break; + + case 'enable_requests': + include(SERVER_ROOT.'/sections/tools/managers/enable_requests.php'); + break; + case 'ajax_take_enable_request': + if (FEATURE_EMAIL_REENABLE) { + include(SERVER_ROOT.'/sections/tools/managers/ajax_take_enable_request.php'); + } else { + // Prevent post requests to the ajax page + header("Location: tools.php"); + die(); + } + break; + + case 'login_watch': + include(SERVER_ROOT.'/sections/tools/managers/login_watch.php'); + break; + + case 'recommend': + include(SERVER_ROOT.'/sections/tools/managers/recommend_list.php'); + break; + + case 'recommend_add': + include(SERVER_ROOT.'/sections/tools/managers/recommend_add.php'); + break; + + case 'recommend_alter': + include(SERVER_ROOT.'/sections/tools/managers/recommend_alter.php'); + break; + + case 'recommend_restore': + include(SERVER_ROOT.'/sections/tools/managers/recommend_restore.php'); + break; + + case 'email_blacklist': + include(SERVER_ROOT.'/sections/tools/managers/email_blacklist.php'); + break; + + case 'email_blacklist_alter': + include(SERVER_ROOT.'/sections/tools/managers/email_blacklist_alter.php'); + break; + + case 'email_blacklist_search': + include(SERVER_ROOT.'/sections/tools/managers/email_blacklist_search.php'); + break; + + case 'dnu': + include(SERVER_ROOT.'/sections/tools/managers/dnu_list.php'); + break; + + case 'dnu_alter': + include(SERVER_ROOT.'/sections/tools/managers/dnu_alter.php'); + break; + + case 'editnews': + case 'news': + include(SERVER_ROOT.'/sections/tools/managers/news.php'); + break; + + case 'takeeditnews': + if (!check_perms('admin_manage_news')) { + error(403); + } + if (is_number($_POST['newsid'])) { + $DB->query(" + UPDATE news + SET Title = '".db_string($_POST['title'])."', + Body = '".db_string($_POST['body'])."' + WHERE ID = '".db_string($_POST['newsid'])."'"); + $Cache->delete_value('news'); + $Cache->delete_value('feed_news'); + } + header('Location: index.php'); + break; + + case 'deletenews': + if (!check_perms('admin_manage_news')) { + error(403); + } + if (is_number($_GET['id'])) { + authorize(); + $DB->query(" + DELETE FROM news + WHERE ID = '".db_string($_GET['id'])."'"); + $Cache->delete_value('news'); + $Cache->delete_value('feed_news'); + + // Deleting latest news + $LatestNews = $Cache->get_value('news_latest_id'); + if ($LatestNews !== false && $LatestNews == $_GET['id']) { + $Cache->delete_value('news_latest_id'); + $Cache->delete_value('news_latest_title'); + } + } + header('Location: index.php'); + break; + + case 'takenewnews': + if (!check_perms('admin_manage_news')) { + error(403); + } + + $DB->query(" + INSERT INTO news (UserID, Title, Body, Time) + VALUES ('$LoggedUser[ID]', '".db_string($_POST['title'])."', '".db_string($_POST['body'])."', '".sqltime()."')"); + $Cache->delete_value('news_latest_id'); + $Cache->delete_value('news_latest_title'); + $Cache->delete_value('news'); + + + + NotificationsManager::send_push(NotificationsManager::get_push_enabled_users(), $_POST['title'], $_POST['body'], site_url() . 'index.php', NotificationsManager::NEWS); + + header('Location: index.php'); + break; + + case 'bonus_points': + include(SERVER_ROOT.'/sections/tools/managers/bonus_points.php'); + break; + case 'tokens': + include(SERVER_ROOT.'/sections/tools/managers/tokens.php'); + break; + case 'multiple_freeleech': + include(SERVER_ROOT.'/sections/tools/managers/multiple_freeleech.php'); + break; + case 'ocelot': + include(SERVER_ROOT.'/sections/tools/managers/ocelot.php'); + break; + case 'ocelot_info': + include(SERVER_ROOT.'/sections/tools/data/ocelot_info.php'); + break; + case 'official_tags': + include(SERVER_ROOT.'/sections/tools/managers/official_tags.php'); + break; + case 'edit_tags': + include(SERVER_ROOT.'/sections/tools/misc/tags.php'); + break; + case 'tag_aliases': + include(SERVER_ROOT.'/sections/tools/managers/tag_aliases.php'); + break; + case 'label_aliases': + include(SERVER_ROOT.'/sections/tools/managers/label_aliases.php'); + break; + case 'change_log': + include(SERVER_ROOT.'/sections/tools/managers/change_log.php'); + break; + case 'global_notification': + include(SERVER_ROOT.'/sections/tools/managers/global_notification.php'); + break; + case 'take_global_notification': + include(SERVER_ROOT.'/sections/tools/managers/take_global_notification.php'); + break; + case 'permissions': + if (!check_perms('admin_manage_permissions')) { + error(403); + } + + if (!empty($_REQUEST['id'])) { + $Val->SetFields('name', true, 'string', 'You did not enter a valid name for this permission set.'); + $Val->SetFields('level', true, 'number', 'You did not enter a valid level for this permission set.'); + $_POST['maxcollages'] = (empty($_POST['maxcollages'])) ? 0 : $_POST['maxcollages']; + $Val->SetFields('maxcollages', true, 'number', 'You did not enter a valid number of personal collages.'); + + if (is_numeric($_REQUEST['id'])) { + $DB->prepared_query(" + SELECT p.ID, p.Name, p.Level, p.Secondary, p.PermittedForums, p.Values, p.DisplayStaff, p.StaffGroup, COUNT(u.ID) + FROM permissions AS p + LEFT JOIN users_main AS u ON u.PermissionID = p.ID + WHERE p.ID = ? + GROUP BY p.ID", $_REQUEST['id']); + list($ID, $Name, $Level, $Secondary, $Forums, $Values, $DisplayStaff, $StaffGroup, $UserCount) = $DB->next_record(MYSQLI_NUM, array(5)); + + if (!check_perms('admin_manage_permissions', $Level)) { + error(403); + } + $Values = unserialize($Values); + } + + if (!empty($_POST['submit'])) { + $Err = $Val->ValidateForm($_POST); + + if (!is_numeric($_REQUEST['id'])) { + $DB->prepared_query(" + SELECT ID + FROM permissions + WHERE Level = ?", $_REQUEST['level']); + list($DupeCheck)=$DB->next_record(); + + if ($DupeCheck) { + $Err = 'There is already a permission class with that level.'; + } + } + + $Values = []; + foreach ($_REQUEST as $Key => $Perms) { + if (substr($Key, 0, 5) == 'perm_') { + $Values[substr($Key, 5)] = (int)$Perms; + } + } + + $Name = $_REQUEST['name']; + $Level = $_REQUEST['level']; + $Secondary = empty($_REQUEST['secondary']) ? 0 : 1; + $Forums = $_REQUEST['forums']; + $DisplayStaff = $_REQUEST['displaystaff']; + $StaffGroup = $_REQUEST['staffgroup']; + if (!$StaffGroup) { + $StaffGroup = null; + } + $Values['MaxCollages'] = $_REQUEST['maxcollages']; + + if (!$Err) { + if (!is_numeric($_REQUEST['id'])) { + $DB->prepared_query(" + INSERT INTO permissions (Level, Name, Secondary, PermittedForums, `Values`, DisplayStaff, StaffGroup) + VALUES (?, ?, ?, ?, ?, ?, ?)", + $Level, $Name, $Secondary, $Forums, serialize($Values), $DisplayStaff, $StaffGroup); + } else { + $DB->prepared_query(" + UPDATE permissions + SET Level = ?, + Name = ?, + Secondary = ?, + PermittedForums = ?, + `Values` = ?, + DisplayStaff = ?, + StaffGroup = ? + WHERE ID = ?", + $Level, $Name, $Secondary, $Forums, serialize($Values), $DisplayStaff, $StaffGroup, $_REQUEST['id']); + $Cache->delete_value('perm_'.$_REQUEST['id']); + if ($Secondary) { + $DB->prepared_query(" + SELECT DISTINCT UserID + FROM users_levels + WHERE PermissionID = ?", $_REQUEST['id']); + while ($UserID = $DB->next_record()) { + $Cache->delete_value("user_info_heavy_$UserID"); + } + } + } + $Cache->delete_value('classes'); + $Cache->delete_value('staff'); + } else { + error($Err); + } + } + + include(SERVER_ROOT.'/sections/tools/managers/permissions_alter.php'); + + } else { + if (!empty($_REQUEST['removeid'])) { + $DB->prepared_query(" + DELETE FROM permissions + WHERE ID = ?", $_REQUEST['removeid']); + $DB->prepared_query(" + SELECT UserID + FROM users_levels + WHERE PermissionID = ?", $_REQUEST['removeid']); + while (list($UserID) = $DB->next_record()) { + $Cache->delete_value("user_info_$UserID"); + $Cache->delete_value("user_info_heavy_$UserID"); + } + $DB->prepared_query(" + DELETE FROM users_levels + WHERE PermissionID = ?", $_REQUEST['removeid']); + $DB->prepared_query(" + SELECT ID + FROM users_main + WHERE PermissionID = ?", $_REQUEST['removeid']); + while (list($UserID) = $DB->next_record()) { + $Cache->delete_value("user_info_$UserID"); + $Cache->delete_value("user_info_heavy_$UserID"); + } + $DB->prepared_query(" + UPDATE users_main + SET PermissionID = ? + WHERE PermissionID = ?", USER, $_REQUEST['removeid']); + + $Cache->delete_value('classes'); + } + + include(SERVER_ROOT.'/sections/tools/managers/permissions_list.php'); + } + break; + case 'staff_groups_alter': + include(SERVER_ROOT.'/sections/tools/managers/staff_groups_alter.php'); + break; + case 'staff_groups': + include(SERVER_ROOT.'/sections/tools/managers/staff_groups_list.php'); + break; + case 'ip_ban': + //TODO: Clean up DB table ip_bans. + include(SERVER_ROOT.'/sections/tools/managers/bans.php'); + break; + case 'quick_ban': + include(SERVER_ROOT.'/sections/tools/misc/quick_ban.php'); + break; + //Data + case 'registration_log': + include(SERVER_ROOT.'/sections/tools/data/registration_log.php'); + break; + + case 'prvlog': + include(SERVER_ROOT.'/sections/tools/finances/btc_log.php'); + break; + + case 'bitcoin_unproc': + include(SERVER_ROOT.'/sections/tools/finances/bitcoin_unproc.php'); + break; + + case 'bitcoin_balance': + include(SERVER_ROOT.'/sections/tools/finances/bitcoin_balance.php'); + break; + + case 'donor_rewards': + include(SERVER_ROOT.'/sections/tools/finances/donor_rewards.php'); + break; + case 'upscale_pool': + include(SERVER_ROOT.'/sections/tools/data/upscale_pool.php'); + break; + + case 'invite_pool': + include(SERVER_ROOT.'/sections/tools/data/invite_pool.php'); + break; + + case 'torrent_stats': + include(SERVER_ROOT.'/sections/tools/data/torrent_stats.php'); + break; + + case 'user_flow': + include(SERVER_ROOT.'/sections/tools/data/user_flow.php'); + break; + + case 'economic_stats': + include(SERVER_ROOT.'/sections/tools/data/economic_stats.php'); + break; + + case 'service_stats': + include(SERVER_ROOT.'/sections/tools/development/service_stats.php'); + break; + + case 'database_specifics': + include(SERVER_ROOT.'/sections/tools/data/database_specifics.php'); + break; + + case 'special_users': + include(SERVER_ROOT.'/sections/tools/data/special_users.php'); + break; + + case 'platform_usage': + include(SERVER_ROOT.'/sections/tools/data/platform_usage.php'); + break; + //END Data + + //Misc + case 'update_geoip': + include(SERVER_ROOT.'/sections/tools/development/update_geoip.php'); + break; + + case 'update_offsets': + include(SERVER_ROOT.'/sections/tools/development/update_offsets.php'); + break; + + case 'dupe_ips': + include(SERVER_ROOT.'/sections/tools/misc/dupe_ip.php'); + break; + + case 'clear_cache': + include(SERVER_ROOT.'/sections/tools/development/clear_cache.php'); + break; + + case 'create_user': + include(SERVER_ROOT.'/sections/tools/misc/create_user.php'); + break; + + case 'manipulate_tree': + include(SERVER_ROOT.'/sections/tools/misc/manipulate_tree.php'); + break; + + case 'site_info': + include(SERVER_ROOT.'/sections/tools/development/site_info.php'); + break; + + case 'site_options': + include(SERVER_ROOT.'/sections/tools/development/site_options.php'); + break; + + case 'recommendations': + include(SERVER_ROOT.'/sections/tools/misc/recommendations.php'); + break; + + case 'analysis': + include(SERVER_ROOT.'/sections/tools/misc/analysis.php'); + break; + + case 'process_info': + include(SERVER_ROOT.'/sections/tools/development/process_info.php'); + break; + + case 'rerender_gallery': + include(SERVER_ROOT.'/sections/tools/development/rerender_gallery.php'); + break; + + case 'public_sandbox': + include(SERVER_ROOT.'/sections/tools/sandboxes/public_sandbox.php'); + break; + + case 'mod_sandbox': + if (check_perms('users_mod')) { + include(SERVER_ROOT.'/sections/tools/sandboxes/mod_sandbox.php'); + } else { + error(403); + } + break; + case 'bbcode_sandbox': + include(SERVER_ROOT.'/sections/tools/sandboxes/bbcode_sandbox.php'); + break; + case 'artist_importance_sandbox': + include(SERVER_ROOT.'/sections/tools/sandboxes/artist_importance_sandbox.php'); + break; + case 'db_sandbox': + include(SERVER_ROOT.'/sections/tools/sandboxes/db_sandbox.php'); + break; + case 'referral_sandbox': + include(SERVER_ROOT.'/sections/tools/sandboxes/referral_sandbox.php'); + break; + case 'calendar': + include(SERVER_ROOT.'/sections/tools/managers/calendar.php'); + break; + case 'get_calendar_event': + include(SERVER_ROOT.'/sections/tools/managers/ajax_get_calendar_event.php'); + break; + case 'take_calendar_event': + include(SERVER_ROOT.'/sections/tools/managers/ajax_take_calendar_event.php'); + break; + case 'stylesheets': + include(SERVER_ROOT.'/sections/tools/managers/stylesheets_list.php'); + break; + case 'mass_pm': + include(SERVER_ROOT.'/sections/tools/managers/mass_pm.php'); + break; + case 'take_mass_pm': + include(SERVER_ROOT.'/sections/tools/managers/take_mass_pm.php'); + break; + case 'monthalbum': + include(SERVER_ROOT.'/sections/tools/misc/album_of_month.php'); + break; + case 'vanityhouse': + include(SERVER_ROOT.'/sections/tools/misc/vanity_house.php'); + break; + case 'dbkey': + include(SERVER_ROOT.'/sections/tools/managers/db_key.php'); + break; + case 'navigation_alter': + include(SERVER_ROOT.'/sections/tools/managers/navigation_alter.php'); + break; + case 'navigation': + include(SERVER_ROOT.'/sections/tools/managers/navigation_list.php'); + break; + case 'forum_transitions': + include(SERVER_ROOT.'/sections/tools/managers/forum_transitions_list.php'); + break; + case 'forum_transitions_alter': + include(SERVER_ROOT.'/sections/tools/managers/forum_transitions_alter.php'); + break; + default: + include(SERVER_ROOT.'/sections/tools/tools.php'); } ?> diff --git a/sections/tools/managers/ajax_get_calendar_event.php b/sections/tools/managers/ajax_get_calendar_event.php index 4be0cc124..9feb63ed5 100644 --- a/sections/tools/managers/ajax_get_calendar_event.php +++ b/sections/tools/managers/ajax_get_calendar_event.php @@ -1,130 +1,141 @@ -
    - - - - - - - - - - - - - - -query(" +query(" SELECT COUNT(CheckedBy), CheckedBy FROM users_enable_requests WHERE CheckedBy IS NOT NULL @@ -151,7 +151,7 @@ -set_query_id($QueryID); ?>
    Title: - -
    Category: - + + + + + + + + + + + + + - - - - + + + + - - - - + + + + - - - - - - - - - - - - - - - - - -"> + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + - - + +
    Title: + +
    Category: + -
    Importance: - +
    Importance: + -
    Team: - +
    Team: + -
    Start date: - - value="" /> - - value="" /> - -
    End date: - - value="" /> - - value="" /> - -
    Created by: - -
    - -
    Start date: + + value="" /> + + value="" /> + +
    End date: + + value="" /> + + value="" /> + +
    Created by: + +
    + +
    - - - - - - + + + +
    diff --git a/sections/tools/managers/ajax_take_calendar_event.php b/sections/tools/managers/ajax_take_calendar_event.php index e19419c06..e1ff4682d 100644 --- a/sections/tools/managers/ajax_take_calendar_event.php +++ b/sections/tools/managers/ajax_take_calendar_event.php @@ -1,16 +1,16 @@ -query('DELETE FROM ip_bans WHERE ID='.$_POST['id']); - $Cache->delete_value('ip_bans_'.$IPA); - } else { //Edit & Create, Shared Validation - $Val->SetFields('start', '1','regex','You must include the starting IP address.',array('regex'=>'/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/i')); - $Val->SetFields('end', '1','regex','You must include the ending IP address.',array('regex'=>'/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/i')); - $Val->SetFields('notes', '1','string','You must include the reason for the ban.'); - $Err=$Val->ValidateForm($_POST); // Validate the form - if ($Err) { - error($Err); - } + $IPA = substr($_POST['start'], 0, strcspn($_POST['start'], '.')); + if ($_POST['submit'] == 'Delete') { //Delete + if (!is_number($_POST['id']) || $_POST['id'] == '') { + error(0); + } + $DB->query('DELETE FROM ip_bans WHERE ID='.$_POST['id']); + $Cache->delete_value('ip_bans_'.$IPA); + } else { //Edit & Create, Shared Validation + $Val->SetFields('start', '1','regex','You must include the starting IP address.',array('regex'=>'/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/i')); + $Val->SetFields('end', '1','regex','You must include the ending IP address.',array('regex'=>'/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/i')); + $Val->SetFields('notes', '1','string','You must include the reason for the ban.'); + $Err=$Val->ValidateForm($_POST); // Validate the form + if ($Err) { + error($Err); + } - $Notes = db_string($_POST['notes']); - $Start = Tools::ip_to_unsigned($_POST['start']); //Sanitized by Validation regex - $End = Tools::ip_to_unsigned($_POST['end']); //See above + $Notes = db_string($_POST['notes']); + $Start = Tools::ip_to_unsigned($_POST['start']); //Sanitized by Validation regex + $End = Tools::ip_to_unsigned($_POST['end']); //See above - if ($_POST['submit'] == 'Edit') { //Edit - if (empty($_POST['id']) || !is_number($_POST['id'])) { - error(404); - } - $DB->query(" - UPDATE ip_bans - SET - FromIP=$Start, - ToIP='$End', - Reason='$Notes' - WHERE ID='".$_POST['id']."'"); - } else { //Create - $DB->query(" - INSERT INTO ip_bans - (FromIP, ToIP, Reason) - VALUES - ('$Start','$End', '$Notes')"); - } - $Cache->delete_value('ip_bans_'.$IPA); - } + if ($_POST['submit'] == 'Edit') { //Edit + if (empty($_POST['id']) || !is_number($_POST['id'])) { + error(404); + } + $DB->query(" + UPDATE ip_bans + SET + FromIP=$Start, + ToIP='$End', + Reason='$Notes' + WHERE ID='".$_POST['id']."'"); + } else { //Create + $DB->query(" + INSERT INTO ip_bans + (FromIP, ToIP, Reason) + VALUES + ('$Start','$End', '$Notes')"); + } + $Cache->delete_value('ip_bans_'.$IPA); + } } define('BANS_PER_PAGE', '20'); list($Page, $Limit) = Format::page_limit(BANS_PER_PAGE); $sql = " - SELECT - SQL_CALC_FOUND_ROWS - ID, - FromIP, - ToIP, - Reason - FROM ip_bans "; + SELECT + SQL_CALC_FOUND_ROWS + ID, + FromIP, + ToIP, + Reason + FROM ip_bans "; if (!empty($_REQUEST['notes'])) { - $sql .= "WHERE Reason LIKE '%".db_string($_REQUEST['notes'])."%' "; + $sql .= "WHERE Reason LIKE '%".db_string($_REQUEST['notes'])."%' "; } if (!empty($_REQUEST['ip']) && preg_match('/'.IP_REGEX.'/', $_REQUEST['ip'])) { - if (!empty($_REQUEST['notes'])) { - $sql .= "AND '".Tools::ip_to_unsigned($_REQUEST['ip'])."' BETWEEN FromIP AND ToIP "; - } else { - $sql .= "WHERE '".Tools::ip_to_unsigned($_REQUEST['ip'])."' BETWEEN FromIP AND ToIP "; - } + if (!empty($_REQUEST['notes'])) { + $sql .= "AND '".Tools::ip_to_unsigned($_REQUEST['ip'])."' BETWEEN FromIP AND ToIP "; + } else { + $sql .= "WHERE '".Tools::ip_to_unsigned($_REQUEST['ip'])."' BETWEEN FromIP AND ToIP "; + } } $sql .= "ORDER BY FromIP ASC"; @@ -86,28 +86,28 @@ ?>
    -

    IP Address Bans

    +

    IP Address Bans

    -
    - - - - - - - - -
    - - - - - - - -
    -
    +
    + + + + + + + + +
    + + + + + + + +
    +

    @@ -116,59 +116,59 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + +next_record()) { - $Row = $Row === 'a' ? 'b' : 'a'; - $Start = long2ip($Start); - $End = long2ip($End); + $Row = $Row === 'a' ? 'b' : 'a'; + $Start = long2ip($Start); + $End = long2ip($End); ?> - - - - - - - - - - -"> + + + + + + + + + +
    - Range - NotesSubmit
    - - - - - - -
    + Range + NotesSubmit
    + + + + + + +
    - - - - - - - -
    + + + + + + + +
    - + diff --git a/sections/tools/managers/bonus_points.php b/sections/tools/managers/bonus_points.php index e28cdf927..1e880116a 100644 --- a/sections/tools/managers/bonus_points.php +++ b/sections/tools/managers/bonus_points.php @@ -1,49 +1,49 @@ query($sql); - $sql = " - SELECT ID - FROM users_main - WHERE Enabled = '1'"; - $DB->query($sql); - while (list($UserID) = $DB->next_record()) { - $Cache->delete_value("user_stats_{$UserID}"); - } - $Message = '' . number_format($Points) . ' bonus points added to all enabled users.

    '; + $sql = " + UPDATE users_main + SET BonusPoints = BonusPoints + {$Points} + WHERE Enabled = '1'"; + $DB->query($sql); + $sql = " + SELECT ID + FROM users_main + WHERE Enabled = '1'"; + $DB->query($sql); + while (list($UserID) = $DB->next_record()) { + $Cache->delete_value("user_stats_{$UserID}"); + } + $Message = '' . number_format($Points) . ' bonus points added to all enabled users.

    '; } View::show_header('Add tokens sitewide'); ?>
    -

    Add bonus points tokens to all enabled users

    +

    Add bonus points tokens to all enabled users

    - -
    - - - Points to add:

    - -
    + +
    + + + Points to add:

    + +

    - \ No newline at end of file +?> diff --git a/sections/tools/managers/calendar.php b/sections/tools/managers/calendar.php index 7df499cda..2095c2f10 100644 --- a/sections/tools/managers/calendar.php +++ b/sections/tools/managers/calendar.php @@ -1,15 +1,15 @@ -
    -
    -prepared_query("SELECT COUNT(*) AS Count FROM forums WHERE CategoryID=? GROUP BY CategoryID", $ID); - if (!$DB->has_results()) { - $DB->prepared_query('DELETE FROM forums_categories WHERE ID = ?', $ID); - } - else { - error('You must move all forums out of a category before deleting it.'); - } + if (!is_number($_POST['id']) || $_POST['id'] == '') { + error(0); + } + $ID = intval($_POST['id']); + $DB->prepared_query("SELECT COUNT(*) AS Count FROM forums WHERE CategoryID=? GROUP BY CategoryID", $ID); + if (!$DB->has_results()) { + $DB->prepared_query('DELETE FROM forums_categories WHERE ID = ?', $ID); + } + else { + error('You must move all forums out of a category before deleting it.'); + } } else { //Edit & Create, Shared Validation - $Val->SetFields('name', '1', 'string', 'The name must be set, and has a max length of 40 characters', array('maxlength' => 40, 'minlength' => 1)); - $Val->SetFields('sort', '1', 'number', 'Sort must be set'); - $Err = $Val->ValidateForm($_POST); // Validate the form - if ($Err) { - error($Err); - } - $Sort = intval($_POST['sort']); + $Val->SetFields('name', '1', 'string', 'The name must be set, and has a max length of 40 characters', array('maxlength' => 40, 'minlength' => 1)); + $Val->SetFields('sort', '1', 'number', 'Sort must be set'); + $Err = $Val->ValidateForm($_POST); // Validate the form + if ($Err) { + error($Err); + } + $Sort = intval($_POST['sort']); - if ($_POST['submit'] == 'Edit') { - //Edit - if (!is_number($_POST['id']) || $_POST['id'] == '') { - error(0); - } - $ID = intval($_POST['id']); - $DB->prepared_query('SELECT * FROM forums_categories WHERE ID = ?', $ID); - if (!$DB->has_results()) { - error(404); - } + if ($_POST['submit'] == 'Edit') { + //Edit + if (!is_number($_POST['id']) || $_POST['id'] == '') { + error(0); + } + $ID = intval($_POST['id']); + $DB->prepared_query('SELECT * FROM forums_categories WHERE ID = ?', $ID); + if (!$DB->has_results()) { + error(404); + } - $DB->prepared_query('UPDATE forums_categories SET Sort=?, Name=? WHERE ID=?', $Sort, $_POST['name'], $ID); - } - else { - //Create - $DB->prepared_query('INSERT INTO forums_categories (Sort, Name) VALUES (?, ?)', $Sort, $_POST['name']); - } + $DB->prepared_query('UPDATE forums_categories SET Sort=?, Name=? WHERE ID=?', $Sort, $_POST['name'], $ID); + } + else { + //Create + $DB->prepared_query('INSERT INTO forums_categories (Sort, Name) VALUES (?, ?)', $Sort, $_POST['name']); + } } $Cache->delete_value('forums_categories'); // Clear cache diff --git a/sections/tools/managers/categories_list.php b/sections/tools/managers/categories_list.php index d35061217..1cf8cc858 100644 --- a/sections/tools/managers/categories_list.php +++ b/sections/tools/managers/categories_list.php @@ -1,76 +1,76 @@ prepared_query(' - SELECT ID, Name, Sort, IFNULL(f.Count, 0) as Count - FROM forums_categories as fc - LEFT JOIN (SELECT CategoryID, COUNT(*) AS Count FROM forums GROUP BY CategoryID) AS f ON f.CategoryID = fc.ID - ORDER BY Sort'); + SELECT ID, Name, Sort, IFNULL(f.Count, 0) as Count + FROM forums_categories as fc + LEFT JOIN (SELECT CategoryID, COUNT(*) AS Count FROM forums GROUP BY CategoryID) AS f ON f.CategoryID = fc.ID + ORDER BY Sort'); ?>
    - -

    Forum Category Control Panel

    + +

    Forum Category Control Panel

    - - - - - - - fetch_record()) { - $Row = $Row === 'a' ? 'b' : 'a'; - ?> - - - - - - - - - + + + + + + + fetch_record()) { + $Row = $Row === 'a' ? 'b' : 'a'; + ?> + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + +
    SortNameForumsSubmit
    - - - - - - - - - - -
    SortNameForumsSubmit
    + + + + + + + + + + +
    Create Category
    - - - - - -
    Create Category
    + + + + + +
    - + diff --git a/sections/tools/managers/change_log.php b/sections/tools/managers/change_log.php index 95f8f656c..99b6b616c 100644 --- a/sections/tools/managers/change_log.php +++ b/sections/tools/managers/change_log.php @@ -1,43 +1,43 @@ -query(" - INSERT INTO changelog (Message, Author, Time) - VALUES ('$Message', '$Author', '$Date')"); - $ID = $DB->inserted_id(); - // SiteHistory::add_event(sqltime(), "Change log $ID", "tools.php?action=change_log", 1, 3, "", $Message, $LoggedUser['ID']); + authorize(); + if ($_POST['perform'] === 'add' && !empty($_POST['message'])) { + $Message = db_string($_POST['message']); + $Author = db_string($_POST['author']); + $Date = db_string($_POST['date']); + if (!is_valid_date($Date)) { + $Date = sqltime(); + } + $DB->query(" + INSERT INTO changelog (Message, Author, Time) + VALUES ('$Message', '$Author', '$Date')"); + $ID = $DB->inserted_id(); + // SiteHistory::add_event(sqltime(), "Change log $ID", "tools.php?action=change_log", 1, 3, "", $Message, $LoggedUser['ID']); - } - if ($_POST['perform'] === 'remove' && !empty($_POST['change_id'])) { - $ID = (int)$_POST['change_id']; - $DB->query(" - DELETE FROM changelog - WHERE ID = '$ID'"); - } + } + if ($_POST['perform'] === 'remove' && !empty($_POST['change_id'])) { + $ID = (int)$_POST['change_id']; + $DB->query(" + DELETE FROM changelog + WHERE ID = '$ID'"); + } } $DB->query(" - SELECT - SQL_CALC_FOUND_ROWS - ID, - Message, - Author, - Date(Time) as Time2 - FROM changelog - ORDER BY Time DESC - LIMIT $Limit"); + SELECT + SQL_CALC_FOUND_ROWS + ID, + Message, + Author, + Date(Time) as Time2 + FROM changelog + ORDER BY Time DESC + LIMIT $Limit"); $ChangeLog = $DB->to_array(); $DB->query('SELECT FOUND_ROWS()'); list($NumResults) = $DB->next_record(); @@ -45,66 +45,68 @@ View::show_header('Gazelle Change Log', 'datetime_picker', 'datetime_picker'); ?>
    -

    Gazelle Change Log

    - - + diff --git a/sections/tools/managers/db_key.php b/sections/tools/managers/db_key.php new file mode 100644 index 000000000..ca3014591 --- /dev/null +++ b/sections/tools/managers/db_key.php @@ -0,0 +1,36 @@ + + +
    +

    Database Encryption Key

    +
    +
    +

    DB key

    +
    +
    + + +
    + +
    + +
    +
    +
    +
    + + + diff --git a/sections/tools/managers/dnu_alter.php b/sections/tools/managers/dnu_alter.php index 90b142c5d..013786930 100644 --- a/sections/tools/managers/dnu_alter.php +++ b/sections/tools/managers/dnu_alter.php @@ -1,57 +1,57 @@ - $Item) { - $Position = db_string($Position); - $Item = db_string($Item); - $DB->query(" - UPDATE `do_not_upload` - SET `Sequence` = '" . $Position . "' - WHERE `id` = '" . $Item . "'"); - } + foreach ($_POST['item'] as $Position => $Item) { + $Position = db_string($Position); + $Item = db_string($Item); + $DB->query(" + UPDATE `do_not_upload` + SET `Sequence` = '" . $Position . "' + WHERE `id` = '" . $Item . "'"); + } } elseif ($_POST['submit'] == 'Delete') { //Delete - if (!is_number($_POST['id']) || $_POST['id'] == '') { - error(0); - } - $DB->query(' - DELETE FROM do_not_upload - WHERE ID = '.$_POST['id']); + if (!is_number($_POST['id']) || $_POST['id'] == '') { + error(0); + } + $DB->query(' + DELETE FROM do_not_upload + WHERE ID = '.$_POST['id']); } else { //Edit & Create, Shared Validation - $Val->SetFields('name', '1', 'string', 'The name must be set, have a maximum length of 100 characters, and have a minimum length of 5 characters.', array('maxlength' => 100, 'minlength' => 5)); - $Val->SetFields('comment', '0', 'string', 'The description has a maximum length of 255 characters.', array('maxlength' => 255)); - $Err = $Val->ValidateForm($_POST); // Validate the form - if ($Err) { - error($Err); - } + $Val->SetFields('name', '1', 'string', 'The name must be set, have a maximum length of 100 characters, and have a minimum length of 5 characters.', array('maxlength' => 100, 'minlength' => 5)); + $Val->SetFields('comment', '0', 'string', 'The description has a maximum length of 255 characters.', array('maxlength' => 255)); + $Err = $Val->ValidateForm($_POST); // Validate the form + if ($Err) { + error($Err); + } - $P = array(); - $P = db_array($_POST); // Sanitize the form + $P = []; + $P = db_array($_POST); // Sanitize the form - if ($_POST['submit'] == 'Edit') { //Edit - if (!is_number($_POST['id']) || $_POST['id'] == '') { - error(0); - } - $DB->query(" - UPDATE do_not_upload - SET - Name = '$P[name]', - Comment = '$P[comment]', - UserID = '$LoggedUser[ID]', - Time = '".sqltime()."' - WHERE ID = '$P[id]'"); - } else { //Create - $DB->query(" - INSERT INTO do_not_upload - (Name, Comment, UserID, Time, Sequence) - VALUES - ('$P[name]','$P[comment]','$LoggedUser[ID]','".sqltime()."', 9999)"); - } + if ($_POST['submit'] == 'Edit') { //Edit + if (!is_number($_POST['id']) || $_POST['id'] == '') { + error(0); + } + $DB->query(" + UPDATE do_not_upload + SET + Name = '$P[name]', + Comment = '$P[comment]', + UserID = '$LoggedUser[ID]', + Time = '".sqltime()."' + WHERE ID = '$P[id]'"); + } else { //Create + $DB->query(" + INSERT INTO do_not_upload + (Name, Comment, UserID, Time, Sequence) + VALUES + ('$P[name]','$P[comment]','$LoggedUser[ID]','".sqltime()."', 9999)"); + } } // Go back diff --git a/sections/tools/managers/dnu_list.php b/sections/tools/managers/dnu_list.php index ca0c02f90..a7a26e708 100644 --- a/sections/tools/managers/dnu_list.php +++ b/sections/tools/managers/dnu_list.php @@ -1,74 +1,76 @@ -query(" - SELECT - d.ID, - d.Name, - d.Comment, - d.UserID, - d.Time - FROM do_not_upload AS d - LEFT JOIN users_main AS um ON um.ID = d.UserID - ORDER BY d.Sequence"); - ?> -
    -

    -

    Drag and drop table rows to reorder.

    -
    - - - - - - - - - - - - - - - - - - - - -next_record()) { ?> - - - - - - - - - - -
    Add an entry to the "Do Not Upload" list
    - - - - - -
    NameCommentAddedSubmit
    - - - - - - - -
    - -
    - - -
    - + SELECT + d.ID, + d.Name, + d.Comment, + d.UserID, + d.Time + FROM do_not_upload AS d + LEFT JOIN users_main AS um ON um.ID = d.UserID + ORDER BY d.Sequence"); + ?> +
    +

    +

    Drag and drop table rows to reorder.

    +
    + + + + + + + + + + + + + + + + + + + + +next_record()) { ?> + + + + + + + + + + +
    Add an entry to the "Do Not Upload" list
    + + + + + +
    NameCommentAddedSubmit
    + + + + + + + +
    + +
    + + +
    + diff --git a/sections/tools/managers/email_blacklist.php b/sections/tools/managers/email_blacklist.php index ae0f4eac4..b27d3cfe5 100644 --- a/sections/tools/managers/email_blacklist.php +++ b/sections/tools/managers/email_blacklist.php @@ -1,100 +1,101 @@ -query(" - SELECT - SQL_CALC_FOUND_ROWS - ID, - UserID, - Time, - Email, - Comment - FROM email_blacklist - $Where - ORDER BY Time DESC - LIMIT $Limit"); + SELECT + SQL_CALC_FOUND_ROWS + ID, + UserID, + Time, + Email, + Comment + FROM email_blacklist + $Where + ORDER BY Time DESC + LIMIT $Limit"); $Results = $DB->to_array(false, MYSQLI_ASSOC, false); $DB->query('SELECT FOUND_ROWS()'); list ($NumResults) = $DB->next_record(); ?>
    -

    Email Blacklist

    +

    Email Blacklist


    - - - - + + + +
    - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + +
    Email addressCommentDate addedSubmit
    Add email address or domain to blacklist
    Email addressCommentDate addedSubmit
    Add email address or domain to blacklist
    - - - - -
    - - -
    + + + + +
    + + +
    - + diff --git a/sections/tools/managers/email_blacklist_alter.php b/sections/tools/managers/email_blacklist_alter.php index 1637474b9..ebe3dbaea 100644 --- a/sections/tools/managers/email_blacklist_alter.php +++ b/sections/tools/managers/email_blacklist_alter.php @@ -1,45 +1,45 @@ -query(" - DELETE FROM email_blacklist - WHERE ID = $_POST[id]"); + if (!is_number($_POST['id']) || $_POST['id'] === '') { + error(0); + } + $DB->query(" + DELETE FROM email_blacklist + WHERE ID = $_POST[id]"); } else { // Edit & Create, Shared Validation - $Val->SetFields('email', '1', 'string', 'The email must be set', array('minlength'=>1)); - $Val->SetFields('comment', '0', 'string', 'The description has a max length of 255 characters', array('maxlength'=>255)); - $Err = $Val->ValidateForm($_POST); - if ($Err) { - error($Err); - } + $Val->SetFields('email', '1', 'string', 'The email must be set', array('minlength'=>1)); + $Val->SetFields('comment', '0', 'string', 'The description has a max length of 255 characters', array('maxlength'=>255)); + $Err = $Val->ValidateForm($_POST); + if ($Err) { + error($Err); + } - $P = array(); - $P = db_array($_POST); // Sanitize the form + $P = []; + $P = db_array($_POST); // Sanitize the form - if ($_POST['submit'] === 'Edit') { // Edit - if (!is_number($_POST['id']) || $_POST['id'] === '') { - error(0); - } - $DB->query(" - UPDATE email_blacklist - SET - Email = '$P[email]', - Comment = '$P[comment]', - UserID = '$LoggedUser[ID]', - Time = '".sqltime()."' - WHERE ID = '$P[id]'"); - } else { // Create - $DB->query(" - INSERT INTO email_blacklist (Email, Comment, UserID, Time) - VALUES ('$P[email]', '$P[comment]', '$LoggedUser[ID]', '".sqltime()."')"); - } + if ($_POST['submit'] === 'Edit') { // Edit + if (!is_number($_POST['id']) || $_POST['id'] === '') { + error(0); + } + $DB->query(" + UPDATE email_blacklist + SET + Email = '$P[email]', + Comment = '$P[comment]', + UserID = '$LoggedUser[ID]', + Time = '".sqltime()."' + WHERE ID = '$P[id]'"); + } else { // Create + $DB->query(" + INSERT INTO email_blacklist (Email, Comment, UserID, Time) + VALUES ('$P[email]', '$P[comment]', '$LoggedUser[ID]', '".sqltime()."')"); + } } // Go back diff --git a/sections/tools/managers/email_blacklist_search.php b/sections/tools/managers/email_blacklist_search.php index 4ea7befb8..1cf40ed09 100644 --- a/sections/tools/managers/email_blacklist_search.php +++ b/sections/tools/managers/email_blacklist_search.php @@ -1,42 +1,42 @@ -query(" - SELECT - ID, - UserID, - Time, - Email, - Comment - FROM email_blacklist - WHERE Email LIKE '%$Search%'"); + SELECT + ID, + UserID, + Time, + Email, + Comment + FROM email_blacklist + WHERE Email LIKE '%$Search%'"); $EmailResults = $DB->to_array(false, MYSQLI_ASSOC, false); -$Results = array(); +$Results = []; $Count = $DB->record_count(); $Results['count'] = $Count; -$Emails = array(); +$Emails = []; if ($Count > 0) { - foreach ($EmailResults as $Email) { - $Emails[] = array( - 'id' => (int)$Email['ID'], - 'email' => $Email['Email'], - 'comment' => $Email['Comment'], - 'userid' => (int)$Email['UserID'], - 'time' => $Email['Time']); - } + foreach ($EmailResults as $Email) { + $Emails[] = array( + 'id' => (int)$Email['ID'], + 'email' => $Email['Email'], + 'comment' => $Email['Comment'], + 'userid' => (int)$Email['UserID'], + 'time' => $Email['Time']); + } } $Results['emails'] = $Emails; $JSON['results'] = $Results; diff --git a/sections/tools/managers/enable_requests.php b/sections/tools/managers/enable_requests.php index be6213edd..159d141f1 100644 --- a/sections/tools/managers/enable_requests.php +++ b/sections/tools/managers/enable_requests.php @@ -1,4 +1,4 @@ -Username
    Checked
    > @@ -232,10 +232,10 @@
    - 0) { ?> - diff --git a/sections/tools/managers/recommend_add.php b/sections/tools/managers/recommend_add.php index 41d8bb23a..10273d2db 100644 --- a/sections/tools/managers/recommend_add.php +++ b/sections/tools/managers/recommend_add.php @@ -4,7 +4,7 @@ authorize(); if (!check_perms('site_recommend_own') && !check_perms('site_manage_recommendations')) { - error(403); + error(403); } $URL = trim($_POST['url']); @@ -12,14 +12,14 @@ // Make sure the URL they entered is on our site, and is a link to a torrent $URLRegex = '/^https?:\/\/(www\.|ssl\.)?'.NONSSL_SITE_URL.'\/torrents\.php\?id=([0-9]+)$/i'; $Val->SetFields('url', - '1','regex','The URL must be a link to a torrent on the site.',array('regex' => '/^'.TORRENT_GROUP_REGEX.'/i')); + '1','regex','The URL must be a link to a torrent on the site.',array('regex' => '/^'.TORRENT_GROUP_REGEX.'/i')); $Err = $Val->ValidateForm($_POST); // Validate the form $Location = (empty($_SERVER['HTTP_REFERER'])) ? "tools.php?action=recommend" : $_SERVER['HTTP_REFERER']; if ($Err) { // if something didn't validate - error($Err); - header("Location: {$Location}"); - exit; + error($Err); + header("Location: {$Location}"); + exit; } // Get torrent ID @@ -27,7 +27,7 @@ $GroupID = $Matches[4]; if (empty($GroupID) || !is_number($GroupID)) { - error(404); + error(404); } $DB->query("INSERT INTO torrents_recommended (GroupID, UserID, Time) VALUES ('".db_string($GroupID)."', $LoggedUser[ID], '".sqltime()."')"); diff --git a/sections/tools/managers/recommend_alter.php b/sections/tools/managers/recommend_alter.php index 965223979..89fc7a053 100644 --- a/sections/tools/managers/recommend_alter.php +++ b/sections/tools/managers/recommend_alter.php @@ -3,28 +3,28 @@ //--------------- Delete a recommendation --------------------------------------// if (!check_perms('site_recommend_own') && !check_perms('site_manage_recommendations')) { - error(403); + error(403); } $GroupID = $_GET['groupid']; if (!$GroupID || !is_number($GroupID)) { - error(404); + error(404); } if (!check_perms('site_manage_recommendations')) { - $DB->query(" - SELECT UserID - FROM torrents_recommended - WHERE GroupID = '$GroupID'"); - list($UserID) = $DB->next_record(); - if ($UserID != $LoggedUser['ID']) { - error(403); - } + $DB->query(" + SELECT UserID + FROM torrents_recommended + WHERE GroupID = '$GroupID'"); + list($UserID) = $DB->next_record(); + if ($UserID != $LoggedUser['ID']) { + error(403); + } } $DB->query(" - DELETE FROM torrents_recommended - WHERE GroupID = '$GroupID'"); + DELETE FROM torrents_recommended + WHERE GroupID = '$GroupID'"); $Cache->delete_value('recommend'); $Location = (empty($_SERVER['HTTP_REFERER'])) ? "tools.php?action=recommend" : $_SERVER['HTTP_REFERER']; diff --git a/sections/tools/managers/recommend_list.php b/sections/tools/managers/recommend_list.php index e7056ac39..f6562ffa2 100644 --- a/sections/tools/managers/recommend_list.php +++ b/sections/tools/managers/recommend_list.php @@ -1,60 +1,63 @@ -query(" - SELECT - tr.GroupID, - tr.UserID, - tg.Name, - tg.ArtistID, - ag.Name - FROM torrents_recommended AS tr - JOIN torrents_group AS tg ON tg.ID=tr.GroupID - LEFT JOIN artists_group AS ag ON ag.ArtistID=tg.ArtistID - ORDER BY tr.Time DESC - LIMIT 10 - "); + SELECT + tr.GroupID, + tr.UserID, + tg.Name, + tg.ArtistID, + ag.Name + FROM torrents_recommended AS tr + JOIN torrents_group AS tg ON tg.ID=tr.GroupID + LEFT JOIN artists_group AS ag ON ag.ArtistID=tg.ArtistID + ORDER BY tr.Time DESC + LIMIT 10 + "); ?>
    -
    - + diff --git a/sections/tools/managers/recommend_restore.php b/sections/tools/managers/recommend_restore.php index e6c2d598c..95d947000 100644 --- a/sections/tools/managers/recommend_restore.php +++ b/sections/tools/managers/recommend_restore.php @@ -1,16 +1,16 @@ -query(' - SELECT GroupID - FROM torrents_recommended'); + SELECT GroupID + FROM torrents_recommended'); $ToNL = $DB->next_record(); Torrents::freeleech_groups($ToNL, 2, 3); ?> diff --git a/sections/tools/managers/referral_accounts.php b/sections/tools/managers/referral_accounts.php new file mode 100644 index 000000000..476fbdb21 --- /dev/null +++ b/sections/tools/managers/referral_accounts.php @@ -0,0 +1,128 @@ + $name) { + $Ret .= "