1<?php 2// helpers.php -- HotCRP non-class helper functions 3// Copyright (c) 2006-2018 Eddie Kohler; see LICENSE. 4 5function defappend(&$var, $str) { 6 if (!isset($var)) 7 $var = ""; 8 $var .= $str; 9} 10 11function arrayappend(&$var, $value) { 12 if (isset($var)) 13 $var[] = $value; 14 else 15 $var = array($value); 16} 17 18function mkarray($value) { 19 if (is_array($value)) 20 return $value; 21 else 22 return array($value); 23} 24 25 26// string helpers 27 28function cvtint($value, $default = -1) { 29 $v = trim((string) $value); 30 if (is_numeric($v)) { 31 $ival = intval($v); 32 if ($ival == floatval($v)) 33 return $ival; 34 } 35 return $default; 36} 37 38function cvtnum($value, $default = -1) { 39 $v = trim((string) $value); 40 if (is_numeric($v)) 41 return floatval($v); 42 return $default; 43} 44 45 46// web helpers 47 48function hoturl_add_raw($url, $component) { 49 if (($pos = strpos($url, "#")) !== false) { 50 $component .= substr($url, $pos); 51 $url = substr($url, 0, $pos); 52 } 53 return $url . (strpos($url, "?") === false ? "?" : "&") . $component; 54} 55 56function hoturl_defaults($options = array()) { 57 foreach ($options as $k => $v) 58 if ($v !== null) 59 Conf::$hoturl_defaults[$k] = urlencode($v); 60 else 61 unset(Conf::$hoturl_defaults[$k]); 62 $ret = array(); 63 if (Conf::$hoturl_defaults) 64 foreach (Conf::$hoturl_defaults as $k => $v) 65 $ret[$k] = urldecode($v); 66 return $ret; 67} 68 69function hoturl_site_relative($page, $options = null) { 70 global $Conf; 71 return $Conf->hoturl($page, $options, Conf::HOTURL_SITE_RELATIVE); 72} 73 74function hoturl($page, $options = null) { 75 global $Conf; 76 return $Conf->hoturl($page, $options); 77} 78 79function hoturl_post($page, $options = null) { 80 global $Conf; 81 return $Conf->hoturl($page, $options, Conf::HOTURL_POST); 82} 83 84function hoturl_absolute($page, $options = null) { 85 global $Conf; 86 return $Conf->hoturl($page, $options, Conf::HOTURL_ABSOLUTE); 87} 88 89function hoturl_absolute_nodefaults($page, $options = null) { 90 global $Conf; 91 return $Conf->hoturl($page, $options, Conf::HOTURL_ABSOLUTE | Conf::HOTURL_NO_DEFAULTS); 92} 93 94function hoturl_site_relative_raw($page, $options = null) { 95 return htmlspecialchars_decode(hoturl_site_relative($page, $options)); 96} 97 98function hoturl_raw($page, $options = null) { 99 return htmlspecialchars_decode(hoturl($page, $options)); 100} 101 102function hoturl_post_raw($page, $options = null) { 103 return htmlspecialchars_decode(hoturl_post($page, $options)); 104} 105 106function hoturl_absolute_raw($page, $options = null) { 107 return htmlspecialchars_decode(hoturl_absolute($page, $options)); 108} 109 110 111class SelfHref { 112 static private $argmap = null; 113 static private function set_argmap() { 114 self::$argmap = [ 115 "p" => true, "paperId" => "p", "pap" => "p", 116 "r" => true, "reviewId" => "r", 117 "c" => true, "commentId" => "c", 118 "m" => true, "mode" => true, 119 "u" => true, 120 "g" => true, 121 "q" => true, "t" => true, "qa" => true, "qo" => true, "qx" => true, "qt" => true, 122 "fx" => true, "fy" => true, 123 "forceShow" => true, "ls" => true, 124 "tab" => true, "atab" => true, "sort" => true, 125 "group" => true, "monreq" => true, "noedit" => true, 126 "contact" => true, "reviewer" => true, 127 "editcomment" => true 128 ]; 129 } 130 static function make(Qrequest $qreq = null, $params = [], $options = null) { 131 global $Qreq; 132 $qreq = $qreq ? : $Qreq; 133 if (self::$argmap === null) 134 self::set_argmap(); 135 136 $x = []; 137 foreach ($qreq->make_array() as $k => $v) { 138 $ak = get(self::$argmap, $k); 139 if ($ak === true) 140 $ak = $k; 141 if ($ak 142 && ($ak === $k || !isset($qreq[$ak])) 143 && !array_key_exists($ak, $params) 144 && !is_array($v)) 145 $x[$ak] = $v; 146 } 147 foreach ($params as $k => $v) 148 if ($v !== null) 149 $x[$k] = $v; 150 151 $page = Navigation::page(); 152 if ($options && get($options, "site_relative")) { 153 if (get($options, "raw")) 154 return hoturl_site_relative_raw($page, $x); 155 else 156 return hoturl_site_relative($page, $x); 157 } else if ($options && get($options, "raw")) 158 return hoturl_raw($page, $x); 159 else 160 return hoturl($page, $x); 161 } 162 static function redirect(Qrequest $qreq = null, $params = []) { 163 Navigation::redirect(self::make($qreq, $params, ["raw" => true])); 164 } 165} 166 167function selfHref($params = [], $options = null) { 168 return SelfHref::make(null, $params, $options); 169} 170 171class JsonResult { 172 public $status; 173 public $content; 174 public $has_messages = false; 175 176 function __construct($values = null) { 177 if (is_int($values)) { 178 $this->status = $values; 179 if (func_num_args() === 2) 180 $values = func_get_arg(1); 181 else 182 $values = null; 183 } 184 if ($values === true || $values === false) 185 $this->content = ["ok" => $values]; 186 else if ($values === null) 187 $this->content = []; 188 else if (is_object($values)) { 189 assert(!($values instanceof JsonResult)); 190 $this->content = (array) $values; 191 } else if (is_string($values)) { 192 if ($this->status && $this->status > 299) 193 $this->content = ["ok" => false, "error" => $values]; 194 else 195 $this->content = ["ok" => true, "response" => $values]; 196 } else 197 $this->content = $values; 198 } 199 static function make($json, Conf $conf = null, $arg2 = null) { 200 if (is_int($json)) 201 $json = new JsonResult($json, $arg2); 202 else if (!is_object($json) || !($json instanceof JsonResult)) 203 $json = new JsonResult($json); 204 if (!$json->has_messages && $conf) 205 $json->transfer_messages($conf); 206 return $json; 207 } 208 function transfer_messages(Conf $conf, $div = false) { 209 if (session_id() !== "" 210 && ($msgs = $conf->session("msgs", []))) { 211 $conf->save_session("msgs", null); 212 $t = ""; 213 foreach ($msgs as $msg) { 214 if (($msg[0] === "merror" || $msg[0] === "xmerror") 215 && !isset($this->content["error"])) 216 $this->content["error"] = $msg[1]; 217 if ($div) 218 $t .= Ht::xmsg($msg[0], $msg[1]); 219 else 220 $t .= "<span class=\"$msg[0]\">$msg[1]</span>"; 221 } 222 if ($t !== "") 223 $this->content["response"] = $t . get_s($this->content, "response"); 224 $this->has_messages = true; 225 } 226 } 227} 228 229class JsonResultException extends Exception { 230 public $result; 231 static public $capturing = false; 232 function __construct($j) { 233 $this->result = $j; 234 } 235} 236 237function json_exit($json, $arg2 = null) { 238 global $Conf, $Qreq; 239 $json = JsonResult::make($json, $Conf, $arg2); 240 if (JsonResultException::$capturing) 241 throw new JsonResultException($json); 242 else { 243 if ($json->status) 244 http_response_code($json->status); 245 if (isset($_GET["text"]) && $_GET["text"]) 246 header("Content-Type: text/plain; charset=utf-8"); 247 else 248 header("Content-Type: application/json; charset=utf-8"); 249 if ($Qreq && $Qreq->post_ok()) 250 header("Access-Control-Allow-Origin: *"); 251 echo json_encode_browser($json->content); 252 exit; 253 } 254} 255 256function csv_exit(CsvGenerator $csv) { 257 $csv->download_headers(); 258 $csv->download(); 259 exit; 260} 261 262function foldupbutton($foldnum = 0, $content = "", $js = null) { 263 if ($foldnum) 264 $js["data-fold-target"] = $foldnum; 265 $js["class"] = "ui q js-foldup"; 266 return Ht::link(expander(null, $foldnum) . $content, "#", $js); 267} 268 269function expander($open, $foldnum = null) { 270 $f = $foldnum !== null; 271 $foldnum = ($foldnum !== 0 ? $foldnum : ""); 272 $t = '<span class="expander">'; 273 if ($open === null || !$open) 274 $t .= '<span class="in0' . ($f ? " fx$foldnum" : "") . '">' . Icons::ui_triangle(2) . '</span>'; 275 if ($open === null || $open) 276 $t .= '<span class="in1' . ($f ? " fn$foldnum" : "") . '">' . Icons::ui_triangle(1) . '</span>'; 277 return $t . '</span>'; 278} 279 280function actas_link($cid, $contact = null) { 281 $contact = !$contact && is_object($cid) ? $cid : $contact; 282 $cid = is_object($contact) ? $contact->email : $cid; 283 return '<a href="' . selfHref(array("actas" => $cid)) 284 . '" tabindex="-1">' . Ht::img("viewas.png", "[Act as]", array("title" => "Act as " . Text::name_text($contact))) . '</a>'; 285} 286 287function decorateNumber($n) { 288 if ($n < 0) 289 return "−" . (-$n); 290 else if ($n > 0) 291 return $n; 292 else 293 return 0; 294} 295 296 297function _one_quicklink($id, $baseUrl, $urlrest, $listtype, $isprev) { 298 if ($listtype == "u") { 299 $result = Dbl::ql("select email from ContactInfo where contactId=?", $id); 300 $row = edb_row($result); 301 Dbl::free($result); 302 $paperText = htmlspecialchars($row ? $row[0] : $id); 303 $urlrest["u"] = urlencode($id); 304 } else { 305 $paperText = "#$id"; 306 $urlrest["p"] = $id; 307 } 308 return "<a id=\"quicklink_" . ($isprev ? "prev" : "next") 309 . "\" class=\"x\" href=\"" . hoturl($baseUrl, $urlrest) . "\">" 310 . ($isprev ? Icons::ui_linkarrow(3) : "") 311 . $paperText 312 . ($isprev ? "" : Icons::ui_linkarrow(1)) 313 . "</a>"; 314} 315 316function goPaperForm($baseUrl = null, $args = array()) { 317 global $Conf, $Me; 318 if ($Me->is_empty()) 319 return ""; 320 $list = $Conf->active_list(); 321 $x = Ht::form($Conf->hoturl($baseUrl ? : "paper"), ["method" => "get", "class" => "gopaper"]); 322 if ($baseUrl == "profile") 323 $x .= Ht::entry("u", "", array("id" => "quicksearchq", "size" => 15, "placeholder" => "User search", "class" => "usersearch need-autogrow")); 324 else 325 $x .= Ht::entry("p", "", array("id" => "quicksearchq", "size" => 10, "placeholder" => "(All)", "class" => "papersearch need-autogrow")); 326 foreach ($args as $k => $v) 327 $x .= Ht::hidden($k, $v); 328 $x .= " " . Ht::submit("Search") . "</form>"; 329 return $x; 330} 331 332function rm_rf_tempdir($tempdir) { 333 assert(substr($tempdir, 0, 1) === "/"); 334 exec("/bin/rm -rf " . escapeshellarg($tempdir)); 335} 336 337function clean_tempdirs() { 338 $dir = sys_get_temp_dir() ? : "/"; 339 while (substr($dir, -1) === "/") 340 $dir = substr($dir, 0, -1); 341 $dirh = opendir($dir); 342 $now = time(); 343 while (($fname = readdir($dirh)) !== false) 344 if (preg_match('/\Ahotcrptmp\d+\z/', $fname) 345 && is_dir("$dir/$fname") 346 && ($mtime = @filemtime("$dir/$fname")) !== false 347 && $mtime < $now - 1800) 348 rm_rf_tempdir("$dir/$fname"); 349 closedir($dirh); 350} 351 352function tempdir($mode = 0700) { 353 $dir = sys_get_temp_dir() ? : "/"; 354 while (substr($dir, -1) === "/") 355 $dir = substr($dir, 0, -1); 356 for ($i = 0; $i < 100; $i++) { 357 $path = $dir . "/hotcrptmp" . mt_rand(0, 9999999); 358 if (mkdir($path, $mode)) { 359 register_shutdown_function("rm_rf_tempdir", $path); 360 return $path; 361 } 362 } 363 return false; 364} 365 366 367// text helpers 368function commajoin($what, $joinword = "and") { 369 $what = array_values($what); 370 $c = count($what); 371 if ($c == 0) 372 return ""; 373 else if ($c == 1) 374 return $what[0]; 375 else if ($c == 2) 376 return $what[0] . " " . $joinword . " " . $what[1]; 377 else 378 return join(", ", array_slice($what, 0, -1)) . ", " . $joinword . " " . $what[count($what) - 1]; 379} 380 381function prefix_commajoin($what, $prefix, $joinword = "and") { 382 return commajoin(array_map(function ($x) use ($prefix) { 383 return $prefix . $x; 384 }, $what), $joinword); 385} 386 387function numrangejoin($range) { 388 $a = []; 389 $format = null; 390 foreach ($range as $current) { 391 if ($format !== null 392 && sprintf($format, $intval + 1) === (string) $current) { 393 ++$intval; 394 $last = $current; 395 continue; 396 } else { 397 if ($format !== null && $first === $last) 398 $a[] = $first; 399 else if ($format !== null) 400 $a[] = $first . "–" . substr($last, $plen); 401 if ($current !== "" && ctype_digit($current)) { 402 $format = "%0" . strlen($current) . "d"; 403 $plen = 0; 404 $first = $last = $current; 405 $intval = intval($current); 406 } else if (preg_match('/\A(\D*)(\d+)\z/', $current, $m)) { 407 $format = str_replace("%", "%%", $m[1]) . "%0" . strlen($m[2]) . "d"; 408 $plen = strlen($m[1]); 409 $first = $last = $current; 410 $intval = intval($m[2]); 411 } else { 412 $format = null; 413 $a[] = $current; 414 } 415 } 416 } 417 if ($format !== null && $first === $last) 418 $a[] = $first; 419 else if ($format !== null) 420 $a[] = $first . "–" . substr($last, $plen); 421 return commajoin($a); 422} 423 424function pluralx($n, $what) { 425 if (is_array($n)) 426 $n = count($n); 427 return $n == 1 ? $what : pluralize($what); 428} 429 430function pluralize($what) { 431 if ($what == "this") 432 return "these"; 433 else if ($what == "has") 434 return "have"; 435 else if ($what == "is") 436 return "are"; 437 else if (str_ends_with($what, ")") && preg_match('/\A(.*?)(\s*\([^)]*\))\z/', $what, $m)) 438 return pluralize($m[1]) . $m[2]; 439 else if (preg_match('/\A.*?(?:s|sh|ch|[bcdfgjklmnpqrstvxz]y)\z/', $what)) { 440 if (substr($what, -1) == "y") 441 return substr($what, 0, -1) . "ies"; 442 else 443 return $what . "es"; 444 } else 445 return $what . "s"; 446} 447 448function plural($n, $what) { 449 return (is_array($n) ? count($n) : $n) . ' ' . pluralx($n, $what); 450} 451 452function ordinal($n) { 453 $x = $n; 454 if ($x > 100) 455 $x = $x % 100; 456 if ($x > 20) 457 $x = $x % 10; 458 return $n . ($x < 1 || $x > 3 ? "th" : ($x == 1 ? "st" : ($x == 2 ? "nd" : "rd"))); 459} 460 461function tabLength($text, $all) { 462 $len = 0; 463 for ($i = 0; $i < strlen($text); $i++) 464 if ($text[$i] == ' ') 465 $len++; 466 else if ($text[$i] == '\t') 467 $len += 8 - ($len % 8); 468 else if (!$all) 469 break; 470 else 471 $len++; 472 return $len; 473} 474 475function ini_get_bytes($varname, $value = null) { 476 $val = trim($value !== null ? $value : ini_get($varname)); 477 $last = strlen($val) ? strtolower($val[strlen($val) - 1]) : "."; 478 return (int) ceil(floatval($val) * (1 << (+strpos(".kmg", $last) * 10))); 479} 480 481function filter_whynot($whyNot, $keys) { 482 $revWhyNot = []; 483 foreach ($whyNot as $k => $v) { 484 if ($k === "fail" || $k === "paperId" || $k === "conf" || in_array($k, $keys)) 485 $revWhyNot[$k] = $v; 486 } 487 return $revWhyNot; 488} 489 490function whyNotText($whyNot, $text_only = false) { 491 global $Conf, $Now; 492 if (is_string($whyNot)) 493 $whyNot = array($whyNot => 1); 494 $conf = get($whyNot, "conf") ? : $Conf; 495 $paperId = (isset($whyNot["paperId"]) ? $whyNot["paperId"] : -1); 496 $reviewId = (isset($whyNot["reviewId"]) ? $whyNot["reviewId"] : -1); 497 $ms = []; 498 $quote = $text_only ? function ($x) { return $x; } : "htmlspecialchars"; 499 if (isset($whyNot["invalidId"])) { 500 $x = $whyNot["invalidId"] . "Id"; 501 if (isset($whyNot[$x])) 502 $ms[] = $conf->_("Invalid " . $whyNot["invalidId"] . " number “%s”.", $quote($whyNot[$x])); 503 else 504 $ms[] = $conf->_("Invalid " . $whyNot["invalidId"] . " number."); 505 } 506 if (isset($whyNot["noPaper"])) 507 $ms[] = $conf->_("No such submission #%d.", $paperId); 508 if (isset($whyNot["dbError"])) 509 $ms[] = $whyNot["dbError"]; 510 if (isset($whyNot["administer"])) 511 $ms[] = $conf->_("You can’t administer submission #%d.", $paperId); 512 if (isset($whyNot["permission"])) 513 $ms[] = $conf->_c("eperm", "Permission error.", $whyNot["permission"], $paperId); 514 if (isset($whyNot["pdfPermission"])) 515 $ms[] = $conf->_c("eperm", "Permission error.", "view_pdf", $paperId); 516 if (isset($whyNot["optionPermission"])) 517 $ms[] = $conf->_("You don’t have permission to view the %2\$s for submission #%1\$d.", $paperId, $whyNot["optionPermission"]->message_title); 518 if (isset($whyNot["optionNotAccepted"])) 519 $ms[] = $conf->_("Non-accepted submission #%d can have no %s.", $paperId, $whyNot["optionNotAccepted"]->message_title); 520 if (isset($whyNot["signin"])) 521 $ms[] = $conf->_c("eperm", "You have been signed out.", $whyNot["signin"], $paperId); 522 if (isset($whyNot["withdrawn"])) 523 $ms[] = $conf->_("Submission #%d has been withdrawn.", $paperId); 524 if (isset($whyNot["notWithdrawn"])) 525 $ms[] = $conf->_("Submission #%d is not withdrawn.", $paperId); 526 if (isset($whyNot["notSubmitted"])) 527 $ms[] = $conf->_("Submission #%d is only a draft.", $paperId); 528 if (isset($whyNot["rejected"])) 529 $ms[] = $conf->_("Submission #%d was not accepted for publication.", $paperId); 530 if (isset($whyNot["decided"])) 531 $ms[] = $conf->_("The review process for submission #%d has completed.", $paperId); 532 if (isset($whyNot["updateSubmitted"])) 533 $ms[] = $conf->_("Submission #%d can no longer be updated.", $paperId); 534 if (isset($whyNot["notUploaded"])) 535 $ms[] = $conf->_("A PDF upload is required to submit."); 536 if (isset($whyNot["reviewNotSubmitted"])) 537 $ms[] = $conf->_("This review is not yet ready for others to see."); 538 if (isset($whyNot["reviewNotComplete"])) 539 $ms[] = $conf->_("Your own review for #%d is not complete, so you can’t view other people’s reviews.", $paperId); 540 if (isset($whyNot["responseNotReady"])) 541 $ms[] = $conf->_("The authors’ response is not yet ready for reviewers to view."); 542 if (isset($whyNot["reviewsOutstanding"])) { 543 $ms[] = $conf->_("You will get access to the reviews once you complete your assigned reviews. If you can’t complete your reviews, please let the organizers know via the “Refuse review” links."); 544 if (!$text_only) 545 $ms[] = $conf->_("<a href=\"%s\">List assigned reviews</a>", hoturl("search", "q=&t=r")); 546 } 547 if (isset($whyNot["reviewNotAssigned"])) 548 $ms[] = $conf->_("You are not assigned to review submission #%d.", $paperId); 549 if (isset($whyNot["deadline"])) { 550 $dname = $whyNot["deadline"]; 551 if ($dname[0] == "s") 552 $open_dname = "sub_open"; 553 else if ($dname[0] == "p" || $dname[0] == "e") 554 $open_dname = "rev_open"; 555 else 556 $open_dname = false; 557 $start = $open_dname ? $conf->setting($open_dname, -1) : 1; 558 $end = $conf->setting($dname, -1); 559 if ($dname == "au_seerev") { 560 if ($conf->au_seerev == Conf::AUSEEREV_UNLESSINCOMPLETE) { 561 $ms[] = $conf->_("You will get access to the reviews for this submission when you have completed your own reviews."); 562 if (!$text_only) 563 $ms[] = $conf->_("<a href=\"%s\">List your incomplete reviews</a>", hoturl("search", "t=rout&q=")); 564 } else 565 $ms[] = $conf->_c("etime", "Action not available.", $dname, $paperId); 566 } else if ($start <= 0 || $start == $end) { 567 $ms[] = $conf->_c("etime", "Action not available.", $open_dname, $paperId); 568 } else if ($start > 0 && $Now < $start) { 569 $ms[] = $conf->_c("etime", "Action not available until %3$s.", $open_dname, $paperId, $conf->printableTime($start, "span")); 570 } else if ($end > 0 && $Now > $end) { 571 $ms[] = $conf->_c("etime", "Deadline passed.", $dname, $paperId, $conf->printableTime($end, "span")); 572 } else 573 $ms[] = $conf->_c("etime", "Action not available.", "", $paperId); 574 } 575 if (isset($whyNot["override"])) 576 $ms[] = $conf->_("“Override deadlines” can override this restriction."); 577 if (isset($whyNot["blindSubmission"])) 578 $ms[] = $conf->_("Submission to this conference is blind."); 579 if (isset($whyNot["author"])) 580 $ms[] = $conf->_("You aren’t a contact for #%d.", $paperId); 581 if (isset($whyNot["conflict"])) 582 $ms[] = $conf->_("You have a conflict with #%d.", $paperId); 583 if (isset($whyNot["externalReviewer"])) 584 $ms[] = $conf->_("External reviewers cannot view other reviews."); 585 if (isset($whyNot["differentReviewer"])) 586 $ms[] = $conf->_("You didn’t write this review, so you can’t change it."); 587 if (isset($whyNot["unacceptableReviewer"])) 588 $ms[] = $conf->_("That user can’t be assigned to review #%d.", $paperId); 589 if (isset($whyNot["clickthrough"])) 590 $ms[] = $conf->_("You can’t do that until you agree to the terms."); 591 if (isset($whyNot["otherTwiddleTag"])) 592 $ms[] = $conf->_("Tag “#%s” doesn’t belong to you.", $quote($whyNot["tag"])); 593 if (isset($whyNot["chairTag"])) 594 $ms[] = $conf->_("Tag “#%s” can only be changed by administrators.", $quote($whyNot["tag"])); 595 if (isset($whyNot["voteTag"])) 596 $ms[] = $conf->_("The voting tag “#%s” shouldn’t be changed directly. To vote for this paper, change the “#~%1\$s” tag.", $quote($whyNot["tag"])); 597 if (isset($whyNot["voteTagNegative"])) 598 $ms[] = $conf->_("Negative votes aren’t allowed."); 599 if (isset($whyNot["autosearchTag"])) 600 $ms[] = $conf->_("Tag “#%s” cannot be changed since the system sets it automatically.", $quote($whyNot["tag"])); 601 if (empty($ms) && isset($whyNot["fail"])) 602 $ms[] = $conf->_c("eperm", "Permission error.", "unknown", $paperId); 603 // finish it off 604 if (isset($whyNot["forceShow"]) && !$text_only) 605 $ms[] = $conf->_("<a class=\"nw\" href=\"%s\">Override conflict</a>", selfHref(array("forceShow" => 1))); 606 if (!empty($ms) && isset($whyNot["listViewable"]) && !$text_only) 607 $ms[] = $conf->_("<a href=\"%s\">List the submissions you can view</a>", hoturl("search", "q=")); 608 return join(" ", $ms); 609} 610 611function actionBar($mode = null, $qreq = null) { 612 global $Me, $Conf; 613 $forceShow = ($Me->is_admin_force() ? "&forceShow=1" : ""); 614 615 $paperArg = "p=*"; 616 $xmode = array(); 617 $listtype = "p"; 618 619 $goBase = "paper"; 620 if ($mode == "assign") 621 $goBase = "assign"; 622 else if ($mode == "re") 623 $goBase = "review"; 624 else if ($mode == "account") { 625 $listtype = "u"; 626 if ($Me->privChair) { 627 $goBase = "profile"; 628 $xmode["search"] = 1; 629 } 630 } else if ($qreq && ($qreq->m || $qreq->mode)) 631 $xmode["m"] = $qreq->m ? : $qreq->mode; 632 633 $x = '<table class="vbar"><tr>'; 634 635 // quicklinks 636 if (($list = $Conf->active_list())) { 637 $x .= '<td class="vbar quicklinks">'; 638 if (($prev = $list->neighbor_id(-1)) !== false) 639 $x .= _one_quicklink($prev, $goBase, $xmode, $listtype, true) . " "; 640 if ($list->description) { 641 $url = $list->full_site_relative_url(); 642 if ($url) 643 $x .= '<a id="quicklink_list" class="x" href="' . htmlspecialchars(Navigation::siteurl() . $url) . "\">" . $list->description . "</a>"; 644 else 645 $x .= '<span id="quicklink_list">' . $list->description . '</span>'; 646 } 647 if (($next = $list->neighbor_id(1)) !== false) 648 $x .= " " . _one_quicklink($next, $goBase, $xmode, $listtype, false); 649 $x .= '</td>'; 650 651 if ($Me->privChair && $listtype == "p") 652 $x .= " <td id=\"trackerconnect\" class=\"vbar\"><a id=\"trackerconnectbtn\" class=\"ui tracker-ui start tbtn need-tooltip\" href=\"\" data-tooltip=\"Start meeting tracker\">☟</a><td>\n"; 653 } 654 655 return $x . '<td class="vbar gopaper">' . goPaperForm($goBase, $xmode) . "</td></tr></table>"; 656} 657 658function parseReviewOrdinal($text) { 659 $text = strtoupper($text); 660 if (ctype_alpha($text)) { 661 if (strlen($text) == 1) 662 return ord($text) - 64; 663 else if (strlen($text) == 2) 664 return (ord($text[0]) - 64) * 26 + ord($text[1]) - 64; 665 } 666 return -1; 667} 668 669function unparseReviewOrdinal($ord) { 670 if (!$ord) 671 return "."; 672 else if (is_object($ord)) { 673 if ($ord->reviewOrdinal) 674 return $ord->paperId . unparseReviewOrdinal($ord->reviewOrdinal); 675 else 676 return $ord->reviewId; 677 } else if ($ord <= 26) 678 return chr($ord + 64); 679 else 680 return chr(intval(($ord - 1) / 26) + 64) . chr((($ord - 1) % 26) + 65); 681} 682 683function downloadText($text, $filename, $inline = false) { 684 global $Conf; 685 $csvg = new CsvGenerator(CsvGenerator::TYPE_TAB); 686 $csvg->set_filename($Conf->download_prefix . $filename . $csvg->extension()); 687 $csvg->set_inline($inline); 688 $csvg->download_headers(); 689 if ($text !== false) { 690 $csvg->add_string($text); 691 $csvg->download(); 692 exit; 693 } 694} 695 696function unparse_expertise($expertise) { 697 if ($expertise === null) 698 return ""; 699 else 700 return $expertise > 0 ? "X" : ($expertise == 0 ? "Y" : "Z"); 701} 702 703function unparse_preference($preference, $expertise = null) { 704 if (is_object($preference)) 705 list($preference, $expertise) = array(get($preference, "reviewerPreference"), 706 get($preference, "reviewerExpertise")); 707 else if (is_array($preference)) 708 list($preference, $expertise) = $preference; 709 if ($preference === null || $preference === false) 710 $preference = "0"; 711 return $preference . unparse_expertise($expertise); 712} 713 714function unparse_preference_span($preference, $always = false) { 715 if (is_object($preference)) 716 $preference = array(get($preference, "reviewerPreference"), 717 get($preference, "reviewerExpertise"), 718 get($preference, "topicInterestScore")); 719 else if (!is_array($preference)) 720 $preference = array($preference, null, null); 721 $pv = (int) get($preference, 0); 722 $ev = get($preference, 1); 723 $tv = (int) get($preference, 2); 724 $type = 1; 725 if ($pv < 0 || (!$pv && $tv < 0)) 726 $type = -1; 727 $t = ""; 728 if ($pv || $ev !== null || $always) 729 $t .= "P" . decorateNumber($pv) . unparse_expertise($ev); 730 if ($tv && !$pv) 731 $t .= ($t ? " " : "") . "T" . decorateNumber($tv); 732 if ($t !== "") 733 $t = " <span class=\"asspref$type\">$t</span>"; 734 return $t; 735} 736 737function decisionSelector($curOutcome = 0, $id = null, $extra = "") { 738 global $Conf; 739 $text = "<select" . ($id === null ? "" : " id='$id'") . " name='decision'$extra>\n"; 740 $decs = $Conf->decision_map(); 741 if (!isset($decs[$curOutcome])) 742 $curOutcome = null; 743 $outcomes = array_keys($decs); 744 if ($curOutcome === null) 745 $text .= " <option value='' selected='selected'>Set decision...</option>\n"; 746 foreach ($decs as $dnum => $dname) 747 $text .= " <option value='$dnum'" . ($curOutcome == $dnum && $curOutcome !== null ? " selected='selected'" : "") . ">" . htmlspecialchars($dname) . "</option>\n"; 748 return $text . " </select>"; 749} 750 751function review_type_icon($revtype, $unfinished = null, $title = null) { 752 // see also script.js:review_form 753 static $revtypemap = array(-3 => array("−", "Refused"), 754 -2 => array("A", "Author"), 755 -1 => array("C", "Conflict"), 756 1 => array("E", "External review"), 757 2 => array("P", "PC review"), 758 3 => array("2", "Secondary review"), 759 4 => array("1", "Primary review"), 760 5 => array("M", "Metareview")); 761 if (!$revtype) 762 return '<span class="rt0"></span>'; 763 $x = $revtypemap[$revtype]; 764 return '<span class="rto rt' . $revtype 765 . ($revtype > 0 && $unfinished ? "n" : "") 766 . '" title="' . ($title ? $title : $revtypemap[$revtype][1]) 767 . '"><span class="rti">' . $revtypemap[$revtype][0] . '</span></span>'; 768} 769 770function review_lead_icon() { 771 return '<span class="rto rtlead" title="Lead"><span class="rti">L</span></span>'; 772} 773 774function review_shepherd_icon() { 775 return '<span class="rto rtshep" title="Shepherd"><span class="rti">S</span></span>'; 776} 777 778function displayOptionsSet($sessionvar, $var = null, $val = null) { 779 global $Conf; 780 if (($x = $Conf->session($sessionvar)) !== null) 781 /* use session value */; 782 else if ($sessionvar === "pldisplay") 783 $x = $Conf->setting_data("pldisplay_default", ""); 784 else 785 $x = ""; 786 if ($x == null || strpos($x, " ") === false) { 787 if ($sessionvar == "pldisplay") 788 $x = $Conf->review_form()->default_display(); 789 else if ($sessionvar == "uldisplay") 790 $x = " tags overAllMerit "; 791 else 792 $x = " "; 793 } 794 795 // set $var to $val in list 796 if ($var) { 797 $x = str_replace(" $var ", " ", $x); 798 if ($val) 799 $x .= "$var "; 800 if (($sessionvar === "pldisplay" || $sessionvar === "pfdisplay") 801 && ($f = $Conf->find_review_field($var)) 802 && $var !== $f->id) 803 $x = str_replace(" {$f->id} ", " ", $x); 804 } 805 806 // store list in $_SESSION 807 $Conf->save_session($sessionvar, $x); 808 return $x; 809} 810 811 812if (!function_exists("random_bytes")) { 813 function random_bytes($length) { 814 $x = @file_get_contents("/dev/urandom", false, null, 0, $length); 815 if (($x === false || $x === "") 816 && function_exists("openssl_random_pseudo_bytes")) { 817 $x = openssl_random_pseudo_bytes($length, $strong); 818 $x = $strong ? $x : false; 819 } 820 return $x === "" ? false : $x; 821 } 822} 823 824function hotcrp_random_password($length = 14) { 825 $bytes = random_bytes($length + 10); 826 if ($bytes === false) { 827 $bytes = ""; 828 while (strlen($bytes) < $length) 829 $bytes .= sha1(opt("conferenceKey") . pack("V", mt_rand())); 830 } 831 832 $l = "a e i o u y a e i o u y a e i o u y a e i o u y a e i o u y b c d g h j k l m n p r s t u v w trcrbrfrthdrchphwrstspswprslcl2 3 4 5 6 7 8 9 - @ _ + = "; 833 $pw = ""; 834 $nvow = 0; 835 for ($i = 0; 836 $i < strlen($bytes) && 837 strlen($pw) < $length + max(0, ($nvow - 3) / 3); 838 ++$i) { 839 $x = ord($bytes[$i]) % (strlen($l) / 2); 840 if ($x < 30) 841 ++$nvow; 842 $pw .= rtrim(substr($l, 2 * $x, 2)); 843 } 844 return $pw; 845} 846 847 848function encode_token($x, $format = "") { 849 $s = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; 850 $t = ""; 851 if (is_int($x)) 852 $format = "V"; 853 if ($format) 854 $x = pack($format, $x); 855 $i = 0; 856 $have = 0; 857 $n = 0; 858 while ($have > 0 || $i < strlen($x)) { 859 if ($have < 5 && $i < strlen($x)) { 860 $n += ord($x[$i]) << $have; 861 $have += 8; 862 ++$i; 863 } 864 $t .= $s[$n & 31]; 865 $n >>= 5; 866 $have -= 5; 867 } 868 if ($format == "V") 869 return preg_replace('/(\AA|[^A])A*\z/', '$1', $t); 870 else 871 return $t; 872} 873 874function decode_token($x, $format = "") { 875 $map = "//HIJKLMNO///////01234567/89:;</=>?@ABCDEFG"; 876 $t = ""; 877 $n = $have = 0; 878 $x = trim(strtoupper($x)); 879 for ($i = 0; $i < strlen($x); ++$i) { 880 $o = ord($x[$i]); 881 if ($o >= 48 && $o <= 90 && ($out = ord($map[$o - 48])) >= 48) 882 $o = $out - 48; 883 else if ($o == 46 /*.*/ || $o == 34 /*"*/) 884 continue; 885 else 886 return false; 887 $n += $o << $have; 888 $have += 5; 889 while ($have >= 8 || ($n && $i == strlen($x) - 1)) { 890 $t .= chr($n & 255); 891 $n >>= 8; 892 $have -= 8; 893 } 894 } 895 if ($format == "V") { 896 $x = unpack("Vx", $t . "\x00\x00\x00\x00\x00\x00\x00"); 897 return $x["x"]; 898 } else if ($format) 899 return unpack($format, $t); 900 else 901 return $t; 902} 903