function echo_commit($Info) { global $Conf, $Me, $User, $Pset; global $TABWIDTH, $WDIFF; $Notes = $Info->commit_info(); $TABWIDTH = $Info->commit_info("tabwidth") ?: 4; $WDIFF = isset($Notes->wdiff) ? $Notes->wdiff : false; // current commit and commit selector $sel = array(); $curhead = $grouphead = null; foreach ($Info->recent_commits() as $k) { // visually separate older heads if ($curhead === null) { $curhead = $k->fromhead; } if ($curhead != $k->fromhead) { if (!$grouphead) { $sel["from.{$k->fromhead}"] = (object) array("type" => "optgroup", "label" => "Other snapshots"); } else { $sel["from.{$k->fromhead}"] = null; } $curhead = $grouphead = $k->fromhead; } // actual option $x = UnicodeHelper::utf8_prefix($k->subject, 72); if (strlen($x) != strlen($k->subject)) { $x .= "..."; } $sel[$k->hash] = substr($k->hash, 0, 7) . " " . htmlspecialchars($x); } $result = $Conf->qe("select hash from CommitNotes where (haslinenotes & ?)!=0 and pset=? and hash ?a", $Me == $User && !$Info->can_see_grades ? HASNOTES_COMMENT : HASNOTES_ANY, $Pset->psetid, array_keys($sel)); while ($row = edb_row($result)) { $sel[$row[0]] .= " ♪"; } if (($h = $Info->grading_hash()) && isset($sel[$h])) { $sel[$h] = preg_replace('_(.*?)(?: )?(♪?)\\z_', '$1 ✱$2', $sel[$h]); } if ($Info->is_grading_commit()) { $key = "grading commit"; } else { $key = "this commit"; } $value = Ht::select("newcommit", $sel, $Info->commit_hash(), array("onchange" => "jQuery(this).closest('form').submit()")); if ($Me != $User) { $x = $Info->is_grading_commit() ? "" : "font-weight:bold"; $value .= " " . Ht::submit("grade", "Grade", array("style" => $x)); } // view options $fold_viewoptions = !isset($_REQUEST["tab"]) && !isset($_REQUEST["wdiff"]); $value .= '<div class="viewoptions61">' . '<a class="q" href="#" onclick="return fold61(this.nextSibling,this.parentNode)">' . '<span class="foldarrow">' . ($fold_viewoptions ? '▶' : '▼') . '</span> options</a><span style="padding-left:1em' . ($fold_viewoptions ? ';display:none' : '') . '">tab width:'; foreach (array(2, 4, 8) as $i) { $value .= ' <a href="' . self_href(array("tab" => $i)) . '"' . ($TABWIDTH == $i ? " class=\"q\"><strong>{$i}</strong>" : '>' . $i) . '</a>'; } $value .= '<span style="padding-left:1em">wdiff:'; foreach (array("no", "yes") as $i => $t) { $value .= ' <a href="' . self_href(array("wdiff" => $i)) . '"' . (!$WDIFF == !$i ? " class=\"q\"><strong>{$t}</strong>" : '>' . $t) . '</a>'; } $value .= '</span></span></div>'; // warnings $remarks = array(); if (!$Info->grading_hash() && $Me != $User && !$Pset->gitless_grades) { $remarks[] = array(true, "No commit has been marked for grading."); } else { if (!$Info->is_grading_commit() && $Info->grading_hash()) { $remarks[] = array(true, "This is not " . "<a class=\"uu\" href=\"" . $Info->hoturl("pset", array("commit" => $Info->grading_hash())) . "\">the commit currently marked for grading</a>" . " <span style=\"font-weight:normal\">(<a href=\"" . $Info->hoturl("diff", array("commit1" => $Info->grading_hash())) . "\">see diff</a>)</span>."); } } if (!$Info->is_latest_commit()) { $remarks[] = array(true, "This is not " . "<a class=\"uu\" href=\"" . $Info->hoturl("pset", array("commit" => $Info->latest_hash())) . "\">the latest commit</a>" . " <span style=\"font-weight:normal\">(<a href=\"" . $Info->hoturl("diff", array("commit1" => $Info->latest_hash())) . "\">see diff</a>)</span>."); } if (($lh = $Info->late_hours()) && $lh->hours > 0) { $extra = array(); if (get($lh, "commitat")) { $extra[] = "commit at " . $Conf->printableTimestamp($lh->commitat); } if (get($lh, "deadline")) { $extra[] = "deadline " . $Conf->printableTimestamp($lh->deadline); } $extra = count($extra) ? ' <span style="font-weight:normal">(' . join(", ", $extra) . ')</span>' : ""; $remarks[] = array(true, "This commit uses " . plural($lh->hours, "late hour") . $extra . "."); } if (($Info->is_latest_commit() || $Me->isPC) && $Pset->handout_repo_url) { $Pset->latest_handout_commit(); $last_handout = $Pset->latest_handout_commit(); $last_myhandout = $last_handout ? $Info->derived_handout_hash() : false; if ($last_handout && $last_myhandout && $last_handout->hash == $last_myhandout) { /* this is ideal: they have the latest handout commit */ } else { if ($last_handout && $last_myhandout) { // they don't have the latest updates $remarks[] = array(true, "Updates are available for this problem set <span style=\"font-weight:normal\">(<a href=\"" . $Info->hoturl("diff", array("commit" => $last_myhandout, "commit1" => $last_handout->hash)) . "\">see diff</a>)</span>. Run <code>git pull handout master</code> to merge these updates."); } else { if ($last_handout) { $remarks[] = array(true, "Please create your repository by cloning our repository. Creating your repository from scratch makes it harder for you to get pset updates."); } else { if (!$last_handout && $Me->isPC) { $handout_files = $Pset->handout_repo()->ls_files("master"); if (!count($handout_files)) { $remarks[] = array(true, "The handout repository, " . htmlspecialchars($Pset->handout_repo_url) . ", contains no files; perhaps handout_repo_url is misconfigured."); } else { $remarks[] = array(true, "The handout repository, " . htmlspecialchars($Pset->handout_repo_url) . ", does not contain problem set code yet."); } } } } } } // actually print echo Ht::form($Info->hoturl_post("pset", array("commit" => null, "setcommit" => 1)), array("class" => "commitcontainer61", "data-pa-pset" => $Info->pset->urlkey, "data-pa-commit" => $Info->latest_hash())), "<div class=\"f-contain\">"; ContactView::echo_group($key, $value, $remarks); echo "</div></form>\n"; }
function expandvar_recipient($what, $isbool) { global $Conf; // rest is only there if we have a pset if (!$this->pset) { return self::EXPANDVAR_CONTINUE; } if ($what == "%PSET%" || $what == "%TITLE%") { return $this->pset->title; } if ($what == "%REPO%") { if ($this->pset->gitless) { return $isbool ? false : self::EXPANDVAR_CONTINUE; } $info = $this->get_pset_info(); if (!$info || !$info->repo) { return $isbool ? false : "(no repo)"; } return $info->repo->web_url(); } if ($what == "%PARTNER%") { if (!$this->pset->partner) { return $isbool ? false : self::EXPANDVAR_CONTINUE; } $info = $this->get_pset_info(); if (!$info || !$info->partner) { return $isbool ? false : "N/A"; } return Text::name_text($info->partner); } if (preg_match(',\\A%(?:COMMIT(?:|HASH|ABBREV|TITLE|DATE)|LATEHOURS)%\\z,', $what)) { if ($this->pset->gitless) { return $isbool ? false : self::EXPANDVAR_CONTINUE; } $info = $this->get_pset_info(); $recent = null; if ($info && $info->has_commit_set()) { $recent = $info->commit(); } if (!$recent) { if ($isbool) { return false; } else { if ($what == "%COMMITABBREV%" || $what == "%COMMITDATE%") { return "N/A"; } else { return "(no commit)"; } } } if ($what == "%COMMITHASH%") { return $recent->hash; } if ($what == "%COMMITABBREV%") { return substr($recent->hash, 0, 7); } if ($what == "%COMMIT%" && !$recent) { return substr($recent->hash, 0, 7); } else { if (!$recent) { return $isbool ? false : "(unknown)"; } else { if ($what == "%COMMITTITLE%") { return $recent->subject ?: "(empty)"; } else { if ($what == "%COMMIT%") { $subject = UnicodeHelper::utf8_prefix($recent->subject, 72); if (strlen($subject) != strlen($recent->subject)) { $subject .= "..."; } return substr($recent->hash, 0, 7) . ($subject === "" ? "" : " {$subject}"); } else { if ($what == "%COMMITDATE%") { return date("Y/m/d H:i:s", $recent->commitat); } else { if ($what == "%LATEHOURS%") { // XXX should use PsetView::late_hours if ($this->pset->deadline_extension && ($this->recipient->extension || $info->partner && $info->partner->extension)) { $deadline = $this->pset->deadline_extension; } else { if ($this->pset->deadline_college) { $deadline = $this->pset->deadline_college; } else { $deadline = $this->pset->deadline; } } if (!$deadline || $recent->commitat <= $deadline) { return $isbool ? false : "0"; } else { return (string) (int) (($recent->commitat - $deadline + 3599) / 3600); } } } } } } } } if ($what == "%GRADEENTRIES%") { $info = $this->get_pset_info(); if (!$info->can_see_grades) { return $isbool ? false : ""; } $t = ""; $total = $maxtotal = 0; // XXX better computation foreach ($this->pset->grades as $ge) { $g = $info->current_grade_entry($ge->name); if ($ge->is_extra ? $g : $g !== null) { $t .= (isset($ge->title) ? $ge->title : $ge->name) . ": " . ($g ?: 0); if ($ge->max && !$ge->hide_max) { $t .= " / " . $ge->max; } $t .= "\n"; } if ($g && !$ge->no_total) { $total += $g; } if (!$ge->is_extra && !$ge->no_total && !$ge->hide_max) { $maxtotal += $ge->max; } } if ($total || $maxtotal) { $t .= "TOTAL: " . $total; if ($maxtotal) { $t .= " / " . $maxtotal; } $t .= "\n"; } return $t; } return self::EXPANDVAR_CONTINUE; }
public function save($req, $contact) { global $Conf, $Now; if (is_array($req)) { $req = (object) $req; } $Table = $this->prow->comment_table_name(); $LinkTable = $this->prow->table_name(); $LinkColumn = $this->prow->id_column(); $req_visibility = get($req, "visibility"); $is_response = !!($this->commentType & COMMENTTYPE_RESPONSE); if ($is_response && get($req, "submit")) { $ctype = COMMENTTYPE_RESPONSE | COMMENTTYPE_AUTHOR; } else { if ($is_response) { $ctype = COMMENTTYPE_RESPONSE | COMMENTTYPE_AUTHOR | COMMENTTYPE_DRAFT; } else { if ($req_visibility == "a" || $req_visibility == "au") { $ctype = COMMENTTYPE_AUTHOR; } else { if ($req_visibility == "p" || $req_visibility == "pc") { $ctype = COMMENTTYPE_PCONLY; } else { if ($req_visibility == "admin") { $ctype = COMMENTTYPE_ADMINONLY; } else { if ($this->commentId && $req_visibility === null) { $ctype = $this->commentType; } else { // $req->visibility == "r" || $req->visibility == "rev" $ctype = COMMENTTYPE_REVIEWER; } } } } } } if ($is_response ? $this->prow->blind : $Conf->is_review_blind(!!get($req, "blind"))) { $ctype |= COMMENTTYPE_BLIND; } // tags if ($is_response) { $ctags = " response "; if (($rname = $Conf->resp_round_name($this->commentRound)) != "1") { $ctags .= "{$rname}response "; } } else { if (get($req, "tags") && preg_match_all(',\\S+,', $req->tags, $m)) { $tagger = new Tagger($contact); $ctags = array(); foreach ($m[0] as $text) { if (($text = $tagger->check($text, Tagger::NOVALUE)) && !stri_ends_with($text, "response")) { $ctags[strtolower($text)] = $text; } } $tagger->sort($ctags); $ctags = count($ctags) ? " " . join(" ", $ctags) . " " : null; } else { $ctags = null; } } // notifications $displayed = !($ctype & COMMENTTYPE_DRAFT); // query $text = get_s($req, "text"); $q = ""; $qv = array(); if ($text === "" && $this->commentId) { $change = true; $q = "delete from {$Table} where commentId={$this->commentId}"; } else { if ($text === "") { /* do nothing */ } else { if (!$this->commentId) { $change = true; $qa = ["contactId, {$LinkColumn}, commentType, comment, commentOverflow, timeModified, replyTo"]; $qb = [$contact->contactId, $this->prow->{$LinkColumn}, $ctype, "?", "?", $Now, 0]; if (strlen($text) <= 32000) { array_push($qv, $text, null); } else { array_push($qv, UnicodeHelper::utf8_prefix($text, 200), $text); } if ($ctags !== null) { $qa[] = "commentTags"; $qb[] = "?"; $qv[] = $ctags; } if ($is_response) { $qa[] = "commentRound"; $qb[] = $this->commentRound; } if ($displayed) { $qa[] = "timeDisplayed, timeNotified"; $qb[] = "{$Now}, {$Now}"; } $q = "insert into {$Table} (" . join(", ", $qa) . ") select " . join(", ", $qb) . "\n"; if ($is_response) { // make sure there is exactly one response $q .= " from (select {$LinkTable}.{$LinkColumn}, coalesce(commentId, 0) commentId\n from {$LinkTable}\n left join {$Table} on ({$Table}.{$LinkColumn}={$LinkTable}.{$LinkColumn} and (commentType&" . COMMENTTYPE_RESPONSE . ")!=0 and commentRound={$this->commentRound})\n where {$LinkTable}.{$LinkColumn}={$this->prow->{$LinkColumn}} limit 1) t\n where t.commentId=0"; } } else { $change = $this->commentType >= COMMENTTYPE_AUTHOR != $ctype >= COMMENTTYPE_AUTHOR; if ($this->timeModified >= $Now) { $Now = $this->timeModified + 1; } // do not notify on updates within 3 hours $qa = ""; if ($this->timeNotified + 10800 < $Now || $ctype & COMMENTTYPE_RESPONSE && !($ctype & COMMENTTYPE_DRAFT) && $this->commentType & COMMENTTYPE_DRAFT) { $qa .= ", timeNotified={$Now}"; } // reset timeDisplayed if you change the comment type if ((!$this->timeDisplayed || $this->ordinal_missing($ctype)) && $text !== "" && $displayed) { $qa .= ", timeDisplayed={$Now}"; } $q = "update {$Table} set timeModified={$Now}{$qa}, commentType={$ctype}, comment=?, commentOverflow=?, commentTags=? where commentId={$this->commentId}"; if (strlen($text) <= 32000) { array_push($qv, $text, null); } else { array_push($qv, UnicodeHelper::utf8_prefix($text, 200), $text); } $qv[] = $ctags; } } } $result = Dbl::qe_apply($q, $qv); if (!$result) { return false; } $cmtid = $this->commentId ?: $result->insert_id; if (!$cmtid) { return false; } // log $contact->log_activity("Comment {$cmtid} " . ($text !== "" ? "saved" : "deleted"), $this->prow->{$LinkColumn}); // ordinal if ($text !== "" && $this->ordinal_missing($ctype)) { $this->save_ordinal($cmtid, $ctype, $Table, $LinkTable, $LinkColumn); } // reload if ($text !== "") { $comments = $this->prow->fetch_comments("commentId={$cmtid}"); $this->merge($comments[$cmtid], $this->prow); if ($this->timeNotified == $this->timeModified) { self::$watching = $this; $this->prow->notify(WATCHTYPE_COMMENT, "CommentInfo::watch_callback", $contact); self::$watching = null; } } else { $this->commentId = 0; $this->comment = ""; $this->commentTags = null; } return true; }
xassert_eqq(ReviewField::make_abbreviation("novelty", 0, 0), "Nov"); xassert_eqq(ReviewField::make_abbreviation("novelty is an amazing", 0, 0), "NovIsAma"); xassert_eqq(ReviewField::make_abbreviation("novelty is an AWESOME", 0, 0), "NovIsAWESOME"); xassert_eqq(ReviewField::make_abbreviation("novelty isn't an AWESOME", 0, 0), "NovIsnAWESOME"); xassert_eqq(ReviewField::make_abbreviation("novelty isn't an AWESOME", 0, 1), "novelty-isnt-awesome"); xassert_eqq(ReviewField::make_abbreviation("_format", 0, 1), "format"); // utf8_word_prefix, etc. tests xassert_eqq(UnicodeHelper::utf8_prefix("aaaaaaaa", 7), "aaaaaaa"); xassert_eqq(UnicodeHelper::utf8_prefix("aaaaaaaa", 8), "aaaaaaaa"); xassert_eqq(UnicodeHelper::utf8_prefix("aaaaaaaa", 9), "aaaaaaaa"); xassert_eqq(UnicodeHelper::utf8_prefix("áááááááá", 7), "ááááááá"); xassert_eqq(UnicodeHelper::utf8_prefix("áááááááá", 8), "áááááááá"); xassert_eqq(UnicodeHelper::utf8_prefix("áááááááá", 9), "áááááááá"); xassert_eqq(UnicodeHelper::utf8_prefix("a̓a̓a̓a̓a̓a̓a̓a̓", 7), "a̓a̓a̓a̓a̓a̓a̓"); xassert_eqq(UnicodeHelper::utf8_prefix("a̓a̓a̓a̓a̓a̓a̓a̓", 8), "a̓a̓a̓a̓a̓a̓a̓a̓"); xassert_eqq(UnicodeHelper::utf8_prefix("a̓a̓a̓a̓a̓a̓a̓a̓", 9), "a̓a̓a̓a̓a̓a̓a̓a̓"); xassert_eqq(UnicodeHelper::utf8_word_prefix("aaaaaaaa bbb", 7), "aaaaaaaa"); xassert_eqq(UnicodeHelper::utf8_word_prefix("aaaaaaaa bbb", 8), "aaaaaaaa"); xassert_eqq(UnicodeHelper::utf8_word_prefix("aaaaaaaa bbb", 9), "aaaaaaaa"); xassert_eqq(UnicodeHelper::utf8_word_prefix("aaaaaaaa bbb", 10), "aaaaaaaa"); xassert_eqq(UnicodeHelper::utf8_glyphlen("aaaaaaaa"), 8); xassert_eqq(UnicodeHelper::utf8_glyphlen("áááááááá"), 8); xassert_eqq(UnicodeHelper::utf8_glyphlen("a̓a̓a̓a̓a̓a̓a̓a̓"), 8); xassert_eqq(prefix_word_wrap("+ ", "This is a thing to be wrapped.", "- ", 10), "+ This is\n- a thing\n- to be\n- wrapped.\n"); xassert_eqq(prefix_word_wrap("+ ", "This is a thing to be wrapped.", "- ", 9), "+ This is\n- a thing\n- to be\n- wrapped.\n"); xassert_eqq(prefix_word_wrap("+ ", "This\nis\na thing\nto\nbe wrapped.", "- ", 9), "+ This\n- is\n- a thing\n- to\n- be\n- wrapped.\n"); xassert_eqq(!!preg_match('/\\A\\pZ\\z/u', ' '), true); // deaccent tests xassert_eqq(UnicodeHelper::deaccent("Á é î ç ø U"), "A e i c o U"); $do = UnicodeHelper::deaccent_offsets("Á é î ç ø U .K"); xassert_eqq($do[0], "A e i c o U .K");