diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 17cb1723..1322f267 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -33,7 +33,7 @@ class Kernel extends ConsoleKernel $schedule->command('hr:update_status --ignore_time=1')->hourly()->withoutOverlapping(); $schedule->command('user:delete_expired_token')->dailyAt('04:00'); $schedule->command('claim:settle')->hourly()->between("00:00", "12:00") - ->when(function () {Carbon::now()->format('d') == '01';})->withoutOverlapping(); + ->when(function () {return Carbon::now()->format('d') == '01';})->withoutOverlapping(); } /** diff --git a/database/migrations/2022_05_06_160029_create_complains_table.php b/database/migrations/2022_05_06_160029_create_complains_table.php new file mode 100644 index 00000000..074664da --- /dev/null +++ b/database/migrations/2022_05_06_160029_create_complains_table.php @@ -0,0 +1,35 @@ +id(); + $table->char('uuid', 36); + $table->string('email'); + $table->text('body'); + $table->dateTime('added'); + $table->smallInteger('answered')->default(0); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('complains'); + } +}; diff --git a/database/migrations/2022_05_06_165409_create_complain_replies_table.php b/database/migrations/2022_05_06_165409_create_complain_replies_table.php new file mode 100644 index 00000000..8e45ec19 --- /dev/null +++ b/database/migrations/2022_05_06_165409_create_complain_replies_table.php @@ -0,0 +1,34 @@ +id(); + $table->integer('complain'); + $table->integer('userid')->default(0); + $table->dateTime('added'); + $table->text('body'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('complain_replies'); + } +}; diff --git a/include/functions.php b/include/functions.php index 373aa589..fd76112f 100644 --- a/include/functions.php +++ b/include/functions.php @@ -455,7 +455,7 @@ function highlight($search,$subject,$hlstart='',$hlend function get_user_class() { global $CURUSER; - return $CURUSER["class"]; + return $CURUSER["class"] ?? ''; } function get_user_class_name($class, $compact = false, $b_colored = false, $I18N = false) @@ -1692,7 +1692,7 @@ function check_code ($imagehash, $imagestring, $where = 'signup.php',$maxattempt mysql_real_escape_string($imagehash)); sql_query($delete); if (!$maxattemptlog) - bark($lang_functions['std_invalid_image_code']."".$lang_functions['std_here_to_request_new']); + stderr('Error',$lang_functions['std_invalid_image_code']."".$lang_functions['std_here_to_request_new'], false); else failedlogins($lang_functions['std_invalid_image_code']."".$lang_functions['std_here_to_request_new'],true,$head); }else{ @@ -2706,6 +2706,12 @@ if ($msgalert) if (get_user_class() >= $staffmem_class) { + if(($complaints = $Cache->get_value('COMPLAINTS_COUNT_CACHE')) === false){ + $complaints = get_row_count('complains', 'WHERE answered = 0'); + $Cache->cache_value('COMPLAINTS_COUNT_CACHE', $complaints, 600); + } + if($complaints) msgalert('complains.php?action=list', sprintf($lang_functions['text_complains'], is_or_are($complaints), $complaints, add_s($complaints)), 'darkred'); + $numreports = $Cache->get_value('staff_new_report_count'); if ($numreports == ""){ $numreports = get_row_count("reports","WHERE dealtwith=0"); @@ -3970,7 +3976,7 @@ function permissiondenied(){ function gettime($time, $withago = true, $twoline = false, $forceago = false, $oneunit = false, $isfuturetime = false){ global $lang_functions, $CURUSER; - if ($CURUSER['timetype'] != 'timealive' && !$forceago){ + if (isset($CURUSER) && $CURUSER['timetype'] != 'timealive' && !$forceago){ $newtime = $time; if ($twoline){ $newtime = str_replace(" ", "
", $newtime); @@ -5370,6 +5376,17 @@ function get_share_ratio($uploaded, $downloaded) return $ratio; } +function EchoRow($class = ''){ + if(func_num_args() < 2) return ''; + $args = func_get_args(); + $cells = array_splice($args, 1); + $class = empty($class) ? '' : sprintf(' class="%s"', $class); + $s = ''; + foreach($cells as $cell) $s .= sprintf('%s', $class, $cell); + $s .= "\n"; + return $s; +} + function list_require_search_box_id() { $setting = get_setting('main'); diff --git a/lang/chs/lang_complains.php b/lang/chs/lang_complains.php new file mode 100644 index 00000000..0fda15d2 --- /dev/null +++ b/lang/chs/lang_complains.php @@ -0,0 +1,29 @@ + '申诉区', + 'text_new_complain' => '提交申诉内容', + 'text_new_email' => '账号邮箱:', + 'text_new_body' => '申诉内容:', + 'text_new_body_placeholder' => "申诉问题:\n相关证据:可使用标签[img=URL]外链图片", + 'text_new_submit' => '提交申诉', + 'text_new_failure' => '邮箱填写错误或申诉内容为空', + 'text_created_title' => '申诉已创建', + 'text_created_note' => '请将本页加入书签,以便查看后续更新。切勿将网址发给他人,以免影响申诉过程!', + 'text_search_account' => '搜索该账号', + 'text_reply' => '回复申诉', + 'text_replies' => '申诉处理', + 'text_complainer' => '申诉者', + 'text_added' => '申诉时间', + 'text_no_replies' => '当前暂无申诉跟踪内容', + 'text_closed' => '该申诉已处理完毕,不能回复', + 'text_answer_it' => '关闭该申述', + 'text_unanswer_it' => '重开该申述', + 'th_complain_at' => '申诉时间', + 'th_complain_account' => '申诉账号', + 'th_action_view' => '查看', + 'pending_complaints' => '待处理申诉', + 'no_pending_complaints' => '暂无待处理的申诉', + 'complaints_processed' => '已处理申诉', + 'no_complaints_have_been_processed' => '暂无已处理的申诉', +]; diff --git a/lang/chs/lang_functions.php b/lang/chs/lang_functions.php index 035f8e3e..111eb528 100644 --- a/lang/chs/lang_functions.php +++ b/lang/chs/lang_functions.php @@ -317,6 +317,7 @@ $lang_functions = array 'spoiler_expand_collapse' => '点击展开/收缩', 'spoiler_default_title' => '折叠内容', 'menu_claim' => '认领: ', + 'text_complains' => '有%s%u个待处理的申述%s', ); ?> diff --git a/lang/chs/lang_login.php b/lang/chs/lang_login.php index 45c5a941..460ceb71 100644 --- a/lang/chs/lang_login.php +++ b/lang/chs/lang_login.php @@ -34,5 +34,6 @@ $lang_login = array 'head_login' => "登录", 'rowhead_two_step_code' => '两步验证', 'two_step_code_tooltip' => '如有设置必须填写', + 'text_complain' => '申诉通道', ); ?> diff --git a/lang/cht/lang_complains.php b/lang/cht/lang_complains.php new file mode 100644 index 00000000..bc76c7c8 --- /dev/null +++ b/lang/cht/lang_complains.php @@ -0,0 +1,29 @@ + '申訴區', + 'text_new_complain' => '提交申訴內容', + 'text_new_email' => '帳號郵箱:', + 'text_new_body' => '申訴內容:', + 'text_new_body_placeholder' => "申訴問題:\n相關證據:可使用標籤[img=URL]外鏈圖片", + 'text_new_submit' => '提交申訴', + 'text_new_failure' => '郵箱填寫錯誤或申訴內容為空', + 'text_created_title' => '申訴已創建', + 'text_created_note' => '請將本頁加入書簽,以便查看後續更新。切勿將網址發給他人,以免影響申訴過程!', + 'text_search_account' => '搜索該帳號', + 'text_reply' => '回復申訴', + 'text_replies' => '申訴處理', + 'text_complainer' => '申訴者', + 'text_added' => '申訴時間', + 'text_no_replies' => '當前暫無申訴跟蹤內容', + 'text_closed' => '該申訴已處理完畢,不能回復', + 'text_answer_it' => '關閉該申述', + 'text_unanswer_it' => '重開該申述', + 'th_complain_at' => '申訴時間', + 'th_complain_account' => '申訴賬號', + 'th_action_view' => '查看', + 'pending_complaints' => '待處理申訴', + 'no_pending_complaints' => '暫無待處理的申訴', + 'complaints_processed' => '已處理申訴', + 'no_complaints_have_been_processed' => '暫無已處理的申訴', +]; diff --git a/lang/cht/lang_functions.php b/lang/cht/lang_functions.php index d8983a3f..465f34d8 100644 --- a/lang/cht/lang_functions.php +++ b/lang/cht/lang_functions.php @@ -324,6 +324,7 @@ $lang_functions = array 'spoiler_expand_collapse' => '點擊展開/收縮', 'spoiler_default_title' => '折疊內容', 'menu_claim' => '認領: ', + 'text_complains' => '有%s%u個待處理的申述%s', ); ?> diff --git a/lang/cht/lang_login.php b/lang/cht/lang_login.php index d5367605..66ff717e 100644 --- a/lang/cht/lang_login.php +++ b/lang/cht/lang_login.php @@ -34,5 +34,6 @@ $lang_login = array 'head_login' => "登錄", 'rowhead_two_step_code' => '兩步驗證', 'two_step_code_tooltip' => '如有設置必須填寫', + 'text_complain' => '申訴通道', ); ?> diff --git a/lang/en/lang_complains.php b/lang/en/lang_complains.php new file mode 100644 index 00000000..d0c84712 --- /dev/null +++ b/lang/en/lang_complains.php @@ -0,0 +1,29 @@ + 'Complain account issue', + 'text_new_complain' => 'New complain', + 'text_new_email' => 'Account Email:', + 'text_new_body' => 'Complain Detail:', + 'text_new_body_placeholder' => "Describe issue here:\nRelated proof is applicable: Images are shown in form of [img=URL]", + 'text_new_submit' => 'Submit', + 'text_new_failure' => 'Bad email or empty complain entered.', + 'text_created_title' => 'Complain created', + 'text_created_note' => 'Please bookmark this page so that you can check its updates later. DO NOT REVEAL THIS URL TO ANYBODY! Otherwise your complaint would not be dealt with.', + 'text_search_account' => 'Search account with this email', + 'text_reply' => 'Reply', + 'text_replies' => 'Following up messages', + 'text_complainer' => 'Complainer', + 'text_added' => 'Time added', + 'text_no_replies' => 'No messages now', + 'text_closed' => 'This complaint has been answered and locked for new replies.', + 'text_answer_it' => 'CLOSE', + 'text_unanswer_it' => 'REOPEN', + 'th_complain_at' => 'Complain at', + 'th_complain_account' => 'Complain account', + 'th_action_view' => 'View', + 'pending_complaints' => 'Pending complaints', + 'no_pending_complaints' => 'No pending complaints', + 'complaints_processed' => 'Complaints processed', + 'no_complaints_have_been_processed' => 'No complaints have been processed', +]; diff --git a/lang/en/lang_functions.php b/lang/en/lang_functions.php index 6ef014db..45caea7e 100644 --- a/lang/en/lang_functions.php +++ b/lang/en/lang_functions.php @@ -325,6 +325,7 @@ $lang_functions = array 'spoiler_expand_collapse' => 'Click to expand/collapse', 'spoiler_default_title' => 'Collapse content', 'menu_claim' => 'Claim: ', + 'text_complains' => 'There %s %u pending complaint%s.', ); ?> diff --git a/lang/en/lang_login.php b/lang/en/lang_login.php index 76f32e54..584516b9 100644 --- a/lang/en/lang_login.php +++ b/lang/en/lang_login.php @@ -34,6 +34,7 @@ $lang_login = array 'head_login' => "Login", 'rowhead_two_step_code' => 'Two-Factor Authentication', 'two_step_code_tooltip' => 'If enabled must be filled in', + 'text_complain' => 'Complaint Channel', ); ?> diff --git a/public/complains.php b/public/complains.php new file mode 100644 index 00000000..a4a31590 --- /dev/null +++ b/public/complains.php @@ -0,0 +1,143 @@ += $staffmem_class; + +if($isLogin && !$isAdmin) permissiondenied(); + +if($_SERVER['REQUEST_METHOD'] === 'POST'){ + switch($action = filter_input(INPUT_POST, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS)){ + case 'new': + cur_user_check(); + check_code ($_POST['imagehash'], $_POST['imagestring'],'complains.php'); + $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL); + $body = filter_input(INPUT_POST, 'body', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + if(empty($email) || empty($body)) stderr($lang_functions['std_error'], $lang_complains['text_new_failure']); + sql_query(sprintf('INSERT INTO complains (uuid, email, body, added) VALUES (UUID(), %s, %s, NOW())', sqlesc($email), sqlesc($body))) or sqlerr(__FILE__, __LINE__); + $Cache->delete_value('COMPLAINTS_COUNT_CACHE'); + nexus_redirect(sprintf('complains.php?action=view&id=%s', get_single_value('complains', 'uuid', 'WHERE id = ' . mysql_insert_id()))); + break; + case 'reply': + $id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT); + $body = filter_input(INPUT_POST, 'body', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + if(empty($id) || empty($body)) stderr($lang_functions['std_error'], $lang_complains['text_new_failure']); + sql_query(sprintf('INSERT INTO complain_replies (complain, userid, added, body) VALUES (%u, %u, NOW(), %s)', $id, isset($CURUSER['id']) ? $CURUSER['id'] : 0, sqlesc($body))) or sqlerr(__FILE__, __LINE__); + nexus_redirect($_SERVER['HTTP_REFERER']); + break; + case 'answered': + case 'unanswered': + if(!$isAdmin) permissiondenied(); + $id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT); + if(!$id) permissiondenied(); + sql_query(sprintf('UPDATE complains SET answered = %u WHERE id = %u', $action == 'answered' ? 1 : 0, $id)) or sqlerr(__FILE__, __LINE__); + $Cache->delete_value('COMPLAINTS_COUNT_CACHE'); + nexus_redirect($_SERVER['HTTP_REFERER']); + break; + default: + permissiondenied(); + } +}else{ + switch (filter_input(INPUT_GET, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS)){ + case 'list': + if(!$isAdmin) permissiondenied(); + $showTable = function($res){ + global $lang_complains; + echo ''; + echo EchoRow('colhead', $lang_complains['th_complain_at'], $lang_complains['th_complain_account'], $lang_complains['th_action_view']); + while($row = mysql_fetch_assoc($res)){ + echo EchoRow('rowfollow', gettime($row['added']), htmlspecialchars($row['email']), sprintf('%s', $row['uuid'], $lang_complains['th_action_view'])); + } + echo '
'; + }; + stdhead($lang_complains['text_complain']); + begin_main_frame(); + if(!isset($_GET['page'])){ + $res = sql_query('SELECT added, uuid, email FROM complains WHERE answered = 0 ORDER BY id DESC') or sqlerr(__FILE__, __LINE__); + begin_frame($lang_complains['pending_complaints']); + if(mysql_num_rows($res)){ + $showTable($res); + }else{ + echo $lang_complains['no_pending_complaints']; + } + end_frame(); + } + begin_frame($lang_complains['complaints_processed']); + list($pagertop, $pagerbottom, $limit) = pager(20, get_row_count('complains', 'WHERE answered = 1'), '?action=list&'); + $res = sql_query('SELECT added, uuid, email FROM complains WHERE answered = 1 ORDER BY id DESC ' . $limit) or sqlerr(__FILE__, __LINE__); + if(mysql_num_rows($res)){ + echo $pagertop; + $showTable($res); + echo $pagerbottom; + }else{ + echo $lang_complains['no_complaints_have_been_processed']; + } + end_frame(); + end_main_frame(); + stdfoot(); + break; + case 'view': + $uuid = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_FULL_SPECIAL_CHARS); + if(strlen($uuid) != 36) permissiondenied(); + $res = sql_query(sprintf('SELECT * FROM complains WHERE uuid = %s', sqlesc($uuid))) or sqlerr(__FILE__, __LINE__); + $complain = mysql_fetch_assoc($res); + if(!$complain) permissiondenied(); + stdhead($lang_complains['text_complain']); + begin_main_frame(); + if(!$isLogin){ + begin_frame($lang_complains['text_created_title']); + printf('

%s

', $lang_complains['text_created_note']); + end_frame(); + } + begin_frame($lang_complains['text_new_body']); + printf('%s:%s
%s %s', $lang_complains['text_added'], gettime($complain['added']), $lang_complains['text_new_email'], htmlspecialchars($complain['email'])); + if($isAdmin) printf(' [%s]', urlencode($complain['email']), $lang_complains['text_search_account']); + echo '
', format_comment($complain['body']); + end_frame(); + // REPLIES + begin_frame($lang_complains['text_replies']); + $res = sql_query(sprintf('SELECT * FROM `complain_replies` WHERE complain = %u ORDER BY id DESC', $complain['id'])) or sqlerr(__FILE__, __LINE__); + if(mysql_num_rows($res)){ + while($row = mysql_fetch_assoc($res)){ + printf('%s @ %s: ', $row['userid'] ? get_plain_username($row['userid']) : $lang_complains['text_complainer'], gettime($row['added'])); + echo format_comment($row['body']) . '
'; + } + }else{ + printf('

%s

', $lang_complains['text_no_replies']); + } + end_frame(); + // NEW REPLY + if($complain['answered']){ + printf('

%s

', $lang_complains['text_closed']); + }else{ + printf('

%s


', $lang_complains['text_reply'], $complain['id']); + quickreply('reply', 'body', $lang_complains['text_reply']); + echo '
'; + } + if($isAdmin){ + printf('
', $complain['answered'] ? 'unanswered' : 'answered', $complain['id'],$complain['answered'] ? $lang_complains['text_unanswer_it'] : $lang_complains['text_answer_it']); + } + end_main_frame(); + stdfoot(); + break; + case 'compose': + default: + cur_user_check(); + stdhead($lang_complains['text_complain']); + ?> +

+
+ + + + + + +
+
+ +

[]