public function fetch_comments($where) { $result = Dbl::qe("select PaperComment.*, firstName reviewFirstName, lastName reviewLastName, email reviewEmail\n from PaperComment join ContactInfo on (ContactInfo.contactId=PaperComment.contactId)\n where {$where} order by commentId"); $comments = array(); while ($c = CommentInfo::fetch($result, $this)) { $comments[$c->commentId] = $c; } Dbl::free($result); return $comments; }
assert_search_papers($user_chair, "#fart", "1 2 3 4 5 6 7 8"); assert_search_papers($user_chair, "NOT #fart", "9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30"); assert_search_papers($user_chair, "-#fart", "9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30"); // Check all tags assert_search_papers($user_chair, "#none", "9 10 11 12 14 15 16 18 19 20 21 22 23 24 25 26 27 28 29 30"); xassert_assign($Admin, false, "paper,tag\n9,~private\n10,~~chair\n"); assert_search_papers($user_chair, "#none", "11 12 14 15 16 18 19 20 21 22 23 24 25 26 27 28 29 30"); assert_search_papers($user_mgbaker, "#none", "3 9 10 11 12 14 15 16 18 19 20 21 22 23 24 25 26 27 28 29 30"); // comment searches assert_search_papers($user_chair, "cmt:any", "1"); $comment2 = new CommentInfo(null, $paper18); $c2ok = $comment2->save(array("text" => "test", "visibility" => "a", "blind" => false), $user_mgbaker); xassert($c2ok); assert_search_papers($user_chair, "cmt:any", "1 18"); assert_search_papers($user_chair, "cmt:any>1", ""); $comment3 = new CommentInfo(null, $paper18); $c3ok = $comment3->save(array("text" => "test", "visibility" => "a", "blind" => false, "tags" => "redcmt"), $user_mgbaker); xassert($c3ok); assert_search_papers($user_chair, "cmt:any>1", "18"); assert_search_papers($user_chair, "cmt:jon", ""); assert_search_papers($user_chair, "cmt:mgbaker", "1 18"); assert_search_papers($user_chair, "cmt:mgbaker>1", "18"); assert_search_papers($user_chair, "cmt:#redcmt", "18"); /*$result = Dbl::qe("select paperId, tag, tagIndex from PaperTag order by paperId, tag"); $tags = array(); while ($result && ($row = $result->fetch_row())) $tags[] = "$row[0],$row[1],$row[2]\n"; echo join("", $tags);*/ // check review visibility for “not unless completed on same paper” $Conf->save_setting("pc_seeallrev", Conf::PCSEEREV_IFCOMPLETE); Contact::update_rights();
function save_comment($text, $is_response, $roundnum) { global $Me, $Conf, $prow, $crow; if ($crow) { $roundnum = (int) $crow->commentRound; } // If I have a review token for this paper, save under that anonymous user. $user = $Me; if ((!$crow || $crow->contactId != $Me->contactId) && ($cid = $Me->review_token_cid($prow)) && (!$crow || $crow->contactId == $cid)) { $user = Contact::find_by_id($cid); } $req = array("visibility" => @$_REQUEST["visibility"], "submit" => $is_response && @$_REQUEST["submitresponse"], "text" => $text, "tags" => @$_REQUEST["commenttags"], "blind" => @$_REQUEST["blind"]); if ($is_response && !$crow) { $cinfo = new CommentInfo((object) array("commentType" => COMMENTTYPE_RESPONSE, "commentRound" => $roundnum), $prow); } else { $cinfo = new CommentInfo($crow, $prow); } $ok = $cinfo->save($req, $user); $what = $is_response ? "Response" : "Comment"; $confirm = false; if (!$ok && $is_response) { $crows = $Conf->comment_rows($Conf->comment_query("paperId={$prow->paperId} and (commentType&" . COMMENTTYPE_RESPONSE . ")!=0 and commentRound={$roundnum}"), $Me); reset($crows); $cur_response = @current($crows); if ($cur_response && $cur_response->comment == $text) { $cinfo = new CommentInfo($cur_response, $prow); $ok = true; } else { $confirm = Ht::xmsg("error", "A response was entered concurrently by another user. Reload to see it."); } } if (!$ok) { /* nada */ } else { if ($is_response && (!$cinfo->commentId || $cinfo->commentType & COMMENTTYPE_DRAFT)) { if ($cinfo->commentId) { $confirm = 'Response saved. <strong>This draft response will not be shown to reviewers.</strong>'; } else { $confirm = 'Response deleted.'; } $isuf = $roundnum ? "_{$roundnum}" : ""; if (($dl = $Conf->printableTimeSetting("resp_done{$isuf}")) != "N/A") { $confirm .= " You have until {$dl} to submit the response."; } $confirm = Ht::xmsg("warning", $confirm); } else { if ($is_response) { $rname = $Conf->resp_round_text($roundnum); $confirm = Ht::xmsg("confirm", ($rname ? "{$rname} response" : "Response") . ' submitted.'); } else { if ($cinfo->commentId) { $confirm = Ht::xmsg("confirm", "Comment saved."); } else { $confirm = Ht::xmsg("confirm", "Comment deleted."); } } } } $j = array("ok" => $ok); if ($cinfo->commentId) { $j["cmt"] = $cinfo->unparse_json($Me); } if ($confirm) { $j["msg"] = $confirm; } $Conf->ajaxExit($j); }
public function save($req, $contact) { global $Conf, $Now; if (is_array($req)) { $req = (object) $req; } $Table = $this->prow->comment_table_name(); $LinkTable = $this->prow->table_name(); $LinkColumn = $this->prow->id_column(); $req_visibility = get($req, "visibility"); $is_response = !!($this->commentType & COMMENTTYPE_RESPONSE); if ($is_response && get($req, "submit")) { $ctype = COMMENTTYPE_RESPONSE | COMMENTTYPE_AUTHOR; } else { if ($is_response) { $ctype = COMMENTTYPE_RESPONSE | COMMENTTYPE_AUTHOR | COMMENTTYPE_DRAFT; } else { if ($req_visibility == "a" || $req_visibility == "au") { $ctype = COMMENTTYPE_AUTHOR; } else { if ($req_visibility == "p" || $req_visibility == "pc") { $ctype = COMMENTTYPE_PCONLY; } else { if ($req_visibility == "admin") { $ctype = COMMENTTYPE_ADMINONLY; } else { if ($this->commentId && $req_visibility === null) { $ctype = $this->commentType; } else { // $req->visibility == "r" || $req->visibility == "rev" $ctype = COMMENTTYPE_REVIEWER; } } } } } } if ($is_response ? $this->prow->blind : $Conf->is_review_blind(!!get($req, "blind"))) { $ctype |= COMMENTTYPE_BLIND; } // tags if ($is_response) { $ctags = " response "; if (($rname = $Conf->resp_round_name($this->commentRound)) != "1") { $ctags .= "{$rname}response "; } } else { if (get($req, "tags") && preg_match_all(',\\S+,', $req->tags, $m)) { $tagger = new Tagger($contact); $ctags = array(); foreach ($m[0] as $text) { if (($text = $tagger->check($text, Tagger::NOVALUE)) && !stri_ends_with($text, "response")) { $ctags[strtolower($text)] = $text; } } $tagger->sort($ctags); $ctags = count($ctags) ? " " . join(" ", $ctags) . " " : null; } else { $ctags = null; } } // notifications $displayed = !($ctype & COMMENTTYPE_DRAFT); // query $text = get_s($req, "text"); $q = ""; $qv = array(); if ($text === "" && $this->commentId) { $change = true; $q = "delete from {$Table} where commentId={$this->commentId}"; } else { if ($text === "") { /* do nothing */ } else { if (!$this->commentId) { $change = true; $qa = ["contactId, {$LinkColumn}, commentType, comment, commentOverflow, timeModified, replyTo"]; $qb = [$contact->contactId, $this->prow->{$LinkColumn}, $ctype, "?", "?", $Now, 0]; if (strlen($text) <= 32000) { array_push($qv, $text, null); } else { array_push($qv, UnicodeHelper::utf8_prefix($text, 200), $text); } if ($ctags !== null) { $qa[] = "commentTags"; $qb[] = "?"; $qv[] = $ctags; } if ($is_response) { $qa[] = "commentRound"; $qb[] = $this->commentRound; } if ($displayed) { $qa[] = "timeDisplayed, timeNotified"; $qb[] = "{$Now}, {$Now}"; } $q = "insert into {$Table} (" . join(", ", $qa) . ") select " . join(", ", $qb) . "\n"; if ($is_response) { // make sure there is exactly one response $q .= " from (select {$LinkTable}.{$LinkColumn}, coalesce(commentId, 0) commentId\n from {$LinkTable}\n left join {$Table} on ({$Table}.{$LinkColumn}={$LinkTable}.{$LinkColumn} and (commentType&" . COMMENTTYPE_RESPONSE . ")!=0 and commentRound={$this->commentRound})\n where {$LinkTable}.{$LinkColumn}={$this->prow->{$LinkColumn}} limit 1) t\n where t.commentId=0"; } } else { $change = $this->commentType >= COMMENTTYPE_AUTHOR != $ctype >= COMMENTTYPE_AUTHOR; if ($this->timeModified >= $Now) { $Now = $this->timeModified + 1; } // do not notify on updates within 3 hours $qa = ""; if ($this->timeNotified + 10800 < $Now || $ctype & COMMENTTYPE_RESPONSE && !($ctype & COMMENTTYPE_DRAFT) && $this->commentType & COMMENTTYPE_DRAFT) { $qa .= ", timeNotified={$Now}"; } // reset timeDisplayed if you change the comment type if ((!$this->timeDisplayed || $this->ordinal_missing($ctype)) && $text !== "" && $displayed) { $qa .= ", timeDisplayed={$Now}"; } $q = "update {$Table} set timeModified={$Now}{$qa}, commentType={$ctype}, comment=?, commentOverflow=?, commentTags=? where commentId={$this->commentId}"; if (strlen($text) <= 32000) { array_push($qv, $text, null); } else { array_push($qv, UnicodeHelper::utf8_prefix($text, 200), $text); } $qv[] = $ctags; } } } $result = Dbl::qe_apply($q, $qv); if (!$result) { return false; } $cmtid = $this->commentId ?: $result->insert_id; if (!$cmtid) { return false; } // log $contact->log_activity("Comment {$cmtid} " . ($text !== "" ? "saved" : "deleted"), $this->prow->{$LinkColumn}); // ordinal if ($text !== "" && $this->ordinal_missing($ctype)) { $this->save_ordinal($cmtid, $ctype, $Table, $LinkTable, $LinkColumn); } // reload if ($text !== "") { $comments = $this->prow->fetch_comments("commentId={$cmtid}"); $this->merge($comments[$cmtid], $this->prow); if ($this->timeNotified == $this->timeModified) { self::$watching = $this; $this->prow->notify(WATCHTYPE_COMMENT, "CommentInfo::watch_callback", $contact); self::$watching = null; } } else { $this->commentId = 0; $this->comment = ""; $this->commentTags = null; } return true; }
function reviewLinks($prow, $rrows, $crows, $rrow, $mode, &$allreviewslink) { global $Conf, $Me; $conflictType = $Me->view_conflict_type($prow); $allow_admin = $Me->allow_administer($prow); $any_comments = false; $admin = $Me->can_administer($prow); $xsep = ' <span class="barsep">·</span> '; $nvisible = 0; $myrr = null; if ($rrows) { foreach ($rrows as $rr) { if ($Me->can_view_review($prow, $rr, null)) { $nvisible++; } if ($rr->contactId == $Me->contactId || !$myrr && $Me->is_my_review($rr)) { $myrr = $rr; } } } // comments $pret = ""; if ($crows && count($crows) > 0 && !$rrow && $mode !== "edit") { $cids = array(); $cnames = array(); $tagger = new Tagger($Me); foreach ($crows as $cr) { if ($Me->can_view_comment($prow, $cr, null)) { if ($Me->can_view_comment_identity($prow, $cr, null)) { $n = Text::abbrevname_html($cr->user()); } else { $n = "anonymous"; } if ($cr->commentType & COMMENTTYPE_RESPONSE) { $rname = $Conf->resp_round_name($cr->commentRound); $n = $n === "anonymous" ? "" : " ({$n})"; if ($cr->commentType & COMMENTTYPE_DRAFT && $rname != "1") { $n = "<i>Draft {$rname} Response</i>{$n}"; } else { if ($cr->commentType & COMMENTTYPE_DRAFT) { $n = "<i>Draft Response</i>{$n}"; } else { if ($rname != "1") { $n = "<i>{$rname} Response</i>{$n}"; } else { $n = "<i>Response</i>{$n}"; } } } } $cids[] = $cid = CommentInfo::unparse_html_id($cr); $tclass = "cmtlink"; if ($cr->commentTags && ($tags = Tagger::strip_nonviewable($cr->commentTags, $Me)) && $Me->can_view_comment_tags($prow, $cr, null) && ($color = TagInfo::color_classes($tags))) { if (TagInfo::classes_have_colors($color)) { $tclass .= " tagcolorspan"; } $tclass .= " {$color} taghl"; } $cnames[] = '<a class="' . $tclass . '" href="#' . $cid . '">' . $n . '</a>'; } } if (count($cids) > 0) { $pret = '<div class="revnotes"><a href="#' . $cids[0] . '"><strong>' . plural(count($cids), "Comment") . '</strong></a>: <span class="nb">' . join(',</span> <span class="nb">', $cnames) . "</span></div>"; $any_comments = true; } } $t = ""; // see all reviews $allreviewslink = false; if (($nvisible > 1 || $nvisible > 0 && !$myrr) && ($mode !== "p" || $rrow)) { $allreviewslink = true; $x = '<a href="' . hoturl("paper", "p={$prow->paperId}") . '" class="xx">' . Ht::img("view24.png", "[All reviews]", "dlimg") . " <u>All reviews</u></a>"; $t .= ($t === "" ? "" : $xsep) . $x; } // edit paper if ($mode !== "edit" && $prow->conflictType >= CONFLICT_AUTHOR && !$Me->can_administer($prow)) { $x = '<a href="' . hoturl("paper", "p={$prow->paperId}&m=edit") . '" class="xx">' . Ht::img("edit24.png", "[Edit paper]", "dlimg") . " <u><strong>Edit paper</strong></u></a>"; $t .= ($t === "" ? "" : $xsep) . $x; } // edit review if ($mode === "re" || $mode === "assign" && $t !== "" || !$prow) { /* no link */ } else { if ($myrr && $rrow != $myrr) { $myrlink = unparseReviewOrdinal($myrr); $a = '<a href="' . hoturl("review", "p={$prow->paperId}&r={$myrlink}") . '" class="xx">'; if ($Me->can_review($prow, $myrr)) { $x = $a . Ht::img("review24.png", "[Edit review]", "dlimg") . " <u><b>Edit your review</b></u></a>"; } else { $x = $a . Ht::img("review24.png", "[Your review]", "dlimg") . " <u><b>Your review</b></u></a>"; } $t .= ($t === "" ? "" : $xsep) . $x; } else { if (!$myrr && !$rrow && $Me->can_review($prow, null)) { $x = '<a href="' . hoturl("review", "p={$prow->paperId}&m=re") . '" class="xx">' . Ht::img("review24.png", "[Write review]", "dlimg") . " <u><b>Write review</b></u></a>"; $t .= ($t === "" ? "" : $xsep) . $x; } } } // review assignments if ($mode !== "assign" && $mode !== "edit" && $Me->can_request_review($prow, true)) { $x = '<a href="' . hoturl("assign", "p={$prow->paperId}") . '" class="xx">' . Ht::img("assign24.png", "[Assign]", "dlimg") . " <u>" . ($admin ? "Assign reviews" : "External reviews") . "</u></a>"; $t .= ($t === "" ? "" : $xsep) . $x; } // new comment $nocmt = preg_match('/\\A(?:assign|contact|edit|re)\\z/', $mode); if (!$allreviewslink && !$nocmt && $Me->can_comment($prow, null)) { $x = '<a href="#cnew" onclick="return papercomment.edit_new()" class="xx">' . Ht::img("comment24.png", "[Add comment]", "dlimg") . " <u>Add comment</u></a>"; $t .= ($t === "" ? "" : $xsep) . $x; $any_comments = true; } // new response if (!$nocmt && ($prow->conflictType >= CONFLICT_AUTHOR || $allow_admin) && ($rrounds = $Conf->time_author_respond())) { foreach ($rrounds as $i => $rname) { $cid = ($i ? $rname : "") . "response"; $what = "Add"; if ($crows) { foreach ($crows as $cr) { if ($cr->commentType & COMMENTTYPE_RESPONSE && $cr->commentRound == $i) { $what = "Edit"; if ($cr->commentType & COMMENTTYPE_DRAFT) { $what = "Edit draft"; } } } } $x = '<a href="#' . $cid . '" onclick=\'return papercomment.edit_response(' . json_encode($rname) . ')\' class="xx">' . Ht::img("comment24.png", "[{$what} response]", "dlimg") . " " . ($conflictType >= CONFLICT_AUTHOR ? '<u style="font-weight:bold">' : '<u>') . $what . ($i ? " {$rname}" : "") . ' response</u></a>'; $t .= ($t === "" ? "" : $xsep) . $x; $any_comments = true; } } // override conflict if ($allow_admin && !$admin) { $x = '<a href="' . selfHref(array("forceShow" => 1)) . '" class="xx">' . Ht::img("override24.png", "[Override]", "dlimg") . " <u>Override conflict</u></a> to show reviewers and allow editing"; $t .= ($t === "" ? "" : $xsep) . $x; } else { if ($Me->privChair && !$allow_admin) { $x = "You can’t override your conflict because this paper has an administrator."; $t .= ($t === "" ? "" : $xsep) . $x; } } if ($any_comments) { CommentInfo::echo_script($prow); } if (($list = SessionList::active()) && ($pret || $t)) { return '<div class="has_hotcrp_list" data-hotcrp-list="' . $list->listno . '">' . $pret . $t . '</div>'; } else { return $pret . $t; } }
function run(Contact $user, $qreq, $ssel) { global $Conf; $result = Dbl::qe_raw($Conf->paperQuery($user, array("paperId" => $ssel->selection(), "allReviews" => 1, "reviewerName" => 1))); $texts = array(); $errors = array(); $user->set_forceShow(true); $rf = ReviewForm::get(); while ($row = PaperInfo::fetch($result, $user)) { if ($whyNot = $user->perm_view_review($row, null, null)) { $errors[whyNotText($whyNot, "view review")] = true; } else { if ($row->reviewSubmitted) { defappend($texts[$row->paperId], $rf->pretty_text($row, $row, $user) . "\n"); } } } $crows = $Conf->comment_rows($Conf->paperQuery($user, array("paperId" => $ssel->selection(), "allComments" => 1, "reviewerName" => 1)), $user); foreach ($crows as $row) { if ($user->can_view_comment($row, $row, null)) { $crow = new CommentInfo($row, $row); defappend($texts[$row->paperId], $crow->unparse_text($user) . "\n"); } } $this->finish($ssel, $texts, $errors); }
} else { json_exit(["ok" => false]); } } if ($qreq->fn === "events" && $Me->is_reviewer()) { $from = $qreq->from; if (!$from || !ctype_digit($from)) { $from = $Now; } $entries = $Conf->reviewerActivity($Me, $from, 10); $when = $from; $rows = array(); $rf = ReviewForm::get(); foreach ($entries as $which => $xr) { if ($xr->isComment) { $rows[] = CommentInfo::unparse_flow_entry($xr, $Me, ""); $when = $xr->timeModified; } else { $rows[] = $rf->reviewFlowEntry($Me, $xr, ""); $when = $xr->reviewSubmitted; } } json_exit(["ok" => true, "from" => (int) $from, "to" => (int) $when - 1, "rows" => $rows]); } else { if ($qreq->fn === "events") { json_exit(["ok" => false]); } } if ($qreq->fn === "searchcompletion") { $s = new PaperSearch($Me, ""); $Conf->ajaxExit(array("ok" => true, "searchcompletion" => $s->search_completion()));