예제 #1
0
function do_tags()
{
    global $Conf, $Me, $papersel;
    // check tags
    $tagger = new Tagger($Me);
    $t1 = array();
    $errors = array();
    foreach (preg_split('/[\\s,]+/', (string) @$_REQUEST["tag"]) as $t) {
        if ($t === "") {
            /* nada */
        } else {
            if (!($t = $tagger->check($t, Tagger::NOPRIVATE))) {
                $errors[] = $tagger->error_html;
            } else {
                if (TagInfo::base($t) === "pc") {
                    $errors[] = "The “pc” user tag is set automatically for all PC members.";
                } else {
                    $t1[] = $t;
                }
            }
        }
    }
    if (count($errors)) {
        return Conf::msg_error(join("<br>", $errors));
    } else {
        if (!count($t1)) {
            return $Conf->warnMsg("Nothing to do.");
        }
    }
    // modify database
    Dbl::qe("lock tables ContactInfo write");
    Conf::$no_invalidate_caches = true;
    $users = array();
    if ($_REQUEST["tagtype"] === "s") {
        // erase existing tags
        $likes = array();
        $removes = array();
        foreach ($t1 as $t) {
            list($tag, $index) = TagInfo::split_index($t);
            $removes[] = $t;
            $likes[] = "contactTags like " . Dbl::utf8ci("'% " . sqlq_for_like($tag) . "#%'");
        }
        foreach (Dbl::fetch_first_columns(Dbl::qe("select contactId from ContactInfo where " . join(" or ", $likes))) as $cid) {
            $users[(int) $cid] = (object) array("id" => (int) $cid, "add_tags" => [], "remove_tags" => $removes);
        }
    }
    // account for request
    $key = $_REQUEST["tagtype"] === "d" ? "remove_tags" : "add_tags";
    foreach ($papersel as $cid) {
        if (!isset($users[(int) $cid])) {
            $users[(int) $cid] = (object) array("id" => (int) $cid, "add_tags" => [], "remove_tags" => []);
        }
        $users[(int) $cid]->{$key} = array_merge($users[(int) $cid]->{$key}, $t1);
    }
    // apply modifications
    foreach ($users as $cid => $cj) {
        $us = new UserStatus(array("send_email" => false));
        if (!$us->save($cj)) {
            $errors = array_merge($errors, $us->error_messages());
        }
    }
    Dbl::qe("unlock tables");
    Conf::$no_invalidate_caches = false;
    $Conf->invalidateCaches(["pc" => true]);
    // report
    if (!count($errors)) {
        $Conf->confirmMsg("Tags saved.");
        redirectSelf(array("tagact" => null, "tag" => null));
    } else {
        Conf::msg_error(join("<br>", $errors));
    }
}
예제 #2
0
 private function account_votes($pid, $vtag, $state)
 {
     $res = $state->query(array("type" => "tag", "pid" => $pid));
     $tag_re = '{\\A\\d+~' . preg_quote($vtag) . '\\z}i';
     $is_vote = TagInfo::is_vote($vtag);
     $total = 0;
     foreach ($res as $x) {
         if (preg_match($tag_re, $x["ltag"])) {
             $total += $is_vote ? (double) $x["_index"] : 1;
         }
     }
     $state->add(array("type" => "tag", "pid" => $pid, "ltag" => strtolower($vtag), "_tag" => $vtag, "_index" => $total, "_vote" => true));
 }
예제 #3
0
 function pc_members()
 {
     if ($this->_pc_members_cache === null) {
         $pc = $pca = array();
         $result = $this->q("select firstName, lastName, affiliation, email, contactId, roles, contactTags, disabled from ContactInfo where roles!=0 and (roles&" . Contact::ROLE_PCLIKE . ")!=0");
         $by_name_text = $by_first_text = [];
         $this->_pc_tags_cache = ["pc" => "pc"];
         while ($result && ($row = Contact::fetch($result, $this))) {
             $pca[$row->contactId] = $row;
             if ($row->roles & Contact::ROLE_PC) {
                 $pc[$row->contactId] = $row;
             }
             if ($row->firstName || $row->lastName) {
                 $name_text = Text::name_text($row);
                 if (isset($by_name_text[$name_text])) {
                     $row->nameAmbiguous = $by_name_text[$name_text]->nameAmbiguous = true;
                 }
                 $by_name_text[$name_text] = $row;
             }
             if ($row->firstName) {
                 if (isset($by_first_text[$row->firstName])) {
                     $row->firstNameAmbiguous = $by_first_text[$row->firstName]->firstNameAmbiguous = true;
                 }
                 $by_first_text[$row->firstName] = $row;
             }
             if ($row->contactTags) {
                 foreach (explode(" ", $row->contactTags) as $t) {
                     list($tag, $value) = TagInfo::split_index($t);
                     if ($tag) {
                         $this->_pc_tags_cache[strtolower($tag)] = $tag;
                     }
                 }
             }
         }
         Dbl::free($result);
         uasort($pc, "Contact::compare");
         $order = 0;
         foreach ($pc as $row) {
             $row->sort_position = $order;
             ++$order;
         }
         $this->_pc_members_cache = $pc;
         uasort($pca, "Contact::compare");
         $this->_pc_members_and_admins_cache = $pca;
         ksort($this->_pc_tags_cache);
     }
     return $this->_pc_members_cache;
 }
 function crosscheck($sv)
 {
     global $Conf, $Now;
     if ($sv->has_interest("final_open") && $sv->newv("final_open") && ($sv->newv("final_soft") || $sv->newv("final_done")) && (!$sv->newv("final_done") || $sv->newv("final_done") > $Now) && $sv->newv("seedec") != Conf::SEEDEC_ALL) {
         $sv->set_warning(null, "The system is set to collect final versions, but authors cannot submit final versions until they know their papers have been accepted. You may want to update the the “Who can see paper decisions” setting.");
     }
     if ($sv->has_interest("seedec") && $sv->newv("seedec") == Conf::SEEDEC_ALL && $sv->newv("au_seerev") == Conf::AUSEEREV_NO) {
         $sv->set_warning(null, "Authors can see decisions, but not reviews. This is sometimes unintentional.");
     }
     if ($sv->has_interest("au_seerev") && $sv->newv("au_seerev") == Conf::AUSEEREV_TAGS && !$sv->newv("tag_au_seerev") && !$sv->has_error("tag_au_seerev")) {
         $sv->set_warning("tag_au_seerev", "You haven’t set any review visibility tags.");
     }
     if (($sv->has_interest("au_seerev") || $sv->has_interest("tag_chair")) && $sv->newv("au_seerev") == Conf::AUSEEREV_TAGS && $sv->newv("tag_au_seerev") && !$sv->has_error("tag_au_seerev")) {
         foreach (explode(" ", $sv->newv("tag_au_seerev")) as $t) {
             if ($t !== "" && !TagInfo::in_list($t, $sv->newv("tag_chair"))) {
                 $sv->set_warning("tag_au_seerev", "PC members can change the tag “" . htmlspecialchars($t) . "”, which affects whether authors can see reviews. Such tags should usually be <a href=\"" . hoturl("settings", "group=tags") . "\">chair-only</a>.");
                 $sv->set_warning("tag_chair");
             }
         }
     }
 }
예제 #5
0
 public function save($sv, $si)
 {
     global $Conf;
     if ($si->name == "tag_vote" && $sv->has_savedv("tag_vote")) {
         // check allotments
         $pcm = pcMembers();
         foreach (preg_split('/\\s+/', $sv->savedv("tag_vote")) as $t) {
             if ($t === "") {
                 continue;
             }
             $base = substr($t, 0, strpos($t, "#"));
             $allotment = substr($t, strlen($base) + 1);
             $result = Dbl::q("select paperId, tag, tagIndex from PaperTag where tag like '%~" . sqlq_for_like($base) . "'");
             $pvals = array();
             $cvals = array();
             $negative = false;
             while ($row = edb_row($result)) {
                 $who = substr($row[1], 0, strpos($row[1], "~"));
                 if ($row[2] < 0) {
                     $sv->set_error(null, "Removed " . Text::user_html($pcm[$who]) . "’s negative “{$base}” vote for paper #{$row['0']}.");
                     $negative = true;
                 } else {
                     $pvals[$row[0]] = defval($pvals, $row[0], 0) + $row[2];
                     $cvals[$who] = defval($cvals, $who, 0) + $row[2];
                 }
             }
             foreach ($cvals as $who => $what) {
                 if ($what > $allotment) {
                     $sv->set_error("tag_vote", Text::user_html($pcm[$who]) . " already has more than {$allotment} votes for tag “{$base}”.");
                 }
             }
             $q = $negative ? " or (tag like '%~" . sqlq_for_like($base) . "' and tagIndex<0)" : "";
             $Conf->qe("delete from PaperTag where tag='" . sqlq($base) . "'{$q}");
             $q = array();
             foreach ($pvals as $pid => $what) {
                 $q[] = "({$pid}, '" . sqlq($base) . "', {$what})";
             }
             if (count($q) > 0) {
                 $Conf->qe("insert into PaperTag values " . join(", ", $q));
             }
         }
     }
     if ($si->name == "tag_approval" && $sv->has_savedv("tag_approval")) {
         $pcm = pcMembers();
         foreach (preg_split('/\\s+/', $sv->savedv("tag_approval")) as $t) {
             if ($t === "") {
                 continue;
             }
             $result = $Conf->q("select paperId, tag, tagIndex from PaperTag where tag like '%~" . sqlq_for_like($t) . "'");
             $pvals = array();
             $negative = false;
             while ($row = edb_row($result)) {
                 $who = substr($row[1], 0, strpos($row[1], "~"));
                 if ($row[2] < 0) {
                     $sv->set_error(null, "Removed " . Text::user_html($pcm[$who]) . "’s negative “{$t}” approval vote for paper #{$row['0']}.");
                     $negative = true;
                 } else {
                     $pvals[$row[0]] = defval($pvals, $row[0], 0) + 1;
                 }
             }
             $q = $negative ? " or (tag like '%~" . sqlq_for_like($t) . "' and tagIndex<0)" : "";
             $Conf->qe("delete from PaperTag where tag='" . sqlq($t) . "'{$q}");
             $q = array();
             foreach ($pvals as $pid => $what) {
                 $q[] = "({$pid}, '" . sqlq($t) . "', {$what})";
             }
             if (count($q) > 0) {
                 $Conf->qe("insert into PaperTag values " . join(", ", $q));
             }
         }
     }
     TagInfo::invalidate_defined_tags();
 }
예제 #6
0
function pcMembers()
{
    global $Conf, $Opt, $PcMembersCache;
    if (!@$PcMembersCache || $Conf->setting("pc") <= 0 || $PcMembersCache[0] < $Conf->setting("pc") || $PcMembersCache[1] != @$Opt["sortByLastName"]) {
        $pc = array();
        $result = Dbl::q("select firstName, lastName, affiliation, email, contactId, roles, contactTags, disabled from ContactInfo where (roles&" . Contact::ROLE_PC . ")!=0");
        $by_name_text = array();
        $pctags = array("pc" => "pc");
        while ($result && ($row = Contact::fetch($result))) {
            $pc[$row->contactId] = $row;
            if ($row->firstName || $row->lastName) {
                $name_text = Text::name_text($row);
                if (isset($by_name_text[$name_text])) {
                    $row->nameAmbiguous = $by_name_text[$name_text]->nameAmbiguous = true;
                }
                $by_name_text[$name_text] = $row;
            }
            if ($row->contactTags) {
                foreach (explode(" ", $row->contactTags) as $t) {
                    list($tag, $value) = TagInfo::split_index($t);
                    if ($tag) {
                        $pctags[strtolower($tag)] = $tag;
                    }
                }
            }
        }
        uasort($pc, "Contact::compare");
        ksort($pctags);
        $order = 0;
        foreach ($pc as $row) {
            $row->sort_position = $order;
            ++$order;
        }
        $PcMembersCache = array($Conf->setting("pc"), @$Opt["sortByLastName"], $pc, $pctags);
    }
    return $PcMembersCache[2];
}
예제 #7
0
function tags()
{
    global $Conf, $Me;
    // get current tag settings
    $chairtags = "";
    $votetags = "";
    $conflictmsg1 = "";
    $conflictmsg2 = "";
    $conflictmsg3 = "";
    $setting = "";
    if ($Me->isPC) {
        $ct = array_keys(TagInfo::chair_tags());
        if (count($ct)) {
            sort($ct);
            $chairtags = " (currently ";
            foreach ($ct as $c) {
                $chairtags .= "“<a href=\"" . hoturl("search", "q=%23{$c}") . "\">{$c}</a>”, ";
            }
            $chairtags = substr($chairtags, 0, strlen($chairtags) - 2) . ")";
        }
        $votetags = _currentVoteTags();
        if ($Me->privChair) {
            $setting = "  (<a href='" . hoturl("settings", "group=tags") . "'>Change this setting</a>)";
        }
        if ($Conf->setting("tag_seeall") > 0) {
            $conflictmsg3 = "Currently PC members can see tags for any paper, including conflicts.";
        } else {
            $conflictmsg1 = " or conflicted PC members";
            $conflictmsg2 = "  However, since PC members currently can’t see tags for conflicted papers, each PC member might see a different list." . $setting;
            $conflictmsg3 = "They are currently hidden from conflicted PC members&mdash;for instance, if a PC member searches for a tag, the results will never include conflicts.";
        }
    }
    _subhead("", "\n<p>PC members and administrators can attach tag names to papers.\nIt’s easy to add and remove tags and to list all papers with a given tag,\nand <em>ordered</em> tags preserve a particular paper order.\nTags also affect color highlighting in paper lists.</p>\n\n<p>Tags are generally visible to the PC.\nThey are never shown to authors{$conflictmsg1}.\n<em>Twiddle tags</em>, with names like “#~tag”, are visible only\nto their creators.  Tags with two twiddles, such as “#~~tag”, are\nvisible only to PC chairs.</p>");
    _subhead("Finding tags", "\n<p>A paper’s tags are shown like this on the paper page:</p>\n\n<div class='pspcard_container' style='position:static'><div class='pspcard'><div class='pspcard_body'>\n<div class='psc psc1'>\n <div class='pst'>\n  <span class='psfn'>Tags</span>\n  <span class='pstedit'><a class='xx'><span style='display:inline-block;position:relative;width:15px'>" . Ht::img("edit.png", "[Edit]", "bmabs") . "</span>&nbsp;<u class='x'>Edit</u></a></span>\n  <hr class='c' /></div>\n<div class='psv'><div class='taghl'>#earlyaccept</div></div></div>\n</div></div></div><hr class='c' />\n\n<p>To find all papers with tag “#discuss”:&nbsp; " . _searchForm("#discuss") . "</p>\n\n<p>You can also search with “" . _searchLink("show:tags") . "” to see each\npaper’s tags, or “" . _searchLink("show:#tagname") . "” to see a particular tag\nas a column.</p>\n\n<p>Tags are only shown to PC members and administrators.\n{$conflictmsg3}{$setting}\nAdditionally, twiddle tags, which have names like “#~tag”, are\nvisible only to their creators; each PC member has an independent set.\nTags are not case sensitive.</p>");
    _subhead("<a name='changing'>Changing tags</a>", "\n<p>To change a paper’s tags, go to the paper page and select the Tags box’s " . Ht::img("edit.png", "[Edit]") . "&nbsp;Edit\nlink. Then enter one or more alphanumeric tags separated by spaces.</p>\n\n<p>" . Ht::img("extagsset.png", "[Tag entry on review screen]") . "</p>\n\n<p>To set tags from\n<a href='" . hoturl("search") . "'>search</a>, you can select papers and\nuse the action area:</p>\n\n<p>" . Ht::img("extagssearch.png", "[Setting tags on the search page]") . "</p>\n\n<p><b>Add</b> adds tags to the selected papers, <b>Remove</b> removes tags\nfrom the selected papers, and <b>Define</b> adds the tag to the selected\npapers and removes it from all others.  The chair-only <b>Clear twiddle</b>\naction removes a tag and all users’ matching twiddle tags.</p></li>\n\n<p>You can also edit tags directly on the search page. Search for “" . _searchLink("edit:tag:tagname") . "” to\nget a column of checkboxes; checked papers are given the “#tagname” tag.\nSearch for “" . _searchLink("edit:tagval:tagname") . "” to set <a\nhref='#values'>tag values</a>. Or search for “" . _searchLink("edit:tags") . "” to edit papers’ full tag lists.</p>\n\n<p>Finally, the chair may upload tag assignments using the <a href='" . hoturl("bulkassign") . "'>bulk assignment page</a>.</p>\n\n<p>Although any PC member can view or search\nmost tags, certain tags may be changed only by PC chairs{$chairtags}.  {$setting}</p>");
    _subhead("<a id='values'>Tag values and discussion orders</a>", "\n<p>Tags have optional numeric values, which are displayed as\n“#tag#100”. Search for “" . _searchLink("order:tag") . "” to sort tagged\npapers by value. You can also search for specific values with search terms\nlike “" . _searchLink("#discuss#2") . "” or “" . _searchLink("#discuss>1") . "”.</p>\n\n<p>It’s common to assign increasing tag values to a set of papers.  Do this\nusing the <a href='" . hoturl("search") . "'>search screen</a>.  Search for the\npapers you want, sort them into the right order, select their checkboxes, and\nchoose <b>Define order</b> in the tag action area.  If no sort gives what\nyou want, search for the desired paper numbers in order—for instance,\n“" . _searchLink("4 1 12 9") . "”—then <b>Select all</b> and <b>Define\norder</b>. To add new papers at the end of an existing discussion order, use\n<b>Add to order</b>. To insert papers into an existing order, use <b>Add to\norder</b> with a tag value; for example, to insert starting at value 5, use\n<b>Add to order</b> with “#tag#5”.  The rest of the order is renumbered to\naccomodate the insertion.</p>\n\n<p>Even easier, you can <em>drag</em> papers into order using a search like “" . _searchLink("editsort:#tag") . "”.</p>\n\n<p><b>Define order</b> might assign values “#tag#1”,\n“#tag#3”, “#tag#6”, and “#tag#7”\nto adjacent papers.  The gaps make it harder to infer\nconflicted papers’ positions.  (Any given gap might or might not hold a\nconflicted paper.)  The <b>Define gapless order</b> action assigns\nstrictly sequential values, like “#tag#1”,\n“#tag#2”, “#tag#3”, “#tag#4”.\n<b>Define order</b> is better for most purposes.</p>\n\n<p>The <a href=\"" . hoturl("autoassign", "a=discorder") . "\">autoassigner</a>\nhas special support for creating discussion orders. It tries to group papers\nwith similar PC conflicts, which can make the meeting run smoother.</p>");
    _subhead("Tag colors", "\n<p>The tag names “red”, “orange”, “yellow”,\n“green”, “blue”, “purple”, and\n“gray” act as highlight colors. For example, papers tagged with\n“#red” will appear red in paper lists (for people who can see that\ntag).  Tag a paper “#~red” to make it red on your displays, but not\nothers’. Other styles are available; try\n“#bold”, “#italic”, “#big”, “#small”, and “#dim”. System administrators can <a\nhref='" . hoturl("settings", "group=tags") . "'>associate other tags with colors</a>\nso that, for example, “" . _searchLink("#reject") . "” papers show up as\ngray.</p>");
    _subhead("Using tags", "\n<p>Here are some example ways to use tags.</p>\n\n<ul>\n\n<li><strong>Skip low-ranked submissions at the PC meeting.</strong> Mark\nlow-ranked submissions with tag “#nodiscuss”, then ask the PC to " . _searchLink("#nodiscuss", "search for “#nodiscuss”") . " (“" . _searchLink("tag:nodiscuss") . "” also works). PC members can check the list\nfor papers they’d like to discuss anyway. They can email the chairs about\nsuch papers, or, even easier, add a “#discussanyway” tag. (You might make the\n“#nodiscuss” tag chair-only so an evil PC member couldn’t add it to a\nhigh-ranked paper, but it’s usually better to trust the PC.)</li>\n\n<li><strong>Mark controversial papers that would benefit from additional review.</strong>\n PC members could add the “#controversy” tag when the current reviewers disagree.\n A <a href='" . hoturl("search", "q=%23controversy") . "'>search</a> shows where the PC thinks more review is needed.</li>\n\n<li><strong>Mark PC-authored papers for extra scrutiny.</strong>\n First, <a href='" . hoturl("search", "t=s&amp;qt=au") . "'>search for PC members’ last names in author fields</a>.\n Check for accidental matches and select the papers with PC members as authors, then use the action area below the search list to add the tag “#pcpaper”.\n A <a href='" . hoturl("search", "t=s&amp;qx=%23pcpaper") . "'>search</a> shows papers without PC authors.\n (Since PC members can see whether a paper is tagged “#pcpaper”, you may want to delay defining the tag until just before the meeting.)</li>\n\n<li><strong>Vote for papers.</strong>\n The chair can define special voting tags{$votetags}{$setting}.\n Each PC member is assigned an allotment of votes to distribute among papers.\n For instance, if “#v” were a voting tag with an allotment of 10, then a PC member could assign 5 votes to a paper by adding the twiddle tag “#~v#5”.\n The system automatically sums PC members’ votes into the public “#v” tag.\n To search for papers by vote count, search for “<a href='" . hoturl("search", "t=s&amp;q=rorder:v") . "'>rorder:v</a>”. (<a href='" . hoturl("help", "t=votetags") . "'>Learn more</a>)</li>\n\n<li><strong>Rank papers.</strong>\n Each PC member can set tags indicating their preference ranking for papers.\n For instance, a PC member’s favorite paper would get tag “#~rank#1”, the next favorite “#~rank#2”, and so forth.\n The chair can then combine these rankings into a global preference order using a Condorcet method.\n (<a href='" . hoturl("help", "t=ranking") . "'>Learn more</a>)</li>\n\n<li><strong>Define a discussion order for the PC meeting.</strong>\n Publishing the order lets PC members prepare to discuss upcoming papers.\n Define an ordered tag such as “#discuss”, then ask the PC to <a href='" . hoturl("search", "q=order:discuss") . "'>search for “order:discuss”</a>.\n The PC can now see the order and use quick links to go from paper to paper.{$conflictmsg2}</li>\n\n<li><strong>Mark tentative decisions during the PC meeting</strong> either\n using decision selectors or, perhaps, “#accept” and\n “#reject” tags.</li>\n\n</ul>");
}
 public function prepare(PaperList $pl, $visible)
 {
     if (!$pl->contact->can_view_any_peruser_tags($this->tag)) {
         return false;
     }
     if ($visible) {
         $pl->qopts["tags"] = 1;
     }
     $dt = TagInfo::defined_tag($this->tag);
     if (!$dt || $dt->rank || !$dt->vote && !$dt->approval) {
         $this->viewtype = 0;
     } else {
         $this->viewtype = $dt->approval ? 1 : 2;
     }
     return true;
 }
 public function run_discussion_order($tag, $sequential = false)
 {
     global $Conf;
     $this->mcmf_round_descriptor = "";
     $this->mcmf_optimizing_for = "Optimizing assignment";
     // load conflicts
     $cflt = array();
     foreach ($this->papersel as $pid) {
         $cflt[$pid] = array();
     }
     $result = Dbl::qe("select paperId, contactId from PaperConflict where paperId ?a and contactId ?a and conflictType>0", $this->papersel, array_keys($this->pcm));
     while ($row = edb_row($result)) {
         $cflt[(int) $row[0]][] = (int) $row[1];
     }
     Dbl::free($result);
     // run max-flow
     $result = $this->papersel;
     for ($roundno = 0; !$roundno || count($result) > 1; ++$roundno) {
         $this->mcmf_round_descriptor = $roundno ? ", round " . ($roundno + 1) : "";
         $result = $this->run_discussion_order_once($cflt, $result);
         if (!$roundno) {
             $groupmap = array();
             foreach ($result as $i => $pids) {
                 foreach ($pids as $pid) {
                     $groupmap[$pid] = $i;
                 }
             }
         }
     }
     // make assignments
     $this->set_progress("Completing assignment");
     $this->ass = array("paper,action,tag", "# hotcrp_assign_display_search", "# hotcrp_assign_show pcconf", "all,cleartag,{$tag}");
     $curgroup = -1;
     $index = 0;
     $search = array("HEADING:none");
     foreach ($result[0] as $pid) {
         if ($groupmap[$pid] != $curgroup && $curgroup != -1) {
             $search[] = "THEN HEADING:none";
         }
         $curgroup = $groupmap[$pid];
         $index += TagInfo::value_increment($sequential ? "aos" : "ao");
         $this->ass[] = "{$pid},tag,{$tag}#{$index}";
         $search[] = $pid;
     }
     $this->ass[1] = "# hotcrp_assign_display_search " . join(" ", $search);
     //global $Conf; $Conf->echoScript("$('#propass').before(" . json_encode(Ht::pre_text_wrap($m->debug_info(true) . "\n")) . ")");
 }
예제 #10
0
 static function votereport_api($user, $qreq, $prow)
 {
     $tagger = new Tagger($user);
     if (!($tag = $tagger->check($qreq->tag, Tagger::NOVALUE))) {
         json_exit(["ok" => false, "error" => $tagger->error_html]);
     }
     if (!$user->can_view_peruser_tags($prow, $tag)) {
         json_exit(["ok" => false, "error" => "Permission error."]);
     }
     $votemap = [];
     preg_match_all('/ (\\d+)~' . preg_quote($tag) . '#(\\S+)/i', $prow->all_tags_text(), $m);
     $is_approval = TagInfo::is_approval($tag);
     $min_vote = $is_approval ? 0 : 0.001;
     for ($i = 0; $i != count($m[0]); ++$i) {
         if ($m[2][$i] >= $min_vote) {
             $votemap[$m[1][$i]] = $m[2][$i];
         }
     }
     $user->ksort_cid_array($votemap);
     $result = [];
     foreach ($votemap as $k => $v) {
         if ($is_approval) {
             $result[] = $user->reviewer_html_for($k);
         } else {
             $result[] = $user->reviewer_html_for($k) . " ({$v})";
         }
     }
     if (empty($result)) {
         json_exit(["ok" => true, "result" => ""]);
     } else {
         json_exit(["ok" => true, "result" => '<span class="nw">' . join(',</span> <span class="nw">', $result) . '</span>']);
     }
 }
예제 #11
0
 private function _nextRank()
 {
     $this->currank += TagInfo::value_increment($this->ordertype);
     return $this->currank;
 }
 private function _prepare_reviewer_color(Contact $user)
 {
     $this->reviewer_color = array();
     foreach (pcMembers() as $p) {
         $this->reviewer_color[$p->contactId] = TagInfo::color_classes($p->viewable_tags($user));
     }
 }
예제 #13
0
 function table_html($listname, $url, $listtitle = "", $foldsession = null)
 {
     global $Conf, $contactListFields;
     // PC tags
     $listquery = $listname;
     $queryOptions = array();
     if (str_starts_with($listname, "#")) {
         $queryOptions["where"] = "(u.contactTags like " . Dbl::utf8ci("'% " . sqlq_for_like(substr($listname, 1)) . "#%'") . ")";
         $listquery = "pc";
     }
     // get paper list
     if (!($baseFieldId = $this->listFields($listquery))) {
         Conf::msg_error("There is no people list query named “" . htmlspecialchars($listquery) . "”.");
         return null;
     }
     $this->limit = array_shift($baseFieldId);
     // get field array
     $fieldDef = array();
     $acceptable_fields = array();
     $this->any = (object) array("sel" => false);
     $ncol = 0;
     foreach ($baseFieldId as $fid) {
         if ($this->selector($fid, $queryOptions) === false) {
             continue;
         }
         if (!($fieldDef[$fid] = @$contactListFields[$fid])) {
             $fieldDef[$fid] = $contactListFields[self::FIELD_SCORE];
         }
         $acceptable_fields[$fid] = true;
         if ($fieldDef[$fid][1] == 1) {
             $ncol++;
         }
     }
     // run query
     $rows = $this->_rows($queryOptions);
     if (!$rows || count($rows) == 0) {
         return "No matching people";
     }
     // list number
     if ($this->listNumber === true) {
         $this->listNumber = SessionList::allocate("u/" . $this->limit);
         $this->contactLinkArgs .= "&amp;ls=" . $this->listNumber;
     }
     // sort rows
     if (!@$acceptable_fields[$this->sortField]) {
         $this->sortField = null;
     }
     $srows = $this->_sort($rows);
     // count non-callout columns
     $firstcallout = $lastcallout = null;
     $n = 0;
     foreach ($fieldDef as $fieldId => $fdef) {
         if ($fdef[1] == 1) {
             if ($firstcallout === null && $fieldId < self::FIELD_SELECTOR) {
                 $firstcallout = $n;
             }
             if ($fieldId < self::FIELD_SCORE) {
                 $lastcallout = $n + 1;
             }
             ++$n;
         }
     }
     $firstcallout = $firstcallout ? $firstcallout : 0;
     $lastcallout = ($lastcallout ? $lastcallout : $ncol) - $firstcallout;
     // collect row data
     $this->count = 0;
     $show_colors = $this->contact->isPC;
     $anyData = array();
     $body = '';
     $extrainfo = $hascolors = false;
     $ids = array();
     foreach ($srows as $row) {
         if (($this->limit == "resub" || $this->limit == "extsub") && $row->numReviewsSubmitted == 0) {
             continue;
         }
         $trclass = "k" . $this->count % 2;
         if ($show_colors && ($m = $row->viewable_color_classes($this->contact))) {
             if (TagInfo::classes_have_colors($m)) {
                 $trclass = $m;
                 $hascolors = true;
             } else {
                 $trclass .= " {$m}";
             }
         }
         if ($row->disabled && $this->contact->isPC) {
             $trclass .= " graytext";
         }
         $this->count++;
         $ids[] = (int) $row->contactId;
         // First create the expanded callout row
         $tt = "";
         foreach ($fieldDef as $fieldId => $fdef) {
             if ($fdef[1] >= 2 && ($d = $this->content($fieldId, $row)) !== "") {
                 $tt .= "<div";
                 //$t .= "  <tr class=\"pl_$fdef[0] pl_callout $trclass";
                 if ($fdef[1] >= 3) {
                     $tt .= " class=\"fx" . ($fdef[1] - 2) . "\"";
                 }
                 $tt .= '><em class="plx">' . $this->header($fieldId, -1, $row) . ":</em> " . $d . "</div>";
             }
         }
         if ($tt !== "") {
             $x = "  <tr class=\"plx {$trclass}\">";
             if ($firstcallout > 0) {
                 $x .= "<td colspan=\"{$firstcallout}\"></td>";
             }
             $tt = $x . "<td class=\"plx\" colspan=\"" . ($lastcallout - $firstcallout) . "\">" . $tt . "</td></tr>\n";
         }
         // Now the normal row
         $t = "  <tr class=\"pl {$trclass}\">\n";
         $n = 0;
         foreach ($fieldDef as $fieldId => $fdef) {
             if ($fdef[1] == 1) {
                 $c = $this->content($fieldId, $row);
                 $t .= "    <td class=\"pl pl_{$fdef['0']}\"";
                 if ($n >= $lastcallout && $tt != "") {
                     $t .= " rowspan=\"2\"";
                 }
                 $t .= ">" . $c . "</td>\n";
                 if ($c != "") {
                     $anyData[$fieldId] = 1;
                 }
                 ++$n;
             }
         }
         $t .= "  </tr>\n";
         $body .= $t . $tt;
     }
     $foldclasses = array();
     foreach (self::$folds as $k => $fold) {
         if (@$this->have_folds[$fold] !== null) {
             $this->have_folds[$fold] = strpos(displayOptionsSet("uldisplay"), " {$fold} ") !== false;
             $foldclasses[] = "fold" . ($k + 1) . ($this->have_folds[$fold] ? "o" : "c");
         }
     }
     $x = "<table id=\"foldul\" class=\"pltable pltable_full plt_" . htmlspecialchars($listquery);
     if ($foldclasses) {
         $x .= " " . join(" ", $foldclasses);
     }
     if ($foldclasses && $foldsession) {
         $x .= "\" data-fold-session=\"{$foldsession}";
     }
     $x .= "\">\n";
     if ($this->showHeader) {
         $x .= "  <thead class=\"pltable\">\n  <tr class=\"pl_headrow\">\n";
         $ord = 0;
         if ($this->sortable && $url) {
             $sortUrl = htmlspecialchars($url) . (strpos($url, "?") ? "&amp;" : "?") . "sort=";
             $q = '<a class="pl_sort" rel="nofollow" href="' . $sortUrl;
             foreach ($fieldDef as $fieldId => $fdef) {
                 if ($fdef[1] != 1) {
                     continue;
                 } else {
                     if (!isset($anyData[$fieldId])) {
                         $x .= "    <th class=\"pl pl_{$fdef['0']}\"></th>\n";
                         continue;
                     }
                 }
                 $x .= "    <th class=\"pl pl_{$fdef['0']}\">";
                 $ftext = $this->header($fieldId, $ord++);
                 if ($this->sortField == null && $fieldId == 1) {
                     $this->sortField = $fieldId;
                 }
                 if ($fieldId == $this->sortField) {
                     $x .= '<a class="pl_sort_def' . ($this->reverseSort ? "_rev" : "") . '" rel="nofollow" href="' . $sortUrl . $fieldId . ($this->reverseSort ? "N" : "R") . '">' . $ftext . "</a>";
                 } else {
                     if ($fdef[2]) {
                         $x .= $q . $fieldId . "\">" . $ftext . "</a>";
                     } else {
                         $x .= $ftext;
                     }
                 }
                 $x .= "</th>\n";
             }
         } else {
             foreach ($fieldDef as $fieldId => $fdef) {
                 if ($fdef[1] == 1 && isset($anyData[$fieldId])) {
                     $x .= "    <th class=\"pl pl_{$fdef['0']}\">" . $this->header($fieldId, $ord++) . "</th>\n";
                 } else {
                     if ($fdef[1] == 1) {
                         $x .= "    <th class=\"pl pl_{$fdef['0']}\"></th>\n";
                     }
                 }
             }
         }
         $x .= "  </tr></thead>\n";
     }
     reset($fieldDef);
     if (key($fieldDef) == self::FIELD_SELECTOR) {
         $x .= $this->footer($ncol, $hascolors);
     }
     $x .= "<tbody class=\"pltable" . ($hascolors ? " pltable_colored" : "") . "\">" . $body . "</tbody></table>";
     if ($this->listNumber) {
         $l = SessionList::create("u/" . $listname, $ids, $listtitle ? $listtitle : "Users", hoturl_site_relative_raw("users", ["t" => $listname]));
         SessionList::change($this->listNumber, $l);
     }
     return $x;
 }
예제 #14
0
 private function normalize($cj, $old_user)
 {
     // Errors prevent saving
     global $Conf, $Me, $Now;
     // Canonicalize keys
     foreach (array("preferredEmail" => "preferred_email", "institution" => "affiliation", "voicePhoneNumber" => "phone", "addressLine1" => "address", "zipCode" => "zip", "postal_code" => "zip") as $x => $y) {
         if (isset($cj->{$x}) && !isset($cj->{$y})) {
             $cj->{$y} = $cj->{$x};
         }
     }
     // Stringiness
     foreach (array("firstName", "lastName", "email", "preferred_email", "affiliation", "phone", "old_password", "new_password", "city", "state", "zip", "country") as $k) {
         if (isset($cj->{$k}) && !is_string($cj->{$k})) {
             $this->set_error($k, "Format error [{$k}]");
             unset($cj->{$k});
         }
     }
     // Email
     if (!get($cj, "email") && $old_user) {
         $cj->email = $old_user->email;
     } else {
         if (!get($cj, "email")) {
             $this->set_error("email", "Email is required.");
         } else {
             if (!isset($this->errf["email"]) && !validate_email($cj->email) && (!$old_user || $old_user->email !== $cj->email)) {
                 $this->set_error("email", "Invalid email address “" . htmlspecialchars($cj->email) . "”.");
             }
         }
     }
     // ID
     if (get($cj, "id") === "new") {
         if (get($cj, "email") && Contact::id_by_email($cj->email)) {
             $this->set_error("email", "Email address “" . htmlspecialchars($cj->email) . "” is already in use.");
             $this->errf["email_inuse"] = true;
         }
     } else {
         if (!get($cj, "id") && $old_user && $old_user->contactId) {
             $cj->id = $old_user->contactId;
         }
         if (get($cj, "id") && !is_int($cj->id)) {
             $this->set_error("id", "Format error [id]");
         }
         if ($old_user && get($cj, "email") && strtolower($old_user->email) !== strtolower($cj->email) && Contact::id_by_email($cj->email)) {
             $this->set_error("email", "Email address “" . htmlspecialchars($cj->email) . "” is already in use. You may want to <a href=\"" . hoturl("mergeaccounts") . "\">merge these accounts</a>.");
         }
     }
     // Contactdb information
     if ($old_user && !$old_user->contactId) {
         if (!isset($cj->firstName) && !isset($cj->lastName)) {
             $cj->firstName = $old_user->firstName;
             $cj->lastName = $old_user->lastName;
         }
         if (!isset($cj->affiliation)) {
             $cj->affiliation = $old_user->affiliation;
         }
         if (!isset($cj->collaborators)) {
             $cj->collaborators = $old_user->collaborators;
         }
     }
     // Preferred email
     if (get($cj, "preferred_email") && !isset($this->errf["preferred_email"]) && !validate_email($cj->preferred_email) && (!$old_user || $old_user->preferredEmail !== $cj->preferred_email)) {
         $this->set_error("preferred_email", "Invalid email address “" . htmlspecialchars($cj->preferred_email) . "”");
     }
     // Address
     $address = array();
     if (is_array(get($cj, "address"))) {
         $address = $cj->address;
     } else {
         if (is_string(get($cj, "address"))) {
             $address[] = $cj->address;
         } else {
             if (get($cj, "address")) {
                 $this->set_error("address", "Format error [address]");
             }
         }
         if (is_string(get($cj, "address2"))) {
             $address[] = $cj->address2;
         } else {
             if (is_string(get($cj, "addressLine2"))) {
                 $address[] = $cj->addressLine2;
             } else {
                 if (get($cj, "address2") || get($cj, "addressLine2")) {
                     $this->set_error("address2", "Format error [address2]");
                 }
             }
         }
     }
     foreach ($address as $a) {
         if (!is_string($a)) {
             $this->set_error("address", "Format error [address]");
         }
     }
     if (count($address)) {
         $cj->address = $address;
     }
     // Collaborators
     if (is_array(get($cj, "collaborators"))) {
         foreach ($cj->collaborators as $c) {
             if (!is_string($c)) {
                 $this->set_error("collaborators", "Format error [collaborators]");
             }
         }
     }
     if (is_array(get($cj, "collaborators")) && !isset($this->errf["collaborators"])) {
         $cj->collaborators = join("\n", $cj->collaborators);
     }
     if (get($cj, "collaborators") && !is_string($cj->collaborators) && !isset($this->errf["collaborators"])) {
         $this->set_error("collaborators", "Format error [collaborators]");
     }
     // Disabled
     if (isset($cj->disabled)) {
         if (($x = friendly_boolean($cj->disabled)) !== null) {
             $cj->disabled = $x;
         } else {
             $this->set_error("disabled", "Format error [disabled]");
         }
     }
     // Follow
     if (isset($cj->follow)) {
         $cj->follow = $this->make_keyed_object($cj->follow, "follow");
         $cj->bad_follow = array();
         foreach ((array) $cj->follow as $k => $v) {
             if ($v && $k !== "reviews" && $k !== "allreviews" && $k !== "allfinal") {
                 $cj->bad_follow[] = $k;
             }
         }
     }
     // Roles
     if (isset($cj->roles)) {
         $cj->roles = $this->make_keyed_object($cj->roles, "roles");
         $cj->bad_roles = array();
         foreach ((array) $cj->roles as $k => $v) {
             if ($v && $k !== "pc" && $k !== "chair" && $k !== "sysadmin" && $k !== "no") {
                 $cj->bad_roles[] = $k;
             }
         }
         if ($this->no_deprivilege_self && $Me && $old_user && $old_user->contactId == $Me->contactId && Contact::parse_roles_json($cj->roles) < $Me->roles) {
             unset($cj->roles);
             $this->set_warning("roles", "Ignoring request to drop your privileges.");
         }
     }
     // Tags
     if (isset($cj->tags)) {
         $cj->tags = $this->make_tags_array($cj->tags, "tags");
     }
     if (isset($cj->add_tags) || isset($cj->remove_tags)) {
         // collect old tags as map by base
         if (!isset($cj->tags) && $old_user) {
             $cj->tags = preg_split("/[\\s,]+/", $old_user->contactTags);
         } else {
             if (!isset($cj->tags)) {
                 $cj->tags = array();
             }
         }
         $old_tags = array();
         foreach ($cj->tags as $t) {
             if ($t !== "") {
                 list($tag, $index) = TagInfo::split_index($t);
                 $old_tags[$tag] = $index;
             }
         }
         // process removals, then additions
         foreach ($this->make_tags_array(get($cj, "remove_tags"), "remove_tags") as $t) {
             list($tag, $index) = TagInfo::split_index($t);
             if ($index === false || get($old_tags, $tag) == $index) {
                 unset($old_tags[$tag]);
             }
         }
         foreach ($this->make_tags_array($cj->add_tags, "add_tags") as $t) {
             list($tag, $index) = TagInfo::split_index($t);
             $old_tags[$tag] = $index;
         }
         // collect results
         $cj->tags = array();
         foreach ($old_tags as $tag => $index) {
             $cj->tags[] = $tag . "#" . (double) $index;
         }
     }
     // Topics
     if (isset($cj->topics)) {
         $topics = $this->make_keyed_object($cj->topics, "topics");
         $topic_map = $Conf->topic_map();
         $cj->topics = (object) array();
         $cj->bad_topics = array();
         foreach ((array) $topics as $k => $v) {
             if (get($topic_map, $k)) {
                 /* OK */
             } else {
                 if (($x = array_search($k, $topic_map, true)) !== false) {
                     $k = $x;
                 } else {
                     $cj->bad_topics[] = $k;
                     continue;
                 }
             }
             if ($v === "mlow" || $v === "medium-low") {
                 $v = -1;
             } else {
                 if ($v === true || $v === "mhigh" || $v === "medium-high") {
                     $v = 2;
                 } else {
                     if ($v === "low") {
                         $v = -2;
                     } else {
                         if ($v === "high") {
                             $v = 4;
                         } else {
                             if ($v === "medium" || $v === "none" || $v === false) {
                                 $v = 0;
                             } else {
                                 if (is_numeric($v)) {
                                     $v = (int) $v;
                                 } else {
                                     $this->set_error("topics", "Topic interest format error");
                                     continue;
                                 }
                             }
                         }
                     }
                 }
             }
             $k = (string) $k;
             $cj->topics->{$k} = $v;
         }
     }
 }
예제 #15
0
 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;
 }
예제 #16
0
        Dbl::qe("update Paper set timeWithdrawn={$Now}, timeSubmitted=if(timeSubmitted>0,-100,0), withdrawReason=? where paperId={$prow->paperId}", $reason != "" ? $reason : null);
        $numreviews = Dbl::fetch_ivalue("select count(*) from PaperReview where paperId={$prow->paperId} and reviewNeedsSubmit!=0");
        $Conf->update_papersub_setting(false);
        loadRows();
        // email contact authors themselves
        if (!$Me->privChair || defval($_REQUEST, "doemail") > 0) {
            HotCRPMailer::send_contacts($prow->conflictType >= CONFLICT_AUTHOR ? "@authorwithdraw" : "@adminwithdraw", $prow, array("reason" => $reason, "infoNames" => 1));
        }
        // email reviewers
        if ($numreviews > 0 && $Conf->time_review_open() || $prow->num_reviews_assigned() > 0) {
            HotCRPMailer::send_reviewers("@withdrawreviewer", $prow, array("reason" => $reason));
        }
        // remove voting tags so people don't have phantom votes
        if (TagInfo::has_vote()) {
            $q = array();
            foreach (TagInfo::vote_tags() as $t => $v) {
                $q[] = "tag='" . sqlq($t) . "' or tag like '%~" . sqlq_for_like($t) . "'";
            }
            Dbl::qe_raw("delete from PaperTag where paperId={$prow->paperId} and (" . join(" or ", $q) . ")");
        }
        $Me->log_activity("Withdrew", $prow->paperId);
        redirectSelf();
    } else {
        Conf::msg_error(whyNotText($whyNot, "withdraw"));
    }
}
if (isset($_REQUEST["revive"]) && !$newPaper && check_post()) {
    if (!($whyNot = $Me->perm_revive_paper($prow))) {
        Dbl::qe("update Paper set timeWithdrawn=0, timeSubmitted=if(timeSubmitted=-100,{$Now},0), withdrawReason=null where paperId={$prow->paperId}");
        $Conf->update_papersub_setting(true);
        loadRows();
예제 #17
0
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") . "&nbsp;<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}&amp;m=edit") . '" class="xx">' . Ht::img("edit24.png", "[Edit paper]", "dlimg") . "&nbsp;<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") . "&nbsp;<u><b>Edit your review</b></u></a>";
            } else {
                $x = $a . Ht::img("review24.png", "[Your review]", "dlimg") . "&nbsp;<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}&amp;m=re") . '" class="xx">' . Ht::img("review24.png", "[Write review]", "dlimg") . "&nbsp;<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") . "&nbsp;<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") . "&nbsp;<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") . "&nbsp;" . ($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") . "&nbsp;<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;
    }
}
예제 #18
0
 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);
 }
예제 #19
0
 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;
 }
예제 #20
0
 function can_change_tag_anno($tag, $forceShow = null)
 {
     $twiddle = strpos($tag, "~");
     return $this->privChair || $this->isPC && !TagInfo::is_chair($tag) && ($twiddle === false || $twiddle === 0 && $tag[1] !== "~" || $twiddle > 0 && substr($tag, 0, $twiddle) == $this->contactId);
 }
예제 #21
0
 function run(Contact $user, $qreq, $ssel)
 {
     global $Conf;
     $papers = $ssel->selection();
     $act = $qreq->tagfn;
     $tagreq = trim(str_replace(",", " ", (string) $qreq->tag));
     $tags = preg_split('/\\s+/', $tagreq);
     if ($act == "da") {
         $otags = $tags;
         foreach ($otags as $t) {
             $tags[] = "all~" . preg_replace(',\\A.*~([^~]+)\\z', '$1', $t);
         }
         $act = "d";
     } else {
         if ($act == "sor") {
             shuffle($papers);
         }
     }
     $x = array("action,paper,tag\n");
     if ($act == "s" || $act == "so" || $act == "sos" || $act == "sor") {
         foreach ($tags as $t) {
             $x[] = "cleartag,all," . TagInfo::base($t) . "\n";
         }
     }
     if ($act == "s" || $act == "a") {
         $action = "tag";
     } else {
         if ($act == "d") {
             $action = "cleartag";
         } else {
             if ($act == "so" || $act == "sor" || $act == "ao") {
                 $action = "nexttag";
             } else {
                 if ($act == "sos" || $act == "aos") {
                     $action = "seqnexttag";
                 } else {
                     $action = null;
                 }
             }
         }
     }
     $assignset = new AssignmentSet($user, $user->privChair);
     if (count($papers) && $action) {
         foreach ($papers as $p) {
             foreach ($tags as $t) {
                 $x[] = "{$action},{$p},{$t}\n";
             }
         }
         $assignset->parse(join("", $x));
     } else {
         if (count($papers) && $act == "cr" && $user->privChair) {
             $source_tag = trim((string) $qreq->tagcr_source);
             if ($source_tag == "") {
                 $source_tag = substr($tagreq, 0, 2) == "~~" ? substr($tagreq, 2) : $tagreq;
             }
             $tagger = new Tagger($user);
             if ($tagger->check($tagreq, Tagger::NOPRIVATE | Tagger::NOVALUE) && $tagger->check($source_tag, Tagger::NOPRIVATE | Tagger::NOCHAIR | Tagger::NOVALUE)) {
                 $r = new PaperRank($source_tag, $tagreq, $papers, $qreq->tagcr_gapless, "Search", "search");
                 $r->run($qreq->tagcr_method);
                 $r->apply($assignset);
                 $assignset->finish();
                 if ($qreq->q === "") {
                     $qreq->q = "order:{$tagreq}";
                 }
             } else {
                 $assignset->error($tagger->error_html);
             }
         }
     }
     if ($errors = join("<br />\n", $assignset->errors_html())) {
         if ($assignset->has_assigners()) {
             Conf::msg_warning("Some tag assignments were ignored:<br />\n{$errors}");
             $assignset->clear_errors();
         } else {
             Conf::msg_error($errors);
         }
     }
     $success = $assignset->execute();
     if (!$Conf->headerPrinted && $qreq->ajax) {
         $Conf->ajaxExit(array("ok" => $success));
     } else {
         if (!$Conf->headerPrinted && $success) {
             if (!$errors) {
                 $Conf->confirmMsg("Tags saved.");
             }
             $args = array("atab" => "tag");
             foreach (array("tag", "tagfn", "tagcr_method", "tagcr_source", "tagcr_gapless") as $arg) {
                 if (isset($qreq[$arg])) {
                     $args[$arg] = $qreq[$arg];
                 }
             }
             redirectSelf($args);
         }
     }
 }
예제 #22
0
 private function _row_text($rstate, $row, $fieldDef)
 {
     global $Conf;
     $rowidx = count($rstate->ids);
     $rstate->ids[] = (int) $row->paperId;
     $trclass = "k" . $rstate->colorindex;
     if (get($row, "paperTags") && ($viewable = $row->viewable_tags($this->contact, true)) && ($m = TagInfo::color_classes($viewable))) {
         if (TagInfo::classes_have_colors($m)) {
             $rstate->hascolors = true;
             $trclass = $m;
         } else {
             $trclass .= " " . $m;
         }
         if ($row->conflictType > 0 && !$this->contact->can_view_tags($row, false)) {
             $trclass .= " conflictmark";
         }
     }
     if ($highlightclass = get($this->search->highlightmap, $row->paperId)) {
         $trclass .= " {$highlightclass}mark";
     }
     $rstate->colorindex = 1 - $rstate->colorindex;
     $rstate->last_trclass = $trclass;
     $this->row_attr = [];
     // main columns
     $tm = "";
     foreach ($fieldDef as $fdef) {
         if ($fdef->view != Column::VIEW_COLUMN) {
             continue;
         }
         $empty = $fdef->content_empty($this, $row);
         if ($fdef->is_folded) {
             if (!$empty) {
                 $fdef->has_content = true;
             }
         } else {
             $tm .= "<td class=\"pl " . $fdef->className;
             if ($fdef->foldable) {
                 $tm .= " fx{$fdef->foldable}";
             }
             $tm .= "\">";
             if (!$empty && ($c = $fdef->content($this, $row, $rowidx)) !== "") {
                 $tm .= $c;
                 $fdef->has_content = true;
             }
             $tm .= "</td>";
         }
     }
     // extension columns
     $tt = "";
     foreach ($fieldDef as $fdef) {
         if ($fdef->view != Column::VIEW_ROW) {
             continue;
         }
         $empty = $fdef->content_empty($this, $row);
         $is_authors = $fdef->name === "authors";
         if ($fdef->is_folded && !$is_authors) {
             if (!$empty) {
                 $fdef->has_content = true;
             }
         } else {
             $tt .= "<div class=\"" . $fdef->className;
             if ($is_authors) {
                 $tt .= " fx1";
                 if ($this->contact->can_view_authors($row, false)) {
                     $rstate->has_openau = true;
                 } else {
                     $tt .= " fx2";
                     $rstate->has_anonau = true;
                 }
             } else {
                 if ($fdef->foldable) {
                     $tt .= " fx" . $fdef->foldable;
                 }
             }
             $tt .= "\">";
             if (!$empty && ($c = $fdef->content($this, $row, $rowidx)) !== "") {
                 if ($c[0] !== "<" || !preg_match('/\\A((?:<(?:div|p).*?>)*)([\\s\\S]*)\\z/', $c, $cm)) {
                     $cm = [null, "", $c];
                 }
                 $tt .= $cm[1] . '<em class="plx">' . $fdef->header($this, -1) . ':</em> ' . $cm[2];
                 $fdef->has_content = true;
             }
             $tt .= "</div>";
         }
     }
     if (isset($row->folded) && $row->folded) {
         $trclass .= " fx3";
         $rstate->row_folded = true;
     } else {
         if ($rstate->row_folded) {
             $rstate->row_folded = false;
         }
     }
     $t = "  <tr";
     if ($this->_row_id_pattern) {
         $t .= " id=\"" . str_replace("#", $row->paperId, $this->_row_id_pattern) . "\"";
     }
     $t .= " class=\"pl {$trclass}\" data-pid=\"{$row->paperId}";
     foreach ($this->row_attr as $k => $v) {
         $t .= "\" {$k}=\"" . htmlspecialchars($v);
     }
     $t .= "\">" . $tm . "</tr>\n";
     if ($tt !== "") {
         $t .= "  <tr class=\"plx {$trclass}\" data-pid=\"{$row->paperId}\">";
         if ($rstate->skipcallout > 0) {
             $t .= "<td colspan=\"{$rstate->skipcallout}\"></td>";
         }
         $t .= "<td class=\"plx\" colspan=\"" . ($rstate->ncol - $rstate->skipcallout) . "\">{$tt}</td></tr>\n";
     }
     return $t;
 }
예제 #23
0
 public function unparse_and_link($viewable, $alltags, $highlight = false)
 {
     $vtags = $this->unparse($viewable);
     if ($vtags === "") {
         return "";
     }
     // decorate with URL matches
     $tt = "";
     // clean $highlight
     $byhighlight = array();
     $anyhighlight = false;
     assert($highlight === false || is_array($highlight));
     if ($highlight) {
         foreach ($highlight as $h) {
             $tag = is_object($h) ? $h->tag : $h;
             if (($pos = strpos($tag, "~")) !== false) {
                 $tag = substr($tag, $pos);
             }
             $byhighlight[strtolower($tag)] = "";
         }
     }
     foreach (preg_split('/\\s+/', $vtags) as $tag) {
         if (!($base = TagInfo::base($tag))) {
             continue;
         }
         $lbase = strtolower($base);
         if ($link = $this->link($tag)) {
             $tx = '<a class="qq nw" href="' . $link . '">#' . $base . '</a>';
         } else {
             $tx = "#" . $base;
         }
         $tx .= substr($tag, strlen($base));
         if (isset($byhighlight[$lbase])) {
             $byhighlight[$lbase] .= "<strong>" . $tx . "</strong> ";
             $anyhighlight = true;
         } else {
             $tt .= $tx . " ";
         }
     }
     if ($anyhighlight) {
         return rtrim(join("", $byhighlight) . $tt);
     } else {
         return rtrim($tt);
     }
 }