public function add_tag_info_json($pj, Contact $user)
 {
     if (!property_exists($this, "paperTags")) {
         $this->load_tags();
     }
     $tagger = new Tagger($user);
     $editable = $this->editable_tags($user);
     $viewable = $this->viewable_tags($user);
     $tags_view_html = $tagger->unparse_and_link($viewable, $this->paperTags, false);
     $pj->tags = TagInfo::split($viewable);
     $pj->tags_edit_text = $tagger->unparse($editable);
     $pj->tags_view_html = $tags_view_html;
     $pj->color_classes = TagInfo::color_classes($viewable);
 }
 static function settags_api($user, $qreq, $prow)
 {
     global $Conf;
     if ($qreq->cancelsettags) {
         json_exit(["ok" => true]);
     }
     if ($prow && !$user->can_view_paper($prow)) {
         json_exit(["ok" => false, "error" => "No such paper."]);
     }
     // save tags using assigner
     $x = array("paper,action,tag");
     if ($prow) {
         if (isset($qreq->tags)) {
             $x[] = "{$prow->paperId},tag,all#clear";
             foreach (TagInfo::split($qreq->tags) as $t) {
                 $x[] = "{$prow->paperId},tag," . CsvGenerator::quote($t);
             }
         }
         foreach (TagInfo::split((string) $qreq->addtags) as $t) {
             $x[] = "{$prow->paperId},tag," . CsvGenerator::quote($t);
         }
         foreach (TagInfo::split((string) $qreq->deltags) as $t) {
             $x[] = "{$prow->paperId},tag," . CsvGenerator::quote($t . "#clear");
         }
     } else {
         if (isset($qreq->tagassignment)) {
             $pids = [];
             $pid = -1;
             foreach (preg_split('/[\\s,]+/', $qreq->tagassignment) as $w) {
                 if ($w !== "" && ctype_digit($w)) {
                     $pid = intval($w);
                 } else {
                     if ($w !== "" && $pid > 0) {
                         $x[] = "{$pid},tag," . CsvGenerator::quote($w);
                         $pids[$pid] = true;
                     }
                 }
             }
         }
     }
     $assigner = new AssignmentSet($user, $user->is_admin_force());
     $assigner->parse(join("\n", $x));
     $error = join("<br />", $assigner->errors_html());
     $ok = $assigner->execute();
     // exit
     if ($ok && $prow) {
         $prow->load_tags();
         $treport = self::tagreport($user, $prow);
         if ($treport->warnings) {
             $Conf->warnMsg(join("<br>", $treport->warnings));
         }
         $taginfo = (object) ["ok" => true, "pid" => $prow->paperId];
         $prow->add_tag_info_json($taginfo, $user);
         json_exit($taginfo, true);
     } else {
         if ($ok) {
             $p = [];
             $result = Dbl::qe_raw($Conf->paperQuery($user, ["paperId" => array_keys($pids), "tags" => true]));
             while ($prow = PaperInfo::fetch($result, $user)) {
                 $p[$prow->paperId] = (object) [];
                 $prow->add_tag_info_json($p[$prow->paperId], $user);
             }
             json_exit(["ok" => true, "p" => $p]);
         } else {
             json_exit(["ok" => false, "error" => $error], true);
         }
     }
 }
 public function unparse_json($contact, $include_displayed_at = false)
 {
     global $Conf;
     if ($this->commentId && !$contact->can_view_comment($this->prow, $this, null)) {
         return false;
     }
     // placeholder for new comment
     if (!$this->commentId) {
         if (!$contact->can_comment($this->prow, $this)) {
             return false;
         }
         $cj = (object) array("pid" => $this->prow->paperId, "is_new" => true, "editable" => true);
         if ($this->commentType & COMMENTTYPE_RESPONSE) {
             $cj->response = $Conf->resp_round_name($this->commentRound);
         }
         return $cj;
     }
     // otherwise, viewable comment
     $cj = (object) array("pid" => $this->prow->paperId, "cid" => $this->commentId);
     $cj->ordinal = $this->unparse_ordinal();
     $cj->visibility = self::$visibility_map[$this->commentType & COMMENTTYPE_VISIBILITY];
     if ($this->commentType & COMMENTTYPE_BLIND) {
         $cj->blind = true;
     }
     if ($this->commentType & COMMENTTYPE_DRAFT) {
         $cj->draft = true;
     }
     if ($this->commentType & COMMENTTYPE_RESPONSE) {
         $cj->response = $Conf->resp_round_name($this->commentRound);
     }
     if ($contact->can_comment($this->prow, $this)) {
         $cj->editable = true;
     }
     // tags
     if ($this->commentTags && $contact->can_view_comment_tags($this->prow, $this, null)) {
         if ($tags = $this->viewable_tags($contact)) {
             $cj->tags = TagInfo::split($tags);
         }
         if ($tags && ($cc = TagInfo::color_classes($tags))) {
             $cj->color_classes = $cc;
         }
     }
     // identity and time
     $idable = $contact->can_view_comment_identity($this->prow, $this, null);
     $idable_override = $idable || $contact->can_view_comment_identity($this->prow, $this, true);
     if ($idable || $idable_override) {
         $user = $this->user();
         $cj->author = Text::user_html($user);
         $cj->author_email = $user->email;
         if (!$idable) {
             $cj->author_hidden = true;
         }
     }
     if ($this->timeModified > 0 && $idable_override) {
         $cj->modified_at = (int) $this->timeModified;
         $cj->modified_at_text = $Conf->printableTime($cj->modified_at);
     } else {
         if ($this->timeModified > 0) {
             $cj->modified_at = $Conf->obscure_time($this->timeModified);
             $cj->modified_at_text = $Conf->unparse_time_obscure($cj->modified_at);
             $cj->modified_at_obscured = true;
         }
     }
     if ($include_displayed_at) {
         // XXX exposes information, should hide before export
         $cj->displayed_at = (int) $this->timeDisplayed;
     }
     // text
     if ($this->commentOverflow) {
         $cj->text = $this->commentOverflow;
     } else {
         $cj->text = $this->comment;
     }
     // format
     if (($fmt = $this->commentFormat) === null) {
         $fmt = Conf::$gDefaultFormat;
     }
     if ($fmt) {
         $cj->format = (int) $fmt;
     }
     return $cj;
 }
 function content($fieldId, $row)
 {
     global $Conf;
     switch ($fieldId) {
         case self::FIELD_NAME:
             $t = Text::name_html($row);
             if (trim($t) == "") {
                 $t = "[No name]";
             }
             $t = '<span class="taghl">' . $t . '</span>';
             if ($this->contact->privChair) {
                 $t = "<a href=\"" . hoturl("profile", "u=" . urlencode($row->email) . $this->contactLinkArgs) . "\"" . ($row->disabled ? " class='uu'" : "") . ">{$t}</a>";
             }
             if ($row->roles & Contact::ROLE_CHAIR) {
                 $t .= ' <span class="pcrole">(chair)</span>';
             } else {
                 if (($row->roles & (Contact::ROLE_ADMIN | Contact::ROLE_PC)) == (Contact::ROLE_ADMIN | Contact::ROLE_PC)) {
                     $t .= ' <span class="pcrole">(PC, sysadmin)</span>';
                 } else {
                     if ($row->roles & Contact::ROLE_ADMIN) {
                         $t .= ' <span class="pcrole">(sysadmin)</span>';
                     } else {
                         if ($row->roles & Contact::ROLE_PC && $this->limit != "pc") {
                             $t .= ' <span class="pcrole">(PC)</span>';
                         }
                     }
                 }
             }
             if ($this->contact->privChair && $row->email != $this->contact->email) {
                 $t .= " <a href=\"" . hoturl("index", "actas=" . urlencode($row->email)) . "\">" . Ht::img("viewas.png", "[Act as]", array("title" => "Act as " . Text::name_text($row))) . "</a>";
             }
             if ($row->disabled && $this->contact->isPC) {
                 $t .= ' <span class="hint">(disabled)</span>';
             }
             return $t;
         case self::FIELD_EMAIL:
             if (!$this->contact->isPC) {
                 return "";
             }
             $e = htmlspecialchars($row->email);
             if (strpos($row->email, "@") === false) {
                 return $e;
             } else {
                 return "<a href=\"mailto:{$e}\">{$e}</a>";
             }
         case self::FIELD_AFFILIATION:
         case self::FIELD_AFFILIATION_ROW:
             return htmlspecialchars($row->affiliation);
         case self::FIELD_LASTVISIT:
             if (!$row->lastLogin) {
                 return "Never";
             }
             return $Conf->printableTimeShort($row->lastLogin);
         case self::FIELD_SELECTOR:
         case self::FIELD_SELECTOR_ON:
             $this->any->sel = true;
             $c = "";
             if ($fieldId == self::FIELD_SELECTOR_ON) {
                 $c = " checked='checked'";
             }
             return "<input type='checkbox' class='cb' name='pap[]' value='{$row->contactId}' tabindex='1' id='psel{$this->count}' onclick='rangeclick(event,this)' {$c}/>";
         case self::FIELD_HIGHTOPICS:
         case self::FIELD_LOWTOPICS:
             if (!defval($row, "topicIds")) {
                 return "";
             }
             $wanthigh = $fieldId == self::FIELD_HIGHTOPICS;
             $topics = array_combine(explode(",", $row->topicIds), explode(",", $row->topicInterest));
             $nt = $nti = array();
             foreach ($topics as $k => $v) {
                 if ($wanthigh ? $v > 0 : $v < 0) {
                     $nt[] = $k;
                     $nti[] = $v;
                 }
             }
             if (count($nt)) {
                 return PaperInfo::unparse_topic_list_html($nt, $nti, true);
             } else {
                 return "";
             }
         case self::FIELD_REVIEWS:
             if (!$row->numReviews && !$row->numReviewsSubmitted) {
                 return "";
             }
             $a1 = "<a href=\"" . hoturl("search", "t=s&amp;q=re:" . urlencode($row->email)) . "\">";
             if ($row->numReviews == $row->numReviewsSubmitted) {
                 return "{$a1}<b>{$row->numReviewsSubmitted}</b></a>";
             } else {
                 return "{$a1}<b>{$row->numReviewsSubmitted}</b>/{$row->numReviews}</a>";
             }
         case self::FIELD_LEADS:
             if (!$row->numLeads) {
                 return "";
             }
             return "<a href=\"" . hoturl("search", "t=s&amp;q=lead:" . urlencode($row->email)) . "\">{$row->numLeads}</a>";
         case self::FIELD_SHEPHERDS:
             if (!$row->numShepherds) {
                 return "";
             }
             return "<a href=\"" . hoturl("search", "t=s&amp;q=shepherd:" . urlencode($row->email)) . "\">{$row->numShepherds}</a>";
         case self::FIELD_REVIEW_RATINGS:
             if (!$row->numReviews && !$row->numReviewsSubmitted || !$row->numRatings) {
                 return "";
             }
             $a = array();
             $b = array();
             if ($row->sumRatings > 0) {
                 $a[] = $row->sumRatings . " positive";
                 $b[] = "<a href=\"" . hoturl("search", "q=re:" . urlencode($row->email) . "+rate:%2B") . "\">+" . $row->sumRatings . "</a>";
             }
             if ($row->sumRatings < $row->numRatings) {
                 $a[] = $row->numRatings - $row->sumRatings . " negative";
                 $b[] = "<a href=\"" . hoturl("search", "q=re:" . urlencode($row->email) . "+rate:-") . "\">&minus;" . ($row->numRatings - $row->sumRatings) . "</a>";
             }
             return "<span class='hastitle' title='" . join(", ", $a) . "'>" . join(" ", $b) . "</span>";
         case self::FIELD_PAPERS:
             if (!$row->paperIds) {
                 return "";
             }
             $x = explode(",", $row->paperIds);
             sort($x, SORT_NUMERIC);
             foreach ($x as &$v) {
                 $v = '<a href="' . hoturl("paper", "p={$v}") . '">' . $v . '</a>';
             }
             $ls = "p/s/";
             if ($this->limit == "auuns" || $this->limit == "all") {
                 $ls = "p/all/";
             }
             $ls = htmlspecialchars($ls . urlencode("au:" . $row->email));
             return '<div class="has_hotcrp_list" data-hotcrp-list="' . $ls . '">' . join(", ", $x) . '</div>';
         case self::FIELD_REVIEW_PAPERS:
             if (!$row->paperIds) {
                 return "";
             }
             $pids = explode(",", $row->paperIds);
             $rids = explode(",", $row->reviewIds);
             $ords = explode(",", $row->reviewOrdinals);
             $spids = $pids;
             sort($spids, SORT_NUMERIC);
             $m = array();
             for ($i = 0; $i != count($pids); ++$i) {
                 if ($ords[$i]) {
                     $url = hoturl("paper", "p=" . $pids[$i] . "#r" . $pids[$i] . unparseReviewOrdinal($ords[$i]));
                 } else {
                     $url = hoturl("review", "p=" . $pids[$i] . "&amp;r=" . $rids[$i]);
                 }
                 $m[$pids[$i]] = "<a href=\"{$url}\">" . $pids[$i] . "</a>";
             }
             ksort($m, SORT_NUMERIC);
             $ls = htmlspecialchars("p/s/" . urlencode("re:" . $row->email));
             return '<div class="has_hotcrp_list" data-hotcrp-list="' . $ls . '">' . join(", ", $m) . '</div>';
         case self::FIELD_TAGS:
             if ($this->contact->isPC && ($tags = $row->viewable_tags($this->contact))) {
                 $x = [];
                 foreach (TagInfo::split($tags) as $t) {
                     $x[] = '<a class="qq nw" href="' . hoturl("users", "t=%23" . TagInfo::base($t)) . '">' . $this->tagger->unparse_hashed($t) . '</a>';
                 }
                 return join(" ", $x);
             }
             return "";
         case self::FIELD_COLLABORATORS:
             if (!$this->contact->isPC || !($row->roles & Contact::ROLE_PC)) {
                 return "";
             }
             $t = array();
             foreach (explode("\n", $row->collaborators) as $collab) {
                 if (preg_match(',\\A(.*?)\\s*(\\(.*\\))\\s*\\z,', $collab, $m)) {
                     $t[] = '<span class="nw">' . htmlspecialchars($m[1]) . ' <span class="auaff">' . htmlspecialchars($m[2]) . '</span></span>';
                 } else {
                     if (($collab = trim($collab)) !== "" && strcasecmp($collab, "None")) {
                         $t[] = '<span class="nw">' . htmlspecialchars($collab) . '</span>';
                     }
                 }
             }
             return join("; ", $t);
         default:
             $f = ReviewForm::field($fieldId);
             if (!$f) {
                 return "";
             }
             if (!($row->roles & Contact::ROLE_PC) && !$this->contact->privChair && $this->limit != "req") {
                 return "";
             }
             $v = scoreCounts($row->{$fieldId}, $this->scoreMax[$fieldId]);
             $m = "";
             if ($v->n > 0) {
                 $m = $f->unparse_graph($v, 2, 0);
             }
             return $m;
     }
 }