function _search() { global $Conf; if ($this->_matches === false) { return false; } assert($this->_matches === null); if ($this->limitName === "x") { $this->_matches = array(); return true; } // parse and clean the query $qe = $this->_searchQueryType($this->q); //Conf::msg_info(Ht::pre_text(var_export($qe, true))); if (!$qe) { $qe = new SearchTerm("t"); } // apply complex limiters (only current example: "acc" for non-chairs) $limit = $this->limitName; if ($limit === "acc" && !$this->privChair) { $qe = SearchTerm::make_op("and", array($qe, $this->_searchQueryWord("dec:yes", false))); } // apply review rounds (top down, needs separate step) if ($this->reviewAdjust) { $qe = $this->_query_adjust_reviews($qe, null); if ($this->_reviewAdjustError) { $this->warn("Unexpected use of “round:” or “rate:” ignored. Stick to the basics, such as “re:reviewername round:roundname”."); } } //Conf::msg_info(Ht::pre_text(var_export($qe, true))); // collect clauses into tables, columns, and filters $sqi = new SearchQueryInfo(); $sqi->add_table("Paper"); $sqi->add_column("paperId", "Paper.paperId"); // always include columns needed by rights machinery $sqi->add_column("timeSubmitted", "Paper.timeSubmitted"); $sqi->add_column("timeWithdrawn", "Paper.timeWithdrawn"); $sqi->add_column("outcome", "Paper.outcome"); $filters = array(); $this->_clauseTermSet($qe, $sqi, $filters); //Conf::msg_info(Ht::pre_text(var_export($filters, true))); // status limitation parts if ($limit === "rable") { $limitcontact = $this->_reviewer_fixed ? $this->reviewer() : $this->contact; if ($limitcontact->can_accept_review_assignment_ignore_conflict(null)) { $limit = $Conf->can_pc_see_all_submissions() ? "act" : "s"; } else { if (!$limitcontact->isPC) { $limit = "r"; } } } if ($limit === "s" || $limit === "req" || $limit === "acc" || $limit === "und" || $limit === "unm" || $limit === "rable" && !$Conf->can_pc_see_all_submissions()) { $filters[] = "Paper.timeSubmitted>0"; } else { if ($limit === "act" || $limit === "r" || $limit === "rable") { $filters[] = "Paper.timeWithdrawn<=0"; } else { if ($limit === "unsub") { $filters[] = "(Paper.timeSubmitted<=0 and Paper.timeWithdrawn<=0)"; } else { if ($limit === "lead") { $filters[] = "Paper.leadContactId=" . $this->cid; } else { if ($limit === "manager") { if ($this->privChair) { $filters[] = "(Paper.managerContactId=" . $this->cid . " or Paper.managerContactId=0)"; } else { $filters[] = "Paper.managerContactId=" . $this->cid; } $filters[] = "Paper.timeSubmitted>0"; } } } } } // decision limitation parts if ($limit === "acc") { $filters[] = "Paper.outcome>0"; } else { if ($limit === "und") { $filters[] = "Paper.outcome=0"; } } // other search limiters if ($limit === "a") { $filters[] = $this->contact->actAuthorSql("PaperConflict"); $this->needflags |= self::F_AUTHOR; } else { if ($limit === "r") { $filters[] = "MyReview.reviewType is not null"; $this->needflags |= self::F_REVIEWER; } else { if ($limit === "ar") { $filters[] = "(" . $this->contact->actAuthorSql("PaperConflict") . " or (Paper.timeWithdrawn<=0 and MyReview.reviewType is not null))"; $this->needflags |= self::F_AUTHOR | self::F_REVIEWER; } else { if ($limit === "rout") { $filters[] = "MyReview.reviewNeedsSubmit!=0"; $this->needflags |= self::F_REVIEWER; } else { if ($limit === "revs") { $sqi->add_table("Limiter", array("join", "PaperReview")); } else { if ($limit === "req") { $sqi->add_table("Limiter", array("join", "PaperReview", "Limiter.requestedBy={$this->cid} and Limiter.reviewType=" . REVIEW_EXTERNAL)); } else { if ($limit === "unm") { $filters[] = "Paper.managerContactId=0"; } } } } } } } // add common tables: conflicts, my own review, paper blindness if ($this->needflags & (self::F_MANAGER | self::F_NONCONFLICT | self::F_AUTHOR)) { $sqi->add_table("PaperConflict", array("left join", "PaperConflict", "PaperConflict.contactId={$this->cid}")); $sqi->add_column("conflictType", "PaperConflict.conflictType"); } if ($this->needflags & self::F_REVIEWER) { if ($Conf->submission_blindness() == Conf::BLIND_OPTIONAL) { $sqi->add_column("paperBlind", "Paper.blind"); } $qb = ""; if ($tokens = $this->contact->review_tokens()) { $qb = " or MyReview.reviewToken in (" . join(",", $tokens) . ")"; } $sqi->add_table("MyReview", array("left join", "PaperReview", "(MyReview.contactId={$this->cid}{$qb})")); $sqi->add_column("myReviewType", "MyReview.reviewType"); $sqi->add_column("myReviewNeedsSubmit", "MyReview.reviewNeedsSubmit"); $sqi->add_column("myReviewSubmitted", "MyReview.reviewSubmitted"); } // check for annotated order $order_anno_tag = null; if ($qe->type !== "then" && ($sort = $qe->get_float("sort")) && ($tag = self::_check_sort_order_anno($sort))) { $dt = TagInfo::make_defined_tag($tag); if (count($dt->order_anno_list())) { $order_anno_tag = $dt; } } // add permissions tables if we will filter the results $need_filter = $this->needflags & self::F_XVIEW || $Conf->has_tracks() || $qe->type === "then" || $qe->get_float("heading") || $limit === "rable" || $order_anno_tag; if ($need_filter) { $sqi->add_rights_columns(); if ($Conf->submission_blindness() == Conf::BLIND_OPTIONAL) { $sqi->add_column("paperBlind", "Paper.blind"); } } // XXX some of this should be shared with paperQuery if ($need_filter && $Conf->has_track_tags() || get($this->_query_options, "tags") || $order_anno_tag) { $sqi->add_column("paperTags", "(select group_concat(' ', tag, '#', tagIndex separator '') from PaperTag where PaperTag.paperId=Paper.paperId)"); } if (get($this->_query_options, "scores") || get($this->_query_options, "reviewTypes") || get($this->_query_options, "reviewContactIds")) { $j = "group_concat(contactId order by reviewId) reviewContactIds"; $sqi->add_column("reviewContactIds", "R_submitted.reviewContactIds"); if (get($this->_query_options, "reviewTypes")) { $j .= ", group_concat(reviewType order by reviewId) reviewTypes"; $sqi->add_column("reviewTypes", "R_submitted.reviewTypes"); } foreach (get($this->_query_options, "scores") ?: array() as $f) { $j .= ", group_concat({$f} order by reviewId) {$f}Scores"; $sqi->add_column("{$f}Scores", "R_submitted.{$f}Scores"); } $sqi->add_table("R_submitted", array("left join", "(select paperId, {$j} from PaperReview where reviewSubmitted>0 group by paperId)")); } // create query $q = "select "; foreach ($sqi->columns as $colname => $value) { $q .= $value . " " . $colname . ", "; } $q = substr($q, 0, strlen($q) - 2) . "\n from "; foreach ($sqi->tables as $tabname => $value) { if (!$value) { $q .= $tabname; } else { $joiners = array("{$tabname}.paperId=Paper.paperId"); for ($i = 2; $i < count($value); ++$i) { $joiners[] = "(" . $value[$i] . ")"; } $q .= "\n " . $value[0] . " " . $value[1] . " as " . $tabname . " on (" . join("\n and ", $joiners) . ")"; } } if (count($filters)) { $q .= "\n where " . join("\n and ", $filters); } $q .= "\n group by Paper.paperId"; //Conf::msg_info(Ht::pre_text_wrap($q)); // actually perform query $result = Dbl::qe_raw($q); if (!$result) { return $this->_matches = false; } $this->_matches = array(); // correct query, create thenmap, groupmap, highlightmap $need_then = $qe->type === "then"; $this->thenmap = null; if ($need_then && $qe->nthen > 1) { $this->thenmap = array(); } $this->highlightmap = array(); if ($need_filter) { $tag_order = []; while ($row = PaperInfo::fetch($result, $this->cid)) { if (!$this->contact->can_view_paper($row) || $limit === "rable" && !$limitcontact->can_accept_review_assignment_ignore_conflict($row)) { $x = false; } else { if ($need_then) { $x = false; for ($i = 0; $i < $qe->nthen && $x === false; ++$i) { if ($this->_clauseTermCheck($qe->value[$i], $row)) { $x = $i; } } } else { $x = !!$this->_clauseTermCheck($qe, $row); } } if ($x === false) { continue; } $this->_matches[] = $row->paperId; if ($this->thenmap !== null) { $this->thenmap[$row->paperId] = $x; } if ($need_then) { for ($j = $qe->nthen; $j < count($qe->value); ++$j) { if ($this->_clauseTermCheck($qe->value[$j], $row) && $qe->highlights[$j - $qe->nthen] & 1 << $x) { $this->highlightmap[$row->paperId] = $qe->highlight_types[$j - $qe->nthen] . "highlight"; break; } } } if ($order_anno_tag) { if ($row->has_viewable_tag($order_anno_tag->tag, $this->contact)) { $tag_order[] = [$row->paperId, $row->tag_value($order_anno_tag->tag)]; } else { $tag_order[] = [$row->paperId, TAG_INDEXBOUND]; } } } } else { while ($row = $result->fetch_object()) { $this->_matches[] = (int) $row->paperId; } } Dbl::free($result); // add deleted papers explicitly listed by number (e.g. action log) if ($this->_allow_deleted) { $this->_add_deleted_papers($qe); } // view and sort information $this->viewmap = $qe->get_float("view", array()); $this->sorters = array(); $this->_add_sorters($qe, null); if ($qe->type === "then") { for ($i = 0; $i < $qe->nthen; ++$i) { $this->_add_sorters($qe->value[$i], $this->thenmap ? $i : null); } } $this->groupmap = []; if ($qe->type === "then") { for ($i = 0; $i < $qe->nthen; ++$i) { $h = $qe->value[$i]->get_float("heading"); $this->groupmap[$i] = (object) ["heading" => $h, "annoFormat" => 0]; } } else { if ($h = $qe->get_float("heading")) { $this->groupmap[0] = (object) ["heading" => $h, "annoFormat" => 0]; } else { if ($order_anno_tag) { $this->_assign_order_anno($order_anno_tag, $tag_order); $this->is_order_anno = $order_anno_tag->tag; } } } // extract regular expressions and set _reviewer if the query is // about exactly one reviewer, and warn about contradictions $contradictions = array(); $this->_queryExtractInfo($qe, true, false, $contradictions); foreach ($contradictions as $contradiction => $garbage) { $this->warn($contradiction); } // set $this->matchPreg from $this->regex if (!$this->overrideMatchPreg) { $this->matchPreg = array(); foreach (array("ti" => "title", "au" => "authorInformation", "ab" => "abstract", "co" => "collaborators") as $k => $v) { if (isset($this->regex[$k]) && count($this->regex[$k])) { $a = $b = array(); foreach ($this->regex[$k] as $x) { $a[] = $x->preg_utf8; if (isset($x->preg_raw)) { $b[] = $x->preg_raw; } } $x = (object) array("preg_utf8" => join("|", $a)); if (count($a) == count($b)) { $x->preg_raw = join("|", $b); } $this->matchPreg[$v] = $x; } } } return true; }
static function taganno_api($user, $qreq, $prow) { global $Conf; $tagger = new Tagger($user); if (!($tag = $tagger->check($qreq->tag, Tagger::NOVALUE))) { json_exit(["ok" => false, "error" => $tagger->error_html]); } $j = ["ok" => true, "tag" => $tag, "editable" => $user->can_change_tag_anno($tag), "anno" => []]; $dt = TagInfo::make_defined_tag($tag); foreach ($dt->order_anno_list() as $oa) { if ($oa->annoId !== null) { $j["anno"][] = TagInfo::unparse_anno_json($oa); } } json_exit($j); }