diff --git a/.arclint b/.arclint index ca89c601e4..7fb4c0f4a6 100644 --- a/.arclint +++ b/.arclint @@ -80,7 +80,8 @@ "xhpast": { "type": "xhpast", "include": "(\\.php$)", - "standard": "phutil.xhpast" + "standard": "phutil.xhpast", + "xhpast.php-version": "5.5" } } } diff --git a/NOTICE b/NOTICE index 23a28a1e40..f514a400e2 100644 --- a/NOTICE +++ b/NOTICE @@ -1,7 +1,7 @@ Phabricator Copyright 2014 Phacility, Inc. -This software is primarily developed and maintained by Phacility, Inc. +Phabricator was originally developed and maintained by Phacility, Inc. http://www.phacility.com/ diff --git a/externals/diff_match_patch/diff_match_patch.php b/externals/diff_match_patch/diff_match_patch.php deleted file mode 100644 index 35bdb82068..0000000000 --- a/externals/diff_match_patch/diff_match_patch.php +++ /dev/null @@ -1,2117 +0,0 @@ ->} Array of diff tuples. - */ - function diff_main($text1, $text2, $checklines = true) { - // Check for equality (speedup) - if ($text1 === $text2) { - return array ( array ( DIFF_EQUAL, $text1) ); - } - - // Trim off common prefix (speedup) - $commonlength = $this->diff_commonPrefix($text1, $text2); - $commonprefix = mb_substr($text1, 0, $commonlength); - $text1 = mb_substr($text1, $commonlength); - $text2 = mb_substr($text2, $commonlength); - - // Trim off common suffix (speedup) - $commonlength = $this->diff_commonSuffix($text1, $text2); - $commonsuffix = mb_substr($text1, mb_strlen($text1) - $commonlength); - $text1 = mb_substr($text1, 0, mb_strlen($text1) - $commonlength); - $text2 = mb_substr($text2, 0, mb_strlen($text2) - $commonlength); - - // Compute the diff on the middle block - $diffs = $this->diff_compute($text1, $text2, $checklines); - - // Restore the prefix and suffix - if ($commonprefix !== '') { - array_unshift($diffs, array ( DIFF_EQUAL, $commonprefix )); - } - if ($commonsuffix !== '') { - array_push($diffs, array ( DIFF_EQUAL, $commonsuffix )); - } - $this->diff_cleanupMerge($diffs); - return $diffs; - } - - /** - * Find the differences between two texts. Assumes that the texts do not - * have any common prefix or suffix. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @param {boolean} checklines Speedup flag. If false, then don't run a - * line-level diff first to identify the changed areas. - * If true, then run a faster, slightly less optimal diff - * @return {Array.>} Array of diff tuples. - * @private - */ - function diff_compute($text1, $text2, $checklines) { - - if ($text1 === '') { - // Just add some text (speedup) - return array ( array ( DIFF_INSERT, $text2 ) ); - } - - if ($text2 === '') { - // Just delete some text (speedup) - return array ( array ( DIFF_DELETE, $text1 ) ); - } - - $longtext = mb_strlen($text1) > mb_strlen($text2) ? $text1 : $text2; - $shorttext = mb_strlen($text1) > mb_strlen($text2) ? $text2 : $text1; - $i = mb_strpos($longtext, $shorttext); - if ($i !== false) { - // Shorter text is inside the longer text (speedup) - $diffs = array ( - array ( DIFF_INSERT, mb_substr($longtext, 0, $i) ), - array ( DIFF_EQUAL, $shorttext ), - array ( DIFF_INSERT, mb_substr($longtext, $i +mb_strlen($shorttext)) ) - ); - - // Swap insertions for deletions if diff is reversed. - if (mb_strlen($text1) > mb_strlen($text2)) { - $diffs[0][0] = $diffs[2][0] = DIFF_DELETE; - } - return $diffs; - } - $longtext = $shorttext = null; // Garbage collect - - // Check to see if the problem can be split in two. - $hm = $this->diff_halfMatch($text1, $text2); - if ($hm) { - // A half-match was found, sort out the return data. - $text1_a = $hm[0]; - $text1_b = $hm[1]; - $text2_a = $hm[2]; - $text2_b = $hm[3]; - $mid_common = $hm[4]; - // Send both pairs off for separate processing. - $diffs_a = $this->diff_main($text1_a, $text2_a, $checklines); - $diffs_b = $this->diff_main($text1_b, $text2_b, $checklines); - // Merge the results. - return array_merge($diffs_a, array ( - array ( - DIFF_EQUAL, - $mid_common - ) - ), $diffs_b); - } - - // Perform a real diff. - if ($checklines && (mb_strlen($text1) < 100 || mb_strlen($text2) < 100)) { - // Too trivial for the overhead. - $checklines = false; - } - $linearray = null; - if ($checklines) { - // Scan the text on a line-by-line basis first. - $a = $this->diff_linesToChars($text1, $text2); - $text1 = $a[0]; - $text2 = $a[1]; - $linearray = $a[2]; - } - $diffs = $this->diff_map($text1, $text2); - if (!$diffs) { - // No acceptable result. - $diffs = array ( - array ( - DIFF_DELETE, - $text1 - ), - array ( - DIFF_INSERT, - $text2 - ) - ); - } - if ($checklines) { - // Convert the diff back to original text. - $this->diff_charsToLines($diffs, $linearray); - // Eliminate freak matches (e.g. blank lines) - $this->diff_cleanupSemantic($diffs); - - // Rediff any replacement blocks, this time character-by-character. - // Add a dummy entry at the end. - array_push($diffs, array ( - DIFF_EQUAL, - '' - )); - $pointer = 0; - $count_delete = 0; - $count_insert = 0; - $text_delete = ''; - $text_insert = ''; - while ($pointer < count($diffs)) { - switch ($diffs[$pointer][0]) { - case DIFF_INSERT : - $count_insert++; - $text_insert .= $diffs[$pointer][1]; - break; - case DIFF_DELETE : - $count_delete++; - $text_delete .= $diffs[$pointer][1]; - break; - case DIFF_EQUAL : - // Upon reaching an equality, check for prior redundancies. - if ($count_delete >= 1 && $count_insert >= 1) { - // Delete the offending records and add the merged ones. - $a = $this->diff_main($text_delete, $text_insert, false); - array_splice($diffs, $pointer - $count_delete - $count_insert, $count_delete + $count_insert); - - $pointer = $pointer - $count_delete - $count_insert; - for ($j = count($a) - 1; $j >= 0; $j--) { - array_splice($diffs, $pointer, 0, array($a[$j])); - } - $pointer = $pointer +count($a); - } - $count_insert = 0; - $count_delete = 0; - $text_delete = ''; - $text_insert = ''; - break; - } - $pointer++; - } - array_pop($diffs); // Remove the dummy entry at the end. - } - return $diffs; - } - - /** - * Split two texts into an array of strings. Reduce the texts to a string of - * hashes where each Unicode character represents one line. - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {Array.>} Three element Array, containing the - * encoded text1, the encoded text2 and the array of unique strings. The - * zeroth element of the array of unique strings is intentionally blank. - * @private - */ - function diff_linesToChars($text1, $text2) { - $lineArray = array(); // e.g. lineArray[4] == 'Hello\n' - $lineHash = array(); // e.g. lineHash['Hello\n'] == 4 - - // '\x00' is a valid character, but various debuggers don't like it. - // So we'll insert a junk entry to avoid generating a null character. - $lineArray[0] = ''; - - $chars1 = $this->diff_linesToCharsMunge($text1, $lineArray, $lineHash); - $chars2 = $this->diff_linesToCharsMunge($text2, $lineArray, $lineHash); - return array ( - $chars1, - $chars2, - $lineArray - ); - } - - /** - * Split a text into an array of strings. Reduce the texts to a string of - * hashes where each Unicode character represents one line. - * Modifies linearray and linehash through being a closure. - * @param {string} text String to encode - * @return {string} Encoded string - * @private - */ - function diff_linesToCharsMunge($text, &$lineArray, &$lineHash) { - $chars = ''; - // Walk the text, pulling out a mb_substring for each line. - // text.split('\n') would would temporarily double our memory footprint. - // Modifying text would create many large strings to garbage collect. - $lineStart = 0; - $lineEnd = -1; - // Keeping our own length variable is faster than looking it up. - $lineArrayLength = count($lineArray); - while ($lineEnd < mb_strlen($text) - 1) { - $lineEnd = mb_strpos($text, "\n", $lineStart); - if ($lineEnd === false) { - $lineEnd = mb_strlen($text) - 1; - } - $line = mb_substr($text, $lineStart, $lineEnd +1 -$lineStart); - $lineStart = $lineEnd +1; - - if ( isset($lineHash[$line]) ) { - $chars .= mb_chr($lineHash[$line]); - } else { - $chars .= mb_chr($lineArrayLength); - $lineHash[$line] = $lineArrayLength; - $lineArray[$lineArrayLength++] = $line; - } - } - return $chars; - } - /** - * Rehydrate the text in a diff from a string of line hashes to real lines of - * text. - * @param {Array.>} diffs Array of diff tuples. - * @param {Array.} lineArray Array of unique strings. - * @private - */ - function diff_charsToLines(&$diffs, $lineArray) { - for ($x = 0; $x < count($diffs); $x++) { - $chars = $diffs[$x][1]; - $text = array (); - for ($y = 0; $y < mb_strlen($chars); $y++) { - $text[$y] = $lineArray[charCodeAt($chars, $y)]; - } - $diffs[$x][1] = implode('',$text); - } - } - - /** - * Explore the intersection points between the two texts. - * @param {string} text1 Old string to be diffed. - * @param {string} text2 New string to be diffed. - * @return {Array.>?} Array of diff tuples or null if no - * diff available. - * @private - */ - function diff_map($text1, $text2) { - // Don't run for too long. - $ms_end = microtime(true) + $this->Diff_Timeout; - - // Cache the text lengths to prevent multiple calls. - $text1_length = mb_strlen($text1); - $text2_length = mb_strlen($text2); - $max_d = $text1_length + $text2_length -1; - $doubleEnd = $this->Diff_DualThreshold * 2 < $max_d; - $v_map1 = array(); - $v_map2 = array(); - $v1 = array(); - $v2 = array(); - $v1[1] = 0; - $v2[1] = 0; - $x = null; - $y = null; - $footstep = null; // Used to track overlapping paths. - $footsteps = array(); - $done = false; - // Safari 1.x doesn't have hasOwnProperty - //? $hasOwnProperty = !!(footsteps.hasOwnProperty); - // If the total number of characters is odd, then the front path will collide - // with the reverse path. - $front = ($text1_length + $text2_length) % 2; - for ($d = 0; $d < $max_d; $d++) { - // Bail out if timeout reached. - if ($this->Diff_Timeout > 0 && microtime(true) > $ms_end) { - return null; // zzz - } - - // Walk the front path one step. - $v_map1[$d] = array (); - for ($k = -$d; $k <= $d; $k += 2) { - if ($k == -$d || $k != $d && $v1[$k -1] < $v1[$k +1]) { - $x = $v1[$k +1]; - } else { - $x = $v1[$k -1] + 1; - } - $y = $x - $k; - if ($doubleEnd) { - $footstep = $x . ',' . $y; - if ($front && isset ($footsteps[$footstep])) { - $done = true; - } - if (!$front) { - $footsteps[$footstep] = $d; - } - } - while (!$done && ($x < $text1_length) && ($y < $text2_length) && (mb_substr($text1, $x, 1) == mb_substr($text2, $y, 1)) ) { - $x++; - $y++; - if ($doubleEnd) { - $footstep = $x . ',' . $y; - if ($front && isset ($footsteps[$footstep])) { - $done = true; - } - if (!$front) { - $footsteps[$footstep] = $d; - } - } - } - $v1[$k] = $x; - $v_map1[$d][$x . ',' . $y] = true; - if ($x == $text1_length && $y == $text2_length) { - // Reached the end in single-path mode. - return $this->diff_path1($v_map1, $text1, $text2); - } - elseif ($done) { - // Front path ran over reverse path. - - $v_map2 = array_slice($v_map2, 0, $footsteps[$footstep] + 1); - $a = $this->diff_path1($v_map1, mb_substr($text1, 0, $x), mb_substr($text2, 0, $y)); - - return array_merge($a, $this->diff_path2($v_map2, mb_substr($text1, $x), mb_substr($text2, $y))); - } - } - - if ($doubleEnd) { - // Walk the reverse path one step. - $v_map2[$d] = array(); - for ($k = -$d; $k <= $d; $k += 2) { - if ($k == -$d || $k != $d && $v2[$k -1] < $v2[$k +1]) { - $x = $v2[$k +1]; - } else { - $x = $v2[$k -1] + 1; - } - $y = $x - $k; - $footstep = ($text1_length - $x) . ',' . ($text2_length - $y); - if (!$front && isset ($footsteps[$footstep])) { - $done = true; - } - if ($front) { - $footsteps[$footstep] = $d; - } - while (!$done && $x < $text1_length && $y < $text2_length && mb_substr($text1, $text1_length - $x -1, 1) == mb_substr($text2, $text2_length - $y -1, 1) ) { - $x++; - $y++; - $footstep = ($text1_length - $x) . ',' . ($text2_length - $y); - if (!$front && isset ($footsteps[$footstep])) { - $done = true; - } - if ($front) { - $footsteps[$footstep] = $d; - } - } - $v2[$k] = $x; - $v_map2[$d][$x . ',' . $y] = true; - if ($done) { - // Reverse path ran over front path. - $v_map1 = array_slice($v_map1, 0, $footsteps[$footstep] + 1); - $a = $this->diff_path1($v_map1, mb_substr($text1, 0, $text1_length - $x), mb_substr($text2, 0, $text2_length - $y)); - return array_merge($a, $this->diff_path2($v_map2, mb_substr($text1, $text1_length - $x), mb_substr($text2, $text2_length - $y))); - } - } - } - } - // Number of diffs equals number of characters, no commonality at all. - return null; - } - - /** - * Work from the middle back to the start to determine the path. - * @param {Array.} v_map Array of paths.ers - * @param {string} text1 Old string fragment to be diffed. - * @param {string} text2 New string fragment to be diffed. - * @return {Array.>} Array of diff tuples. - * @private - */ - function diff_path1($v_map, $text1, $text2) { - $path = array (); - $x = mb_strlen($text1); - $y = mb_strlen($text2); - /** @type {number?} */ - $last_op = null; - for ($d = count($v_map) - 2; $d >= 0; $d--) { - while (1) { - if (isset ($v_map[$d][($x -1) . ',' . $y])) { - $x--; - if ($last_op === DIFF_DELETE) { - $path[0][1] = mb_substr($text1, $x, 1) . $path[0][1]; - } else { - array_unshift($path, array ( - DIFF_DELETE, - mb_substr($text1, $x, 1) - )); - } - $last_op = DIFF_DELETE; - break; - } elseif (isset ($v_map[$d][$x . ',' . ($y -1)])) { - $y--; - if ($last_op === DIFF_INSERT) { - $path[0][1] = mb_substr($text2, $y, 1) . $path[0][1]; - } else { - array_unshift($path, array ( - DIFF_INSERT, - mb_substr($text2, $y, 1) - )); - } - $last_op = DIFF_INSERT; - break; - } else { - $x--; - $y--; - //if (text1.charAt(x) != text2.charAt(y)) { - // throw new Error('No diagonal. Can\'t happen. (diff_path1)'); - //} - if ($last_op === DIFF_EQUAL) { - $path[0][1] = mb_substr($text1, $x, 1) . $path[0][1]; - } else { - array_unshift($path, array ( - DIFF_EQUAL, - mb_substr($text1, $x, 1) - )); - } - $last_op = DIFF_EQUAL; - } - } - } - return $path; - } - - /** - * Work from the middle back to the end to determine the path. - * @param {Array.} v_map Array of paths. - * @param {string} text1 Old string fragment to be diffed. - * @param {string} text2 New string fragment to be diffed. - * @return {Array.>} Array of diff tuples. - * @private - */ - function diff_path2($v_map, $text1, $text2) { - $path = array (); - $pathLength = 0; - $x = mb_strlen($text1); - $y = mb_strlen($text2); - /** @type {number?} */ - $last_op = null; - for ($d = count($v_map) - 2; $d >= 0; $d--) { - while (1) { - if (isset ($v_map[$d][($x -1) . ',' . $y])) { - $x--; - if ($last_op === DIFF_DELETE) { - $path[$pathLength -1][1] .= $text1[mb_strlen($text1) - $x -1]; - } else { - $path[$pathLength++] = array ( - DIFF_DELETE, - $text1[mb_strlen($text1) - $x -1] - ); - } - $last_op = DIFF_DELETE; - break; - } - elseif (isset ($v_map[$d][$x . ',' . ($y -1)])) { - $y--; - if ($last_op === DIFF_INSERT) { - $path[$pathLength -1][1] .= $text2[mb_strlen($text2) - $y -1]; - } else { - $path[$pathLength++] = array ( - DIFF_INSERT, - $text2[mb_strlen($text2) - $y -1] - ); - } - $last_op = DIFF_INSERT; - break; - } else { - $x--; - $y--; - //if (text1.charAt(text1.length - x - 1) != - // text2.charAt(text2.length - y - 1)) { - // throw new Error('No diagonal. Can\'t happen. (diff_path2)'); - //} - if ($last_op === DIFF_EQUAL) { - $path[$pathLength -1][1] .= $text1[mb_strlen($text1) - $x -1]; - } else { - $path[$pathLength++] = array ( - DIFF_EQUAL, - $text1[mb_strlen($text1) - $x -1] - ); - } - $last_op = DIFF_EQUAL; - } - } - } - return $path; - } - - /** - * Determine the common prefix of two strings - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {number} The number of characters common to the start of each - * string. - */ - function diff_commonPrefix($text1, $text2) { - for ($i = 0; 1; $i++) { - $t1 = mb_substr($text1, $i, 1); - $t2 = mb_substr($text2, $i, 1); - if($t1==='' || $t2==='' || $t1 !== $t2 ){ - return $i; - } - } - } - - /** - * Determine the common suffix of two strings - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {number} The number of characters common to the end of each string. - */ - function diff_commonSuffix($text1, $text2) { - return $this->diff_commonPrefix( strrev($text1), strrev($text2) ); - } - - /** - * Do the two texts share a mb_substring which is at least half the length of the - * longer text? - * @param {string} text1 First string. - * @param {string} text2 Second string. - * @return {Array.?} Five element Array, containing the prefix of - * text1, the suffix of text1, the prefix of text2, the suffix of - * text2 and the common middle. Or null if there was no match. - */ - function diff_halfMatch($text1, $text2) { - $longtext = mb_strlen($text1) > mb_strlen($text2) ? $text1 : $text2; - $shorttext = mb_strlen($text1) > mb_strlen($text2) ? $text2 : $text1; - if (mb_strlen($longtext) < 10 || mb_strlen($shorttext) < 1) { - return null; // Pointless. - } - - // First check if the second quarter is the seed for a half-match. - $hm1 = $this->diff_halfMatchI($longtext, $shorttext, ceil(mb_strlen($longtext) / 4)); - // Check again based on the third quarter. - $hm2 = $this->diff_halfMatchI($longtext, $shorttext, ceil(mb_strlen($longtext) / 2)); - - if (!$hm1 && !$hm2) { - return null; - } elseif (!$hm2) { - $hm = $hm1; - } elseif (!$hm1) { - $hm = $hm2; - } else { - // Both matched. Select the longest. - $hm = mb_strlen($hm1[4]) > mb_strlen($hm2[4]) ? $hm1 : $hm2; - } - - // A half-match was found, sort out the return data. - if (mb_strlen($text1) > mb_strlen($text2)) { - $text1_a = $hm[0]; - $text1_b = $hm[1]; - $text2_a = $hm[2]; - $text2_b = $hm[3]; - } else { - $text2_a = $hm[0]; - $text2_b = $hm[1]; - $text1_a = $hm[2]; - $text1_b = $hm[3]; - } - $mid_common = $hm[4]; - return array( $text1_a, $text1_b, $text2_a, $text2_b, $mid_common ); - } - - /** - * Does a mb_substring of shorttext exist within longtext such that the mb_substring - * is at least half the length of longtext? - * Closure, but does not reference any external variables. - * @param {string} longtext Longer string. - * @param {string} shorttext Shorter string. - * @param {number} i Start index of quarter length mb_substring within longtext - * @return {Array.?} Five element Array, containing the prefix of - * longtext, the suffix of longtext, the prefix of shorttext, the suffix - * of shorttext and the common middle. Or null if there was no match. - * @private - */ - function diff_halfMatchI($longtext, $shorttext, $i) { - // Start with a 1/4 length mb_substring at position i as a seed. - $seed = mb_substr($longtext, $i, floor(mb_strlen($longtext) / 4)); - - $j = -1; - $best_common = ''; - $best_longtext_a = null; - $best_longtext_b = null; - $best_shorttext_a = null; - $best_shorttext_b = null; - while ( ($j = mb_strpos($shorttext, $seed, $j + 1)) !== false ) { - $prefixLength = $this->diff_commonPrefix(mb_substr($longtext, $i), mb_substr($shorttext, $j)); - $suffixLength = $this->diff_commonSuffix(mb_substr($longtext, 0, $i), mb_substr($shorttext, 0, $j)); - if (mb_strlen($best_common) < $suffixLength + $prefixLength) { - $best_common = mb_substr($shorttext, $j - $suffixLength, $suffixLength) . mb_substr($shorttext, $j, $prefixLength); - $best_longtext_a = mb_substr($longtext, 0, $i - $suffixLength); - $best_longtext_b = mb_substr($longtext, $i + $prefixLength); - $best_shorttext_a = mb_substr($shorttext, 0, $j - $suffixLength); - $best_shorttext_b = mb_substr($shorttext, $j + $prefixLength); - } - } - if (mb_strlen($best_common) >= mb_strlen($longtext) / 2) { - return array ( - $best_longtext_a, - $best_longtext_b, - $best_shorttext_a, - $best_shorttext_b, - $best_common - ); - } else { - return null; - } - } - - /** - * Reduce the number of edits by eliminating semantically trivial equalities. - * @param {Array.>} diffs Array of diff tuples. - */ - function diff_cleanupSemantic(&$diffs) { - $changes = false; - $equalities = array (); // Stack of indices where equalities are found. - $equalitiesLength = 0; // Keeping our own length var is faster in JS. - $lastequality = null; // Always equal to equalities[equalitiesLength-1][1] - $pointer = 0; // Index of current position. - // Number of characters that changed prior to the equality. - $length_changes1 = 0; - // Number of characters that changed after the equality. - $length_changes2 = 0; - while ($pointer < count($diffs)) { - if ($diffs[$pointer][0] == DIFF_EQUAL) { // equality found - $equalities[$equalitiesLength++] = $pointer; - $length_changes1 = $length_changes2; - $length_changes2 = 0; - $lastequality = $diffs[$pointer][1]; - } else { // an insertion or deletion - $length_changes2 += mb_strlen($diffs[$pointer][1]); - if ($lastequality !== null && (mb_strlen($lastequality) <= $length_changes1) && (mb_strlen($lastequality) <= $length_changes2)) { - // Duplicate record - $zzz_diffs = array_splice($diffs, $equalities[$equalitiesLength -1], 0, array(array ( - DIFF_DELETE, - $lastequality - ))); - // Change second copy to insert. - $diffs[$equalities[$equalitiesLength -1] + 1][0] = DIFF_INSERT; - // Throw away the equality we just deleted. - $equalitiesLength--; - // Throw away the previous equality (it needs to be reevaluated). - $equalitiesLength--; - $pointer = $equalitiesLength > 0 ? $equalities[$equalitiesLength -1] : -1; - $length_changes1 = 0; // Reset the counters. - $length_changes2 = 0; - $lastequality = null; - $changes = true; - } - } - $pointer++; - } - if ($changes) { - $this->diff_cleanupMerge($diffs); - } - $this->diff_cleanupSemanticLossless($diffs); - } - - /** - * Look for single edits surrounded on both sides by equalities - * which can be shifted sideways to align the edit to a word boundary. - * e.g: The cat came. -> The cat came. - * @param {Array.>} diffs Array of diff tuples. - */ - function diff_cleanupSemanticLossless(&$diffs) { - - $pointer = 1; - // Intentionally ignore the first and last element (don't need checking). - while ($pointer < count($diffs) - 1) { - if ($diffs[$pointer -1][0] == DIFF_EQUAL && $diffs[$pointer +1][0] == DIFF_EQUAL) { - // This is a single edit surrounded by equalities. - $equality1 = $diffs[$pointer -1][1]; - $edit = $diffs[$pointer][1]; - $equality2 = $diffs[$pointer +1][1]; - - // First, shift the edit as far left as possible. - $commonOffset = $this->diff_commonSuffix($equality1, $edit); - if ($commonOffset !== '') { - $commonString = mb_substr($edit, mb_strlen($edit) - $commonOffset); - $equality1 = mb_substr($equality1, 0, mb_strlen($equality1) - $commonOffset); - $edit = $commonString . mb_substr($edit, 0, mb_strlen($edit) - $commonOffset); - $equality2 = $commonString . $equality2; - } - - // Second, step character by character right, looking for the best fit. - $bestEquality1 = $equality1; - $bestEdit = $edit; - $bestEquality2 = $equality2; - $bestScore = $this->diff_cleanupSemanticScore($equality1, $edit) + $this->diff_cleanupSemanticScore($edit, $equality2); - while (isset($equality2[0]) && $edit[0] === $equality2[0]) { - $equality1 .= $edit[0]; - $edit = mb_substr($edit, 1) . $equality2[0]; - $equality2 = mb_substr($equality2, 1); - $score = $this->diff_cleanupSemanticScore($equality1, $edit) + $this->diff_cleanupSemanticScore($edit, $equality2); - // The >= encourages trailing rather than leading whitespace on edits. - if ($score >= $bestScore) { - $bestScore = $score; - $bestEquality1 = $equality1; - $bestEdit = $edit; - $bestEquality2 = $equality2; - } - } - - if ($diffs[$pointer -1][1] != $bestEquality1) { - // We have an improvement, save it back to the diff. - if ($bestEquality1) { - $diffs[$pointer -1][1] = $bestEquality1; - } else { - $zzz_diffs = array_splice($diffs, $pointer -1, 1); - $pointer--; - } - $diffs[$pointer][1] = $bestEdit; - if ($bestEquality2) { - $diffs[$pointer +1][1] = $bestEquality2; - } else { - $zzz_diffs = array_splice($diffs, $pointer +1, 1); - $pointer--; - } - } - } - $pointer++; - } - } - - /** - * Given two strings, compute a score representing whether the internal - * boundary falls on logical boundaries. - * Scores range from 5 (best) to 0 (worst). - * Closure, makes reference to regex patterns defined above. - * @param {string} one First string - * @param {string} two Second string - * @return {number} The score. - */ - function diff_cleanupSemanticScore($one, $two) { - // Define some regex patterns for matching boundaries. - $punctuation = '/[^a-zA-Z0-9]/'; - $whitespace = '/\s/'; - $linebreak = '/[\r\n]/'; - $blanklineEnd = '/\n\r?\n$/'; - $blanklineStart = '/^\r?\n\r?\n/'; - - if (!$one || !$two) { - // Edges are the best. - return 5; - } - - // Each port of this function behaves slightly differently due to - // subtle differences in each language's definition of things like - // 'whitespace'. Since this function's purpose is largely cosmetic, - // the choice has been made to use each language's native features - // rather than force total conformity. - $score = 0; - // One point for non-alphanumeric. - if (preg_match($punctuation, $one[mb_strlen($one) - 1]) || preg_match($punctuation, $two[0])) { - $score++; - // Two points for whitespace. - if (preg_match($whitespace, $one[mb_strlen($one) - 1] ) || preg_match($whitespace, $two[0])) { - $score++; - // Three points for line breaks. - if (preg_match($linebreak, $one[mb_strlen($one) - 1]) || preg_match($linebreak, $two[0])) { - $score++; - // Four points for blank lines. - if (preg_match($blanklineEnd, $one) || preg_match($blanklineStart, $two)) { - $score++; - } - } - } - } - return $score; - } - - /** - * Reduce the number of edits by eliminating operationally trivial equalities. - * @param {Array.>} diffs Array of diff tuples. - */ - function diff_cleanupEfficiency(&$diffs) { - $changes = false; - $equalities = array (); // Stack of indices where equalities are found. - $equalitiesLength = 0; // Keeping our own length var is faster in JS. - $lastequality = ''; // Always equal to equalities[equalitiesLength-1][1] - $pointer = 0; // Index of current position. - // Is there an insertion operation before the last equality. - $pre_ins = false; - // Is there a deletion operation before the last equality. - $pre_del = false; - // Is there an insertion operation after the last equality. - $post_ins = false; - // Is there a deletion operation after the last equality. - $post_del = false; - while ($pointer < count($diffs)) { - if ($diffs[$pointer][0] == DIFF_EQUAL) { // equality found - if (mb_strlen($diffs[$pointer][1]) < $this->Diff_EditCost && ($post_ins || $post_del)) { - // Candidate found. - $equalities[$equalitiesLength++] = $pointer; - $pre_ins = $post_ins; - $pre_del = $post_del; - $lastequality = $diffs[$pointer][1]; - } else { - // Not a candidate, and can never become one. - $equalitiesLength = 0; - $lastequality = ''; - } - $post_ins = $post_del = false; - } else { // an insertion or deletion - if ($diffs[$pointer][0] == DIFF_DELETE) { - $post_del = true; - } else { - $post_ins = true; - } - /* - * Five types to be split: - * ABXYCD - * AXCD - * ABXC - * AXCD - * ABXC - */ - if ($lastequality && (($pre_ins && $pre_del && $post_ins && $post_del) || ((mb_strlen($lastequality) < $this->Diff_EditCost / 2) && ($pre_ins + $pre_del + $post_ins + $post_del) == 3))) { - // Duplicate record - $zzz_diffs = array_splice($diffs, $equalities[$equalitiesLength -1], 0, array(array ( - DIFF_DELETE, - $lastequality - ))); - // Change second copy to insert. - $diffs[$equalities[$equalitiesLength -1] + 1][0] = DIFF_INSERT; - $equalitiesLength--; // Throw away the equality we just deleted; - $lastequality = ''; - if ($pre_ins && $pre_del) { - // No changes made which could affect previous entry, keep going. - $post_ins = $post_del = true; - $equalitiesLength = 0; - } else { - $equalitiesLength--; // Throw away the previous equality; - $pointer = $equalitiesLength > 0 ? $equalities[$equalitiesLength -1] : -1; - $post_ins = $post_del = false; - } - $changes = true; - } - } - $pointer++; - } - - if ($changes) { - $this->diff_cleanupMerge($diffs); - } - } - - /** - * Reorder and merge like edit sections. Merge equalities. - * Any edit section can move as long as it doesn't cross an equality. - * @param {Array.>} diffs Array of diff tuples. - */ - function diff_cleanupMerge(&$diffs) { - array_push($diffs, array ( DIFF_EQUAL, '' )); // Add a dummy entry at the end. - $pointer = 0; - $count_delete = 0; - $count_insert = 0; - $text_delete = ''; - $text_insert = ''; - $commonlength = null; - while ($pointer < count($diffs)) { - switch ($diffs[$pointer][0]) { - case DIFF_INSERT : - $count_insert++; - $text_insert .= $diffs[$pointer][1]; - $pointer++; - break; - case DIFF_DELETE : - $count_delete++; - $text_delete .= $diffs[$pointer][1]; - $pointer++; - break; - case DIFF_EQUAL : - // Upon reaching an equality, check for prior redundancies. - if ($count_delete !== 0 || $count_insert !== 0) { - if ($count_delete !== 0 && $count_insert !== 0) { - // Factor out any common prefixies. - $commonlength = $this->diff_commonPrefix($text_insert, $text_delete); - if ($commonlength !== 0) { - if (($pointer - $count_delete - $count_insert) > 0 && $diffs[$pointer - $count_delete - $count_insert -1][0] == DIFF_EQUAL) { - $diffs[$pointer - $count_delete - $count_insert -1][1] .= mb_substr($text_insert, 0, $commonlength); - } else { - array_splice($diffs, 0, 0, array(array ( - DIFF_EQUAL, - mb_substr($text_insert, 0, $commonlength) - ))); - $pointer++; - } - $text_insert = mb_substr($text_insert, $commonlength); - $text_delete = mb_substr($text_delete, $commonlength); - } - // Factor out any common suffixies. - $commonlength = $this->diff_commonSuffix($text_insert, $text_delete); - if ($commonlength !== 0) { - $diffs[$pointer][1] = mb_substr($text_insert, mb_strlen($text_insert) - $commonlength) . $diffs[$pointer][1]; - $text_insert = mb_substr($text_insert, 0, mb_strlen($text_insert) - $commonlength); - $text_delete = mb_substr($text_delete, 0, mb_strlen($text_delete) - $commonlength); - } - } - // Delete the offending records and add the merged ones. - if ($count_delete === 0) { - array_splice($diffs, $pointer-$count_delete-$count_insert, $count_delete+$count_insert, array(array( - DIFF_INSERT, - $text_insert - ))); - } elseif ($count_insert === 0) { - array_splice($diffs, $pointer-$count_delete-$count_insert, $count_delete+$count_insert, array(array( - DIFF_DELETE, - $text_delete - ))); - } else { - array_splice($diffs, $pointer-$count_delete-$count_insert, $count_delete+$count_insert, array(array( - DIFF_DELETE, - $text_delete - ), array ( - DIFF_INSERT, - $text_insert - ))); - } - $pointer = $pointer - $count_delete - $count_insert + ($count_delete ? 1 : 0) + ($count_insert ? 1 : 0) + 1; - } elseif ($pointer !== 0 && $diffs[$pointer -1][0] == DIFF_EQUAL) { - // Merge this equality with the previous one. - $diffs[$pointer -1][1] .= $diffs[$pointer][1]; - array_splice($diffs, $pointer, 1); - } else { - $pointer++; - } - $count_insert = 0; - $count_delete = 0; - $text_delete = ''; - $text_insert = ''; - break; - } - } - if ($diffs[count($diffs) - 1][1] === '') { - array_pop($diffs); // Remove the dummy entry at the end. - } - - // Second pass: look for single edits surrounded on both sides by equalities - // which can be shifted sideways to eliminate an equality. - // e.g: ABAC -> ABAC - $changes = false; - $pointer = 1; - // Intentionally ignore the first and last element (don't need checking). - while ($pointer < count($diffs) - 1) { - if ($diffs[$pointer-1][0] == DIFF_EQUAL && $diffs[$pointer+1][0] == DIFF_EQUAL) { - // This is a single edit surrounded by equalities. - if ( mb_substr($diffs[$pointer][1], mb_strlen($diffs[$pointer][1]) - mb_strlen($diffs[$pointer -1][1])) == $diffs[$pointer -1][1]) { - // Shift the edit over the previous equality. - $diffs[$pointer][1] = $diffs[$pointer -1][1] . mb_substr($diffs[$pointer][1], 0, mb_strlen($diffs[$pointer][1]) - mb_strlen($diffs[$pointer -1][1])); - $diffs[$pointer +1][1] = $diffs[$pointer -1][1] . $diffs[$pointer +1][1]; - array_splice($diffs, $pointer -1, 1); - $changes = true; - } elseif (mb_substr($diffs[$pointer][1], 0, mb_strlen($diffs[$pointer +1][1])) == $diffs[$pointer +1][1]) { - // Shift the edit over the next equality. - $diffs[$pointer -1][1] .= $diffs[$pointer +1][1]; - - $diffs[$pointer][1] = mb_substr($diffs[$pointer][1], mb_strlen($diffs[$pointer +1][1])) . $diffs[$pointer +1][1]; - array_splice($diffs, $pointer +1, 1); - $changes = true; - } - } - $pointer++; - } - // If shifts were made, the diff needs reordering and another shift sweep. - if ($changes) { - $this->diff_cleanupMerge($diffs); - } - } - - /** - * loc is a location in text1, compute and return the equivalent location in - * text2. - * e.g. 'The cat' vs 'The big cat', 1->1, 5->8 - * @param {Array.>} diffs Array of diff tuples. - * @param {number} loc Location within text1. - * @return {number} Location within text2. - */ - function diff_xIndex($diffs, $loc) { - $chars1 = 0; - $chars2 = 0; - $last_chars1 = 0; - $last_chars2 = 0; - for ($x = 0; $x < count($diffs); $x++) { - if ($diffs[$x][0] !== DIFF_INSERT) { // Equality or deletion. - $chars1 += mb_strlen($diffs[$x][1]); - } - if ($diffs[$x][0] !== DIFF_DELETE) { // Equality or insertion. - $chars2 += mb_strlen($diffs[$x][1]); - } - if ($chars1 > $loc) { // Overshot the location. - break; - } - $last_chars1 = $chars1; - $last_chars2 = $chars2; - } - // Was the location was deleted? - if (count($diffs) != $x && $diffs[$x][0] === DIFF_DELETE) { - return $last_chars2; - } - // Add the remaining character length. - return $last_chars2 + ($loc - $last_chars1); - } - - /** - * Convert a diff array into a pretty HTML report. - * @param {Array.>} diffs Array of diff tuples. - * @return {string} HTML representation. - */ - function diff_prettyHtml($diffs) { - $html = array (); - $i = 0; - for ($x = 0; $x < count($diffs); $x++) { - $op = $diffs[$x][0]; // Operation (insert, delete, equal) - $data = $diffs[$x][1]; // Text of change. - $text = preg_replace(array ( - '/&/', - '//', - "/\n/" - ), array ( - '&', - '<', - '>', - '¶
' - ), $data); - - switch ($op) { - case DIFF_INSERT : - $html[$x] = '' . $text . ''; - break; - case DIFF_DELETE : - $html[$x] = '' . $text . ''; - break; - case DIFF_EQUAL : - $html[$x] = '' . $text . ''; - break; - } - if ($op !== DIFF_DELETE) { - $i += mb_strlen($data); - } - } - return implode('',$html); - } - - /** - * Compute and return the source text (all equalities and deletions). - * @param {Array.>} diffs Array of diff tuples. - * @return {string} Source text. - */ - function diff_text1($diffs) { - $text = array (); - for ($x = 0; $x < count($diffs); $x++) { - if ($diffs[$x][0] !== DIFF_INSERT) { - $text[$x] = $diffs[$x][1]; - } - } - return implode('',$text); - } - - /** - * Compute and return the destination text (all equalities and insertions). - * @param {Array.>} diffs Array of diff tuples. - * @return {string} Destination text. - */ - function diff_text2($diffs) { - $text = array (); - for ($x = 0; $x < count($diffs); $x++) { - if ($diffs[$x][0] !== DIFF_DELETE) { - $text[$x] = $diffs[$x][1]; - } - } - return implode('',$text); - } - - /** - * Compute the Levenshtein distance; the number of inserted, deleted or - * substituted characters. - * @param {Array.>} diffs Array of diff tuples. - * @return {number} Number of changes. - */ - function diff_levenshtein($diffs) { - $levenshtein = 0; - $insertions = 0; - $deletions = 0; - for ($x = 0; $x < count($diffs); $x++) { - $op = $diffs[$x][0]; - $data = $diffs[$x][1]; - switch ($op) { - case DIFF_INSERT : - $insertions += mb_strlen($data); - break; - case DIFF_DELETE : - $deletions += mb_strlen($data); - break; - case DIFF_EQUAL : - // A deletion and an insertion is one substitution. - $levenshtein += max($insertions, $deletions); - $insertions = 0; - $deletions = 0; - break; - } - } - $levenshtein += max($insertions, $deletions); - return $levenshtein; - } - - /** - * Crush the diff into an encoded string which describes the operations - * required to transform text1 into text2. - * E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'. - * Operations are tab-separated. Inserted text is escaped using %xx notation. - * @param {Array.>} diffs Array of diff tuples. - * @return {string} Delta text. - */ - function diff_toDelta($diffs) { - $text = array (); - for ($x = 0; $x < count($diffs); $x++) { - switch ($diffs[$x][0]) { - case DIFF_INSERT : - $text[$x] = '+' .encodeURI($diffs[$x][1]); - break; - case DIFF_DELETE : - $text[$x] = '-' .mb_strlen($diffs[$x][1]); - break; - case DIFF_EQUAL : - $text[$x] = '=' .mb_strlen($diffs[$x][1]); - break; - } - } - return str_replace('%20', ' ', implode("\t", $text)); - } - - /** - * Given the original text1, and an encoded string which describes the - * operations required to transform text1 into text2, compute the full diff. - * @param {string} text1 Source string for the diff. - * @param {string} delta Delta text. - * @return {Array.>} Array of diff tuples. - * @throws {Error} If invalid input. - */ - function diff_fromDelta($text1, $delta) { - $diffs = array (); - $diffsLength = 0; // Keeping our own length var is faster in JS. - $pointer = 0; // Cursor in text1 - $tokens = preg_split("/\t/", $delta); - - for ($x = 0; $x < count($tokens); $x++) { - // Each token begins with a one character parameter which specifies the - // operation of this token (delete, insert, equality). - $param = mb_substr($tokens[$x], 1); - switch ($tokens[$x][0]) { - case '+' : - try { - $diffs[$diffsLength++] = array ( - DIFF_INSERT, - decodeURI($param) - ); - } catch (Exception $ex) { - echo_Exception('Illegal escape in diff_fromDelta: ' . $param); - // Malformed URI sequence. - } - break; - case '-' : - // Fall through. - case '=' : - $n = (int) $param; - if ($n < 0) { - echo_Exception('Invalid number in diff_fromDelta: ' . $param); - } - $text = mb_substr($text1, $pointer, $n); - $pointer += $n; - if ($tokens[$x][0] == '=') { - $diffs[$diffsLength++] = array ( - DIFF_EQUAL, - $text - ); - } else { - $diffs[$diffsLength++] = array ( - DIFF_DELETE, - $text - ); - } - break; - default : - // Blank tokens are ok (from a trailing \t). - // Anything else is an error. - if ($tokens[$x]) { - echo_Exception('Invalid diff operation in diff_fromDelta: ' . $tokens[$x]); - } - } - } - if ($pointer != mb_strlen($text1)) { -// throw new Exception('Delta length (' . $pointer . ') does not equal source text length (' . mb_strlen($text1) . ').'); - echo_Exception('Delta length (' . $pointer . ') does not equal source text length (' . mb_strlen($text1) . ').'); - } - return $diffs; - } - - // MATCH FUNCTIONS - - /** - * Locate the best instance of 'pattern' in 'text' near 'loc'. - * @param {string} text The text to search. - * @param {string} pattern The pattern to search for. - * @param {number} loc The location to search around. - * @return {number} Best match index or -1. - */ - function match_main($text, $pattern, $loc) { - $loc = max(0, min($loc, mb_strlen($text))); - if ($text == $pattern) { - // Shortcut (potentially not guaranteed by the algorithm) - return 0; - } - elseif (!mb_strlen($text)) { - // Nothing to match. - return -1; - } - elseif (mb_substr($text, $loc, mb_strlen($pattern)) == $pattern) { - // Perfect match at the perfect spot! (Includes case of null pattern) - return $loc; - } else { - // Do a fuzzy compare. - return $this->match_bitap($text, $pattern, $loc); - } - } - - /** - * Locate the best instance of 'pattern' in 'text' near 'loc' using the - * Bitap algorithm. - * @param {string} text The text to search. - * @param {string} pattern The pattern to search for. - * @param {number} loc The location to search around. - * @return {number} Best match index or -1. - * @private - */ - function match_bitap($text, $pattern, $loc) { - if (mb_strlen($pattern) > Match_MaxBits) { - echo_Exception('Pattern too long for this browser.'); - } - - // Initialise the alphabet. - $s = $this->match_alphabet($pattern); - - // Highest score beyond which we give up. - $score_threshold = $this->Match_Threshold; - - // Is there a nearby exact match? (speedup) - $best_loc = mb_strpos($text, $pattern, $loc); - if ($best_loc !== false) { - $score_threshold = min($this->match_bitapScore(0, $best_loc, $pattern, $loc), $score_threshold); - } - - // What about in the other direction? (speedup) - $best_loc = mb_strrpos( $text, $pattern, min($loc + mb_strlen($pattern), mb_strlen($text)) ); - if ($best_loc !== false) { - $score_threshold = min($this->match_bitapScore(0, $best_loc, $pattern, $loc), $score_threshold); - } - - // Initialise the bit arrays. - $matchmask = 1 << (mb_strlen($pattern) - 1); - $best_loc = -1; - - $bin_min = null; - $bin_mid = null; - $bin_max = mb_strlen($pattern) + mb_strlen($text); - $last_rd = null; - for ($d = 0; $d < mb_strlen($pattern); $d++) { - // Scan for the best match; each iteration allows for one more error. - // Run a binary search to determine how far from 'loc' we can stray at this - // error level. - $bin_min = 0; - $bin_mid = $bin_max; - while ($bin_min < $bin_mid) { - if ($this->match_bitapScore($d, $loc + $bin_mid, $pattern, $loc) <= $score_threshold) { - $bin_min = $bin_mid; - } else { - $bin_max = $bin_mid; - } - $bin_mid = floor(($bin_max - $bin_min) / 2 + $bin_min); - } - // Use the result from this iteration as the maximum for the next. - $bin_max = $bin_mid; - $start = max(1, $loc - $bin_mid +1); - $finish = min($loc + $bin_mid, mb_strlen($text)) + mb_strlen($pattern); - - $rd = Array ( - $finish +2 - ); - $rd[$finish +1] = (1 << $d) - 1; - for ($j = $finish; $j >= $start; $j--) { - // The alphabet (s) is a sparse hash, so the following line generates - // warnings. -@ $charMatch = $s[ $text[$j -1] ]; - if ($d === 0) { // First pass: exact match. - $rd[$j] = (($rd[$j +1] << 1) | 1) & $charMatch; - } else { // Subsequent passes: fuzzy match. - $rd[$j] = (($rd[$j +1] << 1) | 1) & $charMatch | ((($last_rd[$j +1] | $last_rd[$j]) << 1) | 1) | $last_rd[$j +1]; - } - if ($rd[$j] & $matchmask) { - $score = $this->match_bitapScore($d, $j -1, $pattern, $loc); - // This match will almost certainly be better than any existing match. - // But check anyway. - if ($score <= $score_threshold) { - // Told you so. - $score_threshold = $score; - $best_loc = $j -1; - if ($best_loc > $loc) { - // When passing loc, don't exceed our current distance from loc. - $start = max(1, 2 * $loc - $best_loc); - } else { - // Already passed loc, downhill from here on in. - break; - } - } - } - } - // No hope for a (better) match at greater error levels. - if ($this->match_bitapScore($d +1, $loc, $pattern, $loc) > $score_threshold) { - break; - } - $last_rd = $rd; - } - return (int)$best_loc; - } - - /** - * Compute and return the score for a match with e errors and x location. - * Accesses loc and pattern through being a closure. - * @param {number} e Number of errors in match. - * @param {number} x Location of match. - * @return {number} Overall score for match (0.0 = good, 1.0 = bad). - * @private - */ - function match_bitapScore($e, $x, $pattern, $loc) { - $accuracy = $e / mb_strlen($pattern); - $proximity = abs($loc - $x); - if (!$this->Match_Distance) { - // Dodge divide by zero error. - return $proximity ? 1.0 : $accuracy; - } - return $accuracy + ($proximity / $this->Match_Distance); - } - - /** - * Initialise the alphabet for the Bitap algorithm. - * @param {string} pattern The text to encode. - * @return {Object} Hash of character locations. - * @private - */ - function match_alphabet($pattern) { - $s = array (); - for ($i = 0; $i < mb_strlen($pattern); $i++) { - $s[ $pattern[$i] ] = 0; - } - for ($i = 0; $i < mb_strlen($pattern); $i++) { - $s[ $pattern[$i] ] |= 1 << (mb_strlen($pattern) - $i - 1); - } - return $s; - } - - // PATCH FUNCTIONS - - /** - * Increase the context until it is unique, - * but don't let the pattern expand beyond Match_MaxBits. - * @param {patch_obj} patch The patch to grow. - * @param {string} text Source text. - * @private - */ - function patch_addContext($patch, $text) { - $pattern = mb_substr($text, $patch->start2, $patch->length1 ); - $previousPattern = null; - $padding = 0; - $i = 0; - while ( - ( mb_strlen($pattern) === 0 // Javascript's indexOf/lastIndexOd return 0/strlen respectively if pattern = '' - || mb_strpos($text, $pattern) !== mb_strrpos($text, $pattern) - ) - && $pattern !== $previousPattern // avoid infinte loop - && mb_strlen($pattern) < Match_MaxBits - $this->Patch_Margin - $this->Patch_Margin ) { - $padding += $this->Patch_Margin; - $previousPattern = $pattern; - $pattern = mb_substr($text, max($patch->start2 - $padding,0), ($patch->start2 + $patch->length1 + $padding) - max($patch->start2 - $padding,0) ); - } - // Add one chunk for good luck. - $padding += $this->Patch_Margin; - // Add the prefix. - $prefix = mb_substr($text, max($patch->start2 - $padding,0), $patch->start2 - max($patch->start2 - $padding,0) ); - if ($prefix!=='') { - array_unshift($patch->diffs, array ( - DIFF_EQUAL, - $prefix - )); - } - // Add the suffix. - $suffix = mb_substr($text, $patch->start2 + $patch->length1, ($patch->start2 + $patch->length1 + $padding) - ($patch->start2 + $patch->length1) ); - if ($suffix!=='') { - array_push($patch->diffs, array ( - DIFF_EQUAL, - $suffix - )); - } - - // Roll back the start points. - $patch->start1 -= mb_strlen($prefix); - $patch->start2 -= mb_strlen($prefix); - // Extend the lengths. - $patch->length1 += mb_strlen($prefix) + mb_strlen($suffix); - $patch->length2 += mb_strlen($prefix) + mb_strlen($suffix); - } - - /** - * Compute a list of patches to turn text1 into text2. - * Use diffs if provided, otherwise compute it ourselves. - * There are four ways to call this function, depending on what data is - * available to the caller: - * Method 1: - * a = text1, b = text2 - * Method 2: - * a = diffs - * Method 3 (optimal): - * a = text1, b = diffs - * Method 4 (deprecated, use method 3): - * a = text1, b = text2, c = diffs - * - * @param {string|Array.>} a text1 (methods 1,3,4) or - * Array of diff tuples for text1 to text2 (method 2). - * @param {string|Array.>} opt_b text2 (methods 1,4) or - * Array of diff tuples for text1 to text2 (method 3) or undefined (method 2). - * @param {string|Array.>} opt_c Array of diff tuples for - * text1 to text2 (method 4) or undefined (methods 1,2,3). - * @return {Array.} Array of patch objects. - */ - function patch_make($a, $opt_b = null, $opt_c = null ) { - if (is_string($a) && is_string($opt_b) && $opt_c === null ) { - // Method 1: text1, text2 - // Compute diffs from text1 and text2. - $text1 = $a; - $diffs = $this->diff_main($text1, $opt_b, true); - if ( count($diffs) > 2) { - $this->diff_cleanupSemantic($diffs); - $this->diff_cleanupEfficiency($diffs); - } - } elseif ( is_array($a) && $opt_b === null && $opt_c === null) { - // Method 2: diffs - // Compute text1 from diffs. - $diffs = $a; - $text1 = $this->diff_text1($diffs); - } elseif ( is_string($a) && is_array($opt_b) && $opt_c === null) { - // Method 3: text1, diffs - $text1 = $a; - $diffs = $opt_b; - } elseif ( is_string($a) && is_string($opt_b) && is_array($opt_c) ) { - // Method 4: text1, text2, diffs - // text2 is not used. - $text1 = $a; - $diffs = $opt_c; - } else { - echo_Exception('Unknown call format to patch_make.'); - } - - if ( count($diffs) === 0) { - return array(); // Get rid of the null case. - } - $patches = array(); - $patch = new patch_obj(); - $patchDiffLength = 0; // Keeping our own length var is faster in JS. - $char_count1 = 0; // Number of characters into the text1 string. - $char_count2 = 0; // Number of characters into the text2 string. - // Start with text1 (prepatch_text) and apply the diffs until we arrive at - // text2 (postpatch_text). We recreate the patches one by one to determine - // context info. - $prepatch_text = $text1; - $postpatch_text = $text1; - for ($x = 0; $x < count($diffs); $x++) { - $diff_type = $diffs[$x][0]; - $diff_text = $diffs[$x][1]; - - if (!$patchDiffLength && $diff_type !== DIFF_EQUAL) { - // A new patch starts here. - $patch->start1 = $char_count1; - $patch->start2 = $char_count2; - } - - switch ($diff_type) { - case DIFF_INSERT : - $patch->diffs[$patchDiffLength++] = $diffs[$x]; - $patch->length2 += mb_strlen($diff_text); - $postpatch_text = mb_substr($postpatch_text, 0, $char_count2) . $diff_text . mb_substr($postpatch_text,$char_count2); - break; - case DIFF_DELETE : - $patch->length1 += mb_strlen($diff_text); - $patch->diffs[$patchDiffLength++] = $diffs[$x]; - $postpatch_text = mb_substr($postpatch_text, 0, $char_count2) . mb_substr($postpatch_text, $char_count2 + mb_strlen($diff_text) ); - break; - case DIFF_EQUAL : - if ( mb_strlen($diff_text) <= 2 * $this->Patch_Margin && $patchDiffLength && count($diffs) != $x + 1) { - // Small equality inside a patch. - $patch->diffs[$patchDiffLength++] = $diffs[$x]; - $patch->length1 += mb_strlen($diff_text); - $patch->length2 += mb_strlen($diff_text); - } elseif ( mb_strlen($diff_text) >= 2 * $this->Patch_Margin ) { - // Time for a new patch. - if ($patchDiffLength) { - $this->patch_addContext($patch, $prepatch_text); - array_push($patches,$patch); - $patch = new patch_obj(); - $patchDiffLength = 0; - // Unlike Unidiff, our patch lists have a rolling context. - // http://code.google.com/p/google-diff-match-patch/wiki/Unidiff - // Update prepatch text & pos to reflect the application of the - // just completed patch. - $prepatch_text = $postpatch_text; - $char_count1 = $char_count2; - } - } - break; - } - - // Update the current character count. - if ($diff_type !== DIFF_INSERT) { - $char_count1 += mb_strlen($diff_text); - } - if ($diff_type !== DIFF_DELETE) { - $char_count2 += mb_strlen($diff_text); - } - } - // Pick up the leftover patch if not empty. - if ($patchDiffLength) { - $this->patch_addContext($patch, $prepatch_text); - array_push($patches, $patch); - } - - return $patches; - } - - /** - * Given an array of patches, return another array that is identical. - * @param {Array.} patches Array of patch objects. - * @return {Array.} Array of patch objects. - */ - function patch_deepCopy($patches) { - // Making deep copies is hard in JavaScript. - $patchesCopy = array(); - for ($x = 0; $x < count($patches); $x++) { - $patch = $patches[$x]; - $patchCopy = new patch_obj(); - for ($y = 0; $y < count($patch->diffs); $y++) { - $patchCopy->diffs[$y] = $patch->diffs[$y]; // ?? . slice(); - } - $patchCopy->start1 = $patch->start1; - $patchCopy->start2 = $patch->start2; - $patchCopy->length1 = $patch->length1; - $patchCopy->length2 = $patch->length2; - $patchesCopy[$x] = $patchCopy; - } - return $patchesCopy; - } - - /** - * Merge a set of patches onto the text. Return a patched text, as well - * as a list of true/false values indicating which patches were applied. - * @param {Array.} patches Array of patch objects. - * @param {string} text Old text. - * @return {Array.>} Two element Array, containing the - * new text and an array of boolean values. - */ - function patch_apply($patches, $text) { - if ( count($patches) == 0) { - return array($text,array()); - } - - // Deep copy the patches so that no changes are made to originals. - $patches = $this->patch_deepCopy($patches); - - $nullPadding = $this->patch_addPadding($patches); - $text = $nullPadding . $text . $nullPadding; - - $this->patch_splitMax($patches); - // delta keeps track of the offset between the expected and actual location - // of the previous patch. If there are patches expected at positions 10 and - // 20, but the first patch was found at 12, delta is 2 and the second patch - // has an effective expected position of 22. - $delta = 0; - $results = array(); - for ($x = 0; $x < count($patches) ; $x++) { - $expected_loc = $patches[$x]->start2 + $delta; - $text1 = $this->diff_text1($patches[$x]->diffs); - $start_loc = null; - $end_loc = -1; - if (mb_strlen($text1) > Match_MaxBits) { - // patch_splitMax will only provide an oversized pattern in the case of - // a monster delete. - $start_loc = $this->match_main($text, mb_substr($text1, 0, Match_MaxBits ), $expected_loc); - if ($start_loc != -1) { - $end_loc = $this->match_main($text, mb_substr($text1,mb_strlen($text1) - Match_MaxBits), $expected_loc + mb_strlen($text1) - Match_MaxBits); - if ($end_loc == -1 || $start_loc >= $end_loc) { - // Can't find valid trailing context. Drop this patch. - $start_loc = -1; - } - } - } else { - $start_loc = $this->match_main($text, $text1, $expected_loc); - } - if ($start_loc == -1) { - // No match found. :( - $results[$x] = false; - // Subtract the delta for this failed patch from subsequent patches. - $delta -= $patches[$x]->length2 - $patches[$x]->length1; - } else { - // Found a match. :) - $results[$x] = true; - $delta = $start_loc - $expected_loc; - $text2 = null; - if ($end_loc == -1) { - $text2 = mb_substr($text, $start_loc, mb_strlen($text1) ); - } else { - $text2 = mb_substr($text, $start_loc, $end_loc + Match_MaxBits - $start_loc); - } - if ($text1 == $text2) { - // Perfect match, just shove the replacement text in. - $text = mb_substr($text, 0, $start_loc) . $this->diff_text2($patches[$x]->diffs) . mb_substr($text,$start_loc + mb_strlen($text1) ); - } else { - // Imperfect match. Run a diff to get a framework of equivalent - // indices. - $diffs = $this->diff_main($text1, $text2, false); - if ( mb_strlen($text1) > Match_MaxBits && $this->diff_levenshtein($diffs) / mb_strlen($text1) > $this->Patch_DeleteThreshold) { - // The end points match, but the content is unacceptably bad. - $results[$x] = false; - } else { - $this->diff_cleanupSemanticLossless($diffs); - $index1 = 0; - $index2 = NULL; - for ($y = 0; $y < count($patches[$x]->diffs); $y++) { - $mod = $patches[$x]->diffs[$y]; - if ($mod[0] !== DIFF_EQUAL) { - $index2 = $this->diff_xIndex($diffs, $index1); - } - if ($mod[0] === DIFF_INSERT) { // Insertion - $text = mb_substr($text, 0, $start_loc + $index2) . $mod[1] . mb_substr($text, $start_loc + $index2); - } elseif ($mod[0] === DIFF_DELETE) { // Deletion - $text = mb_substr($text, 0, $start_loc + $index2) . mb_substr($text,$start_loc + $this->diff_xIndex($diffs, $index1 + mb_strlen($mod[1]) )); - } - if ($mod[0] !== DIFF_DELETE) { - $index1 += mb_strlen($mod[1]); - } - } - } - } - } - } - // Strip the padding off. - $text = mb_substr($text, mb_strlen($nullPadding), mb_strlen($text) - 2*mb_strlen($nullPadding) ); - return array($text, $results); - } - - /** - * Add some padding on text start and end so that edges can match something. - * Intended to be called only from within patch_apply. - * @param {Array.} patches Array of patch objects. - * @return {string} The padding string added to each side. - */ - function patch_addPadding(&$patches){ - $paddingLength = $this->Patch_Margin; - $nullPadding = ''; - for ($x = 1; $x <= $paddingLength; $x++) { - $nullPadding .= mb_chr($x); - } - - // Bump all the patches forward. - for ($x = 0; $x < count($patches); $x++) { - $patches[$x]->start1 += $paddingLength; - $patches[$x]->start2 += $paddingLength; - } - - // Add some padding on start of first diff. - $patch = &$patches[0]; - $diffs = &$patch->diffs; - if (count($diffs) == 0 || $diffs[0][0] != DIFF_EQUAL) { - // Add nullPadding equality. - array_unshift($diffs, array(DIFF_EQUAL, $nullPadding)); - $patch->start1 -= $paddingLength; // Should be 0. - $patch->start2 -= $paddingLength; // Should be 0. - $patch->length1 += $paddingLength; - $patch->length2 += $paddingLength; - } elseif ($paddingLength > mb_strlen($diffs[0][1]) ) { - // Grow first equality. - $extraLength = $paddingLength - mb_strlen($diffs[0][1]); - $diffs[0][1] = mb_substr( $nullPadding , mb_strlen($diffs[0][1]) ) . $diffs[0][1]; - $patch->start1 -= $extraLength; - $patch->start2 -= $extraLength; - $patch->length1 += $extraLength; - $patch->length2 += $extraLength; - } - - // Add some padding on end of last diff. - $patch = &$patches[count($patches) - 1]; - $diffs = &$patch->diffs; - if ( count($diffs) == 0 || $diffs[ count($diffs) - 1][0] != DIFF_EQUAL) { - // Add nullPadding equality. - array_push($diffs, array(DIFF_EQUAL, $nullPadding) ); - $patch->length1 += $paddingLength; - $patch->length2 += $paddingLength; - } elseif ($paddingLength > mb_strlen( $diffs[count($diffs)-1][1] ) ) { - // Grow last equality. - $extraLength = $paddingLength - mb_strlen( $diffs[count($diffs)-1][1] ); - $diffs[ count($diffs)-1][1] .= mb_substr($nullPadding,0,$extraLength); - $patch->length1 += $extraLength; - $patch->length2 += $extraLength; - } - - return $nullPadding; - } - - /** - * Look through the patches and break up any which are longer than the maximum - * limit of the match algorithm. - * @param {Array.} patches Array of patch objects. - */ - function patch_splitMax(&$patches) { - for ($x = 0; $x < count($patches); $x++) { - if ( $patches[$x]->length1 > Match_MaxBits) { - $bigpatch = $patches[$x]; - // Remove the big old patch. - array_splice($patches,$x--,1); - $patch_size = Match_MaxBits; - $start1 = $bigpatch->start1; - $start2 = $bigpatch->start2; - $precontext = ''; - while ( count($bigpatch->diffs) !== 0) { - // Create one of several smaller patches. - $patch = new patch_obj(); - $empty = true; - $patch->start1 = $start1 - mb_strlen($precontext); - $patch->start2 = $start2 - mb_strlen($precontext); - if ($precontext !== '') { - $patch->length1 = $patch->length2 = mb_strlen($precontext); - array_push($patch->diffs, array(DIFF_EQUAL, $precontext) ); - } - while ( count($bigpatch->diffs) !== 0 && $patch->length1 < $patch_size - $this->Patch_Margin) { - $diff_type = $bigpatch->diffs[0][0]; - $diff_text = $bigpatch->diffs[0][1]; - if ($diff_type === DIFF_INSERT) { - // Insertions are harmless. - $patch->length2 += mb_strlen($diff_text); - $start2 += mb_strlen($diff_text); - array_push($patch->diffs, array_shift($bigpatch->diffs) ); - $empty = false; - } else - if ($diff_type === DIFF_DELETE && count($patch->diffs) == 1 && $patch->diffs[0][0] == DIFF_EQUAL && (mb_strlen($diff_text) > 2 * $patch_size) ) { - // This is a large deletion. Let it pass in one chunk. - $patch->length1 += mb_strlen($diff_text); - $start1 += mb_strlen($diff_text); - $empty = false; - array_push( $patch->diffs, array($diff_type, $diff_text) ); - array_shift($bigpatch->diffs); - } else { - // Deletion or equality. Only take as much as we can stomach. - $diff_text = mb_substr($diff_text, 0, $patch_size - $patch->length1 - $this->Patch_Margin); - $patch->length1 += mb_strlen($diff_text); - $start1 += mb_strlen($diff_text); - if ($diff_type === DIFF_EQUAL) { - $patch->length2 += mb_strlen($diff_text); - $start2 += mb_strlen($diff_text); - } else { - $empty = false; - } - array_push($patch->diffs, array($diff_type, $diff_text) ); - if ($diff_text == $bigpatch->diffs[0][1]) { - array_shift($bigpatch->diffs); - } else { - $bigpatch->diffs[0][1] = mb_substr( $bigpatch->diffs[0][1],mb_strlen($diff_text) ); - } - } - } - // Compute the head context for the next patch. - $precontext = $this->diff_text2($patch->diffs); - $precontext = mb_substr($precontext, mb_strlen($precontext)-$this->Patch_Margin); - // Append the end context for this patch. - $postcontext = mb_substr( $this->diff_text1($bigpatch->diffs), 0, $this->Patch_Margin ); - if ($postcontext !== '') { - $patch->length1 += mb_strlen($postcontext); - $patch->length2 += mb_strlen($postcontext); - if ( count($patch->diffs) !== 0 && $patch->diffs[ count($patch->diffs) - 1][0] === DIFF_EQUAL) { - $patch->diffs[ count($patch->diffs) - 1][1] .= $postcontext; - } else { - array_push($patch->diffs, array(DIFF_EQUAL, $postcontext)); - } - } - if (!$empty) { - array_splice($patches, ++$x, 0, array($patch)); - } - } - } - } - } - - /** - * Take a list of patches and return a textual representation. - * @param {Array.} patches Array of patch objects. - * @return {string} Text representation of patches. - */ - function patch_toText($patches) { - $text = array(); - for ($x = 0; $x < count($patches) ; $x++) { - $text[$x] = $patches[$x]; - } - return implode('',$text); - } - - /** - * Parse a textual representation of patches and return a list of patch objects. - * @param {string} textline Text representation of patches. - * @return {Array.} Array of patch objects. - * @throws {Error} If invalid input. - */ - function patch_fromText($textline) { - $patches = array(); - if ($textline === '') { - return $patches; - } - $text = explode("\n",$textline); - foreach($text as $i=>$t){ if($t===''){ unset($text[$i]); } } - $textPointer = 0; - while ($textPointer < count($text) ) { - $m = null; - preg_match('/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/',$text[$textPointer],$m); - if (!$m) { - echo_Exception('Invalid patch string: ' . $text[$textPointer]); - } - $patch = new patch_obj(); - array_push($patches, $patch); - @$patch->start1 = (int)$m[1]; - if (@$m[2] === '') { - $patch->start1--; - $patch->length1 = 1; - } elseif ( @$m[2] == '0') { - $patch->length1 = 0; - } else { - $patch->start1--; - @$patch->length1 = (int)$m[2]; - } - - @$patch->start2 = (int)$m[3]; - if (@$m[4] === '') { - $patch->start2--; - $patch->length2 = 1; - } elseif ( @$m[4] == '0') { - $patch->length2 = 0; - } else { - $patch->start2--; - @$patch->length2 = (int)$m[4]; - } - $textPointer++; - - while ($textPointer < count($text) ) { - $sign = $text[$textPointer][0]; - try { - $line = decodeURI( mb_substr($text[$textPointer],1) ); - } catch (Exception $ex) { - // Malformed URI sequence. - throw new Exception('Illegal escape in patch_fromText: ' . $line); - } - if ($sign == '-') { - // Deletion. - array_push( $patch->diffs, array(DIFF_DELETE, $line) ); - } elseif ($sign == '+') { - // Insertion. - array_push($patch->diffs, array(DIFF_INSERT, $line) ); - } elseif ($sign == ' ') { - // Minor equality. - array_push($patch->diffs, array(DIFF_EQUAL, $line) ); - } elseif ($sign == '@') { - // Start of next patch. - break; - } elseif ($sign === '') { - // Blank line? Whatever. - } else { - // WTF? - echo_Exception('Invalid patch mode "' . $sign . '" in: ' . $line); - } - $textPointer++; - } - } - return $patches; - } -} - -/** - * Class representing one patch operation. - * @constructor - */ -class patch_obj { - /** @type {Array.>} */ - public $diffs = array(); - /** @type {number?} */ - public $start1 = null; - /** @type {number?} */ - public $start2 = null; - /** @type {number} */ - public $length1 = 0; - /** @type {number} */ - public $length2 = 0; - - /** - * Emmulate GNU diff's format. - * Header: @@ -382,8 +481,9 @@ - * Indicies are printed as 1-based, not 0-based. - * @return {string} The GNU diff string. - */ - function toString() { - if ($this->length1 === 0) { - $coords1 = $this->start1 . ',0'; - } elseif ($this->length1 == 1) { - $coords1 = $this->start1 + 1; - } else { - $coords1 = ($this->start1 + 1) . ',' . $this->length1; - } - if ($this->length2 === 0) { - $coords2 = $this->start2 . ',0'; - } elseif ($this->length2 == 1) { - $coords2 = $this->start2 + 1; - } else { - $coords2 = ($this->start2 + 1) . ',' . $this->length2; - } - $text = array ( '@@ -' . $coords1 . ' +' . $coords2 . " @@\n" ); - - // Escape the body of the patch with %xx notation. - for ($x = 0; $x < count($this->diffs); $x++) { - switch ($this->diffs[$x][0]) { - case DIFF_INSERT : - $op = '+'; - break; - case DIFF_DELETE : - $op = '-'; - break; - case DIFF_EQUAL : - $op = ' '; - break; - } - $text[$x +1] = $op . encodeURI($this->diffs[$x][1]) . "\n"; - } - return str_replace('%20', ' ', implode('',$text)); - } - function __toString(){ - return $this->toString(); - } -} - -define('DIFF_DELETE', -1); -define('DIFF_INSERT', 1); -define('DIFF_EQUAL', 0); - -define('Match_MaxBits', PHP_INT_SIZE * 8); - - -function charCodeAt($str, $pos) { - return mb_ord(mb_substr($str, $pos, 1)); -} -function mb_ord($v) { - $k = mb_convert_encoding($v, 'UCS-2LE', 'UTF-8'); - $k1 = ord(substr($k, 0, 1)); - $k2 = ord(substr($k, 1, 1)); - return $k2 * 256 + $k1; -} -function mb_chr($num){ - return mb_convert_encoding('&#'.intval($num).';', 'UTF-8', 'HTML-ENTITIES'); -} - -/** - * as in javascript encodeURI() following the MDN description - * - * @link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/encodeURI - * @param $url - * @return string - */ -function encodeURI($url) { - return strtr(rawurlencode($url), array ( - '%3B' => ';', '%2C' => ',', '%2F' => '/', '%3F' => '?', '%3A' => ':', '%40' => '@', '%26' => '&', '%3D' => '=', - '%2B' => '+', '%24' => '$', '%21' => '!', '%2A' => '*', '%27' => '\'', '%28' => '(', '%29' => ')', '%23' => '#', - )); -} - -function decodeURI($encoded) { - static $dontDecode; - if (!$dontDecode) { - $table = array ( - '%3B' => ';', '%2C' => ',', '%2F' => '/', '%3F' => '?', '%3A' => ':', '%40' => '@', '%26' => '&', '%3D' => '=', - '%2B' => '+', '%24' => '$', '%21' => '!', '%2A' => '*', '%27' => '\'', '%28' => '(', '%29' => ')', '%23' => '#', - ); - $dontDecode = array(); - foreach ($table as $k => $v) { - $dontDecode[$k] = encodeURI($k); - } - } - return rawurldecode(strtr($encoded, $dontDecode)); -} - -function echo_Exception($str){ - global $lastException; - $lastException = $str; - echo $str; -} -//mb_internal_encoding("UTF-8"); - -?> \ No newline at end of file diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 7625e174cb..e5f124fcf3 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,11 +9,11 @@ return array( 'names' => array( 'conpherence.pkg.css' => '0e3cf785', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '0ae696de', - 'core.pkg.js' => '68f29322', + 'core.pkg.css' => 'b816811e', + 'core.pkg.js' => 'd2de90d9', 'dark-console.pkg.js' => '187792c2', 'differential.pkg.css' => 'ffb69e3d', - 'differential.pkg.js' => '8deec4cd', + 'differential.pkg.js' => 'c60bec1b', 'diffusion.pkg.css' => '42c75c37', 'diffusion.pkg.js' => '78c9885d', 'maniphest.pkg.css' => '35995d6d', @@ -104,10 +104,6 @@ return array( 'rsrc/css/application/project/project-card-view.css' => 'a9f2c2dd', 'rsrc/css/application/project/project-triggers.css' => 'cd9c8bb9', 'rsrc/css/application/project/project-view.css' => '567858b3', - 'rsrc/css/application/releeph/releeph-core.css' => 'f81ff2db', - 'rsrc/css/application/releeph/releeph-preview-branch.css' => '22db5c07', - 'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '0ac1ea31', - 'rsrc/css/application/releeph/releeph-request-typeahead.css' => 'bce37359', 'rsrc/css/application/search/application-search-view.css' => '0f7c06d8', 'rsrc/css/application/search/search-results.css' => '9ea70ace', 'rsrc/css/application/slowvote/slowvote.css' => '1694baed', @@ -151,7 +147,7 @@ return array( 'rsrc/css/phui/phui-comment-form.css' => '68a2d99a', 'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0', 'rsrc/css/phui/phui-crumbs-view.css' => '614f43cf', - 'rsrc/css/phui/phui-curtain-object-ref-view.css' => '5f752bdb', + 'rsrc/css/phui/phui-curtain-object-ref-view.css' => '51d93266', 'rsrc/css/phui/phui-curtain-view.css' => '68c5efb6', 'rsrc/css/phui/phui-document-pro.css' => 'b9613a10', 'rsrc/css/phui/phui-document-summary.css' => 'b068eed1', @@ -171,7 +167,7 @@ return array( 'rsrc/css/phui/phui-invisible-character-view.css' => 'c694c4a4', 'rsrc/css/phui/phui-left-right.css' => '68513c34', 'rsrc/css/phui/phui-lightbox.css' => '4ebf22da', - 'rsrc/css/phui/phui-list.css' => '2f253c22', + 'rsrc/css/phui/phui-list.css' => '0c04affd', 'rsrc/css/phui/phui-object-box.css' => 'b8d7eea0', 'rsrc/css/phui/phui-pager.css' => 'd022c7ad', 'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8', @@ -246,7 +242,7 @@ return array( 'rsrc/externals/javelin/ext/view/__tests__/ViewInterpreter.js' => 'a9f35511', 'rsrc/externals/javelin/ext/view/__tests__/ViewRenderer.js' => '3a1b81f6', 'rsrc/externals/javelin/lib/Cookie.js' => '05d290ef', - 'rsrc/externals/javelin/lib/DOM.js' => '94681e22', + 'rsrc/externals/javelin/lib/DOM.js' => 'e4c7622a', 'rsrc/externals/javelin/lib/History.js' => '030b4f7a', 'rsrc/externals/javelin/lib/JSON.js' => '541f81c3', 'rsrc/externals/javelin/lib/Leader.js' => '0d2490ce', @@ -435,9 +431,6 @@ return array( 'rsrc/js/application/projects/behavior-project-boards.js' => '58cb6a88', 'rsrc/js/application/projects/behavior-project-create.js' => '34c53422', 'rsrc/js/application/projects/behavior-reorder-columns.js' => '8ac32fd9', - 'rsrc/js/application/releeph/releeph-preview-branch.js' => '75184d68', - 'rsrc/js/application/releeph/releeph-request-state-change.js' => '9f081f05', - 'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'aa3a100c', 'rsrc/js/application/repository/repository-crossreference.js' => '44d48cd1', 'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e5bdb730', 'rsrc/js/application/search/behavior-reorder-queries.js' => 'b86f297f', @@ -480,8 +473,8 @@ return array( 'rsrc/js/core/behavior-copy.js' => 'cf32921f', 'rsrc/js/core/behavior-detect-timezone.js' => '78bc5d94', 'rsrc/js/core/behavior-device.js' => 'ac2b1e01', - 'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '7ad020a5', - 'rsrc/js/core/behavior-fancy-datepicker.js' => 'e77fcc9d', + 'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '3277c62d', + 'rsrc/js/core/behavior-fancy-datepicker.js' => 'b545d0a0', 'rsrc/js/core/behavior-form.js' => '55d7b788', 'rsrc/js/core/behavior-gesture.js' => 'b58d1a2a', 'rsrc/js/core/behavior-global-drag-and-drop.js' => '1cab0e9a', @@ -596,7 +589,7 @@ return array( 'javelin-behavior-aphlict-listen' => '4e61fa88', 'javelin-behavior-aphlict-status' => 'c3703a16', 'javelin-behavior-aphront-basic-tokenizer' => '3b4899b0', - 'javelin-behavior-aphront-drag-and-drop-textarea' => '7ad020a5', + 'javelin-behavior-aphront-drag-and-drop-textarea' => '3277c62d', 'javelin-behavior-aphront-form-disable-on-submit' => '55d7b788', 'javelin-behavior-aphront-more' => '506aa3f4', 'javelin-behavior-audio-source' => '3dc5ad43', @@ -635,7 +628,7 @@ return array( 'javelin-behavior-editengine-reorder-configs' => '4842f137', 'javelin-behavior-editengine-reorder-fields' => '0ad8d31f', 'javelin-behavior-event-all-day' => '0b1bc990', - 'javelin-behavior-fancy-datepicker' => 'e77fcc9d', + 'javelin-behavior-fancy-datepicker' => 'b545d0a0', 'javelin-behavior-global-drag-and-drop' => '1cab0e9a', 'javelin-behavior-harbormaster-log' => 'b347a301', 'javelin-behavior-herald-rule-editor' => '0922e81d', @@ -686,9 +679,6 @@ return array( 'javelin-behavior-read-only-warning' => 'b9109f8f', 'javelin-behavior-redirect' => '407ee861', 'javelin-behavior-refresh-csrf' => '46116c01', - 'javelin-behavior-releeph-preview-branch' => '75184d68', - 'javelin-behavior-releeph-request-state-change' => '9f081f05', - 'javelin-behavior-releeph-request-typeahead' => 'aa3a100c', 'javelin-behavior-remarkup-load-image' => '202bfa3f', 'javelin-behavior-remarkup-preview' => 'd8a86cfb', 'javelin-behavior-reorder-applications' => 'aa371860', @@ -717,7 +707,7 @@ return array( 'javelin-color' => '78f811c9', 'javelin-cookie' => '05d290ef', 'javelin-diffusion-locate-file-source' => '94243d89', - 'javelin-dom' => '94681e22', + 'javelin-dom' => 'e4c7622a', 'javelin-dynval' => '202a2e85', 'javelin-event' => 'c03f2fb4', 'javelin-external-editor-link-engine' => '48a8641f', @@ -848,7 +838,7 @@ return array( 'phui-comment-form-css' => '68a2d99a', 'phui-comment-panel-css' => 'ec4e31c0', 'phui-crumbs-view-css' => '614f43cf', - 'phui-curtain-object-ref-view-css' => '5f752bdb', + 'phui-curtain-object-ref-view-css' => '51d93266', 'phui-curtain-view-css' => '68c5efb6', 'phui-document-summary-view-css' => 'b068eed1', 'phui-document-view-css' => '52b748a5', @@ -872,7 +862,7 @@ return array( 'phui-invisible-character-view-css' => 'c694c4a4', 'phui-left-right-css' => '68513c34', 'phui-lightbox-css' => '4ebf22da', - 'phui-list-view-css' => '2f253c22', + 'phui-list-view-css' => '0c04affd', 'phui-object-box-css' => 'b8d7eea0', 'phui-oi-big-ui-css' => 'fa74cc35', 'phui-oi-color-css' => 'b517bfa0', @@ -913,10 +903,6 @@ return array( 'project-card-view-css' => 'a9f2c2dd', 'project-triggers-css' => 'cd9c8bb9', 'project-view-css' => '567858b3', - 'releeph-core' => 'f81ff2db', - 'releeph-preview-branch' => '22db5c07', - 'releeph-request-differential-create-dialog' => '0ac1ea31', - 'releeph-request-typeahead-css' => 'bce37359', 'setup-issue-css' => '5eed85b2', 'sprite-login-css' => '18b368a6', 'sprite-tokens-css' => 'f1896dc5', @@ -1209,6 +1195,13 @@ return array( 'javelin-install', 'javelin-util', ), + '3277c62d' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-json', + 'phabricator-drag-and-drop-file-upload', + 'phabricator-textareautils', + ), '32db8374' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1599,12 +1592,6 @@ return array( 'javelin-behavior', 'javelin-dom', ), - '75184d68' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-uri', - 'javelin-request', - ), '78bc5d94' => array( 'javelin-behavior', 'javelin-uri', @@ -1617,12 +1604,6 @@ return array( 'javelin-install', 'javelin-dom', ), - '7ad020a5' => array( - 'javelin-behavior', - 'javelin-dom', - 'phabricator-drag-and-drop-file-upload', - 'phabricator-textareautils', - ), '7b139193' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1774,13 +1755,6 @@ return array( 'javelin-uri', 'javelin-routable', ), - '94681e22' => array( - 'javelin-magical-init', - 'javelin-install', - 'javelin-util', - 'javelin-vector', - 'javelin-stratcom', - ), '9623adc1' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1821,14 +1795,6 @@ return array( 'javelin-uri', 'phabricator-textareautils', ), - '9f081f05' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'javelin-workflow', - 'javelin-util', - 'phabricator-keyboard-shortcut', - ), 'a17b84f1' => array( 'javelin-behavior', 'javelin-dom', @@ -1896,13 +1862,6 @@ return array( 'javelin-dom', 'phabricator-draggable-list', ), - 'aa3a100c' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-typeahead', - 'javelin-typeahead-ondemand-source', - 'javelin-dom', - ), 'aa51efb4' => array( 'javelin-dom', ), @@ -1987,6 +1946,13 @@ return array( 'b517bfa0' => array( 'phui-oi-list-view-css', ), + 'b545d0a0' => array( + 'javelin-behavior', + 'javelin-util', + 'javelin-dom', + 'javelin-stratcom', + 'javelin-vector', + ), 'b557770a' => array( 'javelin-install', 'javelin-util', @@ -2160,6 +2126,13 @@ return array( 'javelin-dom', 'phuix-dropdown-menu', ), + 'e4c7622a' => array( + 'javelin-magical-init', + 'javelin-install', + 'javelin-util', + 'javelin-vector', + 'javelin-stratcom', + ), 'e5bdb730' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2167,13 +2140,6 @@ return array( 'javelin-dom', 'phabricator-draggable-list', ), - 'e77fcc9d' => array( - 'javelin-behavior', - 'javelin-util', - 'javelin-dom', - 'javelin-stratcom', - 'javelin-vector', - ), 'e8240b50' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/resources/sprite/login_1x/MediaWiki.png b/resources/sprite/login_1x/MediaWiki.png index 0ea13b0509..64fa50d972 100644 Binary files a/resources/sprite/login_1x/MediaWiki.png and b/resources/sprite/login_1x/MediaWiki.png differ diff --git a/resources/sprite/login_2x/MediaWiki.png b/resources/sprite/login_2x/MediaWiki.png index bd1c0f6d18..db64d841eb 100644 Binary files a/resources/sprite/login_2x/MediaWiki.png and b/resources/sprite/login_2x/MediaWiki.png differ diff --git a/resources/sprite/manifest/login.json b/resources/sprite/manifest/login.json index e9bbb9f2cb..59312820e1 100644 --- a/resources/sprite/manifest/login.json +++ b/resources/sprite/manifest/login.json @@ -59,7 +59,7 @@ "login-MediaWiki": { "name": "login-MediaWiki", "rule": ".login-MediaWiki", - "hash": "f1f0a9382434081a9a84e7584828c2dd" + "hash": "68eba44e85ea942ecf14d3c08992a2e2" }, "login-PayPal": { "name": "login-PayPal", diff --git a/resources/sql/autopatches/20140904.macroattach.php b/resources/sql/autopatches/20140904.macroattach.php index 4761964758..4c4f4e8494 100644 --- a/resources/sql/autopatches/20140904.macroattach.php +++ b/resources/sql/autopatches/20140904.macroattach.php @@ -16,7 +16,7 @@ foreach (new LiskMigrationIterator($table) as $macro) { foreach ($phids as $phid) { $editor->addEdge( $macro->getPHID(), - PhabricatorObjectHasFileEdgeType::EDGECONST, + 25, $phid); } $editor->save(); diff --git a/resources/sql/autopatches/20210625.owners.01.authority.sql b/resources/sql/autopatches/20210625.owners.01.authority.sql new file mode 100644 index 0000000000..96c36e7701 --- /dev/null +++ b/resources/sql/autopatches/20210625.owners.01.authority.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_owners.owners_package + ADD authorityMode VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20210625.owners.02.authority-default.sql b/resources/sql/autopatches/20210625.owners.02.authority-default.sql new file mode 100644 index 0000000000..98cd939777 --- /dev/null +++ b/resources/sql/autopatches/20210625.owners.02.authority-default.sql @@ -0,0 +1,3 @@ +UPDATE {$NAMESPACE}_owners.owners_package + SET authorityMode = 'strong' + WHERE authorityMode = ''; diff --git a/resources/sql/autopatches/20210713.harborcommand.01.migrate.sql b/resources/sql/autopatches/20210713.harborcommand.01.migrate.sql new file mode 100644 index 0000000000..7acf3b33c8 --- /dev/null +++ b/resources/sql/autopatches/20210713.harborcommand.01.migrate.sql @@ -0,0 +1,4 @@ +INSERT IGNORE INTO {$NAMESPACE}_harbormaster.harbormaster_buildmessage + (authorPHID, receiverPHID, type, isConsumed, dateCreated, dateModified) + SELECT authorPHID, targetPHID, command, 0, dateCreated, dateModified + FROM {$NAMESPACE}_harbormaster.harbormaster_buildcommand; diff --git a/resources/sql/autopatches/20210713.harborcommand.02.drop.sql b/resources/sql/autopatches/20210713.harborcommand.02.drop.sql new file mode 100644 index 0000000000..0d04376c13 --- /dev/null +++ b/resources/sql/autopatches/20210713.harborcommand.02.drop.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS {$NAMESPACE}_harbormaster.harbormaster_buildcommand; diff --git a/resources/sql/autopatches/20210715.harborcommand.01.xactions.php b/resources/sql/autopatches/20210715.harborcommand.01.xactions.php new file mode 100644 index 0000000000..05cd6d4744 --- /dev/null +++ b/resources/sql/autopatches/20210715.harborcommand.01.xactions.php @@ -0,0 +1,34 @@ +establishConnection('w'); +$row_iterator = new LiskRawMigrationIterator( + $xactions_conn, + $xactions_table->getTableName()); + +$map = array( + '"pause"' => 'message/pause', + '"abort"' => 'message/abort', + '"resume"' => 'message/resume', + '"restart"' => 'message/restart', +); + +foreach ($row_iterator as $row) { + if ($row['transactionType'] !== 'harbormaster:build:command') { + continue; + } + + $raw_value = $row['newValue']; + + if (isset($map[$raw_value])) { + queryfx( + $xactions_conn, + 'UPDATE %R SET transactionType = %s WHERE id = %d', + $xactions_table, + $map[$raw_value], + $row['id']); + } +} diff --git a/resources/sql/autopatches/20220401.phameinteract.01.sql b/resources/sql/autopatches/20220401.phameinteract.01.sql new file mode 100644 index 0000000000..cb8121887f --- /dev/null +++ b/resources/sql/autopatches/20220401.phameinteract.01.sql @@ -0,0 +1,5 @@ +UPDATE {$NAMESPACE}_phame.phame_blog + SET editPolicy = 'admin' WHERE editPolicy IS NULL; + +ALTER TABLE {$NAMESPACE}_phame.phame_blog + CHANGE editPolicy editPolicy VARBINARY(64) NOT NULL; diff --git a/resources/sql/autopatches/20220401.phameinteract.02.sql b/resources/sql/autopatches/20220401.phameinteract.02.sql new file mode 100644 index 0000000000..057c02d30e --- /dev/null +++ b/resources/sql/autopatches/20220401.phameinteract.02.sql @@ -0,0 +1,5 @@ +UPDATE {$NAMESPACE}_phame.phame_blog + SET viewPolicy = 'admin' WHERE viewPolicy IS NULL; + +ALTER TABLE {$NAMESPACE}_phame.phame_blog + CHANGE viewPolicy viewPolicy VARBINARY(64) NOT NULL; diff --git a/resources/sql/autopatches/20220401.phameinteract.03.sql b/resources/sql/autopatches/20220401.phameinteract.03.sql new file mode 100644 index 0000000000..d5ac27b2ef --- /dev/null +++ b/resources/sql/autopatches/20220401.phameinteract.03.sql @@ -0,0 +1,6 @@ +ALTER TABLE {$NAMESPACE}_phame.phame_blog + ADD interactPolicy VARBINARY(64) NOT NULL; + +UPDATE {$NAMESPACE}_phame.phame_blog + SET interactPolicy = 'users' + WHERE interactPolicy = ''; diff --git a/resources/sql/autopatches/20220401.phameinteract.04.postinteract.sql b/resources/sql/autopatches/20220401.phameinteract.04.postinteract.sql new file mode 100644 index 0000000000..723a1ff69c --- /dev/null +++ b/resources/sql/autopatches/20220401.phameinteract.04.postinteract.sql @@ -0,0 +1,6 @@ +ALTER TABLE {$NAMESPACE}_phame.phame_post + ADD interactPolicy VARBINARY(64) NOT NULL; + +UPDATE {$NAMESPACE}_phame.phame_post + SET interactPolicy = 'obj.phame.blog' + WHERE interactPolicy = ''; diff --git a/resources/sql/autopatches/20220510.file.01.attach.sql b/resources/sql/autopatches/20220510.file.01.attach.sql new file mode 100644 index 0000000000..3ca8bacac4 --- /dev/null +++ b/resources/sql/autopatches/20220510.file.01.attach.sql @@ -0,0 +1,9 @@ +CREATE TABLE {$NAMESPACE}_file.file_attachment ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + objectPHID VARBINARY(64) NOT NULL, + filePHID VARBINARY(64) NOT NULL, + attacherPHID VARBINARY(64), + attachmentMode VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20220519.file.02.migrate.sql b/resources/sql/autopatches/20220519.file.02.migrate.sql new file mode 100644 index 0000000000..89d2d30a82 --- /dev/null +++ b/resources/sql/autopatches/20220519.file.02.migrate.sql @@ -0,0 +1,7 @@ +INSERT IGNORE INTO {$NAMESPACE}_file.file_attachment + (objectPHID, filePHID, attachmentMode, attacherPHID, + dateCreated, dateModified) + SELECT dst, src, 'attach', null, dateCreated, dateCreated + FROM {$NAMESPACE}_file.edge + WHERE type = 26 + ORDER BY dateCreated ASC; diff --git a/resources/sql/autopatches/20220525.slowvote.01.mailkey.php b/resources/sql/autopatches/20220525.slowvote.01.mailkey.php new file mode 100644 index 0000000000..ed0355e105 --- /dev/null +++ b/resources/sql/autopatches/20220525.slowvote.01.mailkey.php @@ -0,0 +1,28 @@ +establishConnection('w'); + +$properties_table = new PhabricatorMetaMTAMailProperties(); +$conn = $properties_table->establishConnection('w'); + +$iterator = new LiskRawMigrationIterator( + $poll_conn, + $poll_table->getTableName()); + +foreach ($iterator as $row) { + queryfx( + $conn, + 'INSERT IGNORE INTO %R + (objectPHID, mailProperties, dateCreated, dateModified) + VALUES + (%s, %s, %d, %d)', + $properties_table, + $row['phid'], + phutil_json_encode( + array( + 'mailKey' => $row['mailKey'], + )), + PhabricatorTime::getNow(), + PhabricatorTime::getNow()); +} diff --git a/resources/sql/autopatches/20220525.slowvote.02.mailkey-drop.sql b/resources/sql/autopatches/20220525.slowvote.02.mailkey-drop.sql new file mode 100644 index 0000000000..54e65fd14c --- /dev/null +++ b/resources/sql/autopatches/20220525.slowvote.02.mailkey-drop.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_slowvote.slowvote_poll + DROP mailKey; diff --git a/resources/sql/autopatches/20220525.slowvote.03.response-type.sql b/resources/sql/autopatches/20220525.slowvote.03.response-type.sql new file mode 100644 index 0000000000..8cefc4d578 --- /dev/null +++ b/resources/sql/autopatches/20220525.slowvote.03.response-type.sql @@ -0,0 +1,3 @@ +ALTER TABLE {$NAMESPACE}_slowvote.slowvote_poll + CHANGE responseVisibility + responseVisibility VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20220525.slowvote.04.response-value.sql b/resources/sql/autopatches/20220525.slowvote.04.response-value.sql new file mode 100644 index 0000000000..b76bcdb784 --- /dev/null +++ b/resources/sql/autopatches/20220525.slowvote.04.response-value.sql @@ -0,0 +1,8 @@ +UPDATE {$NAMESPACE}_slowvote.slowvote_poll + SET responseVisibility = 'visible' WHERE responseVisibility = '0'; + +UPDATE {$NAMESPACE}_slowvote.slowvote_poll + SET responseVisibility = 'voters' WHERE responseVisibility = '1'; + +UPDATE {$NAMESPACE}_slowvote.slowvote_poll + SET responseVisibility = 'owner' WHERE responseVisibility = '2'; diff --git a/resources/sql/autopatches/20220525.slowvote.05.response-xactions.sql b/resources/sql/autopatches/20220525.slowvote.05.response-xactions.sql new file mode 100644 index 0000000000..7e819d5893 --- /dev/null +++ b/resources/sql/autopatches/20220525.slowvote.05.response-xactions.sql @@ -0,0 +1,23 @@ +UPDATE {$NAMESPACE}_slowvote.slowvote_transaction + SET oldValue = '"visible"' WHERE + transactionType = 'vote:responses' AND oldValue IN ('0', '"0"'); + +UPDATE {$NAMESPACE}_slowvote.slowvote_transaction + SET newValue = '"visible"' WHERE + transactionType = 'vote:responses' AND newValue IN ('0', '"0"'); + +UPDATE {$NAMESPACE}_slowvote.slowvote_transaction + SET oldValue = '"voters"' WHERE + transactionType = 'vote:responses' AND oldValue IN ('1', '"1"'); + +UPDATE {$NAMESPACE}_slowvote.slowvote_transaction + SET newValue = '"voters"' WHERE + transactionType = 'vote:responses' AND newValue IN ('1', '"1"'); + +UPDATE {$NAMESPACE}_slowvote.slowvote_transaction + SET oldValue = '"owner"' WHERE + transactionType = 'vote:responses' AND oldValue IN ('2', '"2"'); + +UPDATE {$NAMESPACE}_slowvote.slowvote_transaction + SET newValue = '"owner"' WHERE + transactionType = 'vote:responses' AND newValue IN ('2', '"2"'); diff --git a/resources/sql/autopatches/20220525.slowvote.06.method-type.sql b/resources/sql/autopatches/20220525.slowvote.06.method-type.sql new file mode 100644 index 0000000000..e2af0643bc --- /dev/null +++ b/resources/sql/autopatches/20220525.slowvote.06.method-type.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_slowvote.slowvote_poll + CHANGE method method VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20220525.slowvote.07.method-value.sql b/resources/sql/autopatches/20220525.slowvote.07.method-value.sql new file mode 100644 index 0000000000..04d0f6f430 --- /dev/null +++ b/resources/sql/autopatches/20220525.slowvote.07.method-value.sql @@ -0,0 +1,5 @@ +UPDATE {$NAMESPACE}_slowvote.slowvote_poll + SET method = 'plurality' WHERE method = '0'; + +UPDATE {$NAMESPACE}_slowvote.slowvote_poll + SET method = 'approval' WHERE method = '1'; diff --git a/resources/sql/autopatches/20220525.slowvote.08.status-type.sql b/resources/sql/autopatches/20220525.slowvote.08.status-type.sql new file mode 100644 index 0000000000..e8575fc50a --- /dev/null +++ b/resources/sql/autopatches/20220525.slowvote.08.status-type.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_slowvote.slowvote_poll + CHANGE isClosed status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20220525.slowvote.09.status-value.sql b/resources/sql/autopatches/20220525.slowvote.09.status-value.sql new file mode 100644 index 0000000000..686e663af7 --- /dev/null +++ b/resources/sql/autopatches/20220525.slowvote.09.status-value.sql @@ -0,0 +1,5 @@ +UPDATE {$NAMESPACE}_slowvote.slowvote_poll + SET status = 'open' WHERE status = '0'; + +UPDATE {$NAMESPACE}_slowvote.slowvote_poll + SET status = 'closed' WHERE status = '1'; diff --git a/resources/sql/autopatches/20220525.slowvote.10.status-xactions.sql b/resources/sql/autopatches/20220525.slowvote.10.status-xactions.sql new file mode 100644 index 0000000000..fd06f9ebb2 --- /dev/null +++ b/resources/sql/autopatches/20220525.slowvote.10.status-xactions.sql @@ -0,0 +1,19 @@ +UPDATE {$NAMESPACE}_slowvote.slowvote_transaction + SET transactionType = 'vote:status' + WHERE transactionType = 'vote:close'; + +UPDATE {$NAMESPACE}_slowvote.slowvote_transaction + SET oldValue = '"open"' WHERE + transactionType = 'vote:status' AND oldValue IN ('0', '"0"', 'false'); + +UPDATE {$NAMESPACE}_slowvote.slowvote_transaction + SET newValue = '"open"' WHERE + transactionType = 'vote:status' AND newValue IN ('0', '"0"', 'false'); + +UPDATE {$NAMESPACE}_slowvote.slowvote_transaction + SET oldValue = '"closed"' WHERE + transactionType = 'vote:status' AND oldValue IN ('1', '"1"', 'true'); + +UPDATE {$NAMESPACE}_slowvote.slowvote_transaction + SET newValue = '"closed"' WHERE + transactionType = 'vote:status' AND newValue IN ('1', '"1"', 'true'); diff --git a/resources/sql/patches/20130611.migrateoauth.php b/resources/sql/patches/20130611.migrateoauth.php index 92fe854cfd..9d1490c5c1 100644 --- a/resources/sql/patches/20130611.migrateoauth.php +++ b/resources/sql/patches/20130611.migrateoauth.php @@ -7,8 +7,8 @@ $table_name = 'user_oauthinfo'; foreach (new LiskRawMigrationIterator($conn, $table_name) as $row) { throw new Exception( pht( - 'Your Phabricator install has ancient OAuth account data and is '. - 'too old to upgrade directly to a modern version of Phabricator. '. - 'Upgrade to a version released between June 2013 and February 2019 '. - 'first, then upgrade to a modern version.')); + 'This database has ancient OAuth account data and is too old to '. + 'upgrade directly to a modern software version. Upgrade to a version '. + 'released between June 2013 and February 2019 first, then upgrade to '. + 'a modern version.')); } diff --git a/resources/sql/patches/20130611.nukeldap.php b/resources/sql/patches/20130611.nukeldap.php index 0f0b976a58..70a3a3fec4 100644 --- a/resources/sql/patches/20130611.nukeldap.php +++ b/resources/sql/patches/20130611.nukeldap.php @@ -7,8 +7,8 @@ $table_name = 'user_ldapinfo'; foreach (new LiskRawMigrationIterator($conn, $table_name) as $row) { throw new Exception( pht( - 'Your Phabricator install has ancient LDAP account data and is '. - 'too old to upgrade directly to a modern version of Phabricator. '. - 'Upgrade to a version released between June 2013 and February 2019 '. - 'first, then upgrade to a modern version.')); + 'This database has ancient LDAP account data and is too old to upgrade '. + 'directly to a modern version of the software. Upgrade to a version '. + 'released between June 2013 and February 2019 first, then upgrade to a '. + 'modern version.')); } diff --git a/resources/sql/patches/20130820.file-mailkey-populate.php b/resources/sql/patches/20130820.file-mailkey-populate.php index 0db10bef58..2cf6371c4d 100644 --- a/resources/sql/patches/20130820.file-mailkey-populate.php +++ b/resources/sql/patches/20130820.file-mailkey-populate.php @@ -1,6 +1,6 @@ getTableName(); diff --git a/resources/sql/patches/20131004.dxreviewers.php b/resources/sql/patches/20131004.dxreviewers.php index 4f853f5ddc..75a2ba53c1 100644 --- a/resources/sql/patches/20131004.dxreviewers.php +++ b/resources/sql/patches/20131004.dxreviewers.php @@ -29,8 +29,7 @@ foreach (new LiskMigrationIterator($table) as $revision) { foreach ($reviewer_phids as $dst) { if (phid_get_type($dst) == PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN) { // At least one old install ran into some issues here. Skip the row if we - // can't figure out what the destination PHID is. See here: - // https://github.com/phacility/phabricator/pull/507 + // can't figure out what the destination PHID is. continue; } diff --git a/resources/sql/quickstart.sql b/resources/sql/quickstart.sql index 2aa25e75ba..b2796d31e6 100644 --- a/resources/sql/quickstart.sql +++ b/resources/sql/quickstart.sql @@ -7552,113 +7552,6 @@ CREATE TABLE `phortune_subscription` ( KEY `key_merchant` (`merchantPHID`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; -CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_phragment` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; - -USE `{$NAMESPACE}_phragment`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `edge` ( - `src` varbinary(64) NOT NULL, - `type` int(10) unsigned NOT NULL, - `dst` varbinary(64) NOT NULL, - `dateCreated` int(10) unsigned NOT NULL, - `seq` int(10) unsigned NOT NULL, - `dataID` int(10) unsigned DEFAULT NULL, - PRIMARY KEY (`src`,`type`,`dst`), - UNIQUE KEY `key_dst` (`dst`,`type`,`src`), - KEY `src` (`src`,`type`,`dateCreated`,`seq`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - -USE `{$NAMESPACE}_phragment`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `edgedata` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `data` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - -USE `{$NAMESPACE}_phragment`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `phragment_fragment` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `phid` varbinary(64) NOT NULL, - `path` varchar(128) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `depth` int(10) unsigned NOT NULL, - `latestVersionPHID` varbinary(64) DEFAULT NULL, - `viewPolicy` varbinary(64) NOT NULL, - `editPolicy` varbinary(64) NOT NULL, - `dateCreated` int(10) unsigned NOT NULL, - `dateModified` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `key_phid` (`phid`), - UNIQUE KEY `key_path` (`path`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - -USE `{$NAMESPACE}_phragment`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `phragment_fragmentversion` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `phid` varbinary(64) NOT NULL, - `sequence` int(10) unsigned NOT NULL, - `fragmentPHID` varbinary(64) NOT NULL, - `filePHID` varbinary(64) DEFAULT NULL, - `dateCreated` int(10) unsigned NOT NULL, - `dateModified` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `key_version` (`fragmentPHID`,`sequence`), - UNIQUE KEY `key_phid` (`phid`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - -USE `{$NAMESPACE}_phragment`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `phragment_snapshot` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `phid` varbinary(64) NOT NULL, - `primaryFragmentPHID` varbinary(64) NOT NULL, - `name` varchar(128) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `dateCreated` int(10) unsigned NOT NULL, - `dateModified` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `key_phid` (`phid`), - UNIQUE KEY `key_name` (`primaryFragmentPHID`,`name`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - -USE `{$NAMESPACE}_phragment`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `phragment_snapshotchild` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `snapshotPHID` varbinary(64) NOT NULL, - `fragmentPHID` varbinary(64) NOT NULL, - `fragmentVersionPHID` varbinary(64) DEFAULT NULL, - `dateCreated` int(10) unsigned NOT NULL, - `dateModified` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `key_child` (`snapshotPHID`,`fragmentPHID`,`fragmentVersionPHID`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_phrequent` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; USE `{$NAMESPACE}_phrequent`; @@ -8667,190 +8560,6 @@ CREATE TABLE `project_triggerusage` ( UNIQUE KEY `key_trigger` (`triggerPHID`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; -CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_releeph` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; - -USE `{$NAMESPACE}_releeph`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `releeph_branch` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `dateCreated` int(10) unsigned NOT NULL, - `dateModified` int(10) unsigned NOT NULL, - `basename` varchar(64) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `releephProjectID` int(10) unsigned NOT NULL, - `createdByUserPHID` varbinary(64) NOT NULL, - `cutPointCommitPHID` varbinary(64) NOT NULL, - `isActive` tinyint(1) NOT NULL DEFAULT '1', - `symbolicName` varchar(64) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} DEFAULT NULL, - `details` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `phid` varbinary(64) NOT NULL, - `name` varchar(128) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `releephProjectID_2` (`releephProjectID`,`basename`), - UNIQUE KEY `releephProjectID_name` (`releephProjectID`,`name`), - UNIQUE KEY `key_phid` (`phid`), - UNIQUE KEY `releephProjectID` (`releephProjectID`,`symbolicName`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - -USE `{$NAMESPACE}_releeph`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `releeph_branchtransaction` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `phid` varbinary(64) NOT NULL, - `authorPHID` varbinary(64) NOT NULL, - `objectPHID` varbinary(64) NOT NULL, - `viewPolicy` varbinary(64) NOT NULL, - `editPolicy` varbinary(64) NOT NULL, - `commentPHID` varbinary(64) DEFAULT NULL, - `commentVersion` int(10) unsigned NOT NULL, - `transactionType` varchar(32) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `oldValue` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `newValue` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `contentSource` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `metadata` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `dateCreated` int(10) unsigned NOT NULL, - `dateModified` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `key_phid` (`phid`), - KEY `key_object` (`objectPHID`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - -USE `{$NAMESPACE}_releeph`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `releeph_producttransaction` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `phid` varbinary(64) NOT NULL, - `authorPHID` varbinary(64) NOT NULL, - `objectPHID` varbinary(64) NOT NULL, - `viewPolicy` varbinary(64) NOT NULL, - `editPolicy` varbinary(64) NOT NULL, - `commentPHID` varbinary(64) DEFAULT NULL, - `commentVersion` int(10) unsigned NOT NULL, - `transactionType` varchar(32) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `oldValue` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `newValue` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `contentSource` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `metadata` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `dateCreated` int(10) unsigned NOT NULL, - `dateModified` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `key_phid` (`phid`), - KEY `key_object` (`objectPHID`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - -USE `{$NAMESPACE}_releeph`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `releeph_project` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `dateCreated` int(10) unsigned NOT NULL, - `dateModified` int(10) unsigned NOT NULL, - `phid` varbinary(64) NOT NULL, - `name` varchar(128) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `trunkBranch` varchar(255) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `repositoryPHID` varbinary(64) NOT NULL, - `createdByUserPHID` varbinary(64) NOT NULL, - `isActive` tinyint(1) NOT NULL DEFAULT '1', - `details` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `projectName` (`name`), - UNIQUE KEY `key_phid` (`phid`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - -USE `{$NAMESPACE}_releeph`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `releeph_request` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `dateCreated` int(10) unsigned NOT NULL, - `dateModified` int(10) unsigned NOT NULL, - `phid` varbinary(64) NOT NULL, - `branchID` int(10) unsigned NOT NULL, - `requestUserPHID` varbinary(64) NOT NULL, - `requestCommitPHID` varbinary(64) DEFAULT NULL, - `commitIdentifier` varchar(40) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} DEFAULT NULL, - `commitPHID` varbinary(64) DEFAULT NULL, - `pickStatus` int(10) unsigned DEFAULT NULL, - `details` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `userIntents` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT}, - `inBranch` tinyint(1) NOT NULL DEFAULT '0', - `mailKey` binary(20) NOT NULL, - `requestedObjectPHID` varbinary(64) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `phid` (`phid`), - UNIQUE KEY `requestIdentifierBranch` (`requestCommitPHID`,`branchID`), - KEY `branchID` (`branchID`), - KEY `key_requestedObject` (`requestedObjectPHID`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - -USE `{$NAMESPACE}_releeph`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `releeph_requesttransaction` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `phid` varbinary(64) NOT NULL, - `authorPHID` varbinary(64) NOT NULL, - `objectPHID` varbinary(64) NOT NULL, - `viewPolicy` varbinary(64) NOT NULL, - `editPolicy` varbinary(64) NOT NULL, - `commentPHID` varbinary(64) DEFAULT NULL, - `commentVersion` int(10) unsigned NOT NULL, - `transactionType` varchar(32) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `oldValue` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `newValue` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `metadata` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `contentSource` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `dateCreated` int(10) unsigned NOT NULL, - `dateModified` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `key_phid` (`phid`), - KEY `key_object` (`objectPHID`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - -USE `{$NAMESPACE}_releeph`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `releeph_requesttransaction_comment` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `phid` varbinary(64) NOT NULL, - `transactionPHID` varbinary(64) DEFAULT NULL, - `authorPHID` varbinary(64) NOT NULL, - `viewPolicy` varbinary(64) NOT NULL, - `editPolicy` varbinary(64) NOT NULL, - `commentVersion` int(10) unsigned NOT NULL, - `content` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `contentSource` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `isDeleted` tinyint(1) NOT NULL, - `dateCreated` int(10) unsigned NOT NULL, - `dateModified` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `key_phid` (`phid`), - UNIQUE KEY `key_version` (`transactionPHID`,`commentVersion`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_repository` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; USE `{$NAMESPACE}_repository`; diff --git a/scripts/install/install_rhel-derivs.sh b/scripts/install/install_rhel-derivs.sh deleted file mode 100755 index 78a3375c95..0000000000 --- a/scripts/install/install_rhel-derivs.sh +++ /dev/null @@ -1,143 +0,0 @@ -#!/bin/bash - -confirm() { - echo "Press RETURN to continue, or ^C to cancel."; - read -e ignored -} - -RHEL_VER_FILE="/etc/redhat-release" -INSTALL_URI=" https://we.phorge.it/book/phorge/article/installation_guide/" -CONFIG_URI=" https://we.phorge.it/book/phorge/article/configuration_guide/" -REPOSITORY_URI="https://we.phorge.it/source" -NAME_MAIN="phorge" -NAME_ARC="arcanist" - -if [[ ! -f $RHEL_VER_FILE ]] -then - echo "It looks like you're not running a Red Hat-derived distribution." - echo "This script is intended to install $NAME_MAIN on RHEL-derived" - echo "distributions such as RHEL, Fedora, CentOS, and Scientific Linux." - echo "Proceed with caution." - confirm -fi - -echo "$NAME_MAIN RED HAT DERIVATIVE INSTALLATION SCRIPT"; -echo "This script will install $NAME_MAIN and all of its core dependencies."; -echo "Run it from the directory you want to install into."; -echo - -RHEL_REGEX="release ([0-9]+)\." - -if [[ $(cat $RHEL_VER_FILE) =~ $RHEL_REGEX ]] -then - RHEL_MAJOR_VER=${BASH_REMATCH[1]} -else - echo "Ut oh, we were unable to determine your distribution's major" - echo "version number. Please make sure you're running 6.0+ before" - echo "proceeding." - confirm -fi - -if [[ $RHEL_MAJOR_VER < 6 && $RHEL_MAJOR_VER > 0 ]] -then - echo "** WARNING **" - echo "A major version less than 6 was detected. Because of this," - echo "several needed dependencies are not available via default repos." - echo "Specifically, RHEL 5 does not have a PEAR package for php53-*." - echo "We will attempt to install it manually, for APC. Please be careful." - confirm -fi - -echo "$NAME_MAIN will be installed to: $(pwd)."; -confirm - -echo "Testing sudo/root..." -if [[ $EUID -ne 0 ]] # Check if we're root. If we are, continue. -then - sudo true - SUDO="sudo" - if [[ $? -ne 0 ]] - then - echo "ERROR: You must be able to sudo to run this script, or run it as root."; - exit 1 - fi - -fi - -if [[ $RHEL_MAJOR_VER == 5 ]] -then - # RHEL 5's "php" package is actually 5.1. The "php53" package won't let us install php-pecl-apc. - # (it tries to pull in php 5.1 stuff) ... - yum repolist | grep -i epel - if [ $? -ne 0 ]; then - echo "It doesn't look like you have the EPEL repo enabled. We are to add it" - echo "for you, so that we can install git." - $SUDO rpm -Uvh https://download.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm - fi - YUMCOMMAND="$SUDO yum install httpd git php53 php53-cli php53-mysql php53-process php53-devel php53-gd gcc wget make pcre-devel mysql-server" -else - # RHEL 6+ defaults with php 5.3 - YUMCOMMAND="$SUDO yum install httpd git php php-cli php-mysql php-process php-devel php-gd php-pecl-apc php-pecl-json php-mbstring mysql-server" -fi - -echo "Dropping to yum to install dependencies..." -echo "Running: ${YUMCOMMAND}" -echo "Yum will prompt you with [Y/n] to continue installing." - -$YUMCOMMAND - -if [[ $? -ne 0 ]] -then - echo "The yum command failed. Please fix the errors and re-run this script." - exit 1 -fi - -if [[ $RHEL_MAJOR_VER == 5 ]] -then - # Now that we've ensured all the devel packages required for pecl/apc are there, let's - # set up PEAR, and install apc. - echo "Attempting to install PEAR" - wget https://pear.php.net/go-pear.phar - $SUDO php go-pear.phar && $SUDO pecl install apc -fi - -if [[ $? -ne 0 ]] -then - echo "The apc install failed. Continuing without APC, performance may be impacted." -fi - -pidof httpd 2>&1 > /dev/null -if [[ $? -eq 0 ]] -then - echo "If php was installed above, please run: /etc/init.d/httpd graceful" -else - echo "Please remember to start the httpd with: /etc/init.d/httpd start" -fi - -pidof mysqld 2>&1 > /dev/null -if [[ $? -ne 0 ]] -then - echo "Please remember to start the mysql server: /etc/init.d/mysqld start" -fi - -confirm - -if [[ ! -e "$NAME_ARC" ]] -then - git clone "$REPOSITORY_URI/$NAME_ARC.git" -else - (cd "$NAME_ARC" && git pull --rebase) -fi - -if [[ ! -e "$NAME_MAIN" ]] -then - git clone "$REPOSITORY_URI/$NAME_MAIN.git" -else - (cd "$NAME_MAIN" && git pull --rebase) -fi - -echo -echo -echo "Install probably worked mostly correctly. Continue with the 'Configuration Guide':"; -echo -echo $CONFIG_URI diff --git a/scripts/install/install_ubuntu.sh b/scripts/install/install_ubuntu.sh deleted file mode 100755 index 2694a4ab9b..0000000000 --- a/scripts/install/install_ubuntu.sh +++ /dev/null @@ -1,123 +0,0 @@ -#!/bin/bash - -confirm() { - echo "Press RETURN to continue, or ^C to cancel."; - read -e ignored -} - -INSTALL_URI=" https://we.phorge.it/book/phorge/article/installation_guide/" -CONFIG_URI=" https://we.phorge.it/book/phorge/article/configuration_guide/" -REPOSITORY_URI="https://we.phorge.it/source" -NAME_MAIN="phorge" -NAME_ARC="arcanist" - -NAME_UPPER="$(echo $NAME_MAIN | tr a-z A-Z)" - -failed() { - echo - echo - echo "Installation has failed." - echo "Text above this message might be useful to understanding what exactly failed." - echo - echo "Please follow this guide to manually complete installation:" - echo - echo $INSTALL_URI - echo - echo "We apologize for the inconvenience." - exit 3 -} - -ISSUE=`cat /etc/issue` -if [[ $ISSUE != Ubuntu* ]] -then - echo "This script is intended for use on Ubuntu, but this system appears"; - echo "to be something else. Your results may vary."; - echo - confirm -fi - -echo "$NAME_UPPER UBUNTU INSTALL SCRIPT"; -echo "This script will install Apache, $NAME_MAIN and its core dependencies."; -echo "Run it from the directory you want to install into."; -echo - -echo "Testing sudo..." -sudo true -if [ $? -ne 0 ] -then - echo "ERROR: You must be able to sudo to run this script."; - exit 1; -fi; - -echo 'Testing Ubuntu version...' - -VERSION=`lsb_release -rs` -MAJOR=`expr match "$VERSION" '\([0-9]*\)'` - -if [ "$MAJOR" -lt 16 ] -then - echo 'This script is intented to install on modern operating systems; Your ' - echo 'operating system is too old for this script.' - echo 'You can still install $NAME_MAIN manually - please consult the' - echo 'installation guide to see how:' - echo - echo $INSTALL_URI - echo - exit 2 -fi - -# Ubuntu 16.04 LTS only has php 7.0 in their repos, so they need this extra ppa. -# Ubuntu 17.4 and up have official 7.2 builds. -if [ "$MAJOR" -eq 16 ] -then - echo 'This version of Ubuntu requires additional resources in order to install' - echo 'and run $NAME_MAIN.' - echo 'We will now add a the following package repository to your system:' - echo ' https://launchpad.net/~ondrej/+archive/ubuntu/php' - echo - echo 'This repository is generally considered safe to use.' - confirm - - sudo add-apt-repository -y ppa:ondrej/php || failed -fi - -ROOT=`pwd` -echo "$NAME_MAIN will be installed to: ${ROOT}."; -confirm - -echo "Installing dependencies: git, apache, mysql, php..."; -echo -sudo apt-get -qq update -sudo apt-get install \ - git mysql-server apache2 libapache2-mod-php \ - php php-mysql php-gd php-curl php-apcu php-cli php-json php-mbstring \ - || failed - -echo "Enabling mod_rewrite in Apache..." -echo -sudo a2enmod rewrite || failed - -echo "Downloading $NAME_MAIN and dependencies..." -echo - -if [ ! -e "$NAME_ARC" ] -then - git clone "$REPOSITORY_URI/$NAME_ARC.git" -else - (cd "$NAME_ARC" && git pull --rebase) -fi - -if [ ! -e "$NAME_MAIN" ] -then - git clone "$REPOSITORY_URI/$NAME_MAIN.git" -else - (cd "$NAME_MAIN" && git pull --rebase) -fi - -echo -echo -echo "Install probably worked mostly correctly. Continue with the 'Configuration Guide':"; -echo -echo $CONFIG_URI -echo -echo 'Next step is "Configuring Apache webserver".' diff --git a/scripts/repository/commit_hook.php b/scripts/repository/commit_hook.php index df49aa7b00..ccbfde08ff 100755 --- a/scripts/repository/commit_hook.php +++ b/scripts/repository/commit_hook.php @@ -122,9 +122,9 @@ if ($is_svnrevprop) { if (!strlen($username)) { throw new Exception( pht( - 'No Direct Pushes: You are pushing directly to a repository hosted '. - 'by Phabricator. This will not work. See "No Direct Pushes" in the '. - 'documentation for more information.')); + 'No Direct Pushes: You are pushing directly to a hosted repository. '. + 'This will not work. See "No Direct Pushes" in the documentation '. + 'for more information.')); } if ($repository->isHg()) { diff --git a/scripts/setup/manage_celerity.php b/scripts/setup/manage_celerity.php index bcda6353e1..2ae67743b2 100755 --- a/scripts/setup/manage_celerity.php +++ b/scripts/setup/manage_celerity.php @@ -2,7 +2,7 @@ setTagline(pht('manage celerity')); diff --git a/scripts/sql/manage_storage.php b/scripts/sql/manage_storage.php index ec3f492e8c..c3de483d72 100755 --- a/scripts/sql/manage_storage.php +++ b/scripts/sql/manage_storage.php @@ -5,13 +5,13 @@ $root = dirname(dirname(dirname(__FILE__))); require_once $root.'/scripts/init/init-setup.php'; $args = new PhutilArgumentParser($argv); -$args->setTagline(pht('manage Phabricator storage and schemata')); +$args->setTagline(pht('manage storage and schemata')); $args->setSynopsis(<< 'disable-utf8mb4', 'help' => pht( 'Disable %s, even if the database supports it. This is an '. - 'advanced feature used for testing changes to Phabricator; you '. + 'advanced feature used for testing internal changes; you '. 'should not normally use this flag.', 'utf8mb4'), ), @@ -95,7 +95,7 @@ if (!$refs) { $host = $args->getArg('host'); $ref_key = $args->getArg('ref'); -if (strlen($host) || strlen($ref_key)) { +if (($host !== null) || ($ref_key !== null)) { if ($host && $ref_key) { throw new PhutilArgumentUsageException( pht( @@ -168,9 +168,9 @@ foreach ($refs as $ref) { 'Unable to connect to MySQL using the configured credentials. '. 'You must configure standard credentials before you can upgrade '. 'storage. Run these commands to set up credentials:'), - " phabricator/ $ ./bin/config set mysql.host __host__\n". - " phabricator/ $ ./bin/config set mysql.user __username__\n". - " phabricator/ $ ./bin/config set mysql.pass __password__", + " $ ./bin/config set mysql.host __host__\n". + " $ ./bin/config set mysql.user __username__\n". + " $ ./bin/config set mysql.pass __password__", pht( 'These standard credentials are separate from any administrative '. 'credentials provided to this command with __%s__ or '. diff --git a/scripts/ssh/ssh-connect.php b/scripts/ssh/ssh-connect.php index 0d87f16973..c128f3b32f 100755 --- a/scripts/ssh/ssh-connect.php +++ b/scripts/ssh/ssh-connect.php @@ -154,6 +154,6 @@ $pattern = implode(' ', $pattern); array_unshift($arguments, $pattern); $err = newv('PhutilExecPassthru', $arguments) - ->execute(); + ->resolve(); exit($err); diff --git a/scripts/ssh/ssh-exec.php b/scripts/ssh/ssh-exec.php index c3a85b399c..70c95d28da 100755 --- a/scripts/ssh/ssh-exec.php +++ b/scripts/ssh/ssh-exec.php @@ -130,9 +130,9 @@ try { if (!PhabricatorEnv::isClusterAddress($remote_address)) { throw new Exception( pht( - 'This request originates from outside of the Phabricator cluster '. - 'address range. Requests signed with a trusted device key must '. - 'originate from trusted hosts.')); + 'This request originates from outside of the cluster address range. '. + 'Requests signed with a trusted device key must originate from '. + 'trusted hosts.')); } $device = id(new AlmanacDeviceQuery()) @@ -228,7 +228,9 @@ try { $command_list = implode(', ', $command_list); $error_lines = array(); - $error_lines[] = pht('Welcome to Phabricator.'); + $error_lines[] = pht( + 'Welcome to %s.', + PlatformSymbols::getPlatformServerName()); $error_lines[] = pht( 'You are logged in as %s.', $user_name); @@ -236,7 +238,7 @@ try { if (!$original_argv) { $error_lines[] = pht( 'You have not specified a command to run. This means you are requesting '. - 'an interactive shell, but Phabricator does not provide interactive '. + 'an interactive shell, but this server does not provide interactive '. 'shells over SSH.'); $error_lines[] = pht( '(Usually, you should run a command like "git clone" or "hg push" '. @@ -270,7 +272,7 @@ try { if (empty($workflows[$command])) { $error_lines[] = pht( 'You have specified the command "%s", but that command is not '. - 'supported by Phabricator. As received by Phabricator, your entire '. + 'supported by this server. As received by this server, your entire '. 'argument list was:', $command); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index c92b9b9f77..02353b32c1 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -88,6 +88,7 @@ phutil_register_library_map(array( 'AlmanacInterfaceSearchEngine' => 'applications/almanac/query/AlmanacInterfaceSearchEngine.php', 'AlmanacInterfaceTableView' => 'applications/almanac/view/AlmanacInterfaceTableView.php', 'AlmanacInterfaceTransaction' => 'applications/almanac/storage/AlmanacInterfaceTransaction.php', + 'AlmanacInterfaceTransactionQuery' => 'applications/almanac/query/AlmanacInterfaceTransactionQuery.php', 'AlmanacInterfaceTransactionType' => 'applications/almanac/xaction/AlmanacInterfaceTransactionType.php', 'AlmanacKeys' => 'applications/almanac/util/AlmanacKeys.php', 'AlmanacManageClusterServicesCapability' => 'applications/almanac/capability/AlmanacManageClusterServicesCapability.php', @@ -238,6 +239,7 @@ phutil_register_library_map(array( 'AphrontIsolatedDatabaseConnection' => 'infrastructure/storage/connection/AphrontIsolatedDatabaseConnection.php', 'AphrontIsolatedDatabaseConnectionTestCase' => 'infrastructure/storage/__tests__/AphrontIsolatedDatabaseConnectionTestCase.php', 'AphrontIsolatedHTTPSink' => 'aphront/sink/AphrontIsolatedHTTPSink.php', + 'AphrontJSONHTTPParameterType' => 'aphront/httpparametertype/AphrontJSONHTTPParameterType.php', 'AphrontJSONResponse' => 'aphront/response/AphrontJSONResponse.php', 'AphrontJavelinView' => 'view/AphrontJavelinView.php', 'AphrontKeyboardShortcutsAvailableView' => 'view/widget/AphrontKeyboardShortcutsAvailableView.php', @@ -271,6 +273,7 @@ phutil_register_library_map(array( 'AphrontRedirectResponse' => 'aphront/response/AphrontRedirectResponse.php', 'AphrontRedirectResponseTestCase' => 'aphront/response/__tests__/AphrontRedirectResponseTestCase.php', 'AphrontReloadResponse' => 'aphront/response/AphrontReloadResponse.php', + 'AphrontRemarkupHTTPParameterType' => 'aphront/httpparametertype/AphrontRemarkupHTTPParameterType.php', 'AphrontRequest' => 'aphront/AphrontRequest.php', 'AphrontRequestExceptionHandler' => 'aphront/handler/AphrontRequestExceptionHandler.php', 'AphrontRequestStream' => 'aphront/requeststream/AphrontRequestStream.php', @@ -338,6 +341,7 @@ phutil_register_library_map(array( 'ChatLogConduitAPIMethod' => 'applications/chatlog/conduit/ChatLogConduitAPIMethod.php', 'ChatLogQueryConduitAPIMethod' => 'applications/chatlog/conduit/ChatLogQueryConduitAPIMethod.php', 'ChatLogRecordConduitAPIMethod' => 'applications/chatlog/conduit/ChatLogRecordConduitAPIMethod.php', + 'ConduitAPIDocumentationPage' => 'applications/conduit/data/ConduitAPIDocumentationPage.php', 'ConduitAPIMethod' => 'applications/conduit/method/ConduitAPIMethod.php', 'ConduitAPIMethodTestCase' => 'applications/conduit/method/__tests__/ConduitAPIMethodTestCase.php', 'ConduitAPIRequest' => 'applications/conduit/protocol/ConduitAPIRequest.php', @@ -586,7 +590,6 @@ phutil_register_library_map(array( 'DifferentialQueryConduitAPIMethod' => 'applications/differential/conduit/DifferentialQueryConduitAPIMethod.php', 'DifferentialQueryDiffsConduitAPIMethod' => 'applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php', 'DifferentialRawDiffRenderer' => 'applications/differential/render/DifferentialRawDiffRenderer.php', - 'DifferentialReleephRequestFieldSpecification' => 'applications/releeph/differential/DifferentialReleephRequestFieldSpecification.php', 'DifferentialRemarkupRule' => 'applications/differential/remarkup/DifferentialRemarkupRule.php', 'DifferentialReplyHandler' => 'applications/differential/mail/DifferentialReplyHandler.php', 'DifferentialRepositoryField' => 'applications/differential/customfield/DifferentialRepositoryField.php', @@ -904,6 +907,7 @@ phutil_register_library_map(array( 'DiffusionLowLevelResolveRefsQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php', 'DiffusionMercurialBlameQuery' => 'applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php', 'DiffusionMercurialCommandEngine' => 'applications/diffusion/protocol/DiffusionMercurialCommandEngine.php', + 'DiffusionMercurialCommandEngineTests' => 'applications/diffusion/protocol/__tests__/DiffusionMercurialCommandEngineTests.php', 'DiffusionMercurialFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionMercurialFileContentQuery.php', 'DiffusionMercurialFlagInjectionException' => 'applications/diffusion/exception/DiffusionMercurialFlagInjectionException.php', 'DiffusionMercurialRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionMercurialRawDiffQuery.php', @@ -1227,6 +1231,8 @@ phutil_register_library_map(array( 'DrydockLeaseStatus' => 'applications/drydock/constants/DrydockLeaseStatus.php', 'DrydockLeaseUpdateWorker' => 'applications/drydock/worker/DrydockLeaseUpdateWorker.php', 'DrydockLeaseViewController' => 'applications/drydock/controller/DrydockLeaseViewController.php', + 'DrydockLeaseWaitingForActivationLogType' => 'applications/drydock/logtype/DrydockLeaseWaitingForActivationLogType.php', + 'DrydockLeaseWaitingForReclamationLogType' => 'applications/drydock/logtype/DrydockLeaseWaitingForReclamationLogType.php', 'DrydockLeaseWaitingForResourcesLogType' => 'applications/drydock/logtype/DrydockLeaseWaitingForResourcesLogType.php', 'DrydockLog' => 'applications/drydock/storage/DrydockLog.php', 'DrydockLogController' => 'applications/drydock/controller/DrydockLogController.php', @@ -1290,7 +1296,6 @@ phutil_register_library_map(array( 'DrydockWorkingCopyBlueprintImplementation' => 'applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php', 'EdgeSearchConduitAPIMethod' => 'infrastructure/edges/conduit/EdgeSearchConduitAPIMethod.php', 'FeedConduitAPIMethod' => 'applications/feed/conduit/FeedConduitAPIMethod.php', - 'FeedPublishConduitAPIMethod' => 'applications/feed/conduit/FeedPublishConduitAPIMethod.php', 'FeedPublisherHTTPWorker' => 'applications/feed/worker/FeedPublisherHTTPWorker.php', 'FeedPublisherWorker' => 'applications/feed/worker/FeedPublisherWorker.php', 'FeedPushWorker' => 'applications/feed/worker/FeedPushWorker.php', @@ -1386,8 +1391,9 @@ phutil_register_library_map(array( 'HarbormasterBuildArtifactPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildArtifactPHIDType.php', 'HarbormasterBuildArtifactQuery' => 'applications/harbormaster/query/HarbormasterBuildArtifactQuery.php', 'HarbormasterBuildAutoplan' => 'applications/harbormaster/autoplan/HarbormasterBuildAutoplan.php', - 'HarbormasterBuildCommand' => 'applications/harbormaster/storage/HarbormasterBuildCommand.php', 'HarbormasterBuildDependencyDatasource' => 'applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php', + 'HarbormasterBuildEditAPIMethod' => 'applications/harbormaster/conduit/HarbormasterBuildEditAPIMethod.php', + 'HarbormasterBuildEditEngine' => 'applications/harbormaster/editor/HarbormasterBuildEditEngine.php', 'HarbormasterBuildEngine' => 'applications/harbormaster/engine/HarbormasterBuildEngine.php', 'HarbormasterBuildFailureException' => 'applications/harbormaster/exception/HarbormasterBuildFailureException.php', 'HarbormasterBuildGraph' => 'applications/harbormaster/engine/HarbormasterBuildGraph.php', @@ -1407,7 +1413,12 @@ phutil_register_library_map(array( 'HarbormasterBuildLogView' => 'applications/harbormaster/view/HarbormasterBuildLogView.php', 'HarbormasterBuildLogViewController' => 'applications/harbormaster/controller/HarbormasterBuildLogViewController.php', 'HarbormasterBuildMessage' => 'applications/harbormaster/storage/HarbormasterBuildMessage.php', + 'HarbormasterBuildMessageAbortTransaction' => 'applications/harbormaster/xaction/build/HarbormasterBuildMessageAbortTransaction.php', + 'HarbormasterBuildMessagePauseTransaction' => 'applications/harbormaster/xaction/build/HarbormasterBuildMessagePauseTransaction.php', 'HarbormasterBuildMessageQuery' => 'applications/harbormaster/query/HarbormasterBuildMessageQuery.php', + 'HarbormasterBuildMessageRestartTransaction' => 'applications/harbormaster/xaction/build/HarbormasterBuildMessageRestartTransaction.php', + 'HarbormasterBuildMessageResumeTransaction' => 'applications/harbormaster/xaction/build/HarbormasterBuildMessageResumeTransaction.php', + 'HarbormasterBuildMessageTransaction' => 'applications/harbormaster/xaction/build/HarbormasterBuildMessageTransaction.php', 'HarbormasterBuildPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildPHIDType.php', 'HarbormasterBuildPlan' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php', 'HarbormasterBuildPlanBehavior' => 'applications/harbormaster/plan/HarbormasterBuildPlanBehavior.php', @@ -1458,6 +1469,7 @@ phutil_register_library_map(array( 'HarbormasterBuildTransaction' => 'applications/harbormaster/storage/HarbormasterBuildTransaction.php', 'HarbormasterBuildTransactionEditor' => 'applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php', 'HarbormasterBuildTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildTransactionQuery.php', + 'HarbormasterBuildTransactionType' => 'applications/harbormaster/xaction/build/HarbormasterBuildTransactionType.php', 'HarbormasterBuildUnitMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php', 'HarbormasterBuildUnitMessageQuery' => 'applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php', 'HarbormasterBuildView' => 'applications/harbormaster/view/HarbormasterBuildView.php', @@ -1466,9 +1478,12 @@ phutil_register_library_map(array( 'HarbormasterBuildable' => 'applications/harbormaster/storage/HarbormasterBuildable.php', 'HarbormasterBuildableActionController' => 'applications/harbormaster/controller/HarbormasterBuildableActionController.php', 'HarbormasterBuildableAdapterInterface' => 'applications/harbormaster/herald/HarbormasterBuildableAdapterInterface.php', + 'HarbormasterBuildableEditAPIMethod' => 'applications/harbormaster/conduit/HarbormasterBuildableEditAPIMethod.php', + 'HarbormasterBuildableEditEngine' => 'applications/harbormaster/editor/HarbormasterBuildableEditEngine.php', 'HarbormasterBuildableEngine' => 'applications/harbormaster/engine/HarbormasterBuildableEngine.php', 'HarbormasterBuildableInterface' => 'applications/harbormaster/interface/HarbormasterBuildableInterface.php', 'HarbormasterBuildableListController' => 'applications/harbormaster/controller/HarbormasterBuildableListController.php', + 'HarbormasterBuildableMessageTransaction' => 'applications/harbormaster/xaction/buildable/HarbormasterBuildableMessageTransaction.php', 'HarbormasterBuildablePHIDType' => 'applications/harbormaster/phid/HarbormasterBuildablePHIDType.php', 'HarbormasterBuildableQuery' => 'applications/harbormaster/query/HarbormasterBuildableQuery.php', 'HarbormasterBuildableSearchAPIMethod' => 'applications/harbormaster/conduit/HarbormasterBuildableSearchAPIMethod.php', @@ -1477,6 +1492,7 @@ phutil_register_library_map(array( 'HarbormasterBuildableTransaction' => 'applications/harbormaster/storage/HarbormasterBuildableTransaction.php', 'HarbormasterBuildableTransactionEditor' => 'applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php', 'HarbormasterBuildableTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildableTransactionQuery.php', + 'HarbormasterBuildableTransactionType' => 'applications/harbormaster/xaction/buildable/HarbormasterBuildableTransactionType.php', 'HarbormasterBuildableViewController' => 'applications/harbormaster/controller/HarbormasterBuildableViewController.php', 'HarbormasterBuildkiteBuildStepImplementation' => 'applications/harbormaster/integration/buildkite/HarbormasterBuildkiteBuildStepImplementation.php', 'HarbormasterBuildkiteBuildableInterface' => 'applications/harbormaster/interface/HarbormasterBuildkiteBuildableInterface.php', @@ -1513,6 +1529,7 @@ phutil_register_library_map(array( 'HarbormasterManagementUpdateWorkflow' => 'applications/harbormaster/management/HarbormasterManagementUpdateWorkflow.php', 'HarbormasterManagementWorkflow' => 'applications/harbormaster/management/HarbormasterManagementWorkflow.php', 'HarbormasterManagementWriteLogWorkflow' => 'applications/harbormaster/management/HarbormasterManagementWriteLogWorkflow.php', + 'HarbormasterMessageException' => 'applications/harbormaster/exception/HarbormasterMessageException.php', 'HarbormasterMessageType' => 'applications/harbormaster/engine/HarbormasterMessageType.php', 'HarbormasterObject' => 'applications/harbormaster/storage/HarbormasterObject.php', 'HarbormasterOtherBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterOtherBuildStepGroup.php', @@ -1524,13 +1541,11 @@ phutil_register_library_map(array( 'HarbormasterPlanRunController' => 'applications/harbormaster/controller/HarbormasterPlanRunController.php', 'HarbormasterPlanViewController' => 'applications/harbormaster/controller/HarbormasterPlanViewController.php', 'HarbormasterPrototypeBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterPrototypeBuildStepGroup.php', - 'HarbormasterPublishFragmentBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php', 'HarbormasterQueryAutotargetsConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryAutotargetsConduitAPIMethod.php', 'HarbormasterQueryBuildablesConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryBuildablesConduitAPIMethod.php', 'HarbormasterQueryBuildsConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php', 'HarbormasterQueryBuildsSearchEngineAttachment' => 'applications/harbormaster/engineextension/HarbormasterQueryBuildsSearchEngineAttachment.php', 'HarbormasterRemarkupRule' => 'applications/harbormaster/remarkup/HarbormasterRemarkupRule.php', - 'HarbormasterRestartException' => 'applications/harbormaster/exception/HarbormasterRestartException.php', 'HarbormasterRunBuildPlansHeraldAction' => 'applications/harbormaster/herald/HarbormasterRunBuildPlansHeraldAction.php', 'HarbormasterSchemaSpec' => 'applications/harbormaster/storage/HarbormasterSchemaSpec.php', 'HarbormasterScratchTable' => 'applications/harbormaster/storage/HarbormasterScratchTable.php', @@ -2002,7 +2017,6 @@ phutil_register_library_map(array( 'OwnersConduitAPIMethod' => 'applications/owners/conduit/OwnersConduitAPIMethod.php', 'OwnersEditConduitAPIMethod' => 'applications/owners/conduit/OwnersEditConduitAPIMethod.php', 'OwnersPackageReplyHandler' => 'applications/owners/mail/OwnersPackageReplyHandler.php', - 'OwnersQueryConduitAPIMethod' => 'applications/owners/conduit/OwnersQueryConduitAPIMethod.php', 'OwnersSearchConduitAPIMethod' => 'applications/owners/conduit/OwnersSearchConduitAPIMethod.php', 'PHIDConduitAPIMethod' => 'applications/phid/conduit/PHIDConduitAPIMethod.php', 'PHIDInfoConduitAPIMethod' => 'applications/phid/conduit/PHIDInfoConduitAPIMethod.php', @@ -3441,6 +3455,8 @@ phutil_register_library_map(array( 'PhabricatorFile' => 'applications/files/storage/PhabricatorFile.php', 'PhabricatorFileAES256StorageFormat' => 'applications/files/format/PhabricatorFileAES256StorageFormat.php', 'PhabricatorFileAltTextTransaction' => 'applications/files/xaction/PhabricatorFileAltTextTransaction.php', + 'PhabricatorFileAttachment' => 'applications/files/storage/PhabricatorFileAttachment.php', + 'PhabricatorFileAttachmentQuery' => 'applications/files/query/PhabricatorFileAttachmentQuery.php', 'PhabricatorFileBundleLoader' => 'applications/files/query/PhabricatorFileBundleLoader.php', 'PhabricatorFileChunk' => 'applications/files/storage/PhabricatorFileChunk.php', 'PhabricatorFileChunkIterator' => 'applications/files/engine/PhabricatorFileChunkIterator.php', @@ -3451,6 +3467,7 @@ phutil_register_library_map(array( 'PhabricatorFileDataController' => 'applications/files/controller/PhabricatorFileDataController.php', 'PhabricatorFileDeleteController' => 'applications/files/controller/PhabricatorFileDeleteController.php', 'PhabricatorFileDeleteTransaction' => 'applications/files/xaction/PhabricatorFileDeleteTransaction.php', + 'PhabricatorFileDetachController' => 'applications/files/controller/PhabricatorFileDetachController.php', 'PhabricatorFileDocumentController' => 'applications/files/controller/PhabricatorFileDocumentController.php', 'PhabricatorFileDocumentRenderingEngine' => 'applications/files/document/render/PhabricatorFileDocumentRenderingEngine.php', 'PhabricatorFileDropUploadController' => 'applications/files/controller/PhabricatorFileDropUploadController.php', @@ -3461,7 +3478,6 @@ phutil_register_library_map(array( 'PhabricatorFileExternalRequest' => 'applications/files/storage/PhabricatorFileExternalRequest.php', 'PhabricatorFileExternalRequestGarbageCollector' => 'applications/files/garbagecollector/PhabricatorFileExternalRequestGarbageCollector.php', 'PhabricatorFileFilePHIDType' => 'applications/files/phid/PhabricatorFileFilePHIDType.php', - 'PhabricatorFileHasObjectEdgeType' => 'applications/files/edge/PhabricatorFileHasObjectEdgeType.php', 'PhabricatorFileIconSetSelectController' => 'applications/files/controller/PhabricatorFileIconSetSelectController.php', 'PhabricatorFileImageMacro' => 'applications/macro/storage/PhabricatorFileImageMacro.php', 'PhabricatorFileImageProxyController' => 'applications/files/controller/PhabricatorFileImageProxyController.php', @@ -3496,6 +3512,8 @@ phutil_register_library_map(array( 'PhabricatorFileTransformController' => 'applications/files/controller/PhabricatorFileTransformController.php', 'PhabricatorFileTransformListController' => 'applications/files/controller/PhabricatorFileTransformListController.php', 'PhabricatorFileTransformTestCase' => 'applications/files/transform/__tests__/PhabricatorFileTransformTestCase.php', + 'PhabricatorFileUICurtainAttachController' => 'applications/files/controller/PhabricatorFileUICurtainAttachController.php', + 'PhabricatorFileUICurtainListController' => 'applications/files/controller/PhabricatorFileUICurtainListController.php', 'PhabricatorFileUploadController' => 'applications/files/controller/PhabricatorFileUploadController.php', 'PhabricatorFileUploadDialogController' => 'applications/files/controller/PhabricatorFileUploadDialogController.php', 'PhabricatorFileUploadException' => 'applications/files/exception/PhabricatorFileUploadException.php', @@ -3509,6 +3527,7 @@ phutil_register_library_map(array( 'PhabricatorFilesComposeAvatarBuiltinFile' => 'applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php', 'PhabricatorFilesComposeIconBuiltinFile' => 'applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php', 'PhabricatorFilesConfigOptions' => 'applications/files/config/PhabricatorFilesConfigOptions.php', + 'PhabricatorFilesCurtainExtension' => 'applications/files/engineextension/PhabricatorFilesCurtainExtension.php', 'PhabricatorFilesManagementCatWorkflow' => 'applications/files/management/PhabricatorFilesManagementCatWorkflow.php', 'PhabricatorFilesManagementCompactWorkflow' => 'applications/files/management/PhabricatorFilesManagementCompactWorkflow.php', 'PhabricatorFilesManagementCycleWorkflow' => 'applications/files/management/PhabricatorFilesManagementCycleWorkflow.php', @@ -3926,7 +3945,6 @@ phutil_register_library_map(array( 'PhabricatorObjectHasAsanaTaskEdgeType' => 'applications/doorkeeper/edge/PhabricatorObjectHasAsanaTaskEdgeType.php', 'PhabricatorObjectHasContributorEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasContributorEdgeType.php', 'PhabricatorObjectHasDraftEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasDraftEdgeType.php', - 'PhabricatorObjectHasFileEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasFileEdgeType.php', 'PhabricatorObjectHasJiraIssueEdgeType' => 'applications/doorkeeper/edge/PhabricatorObjectHasJiraIssueEdgeType.php', 'PhabricatorObjectHasSubscriberEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasSubscriberEdgeType.php', 'PhabricatorObjectHasUnsubscriberEdgeType' => 'applications/transactions/edges/PhabricatorObjectHasUnsubscriberEdgeType.php', @@ -3973,6 +3991,7 @@ phutil_register_library_map(array( 'PhabricatorOwnersOwner' => 'applications/owners/storage/PhabricatorOwnersOwner.php', 'PhabricatorOwnersPackage' => 'applications/owners/storage/PhabricatorOwnersPackage.php', 'PhabricatorOwnersPackageAuditingTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackageAuditingTransaction.php', + 'PhabricatorOwnersPackageAuthorityTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackageAuthorityTransaction.php', 'PhabricatorOwnersPackageAutoreviewTransaction' => 'applications/owners/xaction/PhabricatorOwnersPackageAutoreviewTransaction.php', 'PhabricatorOwnersPackageContextFreeGrammar' => 'applications/owners/lipsum/PhabricatorOwnersPackageContextFreeGrammar.php', 'PhabricatorOwnersPackageDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php', @@ -4227,7 +4246,6 @@ phutil_register_library_map(array( 'PhabricatorPhortuneManagementInvoiceWorkflow' => 'applications/phortune/management/PhabricatorPhortuneManagementInvoiceWorkflow.php', 'PhabricatorPhortuneManagementWorkflow' => 'applications/phortune/management/PhabricatorPhortuneManagementWorkflow.php', 'PhabricatorPhortuneTestCase' => 'applications/phortune/__tests__/PhabricatorPhortuneTestCase.php', - 'PhabricatorPhragmentApplication' => 'applications/phragment/application/PhabricatorPhragmentApplication.php', 'PhabricatorPhrequentApplication' => 'applications/phrequent/application/PhabricatorPhrequentApplication.php', 'PhabricatorPhrictionApplication' => 'applications/phriction/application/PhabricatorPhrictionApplication.php', 'PhabricatorPhurlApplication' => 'applications/phurl/application/PhabricatorPhurlApplication.php', @@ -4549,8 +4567,6 @@ phutil_register_library_map(array( 'PhabricatorRefreshCSRFController' => 'applications/auth/controller/PhabricatorRefreshCSRFController.php', 'PhabricatorRegexListConfigType' => 'applications/config/type/PhabricatorRegexListConfigType.php', 'PhabricatorRegistrationProfile' => 'applications/people/storage/PhabricatorRegistrationProfile.php', - 'PhabricatorReleephApplication' => 'applications/releeph/application/PhabricatorReleephApplication.php', - 'PhabricatorReleephApplicationConfigOptions' => 'applications/releeph/config/PhabricatorReleephApplicationConfigOptions.php', 'PhabricatorRemarkupCachePurger' => 'applications/cache/purger/PhabricatorRemarkupCachePurger.php', 'PhabricatorRemarkupControl' => 'view/form/control/PhabricatorRemarkupControl.php', 'PhabricatorRemarkupCowsayBlockInterpreter' => 'infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php', @@ -4823,7 +4839,6 @@ phutil_register_library_map(array( 'PhabricatorSlowvoteApplication' => 'applications/slowvote/application/PhabricatorSlowvoteApplication.php', 'PhabricatorSlowvoteChoice' => 'applications/slowvote/storage/PhabricatorSlowvoteChoice.php', 'PhabricatorSlowvoteCloseController' => 'applications/slowvote/controller/PhabricatorSlowvoteCloseController.php', - 'PhabricatorSlowvoteCloseTransaction' => 'applications/slowvote/xaction/PhabricatorSlowvoteCloseTransaction.php', 'PhabricatorSlowvoteCommentController' => 'applications/slowvote/controller/PhabricatorSlowvoteCommentController.php', 'PhabricatorSlowvoteController' => 'applications/slowvote/controller/PhabricatorSlowvoteController.php', 'PhabricatorSlowvoteDAO' => 'applications/slowvote/storage/PhabricatorSlowvoteDAO.php', @@ -4844,11 +4859,13 @@ phutil_register_library_map(array( 'PhabricatorSlowvoteSchemaSpec' => 'applications/slowvote/storage/PhabricatorSlowvoteSchemaSpec.php', 'PhabricatorSlowvoteSearchEngine' => 'applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php', 'PhabricatorSlowvoteShuffleTransaction' => 'applications/slowvote/xaction/PhabricatorSlowvoteShuffleTransaction.php', + 'PhabricatorSlowvoteStatusTransaction' => 'applications/slowvote/xaction/PhabricatorSlowvoteStatusTransaction.php', 'PhabricatorSlowvoteTransaction' => 'applications/slowvote/storage/PhabricatorSlowvoteTransaction.php', 'PhabricatorSlowvoteTransactionComment' => 'applications/slowvote/storage/PhabricatorSlowvoteTransactionComment.php', 'PhabricatorSlowvoteTransactionQuery' => 'applications/slowvote/query/PhabricatorSlowvoteTransactionQuery.php', 'PhabricatorSlowvoteTransactionType' => 'applications/slowvote/xaction/PhabricatorSlowvoteTransactionType.php', 'PhabricatorSlowvoteVoteController' => 'applications/slowvote/controller/PhabricatorSlowvoteVoteController.php', + 'PhabricatorSlowvoteVotingMethodTransaction' => 'applications/slowvote/xaction/PhabricatorSlowvoteVotingMethodTransaction.php', 'PhabricatorSlug' => 'infrastructure/util/PhabricatorSlug.php', 'PhabricatorSlugTestCase' => 'infrastructure/util/__tests__/PhabricatorSlugTestCase.php', 'PhabricatorSourceCodeView' => 'view/layout/PhabricatorSourceCodeView.php', @@ -5269,6 +5286,7 @@ phutil_register_library_map(array( 'PhameDescriptionView' => 'applications/phame/view/PhameDescriptionView.php', 'PhameDraftListView' => 'applications/phame/view/PhameDraftListView.php', 'PhameHomeController' => 'applications/phame/controller/PhameHomeController.php', + 'PhameInheritBlogPolicyRule' => 'applications/phame/policyrule/PhameInheritBlogPolicyRule.php', 'PhameLiveController' => 'applications/phame/controller/PhameLiveController.php', 'PhameNextPostView' => 'applications/phame/view/PhameNextPostView.php', 'PhamePost' => 'applications/phame/storage/PhamePost.php', @@ -5279,6 +5297,7 @@ phutil_register_library_map(array( 'PhamePostEditConduitAPIMethod' => 'applications/phame/conduit/PhamePostEditConduitAPIMethod.php', 'PhamePostEditController' => 'applications/phame/controller/post/PhamePostEditController.php', 'PhamePostEditEngine' => 'applications/phame/editor/PhamePostEditEngine.php', + 'PhamePostEditEngineLock' => 'applications/phame/editor/PhamePostEditEngineLock.php', 'PhamePostEditor' => 'applications/phame/editor/PhamePostEditor.php', 'PhamePostFerretEngine' => 'applications/phame/search/PhamePostFerretEngine.php', 'PhamePostFulltextEngine' => 'applications/phame/search/PhamePostFulltextEngine.php', @@ -5552,38 +5571,6 @@ phutil_register_library_map(array( 'PhortuneSubscriptionTransactionType' => 'applications/phortune/xaction/subscription/PhortuneSubscriptionTransactionType.php', 'PhortuneSubscriptionWorker' => 'applications/phortune/worker/PhortuneSubscriptionWorker.php', 'PhortuneTestPaymentProvider' => 'applications/phortune/provider/PhortuneTestPaymentProvider.php', - 'PhragmentBrowseController' => 'applications/phragment/controller/PhragmentBrowseController.php', - 'PhragmentCanCreateCapability' => 'applications/phragment/capability/PhragmentCanCreateCapability.php', - 'PhragmentConduitAPIMethod' => 'applications/phragment/conduit/PhragmentConduitAPIMethod.php', - 'PhragmentController' => 'applications/phragment/controller/PhragmentController.php', - 'PhragmentCreateController' => 'applications/phragment/controller/PhragmentCreateController.php', - 'PhragmentDAO' => 'applications/phragment/storage/PhragmentDAO.php', - 'PhragmentFragment' => 'applications/phragment/storage/PhragmentFragment.php', - 'PhragmentFragmentPHIDType' => 'applications/phragment/phid/PhragmentFragmentPHIDType.php', - 'PhragmentFragmentQuery' => 'applications/phragment/query/PhragmentFragmentQuery.php', - 'PhragmentFragmentVersion' => 'applications/phragment/storage/PhragmentFragmentVersion.php', - 'PhragmentFragmentVersionPHIDType' => 'applications/phragment/phid/PhragmentFragmentVersionPHIDType.php', - 'PhragmentFragmentVersionQuery' => 'applications/phragment/query/PhragmentFragmentVersionQuery.php', - 'PhragmentGetPatchConduitAPIMethod' => 'applications/phragment/conduit/PhragmentGetPatchConduitAPIMethod.php', - 'PhragmentHistoryController' => 'applications/phragment/controller/PhragmentHistoryController.php', - 'PhragmentPatchController' => 'applications/phragment/controller/PhragmentPatchController.php', - 'PhragmentPatchUtil' => 'applications/phragment/util/PhragmentPatchUtil.php', - 'PhragmentPolicyController' => 'applications/phragment/controller/PhragmentPolicyController.php', - 'PhragmentQueryFragmentsConduitAPIMethod' => 'applications/phragment/conduit/PhragmentQueryFragmentsConduitAPIMethod.php', - 'PhragmentRevertController' => 'applications/phragment/controller/PhragmentRevertController.php', - 'PhragmentSchemaSpec' => 'applications/phragment/storage/PhragmentSchemaSpec.php', - 'PhragmentSnapshot' => 'applications/phragment/storage/PhragmentSnapshot.php', - 'PhragmentSnapshotChild' => 'applications/phragment/storage/PhragmentSnapshotChild.php', - 'PhragmentSnapshotChildQuery' => 'applications/phragment/query/PhragmentSnapshotChildQuery.php', - 'PhragmentSnapshotCreateController' => 'applications/phragment/controller/PhragmentSnapshotCreateController.php', - 'PhragmentSnapshotDeleteController' => 'applications/phragment/controller/PhragmentSnapshotDeleteController.php', - 'PhragmentSnapshotPHIDType' => 'applications/phragment/phid/PhragmentSnapshotPHIDType.php', - 'PhragmentSnapshotPromoteController' => 'applications/phragment/controller/PhragmentSnapshotPromoteController.php', - 'PhragmentSnapshotQuery' => 'applications/phragment/query/PhragmentSnapshotQuery.php', - 'PhragmentSnapshotViewController' => 'applications/phragment/controller/PhragmentSnapshotViewController.php', - 'PhragmentUpdateController' => 'applications/phragment/controller/PhragmentUpdateController.php', - 'PhragmentVersionController' => 'applications/phragment/controller/PhragmentVersionController.php', - 'PhragmentZIPController' => 'applications/phragment/controller/PhragmentZIPController.php', 'PhrequentConduitAPIMethod' => 'applications/phrequent/conduit/PhrequentConduitAPIMethod.php', 'PhrequentController' => 'applications/phrequent/controller/PhrequentController.php', 'PhrequentCurtainExtension' => 'applications/phrequent/engineextension/PhrequentCurtainExtension.php', @@ -5763,6 +5750,7 @@ phutil_register_library_map(array( 'PhutilRemarkupEngine' => 'infrastructure/markup/remarkup/PhutilRemarkupEngine.php', 'PhutilRemarkupEngineTestCase' => 'infrastructure/markup/remarkup/__tests__/PhutilRemarkupEngineTestCase.php', 'PhutilRemarkupEscapeRemarkupRule' => 'infrastructure/markup/markuprule/PhutilRemarkupEscapeRemarkupRule.php', + 'PhutilRemarkupEvalRule' => 'infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php', 'PhutilRemarkupHeaderBlockRule' => 'infrastructure/markup/blockrule/PhutilRemarkupHeaderBlockRule.php', 'PhutilRemarkupHighlightRule' => 'infrastructure/markup/markuprule/PhutilRemarkupHighlightRule.php', 'PhutilRemarkupHorizontalRuleBlockRule' => 'infrastructure/markup/blockrule/PhutilRemarkupHorizontalRuleBlockRule.php', @@ -5883,100 +5871,17 @@ phutil_register_library_map(array( 'ProjectSearchConduitAPIMethod' => 'applications/project/conduit/ProjectSearchConduitAPIMethod.php', 'QueryFormattingTestCase' => 'infrastructure/storage/__tests__/QueryFormattingTestCase.php', 'QueryFuture' => 'infrastructure/storage/future/QueryFuture.php', - 'ReleephAuthorFieldSpecification' => 'applications/releeph/field/specification/ReleephAuthorFieldSpecification.php', - 'ReleephBranch' => 'applications/releeph/storage/ReleephBranch.php', - 'ReleephBranchAccessController' => 'applications/releeph/controller/branch/ReleephBranchAccessController.php', - 'ReleephBranchCommitFieldSpecification' => 'applications/releeph/field/specification/ReleephBranchCommitFieldSpecification.php', - 'ReleephBranchController' => 'applications/releeph/controller/branch/ReleephBranchController.php', - 'ReleephBranchCreateController' => 'applications/releeph/controller/branch/ReleephBranchCreateController.php', - 'ReleephBranchEditController' => 'applications/releeph/controller/branch/ReleephBranchEditController.php', - 'ReleephBranchEditor' => 'applications/releeph/editor/ReleephBranchEditor.php', - 'ReleephBranchHistoryController' => 'applications/releeph/controller/branch/ReleephBranchHistoryController.php', - 'ReleephBranchNamePreviewController' => 'applications/releeph/controller/branch/ReleephBranchNamePreviewController.php', - 'ReleephBranchPHIDType' => 'applications/releeph/phid/ReleephBranchPHIDType.php', - 'ReleephBranchPreviewView' => 'applications/releeph/view/branch/ReleephBranchPreviewView.php', - 'ReleephBranchQuery' => 'applications/releeph/query/ReleephBranchQuery.php', - 'ReleephBranchSearchEngine' => 'applications/releeph/query/ReleephBranchSearchEngine.php', - 'ReleephBranchTemplate' => 'applications/releeph/view/branch/ReleephBranchTemplate.php', - 'ReleephBranchTransaction' => 'applications/releeph/storage/ReleephBranchTransaction.php', - 'ReleephBranchTransactionQuery' => 'applications/releeph/query/ReleephBranchTransactionQuery.php', - 'ReleephBranchViewController' => 'applications/releeph/controller/branch/ReleephBranchViewController.php', - 'ReleephCommitFinder' => 'applications/releeph/commitfinder/ReleephCommitFinder.php', - 'ReleephCommitFinderException' => 'applications/releeph/commitfinder/ReleephCommitFinderException.php', - 'ReleephCommitMessageFieldSpecification' => 'applications/releeph/field/specification/ReleephCommitMessageFieldSpecification.php', - 'ReleephConduitAPIMethod' => 'applications/releeph/conduit/ReleephConduitAPIMethod.php', - 'ReleephController' => 'applications/releeph/controller/ReleephController.php', - 'ReleephDAO' => 'applications/releeph/storage/ReleephDAO.php', - 'ReleephDefaultFieldSelector' => 'applications/releeph/field/selector/ReleephDefaultFieldSelector.php', - 'ReleephDependsOnFieldSpecification' => 'applications/releeph/field/specification/ReleephDependsOnFieldSpecification.php', - 'ReleephDiffChurnFieldSpecification' => 'applications/releeph/field/specification/ReleephDiffChurnFieldSpecification.php', - 'ReleephDiffMessageFieldSpecification' => 'applications/releeph/field/specification/ReleephDiffMessageFieldSpecification.php', - 'ReleephDiffSizeFieldSpecification' => 'applications/releeph/field/specification/ReleephDiffSizeFieldSpecification.php', - 'ReleephFieldParseException' => 'applications/releeph/field/exception/ReleephFieldParseException.php', - 'ReleephFieldSelector' => 'applications/releeph/field/selector/ReleephFieldSelector.php', - 'ReleephFieldSpecification' => 'applications/releeph/field/specification/ReleephFieldSpecification.php', - 'ReleephGetBranchesConduitAPIMethod' => 'applications/releeph/conduit/ReleephGetBranchesConduitAPIMethod.php', - 'ReleephIntentFieldSpecification' => 'applications/releeph/field/specification/ReleephIntentFieldSpecification.php', - 'ReleephLevelFieldSpecification' => 'applications/releeph/field/specification/ReleephLevelFieldSpecification.php', - 'ReleephOriginalCommitFieldSpecification' => 'applications/releeph/field/specification/ReleephOriginalCommitFieldSpecification.php', - 'ReleephProductActionController' => 'applications/releeph/controller/product/ReleephProductActionController.php', - 'ReleephProductController' => 'applications/releeph/controller/product/ReleephProductController.php', - 'ReleephProductCreateController' => 'applications/releeph/controller/product/ReleephProductCreateController.php', - 'ReleephProductEditController' => 'applications/releeph/controller/product/ReleephProductEditController.php', - 'ReleephProductEditor' => 'applications/releeph/editor/ReleephProductEditor.php', - 'ReleephProductHistoryController' => 'applications/releeph/controller/product/ReleephProductHistoryController.php', - 'ReleephProductListController' => 'applications/releeph/controller/product/ReleephProductListController.php', - 'ReleephProductPHIDType' => 'applications/releeph/phid/ReleephProductPHIDType.php', - 'ReleephProductQuery' => 'applications/releeph/query/ReleephProductQuery.php', - 'ReleephProductSearchEngine' => 'applications/releeph/query/ReleephProductSearchEngine.php', - 'ReleephProductTransaction' => 'applications/releeph/storage/ReleephProductTransaction.php', - 'ReleephProductTransactionQuery' => 'applications/releeph/query/ReleephProductTransactionQuery.php', - 'ReleephProductViewController' => 'applications/releeph/controller/product/ReleephProductViewController.php', - 'ReleephProject' => 'applications/releeph/storage/ReleephProject.php', - 'ReleephQueryBranchesConduitAPIMethod' => 'applications/releeph/conduit/ReleephQueryBranchesConduitAPIMethod.php', - 'ReleephQueryProductsConduitAPIMethod' => 'applications/releeph/conduit/ReleephQueryProductsConduitAPIMethod.php', - 'ReleephQueryRequestsConduitAPIMethod' => 'applications/releeph/conduit/ReleephQueryRequestsConduitAPIMethod.php', - 'ReleephReasonFieldSpecification' => 'applications/releeph/field/specification/ReleephReasonFieldSpecification.php', - 'ReleephRequest' => 'applications/releeph/storage/ReleephRequest.php', - 'ReleephRequestActionController' => 'applications/releeph/controller/request/ReleephRequestActionController.php', - 'ReleephRequestCommentController' => 'applications/releeph/controller/request/ReleephRequestCommentController.php', - 'ReleephRequestConduitAPIMethod' => 'applications/releeph/conduit/ReleephRequestConduitAPIMethod.php', - 'ReleephRequestController' => 'applications/releeph/controller/request/ReleephRequestController.php', - 'ReleephRequestDifferentialCreateController' => 'applications/releeph/controller/request/ReleephRequestDifferentialCreateController.php', - 'ReleephRequestEditController' => 'applications/releeph/controller/request/ReleephRequestEditController.php', - 'ReleephRequestMailReceiver' => 'applications/releeph/mail/ReleephRequestMailReceiver.php', - 'ReleephRequestPHIDType' => 'applications/releeph/phid/ReleephRequestPHIDType.php', - 'ReleephRequestQuery' => 'applications/releeph/query/ReleephRequestQuery.php', - 'ReleephRequestReplyHandler' => 'applications/releeph/mail/ReleephRequestReplyHandler.php', - 'ReleephRequestSearchEngine' => 'applications/releeph/query/ReleephRequestSearchEngine.php', - 'ReleephRequestStatus' => 'applications/releeph/constants/ReleephRequestStatus.php', - 'ReleephRequestTransaction' => 'applications/releeph/storage/ReleephRequestTransaction.php', - 'ReleephRequestTransactionComment' => 'applications/releeph/storage/ReleephRequestTransactionComment.php', - 'ReleephRequestTransactionQuery' => 'applications/releeph/query/ReleephRequestTransactionQuery.php', - 'ReleephRequestTransactionalEditor' => 'applications/releeph/editor/ReleephRequestTransactionalEditor.php', - 'ReleephRequestTypeaheadControl' => 'applications/releeph/view/request/ReleephRequestTypeaheadControl.php', - 'ReleephRequestTypeaheadController' => 'applications/releeph/controller/request/ReleephRequestTypeaheadController.php', - 'ReleephRequestView' => 'applications/releeph/view/ReleephRequestView.php', - 'ReleephRequestViewController' => 'applications/releeph/controller/request/ReleephRequestViewController.php', - 'ReleephRequestorFieldSpecification' => 'applications/releeph/field/specification/ReleephRequestorFieldSpecification.php', - 'ReleephRevisionFieldSpecification' => 'applications/releeph/field/specification/ReleephRevisionFieldSpecification.php', - 'ReleephSeverityFieldSpecification' => 'applications/releeph/field/specification/ReleephSeverityFieldSpecification.php', - 'ReleephSummaryFieldSpecification' => 'applications/releeph/field/specification/ReleephSummaryFieldSpecification.php', - 'ReleephWorkCanPushConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkCanPushConduitAPIMethod.php', - 'ReleephWorkGetAuthorInfoConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkGetAuthorInfoConduitAPIMethod.php', - 'ReleephWorkGetBranchCommitMessageConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkGetBranchCommitMessageConduitAPIMethod.php', - 'ReleephWorkGetBranchConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkGetBranchConduitAPIMethod.php', - 'ReleephWorkGetCommitMessageConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkGetCommitMessageConduitAPIMethod.php', - 'ReleephWorkNextRequestConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkNextRequestConduitAPIMethod.php', - 'ReleephWorkRecordConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkRecordConduitAPIMethod.php', - 'ReleephWorkRecordPickStatusConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkRecordPickStatusConduitAPIMethod.php', 'RemarkupProcessConduitAPIMethod' => 'applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php', + 'RemarkupValue' => 'applications/remarkup/RemarkupValue.php', 'RepositoryConduitAPIMethod' => 'applications/repository/conduit/RepositoryConduitAPIMethod.php', 'RepositoryQueryConduitAPIMethod' => 'applications/repository/conduit/RepositoryQueryConduitAPIMethod.php', 'ShellLogView' => 'applications/harbormaster/view/ShellLogView.php', 'SlowvoteConduitAPIMethod' => 'applications/slowvote/conduit/SlowvoteConduitAPIMethod.php', 'SlowvoteEmbedView' => 'applications/slowvote/view/SlowvoteEmbedView.php', 'SlowvoteInfoConduitAPIMethod' => 'applications/slowvote/conduit/SlowvoteInfoConduitAPIMethod.php', + 'SlowvotePollResponseVisibility' => 'applications/slowvote/constants/SlowvotePollResponseVisibility.php', + 'SlowvotePollStatus' => 'applications/slowvote/constants/SlowvotePollStatus.php', + 'SlowvotePollVotingMethod' => 'applications/slowvote/constants/SlowvotePollVotingMethod.php', 'SlowvoteRemarkupRule' => 'applications/slowvote/remarkup/SlowvoteRemarkupRule.php', 'SlowvoteSearchConduitAPIMethod' => 'applications/slowvote/conduit/SlowvoteSearchConduitAPIMethod.php', 'SubscriptionListDialogBuilder' => 'applications/subscriptions/view/SubscriptionListDialogBuilder.php', @@ -6134,6 +6039,7 @@ phutil_register_library_map(array( 'AlmanacInterfaceSearchEngine' => 'PhabricatorApplicationSearchEngine', 'AlmanacInterfaceTableView' => 'AphrontView', 'AlmanacInterfaceTransaction' => 'AlmanacModularTransaction', + 'AlmanacInterfaceTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'AlmanacInterfaceTransactionType' => 'AlmanacTransactionType', 'AlmanacKeys' => 'Phobject', 'AlmanacManageClusterServicesCapability' => 'PhabricatorPolicyCapability', @@ -6319,6 +6225,7 @@ phutil_register_library_map(array( 'AphrontIsolatedDatabaseConnection' => 'AphrontDatabaseConnection', 'AphrontIsolatedDatabaseConnectionTestCase' => 'PhabricatorTestCase', 'AphrontIsolatedHTTPSink' => 'AphrontHTTPSink', + 'AphrontJSONHTTPParameterType' => 'AphrontHTTPParameterType', 'AphrontJSONResponse' => 'AphrontResponse', 'AphrontJavelinView' => 'AphrontView', 'AphrontKeyboardShortcutsAvailableView' => 'AphrontView', @@ -6355,6 +6262,7 @@ phutil_register_library_map(array( 'AphrontRedirectResponse' => 'AphrontResponse', 'AphrontRedirectResponseTestCase' => 'PhabricatorTestCase', 'AphrontReloadResponse' => 'AphrontRedirectResponse', + 'AphrontRemarkupHTTPParameterType' => 'AphrontHTTPParameterType', 'AphrontRequest' => 'Phobject', 'AphrontRequestExceptionHandler' => 'Phobject', 'AphrontRequestStream' => 'Phobject', @@ -6424,6 +6332,7 @@ phutil_register_library_map(array( 'ChatLogConduitAPIMethod' => 'ConduitAPIMethod', 'ChatLogQueryConduitAPIMethod' => 'ChatLogConduitAPIMethod', 'ChatLogRecordConduitAPIMethod' => 'ChatLogConduitAPIMethod', + 'ConduitAPIDocumentationPage' => 'Phobject', 'ConduitAPIMethod' => array( 'Phobject', 'PhabricatorPolicyInterface', @@ -6701,7 +6610,6 @@ phutil_register_library_map(array( 'DifferentialQueryConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialQueryDiffsConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialRawDiffRenderer' => 'Phobject', - 'DifferentialReleephRequestFieldSpecification' => 'Phobject', 'DifferentialRemarkupRule' => 'PhabricatorObjectRemarkupRule', 'DifferentialReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'DifferentialRepositoryField' => 'DifferentialCoreCustomField', @@ -7047,6 +6955,7 @@ phutil_register_library_map(array( 'DiffusionLowLevelResolveRefsQuery' => 'DiffusionLowLevelQuery', 'DiffusionMercurialBlameQuery' => 'DiffusionBlameQuery', 'DiffusionMercurialCommandEngine' => 'DiffusionCommandEngine', + 'DiffusionMercurialCommandEngineTests' => 'PhabricatorTestCase', 'DiffusionMercurialFileContentQuery' => 'DiffusionFileContentQuery', 'DiffusionMercurialFlagInjectionException' => 'Exception', 'DiffusionMercurialRawDiffQuery' => 'DiffusionRawDiffQuery', @@ -7405,6 +7314,8 @@ phutil_register_library_map(array( 'DrydockLeaseStatus' => 'PhabricatorObjectStatus', 'DrydockLeaseUpdateWorker' => 'DrydockWorker', 'DrydockLeaseViewController' => 'DrydockLeaseController', + 'DrydockLeaseWaitingForActivationLogType' => 'DrydockLogType', + 'DrydockLeaseWaitingForReclamationLogType' => 'DrydockLogType', 'DrydockLeaseWaitingForResourcesLogType' => 'DrydockLogType', 'DrydockLog' => array( 'DrydockDAO', @@ -7478,7 +7389,6 @@ phutil_register_library_map(array( 'DrydockWorkingCopyBlueprintImplementation' => 'DrydockBlueprintImplementation', 'EdgeSearchConduitAPIMethod' => 'ConduitAPIMethod', 'FeedConduitAPIMethod' => 'ConduitAPIMethod', - 'FeedPublishConduitAPIMethod' => 'FeedConduitAPIMethod', 'FeedPublisherHTTPWorker' => 'FeedPushWorker', 'FeedPublisherWorker' => 'FeedPushWorker', 'FeedPushWorker' => 'PhabricatorWorker', @@ -7601,8 +7511,9 @@ phutil_register_library_map(array( 'HarbormasterBuildArtifactPHIDType' => 'PhabricatorPHIDType', 'HarbormasterBuildArtifactQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'HarbormasterBuildAutoplan' => 'Phobject', - 'HarbormasterBuildCommand' => 'HarbormasterDAO', 'HarbormasterBuildDependencyDatasource' => 'PhabricatorTypeaheadDatasource', + 'HarbormasterBuildEditAPIMethod' => 'PhabricatorEditEngineAPIMethod', + 'HarbormasterBuildEditEngine' => 'PhabricatorEditEngine', 'HarbormasterBuildEngine' => 'Phobject', 'HarbormasterBuildFailureException' => 'Exception', 'HarbormasterBuildGraph' => 'AbstractDirectedGraph', @@ -7631,7 +7542,12 @@ phutil_register_library_map(array( 'PhabricatorPolicyInterface', 'PhabricatorDestructibleInterface', ), + 'HarbormasterBuildMessageAbortTransaction' => 'HarbormasterBuildMessageTransaction', + 'HarbormasterBuildMessagePauseTransaction' => 'HarbormasterBuildMessageTransaction', 'HarbormasterBuildMessageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'HarbormasterBuildMessageRestartTransaction' => 'HarbormasterBuildMessageTransaction', + 'HarbormasterBuildMessageResumeTransaction' => 'HarbormasterBuildMessageTransaction', + 'HarbormasterBuildMessageTransaction' => 'HarbormasterBuildTransactionType', 'HarbormasterBuildPHIDType' => 'PhabricatorPHIDType', 'HarbormasterBuildPlan' => array( 'HarbormasterDAO', @@ -7702,9 +7618,10 @@ phutil_register_library_map(array( 'HarbormasterBuildTargetPHIDType' => 'PhabricatorPHIDType', 'HarbormasterBuildTargetQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'HarbormasterBuildTargetSearchEngine' => 'PhabricatorApplicationSearchEngine', - 'HarbormasterBuildTransaction' => 'PhabricatorApplicationTransaction', + 'HarbormasterBuildTransaction' => 'PhabricatorModularTransaction', 'HarbormasterBuildTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 'HarbormasterBuildTransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'HarbormasterBuildTransactionType' => 'PhabricatorModularTransactionType', 'HarbormasterBuildUnitMessage' => array( 'HarbormasterDAO', 'PhabricatorPolicyInterface', @@ -7722,16 +7639,20 @@ phutil_register_library_map(array( 'PhabricatorDestructibleInterface', ), 'HarbormasterBuildableActionController' => 'HarbormasterController', + 'HarbormasterBuildableEditAPIMethod' => 'PhabricatorEditEngineAPIMethod', + 'HarbormasterBuildableEditEngine' => 'PhabricatorEditEngine', 'HarbormasterBuildableEngine' => 'Phobject', 'HarbormasterBuildableListController' => 'HarbormasterController', + 'HarbormasterBuildableMessageTransaction' => 'HarbormasterBuildableTransactionType', 'HarbormasterBuildablePHIDType' => 'PhabricatorPHIDType', 'HarbormasterBuildableQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'HarbormasterBuildableSearchAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'HarbormasterBuildableSearchEngine' => 'PhabricatorApplicationSearchEngine', 'HarbormasterBuildableStatus' => 'Phobject', - 'HarbormasterBuildableTransaction' => 'PhabricatorApplicationTransaction', + 'HarbormasterBuildableTransaction' => 'PhabricatorModularTransaction', 'HarbormasterBuildableTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 'HarbormasterBuildableTransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'HarbormasterBuildableTransactionType' => 'PhabricatorModularTransactionType', 'HarbormasterBuildableViewController' => 'HarbormasterController', 'HarbormasterBuildkiteBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 'HarbormasterBuildkiteHookHandler' => 'HarbormasterHookHandler', @@ -7766,6 +7687,7 @@ phutil_register_library_map(array( 'HarbormasterManagementUpdateWorkflow' => 'HarbormasterManagementWorkflow', 'HarbormasterManagementWorkflow' => 'PhabricatorManagementWorkflow', 'HarbormasterManagementWriteLogWorkflow' => 'HarbormasterManagementWorkflow', + 'HarbormasterMessageException' => 'Exception', 'HarbormasterMessageType' => 'Phobject', 'HarbormasterObject' => 'HarbormasterDAO', 'HarbormasterOtherBuildStepGroup' => 'HarbormasterBuildStepGroup', @@ -7777,13 +7699,11 @@ phutil_register_library_map(array( 'HarbormasterPlanRunController' => 'HarbormasterPlanController', 'HarbormasterPlanViewController' => 'HarbormasterPlanController', 'HarbormasterPrototypeBuildStepGroup' => 'HarbormasterBuildStepGroup', - 'HarbormasterPublishFragmentBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 'HarbormasterQueryAutotargetsConduitAPIMethod' => 'HarbormasterConduitAPIMethod', 'HarbormasterQueryBuildablesConduitAPIMethod' => 'HarbormasterConduitAPIMethod', 'HarbormasterQueryBuildsConduitAPIMethod' => 'HarbormasterConduitAPIMethod', 'HarbormasterQueryBuildsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 'HarbormasterRemarkupRule' => 'PhabricatorObjectRemarkupRule', - 'HarbormasterRestartException' => 'Exception', 'HarbormasterRunBuildPlansHeraldAction' => 'HeraldAction', 'HarbormasterSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'HarbormasterScratchTable' => 'HarbormasterDAO', @@ -8345,7 +8265,6 @@ phutil_register_library_map(array( 'OwnersConduitAPIMethod' => 'ConduitAPIMethod', 'OwnersEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'OwnersPackageReplyHandler' => 'PhabricatorMailReplyHandler', - 'OwnersQueryConduitAPIMethod' => 'OwnersConduitAPIMethod', 'OwnersSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'PHIDConduitAPIMethod' => 'ConduitAPIMethod', 'PHIDInfoConduitAPIMethod' => 'PHIDConduitAPIMethod', @@ -9993,6 +9912,12 @@ phutil_register_library_map(array( ), 'PhabricatorFileAES256StorageFormat' => 'PhabricatorFileStorageFormat', 'PhabricatorFileAltTextTransaction' => 'PhabricatorFileTransactionType', + 'PhabricatorFileAttachment' => array( + 'PhabricatorFileDAO', + 'PhabricatorPolicyInterface', + 'PhabricatorExtendedPolicyInterface', + ), + 'PhabricatorFileAttachmentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorFileBundleLoader' => 'Phobject', 'PhabricatorFileChunk' => array( 'PhabricatorFileDAO', @@ -10010,6 +9935,7 @@ phutil_register_library_map(array( 'PhabricatorFileDataController' => 'PhabricatorFileController', 'PhabricatorFileDeleteController' => 'PhabricatorFileController', 'PhabricatorFileDeleteTransaction' => 'PhabricatorFileTransactionType', + 'PhabricatorFileDetachController' => 'PhabricatorFileController', 'PhabricatorFileDocumentController' => 'PhabricatorFileController', 'PhabricatorFileDocumentRenderingEngine' => 'PhabricatorDocumentRenderingEngine', 'PhabricatorFileDropUploadController' => 'PhabricatorFileController', @@ -10023,7 +9949,6 @@ phutil_register_library_map(array( ), 'PhabricatorFileExternalRequestGarbageCollector' => 'PhabricatorGarbageCollector', 'PhabricatorFileFilePHIDType' => 'PhabricatorPHIDType', - 'PhabricatorFileHasObjectEdgeType' => 'PhabricatorEdgeType', 'PhabricatorFileIconSetSelectController' => 'PhabricatorFileController', 'PhabricatorFileImageMacro' => array( 'PhabricatorFileDAO', @@ -10065,6 +9990,8 @@ phutil_register_library_map(array( 'PhabricatorFileTransformController' => 'PhabricatorFileController', 'PhabricatorFileTransformListController' => 'PhabricatorFileController', 'PhabricatorFileTransformTestCase' => 'PhabricatorTestCase', + 'PhabricatorFileUICurtainAttachController' => 'PhabricatorFileController', + 'PhabricatorFileUICurtainListController' => 'PhabricatorFileController', 'PhabricatorFileUploadController' => 'PhabricatorFileController', 'PhabricatorFileUploadDialogController' => 'PhabricatorFileController', 'PhabricatorFileUploadException' => 'Exception', @@ -10078,6 +10005,7 @@ phutil_register_library_map(array( 'PhabricatorFilesComposeAvatarBuiltinFile' => 'PhabricatorFilesBuiltinFile', 'PhabricatorFilesComposeIconBuiltinFile' => 'PhabricatorFilesBuiltinFile', 'PhabricatorFilesConfigOptions' => 'PhabricatorApplicationConfigOptions', + 'PhabricatorFilesCurtainExtension' => 'PHUICurtainExtension', 'PhabricatorFilesManagementCatWorkflow' => 'PhabricatorFilesManagementWorkflow', 'PhabricatorFilesManagementCompactWorkflow' => 'PhabricatorFilesManagementWorkflow', 'PhabricatorFilesManagementCycleWorkflow' => 'PhabricatorFilesManagementWorkflow', @@ -10538,7 +10466,6 @@ phutil_register_library_map(array( 'PhabricatorObjectHasAsanaTaskEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectHasContributorEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectHasDraftEdgeType' => 'PhabricatorEdgeType', - 'PhabricatorObjectHasFileEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectHasJiraIssueEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectHasSubscriberEdgeType' => 'PhabricatorEdgeType', 'PhabricatorObjectHasUnsubscriberEdgeType' => 'PhabricatorEdgeType', @@ -10598,6 +10525,7 @@ phutil_register_library_map(array( 'PhabricatorNgramsInterface', ), 'PhabricatorOwnersPackageAuditingTransaction' => 'PhabricatorOwnersPackageTransactionType', + 'PhabricatorOwnersPackageAuthorityTransaction' => 'PhabricatorOwnersPackageTransactionType', 'PhabricatorOwnersPackageAutoreviewTransaction' => 'PhabricatorOwnersPackageTransactionType', 'PhabricatorOwnersPackageContextFreeGrammar' => 'PhutilContextFreeGrammar', 'PhabricatorOwnersPackageDatasource' => 'PhabricatorTypeaheadDatasource', @@ -10896,7 +10824,6 @@ phutil_register_library_map(array( 'PhabricatorPhortuneManagementInvoiceWorkflow' => 'PhabricatorPhortuneManagementWorkflow', 'PhabricatorPhortuneManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorPhortuneTestCase' => 'PhabricatorTestCase', - 'PhabricatorPhragmentApplication' => 'PhabricatorApplication', 'PhabricatorPhrequentApplication' => 'PhabricatorApplication', 'PhabricatorPhrictionApplication' => 'PhabricatorApplication', 'PhabricatorPhurlApplication' => 'PhabricatorApplication', @@ -11283,8 +11210,6 @@ phutil_register_library_map(array( 'PhabricatorRefreshCSRFController' => 'PhabricatorAuthController', 'PhabricatorRegexListConfigType' => 'PhabricatorTextListConfigType', 'PhabricatorRegistrationProfile' => 'Phobject', - 'PhabricatorReleephApplication' => 'PhabricatorApplication', - 'PhabricatorReleephApplicationConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorRemarkupCachePurger' => 'PhabricatorCachePurger', 'PhabricatorRemarkupControl' => 'AphrontFormTextAreaControl', 'PhabricatorRemarkupCowsayBlockInterpreter' => 'PhutilRemarkupBlockInterpreter', @@ -11628,7 +11553,6 @@ phutil_register_library_map(array( 'PhabricatorSlowvoteApplication' => 'PhabricatorApplication', 'PhabricatorSlowvoteChoice' => 'PhabricatorSlowvoteDAO', 'PhabricatorSlowvoteCloseController' => 'PhabricatorSlowvoteController', - 'PhabricatorSlowvoteCloseTransaction' => 'PhabricatorSlowvoteTransactionType', 'PhabricatorSlowvoteCommentController' => 'PhabricatorSlowvoteController', 'PhabricatorSlowvoteController' => 'PhabricatorController', 'PhabricatorSlowvoteDAO' => 'PhabricatorLiskDAO', @@ -11660,11 +11584,13 @@ phutil_register_library_map(array( 'PhabricatorSlowvoteSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'PhabricatorSlowvoteSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorSlowvoteShuffleTransaction' => 'PhabricatorSlowvoteTransactionType', + 'PhabricatorSlowvoteStatusTransaction' => 'PhabricatorSlowvoteTransactionType', 'PhabricatorSlowvoteTransaction' => 'PhabricatorModularTransaction', 'PhabricatorSlowvoteTransactionComment' => 'PhabricatorApplicationTransactionComment', 'PhabricatorSlowvoteTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorSlowvoteTransactionType' => 'PhabricatorModularTransactionType', 'PhabricatorSlowvoteVoteController' => 'PhabricatorSlowvoteController', + 'PhabricatorSlowvoteVotingMethodTransaction' => 'PhabricatorSlowvoteTransactionType', 'PhabricatorSlug' => 'Phobject', 'PhabricatorSlugTestCase' => 'PhabricatorTestCase', 'PhabricatorSourceCodeView' => 'AphrontView', @@ -12152,6 +12078,7 @@ phutil_register_library_map(array( 'PhameDescriptionView' => 'AphrontTagView', 'PhameDraftListView' => 'AphrontTagView', 'PhameHomeController' => 'PhamePostController', + 'PhameInheritBlogPolicyRule' => 'PhabricatorPolicyRule', 'PhameLiveController' => 'PhameController', 'PhameNextPostView' => 'AphrontTagView', 'PhamePost' => array( @@ -12165,6 +12092,7 @@ phutil_register_library_map(array( 'PhabricatorDestructibleInterface', 'PhabricatorTokenReceiverInterface', 'PhabricatorConduitResultInterface', + 'PhabricatorEditEngineLockableInterface', 'PhabricatorFulltextInterface', 'PhabricatorFerretInterface', ), @@ -12175,6 +12103,7 @@ phutil_register_library_map(array( 'PhamePostEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'PhamePostEditController' => 'PhamePostController', 'PhamePostEditEngine' => 'PhabricatorEditEngine', + 'PhamePostEditEngineLock' => 'PhabricatorEditEngineLock', 'PhamePostEditor' => 'PhabricatorApplicationTransactionEditor', 'PhamePostFerretEngine' => 'PhabricatorFerretEngine', 'PhamePostFulltextEngine' => 'PhabricatorFulltextEngine', @@ -12515,50 +12444,6 @@ phutil_register_library_map(array( 'PhortuneSubscriptionTransactionType' => 'PhabricatorModularTransactionType', 'PhortuneSubscriptionWorker' => 'PhabricatorWorker', 'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider', - 'PhragmentBrowseController' => 'PhragmentController', - 'PhragmentCanCreateCapability' => 'PhabricatorPolicyCapability', - 'PhragmentConduitAPIMethod' => 'ConduitAPIMethod', - 'PhragmentController' => 'PhabricatorController', - 'PhragmentCreateController' => 'PhragmentController', - 'PhragmentDAO' => 'PhabricatorLiskDAO', - 'PhragmentFragment' => array( - 'PhragmentDAO', - 'PhabricatorPolicyInterface', - ), - 'PhragmentFragmentPHIDType' => 'PhabricatorPHIDType', - 'PhragmentFragmentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', - 'PhragmentFragmentVersion' => array( - 'PhragmentDAO', - 'PhabricatorPolicyInterface', - ), - 'PhragmentFragmentVersionPHIDType' => 'PhabricatorPHIDType', - 'PhragmentFragmentVersionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', - 'PhragmentGetPatchConduitAPIMethod' => 'PhragmentConduitAPIMethod', - 'PhragmentHistoryController' => 'PhragmentController', - 'PhragmentPatchController' => 'PhragmentController', - 'PhragmentPatchUtil' => 'Phobject', - 'PhragmentPolicyController' => 'PhragmentController', - 'PhragmentQueryFragmentsConduitAPIMethod' => 'PhragmentConduitAPIMethod', - 'PhragmentRevertController' => 'PhragmentController', - 'PhragmentSchemaSpec' => 'PhabricatorConfigSchemaSpec', - 'PhragmentSnapshot' => array( - 'PhragmentDAO', - 'PhabricatorPolicyInterface', - ), - 'PhragmentSnapshotChild' => array( - 'PhragmentDAO', - 'PhabricatorPolicyInterface', - ), - 'PhragmentSnapshotChildQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', - 'PhragmentSnapshotCreateController' => 'PhragmentController', - 'PhragmentSnapshotDeleteController' => 'PhragmentController', - 'PhragmentSnapshotPHIDType' => 'PhabricatorPHIDType', - 'PhragmentSnapshotPromoteController' => 'PhragmentController', - 'PhragmentSnapshotQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', - 'PhragmentSnapshotViewController' => 'PhragmentController', - 'PhragmentUpdateController' => 'PhragmentController', - 'PhragmentVersionController' => 'PhragmentController', - 'PhragmentZIPController' => 'PhragmentController', 'PhrequentConduitAPIMethod' => 'ConduitAPIMethod', 'PhrequentController' => 'PhabricatorController', 'PhrequentCurtainExtension' => 'PHUICurtainExtension', @@ -12758,6 +12643,7 @@ phutil_register_library_map(array( 'PhutilRemarkupEngine' => 'PhutilMarkupEngine', 'PhutilRemarkupEngineTestCase' => 'PhutilTestCase', 'PhutilRemarkupEscapeRemarkupRule' => 'PhutilRemarkupRule', + 'PhutilRemarkupEvalRule' => 'PhutilRemarkupRule', 'PhutilRemarkupHeaderBlockRule' => 'PhutilRemarkupBlockRule', 'PhutilRemarkupHighlightRule' => 'PhutilRemarkupRule', 'PhutilRemarkupHorizontalRuleBlockRule' => 'PhutilRemarkupBlockRule', @@ -12898,116 +12784,17 @@ phutil_register_library_map(array( 'ProjectSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'QueryFormattingTestCase' => 'PhabricatorTestCase', 'QueryFuture' => 'Future', - 'ReleephAuthorFieldSpecification' => 'ReleephFieldSpecification', - 'ReleephBranch' => array( - 'ReleephDAO', - 'PhabricatorApplicationTransactionInterface', - 'PhabricatorPolicyInterface', - ), - 'ReleephBranchAccessController' => 'ReleephBranchController', - 'ReleephBranchCommitFieldSpecification' => 'ReleephFieldSpecification', - 'ReleephBranchController' => 'ReleephController', - 'ReleephBranchCreateController' => 'ReleephProductController', - 'ReleephBranchEditController' => 'ReleephBranchController', - 'ReleephBranchEditor' => 'PhabricatorEditor', - 'ReleephBranchHistoryController' => 'ReleephBranchController', - 'ReleephBranchNamePreviewController' => 'ReleephController', - 'ReleephBranchPHIDType' => 'PhabricatorPHIDType', - 'ReleephBranchPreviewView' => 'AphrontFormControl', - 'ReleephBranchQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', - 'ReleephBranchSearchEngine' => 'PhabricatorApplicationSearchEngine', - 'ReleephBranchTemplate' => 'Phobject', - 'ReleephBranchTransaction' => 'PhabricatorApplicationTransaction', - 'ReleephBranchTransactionQuery' => 'PhabricatorApplicationTransactionQuery', - 'ReleephBranchViewController' => 'ReleephBranchController', - 'ReleephCommitFinder' => 'Phobject', - 'ReleephCommitFinderException' => 'Exception', - 'ReleephCommitMessageFieldSpecification' => 'ReleephFieldSpecification', - 'ReleephConduitAPIMethod' => 'ConduitAPIMethod', - 'ReleephController' => 'PhabricatorController', - 'ReleephDAO' => 'PhabricatorLiskDAO', - 'ReleephDefaultFieldSelector' => 'ReleephFieldSelector', - 'ReleephDependsOnFieldSpecification' => 'ReleephFieldSpecification', - 'ReleephDiffChurnFieldSpecification' => 'ReleephFieldSpecification', - 'ReleephDiffMessageFieldSpecification' => 'ReleephFieldSpecification', - 'ReleephDiffSizeFieldSpecification' => 'ReleephFieldSpecification', - 'ReleephFieldParseException' => 'Exception', - 'ReleephFieldSelector' => 'Phobject', - 'ReleephFieldSpecification' => array( - 'PhabricatorCustomField', - 'PhabricatorMarkupInterface', - ), - 'ReleephGetBranchesConduitAPIMethod' => 'ReleephConduitAPIMethod', - 'ReleephIntentFieldSpecification' => 'ReleephFieldSpecification', - 'ReleephLevelFieldSpecification' => 'ReleephFieldSpecification', - 'ReleephOriginalCommitFieldSpecification' => 'ReleephFieldSpecification', - 'ReleephProductActionController' => 'ReleephProductController', - 'ReleephProductController' => 'ReleephController', - 'ReleephProductCreateController' => 'ReleephProductController', - 'ReleephProductEditController' => 'ReleephProductController', - 'ReleephProductEditor' => 'PhabricatorApplicationTransactionEditor', - 'ReleephProductHistoryController' => 'ReleephProductController', - 'ReleephProductListController' => 'ReleephController', - 'ReleephProductPHIDType' => 'PhabricatorPHIDType', - 'ReleephProductQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', - 'ReleephProductSearchEngine' => 'PhabricatorApplicationSearchEngine', - 'ReleephProductTransaction' => 'PhabricatorApplicationTransaction', - 'ReleephProductTransactionQuery' => 'PhabricatorApplicationTransactionQuery', - 'ReleephProductViewController' => 'ReleephProductController', - 'ReleephProject' => array( - 'ReleephDAO', - 'PhabricatorApplicationTransactionInterface', - 'PhabricatorPolicyInterface', - ), - 'ReleephQueryBranchesConduitAPIMethod' => 'ReleephConduitAPIMethod', - 'ReleephQueryProductsConduitAPIMethod' => 'ReleephConduitAPIMethod', - 'ReleephQueryRequestsConduitAPIMethod' => 'ReleephConduitAPIMethod', - 'ReleephReasonFieldSpecification' => 'ReleephFieldSpecification', - 'ReleephRequest' => array( - 'ReleephDAO', - 'PhabricatorApplicationTransactionInterface', - 'PhabricatorPolicyInterface', - 'PhabricatorCustomFieldInterface', - ), - 'ReleephRequestActionController' => 'ReleephRequestController', - 'ReleephRequestCommentController' => 'ReleephRequestController', - 'ReleephRequestConduitAPIMethod' => 'ReleephConduitAPIMethod', - 'ReleephRequestController' => 'ReleephController', - 'ReleephRequestDifferentialCreateController' => 'ReleephController', - 'ReleephRequestEditController' => 'ReleephBranchController', - 'ReleephRequestMailReceiver' => 'PhabricatorObjectMailReceiver', - 'ReleephRequestPHIDType' => 'PhabricatorPHIDType', - 'ReleephRequestQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', - 'ReleephRequestReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', - 'ReleephRequestSearchEngine' => 'PhabricatorApplicationSearchEngine', - 'ReleephRequestStatus' => 'Phobject', - 'ReleephRequestTransaction' => 'PhabricatorApplicationTransaction', - 'ReleephRequestTransactionComment' => 'PhabricatorApplicationTransactionComment', - 'ReleephRequestTransactionQuery' => 'PhabricatorApplicationTransactionQuery', - 'ReleephRequestTransactionalEditor' => 'PhabricatorApplicationTransactionEditor', - 'ReleephRequestTypeaheadControl' => 'AphrontFormControl', - 'ReleephRequestTypeaheadController' => 'PhabricatorTypeaheadDatasourceController', - 'ReleephRequestView' => 'AphrontView', - 'ReleephRequestViewController' => 'ReleephBranchController', - 'ReleephRequestorFieldSpecification' => 'ReleephFieldSpecification', - 'ReleephRevisionFieldSpecification' => 'ReleephFieldSpecification', - 'ReleephSeverityFieldSpecification' => 'ReleephLevelFieldSpecification', - 'ReleephSummaryFieldSpecification' => 'ReleephFieldSpecification', - 'ReleephWorkCanPushConduitAPIMethod' => 'ReleephConduitAPIMethod', - 'ReleephWorkGetAuthorInfoConduitAPIMethod' => 'ReleephConduitAPIMethod', - 'ReleephWorkGetBranchCommitMessageConduitAPIMethod' => 'ReleephConduitAPIMethod', - 'ReleephWorkGetBranchConduitAPIMethod' => 'ReleephConduitAPIMethod', - 'ReleephWorkGetCommitMessageConduitAPIMethod' => 'ReleephConduitAPIMethod', - 'ReleephWorkNextRequestConduitAPIMethod' => 'ReleephConduitAPIMethod', - 'ReleephWorkRecordConduitAPIMethod' => 'ReleephConduitAPIMethod', - 'ReleephWorkRecordPickStatusConduitAPIMethod' => 'ReleephConduitAPIMethod', 'RemarkupProcessConduitAPIMethod' => 'ConduitAPIMethod', + 'RemarkupValue' => 'Phobject', 'RepositoryConduitAPIMethod' => 'ConduitAPIMethod', 'RepositoryQueryConduitAPIMethod' => 'RepositoryConduitAPIMethod', 'ShellLogView' => 'AphrontView', 'SlowvoteConduitAPIMethod' => 'ConduitAPIMethod', 'SlowvoteEmbedView' => 'AphrontView', 'SlowvoteInfoConduitAPIMethod' => 'SlowvoteConduitAPIMethod', + 'SlowvotePollResponseVisibility' => 'Phobject', + 'SlowvotePollStatus' => 'Phobject', + 'SlowvotePollVotingMethod' => 'Phobject', 'SlowvoteRemarkupRule' => 'PhabricatorObjectRemarkupRule', 'SlowvoteSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'SubscriptionListDialogBuilder' => 'Phobject', diff --git a/src/aphront/AphrontRequest.php b/src/aphront/AphrontRequest.php index a3fa05cca2..f35f3f1279 100644 --- a/src/aphront/AphrontRequest.php +++ b/src/aphront/AphrontRequest.php @@ -354,9 +354,9 @@ final class AphrontRequest extends Phobject { $info = array(); $info[] = pht( - 'You are trying to save some data to Phabricator, but the request '. - 'your browser made included an incorrect token. Reload the page '. - 'and try again. You may need to clear your cookies.'); + 'You are trying to save some data to permanent storage, but the '. + 'request your browser made included an incorrect token. Reload the '. + 'page and try again. You may need to clear your cookies.'); if ($this->isAjax()) { $info[] = pht('This was an Ajax request.'); @@ -587,11 +587,11 @@ final class AphrontRequest extends Phobject { throw new AphrontMalformedRequestException( pht('Bad Host Header'), pht( - 'This Phabricator install is configured as "%s", but you are '. - 'using the domain name "%s" to access a page which is trying to '. - 'set a cookie. Access Phabricator on the configured primary '. - 'domain or a configured alternate domain. Phabricator will not '. - 'set cookies on other domains for security reasons.', + 'This server is configured as "%s", but you are using the domain '. + 'name "%s" to access a page which is trying to set a cookie. '. + 'Access this service on the configured primary domain or a '. + 'configured alternate domain. Cookies will not be set on other '. + 'domains for security reasons.', $configured_as, $accessed_as), true); diff --git a/src/aphront/configuration/AphrontApplicationConfiguration.php b/src/aphront/configuration/AphrontApplicationConfiguration.php index 9e22ca7f47..550a5a0316 100644 --- a/src/aphront/configuration/AphrontApplicationConfiguration.php +++ b/src/aphront/configuration/AphrontApplicationConfiguration.php @@ -83,7 +83,7 @@ final class AphrontApplicationConfiguration * @phutil-external-symbol class PhabricatorStartup */ public static function runHTTPRequest(AphrontHTTPSink $sink) { - if (isset($_SERVER['HTTP_X_PHABRICATOR_SELFCHECK'])) { + if (isset($_SERVER['HTTP_X_SETUP_SELFCHECK'])) { $response = self::newSelfCheckResponse(); return self::writeResponse($sink, $response); } @@ -419,9 +419,9 @@ final class AphrontApplicationConfiguration throw new AphrontMalformedRequestException( pht('No %s', 'SERVER_ADDR'), pht( - 'Phabricator is configured to operate in cluster mode, but '. + 'This service is configured to operate in cluster mode, but '. '%s is not defined in the request context. Your webserver '. - 'configuration needs to forward %s to PHP so Phabricator can '. + 'configuration needs to forward %s to PHP so the software can '. 'reject requests received on external interfaces.', 'SERVER_ADDR', 'SERVER_ADDR')); @@ -431,7 +431,7 @@ final class AphrontApplicationConfiguration throw new AphrontMalformedRequestException( pht('External Interface'), pht( - 'Phabricator is configured in cluster mode and the address '. + 'This service is configured in cluster mode and the address '. 'this request was received on ("%s") is not whitelisted as '. 'a cluster address.', $server_addr)); diff --git a/src/aphront/httpparametertype/AphrontJSONHTTPParameterType.php b/src/aphront/httpparametertype/AphrontJSONHTTPParameterType.php new file mode 100644 index 0000000000..3a1081bd75 --- /dev/null +++ b/src/aphront/httpparametertype/AphrontJSONHTTPParameterType.php @@ -0,0 +1,31 @@ +getStr($key); + return phutil_json_decode($str); + } + + protected function getParameterTypeName() { + return 'string (json object)'; + } + + protected function getParameterFormatDescriptions() { + return array( + pht('A JSON-encoded object.'), + ); + } + + protected function getParameterExamples() { + return array( + 'v={...}', + ); + } + +} diff --git a/src/aphront/httpparametertype/AphrontRemarkupHTTPParameterType.php b/src/aphront/httpparametertype/AphrontRemarkupHTTPParameterType.php new file mode 100644 index 0000000000..7970878f49 --- /dev/null +++ b/src/aphront/httpparametertype/AphrontRemarkupHTTPParameterType.php @@ -0,0 +1,50 @@ +newRemarkupValue(); + } + + protected function getParameterValue(AphrontRequest $request, $key) { + $corpus_key = $key; + $corpus_type = new AphrontStringHTTPParameterType(); + $corpus_value = $this->getValueWithType( + $corpus_type, + $request, + $corpus_key); + + $metadata_key = $key.'_metadata'; + $metadata_type = new AphrontJSONHTTPParameterType(); + $metadata_value = $this->getValueWithType( + $metadata_type, + $request, + $metadata_key); + + return $this->newRemarkupValue() + ->setCorpus($corpus_value) + ->setMetadata($metadata_value); + } + + protected function getParameterTypeName() { + return 'string (remarkup)'; + } + + protected function getParameterFormatDescriptions() { + return array( + pht('Remarkup text.'), + ); + } + + protected function getParameterExamples() { + return array( + 'v=Lorem...', + ); + } + + private function newRemarkupValue() { + return new RemarkupValue(); + } + +} diff --git a/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php b/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php index ebe992469f..545a8c3931 100644 --- a/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php +++ b/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php @@ -147,10 +147,9 @@ final class AlmanacManagementRegisterWorkflow if (!$public_key) { throw new PhutilArgumentUsageException( pht( - 'The public key corresponding to the given private key is not '. - 'yet known to Phabricator. Associate the public key with an '. - 'Almanac device in the web interface before registering hosts '. - 'with it.')); + 'The public key corresponding to the given private key is unknown. '. + 'Associate the public key with an Almanac device in the web '. + 'interface before registering hosts with it.')); } if ($public_key->getObjectPHID() !== $device->getPHID()) { diff --git a/src/applications/almanac/management/AlmanacManagementTrustKeyWorkflow.php b/src/applications/almanac/management/AlmanacManagementTrustKeyWorkflow.php index 631cef8c96..f730d0b40f 100644 --- a/src/applications/almanac/management/AlmanacManagementTrustKeyWorkflow.php +++ b/src/applications/almanac/management/AlmanacManagementTrustKeyWorkflow.php @@ -61,14 +61,14 @@ final class AlmanacManagementTrustKeyWorkflow phutil_console_wrap( pht( 'Trusting a public key gives anyone holding the corresponding '. - 'private key complete, unrestricted access to all data in '. - 'Phabricator. The private key will be able to sign requests that '. - 'skip policy and security checks.')), + 'private key complete, unrestricted access to all data. The '. + 'private key will be able to sign requests that bypass policy and '. + 'security checks.')), phutil_console_wrap( pht( 'This is an advanced feature which should normally be used only '. - 'when building a Phabricator cluster. This feature is very '. - 'dangerous if misused.')), + 'when building a cluster. This feature is very dangerous if '. + 'misused.')), pht('This key is associated with device "%s".', $handle->getName())); $prompt = pht( diff --git a/src/applications/almanac/query/AlmanacBindingQuery.php b/src/applications/almanac/query/AlmanacBindingQuery.php index 574967f7bb..c3c7940525 100644 --- a/src/applications/almanac/query/AlmanacBindingQuery.php +++ b/src/applications/almanac/query/AlmanacBindingQuery.php @@ -44,10 +44,6 @@ final class AlmanacBindingQuery return new AlmanacBinding(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $bindings) { $service_phids = mpull($bindings, 'getServicePHID'); $device_phids = mpull($bindings, 'getDevicePHID'); diff --git a/src/applications/almanac/query/AlmanacDeviceQuery.php b/src/applications/almanac/query/AlmanacDeviceQuery.php index 7fcf219571..42796c4839 100644 --- a/src/applications/almanac/query/AlmanacDeviceQuery.php +++ b/src/applications/almanac/query/AlmanacDeviceQuery.php @@ -56,10 +56,6 @@ final class AlmanacDeviceQuery return new AlmanacDevice(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/almanac/query/AlmanacInterfaceQuery.php b/src/applications/almanac/query/AlmanacInterfaceQuery.php index 5738108ffc..dbbc0cd53e 100644 --- a/src/applications/almanac/query/AlmanacInterfaceQuery.php +++ b/src/applications/almanac/query/AlmanacInterfaceQuery.php @@ -38,10 +38,6 @@ final class AlmanacInterfaceQuery return new AlmanacInterface(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $interfaces) { $network_phids = mpull($interfaces, 'getNetworkPHID'); $device_phids = mpull($interfaces, 'getDevicePHID'); diff --git a/src/applications/releeph/query/ReleephBranchTransactionQuery.php b/src/applications/almanac/query/AlmanacInterfaceTransactionQuery.php similarity index 57% rename from src/applications/releeph/query/ReleephBranchTransactionQuery.php rename to src/applications/almanac/query/AlmanacInterfaceTransactionQuery.php index 3e88259b25..ef8d3961d2 100644 --- a/src/applications/releeph/query/ReleephBranchTransactionQuery.php +++ b/src/applications/almanac/query/AlmanacInterfaceTransactionQuery.php @@ -1,10 +1,10 @@ loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/almanac/query/AlmanacNetworkQuery.php b/src/applications/almanac/query/AlmanacNetworkQuery.php index 13176f7de1..7af9db8585 100644 --- a/src/applications/almanac/query/AlmanacNetworkQuery.php +++ b/src/applications/almanac/query/AlmanacNetworkQuery.php @@ -32,10 +32,6 @@ final class AlmanacNetworkQuery $ngrams); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/almanac/query/AlmanacPropertyQuery.php b/src/applications/almanac/query/AlmanacPropertyQuery.php index 17f1e87c07..4261c70fec 100644 --- a/src/applications/almanac/query/AlmanacPropertyQuery.php +++ b/src/applications/almanac/query/AlmanacPropertyQuery.php @@ -33,10 +33,6 @@ final class AlmanacPropertyQuery return new AlmanacProperty(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $properties) { $object_phids = mpull($properties, 'getObjectPHID'); diff --git a/src/applications/almanac/query/AlmanacServiceQuery.php b/src/applications/almanac/query/AlmanacServiceQuery.php index 4e374ec90c..eaca9d4a76 100644 --- a/src/applications/almanac/query/AlmanacServiceQuery.php +++ b/src/applications/almanac/query/AlmanacServiceQuery.php @@ -69,10 +69,6 @@ final class AlmanacServiceQuery return new AlmanacService(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); diff --git a/src/applications/almanac/servicetype/AlmanacClusterDatabaseServiceType.php b/src/applications/almanac/servicetype/AlmanacClusterDatabaseServiceType.php index ba06a78d52..10c84a628a 100644 --- a/src/applications/almanac/servicetype/AlmanacClusterDatabaseServiceType.php +++ b/src/applications/almanac/servicetype/AlmanacClusterDatabaseServiceType.php @@ -10,12 +10,12 @@ final class AlmanacClusterDatabaseServiceType } public function getServiceTypeName() { - return pht('Phabricator Cluster: Database'); + return pht('Cluster: Database'); } public function getServiceTypeDescription() { return pht( - 'Defines a database service for use in a Phabricator cluster.'); + 'Defines a database service for use in a cluster.'); } } diff --git a/src/applications/almanac/servicetype/AlmanacClusterRepositoryServiceType.php b/src/applications/almanac/servicetype/AlmanacClusterRepositoryServiceType.php index 98947f56c1..2f5717f202 100644 --- a/src/applications/almanac/servicetype/AlmanacClusterRepositoryServiceType.php +++ b/src/applications/almanac/servicetype/AlmanacClusterRepositoryServiceType.php @@ -10,12 +10,12 @@ final class AlmanacClusterRepositoryServiceType } public function getServiceTypeName() { - return pht('Phabricator Cluster: Repository'); + return pht('Cluster: Repository'); } public function getServiceTypeDescription() { return pht( - 'Defines a repository service for use in a Phabricator cluster.'); + 'Defines a repository service for use in a cluster.'); } public function getFieldSpecifications() { diff --git a/src/applications/almanac/util/__tests__/AlmanacNamesTestCase.php b/src/applications/almanac/util/__tests__/AlmanacNamesTestCase.php index 78dea769e4..8c3754ac23 100644 --- a/src/applications/almanac/util/__tests__/AlmanacNamesTestCase.php +++ b/src/applications/almanac/util/__tests__/AlmanacNamesTestCase.php @@ -30,7 +30,7 @@ final class AlmanacNamesTestCase extends PhabricatorTestCase { 'abc' => true, 'a.b' => true, - 'db.phacility.instance' => true, + 'db.companyname.instance' => true, 'web002.useast.example.com' => true, 'master.example-corp.com' => true, diff --git a/src/applications/auth/adapter/PhutilPhabricatorAuthAdapter.php b/src/applications/auth/adapter/PhutilPhabricatorAuthAdapter.php index e66ba32f94..8543675a62 100644 --- a/src/applications/auth/adapter/PhutilPhabricatorAuthAdapter.php +++ b/src/applications/auth/adapter/PhutilPhabricatorAuthAdapter.php @@ -89,8 +89,7 @@ final class PhutilPhabricatorAuthAdapter extends PhutilOAuthAuthAdapter { } catch (PhutilJSONParserException $ex) { throw new Exception( pht( - 'Expected valid JSON response from Phabricator %s request.', - 'user.whoami'), + 'Expected valid JSON response from "user.whoami" request.'), $ex); } } diff --git a/src/applications/auth/controller/PhabricatorAuthConfirmLinkController.php b/src/applications/auth/controller/PhabricatorAuthConfirmLinkController.php index 9ceb10df8b..3d79bcfb81 100644 --- a/src/applications/auth/controller/PhabricatorAuthConfirmLinkController.php +++ b/src/applications/auth/controller/PhabricatorAuthConfirmLinkController.php @@ -54,8 +54,9 @@ final class PhabricatorAuthConfirmLinkController ), pht( 'Confirm the link with this %s account. This account will be '. - 'able to log in to your Phabricator account.', - $provider->getProviderName()))) + 'able to log in to your %s account.', + $provider->getProviderName(), + PlatformSymbols::getPlatformServerName()))) ->appendChild( id(new PhabricatorAuthAccountView()) ->setUser($viewer) diff --git a/src/applications/auth/controller/PhabricatorAuthController.php b/src/applications/auth/controller/PhabricatorAuthController.php index c3dc4e2d98..aee93b98d6 100644 --- a/src/applications/auth/controller/PhabricatorAuthController.php +++ b/src/applications/auth/controller/PhabricatorAuthController.php @@ -254,12 +254,16 @@ abstract class PhabricatorAuthController extends PhabricatorController { } $invite_item = id(new PHUIObjectItemView()) - ->setHeader(pht('Welcome to Phabricator!')) + ->setHeader( + pht( + 'Welcome to %s!', + PlatformSymbols::getPlatformServerName())) ->setImageURI($invite_author->getProfileImageURI()) ->addAttribute( pht( - '%s has invited you to join Phabricator.', - $invite_author->getFullName())); + '%s has invited you to join %s.', + $invite_author->getFullName(), + PlatformSymbols::getPlatformServerName())); $invite_list = id(new PHUIObjectItemListView()) ->addItem($invite_item) diff --git a/src/applications/auth/controller/PhabricatorAuthLinkController.php b/src/applications/auth/controller/PhabricatorAuthLinkController.php index 4b127b9ad1..7e1a5f5c67 100644 --- a/src/applications/auth/controller/PhabricatorAuthLinkController.php +++ b/src/applications/auth/controller/PhabricatorAuthLinkController.php @@ -56,8 +56,8 @@ final class PhabricatorAuthLinkController pht('Account Already Linked'), array( pht( - 'Your Phabricator account is already linked to an external '. - 'account for this provider.'), + 'Your account is already linked to an external account for '. + 'this provider.'), )); } break; diff --git a/src/applications/auth/controller/PhabricatorAuthLoginController.php b/src/applications/auth/controller/PhabricatorAuthLoginController.php index 4f957b28a4..2a1ecf97c4 100644 --- a/src/applications/auth/controller/PhabricatorAuthLoginController.php +++ b/src/applications/auth/controller/PhabricatorAuthLoginController.php @@ -80,9 +80,9 @@ final class PhabricatorAuthLoginController } else { return $this->renderError( pht( - 'The external account ("%s") you just authenticated with is '. - 'not configured to allow logins on this Phabricator install. '. - 'An administrator may have recently disabled it.', + 'The external service ("%s") you just authenticated with is '. + 'not configured to allow logins on this server. An '. + 'administrator may have recently disabled it.', $provider->getProviderName())); } } else if ($viewer->getPHID() == $account->getUserPHID()) { @@ -94,11 +94,14 @@ final class PhabricatorAuthLoginController } else { return $this->renderError( pht( - 'The external account ("%s") you just used to log in is already '. - 'associated with another Phabricator user account. Log in to the '. - 'other Phabricator account and unlink the external account before '. - 'linking it to a new Phabricator account.', - $provider->getProviderName())); + 'The external service ("%s") you just used to log in is already '. + 'associated with another %s user account. Log in to the '. + 'other %s account and unlink the external account before '. + 'linking it to a new %s account.', + $provider->getProviderName(), + PlatformSymbols::getPlatformServerName(), + PlatformSymbols::getPlatformServerName(), + PlatformSymbols::getPlatformServerName())); } } else { // The account is not yet attached to a Phabricator user, so this is @@ -109,9 +112,9 @@ final class PhabricatorAuthLoginController } else { return $this->renderError( pht( - 'The external account ("%s") you just authenticated with is '. - 'not configured to allow registration on this Phabricator '. - 'install. An administrator may have recently disabled it.', + 'The external service ("%s") you just authenticated with is '. + 'not configured to allow registration on this server. An '. + 'administrator may have recently disabled it.', $provider->getProviderName())); } } else { @@ -135,11 +138,12 @@ final class PhabricatorAuthLoginController if ($existing_accounts) { return $this->renderError( pht( - 'Your Phabricator account is already connected to an external '. - 'account on this provider ("%s"), but you are currently logged '. - 'in to the provider with a different account. Log out of the '. + 'Your %s account is already connected to an external '. + 'account on this service ("%s"), but you are currently logged '. + 'in to the service with a different account. Log out of the '. 'external service, then log back in with the correct account '. 'before refreshing the account link.', + PlatformSymbols::getPlatformServerName(), $provider->getProviderName())); } @@ -148,9 +152,9 @@ final class PhabricatorAuthLoginController } else { return $this->renderError( pht( - 'The external account ("%s") you just authenticated with is '. - 'not configured to allow account linking on this Phabricator '. - 'install. An administrator may have recently disabled it.', + 'The external service ("%s") you just authenticated with is '. + 'not configured to allow account linking on this server. An '. + 'administrator may have recently disabled it.', $provider->getProviderName())); } } @@ -169,7 +173,8 @@ final class PhabricatorAuthLoginController return $this->renderError( pht( 'The external account you just logged in with is not associated '. - 'with a valid Phabricator user.')); + 'with a valid %s user account.', + PlatformSymbols::getPlatformServerName())); } return $this->loginUser($user); diff --git a/src/applications/auth/controller/PhabricatorAuthNeedsMultiFactorController.php b/src/applications/auth/controller/PhabricatorAuthNeedsMultiFactorController.php index 259e4c6743..0f86614b19 100644 --- a/src/applications/auth/controller/PhabricatorAuthNeedsMultiFactorController.php +++ b/src/applications/auth/controller/PhabricatorAuthNeedsMultiFactorController.php @@ -202,7 +202,7 @@ final class PhabricatorAuthNeedsMultiFactorController $messages = array(); $messages[] = pht( - 'Before you can use Phabricator, you need to add multi-factor '. + 'Before you can use this software, you need to add multi-factor '. 'authentication to your account. Multi-factor authentication helps '. 'secure your account by making it more difficult for attackers to '. 'gain access or take sensitive actions.'); diff --git a/src/applications/auth/controller/PhabricatorAuthOneTimeLoginController.php b/src/applications/auth/controller/PhabricatorAuthOneTimeLoginController.php index d176a67119..8a3ad1afc9 100644 --- a/src/applications/auth/controller/PhabricatorAuthOneTimeLoginController.php +++ b/src/applications/auth/controller/PhabricatorAuthOneTimeLoginController.php @@ -151,7 +151,9 @@ final class PhabricatorAuthOneTimeLoginController switch ($link_type) { case PhabricatorAuthSessionEngine::ONETIME_WELCOME: - $title = pht('Welcome to Phabricator'); + $title = pht( + 'Welcome to %s', + PlatformSymbols::getPlatformServerName()); break; case PhabricatorAuthSessionEngine::ONETIME_RECOVER: $title = pht('Account Recovery'); @@ -159,7 +161,9 @@ final class PhabricatorAuthOneTimeLoginController case PhabricatorAuthSessionEngine::ONETIME_USERNAME: case PhabricatorAuthSessionEngine::ONETIME_RESET: default: - $title = pht('Log in to Phabricator'); + $title = pht( + 'Log in to %s', + PlatformSymbols::getPlatformServerName()); break; } diff --git a/src/applications/auth/controller/PhabricatorAuthRegisterController.php b/src/applications/auth/controller/PhabricatorAuthRegisterController.php index 30aa770f30..9fbf30d092 100644 --- a/src/applications/auth/controller/PhabricatorAuthRegisterController.php +++ b/src/applications/auth/controller/PhabricatorAuthRegisterController.php @@ -83,8 +83,8 @@ final class PhabricatorAuthRegisterController if (!PhabricatorUserEmail::isValidAddress($default_email)) { $errors[] = pht( 'The email address associated with this external account ("%s") is '. - 'not a valid email address and can not be used to register a '. - 'Phabricator account. Choose a different, valid address.', + 'not a valid email address and can not be used to register an '. + 'account. Choose a different, valid address.', phutil_tag('strong', array(), $default_email)); $default_email = null; } @@ -102,8 +102,7 @@ final class PhabricatorAuthRegisterController $errors[] = pht( 'The email address associated with this account ("%s") is '. 'already in use by an application and can not be used to '. - 'register a new Phabricator account. Choose a different, valid '. - 'address.', + 'register a new account. Choose a different, valid address.', phutil_tag('strong', array(), $default_email)); $default_email = null; } @@ -122,8 +121,8 @@ final class PhabricatorAuthRegisterController array( pht( 'The account you are attempting to register with has an invalid '. - 'email address (%s). This Phabricator install only allows '. - 'registration with specific email addresses:', + 'email address (%s). This server only allows registration with '. + 'specific email addresses:', $debug_email), phutil_tag('br'), phutil_tag('br'), @@ -157,16 +156,17 @@ final class PhabricatorAuthRegisterController ->addHiddenInput('phase', 1) ->appendParagraph( pht( - 'You are creating a new Phabricator account linked to an '. - 'existing external account from outside Phabricator.')) + 'You are creating a new account linked to an existing '. + 'external account.')) ->appendParagraph( pht( 'The email address ("%s") associated with the external account '. - 'is already in use by an existing Phabricator account. Multiple '. - 'Phabricator accounts may not have the same email address, so '. - 'you can not use this email address to register a new '. - 'Phabricator account.', - phutil_tag('strong', array(), $show_existing))) + 'is already in use by an existing %s account. Multiple '. + '%s accounts may not have the same email address, so '. + 'you can not use this email address to register a new account.', + phutil_tag('strong', array(), $show_existing), + PlatformSymbols::getPlatformServerName(), + PlatformSymbols::getPlatformServerName())) ->appendParagraph( pht( 'If you want to register a new account, continue with this '. @@ -174,10 +174,11 @@ final class PhabricatorAuthRegisterController 'for the new account.')) ->appendParagraph( pht( - 'If you want to link an existing Phabricator account to this '. + 'If you want to link an existing %s account to this '. 'external account, do not continue. Instead: log in to your '. 'existing account, then go to "Settings" and link the account '. - 'in the "External Accounts" panel.')) + 'in the "External Accounts" panel.', + PlatformSymbols::getPlatformServerName())) ->appendParagraph( pht( 'If you continue, you will create a new account. You will not '. @@ -187,10 +188,10 @@ final class PhabricatorAuthRegisterController } else { $errors[] = pht( 'The external account you are registering with has an email address '. - 'that is already in use ("%s") by an existing Phabricator account. '. - 'Choose a new, valid email address to register a new Phabricator '. - 'account.', - phutil_tag('strong', array(), $show_existing)); + 'that is already in use ("%s") by an existing %s account. '. + 'Choose a new, valid email address to register a new account.', + phutil_tag('strong', array(), $show_existing), + PlatformSymbols::getPlatformServerName()); } } @@ -595,7 +596,9 @@ final class PhabricatorAuthRegisterController if ($is_setup) { $crumbs->addTextCrumb(pht('Setup Admin Account')); - $title = pht('Welcome to Phabricator'); + $title = pht( + 'Welcome to %s', + PlatformSymbols::getPlatformServerName()); } else { $crumbs->addTextCrumb(pht('Register')); $crumbs->addTextCrumb($provider->getProviderName()); @@ -607,7 +610,10 @@ final class PhabricatorAuthRegisterController if ($is_setup) { $welcome_view = id(new PHUIInfoView()) ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) - ->setTitle(pht('Welcome to Phabricator')) + ->setTitle( + pht( + 'Welcome to %s', + PlatformSymbols::getPlatformServerName())) ->appendChild( pht( 'Installation is complete. Register your administrator account '. @@ -710,8 +716,9 @@ final class PhabricatorAuthRegisterController } private function sendWaitingForApprovalEmail(PhabricatorUser $user) { - $title = '[Phabricator] '.pht( - 'New User "%s" Awaiting Approval', + $title = pht( + '[%s] New User "%s" Awaiting Approval', + PlatformSymbols::getPlatformServerName(), $user->getUsername()); $body = new PhabricatorMetaMTAMailBody(); diff --git a/src/applications/auth/controller/PhabricatorAuthSSHKeyGenerateController.php b/src/applications/auth/controller/PhabricatorAuthSSHKeyGenerateController.php index 206a2f1c4a..4a48a666c2 100644 --- a/src/applications/auth/controller/PhabricatorAuthSSHKeyGenerateController.php +++ b/src/applications/auth/controller/PhabricatorAuthSSHKeyGenerateController.php @@ -105,7 +105,7 @@ final class PhabricatorAuthSSHKeyGenerateController 'This workflow will generate a new SSH keypair, add the public '. 'key, and let you download the private key.')) ->appendParagraph( - pht('Phabricator will not retain a copy of the private key.')) + pht('The private key will not be retained.')) ->addSubmitButton(pht('Generate New Keypair')) ->addCancelButton($cancel_uri); } catch (Exception $ex) { diff --git a/src/applications/auth/controller/PhabricatorAuthSetExternalController.php b/src/applications/auth/controller/PhabricatorAuthSetExternalController.php index 51dfcab53f..8b0a44b9dc 100644 --- a/src/applications/auth/controller/PhabricatorAuthSetExternalController.php +++ b/src/applications/auth/controller/PhabricatorAuthSetExternalController.php @@ -42,10 +42,11 @@ final class PhabricatorAuthSetExternalController PhabricatorAuthLinkMessageType::MESSAGEKEY); if (!strlen($text)) { $text = pht( - 'You can link your Phabricator account to an external account to '. + 'You can link your %s account to an external account to '. 'allow you to log in more easily in the future. To continue, choose '. 'an account to link below. If you prefer not to link your account, '. - 'you can skip this step.'); + 'you can skip this step.', + PlatformSymbols::getPlatformServerName()); } $remarkup_view = new PHUIRemarkupView($viewer, $text); diff --git a/src/applications/auth/controller/PhabricatorAuthStartController.php b/src/applications/auth/controller/PhabricatorAuthStartController.php index 7e9b17feff..5789740e14 100644 --- a/src/applications/auth/controller/PhabricatorAuthStartController.php +++ b/src/applications/auth/controller/PhabricatorAuthStartController.php @@ -90,11 +90,11 @@ final class PhabricatorAuthStartController return $this->renderError( pht( - 'This Phabricator install is not configured with any enabled '. - 'authentication providers which can be used to log in. If you '. - 'have accidentally locked yourself out by disabling all providers, '. - 'you can use `%s` to recover access to an account.', - 'phabricator/bin/auth recover ')); + 'This server is not configured with any enabled authentication '. + 'providers which can be used to log in. If you have accidentally '. + 'locked yourself out by disabling all providers, you can use `%s` '. + 'to recover access to an account.', + './bin/auth recover ')); } $next_uri = $request->getStr('next'); @@ -252,7 +252,7 @@ final class PhabricatorAuthStartController $message = pht( 'ERROR: You are making a Conduit API request to "%s", but the correct '. - 'HTTP request path to use in order to access a COnduit method is "%s" '. + 'HTTP request path to use in order to access a Conduit method is "%s" '. '(for example, "%s"). Check your configuration.', $request_path, $conduit_path, diff --git a/src/applications/auth/controller/PhabricatorAuthUnlinkController.php b/src/applications/auth/controller/PhabricatorAuthUnlinkController.php index ede9d9d94a..a4843a7ccd 100644 --- a/src/applications/auth/controller/PhabricatorAuthUnlinkController.php +++ b/src/applications/auth/controller/PhabricatorAuthUnlinkController.php @@ -86,7 +86,7 @@ final class PhabricatorAuthUnlinkController ->appendChild( pht( 'You can not unlink this account because the administrator has '. - 'configured Phabricator to make links to "%s" accounts permanent.', + 'configured this server to make links to "%s" accounts permanent.', $provider->getProviderName())) ->addCancelButton($done_uri); } @@ -123,7 +123,7 @@ final class PhabricatorAuthUnlinkController $title = pht('Unlink "%s" Account?', $provider->getProviderName()); $body = pht( 'You will no longer be able to use your %s account to '. - 'log in to Phabricator.', + 'log in.', $provider->getProviderName()); return $this->newDialog() diff --git a/src/applications/auth/controller/PhabricatorEmailVerificationController.php b/src/applications/auth/controller/PhabricatorEmailVerificationController.php index e8138339af..996795eb94 100644 --- a/src/applications/auth/controller/PhabricatorEmailVerificationController.php +++ b/src/applications/auth/controller/PhabricatorEmailVerificationController.php @@ -44,7 +44,7 @@ final class PhabricatorEmailVerificationController $title = pht('Address Already Verified'); $content = pht( 'This email address has already been verified.'); - $continue = pht('Continue to Phabricator'); + $continue = pht('Continue'); } else if ($request->isFormPost()) { id(new PhabricatorUserEditor()) @@ -55,7 +55,7 @@ final class PhabricatorEmailVerificationController $content = pht( 'The email address %s is now verified.', phutil_tag('strong', array(), $email->getAddress())); - $continue = pht('Continue to Phabricator'); + $continue = pht('Continue'); } else { $title = pht('Verify Email Address'); $content = pht( diff --git a/src/applications/auth/controller/PhabricatorMustVerifyEmailController.php b/src/applications/auth/controller/PhabricatorMustVerifyEmailController.php index bf3410139d..2f15460eac 100644 --- a/src/applications/auth/controller/PhabricatorMustVerifyEmailController.php +++ b/src/applications/auth/controller/PhabricatorMustVerifyEmailController.php @@ -34,8 +34,7 @@ final class PhabricatorMustVerifyEmailController $must_verify = pht( 'You must verify your email address to log in. You should have a '. - 'new email message from Phabricator with verification instructions '. - 'in your inbox (%s).', + 'new email message with verification instructions in your inbox (%s).', phutil_tag('strong', array(), $email_address)); $send_again = pht( diff --git a/src/applications/auth/controller/config/PhabricatorAuthDisableController.php b/src/applications/auth/controller/config/PhabricatorAuthDisableController.php index 252f159ec4..15b5461186 100644 --- a/src/applications/auth/controller/config/PhabricatorAuthDisableController.php +++ b/src/applications/auth/controller/config/PhabricatorAuthDisableController.php @@ -49,12 +49,12 @@ final class PhabricatorAuthDisableController if ($config->getShouldAllowRegistration()) { $body = pht( 'Do you want to enable this provider? Users will be able to use '. - 'their existing external accounts to register new Phabricator '. - 'accounts and log in using linked accounts.'); + 'their existing external accounts to register new accounts and '. + 'log in using linked accounts.'); } else { $body = pht( 'Do you want to enable this provider? Users will be able to log '. - 'in to Phabricator using linked accounts.'); + 'in using linked accounts.'); } $button = pht('Enable Provider'); } else { diff --git a/src/applications/auth/controller/config/PhabricatorAuthEditController.php b/src/applications/auth/controller/config/PhabricatorAuthEditController.php index f602c4fb24..693fc5bffd 100644 --- a/src/applications/auth/controller/config/PhabricatorAuthEditController.php +++ b/src/applications/auth/controller/config/PhabricatorAuthEditController.php @@ -220,7 +220,7 @@ final class PhabricatorAuthEditController } else { $registration_warning = pht( "NOTE: Any user who can browse to this install's login page will be ". - "able to register a Phabricator account. To restrict who can register ". + "able to register an account. To restrict who can register ". "an account, configure [[ %s | %s ]].", $config_href, $config_name); @@ -238,10 +238,9 @@ final class PhabricatorAuthEditController phutil_tag('strong', array(), pht('Allow Registration:')), ' ', pht( - 'Allow users to register new Phabricator accounts using this '. - 'provider. If you disable registration, users can still use this '. - 'provider to log in to existing accounts, but will not be able to '. - 'create new accounts.'), + 'Allow users to register new accounts using this provider. If you '. + 'disable registration, users can still use this provider to log in '. + 'to existing accounts, but will not be able to create new accounts.'), ); $str_link = hsprintf( @@ -249,29 +248,29 @@ final class PhabricatorAuthEditController pht('Allow Linking Accounts'), pht( 'Allow users to link account credentials for this provider to '. - 'existing Phabricator accounts. There is normally no reason to '. - 'disable this unless you are trying to move away from a provider '. - 'and want to stop users from creating new account links.')); + 'existing accounts. There is normally no reason to disable this '. + 'unless you are trying to move away from a provider and want to '. + 'stop users from creating new account links.')); $str_unlink = hsprintf( '%s: %s', pht('Allow Unlinking Accounts'), pht( 'Allow users to unlink account credentials for this provider from '. - 'existing Phabricator accounts. If you disable this, Phabricator '. - 'accounts will be permanently bound to provider accounts.')); + 'existing accounts. If you disable this, accounts will be '. + 'permanently bound to provider accounts.')); $str_trusted_email = hsprintf( '%s: %s', pht('Trust Email Addresses'), pht( - 'Phabricator will skip email verification for accounts registered '. + 'Skip email verification for accounts registered '. 'through this provider.')); $str_auto_login = hsprintf( '%s: %s', pht('Allow Auto Login'), pht( - 'Phabricator will automatically login with this provider if it is '. + 'Automatically log in with this provider if it is '. 'the only available provider.')); $form = id(new AphrontFormView()) diff --git a/src/applications/auth/controller/config/PhabricatorAuthListController.php b/src/applications/auth/controller/config/PhabricatorAuthListController.php index b25c791e27..690aa4e57c 100644 --- a/src/applications/auth/controller/config/PhabricatorAuthListController.php +++ b/src/applications/auth/controller/config/PhabricatorAuthListController.php @@ -53,8 +53,7 @@ final class PhabricatorAuthListController $list->setNoDataString( pht( '%s You have not added authentication providers yet. Use "%s" to add '. - 'a provider, which will let users register new Phabricator accounts '. - 'and log in.', + 'a provider, which will let users register new accounts and log in.', phutil_tag( 'strong', array(), diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberTestController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberTestController.php index 2c25fa3f4a..f8c8b013bf 100644 --- a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberTestController.php +++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberTestController.php @@ -39,7 +39,7 @@ final class PhabricatorAuthContactNumberTestController ->setSensitiveContent(false) ->setBody( pht( - 'This is a terse test text message from Phabricator (%s).', + 'This is a terse test text message (from "%s").', $uri->getDomain())) ->save(); diff --git a/src/applications/auth/factor/PhabricatorDuoAuthFactor.php b/src/applications/auth/factor/PhabricatorDuoAuthFactor.php index a84337a764..65f0aa5e4b 100644 --- a/src/applications/auth/factor/PhabricatorDuoAuthFactor.php +++ b/src/applications/auth/factor/PhabricatorDuoAuthFactor.php @@ -133,7 +133,9 @@ final class PhabricatorDuoAuthFactor ->setTransactionType($xaction_usernames) ->setOptions( array( - 'username' => pht('Use Phabricator Username'), + 'username' => pht( + 'Use %s Username', + PlatformSymbols::getPlatformServerName()), 'email' => pht('Use Primary Email Address'), )), id(new PhabricatorSelectEditField()) @@ -510,7 +512,7 @@ final class PhabricatorDuoAuthFactor ->setIsError(true) ->setErrorMessage( pht( - 'This factor has been removed from your device, so Phabricator '. + 'This factor has been removed from your device, so this server '. 'can not send you a challenge. To continue, an administrator '. 'must strip this factor from your account.')); } @@ -547,7 +549,7 @@ final class PhabricatorDuoAuthFactor // The Duo push timeout is 60 seconds. Set our challenge to expire slightly // more quickly so that we'll re-issue a new challenge before Duo times out. // This should keep users away from a dead-end where they can't respond to - // Duo but Phabricator won't issue a new challenge yet. + // Duo but we won't issue a new challenge yet. $ttl_seconds = 55; return array( diff --git a/src/applications/auth/factor/PhabricatorSMSAuthFactor.php b/src/applications/auth/factor/PhabricatorSMSAuthFactor.php index ba46de980e..33f640e692 100644 --- a/src/applications/auth/factor/PhabricatorSMSAuthFactor.php +++ b/src/applications/auth/factor/PhabricatorSMSAuthFactor.php @@ -388,7 +388,8 @@ final class PhabricatorSMSAuthFactor ->setSensitiveContent(true) ->setBody( pht( - 'Phabricator (%s) MFA Code: %s', + '%s (%s) MFA Code: %s', + PlatformSymbols::getPlatformServerName(), $this->getInstallDisplayName(), $envelope->openEnvelope())) ->save(); diff --git a/src/applications/auth/guidance/PhabricatorAuthProvidersGuidanceEngineExtension.php b/src/applications/auth/guidance/PhabricatorAuthProvidersGuidanceEngineExtension.php index 32482a582a..d4d41f1d83 100644 --- a/src/applications/auth/guidance/PhabricatorAuthProvidersGuidanceEngineExtension.php +++ b/src/applications/auth/guidance/PhabricatorAuthProvidersGuidanceEngineExtension.php @@ -42,7 +42,7 @@ final class PhabricatorAuthProvidersGuidanceEngineExtension if ($domains_value) { $message = pht( - 'Phabricator is configured with an email domain whitelist (in %s), so '. + 'This server is configured with an email domain whitelist (in %s), so '. 'only users with a verified email address at one of these %s '. 'allowed domain(s) will be able to register an account: %s', $domains_link, @@ -53,7 +53,7 @@ final class PhabricatorAuthProvidersGuidanceEngineExtension ->setMessage($message); } else { $message = pht( - 'Anyone who can browse to this Phabricator install will be able to '. + 'Anyone who can browse to this this server will be able to '. 'register an account. To add email domain restrictions, configure '. '%s.', $domains_link); diff --git a/src/applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php b/src/applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php index 3190a842f7..21ef32ae05 100644 --- a/src/applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php +++ b/src/applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php @@ -9,8 +9,7 @@ final class PhabricatorAuthManagementRecoverWorkflow ->setExamples('**recover** __username__') ->setSynopsis( pht( - 'Recover access to an account if you have locked yourself out '. - 'of Phabricator.')) + 'Recover access to an account if you have locked yourself out.')) ->setArguments( array( array( diff --git a/src/applications/auth/management/PhabricatorAuthManagementRevokeWorkflow.php b/src/applications/auth/management/PhabricatorAuthManagementRevokeWorkflow.php index 6d907e67d6..67ee746c2c 100644 --- a/src/applications/auth/management/PhabricatorAuthManagementRevokeWorkflow.php +++ b/src/applications/auth/management/PhabricatorAuthManagementRevokeWorkflow.php @@ -60,7 +60,7 @@ final class PhabricatorAuthManagementRevokeWorkflow $type = $args->getArg('type'); $is_everything = $args->getArg('everything'); - if (!strlen($type) && !$is_everything) { + if ($type === null && !$is_everything) { if ($is_list) { // By default, "bin/revoke --list" implies "--everything". $types = $all_types; @@ -94,7 +94,7 @@ final class PhabricatorAuthManagementRevokeWorkflow $from = $args->getArg('from'); if ($is_list) { - if (strlen($from) || $is_everywhere) { + if ($from !== null || $is_everywhere) { throw new PhutilArgumentUsageException( pht( 'You can not "--list" and revoke credentials (with "--from" or '. diff --git a/src/applications/auth/management/PhabricatorAuthManagementTrustOAuthClientWorkflow.php b/src/applications/auth/management/PhabricatorAuthManagementTrustOAuthClientWorkflow.php index ee5e50b38e..8455b5697f 100644 --- a/src/applications/auth/management/PhabricatorAuthManagementTrustOAuthClientWorkflow.php +++ b/src/applications/auth/management/PhabricatorAuthManagementTrustOAuthClientWorkflow.php @@ -9,9 +9,9 @@ final class PhabricatorAuthManagementTrustOAuthClientWorkflow ->setExamples('**trust-oauth-client** [--id client_id]') ->setSynopsis( pht( - 'Set Phabricator to trust an OAuth client. Phabricator '. - 'redirects to trusted OAuth clients that users have authorized '. - 'without user intervention.')) + 'Mark an OAuth client as trusted. Trusted OAuth clients may be '. + 'reauthorized without requiring users to manually confirm the '. + 'action.')) ->setArguments( array( array( @@ -28,8 +28,7 @@ final class PhabricatorAuthManagementTrustOAuthClientWorkflow if (!$id) { throw new PhutilArgumentUsageException( pht( - 'Specify an OAuth client id with %s.', - '--id')); + 'Specify an OAuth client id with "--id".')); } $client = id(new PhabricatorOAuthServerClientQuery()) @@ -46,7 +45,7 @@ final class PhabricatorAuthManagementTrustOAuthClientWorkflow if ($client->getIsTrusted()) { throw new PhutilArgumentUsageException( pht( - 'Phabricator already trusts OAuth client "%s".', + 'OAuth client "%s" is already trusted.', $client->getName())); } @@ -57,7 +56,7 @@ final class PhabricatorAuthManagementTrustOAuthClientWorkflow $console->writeOut( "%s\n", pht( - 'Updated; Phabricator trusts OAuth client %s.', + 'OAuth client "%s" is now trusted.', $client->getName())); } diff --git a/src/applications/auth/management/PhabricatorAuthManagementUntrustOAuthClientWorkflow.php b/src/applications/auth/management/PhabricatorAuthManagementUntrustOAuthClientWorkflow.php index a0b3e02fa4..d3d7ff3aea 100644 --- a/src/applications/auth/management/PhabricatorAuthManagementUntrustOAuthClientWorkflow.php +++ b/src/applications/auth/management/PhabricatorAuthManagementUntrustOAuthClientWorkflow.php @@ -9,9 +9,8 @@ final class PhabricatorAuthManagementUntrustOAuthClientWorkflow ->setExamples('**untrust-oauth-client** [--id client_id]') ->setSynopsis( pht( - 'Set Phabricator to not trust an OAuth client. Phabricator '. - 'redirects to trusted OAuth clients that users have authorized '. - 'without user intervention.')) + 'Remove trust from an OAuth client. Users must manually confirm '. + 'reauthorization of untrusted OAuth clients.')) ->setArguments( array( array( @@ -46,7 +45,7 @@ final class PhabricatorAuthManagementUntrustOAuthClientWorkflow if (!$client->getIsTrusted()) { throw new PhutilArgumentUsageException( pht( - 'Phabricator already does not trust OAuth client "%s".', + 'OAuth client "%s" is already untrusted.', $client->getName())); } @@ -57,7 +56,7 @@ final class PhabricatorAuthManagementUntrustOAuthClientWorkflow $console->writeOut( "%s\n", pht( - 'Updated; Phabricator does not trust OAuth client %s.', + 'OAuth client "%s" is now trusted.', $client->getName())); } diff --git a/src/applications/auth/provider/PhabricatorAmazonAuthProvider.php b/src/applications/auth/provider/PhabricatorAmazonAuthProvider.php index c35bd547f3..4f62ee9f45 100644 --- a/src/applications/auth/provider/PhabricatorAmazonAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorAmazonAuthProvider.php @@ -14,7 +14,7 @@ final class PhabricatorAmazonAuthProvider $https_note = null; if ($uri->getProtocol() !== 'https') { $https_note = pht( - 'NOTE: Amazon **requires** HTTPS, but your Phabricator install does '. + 'NOTE: Amazon **requires** HTTPS, but this service does '. 'not use HTTPS. **You will not be able to add Amazon as an '. 'authentication provider until you configure HTTPS on this install**.'); } diff --git a/src/applications/auth/provider/PhabricatorGitHubAuthProvider.php b/src/applications/auth/provider/PhabricatorGitHubAuthProvider.php index 6a7667e2d6..52bcae4916 100644 --- a/src/applications/auth/provider/PhabricatorGitHubAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorGitHubAuthProvider.php @@ -19,7 +19,7 @@ final class PhabricatorGitHubAuthProvider "You should use these settings in your application:". "\n\n". " - **URL:** Set this to your full domain with protocol. For this ". - " Phabricator install, the correct value is: `%s`\n". + " server, the correct value is: `%s`\n". " - **Callback URL**: Set this to: `%s`\n". "\n\n". "Once you've created an application, copy the **Client ID** and ". diff --git a/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php b/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php index 8a858f2bea..7b2756dad5 100644 --- a/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php @@ -31,7 +31,7 @@ final class PhabricatorJIRAAuthProvider "settings to create an application:\n\n". " - **Server URL**: `%s`\n". " - Then, click **Next**. On the second page:\n". - " - **Application Name**: `Phabricator`\n". + " - **Application Name**: `%s`\n". " - **Application Type**: `Generic Application`\n". " - Then, click **Create**.\n\n". "**Configure Your Application**: Find the application you just ". @@ -41,13 +41,15 @@ final class PhabricatorJIRAAuthProvider "settings:\n\n". " - **Consumer Key**: Set this to the \"Consumer Key\" value in the ". "form above.\n". - " - **Consumer Name**: `Phabricator`\n". + " - **Consumer Name**: `%s`\n". " - **Public Key**: Set this to the \"Public Key\" value in the ". "form above.\n". " - **Consumer Callback URL**: `%s`\n". "Click **Save** in JIRA. Authentication should now be configured, ". "and this provider should work correctly.", PhabricatorEnv::getProductionURI('/'), + PlatformSymbols::getPlatformServerName(), + PlatformSymbols::getPlatformServerName(), $login_uri); } } @@ -169,7 +171,7 @@ final class PhabricatorJIRAAuthProvider "The PHP 'openssl' extension is not installed. You must install ". "this extension in order to add a JIRA authentication provider, ". "because JIRA OAuth requests use the RSA-SHA1 signing algorithm. ". - "Install the 'openssl' extension, restart Phabricator, and try ". + "Install the 'openssl' extension, restart everything, and try ". "again.")); } @@ -198,8 +200,8 @@ final class PhabricatorJIRAAuthProvider ->appendRemarkupInstructions( pht( "**JIRA Instance Name**\n\n". - "Choose a permanent name for this instance of JIRA. Phabricator ". - "uses this name internally to keep track of this instance of ". + "Choose a permanent name for this instance of JIRA. This name is ". + "used internally to keep track of this particular instance of ". "JIRA, in case the URL changes later.\n\n". "Use lowercase letters, digits, and period. For example, ". "`jira`, `jira.mycompany` or `jira.engineering` are reasonable ". @@ -281,8 +283,7 @@ final class PhabricatorJIRAAuthProvider new PHUIRemarkupView( $viewer, pht( - '**Post a comment** in the JIRA task, similar to the '. - 'emails Phabricator sends.')), + '**Post a comment** in the JIRA task.')), $this->shouldCreateJIRAComment())); } diff --git a/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php b/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php index c899caca71..d1db832aa2 100644 --- a/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php @@ -11,7 +11,7 @@ final class PhabricatorLDAPAuthProvider extends PhabricatorAuthProvider { public function getDescriptionForCreate() { return pht( 'Configure a connection to an LDAP server so that users can use their '. - 'LDAP credentials to log in to Phabricator.'); + 'LDAP credentials to log in.'); } public function getDefaultProviderConfig() { @@ -312,8 +312,8 @@ final class PhabricatorLDAPAuthProvider extends PhabricatorAuthProvider { $instructions = array( self::KEY_SEARCH_ATTRIBUTES => pht( - "When a user types their LDAP username and password into Phabricator, ". - "Phabricator can either bind to LDAP with those credentials directly ". + "When a user provides their LDAP username and password, this ". + "software can either bind to LDAP with those credentials directly ". "(which is simpler, but not as powerful) or bind to LDAP with ". "anonymous credentials, then search for record matching the supplied ". "credentials (which is more complicated, but more powerful).\n\n". diff --git a/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php b/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php index ef1991e8d7..b1590b9c82 100644 --- a/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php +++ b/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php @@ -21,7 +21,7 @@ abstract class PhabricatorOAuth1AuthProvider $config = $this->getProviderConfig(); $adapter->setConsumerKey($config->getProperty(self::PROPERTY_CONSUMER_KEY)); $secret = $config->getProperty(self::PROPERTY_CONSUMER_SECRET); - if (strlen($secret)) { + if (phutil_nonempty_string($secret)) { $adapter->setConsumerSecret(new PhutilOpaqueEnvelope($secret)); } $adapter->setCallbackURI(PhabricatorEnv::getURI($this->getLoginURI())); diff --git a/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php b/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php index 8799c43c6f..4c1c31f146 100644 --- a/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php @@ -7,17 +7,16 @@ final class PhabricatorPhabricatorAuthProvider const PROPERTY_PHABRICATOR_URI = 'oauth2:phabricator:uri'; public function getProviderName() { - return pht('Phabricator'); + return PlatformSymbols::getPlatformServerName(); } public function getConfigurationHelp() { if ($this->isCreate()) { return pht( - "**Step 1 of 2 - Name Phabricator OAuth Instance**\n\n". - 'Choose a permanent name for the OAuth server instance of '. - 'Phabricator. //This// instance of Phabricator uses this name '. - 'internally to keep track of the OAuth server instance of '. - 'Phabricator, in case the URL changes later.'); + "**Step 1 of 2 - Name Remote Server**\n\n". + 'Choose a permanent name for the remote server you want to connect '. + 'to. This name is used internally to keep track of the remote '. + 'server, in case the URL changes later.'); } return parent::getConfigurationHelp(); @@ -29,8 +28,8 @@ final class PhabricatorPhabricatorAuthProvider $login_uri = PhabricatorEnv::getURI($this->getLoginURI()); return pht( - "**Step 2 of 2 - Configure Phabricator OAuth Instance**\n\n". - "To configure Phabricator OAuth, create a new application here:". + "**Step 2 of 2 - Configure OAuth Server**\n\n". + "To configure OAuth, create a new application here:". "\n\n". "%s/oauthserver/client/create/". "\n\n". @@ -54,7 +53,7 @@ final class PhabricatorPhabricatorAuthProvider } protected function getLoginIcon() { - return 'Phabricator'; + return PlatformSymbols::getPlatformServerName(); } private function isCreate() { @@ -106,23 +105,23 @@ final class PhabricatorPhabricatorAuthProvider $key_uri = self::PROPERTY_PHABRICATOR_URI; if (!strlen($values[$key_name])) { - $errors[] = pht('Phabricator instance name is required.'); + $errors[] = pht('Server name is required.'); $issues[$key_name] = pht('Required'); } else if (!preg_match('/^[a-z0-9.]+\z/', $values[$key_name])) { $errors[] = pht( - 'Phabricator instance name must contain only lowercase letters, '. + 'Server name must contain only lowercase letters, '. 'digits, and periods.'); $issues[$key_name] = pht('Invalid'); } if (!strlen($values[$key_uri])) { - $errors[] = pht('Phabricator base URI is required.'); + $errors[] = pht('Base URI is required.'); $issues[$key_uri] = pht('Required'); } else { $uri = new PhutilURI($values[$key_uri]); if (!$uri->getProtocol()) { $errors[] = pht( - 'Phabricator base URI should include protocol (like "%s").', + 'Base URI should include protocol (like "%s").', 'https://'); $issues[$key_uri] = pht('Invalid'); } @@ -161,7 +160,7 @@ final class PhabricatorPhabricatorAuthProvider $form ->appendChild( id(new AphrontFormTextControl()) - ->setLabel(pht('Phabricator Instance Name')) + ->setLabel(pht('Server Name')) ->setValue($v_name) ->setName(self::PROPERTY_PHABRICATOR_NAME) ->setError($e_name) @@ -170,26 +169,25 @@ final class PhabricatorPhabricatorAuthProvider phutil_tag( 'tt', array(), - '`phabricator.oauthserver`')))); + '`example.oauthserver`')))); } else { $form ->appendChild( id(new AphrontFormStaticControl()) - ->setLabel(pht('Phabricator Instance Name')) + ->setLabel(pht('Server Name')) ->setValue($v_name)); } $form ->appendChild( id(new AphrontFormTextControl()) - ->setLabel(pht('Phabricator Base URI')) + ->setLabel(pht('Base URI')) ->setValue($v_uri) ->setName(self::PROPERTY_PHABRICATOR_URI) ->setCaption( pht( - 'The URI where the OAuth server instance of Phabricator is '. - 'installed. For example: %s', - phutil_tag('tt', array(), 'https://phabricator.mycompany.com/'))) + 'The URI where the OAuth server is installed. For example: %s', + phutil_tag('tt', array(), 'https://devtools.example.com/'))) ->setError($e_uri)); if (!$is_setup) { diff --git a/src/applications/auth/provider/PhabricatorWordPressAuthProvider.php b/src/applications/auth/provider/PhabricatorWordPressAuthProvider.php index 092915f58c..8189685f81 100644 --- a/src/applications/auth/provider/PhabricatorWordPressAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorWordPressAuthProvider.php @@ -19,7 +19,7 @@ final class PhabricatorWordPressAuthProvider "You should use these settings in your application:". "\n\n". " - **URL:** Set this to your full domain with protocol. For this ". - " Phabricator install, the correct value is: `%s`\n". + " server, the correct value is: `%s`\n". " - **Redirect URL**: Set this to: `%s`\n". "\n\n". "Once you've created an application, copy the **Client ID** and ". diff --git a/src/applications/auth/query/PhabricatorAuthChallengeQuery.php b/src/applications/auth/query/PhabricatorAuthChallengeQuery.php index 195abe0884..c6abead11f 100644 --- a/src/applications/auth/query/PhabricatorAuthChallengeQuery.php +++ b/src/applications/auth/query/PhabricatorAuthChallengeQuery.php @@ -40,10 +40,6 @@ final class PhabricatorAuthChallengeQuery return new PhabricatorAuthChallenge(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php b/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php index 77b3b559dd..ba507c757b 100644 --- a/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php +++ b/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php @@ -44,10 +44,6 @@ final class PhabricatorAuthContactNumberQuery return new PhabricatorAuthContactNumber(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php b/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php index 5f838f66ba..dd73d0b081 100644 --- a/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php +++ b/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php @@ -38,10 +38,6 @@ final class PhabricatorAuthFactorConfigQuery return new PhabricatorAuthFactorConfig(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php b/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php index 57b554885c..7083545e3e 100644 --- a/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php +++ b/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php @@ -32,10 +32,6 @@ final class PhabricatorAuthFactorProviderQuery return new PhabricatorAuthFactorProvider(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/auth/query/PhabricatorAuthMessageQuery.php b/src/applications/auth/query/PhabricatorAuthMessageQuery.php index 384c8de23b..7158d03a00 100644 --- a/src/applications/auth/query/PhabricatorAuthMessageQuery.php +++ b/src/applications/auth/query/PhabricatorAuthMessageQuery.php @@ -26,10 +26,6 @@ final class PhabricatorAuthMessageQuery return new PhabricatorAuthMessage(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/auth/query/PhabricatorAuthPasswordQuery.php b/src/applications/auth/query/PhabricatorAuthPasswordQuery.php index 483936fb30..a77fd54b13 100644 --- a/src/applications/auth/query/PhabricatorAuthPasswordQuery.php +++ b/src/applications/auth/query/PhabricatorAuthPasswordQuery.php @@ -38,10 +38,6 @@ final class PhabricatorAuthPasswordQuery return new PhabricatorAuthPassword(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php b/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php index ee073e3ac1..30e5dad113 100644 --- a/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php +++ b/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php @@ -32,10 +32,6 @@ final class PhabricatorAuthProviderConfigQuery return new PhabricatorAuthProviderConfig(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php b/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php index 3a310ed173..7d4aac4a01 100644 --- a/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php +++ b/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php @@ -47,10 +47,6 @@ final class PhabricatorAuthSSHKeyQuery return new PhabricatorAuthSSHKey(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $keys) { $object_phids = mpull($keys, 'getObjectPHID'); diff --git a/src/applications/auth/query/PhabricatorAuthSessionQuery.php b/src/applications/auth/query/PhabricatorAuthSessionQuery.php index 00a663e964..4bc7eba73f 100644 --- a/src/applications/auth/query/PhabricatorAuthSessionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthSessionQuery.php @@ -38,10 +38,6 @@ final class PhabricatorAuthSessionQuery return new PhabricatorAuthSession(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $sessions) { $identity_phids = mpull($sessions, 'getUserPHID'); diff --git a/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php b/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php index 72141f75f0..c5cb39096c 100644 --- a/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php +++ b/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php @@ -44,10 +44,6 @@ final class PhabricatorAuthTemporaryTokenQuery return new PhabricatorAuthTemporaryToken(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php b/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php index c44e0aed4f..b5c5b6eaa2 100644 --- a/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php +++ b/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php @@ -38,10 +38,6 @@ final class PhabricatorExternalAccountIdentifierQuery return new PhabricatorExternalAccountIdentifier(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/auth/query/PhabricatorExternalAccountQuery.php b/src/applications/auth/query/PhabricatorExternalAccountQuery.php index bdc030f20c..f44821d7a9 100644 --- a/src/applications/auth/query/PhabricatorExternalAccountQuery.php +++ b/src/applications/auth/query/PhabricatorExternalAccountQuery.php @@ -66,10 +66,6 @@ final class PhabricatorExternalAccountQuery return new PhabricatorExternalAccount(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $accounts) { $viewer = $this->getViewer(); diff --git a/src/applications/auth/worker/PhabricatorAuthInviteWorker.php b/src/applications/auth/worker/PhabricatorAuthInviteWorker.php index 7e9e29be50..25ad6961f2 100644 --- a/src/applications/auth/worker/PhabricatorAuthInviteWorker.php +++ b/src/applications/auth/worker/PhabricatorAuthInviteWorker.php @@ -51,8 +51,10 @@ final class PhabricatorAuthInviteWorker ->setForceDelivery(true) ->setSubject( pht( - '[Phabricator] %s has invited you to join Phabricator', - $author->getFullName())) + '[%s] %s has invited you to join %s', + PlatformSymbols::getPlatformServerName(), + $author->getFullName(), + PlatformSymbols::getPlatformServerName())) ->setBody($template) ->saveAndSend(); } diff --git a/src/applications/badges/query/PhabricatorBadgesAwardQuery.php b/src/applications/badges/query/PhabricatorBadgesAwardQuery.php index 347462de4d..57e53a5a30 100644 --- a/src/applications/badges/query/PhabricatorBadgesAwardQuery.php +++ b/src/applications/badges/query/PhabricatorBadgesAwardQuery.php @@ -57,10 +57,6 @@ final class PhabricatorBadgesAwardQuery return (bool)$this->badgeStatuses; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - public function newResultObject() { return new PhabricatorBadgesAward(); } diff --git a/src/applications/badges/query/PhabricatorBadgesQuery.php b/src/applications/badges/query/PhabricatorBadgesQuery.php index dcadf881fe..cc59465f67 100644 --- a/src/applications/badges/query/PhabricatorBadgesQuery.php +++ b/src/applications/badges/query/PhabricatorBadgesQuery.php @@ -34,10 +34,6 @@ final class PhabricatorBadgesQuery $ngrams); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function getPrimaryTableAlias() { return 'badges'; } diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php index 407f3171c0..ba1edc26d3 100644 --- a/src/applications/base/PhabricatorApplication.php +++ b/src/applications/base/PhabricatorApplication.php @@ -135,10 +135,9 @@ abstract class PhabricatorApplication /** - * Returns true if an application is first-party (developed by Phacility) - * and false otherwise. + * Returns true if an application is first-party and false otherwise. * - * @return bool True if this application is developed by Phacility. + * @return bool True if this application is first-party. */ final public function isFirstParty() { $where = id(new ReflectionClass($this))->getFileName(); @@ -557,7 +556,7 @@ abstract class PhabricatorApplication case PhabricatorPolicyCapability::CAN_VIEW: if (!$this->canUninstall()) { return pht( - 'This application is required for Phabricator to operate, so all '. + 'This application is required, so all '. 'users must have access to it.'); } else { return null; diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php index a463c741d1..db9d456094 100644 --- a/src/applications/base/controller/PhabricatorController.php +++ b/src/applications/base/controller/PhabricatorController.php @@ -420,6 +420,10 @@ abstract class PhabricatorController extends AphrontController { ->setSubmitURI($submit_uri); } + public function newRedirect() { + return id(new AphrontRedirectResponse()); + } + public function newPage() { $page = id(new PhabricatorStandardPageView()) ->setRequest($this->getRequest()) diff --git a/src/applications/cache/management/PhabricatorCacheManagementPurgeWorkflow.php b/src/applications/cache/management/PhabricatorCacheManagementPurgeWorkflow.php index cfe42c918b..37de525a02 100644 --- a/src/applications/cache/management/PhabricatorCacheManagementPurgeWorkflow.php +++ b/src/applications/cache/management/PhabricatorCacheManagementPurgeWorkflow.php @@ -27,11 +27,11 @@ final class PhabricatorCacheManagementPurgeWorkflow $is_all = $args->getArg('all'); $key_list = $args->getArg('caches'); - if ($is_all && strlen($key_list)) { + if ($is_all && phutil_nonempty_string($key_list)) { throw new PhutilArgumentUsageException( pht( 'Specify either "--all" or "--caches", not both.')); - } else if (!$is_all && !strlen($key_list)) { + } else if (!$is_all && !phutil_nonempty_string($key_list)) { throw new PhutilArgumentUsageException( pht( 'Select caches to purge with "--all" or "--caches". Available '. diff --git a/src/applications/cache/spec/PhabricatorCacheSpec.php b/src/applications/cache/spec/PhabricatorCacheSpec.php index 1ed4e713f1..3f3273fd2a 100644 --- a/src/applications/cache/spec/PhabricatorCacheSpec.php +++ b/src/applications/cache/spec/PhabricatorCacheSpec.php @@ -96,8 +96,8 @@ abstract class PhabricatorCacheSpec extends Phobject { $summary = pht('Enabling APC/APCu will improve performance.'); $message = pht( 'The APC or APCu PHP extensions are installed, but not enabled in your '. - 'PHP configuration. Enabling these extensions will improve Phabricator '. - 'performance. Edit the "%s" setting to enable these extensions.', + 'PHP configuration. Enabling these extensions will improve performance. '. + 'Edit the "%s" setting to enable these extensions.', 'apc.enabled'); return $this diff --git a/src/applications/cache/spec/PhabricatorDataCacheSpec.php b/src/applications/cache/spec/PhabricatorDataCacheSpec.php index 103b0d8394..0c0c449c53 100644 --- a/src/applications/cache/spec/PhabricatorDataCacheSpec.php +++ b/src/applications/cache/spec/PhabricatorDataCacheSpec.php @@ -70,7 +70,7 @@ final class PhabricatorDataCacheSpec extends PhabricatorCacheSpec { if (version_compare(phpversion(), '5.5', '>=')) { $message = pht( 'Installing the "APCu" PHP extension will improve performance. '. - 'This extension is strongly recommended. Without it, Phabricator '. + 'This extension is strongly recommended. Without it, this software '. 'must rely on a very inefficient disk-based cache.'); $this diff --git a/src/applications/cache/spec/PhabricatorOpcodeCacheSpec.php b/src/applications/cache/spec/PhabricatorOpcodeCacheSpec.php index 2a59d7e8aa..a4c930fadd 100644 --- a/src/applications/cache/spec/PhabricatorOpcodeCacheSpec.php +++ b/src/applications/cache/spec/PhabricatorOpcodeCacheSpec.php @@ -85,9 +85,9 @@ final class PhabricatorOpcodeCacheSpec extends PhabricatorCacheSpec { $message = pht( 'The "%s" setting is currently disabled in your PHP configuration, '. - 'but Phabricator is running in development mode. This option should '. - 'normally be enabled in development so you do not need to restart '. - 'anything after making changes to the code.', + 'but this software is running in development mode. This option '. + 'should normally be enabled in development so you do not need to '. + 'restart anything after making changes to the code.', 'apc.stat'); $this @@ -174,8 +174,7 @@ final class PhabricatorOpcodeCacheSpec extends PhabricatorCacheSpec { $message = pht( 'The PHP "Zend OPcache" extension is installed, but not enabled in '. 'your PHP configuration. Enabling it will dramatically improve '. - 'Phabricator performance. Edit the "%s" setting to '. - 'enable the extension.', + 'performance. Edit the "%s" setting to enable the extension.', 'opcache.enable'); $this->newIssue('extension.opcache.enable') diff --git a/src/applications/calendar/codex/PhabricatorCalendarEventPolicyCodex.php b/src/applications/calendar/codex/PhabricatorCalendarEventPolicyCodex.php index 97e21d901e..01f6335139 100644 --- a/src/applications/calendar/codex/PhabricatorCalendarEventPolicyCodex.php +++ b/src/applications/calendar/codex/PhabricatorCalendarEventPolicyCodex.php @@ -71,7 +71,7 @@ final class PhabricatorCalendarEventPolicyCodex ->setIsActive($object->isImportedEvent()) ->setDescription( pht( - 'Imported events can not be edited in Phabricator.')); + 'Imported events can not be edited.')); return $rules; } diff --git a/src/applications/calendar/parser/ics/PhutilICSWriter.php b/src/applications/calendar/parser/ics/PhutilICSWriter.php index c5baa791de..c35008e079 100644 --- a/src/applications/calendar/parser/ics/PhutilICSWriter.php +++ b/src/applications/calendar/parser/ics/PhutilICSWriter.php @@ -128,11 +128,15 @@ final class PhutilICSWriter extends Phobject { $properties[] = $this->newTextProperty( 'PRODID', - '-//Phacility//Phabricator//EN'); + self::getICSPRODID()); return $properties; } + public static function getICSPRODID() { + return '-//Phacility//Phabricator//EN'; + } + private function getEventNodeProperties(PhutilCalendarEventNode $event) { $properties = array(); @@ -180,14 +184,14 @@ final class PhutilICSWriter extends Phobject { } $name = $event->getName(); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { $properties[] = $this->newTextProperty( 'SUMMARY', $name); } $description = $event->getDescription(); - if (strlen($description)) { + if (phutil_nonempty_string($description)) { $properties[] = $this->newTextProperty( 'DESCRIPTION', $description); diff --git a/src/applications/calendar/parser/ics/__tests__/PhutilICSWriterTestCase.php b/src/applications/calendar/parser/ics/__tests__/PhutilICSWriterTestCase.php index dec1bf27b0..793afc31f4 100644 --- a/src/applications/calendar/parser/ics/__tests__/PhutilICSWriterTestCase.php +++ b/src/applications/calendar/parser/ics/__tests__/PhutilICSWriterTestCase.php @@ -138,6 +138,12 @@ final class PhutilICSWriterTestCase extends PhutilTestCase { private function assertICS($name, $actual) { $path = dirname(__FILE__).'/data/'.$name; $data = Filesystem::readFile($path); + + $data = str_replace( + '${PRODID}', + PhutilICSWriter::getICSPRODID(), + $data); + $this->assertEqual($data, $actual, pht('ICS: %s', $name)); } diff --git a/src/applications/calendar/parser/ics/__tests__/data/writer-christmas.ics b/src/applications/calendar/parser/ics/__tests__/data/writer-christmas.ics index 4624d151d0..0526034b3f 100644 --- a/src/applications/calendar/parser/ics/__tests__/data/writer-christmas.ics +++ b/src/applications/calendar/parser/ics/__tests__/data/writer-christmas.ics @@ -1,6 +1,6 @@ BEGIN:VCALENDAR VERSION:2.0 -PRODID:-//Phacility//Phabricator//EN +PRODID:${PRODID} BEGIN:VEVENT UID:christmas-day CREATED:20160901T232425Z diff --git a/src/applications/calendar/parser/ics/__tests__/data/writer-office-party.ics b/src/applications/calendar/parser/ics/__tests__/data/writer-office-party.ics index a2fbc81a40..4623e6a11a 100644 --- a/src/applications/calendar/parser/ics/__tests__/data/writer-office-party.ics +++ b/src/applications/calendar/parser/ics/__tests__/data/writer-office-party.ics @@ -1,6 +1,6 @@ BEGIN:VCALENDAR VERSION:2.0 -PRODID:-//Phacility//Phabricator//EN +PRODID:${PRODID} BEGIN:VEVENT UID:office-party CREATED:20161001T120000Z diff --git a/src/applications/calendar/parser/ics/__tests__/data/writer-recurring-christmas.ics b/src/applications/calendar/parser/ics/__tests__/data/writer-recurring-christmas.ics index 2774bb5bb9..d43da0f80c 100644 --- a/src/applications/calendar/parser/ics/__tests__/data/writer-recurring-christmas.ics +++ b/src/applications/calendar/parser/ics/__tests__/data/writer-recurring-christmas.ics @@ -1,6 +1,6 @@ BEGIN:VCALENDAR VERSION:2.0 -PRODID:-//Phacility//Phabricator//EN +PRODID:${PRODID} BEGIN:VEVENT UID:recurring-christmas CREATED:20001225T000000Z diff --git a/src/applications/calendar/parser/ics/__tests__/data/writer-tea-time.ics b/src/applications/calendar/parser/ics/__tests__/data/writer-tea-time.ics index e275fa9730..7d5620e8dc 100644 --- a/src/applications/calendar/parser/ics/__tests__/data/writer-tea-time.ics +++ b/src/applications/calendar/parser/ics/__tests__/data/writer-tea-time.ics @@ -1,6 +1,6 @@ BEGIN:VCALENDAR VERSION:2.0 -PRODID:-//Phacility//Phabricator//EN +PRODID:${PRODID} BEGIN:VEVENT UID:tea-time CREATED:20160915T070000Z diff --git a/src/applications/calendar/query/PhabricatorCalendarExportQuery.php b/src/applications/calendar/query/PhabricatorCalendarExportQuery.php index c51671c806..7ef970216f 100644 --- a/src/applications/calendar/query/PhabricatorCalendarExportQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarExportQuery.php @@ -38,10 +38,6 @@ final class PhabricatorCalendarExportQuery return new PhabricatorCalendarExport(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php b/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php index ea7200e614..35891cfd28 100644 --- a/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php @@ -26,10 +26,6 @@ final class PhabricatorCalendarExternalInviteeQuery return new PhabricatorCalendarExternalInvitee(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php b/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php index 81f309bdca..731b1209cc 100644 --- a/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php @@ -26,10 +26,6 @@ final class PhabricatorCalendarImportLogQuery return new PhabricatorCalendarImportLog(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/calendar/query/PhabricatorCalendarImportQuery.php b/src/applications/calendar/query/PhabricatorCalendarImportQuery.php index 5d44657994..0e3dbbf387 100644 --- a/src/applications/calendar/query/PhabricatorCalendarImportQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarImportQuery.php @@ -32,10 +32,6 @@ final class PhabricatorCalendarImportQuery return new PhabricatorCalendarImport(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php index bc200e7078..a4d5fdd568 100644 --- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php +++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php @@ -261,9 +261,9 @@ final class PhabricatorConduitAPIController return array( 'ERR-INVALID-AUTH', pht( - 'This request originates from outside of the Phabricator '. - 'cluster address range. Requests signed with trusted '. - 'device keys must originate from within the cluster.'), + 'This request originates from outside of the cluster address '. + 'range. Requests signed with trusted device keys must '. + 'originate from within the cluster.'), ); } @@ -364,9 +364,9 @@ final class PhabricatorConduitAPIController return array( 'ERR-INVALID-AUTH', pht( - 'This request originates from outside of the Phabricator '. - 'cluster address range. Requests signed with cluster API '. - 'tokens must originate from within the cluster.'), + 'This request originates from outside of the cluster address '. + 'range. Requests signed with cluster API tokens must '. + 'originate from within the cluster.'), ); } diff --git a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php index b696645e55..70a2253084 100644 --- a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php +++ b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php @@ -88,23 +88,118 @@ final class PhabricatorConduitConsoleController $crumbs->addTextCrumb($method->getAPIMethodName()); $crumbs->setBorder(true); + $documentation_pages = $method->getDocumentationPages($viewer); + + $documentation_view = $this->newDocumentationView( + $method, + $documentation_pages); + $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setFooter(array( + + id(new PhabricatorAnchorView()) + ->setAnchorName('overview'), $info_box, - $method->getMethodDocumentation(), + + id(new PhabricatorAnchorView()) + ->setAnchorName('documentation'), + $documentation_view, + + id(new PhabricatorAnchorView()) + ->setAnchorName('call'), $form_box, + + id(new PhabricatorAnchorView()) + ->setAnchorName('examples'), $this->renderExampleBox($method, null), )); $title = $method->getAPIMethodName(); + $nav = $this->newNavigationView($method, $documentation_pages); + return $this->newPage() ->setTitle($title) ->setCrumbs($crumbs) + ->setNavigation($nav) ->appendChild($view); } + private function newDocumentationView( + ConduitAPIMethod $method, + array $documentation_pages) { + assert_instances_of($documentation_pages, 'ConduitAPIDocumentationPage'); + + $viewer = $this->getViewer(); + + $description_properties = id(new PHUIPropertyListView()); + + $description_properties->addTextContent( + new PHUIRemarkupView($viewer, $method->getMethodDescription())); + + $description_box = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Method Description')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->appendChild($description_properties); + + $view = array(); + $view[] = $description_box; + + foreach ($documentation_pages as $page) { + $view[] = $page->newView(); + } + + return $view; + } + + private function newNavigationView( + ConduitAPIMethod $method, + array $documentation_pages) { + assert_instances_of($documentation_pages, 'ConduitAPIDocumentationPage'); + + $console_uri = urisprintf( + '/method/%s/', + $method->getAPIMethodName()); + $console_uri = $this->getApplicationURI($console_uri); + $console_uri = new PhutilURI($console_uri); + + $nav = id(new AphrontSideNavFilterView()) + ->setBaseURI($console_uri); + + $nav->selectFilter(null); + + $nav->newLink('overview') + ->setHref('#overview') + ->setName(pht('Overview')) + ->setIcon('fa-list'); + + $nav->newLink('documentation') + ->setHref('#documentation') + ->setName(pht('Documentation')) + ->setIcon('fa-book'); + + foreach ($documentation_pages as $page) { + $nav->newLink($page->getAnchor()) + ->setHref('#'.$page->getAnchor()) + ->setName($page->getName()) + ->setIcon($page->getIconIcon()) + ->setIndented(true); + } + + $nav->newLink('call') + ->setHref('#call') + ->setName(pht('Call Method')) + ->setIcon('fa-play'); + + $nav->newLink('examples') + ->setHref('#examples') + ->setName(pht('Examples')) + ->setIcon('fa-folder-open-o'); + + return $nav; + } + private function buildMethodProperties(ConduitAPIMethod $method) { $viewer = $this->getViewer(); @@ -171,7 +266,6 @@ final class PhabricatorConduitConsoleController pht('Errors'), $error_description); - $scope = $method->getRequiredScope(); switch ($scope) { case ConduitAPIMethod::SCOPE_ALWAYS: @@ -201,11 +295,6 @@ final class PhabricatorConduitConsoleController $oauth_description, )); - $view->addSectionHeader( - pht('Description'), PHUIPropertyListView::ICON_SUMMARY); - $view->addTextContent( - new PHUIRemarkupView($viewer, $method->getMethodDescription())); - return $view; } diff --git a/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php b/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php index 7550f92210..d002ea5157 100644 --- a/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php +++ b/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php @@ -87,9 +87,9 @@ final class PhabricatorConduitTokenEditController if ($token->getTokenType() === PhabricatorConduitToken::TYPE_CLUSTER) { $dialog->appendChild( pht( - 'This token is automatically generated by Phabricator, and used '. - 'to make requests between nodes in a Phabricator cluster. You '. - 'can not use this token in external applications.')); + 'This token is automatically generated, and used to make '. + 'requests between nodes in a cluster. You can not use this '. + 'token in external applications.')); } else { $form->appendChild( id(new AphrontFormTextControl()) diff --git a/src/applications/conduit/data/ConduitAPIDocumentationPage.php b/src/applications/conduit/data/ConduitAPIDocumentationPage.php new file mode 100644 index 0000000000..e33266dcd1 --- /dev/null +++ b/src/applications/conduit/data/ConduitAPIDocumentationPage.php @@ -0,0 +1,61 @@ +name = $name; + return $this; + } + + public function getName() { + return $this->name; + } + + public function setAnchor($anchor) { + $this->anchor = $anchor; + return $this; + } + + public function getAnchor() { + return $this->anchor; + } + + public function setContent($content) { + $this->content = $content; + return $this; + } + + public function getContent() { + return $this->content; + } + + public function setIconIcon($icon_icon) { + $this->iconIcon = $icon_icon; + return $this; + } + + public function getIconIcon() { + return $this->iconIcon; + } + + public function newView() { + $anchor_name = $this->getAnchor(); + $anchor_view = id(new PhabricatorAnchorView()) + ->setAnchorName($anchor_name); + + $content = $this->content; + + return array( + $anchor_view, + $content, + ); + } + + +} diff --git a/src/applications/conduit/method/ConduitAPIMethod.php b/src/applications/conduit/method/ConduitAPIMethod.php index 090235500d..1c6ae67be3 100644 --- a/src/applications/conduit/method/ConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitAPIMethod.php @@ -40,8 +40,33 @@ abstract class ConduitAPIMethod */ abstract public function getMethodDescription(); - public function getMethodDocumentation() { - return null; + final public function getDocumentationPages(PhabricatorUser $viewer) { + $pages = $this->newDocumentationPages($viewer); + return $pages; + } + + protected function newDocumentationPages(PhabricatorUser $viewer) { + return array(); + } + + final protected function newDocumentationPage(PhabricatorUser $viewer) { + return id(new ConduitAPIDocumentationPage()) + ->setIconIcon('fa-chevron-right'); + } + + final protected function newDocumentationBoxPage( + PhabricatorUser $viewer, + $title, + $content) { + + $box_view = id(new PHUIObjectBoxView()) + ->setHeaderText($title) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->setTable($content); + + return $this->newDocumentationPage($viewer) + ->setName($title) + ->setContent($box_view); } abstract protected function defineParamTypes(); diff --git a/src/applications/conduit/method/ConduitConnectConduitAPIMethod.php b/src/applications/conduit/method/ConduitConnectConduitAPIMethod.php index 8119deb860..9b02dca353 100644 --- a/src/applications/conduit/method/ConduitConnectConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitConnectConduitAPIMethod.php @@ -80,7 +80,7 @@ final class ConduitConnectConduitAPIMethod extends ConduitAPIMethod { $ex->setErrorDescription( pht( "Your '%s' client version is '%d', which is newer than the ". - "server version, '%d'. Upgrade your Phabricator install.", + "server version, '%d'. Upgrade your server.", 'arc', $client_version, $server_version)); diff --git a/src/applications/conduit/query/PhabricatorConduitLogQuery.php b/src/applications/conduit/query/PhabricatorConduitLogQuery.php index 23a5b46786..19cb31a812 100644 --- a/src/applications/conduit/query/PhabricatorConduitLogQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitLogQuery.php @@ -40,10 +40,6 @@ final class PhabricatorConduitLogQuery return new PhabricatorConduitMethodCallLog(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php index 9d244401e6..06ba536dfe 100644 --- a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php +++ b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php @@ -88,7 +88,7 @@ final class PhabricatorConduitSearchEngine pht('Deprecated Methods'), pht( 'Show old methods which will be deleted in a future '. - 'version of Phabricator.')), + 'version of this software.')), $is_deprecated)); } diff --git a/src/applications/conduit/query/PhabricatorConduitTokenQuery.php b/src/applications/conduit/query/PhabricatorConduitTokenQuery.php index ef35b006a7..384efccadf 100644 --- a/src/applications/conduit/query/PhabricatorConduitTokenQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitTokenQuery.php @@ -38,10 +38,6 @@ final class PhabricatorConduitTokenQuery return new PhabricatorConduitToken(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/config/application/PhabricatorConfigApplication.php b/src/applications/config/application/PhabricatorConfigApplication.php index e7a1b7eb1b..22731ac822 100644 --- a/src/applications/config/application/PhabricatorConfigApplication.php +++ b/src/applications/config/application/PhabricatorConfigApplication.php @@ -31,7 +31,7 @@ final class PhabricatorConfigApplication extends PhabricatorApplication { } public function getShortDescription() { - return pht('Configure Phabricator'); + return pht('Configure %s', PlatformSymbols::getPlatformServerName()); } public function getRoutes() { diff --git a/src/applications/config/check/PhabricatorBaseURISetupCheck.php b/src/applications/config/check/PhabricatorBaseURISetupCheck.php index bf3ac0b071..92e46641d7 100644 --- a/src/applications/config/check/PhabricatorBaseURISetupCheck.php +++ b/src/applications/config/check/PhabricatorBaseURISetupCheck.php @@ -17,7 +17,7 @@ final class PhabricatorBaseURISetupCheck extends PhabricatorSetupCheck { $message = pht( 'This request did not include a "Host" header. This may mean that '. 'your webserver (like nginx or apache) is misconfigured so the '. - '"Host" header is not making it to Phabricator, or that you are '. + '"Host" header is not making it to this software, or that you are '. 'making a raw request without a "Host" header using a tool or '. 'library.'. "\n\n". @@ -38,9 +38,9 @@ final class PhabricatorBaseURISetupCheck extends PhabricatorSetupCheck { 'is required for some browsers to be able to set cookies.'. "\n\n". 'This may mean the base URI is configured incorrectly. You must '. - 'serve Phabricator from a base URI with a dot (like '. - '"https://phabricator.mycompany.com"), not a bare domain '. - '(like "https://phabricator/"). If you are trying to use a bare '. + 'serve this software from a base URI with a dot (like '. + '"https://devtools.example.com"), not a bare domain '. + '(like "https://devtools/"). If you are trying to use a bare '. 'domain, change your configuration to use a full domain with a dot '. 'in it instead.'. "\n\n". @@ -76,7 +76,7 @@ final class PhabricatorBaseURISetupCheck extends PhabricatorSetupCheck { 'will not work properly until you configure it.'. "\n\n". 'You should set the base URI to the URI you will use to access '. - 'Phabricator, like "http://phabricator.example.com/".'. + 'this server, like "http://devtools.example.com/".'. "\n\n". 'Include the protocol (http or https), domain name, and port number if '. 'you are using a port other than 80 (http) or 443 (https).'. @@ -96,7 +96,7 @@ final class PhabricatorBaseURISetupCheck extends PhabricatorSetupCheck { ->setMessage($message) ->addCommand( hsprintf( - 'phabricator/ $ %s', + '$ %s', csprintf( './bin/config set phabricator.base-uri %s', $base_uri_guess))); diff --git a/src/applications/config/check/PhabricatorBinariesSetupCheck.php b/src/applications/config/check/PhabricatorBinariesSetupCheck.php index cf3c87f480..b87282fcc7 100644 --- a/src/applications/config/check/PhabricatorBinariesSetupCheck.php +++ b/src/applications/config/check/PhabricatorBinariesSetupCheck.php @@ -15,7 +15,7 @@ final class PhabricatorBinariesSetupCheck extends PhabricatorSetupCheck { if (!Filesystem::binaryExists($bin_name)) { $message = pht( - "Without '%s', Phabricator can not test for the availability ". + "Without '%s', this software can not test for the availability ". "of other binaries.", $bin_name); $this->raiseWarning($bin_name, $message); @@ -27,7 +27,7 @@ final class PhabricatorBinariesSetupCheck extends PhabricatorSetupCheck { if (!Filesystem::binaryExists('diff')) { $message = pht( - "Without '%s', Phabricator will not be able to generate or render ". + "Without '%s', this software will not be able to generate or render ". "diffs in multiple applications.", 'diff'); $this->raiseWarning('diff', $message); @@ -120,17 +120,11 @@ final class PhabricatorBinariesSetupCheck extends PhabricatorSetupCheck { break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $bad_versions = array( - // We need 1.9 for HTTP cloning, see T3046. - '< 1.9' => pht( - 'The minimum supported version of Mercurial is 1.9, which was '. - 'released in 2011.'), - '= 2.1' => pht( - 'This version of Mercurial returns a bad exit code '. - 'after a successful pull.'), - '= 2.2' => pht( - 'This version of Mercurial has a significant memory leak, fixed '. - 'in 2.2.1. Pushing fails with this version as well; see %s.', - 'T3046#54922'), + // We need 2.4 for utilizing `{p1node}` keyword in templates, see + // D21679 and D21681. + '< 2.4' => pht( + 'The minimum supported version of Mercurial is 2.4, which was '. + 'released in 2012.'), ); break; } @@ -168,7 +162,7 @@ final class PhabricatorBinariesSetupCheck extends PhabricatorSetupCheck { $preamble = pht( "The '%s' binary could not be found. Set the webserver's %s ". "environmental variable to include the directory where it resides, or ". - "add that directory to '%s' in the Phabricator configuration.", + "add that directory to '%s' in configuration.", $bin, 'PATH', 'environment.append-paths'); @@ -176,10 +170,9 @@ final class PhabricatorBinariesSetupCheck extends PhabricatorSetupCheck { $preamble = pht( "The '%s' binary could not be found. Symlink it into '%s', or set the ". "webserver's %s environmental variable to include the directory where ". - "it resides, or add that directory to '%s' in the Phabricator ". - "configuration.", + "it resides, or add that directory to '%s' in configuration.", $bin, - 'phabricator/support/bin/', + 'support/bin/', 'PATH', 'environment.append-paths'); } @@ -200,19 +193,19 @@ final class PhabricatorBinariesSetupCheck extends PhabricatorSetupCheck { $message = pht( 'Unable to determine the version number of "%s". Usually, this means '. - 'the program changed its version format string recently and Phabricator '. - 'does not know how to parse the new one yet, but might indicate that '. - 'you have a very old (or broken) binary.'. + 'the program changed its version format string recently and this '. + 'software does not know how to parse the new one yet, but might '. + 'indicate that you have a very old (or broken) binary.'. "\n\n". 'Because we can not determine the version number, checks against '. 'minimum and known-bad versions will be skipped, so we might fail '. 'to detect an incompatible binary.'. "\n\n". - 'You may be able to resolve this issue by updating Phabricator, since '. - 'a newer version of Phabricator is likely to be able to parse the '. + 'You may be able to resolve this issue by updating this server, since '. + 'a newer version of the software is likely to be able to parse the '. 'newer version string.'. "\n\n". - 'If updating Phabricator does not fix this, you can report the issue '. + 'If updating the software does not fix this, you can report the issue '. 'to the upstream so we can adjust the parser.'. "\n\n". 'If you are confident you have a recent version of "%s" installed and '. diff --git a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php index 7ef44dc384..608bac675e 100644 --- a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php +++ b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php @@ -26,14 +26,13 @@ final class PhabricatorDaemonsSetupCheck extends PhabricatorSetupCheck { $doc_href = PhabricatorEnv::getDoclink('Managing Daemons with phd'); $summary = pht( - 'You must start the Phabricator daemons to send email, rebuild '. - 'search indexes, and do other background processing.'); + 'You must start the daemons to send email, rebuild search indexes, '. + 'and do other background processing.'); $message = pht( - 'The Phabricator daemons are not running, so Phabricator will not '. - 'be able to perform background processing (including sending email, '. - 'rebuilding search indexes, importing commits, cleaning up old data, '. - 'and running builds).'. + 'The daemons are not running, background processing (including '. + 'sending email, rebuilding search indexes, importing commits, '. + 'cleaning up old data, and running builds) can not be performed.'. "\n\n". 'Use %s to start daemons. See %s for more information.', phutil_tag('tt', array(), 'bin/phd start'), @@ -47,10 +46,10 @@ final class PhabricatorDaemonsSetupCheck extends PhabricatorSetupCheck { $this->newIssue('daemons.not-running') ->setShortName(pht('Daemons Not Running')) - ->setName(pht('Phabricator Daemons Are Not Running')) + ->setName(pht('Daemons Are Not Running')) ->setSummary($summary) ->setMessage($message) - ->addCommand('phabricator/ $ ./bin/phd start'); + ->addCommand('$ ./bin/phd start'); } $expect_user = PhabricatorEnv::getEnvConfig('phd.user'); @@ -91,7 +90,7 @@ final class PhabricatorDaemonsSetupCheck extends PhabricatorSetupCheck { ->setSummary($summary) ->setMessage($message) ->addPhabricatorConfig('phd.user') - ->addCommand('phabricator/ $ ./bin/phd restart'); + ->addCommand('$ ./bin/phd restart'); break; } diff --git a/src/applications/config/check/PhabricatorDatabaseSetupCheck.php b/src/applications/config/check/PhabricatorDatabaseSetupCheck.php index e48e22196e..a5989f37b3 100644 --- a/src/applications/config/check/PhabricatorDatabaseSetupCheck.php +++ b/src/applications/config/check/PhabricatorDatabaseSetupCheck.php @@ -35,11 +35,11 @@ final class PhabricatorDatabaseSetupCheck extends PhabricatorSetupCheck { ->addPhabricatorConfig('mysql.port') ->addCommand( hsprintf( - 'phabricator/ $ ./bin/config set mysql.host %s', + '$ ./bin/config set mysql.host %s', $host)) ->addCommand( hsprintf( - 'phabricator/ $ ./bin/config set mysql.port %s', + '$ ./bin/config set mysql.port %s', $port)); } @@ -134,7 +134,7 @@ final class PhabricatorDatabaseSetupCheck extends PhabricatorSetupCheck { ->setName(pht('Setup MySQL Schema')) ->setMessage($message) ->setIsFatal(true) - ->addCommand(hsprintf('phabricator/ $ ./bin/storage upgrade')); + ->addCommand(hsprintf('$ ./bin/storage upgrade')); return true; } @@ -160,7 +160,7 @@ final class PhabricatorDatabaseSetupCheck extends PhabricatorSetupCheck { ->setIsFatal(true) ->setMessage($message) ->addCommand( - hsprintf('phabricator/ $ ./bin/storage upgrade')); + hsprintf('$ ./bin/storage upgrade')); return true; } @@ -177,7 +177,7 @@ final class PhabricatorDatabaseSetupCheck extends PhabricatorSetupCheck { 'Database host "%s" is configured as a master, but is replicating '. 'another host. This is dangerous and can mangle or destroy data. '. 'Only replicas should be replicating. Stop replication on the '. - 'host or reconfigure Phabricator.', + 'host or adjust configuration.', $ref->getRefKey()); $this->newIssue('db.master.replicating') diff --git a/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php b/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php index cd29ecdc78..8466c5a6c6 100644 --- a/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php +++ b/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php @@ -34,8 +34,8 @@ final class PhabricatorElasticsearchSetupCheck extends PhabricatorSetupCheck { } catch (Exception $ex) { $summary = pht('Elasticsearch is not reachable as configured.'); $message = pht( - 'Elasticsearch is configured (with the %s setting) but Phabricator'. - ' encountered an exception when trying to test the index.'. + 'Elasticsearch is configured (with the %s setting) but an '. + 'exception was encountered when trying to test the index.'. "\n\n". '%s', phutil_tag('tt', array(), 'cluster.search'), @@ -69,7 +69,7 @@ final class PhabricatorElasticsearchSetupCheck extends PhabricatorSetupCheck { 'Elasticsearch index exists but needs correction.'); $message = pht( - 'Either the Phabricator schema for Elasticsearch has changed '. + 'Either the schema for Elasticsearch has changed '. 'or Elasticsearch created the index automatically. '. 'Use the following command to rebuild the index.'); diff --git a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php index 1de39f3468..ae14a0ab0a 100644 --- a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php +++ b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php @@ -38,9 +38,9 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { } else { $summary = pht('This option is not recognized. It may be misspelled.'); $message = pht( - "The configuration option '%s' is not recognized. It may be ". - "misspelled, or it might have existed in an older version of ". - "Phabricator. It has no effect, and should be corrected or deleted.", + 'The configuration option "%s" is not recognized. It may be '. + 'misspelled, or it might have existed in an older version of '. + 'the software. It has no effect, and should be corrected or deleted.', $key); $short = pht('Unknown Config'); $name = pht('Unknown Configuration Option "%s"', $key); @@ -76,7 +76,7 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { $issue->setMessage($message); if ($found_local) { - $command = csprintf('phabricator/ $ ./bin/config delete %s', $key); + $command = csprintf('$ ./bin/config delete %s', $key); $issue->addCommand($command); } @@ -155,7 +155,7 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { 'details about this setup issue, see %s.'. "\n\n". 'This database value is currently respected, but a future version '. - 'of Phabricator will stop respecting database values for locked '. + 'of the software will stop respecting database values for locked '. 'configuration options.', $key, $set_command, @@ -167,7 +167,7 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { ), $doc_name)); $command = csprintf( - 'phabricator/ $ ./bin/config delete --database %R', + '$ ./bin/config delete --database %R', $key); $this->newIssue('config.locked.'.$key) @@ -191,7 +191,7 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { pht( 'The "feed.http-hooks" option is deprecated in favor of '. 'Webhooks. This option will be removed in a future version '. - 'of Phabricator.'. + 'of the software.'. "\n\n". 'You can configure Webhooks in Herald.'. "\n\n". @@ -283,8 +283,7 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { 'Reply handlers can no longer be overridden with configuration.'); $monospace_reason = pht( - 'Phabricator no longer supports global customization of monospaced '. - 'fonts.'); + 'Global customization of monospaced fonts is no longer supported.'); $public_mail_reason = pht( 'Inbound mail addresses are now configured for each application '. @@ -346,7 +345,7 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { 'auth.sshkeys.enabled' => pht( 'SSH keys are now actually useful, so they are always enabled.'), 'differential.anonymous-access' => pht( - 'Phabricator now has meaningful global access controls. See `%s`.', + 'Global access controls now exist, see `%s`.', 'policy.allow-public'), 'celerity.resource-path' => pht( 'An alternate resource map is no longer supported. Instead, use '. @@ -356,7 +355,7 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { 'auth.sessions.conduit' => $session_reason, 'auth.sessions.web' => $session_reason, 'tokenizer.ondemand' => pht( - 'Phabricator now manages typeahead strategies automatically.'), + 'Typeahead strategies are now managed automatically.'), 'differential.revision-custom-detail-renderer' => pht( 'Obsolete; use standard rendering events instead.'), 'differential.show-host-field' => $differential_field_reason, @@ -383,16 +382,15 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { 'pool size with `%s`.', 'phd.taskmasters'), 'storage.engine-selector' => pht( - 'Phabricator now automatically discovers available storage engines '. - 'at runtime.'), + 'Storage engines are now discovered automatically at runtime.'), 'storage.upload-size-limit' => pht( - 'Phabricator now supports arbitrarily large files. Consult the '. + 'Arbitrarily large files are now supported. Consult the '. 'documentation for configuration details.'), 'security.allow-outbound-http' => pht( 'This option has been replaced with the more granular option `%s`.', 'security.outbound-blacklist'), 'metamta.reply.show-hints' => pht( - 'Phabricator no longer shows reply hints in mail.'), + 'Reply hints are no longer shown in mail.'), 'metamta.differential.reply-handler-domain' => $reply_domain_reason, 'metamta.diffusion.reply-handler-domain' => $reply_domain_reason, @@ -406,15 +404,15 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { 'metamta.package.reply-handler' => $reply_handler_reason, 'metamta.precedence-bulk' => pht( - 'Phabricator now always sends transaction mail with '. - '"Precedence: bulk" to improve deliverability.'), + 'Transaction mail is now always sent with "Precedence: bulk" to '. + 'improve deliverability.'), 'style.monospace' => $monospace_reason, 'style.monospace.windows' => $monospace_reason, 'search.engine-selector' => pht( - 'Phabricator now automatically discovers available search engines '. - 'at runtime.'), + 'Available search engines are now automatically discovered at '. + 'runtime.'), 'metamta.files.public-create-email' => $public_mail_reason, 'metamta.maniphest.public-create-email' => $public_mail_reason, @@ -469,12 +467,12 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { 'maniphest.priorities.needs-triage' => $dashboard_reason, 'mysql.implementation' => pht( - 'Phabricator now automatically selects the best available '. - 'MySQL implementation.'), + 'The best available MYSQL implementation is now selected '. + 'automatically.'), 'mysql.configuration-provider' => pht( - 'Phabricator now has application-level management of partitioning '. - 'and replicas.'), + 'Partitioning and replication are now managed in primary '. + 'configuration.'), 'search.elastic.host' => $elastic_reason, 'search.elastic.namespace' => $elastic_reason, @@ -541,7 +539,7 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { 'Whitespace rendering is now handled automatically.'), 'phd.pid-directory' => pht( - 'Phabricator daemons no longer use PID files.'), + 'Daemons no longer use PID files.'), 'phd.trace' => $phd_reason, 'phd.verbose' => $phd_reason, diff --git a/src/applications/config/check/PhabricatorFileinfoSetupCheck.php b/src/applications/config/check/PhabricatorFileinfoSetupCheck.php index 543fc4fb7c..9896daac2c 100644 --- a/src/applications/config/check/PhabricatorFileinfoSetupCheck.php +++ b/src/applications/config/check/PhabricatorFileinfoSetupCheck.php @@ -10,7 +10,7 @@ final class PhabricatorFileinfoSetupCheck extends PhabricatorSetupCheck { if (!extension_loaded('fileinfo')) { $message = pht( "The '%s' extension is not installed. Without '%s', ". - "support, Phabricator may not be able to determine the MIME types ". + "support, this software may not be able to determine the MIME types ". "of uploaded files.", 'fileinfo', 'fileinfo'); diff --git a/src/applications/config/check/PhabricatorGDSetupCheck.php b/src/applications/config/check/PhabricatorGDSetupCheck.php index 7ada204801..0aa23035c7 100644 --- a/src/applications/config/check/PhabricatorGDSetupCheck.php +++ b/src/applications/config/check/PhabricatorGDSetupCheck.php @@ -10,7 +10,7 @@ final class PhabricatorGDSetupCheck extends PhabricatorSetupCheck { if (!extension_loaded('gd')) { $message = pht( "The '%s' extension is not installed. Without '%s', support, ". - "Phabricator will not be able to process or resize images ". + "this server will not be able to process or resize images ". "(for example, to generate thumbnails). Install or enable '%s'.", 'gd', 'gd', @@ -41,7 +41,7 @@ final class PhabricatorGDSetupCheck extends PhabricatorSetupCheck { $message = pht( "The '%s' extension has support for only some image types. ". - "Phabricator will be unable to process images of the missing ". + "This server will be unable to process images of the missing ". "types until you build '%s' with support for them. ". "Supported types: %s. Missing types: %s.", 'gd', diff --git a/src/applications/config/check/PhabricatorMailSetupCheck.php b/src/applications/config/check/PhabricatorMailSetupCheck.php index c89e0036a2..0b1374809c 100644 --- a/src/applications/config/check/PhabricatorMailSetupCheck.php +++ b/src/applications/config/check/PhabricatorMailSetupCheck.php @@ -12,9 +12,9 @@ final class PhabricatorMailSetupCheck extends PhabricatorSetupCheck { } $message = pht( - 'You haven\'t configured mailers yet, so Phabricator won\'t be able '. + 'You haven\'t configured mailers yet, so this server won\'t be able '. 'to send outbound mail or receive inbound mail. See the '. - 'configuration setting cluster.mailers for details.'); + 'configuration setting "cluster.mailers" for details.'); $this->newIssue('cluster.mailers') ->setName(pht('Mailers Not Configured')) diff --git a/src/applications/config/check/PhabricatorManualActivitySetupCheck.php b/src/applications/config/check/PhabricatorManualActivitySetupCheck.php index 04457cb12a..660792ca53 100644 --- a/src/applications/config/check/PhabricatorManualActivitySetupCheck.php +++ b/src/applications/config/check/PhabricatorManualActivitySetupCheck.php @@ -42,7 +42,7 @@ final class PhabricatorManualActivitySetupCheck 'function correctly.'); $message[] = pht( - 'You can rebuild the search index while Phabricator is running.'); + 'You can rebuild the search index while the server is running.'); $message[] = pht( 'To rebuild the index, run this command:'); @@ -51,7 +51,7 @@ final class PhabricatorManualActivitySetupCheck 'pre', array(), (string)csprintf( - 'phabricator/ $ ./bin/search index --all --force --background')); + '$ ./bin/search index --all --force --background')); $message[] = pht( 'You can find more information about rebuilding the search '. @@ -71,7 +71,7 @@ final class PhabricatorManualActivitySetupCheck $message[] = phutil_tag( 'pre', array(), - 'phabricator/ $ ./bin/config done reindex'); + '$ ./bin/config done reindex'); $activity_message = phutil_implode_html("\n\n", $message); @@ -84,27 +84,34 @@ final class PhabricatorManualActivitySetupCheck private function raiseRebuildIdentitiesIssue() { $activity_name = pht('Rebuild Repository Identities'); $activity_summary = pht( - 'The mapping from VCS users to Phabricator users has changed '. - 'and must be rebuilt.'); + 'The mapping from VCS users to %s users has changed '. + 'and must be rebuilt.', + PlatformSymbols::getPlatformServerName()); $message = array(); $message[] = pht( - 'The way Phabricator attributes VCS activity to Phabricator users '. - 'has changed. There is a new indirection layer between the strings '. - 'that appear as VCS authors and committers (such as "John Developer '. - '") and the Phabricator user that gets associated '. - 'with VCS commits. This is to support situations where users '. - 'are incorrectly associated with commits by Phabricator making bad '. - 'guesses about the identity of the corresponding Phabricator user. '. + 'The way VCS activity is attributed %s user accounts has changed.', + PlatformSymbols::getPlatformServerName()); + + $message[] = pht( + 'There is a new indirection layer between the strings that appear as '. + 'VCS authors and committers (such as "John Developer '. + '") and the user account that gets associated '. + 'with VCS commits.'); + + $message[] = pht( + 'This change supports situations where users are incorrectly '. + 'associated with commits because the software makes a bad guess '. + 'about how the VCS string maps to a user account. '. 'This also helps with situations where existing repositories are '. 'imported without having created accounts for all the committers to '. 'that repository. Until you rebuild these repository identities, you '. - 'are likely to encounter problems with future Phabricator features '. - 'which will rely on the existence of these identities.'); + 'are likely to encounter problems with features which rely on the '. + 'existence of these identities.'); $message[] = pht( - 'You can rebuild repository identities while Phabricator is running.'); + 'You can rebuild repository identities while the server is running.'); $message[] = pht( 'To rebuild identities, run this command:'); @@ -113,8 +120,7 @@ final class PhabricatorManualActivitySetupCheck 'pre', array(), (string)csprintf( - 'phabricator/ $ '. - './bin/repository rebuild-identities --all-repositories')); + '$ ./bin/repository rebuild-identities --all-repositories')); $message[] = pht( 'You can find more information about this new identity mapping '. @@ -134,7 +140,7 @@ final class PhabricatorManualActivitySetupCheck $message[] = phutil_tag( 'pre', array(), - 'phabricator/ $ ./bin/config done identities'); + '$ ./bin/config done identities'); $activity_message = phutil_implode_html("\n\n", $message); diff --git a/src/applications/config/check/PhabricatorMySQLSetupCheck.php b/src/applications/config/check/PhabricatorMySQLSetupCheck.php index de9a9e8b54..c5b71b1cda 100644 --- a/src/applications/config/check/PhabricatorMySQLSetupCheck.php +++ b/src/applications/config/check/PhabricatorMySQLSetupCheck.php @@ -66,7 +66,7 @@ final class PhabricatorMySQLSetupCheck extends PhabricatorSetupCheck { 'fit into the column), or security concerns (for example, by '. 'truncating keys or credentials).'. "\n\n". - 'Phabricator is developed and tested in "STRICT_ALL_TABLES" mode so '. + 'This software is developed and tested in "STRICT_ALL_TABLES" mode so '. 'you should normally never encounter these situations, but may run '. 'into them if you interact with the database directly, run '. 'third-party code, develop extensions, or just encounter a bug in '. @@ -88,7 +88,7 @@ final class PhabricatorMySQLSetupCheck extends PhabricatorSetupCheck { 'they may not work in strict mode.'. "\n\n". 'If you can not or do not want to enable "STRICT_ALL_TABLES", you '. - 'can safely ignore this warning. Phabricator will work correctly '. + 'can safely ignore this warning. This software will work correctly '. 'with this mode enabled or disabled.', $host_name, phutil_tag('pre', array(), 'sql_mode=STRICT_ALL_TABLES')); @@ -151,7 +151,7 @@ final class PhabricatorMySQLSetupCheck extends PhabricatorSetupCheck { $message = pht( "Database host \"%s\" is using the builtin stopword file for ". - "building search indexes. This can make Phabricator's search ". + "building search indexes. This can make the search ". "feature less useful.\n\n". "Stopwords are common words which are not indexed and thus can not ". "be searched for. The default stopword file has about 500 words, ". @@ -273,11 +273,11 @@ final class PhabricatorMySQLSetupCheck extends PhabricatorSetupCheck { "There are no hard-and-fast rules to setting an appropriate value, ". "but a reasonable starting point for a standard install is something ". "like 40%% of the total memory on the machine. For example, if you ". - "have 4GB of RAM on the machine you have installed Phabricator on, ". + "have 4GB of RAM on the machine you have installed this software on, ". "you might set this value to %s.\n\n". "You can read more about this option in the MySQL documentation to ". "help you make a decision about how to configure it for your use ". - "case. There are no concerns specific to Phabricator which make it ". + "case. There are no concerns specific to this software which make it ". "different from normal workloads with respect to this setting.\n\n". "To adjust the setting, add something like this to your %s file (in ". "the %s section), replacing %s with an appropriate value for your ". diff --git a/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php b/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php index bee2bc91b8..09b96d05cf 100644 --- a/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php +++ b/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php @@ -21,8 +21,8 @@ final class PhabricatorPHPConfigSetupCheck extends PhabricatorSetupCheck { 'REMOTE_ADDR is no longer empty.'); $message = pht( - 'No REMOTE_ADDR is available, so Phabricator cannot determine the '. - 'origin address for requests. This will prevent Phabricator from '. + 'No REMOTE_ADDR is available, so this server cannot determine the '. + 'origin address for requests. This will prevent the software from '. 'performing important security checks. This most often means you '. 'have a mistake in your preamble script. Consult the documentation '. '(%s) and double-check that the script is written correctly.', @@ -76,8 +76,8 @@ final class PhabricatorPHPConfigSetupCheck extends PhabricatorSetupCheck { 'with the database. You should install the newer "mysqli" extension '. 'to improve behaviors (like error handling and query timeouts).'. "\n\n". - 'Phabricator will work with the older extension, but upgrading to the '. - 'newer extension is recommended.'. + 'This software will work with the older extension, but upgrading to '. + 'the newer extension is recommended.'. "\n\n". 'You may be able to install the extension with a command like: %s', @@ -96,10 +96,10 @@ final class PhabricatorPHPConfigSetupCheck extends PhabricatorSetupCheck { $message = pht( 'PHP is currently using the older MySQL external driver instead of '. 'the newer MySQL native driver. The older driver lacks options and '. - 'features (like support for query timeouts) which allow Phabricator '. + 'features (like support for query timeouts) which allow this server '. 'to interact better with the database.'. "\n\n". - 'Phabricator will work with the older driver, but upgrading to the '. + 'This software will work with the older driver, but upgrading to the '. 'native driver is recommended.'. "\n\n". 'You may be able to install the native driver with a command like: %s', @@ -134,7 +134,7 @@ final class PhabricatorPHPConfigSetupCheck extends PhabricatorSetupCheck { 'and the client will comply.'. "\n\n". 'Although it is normally difficult for an attacker to convince '. - 'Phabricator to connect to a malicious MySQL server, you should '. + 'this software to connect to a malicious MySQL server, you should '. 'disable this option: this capability is unnecessary and inherently '. 'dangerous.'. "\n\n". diff --git a/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php b/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php index 30c6036c8f..02c1134dc3 100644 --- a/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php +++ b/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php @@ -15,11 +15,11 @@ final class PhabricatorPHPPreflightSetupCheck extends PhabricatorSetupCheck { if (version_compare($version, 7, '>=') && version_compare($version, 7.1, '<')) { $message = pht( - 'You are running PHP version %s. Phabricator does not support PHP '. - 'versions between 7.0 and 7.1.'. + 'You are running PHP version %s. PHP versions between 7.0 and 7.1 '. + 'are not supported'. "\n\n". - 'PHP removed signal handling features that Phabricator requires in '. - 'PHP 7.0, and did not restore them until PHP 7.1.'. + 'PHP removed reqiured signal handling features in '. + 'PHP 7.0, and did not restore an equivalent mechanism until PHP 7.1.'. "\n\n". 'Upgrade to PHP 7.1 or newer (recommended) or downgrade to an older '. 'version of PHP 5 (discouraged).', @@ -31,15 +31,18 @@ final class PhabricatorPHPPreflightSetupCheck extends PhabricatorSetupCheck { ->setMessage($message) ->addLink( 'https://phurl.io/u/php7', - pht('Phabricator PHP 7 Compatibility Information')); + pht('PHP 7 Compatibility Information')); return; } + // TODO: This can be removed entirely because the minimum PHP version is + // now PHP 5.5, which does not have safe mode. + $safe_mode = ini_get('safe_mode'); if ($safe_mode) { $message = pht( - "You have '%s' enabled in your PHP configuration, but Phabricator ". + "You have '%s' enabled in your PHP configuration, but this software ". "will not run in safe mode. Safe mode has been deprecated in PHP 5.3 ". "and removed in PHP 5.4.\n\nDisable safe mode to continue.", 'safe_mode'); @@ -89,7 +92,7 @@ final class PhabricatorPHPPreflightSetupCheck extends PhabricatorSetupCheck { if ($fatal) { $message = pht( "You have '%s' enabled in your PHP configuration.\n\n". - "This option is not compatible with Phabricator. Remove ". + "This option is not compatible with this software. Remove ". "'%s' from your configuration to continue.", $disable_option, $disable_option); @@ -108,7 +111,7 @@ final class PhabricatorPHPPreflightSetupCheck extends PhabricatorSetupCheck { if ($func_overload) { $message = pht( "You have '%s' enabled in your PHP configuration.\n\n". - "This option is not compatible with Phabricator. Disable ". + "This option is not compatible with this software. Disable ". "'%s' in your PHP configuration to continue.", $overload_option, $overload_option); @@ -131,7 +134,7 @@ final class PhabricatorPHPPreflightSetupCheck extends PhabricatorSetupCheck { // rare (particularly in supported environments). $message = pht( - "Your server is configured with '%s', which prevents Phabricator ". + "Your server is configured with '%s', which prevents this software ". "from opening files it requires access to.\n\n". "Disable this setting to continue.", 'open_basedir'); diff --git a/src/applications/config/check/PhabricatorPathSetupCheck.php b/src/applications/config/check/PhabricatorPathSetupCheck.php index 75c89d332a..8a98c5ba9f 100644 --- a/src/applications/config/check/PhabricatorPathSetupCheck.php +++ b/src/applications/config/check/PhabricatorPathSetupCheck.php @@ -13,15 +13,15 @@ final class PhabricatorPathSetupCheck extends PhabricatorSetupCheck { if (!$path) { $summary = pht( - 'The environmental variable %s is empty. Phabricator will not '. + 'The environmental variable %s is empty. This server will not '. 'be able to execute some commands.', '$PATH'); $message = pht( - "The environmental variable %s is empty. Phabricator needs to execute ". + "The environmental variable %s is empty. This server needs to execute ". "some system commands, like `%s`, `%s`, `%s`, and `%s`. To execute ". "these commands, the binaries must be available in the webserver's ". - "%s. You can set additional paths in Phabricator configuration.", + "%s. You can set additional paths in configuration.", '$PATH', 'svn', 'git', @@ -120,7 +120,7 @@ final class PhabricatorPathSetupCheck extends PhabricatorSetupCheck { ->setMessage( pht( "The configured PATH includes a component which is not usable. ". - "Phabricator will be unable to find or execute binaries located ". + "This server will be unable to find or execute binaries located ". "here:". "\n\n". "%s". diff --git a/src/applications/config/check/PhabricatorPygmentSetupCheck.php b/src/applications/config/check/PhabricatorPygmentSetupCheck.php index 0ffb4ff7ad..74dfe5a324 100644 --- a/src/applications/config/check/PhabricatorPygmentSetupCheck.php +++ b/src/applications/config/check/PhabricatorPygmentSetupCheck.php @@ -42,7 +42,7 @@ final class PhabricatorPygmentSetupCheck extends PhabricatorSetupCheck { 'pygmentize'); $message = pht( - 'Phabricator has %s available in %s, but the binary '. + 'This server has %s available in %s, but the binary '. 'exited with an error code when run as %s. Check that it is '. 'installed correctly.', phutil_tag('tt', array(), 'pygmentize'), @@ -64,7 +64,7 @@ final class PhabricatorPygmentSetupCheck extends PhabricatorSetupCheck { 'to provide advanced syntax highlighting.'); $message = pht( - 'Phabricator can highlight a few languages by default, '. + 'This software can highlight a few languages by default, '. 'but installing and enabling Pygments (a third-party highlighting '. "tool) will add syntax highlighting for many more languages. \n\n". 'For instructions on installing and enabling Pygments, see the '. diff --git a/src/applications/config/check/PhabricatorRepositoriesSetupCheck.php b/src/applications/config/check/PhabricatorRepositoriesSetupCheck.php index 64073ec1ed..7d010fd954 100644 --- a/src/applications/config/check/PhabricatorRepositoriesSetupCheck.php +++ b/src/applications/config/check/PhabricatorRepositoriesSetupCheck.php @@ -45,9 +45,9 @@ final class PhabricatorRepositoriesSetupCheck extends PhabricatorSetupCheck { 'readable by the webserver.'); $message = pht( "The directory for local repositories (%s) does not exist, or is not ". - "readable by the webserver. Phabricator uses this directory to store ". - "information about repositories. If this directory does not exist, ". - "create it:\n\n". + "readable by the webserver. This software uses this directory to ". + "store information about repositories. If this directory does not ". + "exist, create it:\n\n". "%s\n". "If this directory exists, make it readable to the webserver. You ". "can also edit the configuration below to use some other directory.", diff --git a/src/applications/config/check/PhabricatorSecuritySetupCheck.php b/src/applications/config/check/PhabricatorSecuritySetupCheck.php index 0cd1b41504..e84e26a208 100644 --- a/src/applications/config/check/PhabricatorSecuritySetupCheck.php +++ b/src/applications/config/check/PhabricatorSecuritySetupCheck.php @@ -31,7 +31,7 @@ final class PhabricatorSecuritySetupCheck extends PhabricatorSetupCheck { "\n\n". 'Upgrade %s to a patched version.'. "\n\n". - 'To learn more about how this issue affects Phabricator, see %s.', + 'To learn more about how this issue affects this software, see %s.', phutil_tag('tt', array(), 'bash'), phutil_tag('tt', array(), 'bash'), phutil_tag( @@ -61,7 +61,7 @@ final class PhabricatorSecuritySetupCheck extends PhabricatorSetupCheck { 'Improve security by configuring an alternate file domain.')) ->setMessage( pht( - 'Phabricator is currently configured to serve user uploads '. + 'This software is currently configured to serve user uploads '. 'directly from the same domain as other content. This is a '. 'security risk.'. "\n\n". diff --git a/src/applications/config/check/PhabricatorStorageSetupCheck.php b/src/applications/config/check/PhabricatorStorageSetupCheck.php index cc74cce2ea..86aa1f2ebd 100644 --- a/src/applications/config/check/PhabricatorStorageSetupCheck.php +++ b/src/applications/config/check/PhabricatorStorageSetupCheck.php @@ -83,7 +83,7 @@ final class PhabricatorStorageSetupCheck extends PhabricatorSetupCheck { $message = pht( 'When you upload a file via drag-and-drop or the API, chunks must '. 'be buffered into memory before being written to permanent '. - 'storage. Phabricator needs memory available to store these '. + 'storage. This server needs memory available to store these '. 'chunks while they are uploaded, but PHP is currently configured '. 'to severely limit the available memory.'. "\n\n". @@ -97,7 +97,7 @@ final class PhabricatorStorageSetupCheck extends PhabricatorSetupCheck { "The easiest way to resolve this issue is to set %s to %s in your ". "PHP configuration, to disable the memory limit. There is ". "usually little or no value to using this option to limit ". - "Phabricator process memory.". + "process memory.". "\n\n". "You can also increase the limit or ignore this issue and accept ". "that you may encounter problems uploading large files and ". diff --git a/src/applications/config/check/PhabricatorTimezoneSetupCheck.php b/src/applications/config/check/PhabricatorTimezoneSetupCheck.php index 08f6a28d10..71036724f7 100644 --- a/src/applications/config/check/PhabricatorTimezoneSetupCheck.php +++ b/src/applications/config/check/PhabricatorTimezoneSetupCheck.php @@ -40,9 +40,10 @@ final class PhabricatorTimezoneSetupCheck extends PhabricatorSetupCheck { $message = pht( "Your configuration fails to specify a server timezone. You can either ". - "set the PHP configuration value '%s' or the Phabricator ". - "configuration value '%s' to specify one.", + "set the PHP configuration value '%s' or the %s configuration ". + "value '%s' to specify one.", 'date.timezone', + PlatformSymbols::getPlatformServerName(), 'phabricator.timezone'); $this diff --git a/src/applications/config/check/PhabricatorWebServerSetupCheck.php b/src/applications/config/check/PhabricatorWebServerSetupCheck.php index 46255629e7..cc9660325c 100644 --- a/src/applications/config/check/PhabricatorWebServerSetupCheck.php +++ b/src/applications/config/check/PhabricatorWebServerSetupCheck.php @@ -16,10 +16,10 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck { ->setSummary(pht('Pagespeed is enabled, but should be disabled.')) ->setMessage( pht( - 'Phabricator received an "X-Mod-Pagespeed" or "X-Page-Speed" '. + 'This server received an "X-Mod-Pagespeed" or "X-Page-Speed" '. 'HTTP header on this request, which indicates that you have '. 'enabled "mod_pagespeed" on this server. This module is not '. - 'compatible with Phabricator. You should disable it.')); + 'compatible with this software. You should disable the module.')); } $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); @@ -43,7 +43,7 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck { ->replaceQueryParam($expect_key, $expect_value); $self_future = id(new HTTPSFuture($base_uri)) - ->addHeader('X-Phabricator-SelfCheck', 1) + ->addHeader('X-Setup-SelfCheck', 1) ->addHeader('Accept-Encoding', 'gzip') ->setDisableContentDecoding(true) ->setHTTPBasicAuthCredentials( @@ -56,7 +56,7 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck { $gzip_compressed = gzencode($gzip_uncompressed); $gzip_future = id(new HTTPSFuture($base_uri)) - ->addHeader('X-Phabricator-SelfCheck', 1) + ->addHeader('X-Setup-SelfCheck', 1) ->addHeader('Content-Encoding', 'gzip') ->setTimeout(5) ->setData($gzip_compressed); @@ -91,9 +91,9 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck { $body = trim($body); if (preg_match('/^t2/', $body)) { $message = pht( - 'Phabricator appears to be installed on a very small EC2 instance '. + 'This software appears to be installed on a very small EC2 instance '. '(of class "%s") with burstable CPU. This is strongly discouraged. '. - 'Phabricator regularly needs CPU, and these instances are often '. + 'This software regularly needs CPU, and these instances are often '. 'choked to death by CPU throttling. Use an instance with a normal '. 'CPU instead.', $body); @@ -102,7 +102,7 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck { ->setName(pht('Installed on Burstable CPU Instance')) ->setSummary( pht( - 'Do not install Phabricator on an instance class with '. + 'Do not install this software on an instance class with '. 'burstable CPU.')) ->setMessage($message); } @@ -121,7 +121,7 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck { if (BaseHTTPFuture::getHeader($headers, 'Content-Encoding') != 'gzip') { $message = pht( - 'Phabricator sent itself a request with "Accept-Encoding: gzip", '. + 'This software sent itself a request with "Accept-Encoding: gzip", '. 'but received an uncompressed response.'. "\n\n". 'This may indicate that your webserver is not configured to '. @@ -163,8 +163,8 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck { ->truncateString($body); $message = pht( - 'Phabricator sent itself a test request with the '. - '"X-Phabricator-SelfCheck" header and expected to get a valid JSON '. + 'This software sent itself a test request with the '. + '"X-Setup-SelfCheck" header and expected to get a valid JSON '. 'response back. Instead, the response begins:'. "\n\n". '%s'. @@ -173,9 +173,9 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck { phutil_tag('pre', array(), $short)); } else { $message = pht( - 'Phabricator sent itself a test request and expected to get a bare '. - 'JSON response back. It received a JSON response, but the response '. - 'had extra whitespace at the beginning or end.'. + 'This software sent itself a test request and expected to get a '. + 'bare JSON response back. It received a JSON response, but the '. + 'response had extra whitespace at the beginning or end.'. "\n\n". 'This usually means you have edited a file and left whitespace '. 'characters before the opening %s tag, or after a closing %s tag. '. @@ -199,11 +199,11 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck { $actual_pass = idx($structure, 'pass'); if (($expect_user != $actual_user) || ($actual_pass != $expect_pass)) { $message = pht( - 'Phabricator sent itself a test request with an "Authorization" HTTP '. - 'header, and expected those credentials to be transmitted. However, '. - 'they were absent or incorrect when received. Phabricator sent '. - 'username "%s" with password "%s"; received username "%s" and '. - 'password "%s".'. + 'This software sent itself a test request with an "Authorization" '. + 'HTTP header, and expected those credentials to be transmitted. '. + 'However, they were absent or incorrect when received. This '. + 'software sent username "%s" with password "%s"; received '. + 'username "%s" and password "%s".'. "\n\n". 'Your webserver may not be configured to forward HTTP basic '. 'authentication. If you plan to use basic authentication (for '. @@ -222,13 +222,13 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck { $actual_path = idx($structure, 'path'); if ($expect_path != $actual_path) { $message = pht( - 'Phabricator sent itself a test request with an unusual path, to '. + 'This software sent itself a test request with an unusual path, to '. 'test if your webserver is rewriting paths correctly. The path was '. 'not transmitted correctly.'. "\n\n". - 'Phabricator sent a request to path "%s", and expected the webserver '. - 'to decode and rewrite that path so that it received a request for '. - '"%s". However, it received a request for "%s" instead.'. + 'This software sent a request to path "%s", and expected the '. + 'webserver to decode and rewrite that path so that it received a '. + 'request for "%s". However, it received a request for "%s" instead.'. "\n\n". 'Verify that your rewrite rules are configured correctly, following '. 'the instructions in the documentation. If path encoding is not '. @@ -258,12 +258,12 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck { if (($expect_key !== $actual_key) || ($expect_value !== $actual_value)) { $message = pht( - 'Phabricator sent itself a test request with an HTTP GET parameter, '. + 'This software sent itself a test request with an HTTP GET parameter, '. 'but the parameter was not transmitted. Sent "%s" with value "%s", '. 'got "%s" with value "%s".'. "\n\n". 'Your webserver is configured incorrectly and large parts of '. - 'Phabricator will not work until this issue is corrected.'. + 'this software will not work until this issue is corrected.'. "\n\n". '(This problem can be caused by a missing "QSA" in your RewriteRule.)', $expect_key, @@ -320,7 +320,7 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck { $message = array(); $message[] = pht( - 'Phabricator sent itself a test request that was compressed with '. + 'This software sent itself a test request that was compressed with '. '"Content-Encoding: gzip", but received different bytes than it '. 'sent.'); @@ -342,17 +342,17 @@ final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck { 'compressed requests.'); $message[] = pht( - 'The request body Phabricator sent began:'); + 'The request body that was sent began:'); $message[] = $this->snipBytes($compressed); $message[] = pht( - 'The request body Phabricator received began:'); + 'The request body that was received began:'); $message[] = $this->snipBytes($raw_body); } $message[] = pht( 'Identify the component in your webserver configuration which is '. - 'decompressing or mangling requests and disable it. Phabricator '. + 'decompressing or mangling requests and disable it. This software '. 'will not work properly until you do.'); $message = phutil_implode_html("\n\n", $message); diff --git a/src/applications/config/controller/PhabricatorConfigConsoleController.php b/src/applications/config/controller/PhabricatorConfigConsoleController.php index b8eae9c213..bd23d6dde5 100644 --- a/src/applications/config/controller/PhabricatorConfigConsoleController.php +++ b/src/applications/config/controller/PhabricatorConfigConsoleController.php @@ -56,7 +56,7 @@ final class PhabricatorConfigConsoleController ->setBorder(true); $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Phabricator Configuation')) + ->setHeaderText(pht('Configuration')) ->setBackground(PHUIObjectBoxView::WHITE_CONFIG) ->setObjectList($menu); @@ -72,7 +72,7 @@ final class PhabricatorConfigConsoleController ->setFooter($launcher_view); return $this->newPage() - ->setTitle(pht('Phabricator Configuation')) + ->setTitle(pht('Configuration')) ->setCrumbs($crumbs) ->appendChild($view); } @@ -131,7 +131,7 @@ final class PhabricatorConfigConsoleController )); return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Phabricator Version Information')) + ->setHeaderText(pht('Version Information')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($table_view); } diff --git a/src/applications/config/controller/services/PhabricatorConfigClusterDatabasesController.php b/src/applications/config/controller/services/PhabricatorConfigClusterDatabasesController.php index ca22698212..096495cc84 100644 --- a/src/applications/config/controller/services/PhabricatorConfigClusterDatabasesController.php +++ b/src/applications/config/controller/services/PhabricatorConfigClusterDatabasesController.php @@ -207,7 +207,7 @@ final class PhabricatorConfigClusterDatabasesController $table = id(new AphrontTableView($rows)) ->setNoDataString( - pht('Phabricator is not configured in cluster mode.')) + pht('This server is not configured in cluster mode.')) ->setHeaders( array( null, diff --git a/src/applications/config/controller/settings/PhabricatorConfigEditController.php b/src/applications/config/controller/settings/PhabricatorConfigEditController.php index 2cb217db69..38c592efca 100644 --- a/src/applications/config/controller/settings/PhabricatorConfigEditController.php +++ b/src/applications/config/controller/settings/PhabricatorConfigEditController.php @@ -18,7 +18,7 @@ final class PhabricatorConfigEditController } else { $desc = pht( 'This configuration option is unknown. It may be misspelled, '. - 'or have existed in a previous version of Phabricator.'); + 'or have existed in a previous version of the software.'); } // This may be a dead config entry, which existed in the past but no diff --git a/src/applications/config/custom/PhabricatorCustomLogoConfigType.php b/src/applications/config/custom/PhabricatorCustomLogoConfigType.php index ff29050602..cc20768119 100644 --- a/src/applications/config/custom/PhabricatorCustomLogoConfigType.php +++ b/src/applications/config/custom/PhabricatorCustomLogoConfigType.php @@ -108,7 +108,7 @@ final class PhabricatorCustomLogoConfigType $controls[] = id(new AphrontFormTextControl()) ->setName('wordmarkText') ->setLabel(pht('Wordmark')) - ->setPlaceholder(pht('Phabricator')) + ->setPlaceholder(PlatformSymbols::getPlatformServerName()) ->setValue($wordmark_text); return $controls; diff --git a/src/applications/config/editor/PhabricatorConfigEditor.php b/src/applications/config/editor/PhabricatorConfigEditor.php index deccf1ef5c..7a8833c84e 100644 --- a/src/applications/config/editor/PhabricatorConfigEditor.php +++ b/src/applications/config/editor/PhabricatorConfigEditor.php @@ -8,7 +8,7 @@ final class PhabricatorConfigEditor } public function getEditorObjectsDescription() { - return pht('Phabricator Configuration'); + return pht('Configuration'); } public function getTransactionTypes() { diff --git a/src/applications/config/issue/PhabricatorSetupIssue.php b/src/applications/config/issue/PhabricatorSetupIssue.php index a2e9532ede..cadedfc7da 100644 --- a/src/applications/config/issue/PhabricatorSetupIssue.php +++ b/src/applications/config/issue/PhabricatorSetupIssue.php @@ -28,7 +28,8 @@ final class PhabricatorSetupIssue extends Phobject { $message = pht( "Unable to connect to MySQL!\n\n". "%s\n\n". - "Make sure Phabricator and MySQL are correctly configured.", + "Make sure databases connection information and MySQL are ". + "correctly configured.", $ex->getMessage()); $issue = id(new self()) diff --git a/src/applications/config/option/PhabricatorAWSConfigOptions.php b/src/applications/config/option/PhabricatorAWSConfigOptions.php index 5f2246fdaa..a7a3a1e2ea 100644 --- a/src/applications/config/option/PhabricatorAWSConfigOptions.php +++ b/src/applications/config/option/PhabricatorAWSConfigOptions.php @@ -42,7 +42,7 @@ final class PhabricatorAWSConfigOptions pht( 'Explicit S3 endpoint to use. This should be the endpoint '. 'which corresponds to the region you have selected in '. - '`amazon-s3.region`. Phabricator can not determine the correct '. + '`amazon-s3.region`. This software can not determine the correct '. 'endpoint automatically because some endpoint locations are '. 'irregular.')) ->addExample( diff --git a/src/applications/config/option/PhabricatorAccessLogConfigOptions.php b/src/applications/config/option/PhabricatorAccessLogConfigOptions.php index 45f730b977..0e3d336fc5 100644 --- a/src/applications/config/option/PhabricatorAccessLogConfigOptions.php +++ b/src/applications/config/option/PhabricatorAccessLogConfigOptions.php @@ -73,8 +73,8 @@ final class PhabricatorAccessLogConfigOptions ->setSummary(pht('Access log location.')) ->setDescription( pht( - "To enable the Phabricator access log, specify a path. The ". - "Phabricator access than normal HTTP access logs (for instance, ". + "To enable the HTTP access log, specify a path. This log is ". + "more detailed than normal HTTP access logs (for instance, ". "it can show logged-in users, controllers, and other application ". "data).\n\n". "If not set, no log will be written.")) @@ -82,7 +82,7 @@ final class PhabricatorAccessLogConfigOptions null, pht('Disable access log.')) ->addExample( - '/var/log/phabricator/access.log', + '/var/log/devtools/access.log', pht('Write access log here.')), $this->newOption( 'log.access.format', @@ -98,16 +98,16 @@ final class PhabricatorAccessLogConfigOptions ->setSummary(pht('SSH log location.')) ->setDescription( pht( - "To enable the Phabricator SSH log, specify a path. The ". - "access log can provide more detailed information about SSH ". - "access than a normal SSH log (for instance, it can show ". - "logged-in users, commands, and other application data).\n\n". + "To enable the SSH log, specify a path. This log can provide ". + "more detailed information about SSH access than a normal SSH ". + "log (for instance, it can show logged-in users, commands, and ". + "other application data).\n\n". "If not set, no log will be written.")) ->addExample( null, pht('Disable SSH log.')) ->addExample( - '/var/log/phabricator/ssh.log', + '/var/log/devtools/ssh.log', pht('Write SSH log here.')), $this->newOption( 'log.ssh.format', @@ -121,14 +121,14 @@ final class PhabricatorAccessLogConfigOptions ->setSummary(pht('SSH error log location.')) ->setDescription( pht( - 'To enable the Phabricator SSH error log, specify a path. Errors '. - 'occurring in contexts where Phabricator is serving SSH requests '. + 'To enable the SSH error log, specify a path. Errors occurring '. + 'in contexts where this software is serving SSH requests '. 'will be written to this log.'. "\n\n". 'If not set, no log will be written.')) ->addExample(null, pht('Disable SSH error log.')) ->addExample( - '/var/log/phabricator/ssh-error.log', + '/var/log/devtools/ssh-error.log', pht('Write SSH error log here.')), ); } diff --git a/src/applications/config/option/PhabricatorAuthenticationConfigOptions.php b/src/applications/config/option/PhabricatorAuthenticationConfigOptions.php index 8058ecafcb..976e27b324 100644 --- a/src/applications/config/option/PhabricatorAuthenticationConfigOptions.php +++ b/src/applications/config/option/PhabricatorAuthenticationConfigOptions.php @@ -44,14 +44,14 @@ final class PhabricatorAuthenticationConfigOptions pht('Require administrators to approve new accounts.')) ->setDescription( pht( - "Newly registered Phabricator accounts can either be placed ". + "Newly registered accounts can either be placed ". "into a manual approval queue for administrative review, or ". "automatically activated immediately. The approval queue is ". "enabled by default because it gives you greater control over ". - "who can register an account and access Phabricator.\n\n". + "who can register an account and access the server.\n\n". "If your install is completely public, or on a VPN, or users can ". "only register with a trusted provider like LDAP, or you've ". - "otherwise configured Phabricator to prevent unauthorized ". + "otherwise configured the server to prevent unauthorized ". "registration, you can disable the queue to reduce administrative ". "overhead.\n\n". "NOTE: Before you disable the queue, make sure ". @@ -107,7 +107,7 @@ final class PhabricatorAuthenticationConfigOptions 'This option controls whether users can edit account email '. 'addresses and profile real names.'. "\n\n". - 'If you set up Phabricator to automatically synchronize account '. + 'If you set things up to automatically synchronize account '. 'information from some other authoritative system, you can '. 'prevent users from making these edits to ensure information '. 'remains consistent across both systems.')), diff --git a/src/applications/config/option/PhabricatorClusterConfigOptions.php b/src/applications/config/option/PhabricatorClusterConfigOptions.php index ca4153a8b2..3cbdf6706f 100644 --- a/src/applications/config/option/PhabricatorClusterConfigOptions.php +++ b/src/applications/config/option/PhabricatorClusterConfigOptions.php @@ -8,7 +8,7 @@ final class PhabricatorClusterConfigOptions } public function getDescription() { - return pht('Configure Phabricator to run on a cluster of hosts.'); + return pht('Configure services to run on a cluster of hosts.'); } public function getIcon() { @@ -25,7 +25,7 @@ final class PhabricatorClusterConfigOptions WARNING: This is a prototype option and the description below is currently pure fantasy. -This option allows you to make Phabricator aware of database read replicas so +This option allows you to make this service aware of database read replicas so it can monitor database health, spread load, and degrade gracefully to read-only mode in the event of a failure on the primary host. For help with configuring cluster databases, see **[[ %s | %s ]]** in the documentation. @@ -55,7 +55,7 @@ EOTEXT ->setSummary(pht('Address ranges of cluster hosts.')) ->setDescription( pht( - 'Define a Phabricator cluster by providing a whitelist of host '. + 'Define a cluster by providing a whitelist of host '. 'addresses that are part of the cluster.'. "\n\n". 'Hosts on this whitelist have special powers. These hosts are '. @@ -67,7 +67,7 @@ EOTEXT 'cluster and no additional hosts. See the examples below for '. 'details.'. "\n\n". - 'When cluster addresses are defined, Phabricator hosts will also '. + 'When cluster addresses are defined, hosts will also '. 'reject requests to interfaces which are not whitelisted.', $intro_href, $intro_name)) @@ -101,7 +101,7 @@ EOTEXT 'hosting providers running multi-tenant clusters.'. "\n\n". 'If you provide an instance identifier here (normally by '. - 'injecting it with a `%s`), Phabricator will pass it to '. + 'injecting it with a `%s`), the server will pass it to '. 'subprocesses and commit hooks in the `%s` environmental variable.', 'PhabricatorConfigSiteSource', 'PHABRICATOR_INSTANCE')), @@ -115,7 +115,7 @@ EOTEXT 'WARNING: This is a prototype option and the description below '. 'is currently pure fantasy.'. "\n\n". - 'Switch Phabricator to read-only mode. In this mode, users will '. + 'Switch the service to read-only mode. In this mode, users will '. 'be unable to write new data. Normally, the cluster degrades '. 'into this mode automatically when it detects that the database '. 'master is unreachable, but you can activate it manually in '. diff --git a/src/applications/config/option/PhabricatorCoreConfigOptions.php b/src/applications/config/option/PhabricatorCoreConfigOptions.php index 77c48115a4..b09c12e269 100644 --- a/src/applications/config/option/PhabricatorCoreConfigOptions.php +++ b/src/applications/config/option/PhabricatorCoreConfigOptions.php @@ -38,15 +38,15 @@ final class PhabricatorCoreConfigOptions $applications_app_href = '/applications/'; $silent_description = $this->deformat(pht(<<newOption('phabricator.base-uri', 'string', null) ->setLocked(true) - ->setSummary(pht('URI where Phabricator is installed.')) + ->setSummary(pht('URI where this software is installed.')) ->setDescription( pht( - 'Set the URI where Phabricator is installed. Setting this '. + 'Set the URI where this software is installed. Setting this '. 'improves security by preventing cookies from being set on other '. 'domains, and allows daemons to send emails with links that have '. 'the correct domain.')) - ->addExample('http://phabricator.example.com/', pht('Valid Setting')), + ->addExample('http://devtools.example.com/', pht('Valid Setting')), $this->newOption('phabricator.production-uri', 'string', null) ->setSummary( pht('Primary install URI, for multi-environment installs.')) ->setDescription( pht( - 'If you have multiple Phabricator environments (like a '. - 'development/staging environment for working on testing '. - 'Phabricator, and a production environment for deploying it), '. + 'If you have multiple %s environments (like a '. + 'development/staging environment and a production environment), '. 'set the production environment URI here so that emails and other '. 'durable URIs will always generate with links pointing at the '. 'production environment. If unset, defaults to `%s`. Most '. 'installs do not need to set this option.', + PlatformSymbols::getPlatformServerName(), 'phabricator.base-uri')) - ->addExample('http://phabricator.example.com/', pht('Valid Setting')), + ->addExample('http://devtools.example.com/', pht('Valid Setting')), $this->newOption('phabricator.allowed-uris', 'list', array()) ->setLocked(true) - ->setSummary(pht('Alternative URIs that can access Phabricator.')) + ->setSummary(pht('Alternative URIs that can access this service.')) ->setDescription( pht( "These alternative URIs will be able to access 'normal' pages ". - "on your Phabricator install. Other features such as OAuth ". + "on your this install. Other features such as OAuth ". "won't work. The major use case for this is moving installs ". "across domains.")) ->addExample( @@ -109,7 +109,7 @@ EOREMARKUP pht('Valid Setting')), $this->newOption('phabricator.timezone', 'string', null) ->setSummary( - pht('The timezone Phabricator should use.')) + pht('The timezone this software should use by default.')) ->setDescription($timezone_description) ->addExample('America/New_York', pht('US East (EDT)')) ->addExample('America/Chicago', pht('US Central (CDT)')) @@ -119,12 +119,12 @@ EOREMARKUP ->setLocked(true) ->setSummary( pht( - 'Set a string Phabricator should use to prefix cookie names.')) + 'Set a string this software should use to prefix cookie names.')) ->setDescription( pht( 'Cookies set for x.com are also sent for y.x.com. Assuming '. - 'Phabricator instances are running on both domains, this will '. - 'create a collision preventing you from logging in.')) + 'instances are running on both domains, this will create a '. + 'collision preventing you from logging in.')) ->addExample('dev', pht('Prefix cookie with "%s"', 'dev')), $this->newOption('phabricator.show-prototypes', 'bool', false) ->setLocked(true) @@ -141,11 +141,11 @@ EOREMARKUP "IMPORTANT: The upstream does not provide support for prototype ". "applications.". "\n\n". - "Phabricator includes prototype applications which are in an ". + "This platform includes prototype applications which are in an ". "**early stage of development**. By default, prototype ". "applications are not installed, because they are often not yet ". "developed enough to be generally usable. You can enable ". - "this option to install them if you're developing Phabricator ". + "this option to install them if you're developing applications ". "or are interested in previewing upcoming features.". "\n\n". "To learn more about prototypes, see [[ %s | %s ]].". @@ -164,7 +164,7 @@ EOREMARKUP pht('Allows you to remove levity and jokes from the UI.')) ->setDescription( pht( - 'By default, Phabricator includes some flavor text in the UI, '. + 'By default, this software includes some flavor text in the UI, '. 'like a prompt to "Weigh In" rather than "Add Comment" in '. 'Maniphest. If you\'d prefer more traditional UI strings like '. '"Add Comment", you can set this flag to disable most of the '. @@ -189,7 +189,7 @@ EOREMARKUP 'will not be linked.')) ->setDescription( pht( - 'By default, Phabricator links object names in Remarkup fields '. + 'By default, this software links object names in Remarkup fields '. 'to the corresponding object. This regex can be used to modify '. 'this behavior; object names that match this regex will not be '. 'linked.')), @@ -200,11 +200,11 @@ EOREMARKUP '$PATH')) ->setDescription( pht( - "Phabricator occasionally shells out to other binaries on the ". + "Thhi software sometimes executes other binaries on the ". "server. An example of this is the `%s` command, used to ". "syntax-highlight code written in languages other than PHP. By ". "default, it is assumed that these binaries are in the %s of the ". - "user running Phabricator (normally 'apache', 'httpd', or ". + "user running this software (normally 'apache', 'httpd', or ". "'nobody'). Here you can add extra directories to the %s ". "environment variable, for when these binaries are in ". "non-standard locations.\n\n". @@ -216,7 +216,7 @@ EOREMARKUP 'pygmentize', '$PATH', '$PATH', - 'phabricator/support/bin/', + 'support/bin/', $path)) ->setLocked(true) ->addExample('/usr/local/bin', pht('Add One Path')) @@ -251,7 +251,7 @@ EOREMARKUP $this->newOption('phabricator.application-settings', 'wild', array()) ->setLocked(true) ->setDescription( - pht('Customized settings for Phabricator applications.')), + pht('Customized settings for applications.')), $this->newOption('phabricator.cache-namespace', 'string', 'phabricator') ->setLocked(true) ->setDescription(pht('Cache namespace.')), @@ -262,7 +262,7 @@ EOREMARKUP pht('Run Silently'), pht('Run Normally'), )) - ->setSummary(pht('Stop Phabricator from sending any email, etc.')) + ->setSummary(pht('Stop this software from sending any email, etc.')) ->setDescription($silent_description), ); @@ -306,11 +306,11 @@ EOREMARKUP throw new PhabricatorConfigValidationException( pht( "Config option '%s' is invalid. The URI must NOT have a path, ". - "e.g. '%s' is OK, but '%s' is not. Phabricator must be installed ". - "on an entire domain; it can not be installed on a path.", + "e.g. '%s' is OK, but '%s' is not. This software must be '. + 'installed on an entire domain; it can not be installed on a path.", $key, - 'http://phabricator.example.com/', - 'http://example.com/phabricator/')); + 'http://devtools.example.com/', + 'http://example.com/devtools/')); } } diff --git a/src/applications/config/option/PhabricatorDeveloperConfigOptions.php b/src/applications/config/option/PhabricatorDeveloperConfigOptions.php index f4b8e8b157..cd09717bc7 100644 --- a/src/applications/config/option/PhabricatorDeveloperConfigOptions.php +++ b/src/applications/config/option/PhabricatorDeveloperConfigOptions.php @@ -8,7 +8,7 @@ final class PhabricatorDeveloperConfigOptions } public function getDescription() { - return pht('Options for Phabricator developers, including debugging.'); + return pht('Options for platform developers, including debugging.'); } public function getIcon() { @@ -27,18 +27,19 @@ final class PhabricatorDeveloperConfigOptions pht('Enable DarkConsole'), pht('Disable DarkConsole'), )) - ->setSummary(pht("Enable Phabricator's debugging console.")) + ->setSummary(pht('Enable the debugging console.')) ->setDescription( pht( "DarkConsole is a development and profiling tool built into ". - "Phabricator's web interface. You should leave it disabled unless ". - "you are developing or debugging Phabricator.\n\n". + "the web interface. You should leave it disabled unless ". + "you are developing or debugging %s.\n\n". "Once you activate DarkConsole for the install, **you need to ". "enable it for your account before it will actually appear on ". "pages.** You can do this in Settings > Developer Settings.\n\n". "DarkConsole exposes potentially sensitive data (like queries, ". "stack traces, and configuration) so you generally should not ". - "turn it on in production.")), + "turn it on in production.", + PlatformSymbols::getPlatformServerName())), $this->newOption('darkconsole.always-on', 'bool', false) ->setBoolOptions( array( @@ -91,11 +92,11 @@ final class PhabricatorDeveloperConfigOptions 'Confirm before redirecting so DarkConsole can be examined.')) ->setDescription( pht( - 'Normally, Phabricator issues HTTP redirects after a successful '. + 'Normally, this software issues HTTP redirects after a successful '. 'POST. This can make it difficult to debug things which happen '. 'while processing the POST, because service and profiling '. 'information are lost. By setting this configuration option, '. - 'Phabricator will show a page instead of automatically '. + 'an interstitial page will be shown instead of automatically '. 'redirecting, allowing you to examine service and profiling '. 'information. It also makes the UX awful, so you should only '. 'enable it when debugging.')), @@ -106,7 +107,7 @@ final class PhabricatorDeveloperConfigOptions ->setSummary(pht('Automatically profile some percentage of pages.')) ->setDescription( pht( - "Normally, Phabricator profiles pages only when explicitly ". + "Normally, pages are profiled only when explicitly ". "requested via DarkConsole. However, it may be useful to profile ". "some pages automatically.\n\n". "Set this option to a positive integer N to profile 1 / N pages ". @@ -128,7 +129,7 @@ final class PhabricatorDeveloperConfigOptions ->setDescription( pht( "The Multimeter application collects performance samples. You ". - "can use this data to help you understand what Phabricator is ". + "can use this data to help you understand what the software is ". "spending time and resources doing, and to identify problematic ". "access patterns.". "\n\n". diff --git a/src/applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php b/src/applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php index 0a3ea32e44..b4ecb4bdc1 100644 --- a/src/applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php +++ b/src/applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php @@ -4,11 +4,11 @@ final class PhabricatorExtendingPhabricatorConfigOptions extends PhabricatorApplicationConfigOptions { public function getName() { - return pht('Extending Phabricator'); + return pht('Extensions'); } public function getDescription() { - return pht('Make Phabricator even cooler!'); + return pht('Manage extensions.'); } public function getIcon() { diff --git a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php index 7e6978dfd8..cca8794055 100644 --- a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php +++ b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php @@ -22,33 +22,32 @@ final class PhabricatorMetaMTAConfigOptions public function getOptions() { $send_as_user_desc = $this->deformat(pht(<<deformat(pht(<<deformat(pht(<<deformat(pht(<<deformat(pht(<<deformat(pht(<<deformat(pht(<<' @@ -192,12 +191,12 @@ Default address used as a "From" or "To" email address when an address is required but no meaningful address is available. If you configure inbound mail, you generally do not need to set this: -Phabricator will automatically generate and use a suitable mailbox on the +the software will automatically generate and use a suitable mailbox on the inbound mail domain. Otherwise, this option should be configured to point at a valid mailbox which discards all mail sent to it. If you point it at an invalid mailbox, mail sent -by Phabricator and some mail sent by users will bounce. If you point it at a +by the software and some mail sent by users will bounce. If you point it at a real user mailbox, that user will get a lot of mail they don't want. For further guidance, see **[[ %s | %s ]]** in the documentation. @@ -226,19 +225,22 @@ EODOC )) ->setSummary( pht( - 'Controls whether Phabricator sends one email with multiple '. - 'recipients in the "To:" line, or multiple emails, each with a '. - 'single recipient in the "To:" line.')) + 'Controls whether email for multiple recipients is sent by '. + 'creating one message with everyone in the "To:" line, or '. + 'multiple messages that each have a single recipeint in the '. + '"To:" line.')) ->setDescription($one_mail_per_recipient_desc), $this->newOption('metamta.can-send-as-user', 'bool', false) ->setBoolOptions( array( pht('Send as User Taking Action'), - pht('Send as Phabricator'), + pht( + 'Send as %s', + PlatformSymbols::getPlatformServerName()), )) ->setSummary( pht( - 'Controls whether Phabricator sends email "From" users.')) + 'Controls whether email is sent "From" users.')) ->setDescription($send_as_user_desc), $this->newOption( 'metamta.reply-handler-domain', @@ -246,7 +248,7 @@ EODOC null) ->setLocked(true) ->setDescription(pht('Domain used for reply email addresses.')) - ->addExample('phabricator.example.com', ''), + ->addExample('devtools.example.com', ''), $this->newOption('metamta.recipients.show-hints', 'bool', true) ->setBoolOptions( array( @@ -271,12 +273,12 @@ EODOC )) ->setSummary( pht( - 'Phabricator can use less-secure but mailing list friendly public '. - 'reply addresses.')) + 'Reply addresses can either be private (more secure) or '. + 'public (which works better with mailing lists).')) ->setDescription($public_replies_description), $this->newOption('metamta.single-reply-handler-prefix', 'string', null) ->setSummary( - pht('Allow Phabricator to use a single mailbox for all replies.')) + pht('Allow a single mailbox to be used for all replies.')) ->setDescription($single_description), $this->newOption('metamta.user-address-format', 'enum', 'full') ->setEnumOptions( @@ -285,7 +287,7 @@ EODOC 'real' => pht('Real'), 'full' => pht('Full'), )) - ->setSummary(pht('Control how Phabricator renders user names in mail.')) + ->setSummary(pht('Control how user names are rendered in mail.')) ->setDescription($address_description) ->addExample('gwashington ', 'short') ->addExample('George Washington ', 'real') diff --git a/src/applications/config/option/PhabricatorMySQLConfigOptions.php b/src/applications/config/option/PhabricatorMySQLConfigOptions.php index 97ae6af016..7b472cf379 100644 --- a/src/applications/config/option/PhabricatorMySQLConfigOptions.php +++ b/src/applications/config/option/PhabricatorMySQLConfigOptions.php @@ -38,14 +38,14 @@ final class PhabricatorMySQLConfigOptions $this->newOption('storage.default-namespace', 'string', 'phabricator') ->setLocked(true) ->setSummary( - pht('The namespace that Phabricator databases should use.')) + pht('The namespace that databases should use.')) ->setDescription( pht( - "Phabricator puts databases in a namespace, which defaults to ". + "Databases are created in a namespace, which defaults to ". "'phabricator' -- for instance, the Differential database is ". "named 'phabricator_differential' by default. You can change ". "this namespace if you want. Normally, you should not do this ". - "unless you are developing Phabricator and using namespaces to ". + "unless you are developing extensions and using namespaces to ". "separate multiple sandbox datasets.")), $this->newOption('mysql.port', 'string', null) ->setLocked(true) diff --git a/src/applications/config/option/PhabricatorPHDConfigOptions.php b/src/applications/config/option/PhabricatorPHDConfigOptions.php index 259660d562..5243bb3ee8 100644 --- a/src/applications/config/option/PhabricatorPHDConfigOptions.php +++ b/src/applications/config/option/PhabricatorPHDConfigOptions.php @@ -50,7 +50,7 @@ final class PhabricatorPHDConfigOptions pht( 'Specify a system user to run the daemons as. Primarily, this '. 'user will own the working copies of any repositories that '. - 'Phabricator imports or manages. This option is new and '. + 'this software imports or manages. This option is new and '. 'experimental.')), $this->newOption('phd.garbage-collection', 'wild', array()) ->setLocked(true) diff --git a/src/applications/config/option/PhabricatorSecurityConfigOptions.php b/src/applications/config/option/PhabricatorSecurityConfigOptions.php index 3438d3bd67..c57bd2ad87 100644 --- a/src/applications/config/option/PhabricatorSecurityConfigOptions.php +++ b/src/applications/config/option/PhabricatorSecurityConfigOptions.php @@ -67,7 +67,7 @@ EOTEXT PhabricatorEnv::getDoclink('Configuring Encryption'))); $require_mfa_description = $this->deformat(pht(<<setSummary(pht('Alternate domain to serve files from.')) ->setDescription( pht( - 'By default, Phabricator serves files from the same domain '. + 'By default, this software serves files from the same domain '. 'the application is served from. This is convenient, but '. 'presents a security risk.'. "\n\n". @@ -119,7 +119,7 @@ EOTEXT pht( "If the web server responds to both HTTP and HTTPS requests but ". "you want users to connect with only HTTPS, you can set this ". - "to `true` to make Phabricator redirect HTTP requests to HTTPS.". + "to `true` to make this service redirect HTTP requests to HTTPS.". "\n\n". "Normally, you should just configure your server not to accept ". "HTTP traffic, but this setting may be useful if you originally ". @@ -128,15 +128,14 @@ EOTEXT "balancer which terminates HTTPS connections and you can not ". "reasonably configure more granular behavior there.". "\n\n". - "IMPORTANT: Phabricator determines if a request is HTTPS or not ". - "by examining the PHP `%s` variable. If you run ". - "Apache/mod_php this will probably be set correctly for you ". - "automatically, but if you run Phabricator as CGI/FCGI (e.g., ". - "through nginx or lighttpd), you need to configure your web ". - "server so that it passes the value correctly based on the ". - "connection type.". + "IMPORTANT: A request is identified as HTTP or HTTPS by examining ". + "the PHP `%s` variable. If you run Apache/mod_php this will ". + "probably be set correctly for you automatically, but if you run ". + "as CGI/FCGI (e.g., through nginx or lighttpd), you need to ". + "configure your web server so that it passes the value correctly ". + "based on the connection type.". "\n\n". - "If you configure Phabricator in cluster mode, note that this ". + "If you configure clustering, note that this ". "setting is ignored by intracluster requests.", "\$_SERVER['HTTPS']")) ->setBoolOptions( @@ -177,12 +176,12 @@ EOTEXT 'dangerous URI handlers.'. "\n\n". 'This set is also used to enforce valid redirect URIs. '. - 'Phabricator will refuse to issue a HTTP "Location" redirect to a '. - 'URI with a protocol not on this set.'. + 'This service will refuse to issue a HTTP "Location" redirect '. + 'to a URI with a protocol not on this set.'. "\n\n". 'Usually, "http" and "https" should be present in this set. If '. - 'you remove one or both protocols, some Phabricator features '. - 'which rely on links or redirects may not work.')) + 'you remove one or both protocols, some features which rely on '. + 'links or redirects may not work.')) ->addExample("http\nhttps", pht('Valid Setting')) ->setLocked(true), $this->newOption( @@ -248,10 +247,9 @@ EOTEXT 'requests.')) ->setDescription( pht( - 'Phabricator users can make requests to other services from '. - 'the Phabricator host in some circumstances (for example, by '. - 'creating a repository with a remote URL or having Phabricator '. - 'fetch an image from a remote server).'. + 'Users can make requests to other services from '. + 'service hosts in some circumstances (for example, by '. + 'creating a repository with a remote URL).'. "\n\n". 'This may represent a security vulnerability if services on '. 'the same subnet will accept commands or reveal private '. @@ -259,8 +257,8 @@ EOTEXT 'IP address. In particular, all hosts in EC2 have access to '. 'such a service.'. "\n\n". - 'This option defines a list of netblocks which Phabricator '. - 'will decline to connect to. Generally, you should list all '. + 'This option defines a list of netblocks which requests will '. + 'never be issued to. Generally, you should list all '. 'private IP space here.')) ->addExample(array('0.0.0.0/0'), pht('No Outbound Requests')), $this->newOption('security.strict-transport-security', 'bool', false) @@ -326,11 +324,11 @@ EOTEXT throw new PhabricatorConfigValidationException( pht( "Config option '%s' is invalid. The URI must NOT have a path, ". - "e.g. '%s' is OK, but '%s' is not. Phabricator must be installed ". - "on an entire domain; it can not be installed on a path.", + "e.g. '%s' is OK, but '%s' is not. This software must be ". + "installed on an entire domain; it can not be installed on a path.", $key, - 'http://phabricator.example.com/', - 'http://example.com/phabricator/')); + 'http://devtools.example.com/', + 'http://example.com/devtools/')); } } } diff --git a/src/applications/config/option/PhabricatorSyntaxHighlightingConfigOptions.php b/src/applications/config/option/PhabricatorSyntaxHighlightingConfigOptions.php index 27596f7f07..05303e2b96 100644 --- a/src/applications/config/option/PhabricatorSyntaxHighlightingConfigOptions.php +++ b/src/applications/config/option/PhabricatorSyntaxHighlightingConfigOptions.php @@ -31,13 +31,12 @@ final class PhabricatorSyntaxHighlightingConfigOptions ->setSummary(pht('Default non-pygments syntax highlighter engine.')) ->setDescription( pht( - 'Phabricator can highlight PHP by default and use Pygments for '. - 'other languages if enabled. You can provide a custom '. - 'highlighter engine by extending class %s.', + 'You can provide a custom highlighter engine by extending '. + 'class %s.', 'PhutilSyntaxHighlighterEngine')), $this->newOption('pygments.enabled', 'bool', false) ->setSummary( - pht('Should Phabricator use Pygments to highlight code?')) + pht('Use Pygments to highlight code?')) ->setBoolOptions( array( pht('Use Pygments'), @@ -45,7 +44,7 @@ final class PhabricatorSyntaxHighlightingConfigOptions )) ->setDescription( pht( - 'Phabricator supports syntax highlighting a few languages by '. + 'Syntax highlighting a supported for a few languages by '. 'default, but you can install Pygments (a third-party syntax '. 'highlighting tool) to provide support for many more languages.'. "\n\n". @@ -54,12 +53,12 @@ final class PhabricatorSyntaxHighlightingConfigOptions 'download and install instructions.'. "\n\n". 'Once Pygments is installed, enable this option '. - '(`pygments.enabled`) to make Phabricator use Pygments when '. + '(`pygments.enabled`) to make use of Pygments when '. 'highlighting source code.'. "\n\n". 'After you install and enable Pygments, newly created source '. 'code (like diffs and pastes) should highlight correctly. '. - 'You may need to clear Phabricator\'s caches to get previously '. + 'You may need to clear caches to get previously '. 'existing source code to highlight. For instructions on '. 'managing caches, see [[ %s | Managing Caches ]].', $caches_href)), diff --git a/src/applications/config/option/PhabricatorUIConfigOptions.php b/src/applications/config/option/PhabricatorUIConfigOptions.php index 391ba7e123..6802149083 100644 --- a/src/applications/config/option/PhabricatorUIConfigOptions.php +++ b/src/applications/config/option/PhabricatorUIConfigOptions.php @@ -8,7 +8,7 @@ final class PhabricatorUIConfigOptions } public function getDescription() { - return pht('Configure the Phabricator UI, including colors.'); + return pht('Configure the UI, including colors.'); } public function getIcon() { @@ -51,7 +51,7 @@ EOJSON; return array( $this->newOption('ui.header-color', 'enum', 'blindigo') ->setDescription( - pht('Sets the default color scheme of Phabricator.')) + pht('Sets the default color scheme.')) ->setEnumOptions($options), $this->newOption('ui.logo', $logo_type, array()) ->setSummary( @@ -61,9 +61,10 @@ EOJSON; "Customize the logo image and text which appears in the main ". "site header:\n\n". " - **Logo Image**: Upload a new 80 x 80px image to replace the ". - "Phabricator logo in the site header.\n\n". + "logo in the site header.\n\n". " - **Wordmark**: Choose new text to display next to the logo. ". - "By default, the header displays //Phabricator//.\n\n")), + "By default, the header displays //%s//.\n\n", + PlatformSymbols::getPlatformServerName())), $this->newOption('ui.favicons', 'wild', array()) ->setSummary(pht('Customize favicons.')) ->setDescription(pht('Customize favicons.')) diff --git a/src/applications/config/response/PhabricatorConfigResponse.php b/src/applications/config/response/PhabricatorConfigResponse.php index 588f60255f..3f9a9c86e9 100644 --- a/src/applications/config/response/PhabricatorConfigResponse.php +++ b/src/applications/config/response/PhabricatorConfigResponse.php @@ -21,7 +21,7 @@ final class PhabricatorConfigResponse extends AphrontStandaloneHTMLResponse { } protected function getResponseTitle() { - return pht('Phabricator Setup Error'); + return pht('Setup Error'); } protected function getResponseBodyClass() { diff --git a/src/applications/config/view/PhabricatorSetupIssueView.php b/src/applications/config/view/PhabricatorSetupIssueView.php index 74bdc2d522..003725d5fc 100644 --- a/src/applications/config/view/PhabricatorSetupIssueView.php +++ b/src/applications/config/view/PhabricatorSetupIssueView.php @@ -90,14 +90,14 @@ final class PhabricatorSetupIssueView extends AphrontView { $fallback_info = pht( "If those commands don't work, try Google. The process of installing ". - "PHP extensions is not specific to Phabricator, and any instructions ". - "you can find for installing them on your system should work. On Mac ". - "OS X, you might want to try Homebrew."); + "PHP extensions is not specific to this software, and any ". + "instructions you can find for installing them on your system should ". + "work. On Mac OS X, you might want to try Homebrew."); $restart_info = pht( - 'After installing new PHP extensions, restart Phabricator '. + 'After installing new PHP extensions, restart everything '. 'for the changes to take effect. For help with restarting '. - 'Phabricator, see %s in the documentation.', + 'everything, see %s in the documentation.', $this->renderRestartLink()); $description[] = phutil_tag( @@ -249,7 +249,7 @@ final class PhabricatorSetupIssueView extends AphrontView { 'p', array(), pht( - 'The current Phabricator configuration has these %d value(s):', + 'The current configuration has these %d value(s):', count($configs))); $options = PhabricatorApplicationConfigOptions::loadAllOptions(); @@ -284,7 +284,7 @@ final class PhabricatorSetupIssueView extends AphrontView { $update = array(); foreach ($configs as $key) { $update[] = hsprintf( - 'phabricator/ $ ./bin/config set %s value', + '$ ./bin/config set %s value', $key); } $update = phutil_tag('pre', array(), phutil_implode_html("\n", $update)); @@ -460,9 +460,9 @@ final class PhabricatorSetupIssueView extends AphrontView { 'p', array(), pht( - 'After editing the PHP configuration, restart Phabricator for '. + 'After editing the PHP configuration, restart everything for '. 'the changes to take effect. For help with restarting '. - 'Phabricator, see %s in the documentation.', + 'everything, see %s in the documentation.', $this->renderRestartLink())); return phutil_tag( diff --git a/src/applications/console/plugin/DarkConsoleEventPlugin.php b/src/applications/console/plugin/DarkConsoleEventPlugin.php index 070227c12d..182e220659 100644 --- a/src/applications/console/plugin/DarkConsoleEventPlugin.php +++ b/src/applications/console/plugin/DarkConsoleEventPlugin.php @@ -7,7 +7,7 @@ final class DarkConsoleEventPlugin extends DarkConsolePlugin { } public function getDescription() { - return pht('Information about Phabricator events and event listeners.'); + return pht('Information about events and event listeners.'); } public function generateData() { diff --git a/src/applications/countdown/query/PhabricatorCountdownQuery.php b/src/applications/countdown/query/PhabricatorCountdownQuery.php index 67a2f3a9e3..4a6df16e8e 100644 --- a/src/applications/countdown/query/PhabricatorCountdownQuery.php +++ b/src/applications/countdown/query/PhabricatorCountdownQuery.php @@ -28,10 +28,6 @@ final class PhabricatorCountdownQuery return $this; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - public function newResultObject() { return new PhabricatorCountdown(); } diff --git a/src/applications/daemon/application/PhabricatorDaemonsApplication.php b/src/applications/daemon/application/PhabricatorDaemonsApplication.php index 08e81d5d7e..3aa6b2f086 100644 --- a/src/applications/daemon/application/PhabricatorDaemonsApplication.php +++ b/src/applications/daemon/application/PhabricatorDaemonsApplication.php @@ -7,7 +7,7 @@ final class PhabricatorDaemonsApplication extends PhabricatorApplication { } public function getShortDescription() { - return pht('Manage Phabricator Daemons'); + return pht('Manage Daemons'); } public function getBaseURI() { diff --git a/src/applications/daemon/event/PhabricatorDaemonEventListener.php b/src/applications/daemon/event/PhabricatorDaemonEventListener.php index 41324cc9c9..a17bab77af 100644 --- a/src/applications/daemon/event/PhabricatorDaemonEventListener.php +++ b/src/applications/daemon/event/PhabricatorDaemonEventListener.php @@ -63,9 +63,12 @@ final class PhabricatorDaemonEventListener extends PhabricatorEventListener { // TODO: This is a bit awkward for historical reasons, clean it up after // removing Conduit. $message = $event->getValue('message'); + $context = $event->getValue('context'); - if (strlen($context) && $context !== $message) { - $message = "({$context}) {$message}"; + if (phutil_nonempty_scalar($context)) { + if ($context !== $message) { + $message = "({$context}) {$message}"; + } } $type = $event->getValue('type'); diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php index eb2d5d229c..a9778724af 100644 --- a/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php +++ b/src/applications/daemon/management/PhabricatorDaemonManagementRestartWorkflow.php @@ -24,7 +24,7 @@ final class PhabricatorDaemonManagementRestartWorkflow 'name' => 'force', 'help' => pht( 'Stop all daemon processes on this host, even if they belong '. - 'to another Phabricator instance.'), + 'to another instance.'), ), array( 'name' => 'gently', diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementStartWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementStartWorkflow.php index bd23d3bc7d..0aca9e5c71 100644 --- a/src/applications/daemon/management/PhabricatorDaemonManagementStartWorkflow.php +++ b/src/applications/daemon/management/PhabricatorDaemonManagementStartWorkflow.php @@ -8,7 +8,7 @@ final class PhabricatorDaemonManagementStartWorkflow ->setName('start') ->setSynopsis( pht( - 'Start the standard configured collection of Phabricator daemons. '. + 'Start the standard configured collection of daemons. '. 'This is appropriate for most installs. Use **%s** to '. 'customize which daemons are launched.', 'phd launch')) diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php index 19b9fc44fb..f944b8b171 100644 --- a/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php +++ b/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php @@ -21,7 +21,7 @@ final class PhabricatorDaemonManagementStopWorkflow 'name' => 'force', 'help' => pht( 'Stop all daemon processes on this host, even if they belong '. - 'to another Phabricator instance.'), + 'to another instance.'), ), array( 'name' => 'gently', diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementWorkflow.php index b9645323c2..853a797448 100644 --- a/src/applications/daemon/management/PhabricatorDaemonManagementWorkflow.php +++ b/src/applications/daemon/management/PhabricatorDaemonManagementWorkflow.php @@ -93,7 +93,7 @@ abstract class PhabricatorDaemonManagementWorkflow pht( "You are trying to run a daemon as a nonstandard user, ". "and `%s` was not able to `%s` to the correct user. \n". - 'Phabricator is configured to run daemons as "%s", '. + 'The daemons are configured to run as "%s", '. 'but the current user is "%s". '."\n". 'Use `%s` to run as a different user, pass `%s` to ignore this '. 'warning, or edit `%s` to change the configuration.', @@ -154,7 +154,7 @@ abstract class PhabricatorDaemonManagementWorkflow SIGINT, array(__CLASS__, 'ignoreSignal')); - echo "\n phabricator/scripts/daemon/ \$ {$command}\n\n"; + echo "\n scripts/daemon/ \$ {$command}\n\n"; $tempfile = new TempFile('daemon.config'); Filesystem::writeFile($tempfile, json_encode($config)); @@ -579,7 +579,7 @@ abstract class PhabricatorDaemonManagementWorkflow $console->writeErr( "%s\n", pht( - 'PID "%d" is not a known Phabricator daemon PID.', + 'PID "%d" is not a known daemon PID.', $pid)); continue; } else { diff --git a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php index 8969151c06..52e8cc70d5 100644 --- a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php +++ b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php @@ -140,8 +140,7 @@ final class PhabricatorDashboardPanelRenderingEngine extends Phobject { return $this->renderErrorPanel( $panel->getName(), pht( - 'This panel has type "%s", but that panel type is not known to '. - 'Phabricator.', + 'This panel has type "%s", but that panel type is unknown.', $panel->getPanelType())); } @@ -425,8 +424,9 @@ final class PhabricatorDashboardPanelRenderingEngine extends Phobject { throw new Exception( pht( 'To render more than %s levels of panels nested inside other '. - 'panels, purchase a subscription to Phabricator Gold.', - new PhutilNumber($max_depth))); + 'panels, purchase a subscription to %s Gold.', + new PhutilNumber($max_depth), + PlatformSymbols::getPlatformServerName())); } if (in_array($panel->getPHID(), $this->parentPanelPHIDs)) { diff --git a/src/applications/dashboard/paneltype/PhabricatorDashboardQueryPanelType.php b/src/applications/dashboard/paneltype/PhabricatorDashboardQueryPanelType.php index 2174f793a4..c8cfe4fd2e 100644 --- a/src/applications/dashboard/paneltype/PhabricatorDashboardQueryPanelType.php +++ b/src/applications/dashboard/paneltype/PhabricatorDashboardQueryPanelType.php @@ -194,7 +194,7 @@ final class PhabricatorDashboardQueryPanelType if (!$engine) { throw new Exception( pht( - 'The application search engine "%s" is not known to Phabricator!', + 'The application search engine "%s" is unknown.', $class)); } diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php index dd32d1c9dc..c67b756262 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php @@ -34,10 +34,6 @@ final class PhabricatorDashboardPanelQuery return $this; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - public function newResultObject() { // TODO: If we don't do this, SearchEngine explodes when trying to // enumerate custom fields. For now, just give the panel a default panel diff --git a/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php index 857c4dc215..418262c745 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php @@ -26,10 +26,6 @@ final class PhabricatorDashboardPortalQuery return new PhabricatorDashboardPortal(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/dashboard/query/PhabricatorDashboardQuery.php b/src/applications/dashboard/query/PhabricatorDashboardQuery.php index 76854bffc1..954a565ac6 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardQuery.php @@ -34,10 +34,6 @@ final class PhabricatorDashboardQuery return $this; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - public function newResultObject() { return new PhabricatorDashboard(); } diff --git a/src/applications/dashboard/storage/PhabricatorDashboardPanel.php b/src/applications/dashboard/storage/PhabricatorDashboardPanel.php index 4173b208ba..61cc2bbdf2 100644 --- a/src/applications/dashboard/storage/PhabricatorDashboardPanel.php +++ b/src/applications/dashboard/storage/PhabricatorDashboardPanel.php @@ -96,8 +96,7 @@ final class PhabricatorDashboardPanel throw new Exception( pht( 'Attempting to use a panel in a way that requires an '. - 'implementation, but the panel implementation ("%s") is unknown to '. - 'Phabricator.', + 'implementation, but the panel implementation ("%s") is unknown.', $this->getPanelType())); } return $impl; diff --git a/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php b/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php index 9634756dac..8c33bd5fad 100644 --- a/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php +++ b/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php @@ -210,8 +210,8 @@ EOHELP 'to "Close" the revision and move it off the dashboard.'. "\n\n". 'If you have an unusual workflow where Differential is used for '. - 'post-commit review (normally called "Audit", elsewhere in '. - 'Phabricator), you can set this flag to treat the "Accepted" '. + 'post-commit review (normally called "Audit", elsewhere), you '. + 'can set this flag to treat the "Accepted" '. 'state as a "Closed" state and end the review workflow early.'. "\n\n". 'This sort of workflow is very unusual. Very few installs should '. @@ -228,7 +228,7 @@ EOHELP ->setSummary(pht('Attach patches to email, as text attachments.')) ->setDescription( pht( - 'If you set this to true, Phabricator will attach patches to '. + 'If you set this to true, patches will be attached to '. 'Differential mail (as text attachments). This will not work if '. 'you are using SendGrid as your mail adapter.')), $this->newOption( diff --git a/src/applications/differential/controller/DifferentialDiffCreateController.php b/src/applications/differential/controller/DifferentialDiffCreateController.php index f9d1006555..c3bfc01743 100644 --- a/src/applications/differential/controller/DifferentialDiffCreateController.php +++ b/src/applications/differential/controller/DifferentialDiffCreateController.php @@ -112,8 +112,9 @@ final class DifferentialDiffCreateController extends DifferentialController { array( array( pht( - 'The best way to create a diff is to use the Arcanist '. - 'command-line tool.'), + 'The best way to create a diff is to use the %s '. + 'command-line tool.', + PlatformSymbols::getPlatformClientName()), ' ', $arcanist_link, ), diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index 13ab8a8954..102c7f560b 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -1282,7 +1282,7 @@ final class DifferentialRevisionViewController } private function buildUnitMessagesView( - $diff, + DifferentialDiff $diff, DifferentialRevision $revision) { $viewer = $this->getViewer(); @@ -1310,14 +1310,8 @@ final class DifferentialRevisionViewController return null; } - $excuse = null; - if ($diff->hasDiffProperty('arc:unit-excuse')) { - $excuse = $diff->getProperty('arc:unit-excuse'); - } - return id(new HarbormasterUnitSummaryView()) ->setViewer($viewer) - ->setExcuse($excuse) ->setBuildable($diff->getBuildable()) ->setUnitMessages($diff->getUnitMessages()) ->setLimit(5) diff --git a/src/applications/differential/customfield/DifferentialJIRAIssuesField.php b/src/applications/differential/customfield/DifferentialJIRAIssuesField.php index e97efd6ad5..8bd0b052dd 100644 --- a/src/applications/differential/customfield/DifferentialJIRAIssuesField.php +++ b/src/applications/differential/customfield/DifferentialJIRAIssuesField.php @@ -166,8 +166,9 @@ final class DifferentialJIRAIssuesField pht('Not Linked'), pht( 'You can not add JIRA issues (%s) to this revision because your '. - 'Phabricator account is not linked to a JIRA account.', - implode(', ', $add)), + '%s account is not linked to a JIRA account.', + implode(', ', $add), + PlatformSymbols::getPlatformServerName()), $xaction); continue; } diff --git a/src/applications/differential/parser/DifferentialChangesetParser.php b/src/applications/differential/parser/DifferentialChangesetParser.php index ad62b2a1ef..5b39269bdd 100644 --- a/src/applications/differential/parser/DifferentialChangesetParser.php +++ b/src/applications/differential/parser/DifferentialChangesetParser.php @@ -1816,7 +1816,7 @@ final class DifferentialChangesetParser extends Phobject { $viewstate = $this->getViewState(); $engine_key = $viewstate->getDocumentEngineKey(); - if (strlen($engine_key)) { + if (phutil_nonempty_string($engine_key)) { if (isset($shared_engines[$engine_key])) { $document_engine = $shared_engines[$engine_key]; } else { diff --git a/src/applications/differential/query/DifferentialChangesetQuery.php b/src/applications/differential/query/DifferentialChangesetQuery.php index e2357f9278..ee29aa1cf8 100644 --- a/src/applications/differential/query/DifferentialChangesetQuery.php +++ b/src/applications/differential/query/DifferentialChangesetQuery.php @@ -58,10 +58,6 @@ final class DifferentialChangesetQuery return new DifferentialChangeset(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $changesets) { // First, attach all the diffs we already have. We can just do this // directly without worrying about querying for them. When we don't have diff --git a/src/applications/differential/query/DifferentialDiffQuery.php b/src/applications/differential/query/DifferentialDiffQuery.php index 3b1dd3ae62..04019df1e0 100644 --- a/src/applications/differential/query/DifferentialDiffQuery.php +++ b/src/applications/differential/query/DifferentialDiffQuery.php @@ -57,10 +57,6 @@ final class DifferentialDiffQuery return new DifferentialDiff(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $diffs) { $revision_ids = array_filter(mpull($diffs, 'getRevisionID')); diff --git a/src/applications/differential/query/DifferentialHunkQuery.php b/src/applications/differential/query/DifferentialHunkQuery.php index 981c2b4782..21787f04e5 100644 --- a/src/applications/differential/query/DifferentialHunkQuery.php +++ b/src/applications/differential/query/DifferentialHunkQuery.php @@ -34,10 +34,6 @@ final class DifferentialHunkQuery return new DifferentialHunk(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $hunks) { $changesets = mpull($this->changesets, null, 'getID'); foreach ($hunks as $key => $hunk) { diff --git a/src/applications/differential/query/DifferentialViewStateQuery.php b/src/applications/differential/query/DifferentialViewStateQuery.php index 6ef63721e4..604a6de1db 100644 --- a/src/applications/differential/query/DifferentialViewStateQuery.php +++ b/src/applications/differential/query/DifferentialViewStateQuery.php @@ -26,10 +26,6 @@ final class DifferentialViewStateQuery return new DifferentialViewState(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/differential/render/DifferentialChangesetTestRenderer.php b/src/applications/differential/render/DifferentialChangesetTestRenderer.php index 56b96dcbca..b4b2122e59 100644 --- a/src/applications/differential/render/DifferentialChangesetTestRenderer.php +++ b/src/applications/differential/render/DifferentialChangesetTestRenderer.php @@ -76,7 +76,7 @@ abstract class DifferentialChangesetTestRenderer $any_new = true; } $num = nonempty($p['line'], '-'); - $render = $p['render']; + $render = (string)$p['render']; $htype = nonempty($p['htype'], '.'); // TODO: This should probably happen earlier, whenever we deal with diff --git a/src/applications/differential/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php index f5cb91c56b..dfa6d1f791 100644 --- a/src/applications/differential/storage/DifferentialDiff.php +++ b/src/applications/differential/storage/DifferentialDiff.php @@ -42,7 +42,7 @@ final class DifferentialDiff private $unsavedChangesets = array(); private $changesets = self::ATTACHABLE; private $revision = self::ATTACHABLE; - private $properties = array(); + private $properties = self::ATTACHABLE; private $buildable = self::ATTACHABLE; private $unitMessages = self::ATTACHABLE; @@ -338,6 +338,9 @@ final class DifferentialDiff } public function attachProperty($key, $value) { + if (!is_array($this->properties)) { + $this->properties = array(); + } $this->properties[$key] = $value; return $this; } @@ -547,9 +550,9 @@ final class DifferentialDiff 'buildable.revision' => pht('The differential revision ID, if applicable.'), 'repository.callsign' => - pht('The callsign of the repository in Phabricator.'), + pht('The callsign of the repository.'), 'repository.phid' => - pht('The PHID of the repository in Phabricator.'), + pht('The PHID of the repository.'), 'repository.vcs' => pht('The version control system, either "svn", "hg" or "git".'), 'repository.uri' => diff --git a/src/applications/differential/storage/DifferentialRevision.php b/src/applications/differential/storage/DifferentialRevision.php index 23f5db9151..92b303d0ba 100644 --- a/src/applications/differential/storage/DifferentialRevision.php +++ b/src/applications/differential/storage/DifferentialRevision.php @@ -311,9 +311,17 @@ final class DifferentialRevision extends DifferentialDAO // which the actor may be able to use their authority over to gain the // ability to force-accept for other packages. This query doesn't apply // dominion rules yet, and we'll bypass those rules later on. + + // See T13657. We ignore "watcher" packages which don't grant their owners + // permission to force accept anything. + $authority_query = id(new PhabricatorOwnersPackageQuery()) ->setViewer($viewer) ->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE)) + ->withAuthorityModes( + array( + PhabricatorOwnersPackage::AUTHORITY_STRONG, + )) ->withAuthorityPHIDs(array($viewer->getPHID())) ->withControl($repository_phid, $paths); $authority_packages = $authority_query->execute(); diff --git a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php index deda3d43d7..86ec7b7466 100644 --- a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php @@ -229,11 +229,19 @@ final class DiffusionBrowseQueryConduitAPIMethod $commit); if (!$err) { - $tmp = new TempFile(); - Filesystem::writeFile($tmp, $contents); - list($module_info) = $repository->execxLocalCommand( - 'config -l -f %s', - $tmp); + + // NOTE: After T13673, the user executing "git" may not be the same + // as the user this process is running as (usually the webserver user), + // so we can't reliably use a temporary file: the daemon user may not + // be able to use it. + + // Use "--file -" to read from stdin instead. If this fails in some + // older versions of Git, we could exempt this particular command from + // sudoing to the daemon user. + + $future = $repository->getLocalCommandFuture('config -l --file - --'); + $future->write($contents); + list($module_info) = $future->resolvex(); $dict = array(); $lines = explode("\n", trim($module_info)); diff --git a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php index 8c53ff0555..c6ae6dbd2f 100644 --- a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php @@ -136,28 +136,33 @@ final class DiffusionHistoryQueryConduitAPIMethod // stop history (this is more consistent with the Mercurial worldview of // branches). + $path_args = array(); if (strlen($path)) { - $path_arg = csprintf('%s', $path); + $path_args[] = $path; $revset_arg = hgsprintf( 'reverse(ancestors(%s))', $commit_hash); } else { - $path_arg = ''; $revset_arg = hgsprintf( 'reverse(ancestors(%s)) and branch(%s)', - $drequest->getBranch(), - $commit_hash); + $commit_hash, + $drequest->getBranch()); + } + + $hg_analyzer = PhutilBinaryAnalyzer::getForBinary('hg'); + if ($hg_analyzer->isMercurialTemplatePnodeAvailable()) { + $hg_log_template = '{node} {p1.node} {p2.node}\\n'; + } else { + $hg_log_template = '{node} {p1node} {p2node}\\n'; } list($stdout) = $repository->execxLocalCommand( - 'log --debug --template %s --limit %d --rev %s -- %C', - '{node};{parents}\\n', + 'log --template %s --limit %d --rev %s -- %Ls', + $hg_log_template, ($offset + $limit), // No '--skip' in Mercurial. $revset_arg, - $path_arg); + $path_args); - $stdout = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( - $stdout); $lines = explode("\n", trim($stdout)); $lines = array_slice($lines, $offset); @@ -166,28 +171,19 @@ final class DiffusionHistoryQueryConduitAPIMethod $last = null; foreach (array_reverse($lines) as $line) { - list($hash, $parents) = explode(';', $line); - $parents = trim($parents); - if (!$parents) { - if ($last === null) { - $parent_map[$hash] = array('...'); - } else { - $parent_map[$hash] = array($last); - } - } else { - $parents = preg_split('/\s+/', $parents); - foreach ($parents as $parent) { - list($plocal, $phash) = explode(':', $parent); - if (!preg_match('/^0+$/', $phash)) { - $parent_map[$hash][] = $phash; - } - } - // This may happen for the zeroth commit in repository, both hashes - // are "000000000...". - if (empty($parent_map[$hash])) { - $parent_map[$hash] = array('...'); + $parts = explode(' ', trim($line)); + $hash = $parts[0]; + $parents = array_slice($parts, 1, 2); + foreach ($parents as $parent) { + if (!preg_match('/^0+\z/', $parent)) { + $parent_map[$hash][] = $parent; } } + // This may happen for the zeroth commit in repository, both hashes + // are "000000000...". + if (empty($parent_map[$hash])) { + $parent_map[$hash] = array('...'); + } // The rendering code expects the first commit to be "mainline", like // Git. Flip the order so it does the right thing. diff --git a/src/applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php index 88d216e4a1..4d651b4e52 100644 --- a/src/applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php @@ -13,7 +13,7 @@ final class DiffusionLookSoonConduitAPIMethod public function getMethodDescription() { return pht( - 'Advises Phabricator to look for new commits in a repository as soon '. + 'Advises this server to look for new commits in a repository as soon '. 'as possible. This advice is most useful if you have just pushed new '. 'commits to that repository.'); } diff --git a/src/applications/diffusion/config/PhabricatorDiffusionConfigOptions.php b/src/applications/diffusion/config/PhabricatorDiffusionConfigOptions.php index be889b4cc4..f1451be247 100644 --- a/src/applications/diffusion/config/PhabricatorDiffusionConfigOptions.php +++ b/src/applications/diffusion/config/PhabricatorDiffusionConfigOptions.php @@ -107,12 +107,12 @@ final class PhabricatorDiffusionConfigOptions ->setSummary(pht('Enable HTTP Basic Auth for repositories.')) ->setDescription( pht( - "Phabricator can serve repositories over HTTP, using HTTP basic ". + "This server can serve repositories over HTTP, using HTTP basic ". "auth.\n\n". "Because HTTP basic auth is less secure than SSH auth, it is ". "disabled by default. You can enable it here if you'd like to use ". "it anyway. There's nothing fundamentally insecure about it as ". - "long as Phabricator uses HTTPS, but it presents a much lower ". + "long as this server uses HTTPS, but it presents a much lower ". "barrier to attackers than SSH does.\n\n". "Consider using SSH for authenticated access to repositories ". "instead of HTTP.")), @@ -126,9 +126,9 @@ final class PhabricatorDiffusionConfigOptions ->setSummary(pht('Allow Git Large File Storage (LFS).')) ->setDescription( pht( - 'Phabricator supports Git LFS, a Git extension for storing large '. + 'This server supports Git LFS, a Git extension for storing large '. 'files alongside a repository. Activate this setting to allow '. - 'the extension to store file data in Phabricator.')), + 'the extension to store file data.')), $this->newOption('diffusion.ssh-user', 'string', null) ->setLocked(true) ->setSummary(pht('Login username for SSH connections to repositories.')) @@ -150,10 +150,9 @@ final class PhabricatorDiffusionConfigOptions ->setSummary(pht('Host for SSH connections to repositories.')) ->setDescription( pht( - 'If you accept Phabricator SSH traffic on a different host '. - 'from web traffic (for example, if you use different SSH and '. - 'web load balancers), you can set the SSH hostname here. This '. - 'is an advanced option.')), + 'If you accept SSH traffic on a different host from web traffic '. + '(for example, if you use different SSH and web load balancers), '. + 'you can set the SSH hostname here. This is an advanced option.')), $this->newOption('diffusion.fields', $custom_field_type, $default_fields) ->setCustomData( id(new PhabricatorRepositoryCommit()) diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php index 549c0d649f..2b81ab89cc 100644 --- a/src/applications/diffusion/controller/DiffusionBrowseController.php +++ b/src/applications/diffusion/controller/DiffusionBrowseController.php @@ -298,22 +298,8 @@ final class DiffusionBrowseController extends DiffusionController { $empty_result->setDiffusionBrowseResultSet($results); $empty_result->setView($request->getStr('view')); } else { - $phids = array(); - foreach ($results->getPaths() as $result) { - $data = $result->getLastCommitData(); - if ($data) { - if ($data->getCommitDetail('authorPHID')) { - $phids[$data->getCommitDetail('authorPHID')] = true; - } - } - } - - $phids = array_keys($phids); - $handles = $this->loadViewerHandles($phids); - $browse_table = id(new DiffusionBrowseTableView()) ->setDiffusionRequest($drequest) - ->setHandles($handles) ->setPaths($results->getPaths()) ->setUser($request->getUser()); diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php index ae785cff9d..ad3518b81e 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php @@ -2,7 +2,6 @@ final class DiffusionRepositoryController extends DiffusionController { - private $historyFuture; private $browseFuture; private $branchButton = null; private $branchFuture; @@ -191,15 +190,6 @@ final class DiffusionRepositoryController extends DiffusionController { $path = $drequest->getPath(); $futures = array(); - $this->historyFuture = $this->callConduitMethod( - 'diffusion.historyquery', - array( - 'commit' => $commit, - 'path' => $path, - 'offset' => 0, - 'limit' => 15, - )); - $futures[] = $this->historyFuture; $browse_pager = id(new PHUIPagerView()) ->readFromRequest($request); @@ -230,32 +220,8 @@ final class DiffusionRepositoryController extends DiffusionController { // Just resolve all the futures before continuing. } - $phids = array(); $content = array(); - try { - $history_results = $this->historyFuture->resolve(); - $history = DiffusionPathChange::newFromConduit( - $history_results['pathChanges']); - - foreach ($history as $item) { - $data = $item->getCommitData(); - if ($data) { - if ($data->getCommitDetail('authorPHID')) { - $phids[$data->getCommitDetail('authorPHID')] = true; - } - if ($data->getCommitDetail('committerPHID')) { - $phids[$data->getCommitDetail('committerPHID')] = true; - } - } - } - $history_exception = null; - } catch (Exception $ex) { - $history_results = null; - $history = null; - $history_exception = $ex; - } - try { $browse_results = $this->browseFuture->resolve(); $browse_results = DiffusionBrowseResultSet::newFromConduit( @@ -264,18 +230,6 @@ final class DiffusionRepositoryController extends DiffusionController { $browse_paths = $browse_results->getPaths(); $browse_paths = $browse_pager->sliceResults($browse_paths); - foreach ($browse_paths as $item) { - $data = $item->getLastCommitData(); - if ($data) { - if ($data->getCommitDetail('authorPHID')) { - $phids[$data->getCommitDetail('authorPHID')] = true; - } - if ($data->getCommitDetail('committerPHID')) { - $phids[$data->getCommitDetail('committerPHID')] = true; - } - } - } - $browse_exception = null; } catch (Exception $ex) { $browse_results = null; @@ -283,9 +237,6 @@ final class DiffusionRepositoryController extends DiffusionController { $browse_exception = $ex; } - $phids = array_keys($phids); - $handles = $this->loadViewerHandles($phids); - if ($browse_results) { $readme = $this->renderDirectoryReadme($browse_results); } else { @@ -296,7 +247,6 @@ final class DiffusionRepositoryController extends DiffusionController { $browse_results, $browse_paths, $browse_exception, - $handles, $browse_pager); if ($readme) { @@ -524,7 +474,6 @@ final class DiffusionRepositoryController extends DiffusionController { $browse_results, $browse_paths, $browse_exception, - array $handles, PHUIPagerView $pager) { require_celerity_resource('diffusion-icons-css'); @@ -547,8 +496,7 @@ final class DiffusionRepositoryController extends DiffusionController { $browse_table = id(new DiffusionBrowseTableView()) ->setUser($viewer) - ->setDiffusionRequest($drequest) - ->setHandles($handles); + ->setDiffusionRequest($drequest); if ($browse_paths) { $browse_table->setPaths($browse_paths); } else { diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php index 9503a9e386..50f1d34da1 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php @@ -23,7 +23,7 @@ final class DiffusionRepositoryEditDangerousController ->appendParagraph( pht( 'This repository can not be protected from dangerous changes '. - 'because Phabricator does not control what users are allowed '. + 'because this server does not control what users are allowed '. 'to push to it.')) ->addCancelButton($panel_uri); } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditEnormousController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditEnormousController.php index 11a3ee3736..3e0b56a758 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditEnormousController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditEnormousController.php @@ -23,7 +23,7 @@ final class DiffusionRepositoryEditEnormousController ->appendParagraph( pht( 'This repository can not be protected from enormous changes '. - 'because Phabricator does not control what users are allowed '. + 'because this server does not control what users are allowed '. 'to push to it.')) ->addCancelButton($panel_uri); } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php index dd95654302..88ce864f9a 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php @@ -45,7 +45,7 @@ final class DiffusionRepositoryEditUpdateController ->setTitle(pht('Update Repository Now')) ->appendParagraph( pht( - 'Normally, Phabricator automatically updates repositories '. + 'Normally, repositories are automatically updated '. 'based on how much time has elapsed since the last commit. '. 'This helps reduce load if you have a large number of mostly '. 'inactive repositories, which is common.')) @@ -57,7 +57,7 @@ final class DiffusionRepositoryEditUpdateController 'repository.')) ->appendParagraph( pht( - 'To learn more about how Phabricator updates repositories, '. + 'To learn more about how repositories are updated, '. 'read %s in the documentation.', $doc_link)) ->addCancelButton($panel_uri) diff --git a/src/applications/diffusion/controller/DiffusionRepositoryURICredentialController.php b/src/applications/diffusion/controller/DiffusionRepositoryURICredentialController.php index 96b97673ce..63763a6c04 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryURICredentialController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryURICredentialController.php @@ -38,10 +38,10 @@ final class DiffusionRepositoryURICredentialController ->setTitle(pht('Builtin URIs Do Not Use Credentials')) ->appendParagraph( pht( - 'You can not set a credential for builtin URIs which Phabricator '. - 'hosts and serves. Phabricator does not fetch from these URIs or '. - 'push to these URIs, and does not need credentials to '. - 'authenticate any activity against them.')) + 'You can not set a credential for builtin URIs which this '. + 'server hosts. These URIs are not fetched from or pushed to, '. + 'and credentials are not required to authenticate any '. + 'activity against them.')) ->addCancelButton($view_uri); } diff --git a/src/applications/diffusion/controller/DiffusionServeController.php b/src/applications/diffusion/controller/DiffusionServeController.php index d0d897e1f6..3040328fbf 100644 --- a/src/applications/diffusion/controller/DiffusionServeController.php +++ b/src/applications/diffusion/controller/DiffusionServeController.php @@ -421,7 +421,7 @@ final class DiffusionServeController extends DiffusionController { $result = new PhabricatorVCSResponse( 500, pht( - 'Phabricator does not support HTTP access to Subversion '. + 'This server does not support HTTP access to Subversion '. 'repositories.')); break; default: diff --git a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php index a026e176fc..07213afd2b 100644 --- a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php @@ -217,7 +217,7 @@ final class DiffusionRepositoryEditEngine $permanent_value = $object->getPermanentRefRules(); $automation_instructions = pht( - "Configure **Repository Automation** to allow Phabricator to ". + "Configure **Repository Automation** to allow this server to ". "write to this repository.". "\n\n". "IMPORTANT: This feature is new, experimental, and not supported. ". @@ -234,7 +234,7 @@ final class DiffusionRepositoryEditEngine $subpath_instructions = pht( 'If you want to import only part of a repository, like `trunk/`, '. - 'you can set a path in **Import Only**. Phabricator will ignore '. + 'you can set a path in **Import Only**. The import process will ignore '. 'commits which do not affect this path.'); $filesize_warning = null; @@ -256,7 +256,7 @@ final class DiffusionRepositoryEditEngine $track_instructions = pht( 'WARNING: The "Track Only" feature is deprecated. Use "Fetch Refs" '. 'and "Permanent Refs" instead. This feature will be removed in a '. - 'future version of Phabricator.'); + 'future version of this software.'); return array( id(new PhabricatorSelectEditField()) diff --git a/src/applications/diffusion/herald/HeraldCommitAdapter.php b/src/applications/diffusion/herald/HeraldCommitAdapter.php index b8ebf9c022..af7c757cd9 100644 --- a/src/applications/diffusion/herald/HeraldCommitAdapter.php +++ b/src/applications/diffusion/herald/HeraldCommitAdapter.php @@ -267,6 +267,11 @@ final class HeraldCommitAdapter $raw = $diff_file->loadFileData(); + // See T13667. This happens when a commit is empty and affects no files. + if (!strlen($raw)) { + return false; + } + $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($raw); @@ -290,6 +295,10 @@ final class HeraldCommitAdapter } } + if ($this->commitDiff === false) { + return array(); + } + if ($this->commitDiff instanceof Exception) { $ex = $this->commitDiff; $ex_class = get_class($ex); diff --git a/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php index a795c955a7..c4ce110c5d 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php @@ -130,19 +130,19 @@ final class DiffusionRepositoryURIsManagementPanel $messages = array(); if ($repository->isHosted()) { if ($is_new) { - $host_message = pht('Phabricator will host this repository.'); + $host_message = pht('This repository will be hosted.'); } else { - $host_message = pht('Phabricator is hosting this repository.'); + $host_message = pht('This repository is observed.'); } $messages[] = $host_message; } else { if ($is_new) { $observe_message = pht( - 'Phabricator will observe a remote repository.'); + 'This repository will be observed.'); } else { $observe_message = pht( - 'This repository is hosted remotely. Phabricator is observing it.'); + 'This remote repository is being observed.'); } $messages[] = $observe_message; diff --git a/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php b/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php index 789adfbf57..3b6d0c0f6b 100644 --- a/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php +++ b/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php @@ -114,7 +114,7 @@ final class DiffusionSetPasswordSettingsPanel extends PhabricatorSettingsPanel { ->setUser($viewer) ->appendRemarkupInstructions( pht( - 'To access repositories hosted by Phabricator over HTTP, you must '. + 'To access repositories hosted on this server over HTTP, you must '. 'set a version control password. This password should be unique.'. "\n\n". "This password applies to all repositories available over ". diff --git a/src/applications/diffusion/protocol/DiffusionCommandEngine.php b/src/applications/diffusion/protocol/DiffusionCommandEngine.php index a0f1adaf1f..db9d930151 100644 --- a/src/applications/diffusion/protocol/DiffusionCommandEngine.php +++ b/src/applications/diffusion/protocol/DiffusionCommandEngine.php @@ -117,12 +117,16 @@ abstract class DiffusionCommandEngine extends Phobject { return $this->sudoAsDaemon; } + protected function shouldAlwaysSudo() { + return false; + } + public function newFuture() { $argv = $this->newCommandArgv(); $env = $this->newCommandEnvironment(); $is_passthru = $this->getPassthru(); - if ($this->getSudoAsDaemon()) { + if ($this->getSudoAsDaemon() || $this->shouldAlwaysSudo()) { $command = call_user_func_array('csprintf', $argv); $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command); $argv = array('%C', $command); diff --git a/src/applications/diffusion/protocol/DiffusionGitCommandEngine.php b/src/applications/diffusion/protocol/DiffusionGitCommandEngine.php index 995e156d8e..6bc941a6b6 100644 --- a/src/applications/diffusion/protocol/DiffusionGitCommandEngine.php +++ b/src/applications/diffusion/protocol/DiffusionGitCommandEngine.php @@ -13,6 +13,20 @@ final class DiffusionGitCommandEngine return array($pattern, $argv); } + protected function shouldAlwaysSudo() { + + // See T13673. In Git, always try to use "sudo" to execute commands as the + // daemon user (if such a user is configured), because Git 2.35.2 and newer + // (and some older versions of Git with backported security patches) refuse + // to execute if the top level repository directory is not owned by the + // current user. + + // Previously, we used "sudo" only when performing writes to the + // repository directory. + + return true; + } + protected function newCustomEnvironment() { $env = array(); diff --git a/src/applications/diffusion/protocol/DiffusionMercurialCommandEngine.php b/src/applications/diffusion/protocol/DiffusionMercurialCommandEngine.php index 03705ad49d..dc898f81d4 100644 --- a/src/applications/diffusion/protocol/DiffusionMercurialCommandEngine.php +++ b/src/applications/diffusion/protocol/DiffusionMercurialCommandEngine.php @@ -71,12 +71,36 @@ final class DiffusionMercurialCommandEngine // // Separately, it may fail to write to a different branch cache, and may // encounter issues reading the branch cache. + // + // When Mercurial repositories are hosted on external systems with + // multi-user environments it's possible that the branch cache is computed + // on a revision which does not end up being published. When this happens it + // will recompute the cache but also print out "invalid branch cache". + // + // https://www.mercurial-scm.org/pipermail/mercurial/2014-June/047239.html + // + // When observing a repository which uses largefiles, the debug output may + // also contain extraneous output about largefile changes. + // + // At some point Mercurial added/improved support for pager used when + // command output is large. It includes printing out debug information that + // the pager is being started for a command. This seems to happen despite + // the output of the command being piped/read from another process. + // + // When printing color output Mercurial may run into some issue with the + // terminal info. This should never happen in Phabricator since color + // output should be turned off, however in the event it shows up we should + // filter it out anyways. $ignore = array( 'ignoring untrusted configuration option', "couldn't write revision branch cache:", "couldn't write branch cache:", 'invalid branchheads cache', + 'invalid branch cache', + 'updated patterns: .hglf', + 'starting pager for command', + 'no terminfo entry for', ); foreach ($ignore as $key => $pattern) { diff --git a/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php b/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php index 0372e44f4b..dfc86e5f56 100644 --- a/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php +++ b/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php @@ -273,9 +273,10 @@ final class DiffusionRepositoryClusterEngine extends Phobject { throw new Exception( pht( 'Repository "%s" exists on more than one device, but no device '. - 'has any repository version information. Phabricator can not '. - 'guess which copy of the existing data is authoritative. Promote '. - 'a device or see "Ambiguous Leaders" in the documentation.', + 'has any repository version information. There is no way for the '. + 'software to determine which copy of the existing data is '. + 'authoritative. Promote a device or see "Ambiguous Leaders" in '. + 'the documentation.', $repository->getDisplayName())); } diff --git a/src/applications/diffusion/protocol/__tests__/DiffusionMercurialCommandEngineTests.php b/src/applications/diffusion/protocol/__tests__/DiffusionMercurialCommandEngineTests.php new file mode 100644 index 0000000000..3ec09008cf --- /dev/null +++ b/src/applications/diffusion/protocol/__tests__/DiffusionMercurialCommandEngineTests.php @@ -0,0 +1,89 @@ + '', + + "quack\n" => "quack\n", + + "ignoring untrusted configuration option x.y = z\nquack\n" => + "quack\n", + + "ignoring untrusted configuration option x.y = z\n". + "ignoring untrusted configuration option x.y = z\n". + "quack\n" => + "quack\n", + + "ignoring untrusted configuration option x.y = z\n". + "ignoring untrusted configuration option x.y = z\n". + "ignoring untrusted configuration option x.y = z\n". + "quack\n" => + "quack\n", + + "quack\n". + "ignoring untrusted configuration option x.y = z\n". + "ignoring untrusted configuration option x.y = z\n". + "ignoring untrusted configuration option x.y = z\n" => + "quack\n", + + "ignoring untrusted configuration option x.y = z\n". + "ignoring untrusted configuration option x.y = z\n". + "duck\n". + "ignoring untrusted configuration option x.y = z\n". + "ignoring untrusted configuration option x.y = z\n". + "bread\n". + "ignoring untrusted configuration option x.y = z\n". + "quack\n" => + "duck\nbread\nquack\n", + + "ignoring untrusted configuration option x.y = z\n". + "duckignoring untrusted configuration option x.y = z\n". + "quack" => + 'duckquack', + ); + + foreach ($map as $input => $expect) { + $actual = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( + $input); + $this->assertEqual($expect, $actual, $input); + } + + // Output that should be filtered out from the results + $output = + "ignoring untrusted configuration option\n". + "couldn't write revision branch cache:\n". + "couldn't write branch cache: blah blah blah\n". + "invalid branchheads cache\n". + "invalid branch cache (served): tip differs\n". + "starting pager for command 'log'\n". + "updated patterns: ". + ".hglf/project/src/a/b/c/SomeClass.java, ". + "project/src/a/b/c/SomeClass.java\n". + "no terminfo entry for sitm\n"; + + $filtered_output = + DiffusionMercurialCommandEngine::filterMercurialDebugOutput($output); + + $this->assertEqual('', $filtered_output); + + // The output that should make it through the filtering + $output = + "0b33a9e5ceedba14b03214f743957357d7bb46a9;694". + ":8b39f63eb209dd2bdfd4bd3d0721a9e38d75a6d3". + "-1:0000000000000000000000000000000000000000\n". + "8b39f63eb209dd2bdfd4bd3d0721a9e38d75a6d3;693". + ":165bce9ce4ccc97024ba19ed5a22f6a066fa6844". + "-1:0000000000000000000000000000000000000000\n". + "165bce9ce4ccc97024ba19ed5a22f6a066fa6844;692:". + "2337bc9e3cf212b3b386b5197801b1c81db64920". + "-1:0000000000000000000000000000000000000000\n"; + + $filtered_output = + DiffusionMercurialCommandEngine::filterMercurialDebugOutput($output); + + $this->assertEqual($output, $filtered_output); + } + +} diff --git a/src/applications/diffusion/query/DiffusionCommitHintQuery.php b/src/applications/diffusion/query/DiffusionCommitHintQuery.php index bfd8045131..c0794a4387 100644 --- a/src/applications/diffusion/query/DiffusionCommitHintQuery.php +++ b/src/applications/diffusion/query/DiffusionCommitHintQuery.php @@ -56,10 +56,6 @@ final class DiffusionCommitHintQuery $this->commitMap = array(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php b/src/applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php index 60eeb31c4f..5fa9c39f73 100644 --- a/src/applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php +++ b/src/applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php @@ -6,11 +6,26 @@ final class DiffusionMercurialBlameQuery extends DiffusionBlameQuery { $repository = $request->getRepository(); $commit = $request->getCommit(); - // NOTE: We're using "--debug" to make "--changeset" give us the full - // commit hashes. + // NOTE: Using "--template" or "--debug" to get the full commit hashes. + $hg_analyzer = PhutilBinaryAnalyzer::getForBinary('hg'); + if ($hg_analyzer->isMercurialAnnotateTemplatesAvailable()) { + // See `hg help annotate --verbose` for more info on the template format. + // Use array of arguments so the template line does not need wrapped in + // quotes. + $template = array( + '--template', + "{lines % '{node}: {line}'}", + ); + } else { + $template = array( + '--debug', + '--changeset', + ); + } return $repository->getLocalCommandFuture( - 'annotate --debug --changeset --rev %s -- %s', + 'annotate %Ls --rev %s -- %s', + $template, $commit, $path); } @@ -26,6 +41,21 @@ final class DiffusionMercurialBlameQuery extends DiffusionBlameQuery { $lines = phutil_split_lines($stdout); foreach ($lines as $line) { + // If the `--debug` flag was used above instead of `--template` then + // there's a good change additional output was included which is not + // relevant to the information we want. It should be safe to call this + // regardless of whether we used `--debug` or `--template` so there isn't + // a need to track which argument was used. + $line = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( + $line); + + // Just in case new versions of Mercurial add arbitrary output when using + // the `--debug`, do a quick sanity check that this line is formatted in + // a way we're expecting. + if (strpos($line, ':') === false) { + phlog(pht('Unexpected output from hg annotate: %s', $line)); + continue; + } list($commit) = explode(':', $line, 2); $result[] = $commit; } diff --git a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelParentsQuery.php b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelParentsQuery.php index 193f45a029..9e6c2425cb 100644 --- a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelParentsQuery.php +++ b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelParentsQuery.php @@ -47,23 +47,23 @@ final class DiffusionLowLevelParentsQuery private function loadMercurialParents() { $repository = $this->getRepository(); - list($stdout) = $repository->execxLocalCommand( - 'log --debug --limit 1 --template={parents} --rev %s', - $this->identifier); + $hg_analyzer = PhutilBinaryAnalyzer::getForBinary('hg'); + if ($hg_analyzer->isMercurialTemplatePnodeAvailable()) { + $hg_log_template = '{p1.node} {p2.node}'; + } else { + $hg_log_template = '{p1node} {p2node}'; + } - $stdout = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( - $stdout); + list($stdout) = $repository->execxLocalCommand( + 'log --limit 1 --template %s --rev %s', + $hg_log_template, + $this->identifier); $hashes = preg_split('/\s+/', trim($stdout)); foreach ($hashes as $key => $value) { - // Mercurial parents look like "23:ad9f769d6f786fad9f76d9a" -- we want - // to strip out the local rev part. - list($local, $global) = explode(':', $value); - $hashes[$key] = $global; - - // With --debug we get 40-character hashes but also get the "000000..." - // hash for missing parents; ignore it. - if (preg_match('/^0+$/', $global)) { + // We get 40-character hashes but also get the "000000..." hash for + // missing parents; ignore it. + if (preg_match('/^0+\z/', $value)) { unset($hashes[$key]); } } diff --git a/src/applications/diffusion/query/pathchange/DiffusionPathChangeQuery.php b/src/applications/diffusion/query/pathchange/DiffusionPathChangeQuery.php index 850c06f105..98798582a7 100644 --- a/src/applications/diffusion/query/pathchange/DiffusionPathChangeQuery.php +++ b/src/applications/diffusion/query/pathchange/DiffusionPathChangeQuery.php @@ -88,7 +88,12 @@ final class DiffusionPathChangeQuery extends Phobject { $change->setFileType($raw_change['fileType']); $change->setCommitIdentifier($commit->getCommitIdentifier()); - $change->setTargetPath(ltrim($raw_change['targetPathName'], '/')); + $target_path = $raw_change['targetPathName']; + if ($target_path !== null) { + $target_path = ltrim($target_path, '/'); + } + $change->setTargetPath($target_path); + $change->setTargetCommitIdentifier($raw_change['targetCommitIdentifier']); $id = $raw_change['pathID']; diff --git a/src/applications/diffusion/request/DiffusionRequest.php b/src/applications/diffusion/request/DiffusionRequest.php index 49ea305908..4bcb4d3bc5 100644 --- a/src/applications/diffusion/request/DiffusionRequest.php +++ b/src/applications/diffusion/request/DiffusionRequest.php @@ -207,7 +207,7 @@ abstract class DiffusionRequest extends Phobject { */ private function initializeFromDictionary(array $data) { $blob = idx($data, 'blob'); - if (strlen($blob)) { + if (phutil_nonempty_string($blob)) { $blob = self::parseRequestBlob($blob, $this->supportsBranches()); $data = $blob + $data; } @@ -518,12 +518,14 @@ abstract class DiffusionRequest extends Phobject { $result['path'] = $blob; } - $parts = explode('/', $result['path']); - foreach ($parts as $part) { - // Prevent any hyjinx since we're ultimately shipping this to the - // filesystem under a lot of workflows. - if ($part == '..') { - throw new Exception(pht('Invalid path URI.')); + if ($result['path'] !== null) { + $parts = explode('/', $result['path']); + foreach ($parts as $part) { + // Prevent any hyjinx since we're ultimately shipping this to the + // filesystem under a lot of workflows. + if ($part == '..') { + throw new Exception(pht('Invalid path URI.')); + } } } @@ -561,7 +563,7 @@ abstract class DiffusionRequest extends Phobject { throw new DiffusionSetupException( pht( 'The working copy for this repository ("%s") has not been cloned yet '. - 'on this machine ("%s"). Make sure you havestarted the Phabricator '. + 'on this machine ("%s"). Make sure you have started the '. 'daemons. If this problem persists for longer than a clone should '. 'take, check the daemon logs (in the Daemon Console) to see if there '. 'were errors cloning the repository. Consult the "Diffusion User '. diff --git a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php index 358418f44c..0645e76356 100644 --- a/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php +++ b/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php @@ -117,7 +117,7 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow { throw new Exception( pht( 'Unable to determine the username to connect with when trying '. - 'to proxy an SSH request within the Phabricator cluster.')); + 'to proxy an SSH request within the cluster.')); } $port = $uri->getPort(); diff --git a/src/applications/diffusion/view/DiffusionBrowseTableView.php b/src/applications/diffusion/view/DiffusionBrowseTableView.php index 57af6a0bdf..36785ca613 100644 --- a/src/applications/diffusion/view/DiffusionBrowseTableView.php +++ b/src/applications/diffusion/view/DiffusionBrowseTableView.php @@ -3,7 +3,6 @@ final class DiffusionBrowseTableView extends DiffusionView { private $paths; - private $handles = array(); public function setPaths(array $paths) { assert_instances_of($paths, 'DiffusionRepositoryPath'); @@ -11,12 +10,6 @@ final class DiffusionBrowseTableView extends DiffusionView { return $this; } - public function setHandles(array $handles) { - assert_instances_of($handles, 'PhabricatorObjectHandle'); - $this->handles = $handles; - return $this; - } - public function render() { $request = $this->getDiffusionRequest(); $repository = $request->getRepository(); @@ -29,7 +22,6 @@ final class DiffusionBrowseTableView extends DiffusionView { $need_pull = array(); $rows = array(); - $show_edit = false; foreach ($this->paths as $path) { $full_path = $base_path.$path->getPath(); diff --git a/src/applications/diviner/controller/DivinerMainController.php b/src/applications/diviner/controller/DivinerMainController.php index 400fb48d0f..21450698e6 100644 --- a/src/applications/diviner/controller/DivinerMainController.php +++ b/src/applications/diviner/controller/DivinerMainController.php @@ -48,18 +48,20 @@ final class DivinerMainController extends DivinerController { $document->appendChild($list); } else { $text = pht( - "(NOTE) **Looking for Phabricator documentation?** ". - "If you're looking for help and information about Phabricator, ". + "(NOTE) **Looking for documentation?** ". + "If you're looking for help and information about %s, ". "you can [[https://secure.phabricator.com/diviner/ | ". - "browse the public Phabricator documentation]] on the live site.\n\n". - "Diviner is the documentation generator used to build the ". - "Phabricator documentation.\n\n". + "browse the public %s documentation]] on the live site.\n\n". + "Diviner is the documentation generator used to build this ". + "documentation.\n\n". "You haven't generated any Diviner documentation books yet, so ". "there's nothing to show here. If you'd like to generate your own ". - "local copy of the Phabricator documentation and have it appear ". + "local copy of the documentation and have it appear ". "here, run this command:\n\n". " %s\n\n", - 'phabricator/ $ ./bin/diviner generate'); + PlatformSymbols::getPlatformServerName(), + PlatformSymbols::getPlatformServerName(), + '$ ./bin/diviner generate'); $text = new PHUIRemarkupView($viewer, $text); $document->appendChild($text); diff --git a/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php b/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php index e93c818378..64f2f17dd3 100644 --- a/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php +++ b/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php @@ -33,12 +33,14 @@ final class PhabricatorAsanaConfigOptions ->setSummary(pht('Optional Asana projects to use as application tags.')) ->setDescription( pht( - 'When Phabricator creates tasks in Asana, it can add the tasks '. + 'When %s creates tasks in Asana, it can add the tasks '. 'to Asana projects based on which application the corresponding '. - 'object in Phabricator comes from. For example, you can add code '. + 'object in %s comes from. For example, you can add code '. 'reviews in Asana to a "Differential" project.'. "\n\n". - 'NOTE: This feature is new and experimental.')), + 'NOTE: This feature is new and experimental.', + PlatformSymbols::getPlatformServerName(), + PlatformSymbols::getPlatformServerName())), ); } diff --git a/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php b/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php index bc794cc629..2a33206831 100644 --- a/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php +++ b/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php @@ -20,10 +20,6 @@ final class DoorkeeperExternalObjectQuery return new DoorkeeperExternalObject(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/doorkeeper/worker/DoorkeeperAsanaFeedWorker.php b/src/applications/doorkeeper/worker/DoorkeeperAsanaFeedWorker.php index bbc619ae51..8648f5d7f9 100644 --- a/src/applications/doorkeeper/worker/DoorkeeperAsanaFeedWorker.php +++ b/src/applications/doorkeeper/worker/DoorkeeperAsanaFeedWorker.php @@ -512,9 +512,10 @@ final class DoorkeeperAsanaFeedWorker extends DoorkeeperFeedWorker { private function getSynchronizationWarning() { return pht( "\xE2\x9A\xA0 DO NOT EDIT THIS TASK \xE2\x9A\xA0\n". - "\xE2\x98\xA0 Your changes will not be reflected in Phabricator.\n". + "\xE2\x98\xA0 Your changes will not be reflected in %s.\n". "\xE2\x98\xA0 Your changes will be destroyed the next time state ". - "is synchronized."); + "is synchronized.", + PlatformSymbols::getPlatformServerName()); } private function lookupAsanaUserIDs($all_phids) { diff --git a/src/applications/doorkeeper/worker/DoorkeeperFeedWorker.php b/src/applications/doorkeeper/worker/DoorkeeperFeedWorker.php index 404ee348f7..8bbcc0f4fa 100644 --- a/src/applications/doorkeeper/worker/DoorkeeperFeedWorker.php +++ b/src/applications/doorkeeper/worker/DoorkeeperFeedWorker.php @@ -158,7 +158,7 @@ abstract class DoorkeeperFeedWorker extends FeedPushWorker { */ final protected function doWork() { if (PhabricatorEnv::getEnvConfig('phabricator.silent')) { - $this->log("%s\n", pht('Phabricator is running in silent mode.')); + $this->log("%s\n", pht('This software is running in silent mode.')); return; } diff --git a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php index 88bc4d935a..27ba6e623f 100644 --- a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php +++ b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php @@ -329,6 +329,47 @@ abstract class DrydockBlueprintImplementation extends Phobject { ->execute(); } + + /** + * Get all the @{class:DrydockBlueprintImplementation}s which can possibly + * build a resource to satisfy a lease. + * + * This method returns blueprints which might, at some time, be able to + * build a resource which can satisfy the lease. They may not be able to + * build that resource right now. + * + * @param DrydockLease Requested lease. + * @return list List of qualifying blueprint + * implementations. + */ + public static function getAllForAllocatingLease( + DrydockLease $lease) { + + $impls = self::getAllBlueprintImplementations(); + + $keep = array(); + foreach ($impls as $key => $impl) { + // Don't use disabled blueprint types. + if (!$impl->isEnabled()) { + continue; + } + + // Don't use blueprint types which can't allocate the correct kind of + // resource. + if ($impl->getType() != $lease->getResourceType()) { + continue; + } + + if (!$impl->canAnyBlueprintEverAllocateResourceForLease($lease)) { + continue; + } + + $keep[$key] = $impl; + } + + return $keep; + } + public static function getNamedImplementation($class) { return idx(self::getAllBlueprintImplementations(), $class); } @@ -465,28 +506,21 @@ abstract class DrydockBlueprintImplementation extends Phobject { protected function shouldLimitAllocatingPoolSize( DrydockBlueprint $blueprint) { - // TODO: If this mechanism sticks around, these values should be - // configurable by the blueprint implementation. - // Limit on total number of active resources. $total_limit = $this->getConcurrentResourceLimit($blueprint); - - // Always allow at least this many allocations to be in flight at once. - $min_allowed = 1; - - // Allow this fraction of allocating resources as a fraction of active - // resources. - $growth_factor = 0.25; + if ($total_limit === null) { + return false; + } $resource = new DrydockResource(); - $conn_r = $resource->establishConnection('r'); + $conn = $resource->establishConnection('r'); $counts = queryfx_all( - $conn_r, - 'SELECT status, COUNT(*) N FROM %T + $conn, + 'SELECT status, COUNT(*) N FROM %R WHERE blueprintPHID = %s AND status != %s GROUP BY status', - $resource->getTableName(), + $resource, $blueprint->getPHID(), DrydockResourceStatus::STATUS_DESTROYED); $counts = ipull($counts, 'N', 'status'); @@ -498,29 +532,12 @@ abstract class DrydockBlueprintImplementation extends Phobject { // If we're at the limit on total active resources, limit additional // allocations. - if ($total_limit !== null) { - $n_total = ($n_alloc + $n_active + $n_broken + $n_released); - if ($n_total >= $total_limit) { - return true; - } + $n_total = ($n_alloc + $n_active + $n_broken + $n_released); + if ($n_total >= $total_limit) { + return true; } - // If the number of in-flight allocations is fewer than the minimum number - // of allowed allocations, don't impose a limit. - if ($n_alloc < $min_allowed) { - return false; - } - - $allowed_alloc = (int)ceil($n_active * $growth_factor); - - // If the number of in-flight allocation is fewer than the number of - // allowed allocations according to the pool growth factor, don't impose - // a limit. - if ($n_alloc < $allowed_alloc) { - return false; - } - - return true; + return false; } } diff --git a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php index d82f5c2c15..2824444040 100644 --- a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php +++ b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php @@ -43,11 +43,6 @@ final class DrydockWorkingCopyBlueprintImplementation return false; } - // TODO: If we have a pending resource which is compatible with the - // configuration for this lease, prevent a new allocation? Otherwise the - // queue can fill up with copies of requests from the same lease. But - // maybe we can deal with this with "pre-leasing"? - return true; } @@ -135,14 +130,7 @@ final class DrydockWorkingCopyBlueprintImplementation ->setAllowedBlueprintPHIDs($blueprint_phids); $resource->setAttribute('host.leasePHID', $host_lease->getPHID()); - $map = $lease->getAttribute('repositories.map'); - foreach ($map as $key => $value) { - $map[$key] = array_select_keys( - $value, - array( - 'phid', - )); - } + $map = $this->getWorkingCopyRepositoryMap($lease); $resource->setAttribute('repositories.map', $map); $slot_lock = $this->getConcurrentResourceLimitSlotLock($blueprint); @@ -157,6 +145,44 @@ final class DrydockWorkingCopyBlueprintImplementation return $resource; } + private function getWorkingCopyRepositoryMap(DrydockLease $lease) { + $attribute = 'repositories.map'; + $map = $lease->getAttribute($attribute); + + // TODO: Leases should validate their attributes more formally. + + if (!is_array($map) || !$map) { + $message = array(); + if ($map === null) { + $message[] = pht( + 'Working copy lease is missing required attribute "%s".', + $attribute); + } else { + $message[] = pht( + 'Working copy lease has invalid attribute "%s".', + $attribute); + } + + $message[] = pht( + 'Attribute "repositories.map" should be a map of repository '. + 'specifications.'); + + $message = implode("\n\n", $message); + + throw new Exception($message); + } + + foreach ($map as $key => $value) { + $map[$key] = array_select_keys( + $value, + array( + 'phid', + )); + } + + return $map; + } + public function activateResource( DrydockBlueprint $blueprint, DrydockResource $resource) { diff --git a/src/applications/drydock/logtype/DrydockLeaseWaitingForActivationLogType.php b/src/applications/drydock/logtype/DrydockLeaseWaitingForActivationLogType.php new file mode 100644 index 0000000000..7ab2ee7208 --- /dev/null +++ b/src/applications/drydock/logtype/DrydockLeaseWaitingForActivationLogType.php @@ -0,0 +1,23 @@ +renderHandleList($resource_phids)); + } + +} diff --git a/src/applications/drydock/logtype/DrydockLeaseWaitingForReclamationLogType.php b/src/applications/drydock/logtype/DrydockLeaseWaitingForReclamationLogType.php new file mode 100644 index 0000000000..9f99ad720b --- /dev/null +++ b/src/applications/drydock/logtype/DrydockLeaseWaitingForReclamationLogType.php @@ -0,0 +1,23 @@ +renderHandleList($resource_phids)); + } + +} diff --git a/src/applications/drydock/management/DrydockManagementCommandWorkflow.php b/src/applications/drydock/management/DrydockManagementCommandWorkflow.php index ae0bd711b2..1047553883 100644 --- a/src/applications/drydock/management/DrydockManagementCommandWorkflow.php +++ b/src/applications/drydock/management/DrydockManagementCommandWorkflow.php @@ -27,8 +27,7 @@ final class DrydockManagementCommandWorkflow if (!$lease_id) { throw new PhutilArgumentUsageException( pht( - 'Use %s to specify a lease.', - '--lease')); + 'Use "--lease" to specify a lease.')); } $argv = $args->getArg('argv'); diff --git a/src/applications/drydock/management/DrydockManagementLeaseWorkflow.php b/src/applications/drydock/management/DrydockManagementLeaseWorkflow.php index 08f33c6b5f..af85f6bbec 100644 --- a/src/applications/drydock/management/DrydockManagementLeaseWorkflow.php +++ b/src/applications/drydock/management/DrydockManagementLeaseWorkflow.php @@ -26,6 +26,18 @@ final class DrydockManagementLeaseWorkflow 'JSON file with lease attributes. Use "-" to read attributes '. 'from stdin.'), ), + array( + 'name' => 'count', + 'param' => 'N', + 'default' => 1, + 'help' => pht('Lease a given number of identical resources.'), + ), + array( + 'name' => 'blueprint', + 'param' => 'identifier', + 'repeat' => true, + 'help' => pht('Lease resources from a specific blueprint.'), + ), )); } @@ -33,30 +45,36 @@ final class DrydockManagementLeaseWorkflow $viewer = $this->getViewer(); $resource_type = $args->getArg('type'); - if (!$resource_type) { + if (!phutil_nonempty_string($resource_type)) { throw new PhutilArgumentUsageException( pht( - 'Specify a resource type with `%s`.', - '--type')); + 'Specify a resource type with "--type".')); } $until = $args->getArg('until'); - if (strlen($until)) { + if (phutil_nonempty_string($until)) { $until = strtotime($until); if ($until <= 0) { throw new PhutilArgumentUsageException( pht( - 'Unable to parse argument to "%s".', - '--until')); + 'Unable to parse argument to "--until".')); } } + $count = $args->getArgAsInteger('count'); + if ($count < 1) { + throw new PhutilArgumentUsageException( + pht( + 'Value provided to "--count" must be a nonzero, positive '. + 'number.')); + } + $attributes_file = $args->getArg('attributes'); - if (strlen($attributes_file)) { + if (phutil_nonempty_string($attributes_file)) { if ($attributes_file == '-') { echo tsprintf( "%s\n", - 'Reading JSON attributes from stdin...'); + pht('Reading JSON attributes from stdin...')); $data = file_get_contents('php://stdin'); } else { $data = Filesystem::readFile($attributes_file); @@ -67,38 +85,46 @@ final class DrydockManagementLeaseWorkflow $attributes = array(); } - $lease = id(new DrydockLease()) - ->setResourceType($resource_type); - - $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID(); - $lease->setAuthorizingPHID($drydock_phid); - - if ($attributes) { - $lease->setAttributes($attributes); + $filter_identifiers = $args->getArg('blueprint'); + if ($filter_identifiers) { + $filter_blueprints = $this->getBlueprintFilterMap($filter_identifiers); + } else { + $filter_blueprints = array(); } - // TODO: This is not hugely scalable, although this is a debugging workflow - // so maybe it's fine. Do we even need `bin/drydock lease` in the long run? - $all_blueprints = id(new DrydockBlueprintQuery()) - ->setViewer($viewer) - ->execute(); - $allowed_phids = mpull($all_blueprints, 'getPHID'); - if (!$allowed_phids) { - throw new Exception( - pht( - 'No blueprints exist which can plausibly allocate resources to '. - 'satisfy the requested lease.')); - } - $lease->setAllowedBlueprintPHIDs($allowed_phids); + $blueprint_phids = null; - if ($until) { - $lease->setUntil($until); - } + $leases = array(); + for ($idx = 0; $idx < $count; $idx++) { + $lease = id(new DrydockLease()) + ->setResourceType($resource_type); - // If something fatals or the user interrupts the process (for example, - // with "^C"), release the lease. We'll cancel this below, if the lease - // actually activates. - $lease->setReleaseOnDestruction(true); + $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID(); + $lease->setAuthorizingPHID($drydock_phid); + + if ($attributes) { + $lease->setAttributes($attributes); + } + + if ($blueprint_phids === null) { + $blueprint_phids = $this->newAllowedBlueprintPHIDs( + $lease, + $filter_blueprints); + } + + $lease->setAllowedBlueprintPHIDs($blueprint_phids); + + if ($until) { + $lease->setUntil($until); + } + + // If something fatals or the user interrupts the process (for example, + // with "^C"), release the lease. We'll cancel this below, if the lease + // actually activates. + $lease->setReleaseOnDestruction(true); + + $leases[] = $lease; + } // TODO: This would probably be better handled with PhutilSignalRouter, // but it currently doesn't route SIGINT. We're initializing it to setup @@ -107,29 +133,52 @@ final class DrydockManagementLeaseWorkflow pcntl_signal(SIGINT, array($this, 'didReceiveInterrupt')); $t_start = microtime(true); - $lease->queueForActivation(); + echo tsprintf( - "%s\n\n __%s__\n\n%s\n", - pht('Queued lease for activation:'), - PhabricatorEnv::getProductionURI($lease->getURI()), - pht('Waiting for daemons to activate lease...')); + "%s\n\n", + pht('Leases queued for activation:')); - $this->waitUntilActive($lease); + foreach ($leases as $lease) { + $lease->queueForActivation(); + + echo tsprintf( + " __%s__\n", + PhabricatorEnv::getProductionURI($lease->getURI())); + } + + echo tsprintf( + "\n%s\n\n", + pht('Waiting for daemons to activate leases...')); + + foreach ($leases as $lease) { + $this->waitUntilActive($lease); + } // Now that we've survived activation and the lease is good, make it // durable. - $lease->setReleaseOnDestruction(false); + foreach ($leases as $lease) { + $lease->setReleaseOnDestruction(false); + } + $t_end = microtime(true); echo tsprintf( - "%s\n\n %s\n\n%s\n", + "\n%s\n\n", pht( - 'Activation complete. This lease is permanent until manually '. - 'released with:'), - pht('$ ./bin/drydock release-lease --id %d', $lease->getID()), + 'Activation complete. Leases are permanent until manually '. + 'released with:')); + + foreach ($leases as $lease) { + echo tsprintf( + " %s\n", + pht('$ ./bin/drydock release-lease --id %d', $lease->getID())); + } + + echo tsprintf( + "\n%s\n", pht( - 'Lease activated in %sms.', + 'Leases activated in %sms.', new PhutilNumber((int)(($t_end - $t_start) * 1000)))); return 0; @@ -183,7 +232,8 @@ final class DrydockManagementLeaseWorkflow } echo tsprintf( - "<%s> %B\n", + "(Lease #%d) <%s> %B\n", + $lease->getID(), $type, $data); } @@ -218,4 +268,97 @@ final class DrydockManagementLeaseWorkflow } } + private function getBlueprintFilterMap(array $identifiers) { + $viewer = $this->getViewer(); + + $query = id(new DrydockBlueprintQuery()) + ->setViewer($viewer) + ->withIdentifiers($identifiers); + + $blueprints = $query->execute(); + $blueprints = mpull($blueprints, null, 'getPHID'); + + $map = $query->getIdentifierMap(); + + $seen = array(); + foreach ($identifiers as $identifier) { + if (!isset($map[$identifier])) { + throw new PhutilArgumentUsageException( + pht( + 'Blueprint "%s" could not be loaded. Try a blueprint ID or '. + 'PHID.', + $identifier)); + } + + $blueprint = $map[$identifier]; + + $blueprint_phid = $blueprint->getPHID(); + if (isset($seen[$blueprint_phid])) { + throw new PhutilArgumentUsageException( + pht( + 'Blueprint "%s" is specified more than once (as "%s" and "%s").', + $blueprint->getBlueprintName(), + $seen[$blueprint_phid], + $identifier)); + } + + $seen[$blueprint_phid] = true; + } + + return mpull($map, null, 'getPHID'); + } + + private function newAllowedBlueprintPHIDs( + DrydockLease $lease, + array $filter_blueprints) { + assert_instances_of($filter_blueprints, 'DrydockBlueprint'); + + $viewer = $this->getViewer(); + + $impls = DrydockBlueprintImplementation::getAllForAllocatingLease($lease); + + if (!$impls) { + throw new PhutilArgumentUsageException( + pht( + 'No known blueprint class can ever allocate the specified '. + 'lease. Check that the resource type is spelled correctly.')); + } + + $classes = array_keys($impls); + + $blueprints = id(new DrydockBlueprintQuery()) + ->setViewer($viewer) + ->withBlueprintClasses($classes) + ->withDisabled(false) + ->execute(); + + if (!$blueprints) { + throw new PhutilArgumentUsageException( + pht( + 'No enabled blueprints exist with a blueprint class that can '. + 'plausibly allocate resources to satisfy the requested lease.')); + } + + $phids = mpull($blueprints, 'getPHID'); + + if ($filter_blueprints) { + $allowed_map = array_fuse($phids); + $filter_map = mpull($filter_blueprints, null, 'getPHID'); + + foreach ($filter_map as $filter_phid => $blueprint) { + if (!isset($allowed_map[$filter_phid])) { + throw new PhutilArgumentUsageException( + pht( + 'Specified blueprint "%s" is not capable of satisfying the '. + 'configured lease.', + $blueprint->getBlueprintName())); + } + } + + $phids = mpull($filter_blueprints, 'getPHID'); + } + + return $phids; + } + } diff --git a/src/applications/drydock/management/DrydockManagementReleaseLeaseWorkflow.php b/src/applications/drydock/management/DrydockManagementReleaseLeaseWorkflow.php index 20af18ec21..0628f57b38 100644 --- a/src/applications/drydock/management/DrydockManagementReleaseLeaseWorkflow.php +++ b/src/applications/drydock/management/DrydockManagementReleaseLeaseWorkflow.php @@ -15,40 +15,71 @@ final class DrydockManagementReleaseLeaseWorkflow 'repeat' => true, 'help' => pht('Lease ID to release.'), ), + array( + 'name' => 'all', + 'help' => pht('Release all leases. Dangerous!'), + ), )); } public function execute(PhutilArgumentParser $args) { + $is_all = $args->getArg('all'); $ids = $args->getArg('id'); - if (!$ids) { + + if (!$ids && !$is_all) { throw new PhutilArgumentUsageException( pht( - 'Specify one or more lease IDs to release with "%s".', - '--id')); + 'Select which leases you want to release. See "--help" for '. + 'guidance.')); } $viewer = $this->getViewer(); - $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID(); - $leases = id(new DrydockLeaseQuery()) + $statuses = $this->getReleaseableLeaseStatuses(); + + $query = id(new DrydockLeaseQuery()) ->setViewer($viewer) - ->withIDs($ids) - ->execute(); + ->withStatuses(mpull($statuses, 'getKey')); - PhabricatorWorker::setRunAllTasksInProcess(true); - foreach ($ids as $id) { - $lease = idx($leases, $id); - if (!$lease) { - echo tsprintf( - "%s\n", - pht('Lease "%s" does not exist.', $id)); - continue; + if ($ids) { + $query->withIDs($ids); + } + + $leases = $query->execute(); + + if ($ids) { + $id_map = mpull($leases, null, 'getID'); + + foreach ($ids as $id) { + $lease = idx($id_map, $id); + if (!$lease) { + throw new PhutilArgumentUsageException( + pht('Lease "%s" does not exist.', $id)); + } } + $leases = array_select_keys($id_map, $ids); + } + + if (!$leases) { + echo tsprintf( + "%s\n", + pht('No leases selected for release.')); + + return 0; + } + + $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID(); + + PhabricatorWorker::setRunAllTasksInProcess(true); + + foreach ($leases as $lease) { if (!$lease->canRelease()) { echo tsprintf( "%s\n", - pht('Lease "%s" is not releasable.', $id)); + pht( + 'Lease "%s" is not releasable.', + $lease->getDisplayName())); continue; } @@ -62,9 +93,26 @@ final class DrydockManagementReleaseLeaseWorkflow echo tsprintf( "%s\n", - pht('Scheduled release of lease "%s".', $id)); + pht( + 'Scheduled release of lease "%s".', + $lease->getDisplayName())); } } + private function getReleaseableLeaseStatuses() { + $statuses = DrydockLeaseStatus::getAllStatuses(); + foreach ($statuses as $key => $status) { + $statuses[$key] = DrydockLeaseStatus::newStatusObject($status); + } + + foreach ($statuses as $key => $status) { + if (!$status->canRelease()) { + unset($statuses[$key]); + } + } + + return $statuses; + } + } diff --git a/src/applications/drydock/management/DrydockManagementReleaseResourceWorkflow.php b/src/applications/drydock/management/DrydockManagementReleaseResourceWorkflow.php index 01060a5325..afd826cbc0 100644 --- a/src/applications/drydock/management/DrydockManagementReleaseResourceWorkflow.php +++ b/src/applications/drydock/management/DrydockManagementReleaseResourceWorkflow.php @@ -15,41 +15,70 @@ final class DrydockManagementReleaseResourceWorkflow 'repeat' => true, 'help' => pht('Resource ID to release.'), ), + array( + 'name' => 'all', + 'help' => pht('Release all resources. Dangerous!'), + ), )); } public function execute(PhutilArgumentParser $args) { + $is_all = $args->getArg('all'); $ids = $args->getArg('id'); - if (!$ids) { + if (!$ids && !$is_all) { throw new PhutilArgumentUsageException( pht( - 'Specify one or more resource IDs to release with "%s".', - '--id')); + 'Specify which resources you want to release. See "--help" for '. + 'guidance.')); } $viewer = $this->getViewer(); - $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID(); + $statuses = $this->getReleaseableResourceStatuses(); - $resources = id(new DrydockResourceQuery()) + $query = id(new DrydockResourceQuery()) ->setViewer($viewer) - ->withIDs($ids) - ->execute(); + ->withStatuses(mpull($statuses, 'getKey')); - PhabricatorWorker::setRunAllTasksInProcess(true); - foreach ($ids as $id) { - $resource = idx($resources, $id); + if ($ids) { + $query->withIDs($ids); + } - if (!$resource) { - echo tsprintf( - "%s\n", - pht('Resource "%s" does not exist.', $id)); - continue; + $resources = $query->execute(); + + if ($ids) { + $id_map = mpull($resources, null, 'getID'); + + foreach ($ids as $id) { + $resource = idx($resources, $id); + + if (!$resource) { + throw new PhutilArgumentUsageException( + pht('Resource "%s" does not exist.', $id)); + } } + $resources = array_select_keys($id_map, $ids); + } + + if (!$resources) { + echo tsprintf( + "%s\n", + pht('No resources selected for release.')); + + return 0; + } + + $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID(); + + PhabricatorWorker::setRunAllTasksInProcess(true); + + foreach ($resources as $resource) { if (!$resource->canRelease()) { echo tsprintf( "%s\n", - pht('Resource "%s" is not releasable.', $id)); + pht( + 'Resource "%s" is not releasable.', + $resource->getDisplayName())); continue; } @@ -63,9 +92,26 @@ final class DrydockManagementReleaseResourceWorkflow echo tsprintf( "%s\n", - pht('Scheduled release of resource "%s".', $id)); + pht( + 'Scheduled release of resource "%s".', + $resource->getDisplayName())); } + return 0; } + private function getReleaseableResourceStatuses() { + $statuses = DrydockResourceStatus::getAllStatuses(); + foreach ($statuses as $key => $status) { + $statuses[$key] = DrydockResourceStatus::newStatusObject($status); + } + + foreach ($statuses as $key => $status) { + if (!$status->canRelease()) { + unset($statuses[$key]); + } + } + + return $statuses; + } } diff --git a/src/applications/drydock/operation/DrydockLandRepositoryOperation.php b/src/applications/drydock/operation/DrydockLandRepositoryOperation.php index acb48f6f0b..f44d6a1db2 100644 --- a/src/applications/drydock/operation/DrydockLandRepositoryOperation.php +++ b/src/applications/drydock/operation/DrydockLandRepositoryOperation.php @@ -391,7 +391,7 @@ final class DrydockLandRepositoryOperation 'title' => pht('Staging Unavailable'), 'body' => pht( 'When this diff was generated, the server was running an older '. - 'version of Phabricator which did not support staging areas, so '. + 'version of the software which did not support staging areas, so '. 'the change was not pushed to staging. Changes must be pushed '. 'to staging before they can be landed from the web.'), ); @@ -400,7 +400,7 @@ final class DrydockLandRepositoryOperation 'title' => pht('Repository Unsupported'), 'body' => pht( 'When this diff was generated, the server was running an older '. - 'version of Phabricator which did not support staging areas for '. + 'version of the software which did not support staging areas for '. 'this version control system, so the change was not pushed to '. 'staging. Changes must be pushed to staging before they can be '. 'landed from the web.'), @@ -432,8 +432,9 @@ final class DrydockLandRepositoryOperation 'When this diff was generated, it was not pushed to staging for '. 'an unknown reason (the status code was "%s"). Changes must be '. 'pushed to staging before they can be landed from the web. '. - 'The server may be running an out-of-date version of Phabricator, '. - 'and updating may provide more information about this error.', + 'The server may be running an out-of-date version of this '. + 'software, and updating may provide more information about this '. + 'error.', $status), ); } diff --git a/src/applications/drydock/query/DrydockAuthorizationQuery.php b/src/applications/drydock/query/DrydockAuthorizationQuery.php index d6436fc93a..8c5c58c853 100644 --- a/src/applications/drydock/query/DrydockAuthorizationQuery.php +++ b/src/applications/drydock/query/DrydockAuthorizationQuery.php @@ -72,10 +72,6 @@ final class DrydockAuthorizationQuery extends DrydockQuery { return new DrydockAuthorization(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $authorizations) { $blueprint_phids = mpull($authorizations, 'getBlueprintPHID'); if ($blueprint_phids) { diff --git a/src/applications/drydock/query/DrydockBlueprintQuery.php b/src/applications/drydock/query/DrydockBlueprintQuery.php index 6c92927bb8..c163103d16 100644 --- a/src/applications/drydock/query/DrydockBlueprintQuery.php +++ b/src/applications/drydock/query/DrydockBlueprintQuery.php @@ -9,6 +9,11 @@ final class DrydockBlueprintQuery extends DrydockQuery { private $disabled; private $authorizedPHIDs; + private $identifiers; + private $identifierIDs; + private $identifierPHIDs; + private $identifierMap; + public function withIDs(array $ids) { $this->ids = $ids; return $this; @@ -45,6 +50,43 @@ final class DrydockBlueprintQuery extends DrydockQuery { $ngrams); } + public function withIdentifiers(array $identifiers) { + if (!$identifiers) { + throw new Exception( + pht( + 'Can not issue a query with an empty identifier list.')); + } + + $this->identifiers = $identifiers; + + $ids = array(); + $phids = array(); + + foreach ($identifiers as $identifier) { + if (ctype_digit($identifier)) { + $ids[] = $identifier; + } else { + $phids[] = $identifier; + } + } + + $this->identifierIDs = $ids; + $this->identifierPHIDs = $phids; + + return $this; + } + + public function getIdentifierMap() { + if ($this->identifierMap === null) { + throw new Exception( + pht( + 'Execute a query with identifiers before getting the '. + 'identifier map.')); + } + + return $this->identifierMap; + } + public function newResultObject() { return new DrydockBlueprint(); } @@ -53,8 +95,12 @@ final class DrydockBlueprintQuery extends DrydockQuery { return 'blueprint'; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); + protected function willExecute() { + if ($this->identifiers) { + $this->identifierMap = array(); + } else { + $this->identifierMap = null; + } } protected function willFilterPage(array $blueprints) { @@ -70,6 +116,30 @@ final class DrydockBlueprintQuery extends DrydockQuery { $blueprint->attachImplementation($impl); } + if ($this->identifiers) { + $id_map = mpull($blueprints, null, 'getID'); + $phid_map = mpull($blueprints, null, 'getPHID'); + + $map = $this->identifierMap; + + foreach ($this->identifierIDs as $id) { + if (isset($id_map[$id])) { + $map[$id] = $id_map[$id]; + } + } + + foreach ($this->identifierPHIDs as $phid) { + if (isset($phid_map[$phid])) { + $map[$phid] = $phid_map[$phid]; + } + } + + // Just for consistency, reorder the map to match input order. + $map = array_select_keys($map, $this->identifiers); + + $this->identifierMap = $map; + } + return $blueprints; } @@ -111,6 +181,29 @@ final class DrydockBlueprintQuery extends DrydockQuery { (int)$this->disabled); } + if ($this->identifiers !== null) { + $parts = array(); + + if ($this->identifierIDs) { + $parts[] = qsprintf( + $conn, + 'blueprint.id IN (%Ld)', + $this->identifierIDs); + } + + if ($this->identifierPHIDs) { + $parts[] = qsprintf( + $conn, + 'blueprint.phid IN (%Ls)', + $this->identifierPHIDs); + } + + $where[] = qsprintf( + $conn, + '%LO', + $parts); + } + return $where; } diff --git a/src/applications/drydock/query/DrydockCommandQuery.php b/src/applications/drydock/query/DrydockCommandQuery.php index 0d71288a85..fef6170ec9 100644 --- a/src/applications/drydock/query/DrydockCommandQuery.php +++ b/src/applications/drydock/query/DrydockCommandQuery.php @@ -25,10 +25,6 @@ final class DrydockCommandQuery extends DrydockQuery { return new DrydockCommand(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $commands) { $target_phids = mpull($commands, 'getTargetPHID'); diff --git a/src/applications/drydock/query/DrydockLeaseQuery.php b/src/applications/drydock/query/DrydockLeaseQuery.php index e524c1f9c5..4950764486 100644 --- a/src/applications/drydock/query/DrydockLeaseQuery.php +++ b/src/applications/drydock/query/DrydockLeaseQuery.php @@ -9,6 +9,8 @@ final class DrydockLeaseQuery extends DrydockQuery { private $statuses; private $datasourceQuery; private $needUnconsumedCommands; + private $minModified; + private $maxModified; public function withIDs(array $ids) { $this->ids = $ids; @@ -40,6 +42,12 @@ final class DrydockLeaseQuery extends DrydockQuery { return $this; } + public function withDateModifiedBetween($min_epoch, $max_epoch) { + $this->minModified = $min_epoch; + $this->maxModified = $max_epoch; + return $this; + } + public function needUnconsumedCommands($need) { $this->needUnconsumedCommands = $need; return $this; @@ -49,10 +57,6 @@ final class DrydockLeaseQuery extends DrydockQuery { return new DrydockLease(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $leases) { $resource_phids = array_filter(mpull($leases, 'getResourcePHID')); if ($resource_phids) { @@ -146,6 +150,20 @@ final class DrydockLeaseQuery extends DrydockQuery { (int)$this->datasourceQuery); } + if ($this->minModified !== null) { + $where[] = qsprintf( + $conn, + 'dateModified >= %d', + $this->minModified); + } + + if ($this->maxModified !== null) { + $where[] = qsprintf( + $conn, + 'dateModified <= %d', + $this->maxModified); + } + return $where; } diff --git a/src/applications/drydock/query/DrydockLogQuery.php b/src/applications/drydock/query/DrydockLogQuery.php index 80f47f584f..d7e1f77ae6 100644 --- a/src/applications/drydock/query/DrydockLogQuery.php +++ b/src/applications/drydock/query/DrydockLogQuery.php @@ -37,10 +37,6 @@ final class DrydockLogQuery extends DrydockQuery { return new DrydockLog(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function didFilterPage(array $logs) { $blueprint_phids = array_filter(mpull($logs, 'getBlueprintPHID')); if ($blueprint_phids) { diff --git a/src/applications/drydock/query/DrydockRepositoryOperationQuery.php b/src/applications/drydock/query/DrydockRepositoryOperationQuery.php index a5fbe0acc9..6ef5d1b295 100644 --- a/src/applications/drydock/query/DrydockRepositoryOperationQuery.php +++ b/src/applications/drydock/query/DrydockRepositoryOperationQuery.php @@ -55,10 +55,6 @@ final class DrydockRepositoryOperationQuery extends DrydockQuery { return new DrydockRepositoryOperation(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $operations) { $implementations = DrydockRepositoryOperationType::getAllOperationTypes(); diff --git a/src/applications/drydock/query/DrydockResourceQuery.php b/src/applications/drydock/query/DrydockResourceQuery.php index bcbff03663..395e758a23 100644 --- a/src/applications/drydock/query/DrydockResourceQuery.php +++ b/src/applications/drydock/query/DrydockResourceQuery.php @@ -49,10 +49,6 @@ final class DrydockResourceQuery extends DrydockQuery { return new DrydockResource(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $resources) { $blueprint_phids = mpull($resources, 'getBlueprintPHID'); diff --git a/src/applications/drydock/storage/DrydockLease.php b/src/applications/drydock/storage/DrydockLease.php index 2b77146495..12a18b3544 100644 --- a/src/applications/drydock/storage/DrydockLease.php +++ b/src/applications/drydock/storage/DrydockLease.php @@ -111,6 +111,9 @@ final class DrydockLease extends DrydockDAO 'key_owner' => array( 'columns' => array('ownerPHID'), ), + 'key_recent' => array( + 'columns' => array('resourcePHID', 'dateModified'), + ), ), ) + parent::getConfiguration(); } @@ -389,6 +392,62 @@ final class DrydockLease extends DrydockDAO )); } + public function getAllocatedResourcePHIDs() { + return $this->getAttribute('internal.resourcePHIDs.allocated', array()); + } + + public function setAllocatedResourcePHIDs(array $phids) { + return $this->setAttribute('internal.resourcePHIDs.allocated', $phids); + } + + public function addAllocatedResourcePHIDs(array $phids) { + $allocated_phids = $this->getAllocatedResourcePHIDs(); + + foreach ($phids as $phid) { + $allocated_phids[$phid] = $phid; + } + + return $this->setAllocatedResourcePHIDs($allocated_phids); + } + + public function removeAllocatedResourcePHIDs(array $phids) { + $allocated_phids = $this->getAllocatedResourcePHIDs(); + + foreach ($phids as $phid) { + unset($allocated_phids[$phid]); + } + + return $this->setAllocatedResourcePHIDs($allocated_phids); + } + + public function getReclaimedResourcePHIDs() { + return $this->getAttribute('internal.resourcePHIDs.reclaimed', array()); + } + + public function setReclaimedResourcePHIDs(array $phids) { + return $this->setAttribute('internal.resourcePHIDs.reclaimed', $phids); + } + + public function addReclaimedResourcePHIDs(array $phids) { + $reclaimed_phids = $this->getReclaimedResourcePHIDs(); + + foreach ($phids as $phid) { + $reclaimed_phids[$phid] = $phid; + } + + return $this->setReclaimedResourcePHIDs($reclaimed_phids); + } + + public function removeReclaimedResourcePHIDs(array $phids) { + $reclaimed_phids = $this->getReclaimedResourcePHIDs(); + + foreach ($phids as $phid) { + unset($reclaimed_phids[$phid]); + } + + return $this->setReclaimedResourcePHIDs($reclaimed_phids); + } + public function setAwakenTaskIDs(array $ids) { $this->setAttribute('internal.awakenTaskIDs', $ids); return $this; @@ -468,6 +527,10 @@ final class DrydockLease extends DrydockDAO return "/drydock/lease/{$id}/"; } + public function getDisplayName() { + return pht('Drydock Lease %d', $this->getID()); + } + /* -( Status )------------------------------------------------------------- */ diff --git a/src/applications/drydock/storage/DrydockResource.php b/src/applications/drydock/storage/DrydockResource.php index bc672dba3c..38e6660f7e 100644 --- a/src/applications/drydock/storage/DrydockResource.php +++ b/src/applications/drydock/storage/DrydockResource.php @@ -286,6 +286,10 @@ final class DrydockResource extends DrydockDAO return $log->save(); } + public function getDisplayName() { + return pht('Drydock Resource %d', $this->getID()); + } + /* -( Status )------------------------------------------------------------- */ diff --git a/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php b/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php index b83022f720..0402276696 100644 --- a/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php +++ b/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php @@ -188,137 +188,361 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { // First, try to find a suitable open resource which we can acquire a new // lease on. - $resources = $this->loadResourcesForAllocatingLease($blueprints, $lease); + $resources = $this->loadAcquirableResourcesForLease($blueprints, $lease); - // If no resources exist yet, see if we can build one. + list($free_resources, $used_resources) = $this->partitionResources( + $lease, + $resources); + + $resource = $this->leaseAnyResource($lease, $free_resources); + if ($resource) { + return $resource; + } + + // We're about to try creating a resource. If we're already creating + // something, just yield until that resolves. + + $this->yieldForPendingResources($lease); + + // We haven't been able to lease an existing resource yet, so now we try to + // create one. We may still have some less-desirable "used" resources that + // we'll sometimes try to lease later if we fail to allocate a new resource. + + $resource = $this->newLeasedResource($lease, $blueprints); + if ($resource) { + return $resource; + } + + // We haven't been able to lease a desirable "free" resource or create a + // new resource. Try to lease a "used" resource. + + $resource = $this->leaseAnyResource($lease, $used_resources); + if ($resource) { + return $resource; + } + + // If this lease has already triggered a reclaim, just yield and wait for + // it to resolve. + $this->yieldForReclaimingResources($lease); + + // Try to reclaim a resource. This will yield if it reclaims something. + $this->reclaimAnyResource($lease, $blueprints); + + // We weren't able to lease, create, or reclaim any resources. We just have + // to wait for resources to become available. + + $lease->logEvent( + DrydockLeaseWaitingForResourcesLogType::LOGCONST, + array( + 'blueprintPHIDs' => mpull($blueprints, 'getPHID'), + )); + + throw new PhabricatorWorkerYieldException(15); + } + + private function reclaimAnyResource(DrydockLease $lease, array $blueprints) { + assert_instances_of($blueprints, 'DrydockBlueprint'); + + $blueprints = $this->rankBlueprints($blueprints, $lease); + + // Try to actively reclaim unused resources. If we succeed, jump back + // into the queue in an effort to claim it. + + foreach ($blueprints as $blueprint) { + $reclaimed = $this->reclaimResources($blueprint, $lease); + if ($reclaimed) { + + $lease->logEvent( + DrydockLeaseReclaimLogType::LOGCONST, + array( + 'resourcePHIDs' => array($reclaimed->getPHID()), + )); + + // Yield explicitly here: we'll be awakened when the resource is + // reclaimed. + + throw new PhabricatorWorkerYieldException(15); + } + } + } + + private function yieldForPendingResources(DrydockLease $lease) { + // See T13677. If this lease has already triggered the allocation of + // one or more resources and they are still pending, just yield and + // wait for them. + + $viewer = $this->getViewer(); + + $phids = $lease->getAllocatedResourcePHIDs(); + if (!$phids) { + return null; + } + + $resources = id(new DrydockResourceQuery()) + ->setViewer($viewer) + ->withPHIDs($phids) + ->withStatuses( + array( + DrydockResourceStatus::STATUS_PENDING, + )) + ->setLimit(1) + ->execute(); if (!$resources) { - $usable_blueprints = $this->removeOverallocatedBlueprints( - $blueprints, - $lease); + return; + } - // If we get nothing back here, some blueprint claims it can eventually - // satisfy the lease, just not right now. This is a temporary failure, - // and we expect allocation to succeed eventually. - if (!$usable_blueprints) { - $blueprints = $this->rankBlueprints($blueprints, $lease); + $lease->logEvent( + DrydockLeaseWaitingForActivationLogType::LOGCONST, + array( + 'resourcePHIDs' => mpull($resources, 'getPHID'), + )); - // Try to actively reclaim unused resources. If we succeed, jump back - // into the queue in an effort to claim it. - foreach ($blueprints as $blueprint) { - $reclaimed = $this->reclaimResources($blueprint, $lease); - if ($reclaimed) { - $lease->logEvent( - DrydockLeaseReclaimLogType::LOGCONST, - array( - 'resourcePHIDs' => array($reclaimed->getPHID()), - )); + throw new PhabricatorWorkerYieldException(15); + } - throw new PhabricatorWorkerYieldException(15); - } - } + private function yieldForReclaimingResources(DrydockLease $lease) { + $viewer = $this->getViewer(); - $lease->logEvent( - DrydockLeaseWaitingForResourcesLogType::LOGCONST, + $phids = $lease->getReclaimedResourcePHIDs(); + if (!$phids) { + return; + } + + $resources = id(new DrydockResourceQuery()) + ->setViewer($viewer) + ->withPHIDs($phids) + ->withStatuses( + array( + DrydockResourceStatus::STATUS_ACTIVE, + DrydockResourceStatus::STATUS_RELEASED, + )) + ->setLimit(1) + ->execute(); + if (!$resources) { + return; + } + + $lease->logEvent( + DrydockLeaseWaitingForReclamationLogType::LOGCONST, + array( + 'resourcePHIDs' => mpull($resources, 'getPHID'), + )); + + throw new PhabricatorWorkerYieldException(15); + } + + private function newLeasedResource( + DrydockLease $lease, + array $blueprints) { + assert_instances_of($blueprints, 'DrydockBlueprint'); + + $usable_blueprints = $this->removeOverallocatedBlueprints( + $blueprints, + $lease); + + // If we get nothing back here, some blueprint claims it can eventually + // satisfy the lease, just not right now. This is a temporary failure, + // and we expect allocation to succeed eventually. + + // Return, try to lease a "used" resource, and continue from there. + + if (!$usable_blueprints) { + return null; + } + + $usable_blueprints = $this->rankBlueprints($usable_blueprints, $lease); + + $new_resources = $this->newResources($lease, $usable_blueprints); + if (!$new_resources) { + // If we were unable to create any new resources, return and + // try to lease a "used" resource. + return null; + } + + $new_resources = $this->removeUnacquirableResources( + $new_resources, + $lease); + if (!$new_resources) { + // If we make it here, we just built a resource but aren't allowed + // to acquire it. We expect this to happen if the resource prevents + // acquisition until it activates, which is common when a resource + // needs to perform setup steps. + + // Explicitly yield and wait for activation, since we don't want to + // lease a "used" resource. + + throw new PhabricatorWorkerYieldException(15); + } + + $resource = $this->leaseAnyResource($lease, $new_resources); + if ($resource) { + return $resource; + } + + // We may not be able to lease a resource even if we just built it: + // another process may snatch it up before we can lease it. This should + // be rare, but is not concerning. Just try to build another resource. + + // We likely could try to build the next resource immediately, but err on + // the side of caution and yield for now, at least until this code is + // better vetted. + + throw new PhabricatorWorkerYieldException(15); + } + + private function partitionResources( + DrydockLease $lease, + array $resources) { + + assert_instances_of($resources, 'DrydockResource'); + $viewer = $this->getViewer(); + + $lease_statuses = array( + DrydockLeaseStatus::STATUS_PENDING, + DrydockLeaseStatus::STATUS_ACQUIRED, + DrydockLeaseStatus::STATUS_ACTIVE, + ); + + // Partition resources into "free" resources (which we can try to lease + // immediately) and "used" resources, which we can only to lease after we + // fail to allocate a new resource. + + // "Free" resources are unleased and/or prefer reuse over allocation. + // "Used" resources are leased and prefer allocation over reuse. + + $free_resources = array(); + $used_resources = array(); + + foreach ($resources as $resource) { + $blueprint = $resource->getBlueprint(); + + if (!$blueprint->shouldAllocateSupplementalResource($resource, $lease)) { + $free_resources[] = $resource; + continue; + } + + $leases = id(new DrydockLeaseQuery()) + ->setViewer($viewer) + ->withResourcePHIDs(array($resource->getPHID())) + ->withStatuses($lease_statuses) + ->setLimit(1) + ->execute(); + if (!$leases) { + $free_resources[] = $resource; + continue; + } + + $used_resources[] = $resource; + } + + return array($free_resources, $used_resources); + } + + private function newResources( + DrydockLease $lease, + array $blueprints) { + assert_instances_of($blueprints, 'DrydockBlueprint'); + + $resources = array(); + $exceptions = array(); + foreach ($blueprints as $blueprint) { + $caught = null; + try { + $resources[] = $this->allocateResource($blueprint, $lease); + + // Bail after allocating one resource, we don't need any more than + // this. + break; + } catch (Exception $ex) { + $caught = $ex; + } catch (Throwable $ex) { + $caught = $ex; + } + + if ($caught) { + // This failure is not normally expected, so log it. It can be + // caused by something mundane and recoverable, however (see below + // for discussion). + + // We log to the blueprint separately from the log to the lease: + // the lease is not attached to a blueprint yet so the lease log + // will not show up on the blueprint; more than one blueprint may + // fail; and the lease is not really impacted (and won't log) if at + // least one blueprint actually works. + + $blueprint->logEvent( + DrydockResourceAllocationFailureLogType::LOGCONST, array( - 'blueprintPHIDs' => mpull($blueprints, 'getPHID'), + 'class' => get_class($caught), + 'message' => $caught->getMessage(), )); - throw new PhabricatorWorkerYieldException(15); + $exceptions[] = $caught; } + } - $usable_blueprints = $this->rankBlueprints($usable_blueprints, $lease); + if (!$resources) { + // If one or more blueprints claimed that they would be able to allocate + // resources but none are actually able to allocate resources, log the + // failure and yield so we try again soon. - $exceptions = array(); - foreach ($usable_blueprints as $blueprint) { - try { - $resources[] = $this->allocateResource($blueprint, $lease); + // This can happen if some unexpected issue occurs during allocation + // (for example, a call to build a VM fails for some reason) or if we + // raced another allocator and the blueprint is now full. - // Bail after allocating one resource, we don't need any more than - // this. - break; - } catch (Exception $ex) { - // This failure is not normally expected, so log it. It can be - // caused by something mundane and recoverable, however (see below - // for discussion). + $ex = new PhutilAggregateException( + pht( + 'All blueprints failed to allocate a suitable new resource when '. + 'trying to allocate lease ("%s").', + $lease->getPHID()), + $exceptions); - // We log to the blueprint separately from the log to the lease: - // the lease is not attached to a blueprint yet so the lease log - // will not show up on the blueprint; more than one blueprint may - // fail; and the lease is not really impacted (and won't log) if at - // least one blueprint actually works. + $lease->logEvent( + DrydockLeaseAllocationFailureLogType::LOGCONST, + array( + 'class' => get_class($ex), + 'message' => $ex->getMessage(), + )); - $blueprint->logEvent( - DrydockResourceAllocationFailureLogType::LOGCONST, - array( - 'class' => get_class($ex), - 'message' => $ex->getMessage(), - )); + return null; + } - $exceptions[] = $ex; - } - } + return $resources; + } - if (!$resources) { - // If one or more blueprints claimed that they would be able to - // allocate resources but none are actually able to allocate resources, - // log the failure and yield so we try again soon. - // This can happen if some unexpected issue occurs during allocation - // (for example, a call to build a VM fails for some reason) or if we - // raced another allocator and the blueprint is now full. + private function leaseAnyResource( + DrydockLease $lease, + array $resources) { + assert_instances_of($resources, 'DrydockResource'); - $ex = new PhutilAggregateException( - pht( - 'All blueprints failed to allocate a suitable new resource when '. - 'trying to allocate lease ("%s").', - $lease->getPHID()), - $exceptions); - - $lease->logEvent( - DrydockLeaseAllocationFailureLogType::LOGCONST, - array( - 'class' => get_class($ex), - 'message' => $ex->getMessage(), - )); - - throw new PhabricatorWorkerYieldException(15); - } - - $resources = $this->removeUnacquirableResources($resources, $lease); - if (!$resources) { - // If we make it here, we just built a resource but aren't allowed - // to acquire it. We expect this during routine operation if the - // resource prevents acquisition until it activates. Yield and wait - // for activation. - throw new PhabricatorWorkerYieldException(15); - } - - // NOTE: We have not acquired the lease yet, so it is possible that the - // resource we just built will be snatched up by some other lease before - // we can acquire it. This is not problematic: we'll retry a little later - // and should succeed eventually. + if (!$resources) { + return null; } $resources = $this->rankResources($resources, $lease); $exceptions = array(); $yields = array(); - $allocated = false; + + $allocated = null; foreach ($resources as $resource) { try { - $resource = $this->newResourceForAcquisition($resource, $lease); $this->acquireLease($resource, $lease); - $allocated = true; + $allocated = $resource; break; } catch (DrydockResourceLockException $ex) { // We need to lock the resource to actually acquire it. If we aren't // able to acquire the lock quickly enough, we can yield and try again // later. $yields[] = $ex; + } catch (DrydockSlotLockException $ex) { + // This also just indicates we ran into some kind of contention, + // probably from another lease. Just yield. + $yields[] = $ex; } catch (DrydockAcquiredBrokenResourceException $ex) { // If a resource was reclaimed or destroyed by the time we actually - // got around to acquiring it, we just got unlucky. We can yield and - // try again later. + // got around to acquiring it, we just got unlucky. $yields[] = $ex; } catch (PhabricatorWorkerYieldException $ex) { // We can be told to yield, particularly by the supplemental allocator @@ -329,59 +553,19 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { } } - if (!$allocated) { - if ($yields) { - throw new PhabricatorWorkerYieldException(15); - } else { - throw new PhutilAggregateException( - pht( - 'Unable to acquire lease "%s" on any resource.', - $lease->getPHID()), - $exceptions); - } - } - } - - - /** - * Get all the @{class:DrydockBlueprintImplementation}s which can possibly - * build a resource to satisfy a lease. - * - * This method returns blueprints which might, at some time, be able to - * build a resource which can satisfy the lease. They may not be able to - * build that resource right now. - * - * @param DrydockLease Requested lease. - * @return list List of qualifying blueprint - * implementations. - * @task allocator - */ - private function loadBlueprintImplementationsForAllocatingLease( - DrydockLease $lease) { - - $impls = DrydockBlueprintImplementation::getAllBlueprintImplementations(); - - $keep = array(); - foreach ($impls as $key => $impl) { - // Don't use disabled blueprint types. - if (!$impl->isEnabled()) { - continue; - } - - // Don't use blueprint types which can't allocate the correct kind of - // resource. - if ($impl->getType() != $lease->getResourceType()) { - continue; - } - - if (!$impl->canAnyBlueprintEverAllocateResourceForLease($lease)) { - continue; - } - - $keep[$key] = $impl; + if ($allocated) { + return $allocated; } - return $keep; + if ($yields) { + throw new PhabricatorWorkerYieldException(15); + } + + throw new PhutilAggregateException( + pht( + 'Unable to acquire lease "%s" on any resource.', + $lease->getPHID()), + $exceptions); } @@ -397,7 +581,7 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { DrydockLease $lease) { $viewer = $this->getViewer(); - $impls = $this->loadBlueprintImplementationsForAllocatingLease($lease); + $impls = DrydockBlueprintImplementation::getAllForAllocatingLease($lease); if (!$impls) { return array(); } @@ -468,7 +652,7 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { * the lease. * @task allocator */ - private function loadResourcesForAllocatingLease( + private function loadAcquirableResourcesForLease( array $blueprints, DrydockLease $lease) { assert_instances_of($blueprints, 'DrydockBlueprint'); @@ -480,7 +664,6 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { ->withTypes(array($lease->getResourceType())) ->withStatuses( array( - DrydockResourceStatus::STATUS_PENDING, DrydockResourceStatus::STATUS_ACTIVE, )) ->execute(); @@ -600,6 +783,13 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { // If this resource was allocated as a pending resource, queue a task to // activate it. if ($resource->getStatus() == DrydockResourceStatus::STATUS_PENDING) { + + $lease->addAllocatedResourcePHIDs( + array( + $resource->getPHID(), + )); + $lease->save(); + PhabricatorWorker::scheduleTask( 'DrydockResourceUpdateWorker', array( @@ -678,26 +868,6 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { DrydockLease $lease) { $viewer = $this->getViewer(); - // If this lease is marked as already in the process of reclaiming a - // resource, don't let it reclaim another one until the first reclaim - // completes. This stops one lease from reclaiming a large number of - // resources if the reclaims take a while to complete. - $reclaiming_phid = $lease->getAttribute('drydock.reclaimingPHID'); - if ($reclaiming_phid) { - $reclaiming_resource = id(new DrydockResourceQuery()) - ->setViewer($viewer) - ->withPHIDs(array($reclaiming_phid)) - ->withStatuses( - array( - DrydockResourceStatus::STATUS_ACTIVE, - DrydockResourceStatus::STATUS_RELEASED, - )) - ->executeOne(); - if ($reclaiming_resource) { - return null; - } - } - $resources = id(new DrydockResourceQuery()) ->setViewer($viewer) ->withBlueprintPHIDs(array($blueprint->getPHID())) @@ -796,73 +966,6 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { } } - private function newResourceForAcquisition( - DrydockResource $resource, - DrydockLease $lease) { - - // If the resource has no leases against it, never build a new one. This is - // likely already a new resource that just activated. - $viewer = $this->getViewer(); - - $statuses = array( - DrydockLeaseStatus::STATUS_PENDING, - DrydockLeaseStatus::STATUS_ACQUIRED, - DrydockLeaseStatus::STATUS_ACTIVE, - ); - - $leases = id(new DrydockLeaseQuery()) - ->setViewer($viewer) - ->withResourcePHIDs(array($resource->getPHID())) - ->withStatuses($statuses) - ->setLimit(1) - ->execute(); - if (!$leases) { - return $resource; - } - - // If we're about to get a lease on a resource, check if the blueprint - // wants to allocate a supplemental resource. If it does, try to perform a - // new allocation instead. - $blueprint = $resource->getBlueprint(); - if (!$blueprint->shouldAllocateSupplementalResource($resource, $lease)) { - return $resource; - } - - // If the blueprint is already overallocated, we can't allocate a new - // resource. Just return the existing resource. - $remaining = $this->removeOverallocatedBlueprints( - array($blueprint), - $lease); - if (!$remaining) { - return $resource; - } - - // Try to build a new resource. - try { - $new_resource = $this->allocateResource($blueprint, $lease); - } catch (Exception $ex) { - $blueprint->logEvent( - DrydockResourceAllocationFailureLogType::LOGCONST, - array( - 'class' => get_class($ex), - 'message' => $ex->getMessage(), - )); - - return $resource; - } - - // If we can't actually acquire the new resource yet, just yield. - // (We could try to move forward with the original resource instead.) - $acquirable = $this->removeUnacquirableResources( - array($new_resource), - $lease); - if (!$acquirable) { - throw new PhabricatorWorkerYieldException(15); - } - - return $new_resource; - } - /* -( Activating Leases )-------------------------------------------------- */ diff --git a/src/applications/drydock/worker/DrydockWorker.php b/src/applications/drydock/worker/DrydockWorker.php index fcb6876cfe..457624ec5d 100644 --- a/src/applications/drydock/worker/DrydockWorker.php +++ b/src/applications/drydock/worker/DrydockWorker.php @@ -203,14 +203,15 @@ abstract class DrydockWorker extends PhabricatorWorker { // from one another forever without making progress, so make resources // immune to reclamation for a little while after they activate or update. + $now = PhabricatorTime::getNow(); + $max_epoch = ($now - phutil_units('3 minutes in seconds')); + // TODO: It would be nice to use a more narrow time here, like "last // activation or lease release", but we don't currently store that // anywhere. $updated = $resource->getDateModified(); - $now = PhabricatorTime::getNow(); - $ago = ($now - $updated); - if ($ago < phutil_units('3 minutes in seconds')) { + if ($updated > $max_epoch) { return false; } @@ -233,6 +234,21 @@ abstract class DrydockWorker extends PhabricatorWorker { return false; } + // See T13676. Don't reclaim a resource if a lease recently released. + $leases = id(new DrydockLeaseQuery()) + ->setViewer($viewer) + ->withResourcePHIDs(array($resource->getPHID())) + ->withStatuses( + array( + DrydockLeaseStatus::STATUS_DESTROYED, + )) + ->withDateModifiedBetween($max_epoch, null) + ->setLimit(1) + ->execute(); + if ($leases) { + return false; + } + return true; } @@ -244,7 +260,7 @@ abstract class DrydockWorker extends PhabricatorWorker { // Mark the lease as reclaiming this resource. It won't be allowed to start // another reclaim as long as this resource is still in the process of // being reclaimed. - $lease->setAttribute('drydock.reclaimingPHID', $resource->getPHID()); + $lease->addReclaimedResourcePHIDs(array($resource->getPHID())); // When the resource releases, we we want to reawaken this task since it // should (usually) be able to start building a new resource right away. diff --git a/src/applications/feed/conduit/FeedPublishConduitAPIMethod.php b/src/applications/feed/conduit/FeedPublishConduitAPIMethod.php deleted file mode 100644 index 5f83e73b3d..0000000000 --- a/src/applications/feed/conduit/FeedPublishConduitAPIMethod.php +++ /dev/null @@ -1,49 +0,0 @@ - 'required string', - 'data' => 'required dict', - 'time' => 'optional int', - ); - } - - protected function defineReturnType() { - return 'nonempty phid'; - } - - protected function execute(ConduitAPIRequest $request) { - $type = $request->getValue('type'); - $data = $request->getValue('data'); - $time = $request->getValue('time'); - - $author_phid = $request->getUser()->getPHID(); - $phids = array($author_phid); - - $publisher = new PhabricatorFeedStoryPublisher(); - $publisher->setStoryType($type); - $publisher->setStoryData($data); - $publisher->setStoryTime($time); - $publisher->setRelatedPHIDs($phids); - $publisher->setStoryAuthorPHID($author_phid); - - $data = $publisher->publish(); - - return $data->getPHID(); - } - -} diff --git a/src/applications/feed/config/PhabricatorFeedConfigOptions.php b/src/applications/feed/config/PhabricatorFeedConfigOptions.php index 29c5a9549b..eac6a097ae 100644 --- a/src/applications/feed/config/PhabricatorFeedConfigOptions.php +++ b/src/applications/feed/config/PhabricatorFeedConfigOptions.php @@ -24,7 +24,7 @@ final class PhabricatorFeedConfigOptions IMPORTANT: Feed hooks are deprecated and have been replaced by Webhooks. You can configure Webhooks in Herald. This configuration option will be removed -in a future version of Phabricator. +in a future version of the software. (This legacy option may be configured with a list of URIs; feed stories will send to these URIs.) diff --git a/src/applications/files/application/PhabricatorFilesApplication.php b/src/applications/files/application/PhabricatorFilesApplication.php index 2e4c48f0d6..e246d4c90c 100644 --- a/src/applications/files/application/PhabricatorFilesApplication.php +++ b/src/applications/files/application/PhabricatorFilesApplication.php @@ -95,6 +95,16 @@ final class PhabricatorFilesApplication extends PhabricatorApplication { ), 'document/(?P[^/]+)/(?P[^/]+)/' => 'PhabricatorFileDocumentController', + 'ui/' => array( + 'detach/(?P[^/]+)/(?P[^/]+)/' + => 'PhabricatorFileDetachController', + 'curtain/' => array( + 'list/(?P[^/]+)/' + => 'PhabricatorFileUICurtainListController', + 'attach/(?P[^/]+)/(?P[^/]+)/' + => 'PhabricatorFileUICurtainAttachController', + ), + ), ) + $this->getResourceSubroutes(), ); } diff --git a/src/applications/files/config/PhabricatorFilesConfigOptions.php b/src/applications/files/config/PhabricatorFilesConfigOptions.php index 735ddfcb0d..7ed96a412b 100644 --- a/src/applications/files/config/PhabricatorFilesConfigOptions.php +++ b/src/applications/files/config/PhabricatorFilesConfigOptions.php @@ -181,7 +181,7 @@ final class PhabricatorFilesConfigOptions ->setSummary(pht('Local storage disk path.')) ->setDescription( pht( - "Phabricator provides a local disk storage engine, which just ". + "This software provides a local disk storage engine, which just ". "writes files to some directory on local disk. The webserver ". "must have read/write permissions on this directory. This is ". "straightforward and suitable for most installs, but will not ". diff --git a/src/applications/files/controller/PhabricatorFileDetachController.php b/src/applications/files/controller/PhabricatorFileDetachController.php new file mode 100644 index 0000000000..146eb42874 --- /dev/null +++ b/src/applications/files/controller/PhabricatorFileDetachController.php @@ -0,0 +1,120 @@ +getViewer(); + + $object_phid = $request->getURIData('objectPHID'); + $file_phid = $request->getURIData('filePHID'); + + $object = id(new PhabricatorObjectQuery()) + ->setViewer($viewer) + ->withPHIDs(array($object_phid)) + ->executeOne(); + if (!$object) { + return new Aphront404Response(); + } + + $handles = $viewer->loadHandles( + array( + $object_phid, + $file_phid, + )); + + $object_handle = $handles[$object_phid]; + $file_handle = $handles[$file_phid]; + $cancel_uri = $file_handle->getURI(); + + $dialog = $this->newDialog() + ->setViewer($viewer) + ->setTitle(pht('Detach File')) + ->addCancelButton($cancel_uri, pht('Close')); + + $file_link = phutil_tag('strong', array(), $file_handle->renderLink()); + $object_link = phutil_tag('strong', array(), $object_handle->renderLink()); + + $attachment = id(new PhabricatorFileAttachmentQuery()) + ->setViewer($viewer) + ->withObjectPHIDs(array($object->getPHID())) + ->withFilePHIDs(array($file_phid)) + ->needFiles(true) + ->withVisibleFiles(true) + ->executeOne(); + if (!$attachment) { + $body = pht( + 'The file %s is not attached to the object %s.', + $file_link, + $object_link); + + return $dialog->appendParagraph($body); + } + + $mode_reference = PhabricatorFileAttachment::MODE_REFERENCE; + if ($attachment->getAttachmentMode() === $mode_reference) { + $body = pht( + 'The file %s is referenced by the object %s, but not attached to '. + 'it, so it can not be detached.', + $file_link, + $object_link); + + return $dialog->appendParagraph($body); + } + + if (!$attachment->canDetach()) { + $body = pht( + 'The file %s can not be detached from the object %s.', + $file_link, + $object_link); + + return $dialog->appendParagraph($body); + } + + if (!$request->isDialogFormPost()) { + $dialog->appendParagraph( + pht( + 'Detach the file %s from the object %s?', + $file_link, + $object_link)); + + $dialog->addSubmitButton(pht('Detach File')); + + return $dialog; + } + + if (!($object instanceof PhabricatorApplicationTransactionInterface)) { + $dialog->appendParagraph( + pht( + 'This object (of class "%s") does not implement the required '. + 'interface ("%s"), so files can not be manually detached from it.', + get_class($object), + 'PhabricatorApplicationTransactionInterface')); + + return $dialog; + } + + $editor = $object->getApplicationTransactionEditor() + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true); + + $template = $object->getApplicationTransactionTemplate(); + + $xactions = array(); + + $xactions[] = id(clone $template) + ->setTransactionType(PhabricatorTransactions::TYPE_FILE) + ->setNewValue( + array( + $file_phid => PhabricatorFileAttachment::MODE_DETACH, + )); + + $editor->applyTransactions($object, $xactions); + + return $this->newRedirect() + ->setURI($cancel_uri); + } + +} diff --git a/src/applications/files/controller/PhabricatorFileUICurtainAttachController.php b/src/applications/files/controller/PhabricatorFileUICurtainAttachController.php new file mode 100644 index 0000000000..8dcfa5b840 --- /dev/null +++ b/src/applications/files/controller/PhabricatorFileUICurtainAttachController.php @@ -0,0 +1,133 @@ +getViewer(); + + $object_phid = $request->getURIData('objectPHID'); + $file_phid = $request->getURIData('filePHID'); + + $object = id(new PhabricatorObjectQuery()) + ->setViewer($viewer) + ->withPHIDs(array($object_phid)) + ->executeOne(); + if (!$object) { + return new Aphront404Response(); + } + + $attachment = id(new PhabricatorFileAttachmentQuery()) + ->setViewer($viewer) + ->withObjectPHIDs(array($object->getPHID())) + ->withFilePHIDs(array($file_phid)) + ->needFiles(true) + ->withVisibleFiles(true) + ->executeOne(); + if (!$attachment) { + return new Aphront404Response(); + } + + $handles = $viewer->loadHandles( + array( + $object_phid, + $file_phid, + )); + + $object_handle = $handles[$object_phid]; + $file_handle = $handles[$file_phid]; + $cancel_uri = $object_handle->getURI(); + + $dialog = $this->newDialog() + ->setViewer($viewer) + ->setTitle(pht('Attach File')) + ->addCancelButton($cancel_uri, pht('Close')); + + $file_link = phutil_tag('strong', array(), $file_handle->renderLink()); + $object_link = phutil_tag('strong', array(), $object_handle->renderLink()); + + if ($attachment->isPolicyAttachment()) { + $body = pht( + 'The file %s is already attached to the object %s.', + $file_link, + $object_link); + + return $dialog->appendParagraph($body); + } + + if (!$request->isDialogFormPost()) { + $dialog->appendRemarkup( + pht( + '(WARNING) This file is referenced by this object, but '. + 'not formally attached to it. Users who can see the object may '. + 'not be able to see the file.')); + + $dialog->appendParagraph( + pht( + 'Do you want to attach the file %s to the object %s?', + $file_link, + $object_link)); + + $dialog->addSubmitButton(pht('Attach File')); + + return $dialog; + } + + if (!$request->getBool('confirm')) { + $dialog->setTitle(pht('Confirm File Attachment')); + + $dialog->addHiddenInput('confirm', 1); + + $dialog->appendRemarkup( + pht( + '(IMPORTANT) If you attach this file to this object, any user who '. + 'has permission to view the object will be able to view and '. + 'download the file!')); + + $dialog->appendParagraph( + pht( + 'Really attach the file %s to the object %s, allowing any user '. + 'who can view the object to view and download the file?', + $file_link, + $object_link)); + + $dialog->addSubmitButton(pht('Grant Permission')); + + return $dialog; + } + + if (!($object instanceof PhabricatorApplicationTransactionInterface)) { + $dialog->appendParagraph( + pht( + 'This object (of class "%s") does not implement the required '. + 'interface ("%s"), so files can not be manually attached to it.', + get_class($object), + 'PhabricatorApplicationTransactionInterface')); + + return $dialog; + } + + $editor = $object->getApplicationTransactionEditor() + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true); + + $template = $object->getApplicationTransactionTemplate(); + + $xactions = array(); + + $xactions[] = id(clone $template) + ->setTransactionType(PhabricatorTransactions::TYPE_FILE) + ->setNewValue( + array( + $file_phid => PhabricatorFileAttachment::MODE_ATTACH, + )); + + $editor->applyTransactions($object, $xactions); + + return $this->newRedirect() + ->setURI($cancel_uri); + } + +} diff --git a/src/applications/files/controller/PhabricatorFileUICurtainListController.php b/src/applications/files/controller/PhabricatorFileUICurtainListController.php new file mode 100644 index 0000000000..3049f4ddeb --- /dev/null +++ b/src/applications/files/controller/PhabricatorFileUICurtainListController.php @@ -0,0 +1,61 @@ +getViewer(); + + $object_phid = $request->getURIData('phid'); + + $object = id(new PhabricatorObjectQuery()) + ->setViewer($viewer) + ->withPHIDs(array($object_phid)) + ->executeOne(); + if (!$object) { + return new Aphront404Response(); + } + + $attachments = id(new PhabricatorFileAttachmentQuery()) + ->setViewer($viewer) + ->withObjectPHIDs(array($object->getPHID())) + ->needFiles(true) + ->execute(); + + $handles = $viewer->loadHandles(array($object_phid)); + $object_handle = $handles[$object_phid]; + + $file_phids = mpull($attachments, 'getFilePHID'); + $file_handles = $viewer->loadHandles($file_phids); + + $list = id(new PHUIObjectItemListView()) + ->setUser($viewer); + foreach ($attachments as $attachment) { + $file_phid = $attachment->getFilePHID(); + $handle = $file_handles[$file_phid]; + + $item = id(new PHUIObjectItemView()) + ->setHeader($handle->getFullName()) + ->setHref($handle->getURI()) + ->setDisabled($handle->isDisabled()); + + if ($handle->getImageURI()) { + $item->setImageURI($handle->getImageURI()); + } + + $list->addItem($item); + } + + return $this->newDialog() + ->setViewer($viewer) + ->setWidth(AphrontDialogView::WIDTH_FORM) + ->setTitle(pht('Referenced Files')) + ->setObjectList($list) + ->addCancelButton($object_handle->getURI(), pht('Close')); + } + +} diff --git a/src/applications/files/controller/PhabricatorFileUploadController.php b/src/applications/files/controller/PhabricatorFileUploadController.php index c06e0e6d89..dfc4696e08 100644 --- a/src/applications/files/controller/PhabricatorFileUploadController.php +++ b/src/applications/files/controller/PhabricatorFileUploadController.php @@ -46,7 +46,7 @@ final class PhabricatorFileUploadController extends PhabricatorFileController { pht('Drag and Drop:'), pht( 'You can also upload files by dragging and dropping them from your '. - 'desktop onto this page or the Phabricator home page.'))); + 'desktop onto this page or the home page.'))); $policies = id(new PhabricatorPolicyQuery()) ->setViewer($viewer) diff --git a/src/applications/files/controller/PhabricatorFileViewController.php b/src/applications/files/controller/PhabricatorFileViewController.php index 145f066492..389fc66247 100644 --- a/src/applications/files/controller/PhabricatorFileViewController.php +++ b/src/applications/files/controller/PhabricatorFileViewController.php @@ -320,20 +320,13 @@ final class PhabricatorFileViewController extends PhabricatorFileController { $finfo->addProperty(pht('Default Alt Text'), $default_alt); } - $phids = $file->getObjectPHIDs(); - if ($phids) { - $attached = new PHUIPropertyListView(); + $attachments_table = $this->newAttachmentsView($file); - $tab_group->addTab( - id(new PHUITabView()) - ->setName(pht('Attached')) - ->setKey('attached') - ->appendChild($attached)); - - $attached->addProperty( - pht('Attached To'), - $viewer->renderHandleList($phids)); - } + $tab_group->addTab( + id(new PHUITabView()) + ->setName(pht('Attached')) + ->setKey('attached') + ->appendChild($attachments_table)); $engine = $this->loadStorageEngine($file); if ($engine) { @@ -420,4 +413,81 @@ final class PhabricatorFileViewController extends PhabricatorFileController { return $engine->newDocumentView($ref); } + private function newAttachmentsView(PhabricatorFile $file) { + $viewer = $this->getViewer(); + + $attachments = id(new PhabricatorFileAttachmentQuery()) + ->setViewer($viewer) + ->withFilePHIDs(array($file->getPHID())) + ->execute(); + + $handles = $viewer->loadHandles(mpull($attachments, 'getObjectPHID')); + + $rows = array(); + + $mode_map = PhabricatorFileAttachment::getModeNameMap(); + $mode_attach = PhabricatorFileAttachment::MODE_ATTACH; + + foreach ($attachments as $attachment) { + $object_phid = $attachment->getObjectPHID(); + $handle = $handles[$object_phid]; + + $attachment_mode = $attachment->getAttachmentMode(); + + $mode_name = idx($mode_map, $attachment_mode); + if ($mode_name === null) { + $mode_name = pht('Unknown ("%s")', $attachment_mode); + } + + $detach_uri = urisprintf( + '/file/ui/detach/%s/%s/', + $object_phid, + $file->getPHID()); + + $is_disabled = !$attachment->canDetach(); + + $detach_button = id(new PHUIButtonView()) + ->setHref($detach_uri) + ->setTag('a') + ->setWorkflow(true) + ->setDisabled($is_disabled) + ->setColor(PHUIButtonView::GREY) + ->setSize(PHUIButtonView::SMALL) + ->setText(pht('Detach File')); + + javelin_tag( + 'a', + array( + 'href' => $detach_uri, + 'sigil' => 'workflow', + 'disabled' => true, + 'class' => 'small button button-grey disabled', + ), + pht('Detach File')); + + $rows[] = array( + $handle->renderLink(), + $mode_name, + $detach_button, + ); + } + + $table = id(new AphrontTableView($rows)) + ->setHeaders( + array( + pht('Attached To'), + pht('Mode'), + null, + )) + ->setColumnClasses( + array( + 'pri wide', + null, + null, + )); + + return $table; + } + + } diff --git a/src/applications/files/document/PhabricatorDocumentRef.php b/src/applications/files/document/PhabricatorDocumentRef.php index 12d9f4f2fd..98dfda8856 100644 --- a/src/applications/files/document/PhabricatorDocumentRef.php +++ b/src/applications/files/document/PhabricatorDocumentRef.php @@ -112,6 +112,11 @@ final class PhabricatorDocumentRef public function hasAnyMimeType(array $candidate_types) { $mime_full = $this->getMimeType(); + + if (!phutil_nonempty_string($mime_full)) { + return false; + } + $mime_parts = explode(';', $mime_full); $mime_type = head($mime_parts); diff --git a/src/applications/files/document/PhabricatorJSONDocumentEngine.php b/src/applications/files/document/PhabricatorJSONDocumentEngine.php index 42f4469ee6..9797ce3da2 100644 --- a/src/applications/files/document/PhabricatorJSONDocumentEngine.php +++ b/src/applications/files/document/PhabricatorJSONDocumentEngine.php @@ -14,8 +14,12 @@ final class PhabricatorJSONDocumentEngine } protected function getContentScore(PhabricatorDocumentRef $ref) { - if (preg_match('/\.json\z/', $ref->getName())) { - return 2000; + + $name = $ref->getName(); + if ($name !== null) { + if (preg_match('/\.json\z/', $name)) { + return 2000; + } } if ($ref->isProbablyJSON()) { diff --git a/src/applications/files/document/PhabricatorRemarkupDocumentEngine.php b/src/applications/files/document/PhabricatorRemarkupDocumentEngine.php index 296b78196f..053640af5f 100644 --- a/src/applications/files/document/PhabricatorRemarkupDocumentEngine.php +++ b/src/applications/files/document/PhabricatorRemarkupDocumentEngine.php @@ -15,8 +15,11 @@ final class PhabricatorRemarkupDocumentEngine protected function getContentScore(PhabricatorDocumentRef $ref) { $name = $ref->getName(); - if (preg_match('/\\.remarkup\z/i', $name)) { - return 2000; + + if ($name !== null) { + if (preg_match('/\\.remarkup\z/i', $name)) { + return 2000; + } } return 500; diff --git a/src/applications/files/edge/PhabricatorFileHasObjectEdgeType.php b/src/applications/files/edge/PhabricatorFileHasObjectEdgeType.php deleted file mode 100644 index c4a3c2af87..0000000000 --- a/src/applications/files/edge/PhabricatorFileHasObjectEdgeType.php +++ /dev/null @@ -1,27 +0,0 @@ -getViewer(); + + $attachment_table = new PhabricatorFileAttachment(); + $attachment_conn = $attachment_table->establishConnection('r'); + + $exact_limit = 100; + $visible_limit = 8; + + $attachments = id(new PhabricatorFileAttachmentQuery()) + ->setViewer($viewer) + ->withObjectPHIDs(array($object->getPHID())) + ->setLimit($exact_limit + 1) + ->needFiles(true) + ->execute(); + + $visible_attachments = array_slice($attachments, 0, $visible_limit, true); + $visible_phids = mpull($visible_attachments, 'getFilePHID'); + + $handles = $viewer->loadHandles($visible_phids); + + $ref_list = id(new PHUICurtainObjectRefListView()) + ->setViewer($viewer) + ->setEmptyMessage(pht('None')); + + $view_capability = PhabricatorPolicyCapability::CAN_VIEW; + $object_policies = PhabricatorPolicyQuery::loadPolicies( + $viewer, + $object); + $object_policy = idx($object_policies, $view_capability); + + foreach ($visible_attachments as $attachment) { + $file_phid = $attachment->getFilePHID(); + $handle = $handles[$file_phid]; + + $ref = $ref_list->newObjectRefView() + ->setHandle($handle); + + $file = $attachment->getFile(); + if (!$file) { + // ... + } else { + if (!$attachment->isPolicyAttachment()) { + $file_policies = PhabricatorPolicyQuery::loadPolicies( + $viewer, + $file); + $file_policy = idx($file_policies, $view_capability); + + if ($object_policy->isStrongerThanOrEqualTo($file_policy)) { + // The file is not attached to the object, but the file policy + // allows anyone who can see the object to see the file too, so + // there is no material problem with the file not being attached. + } else { + $attach_uri = urisprintf( + '/file/ui/curtain/attach/%s/%s/', + $object->getPHID(), + $file->getPHID()); + + $attached_link = javelin_tag( + 'a', + array( + 'href' => $attach_uri, + 'sigil' => 'workflow', + ), + pht('File Not Attached')); + + $ref->setExiled( + true, + $attached_link); + } + } + } + + $epoch = $attachment->getDateCreated(); + $ref->setEpoch($epoch); + } + + $show_all = (count($visible_attachments) < count($attachments)); + if ($show_all) { + $view_all_uri = urisprintf( + '/file/ui/curtain/list/%s/', + $object->getPHID()); + + $loaded_count = count($attachments); + if ($loaded_count > $exact_limit) { + $link_text = pht('View All Files'); + } else { + $link_text = pht('View All %d Files', new PhutilNumber($loaded_count)); + } + + $ref_list->newTailLink() + ->setURI($view_all_uri) + ->setText($link_text) + ->setWorkflow(true); + } + + return $this->newPanel() + ->setHeaderText(pht('Referenced Files')) + ->setOrder(15000) + ->appendChild($ref_list); + } + + +} diff --git a/src/applications/files/format/__tests__/PhabricatorFileStorageFormatTestCase.php b/src/applications/files/format/__tests__/PhabricatorFileStorageFormatTestCase.php index b3425ba313..bb850edc85 100644 --- a/src/applications/files/format/__tests__/PhabricatorFileStorageFormatTestCase.php +++ b/src/applications/files/format/__tests__/PhabricatorFileStorageFormatTestCase.php @@ -45,6 +45,10 @@ final class PhabricatorFileStorageFormatTestCase extends PhabricatorTestCase { } public function testAES256Storage() { + if (!function_exists('openssl_encrypt')) { + $this->assertSkipped(pht('No OpenSSL extension available.')); + } + $engine = new PhabricatorTestStorageEngine(); $key_name = 'test.abcd'; diff --git a/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php b/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php index 8e2d0cf0c9..9f37bdcaab 100644 --- a/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php +++ b/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php @@ -5,7 +5,7 @@ final class PhabricatorEmbedFileRemarkupRule private $viewer; - const KEY_EMBED_FILE_PHIDS = 'phabricator.embedded-file-phids'; + const KEY_ATTACH_INTENT_FILE_PHIDS = 'files.attach-intent'; protected function getObjectNamePrefix() { return 'F'; @@ -23,13 +23,44 @@ final class PhabricatorEmbedFileRemarkupRule PhabricatorFileThumbnailTransform::TRANSFORM_PREVIEW, )) ->execute(); + $objects = mpull($objects, null, 'getID'); - $phids_key = self::KEY_EMBED_FILE_PHIDS; - $phids = $engine->getTextMetadata($phids_key, array()); - foreach (mpull($objects, 'getPHID') as $phid) { - $phids[] = $phid; + + // Identify files embedded in the block with "attachment intent", i.e. + // those files which the user appears to want to attach to the object. + // Files referenced inside quoted blocks are not considered to have this + // attachment intent. + + $metadata_key = self::KEY_RULE_OBJECT.'.'.$this->getObjectNamePrefix(); + $metadata = $engine->getTextMetadata($metadata_key, array()); + + $attach_key = self::KEY_ATTACH_INTENT_FILE_PHIDS; + $attach_phids = $engine->getTextMetadata($attach_key, array()); + + foreach ($metadata as $item) { + + // If this reference was inside a quoted block, don't count it. Quoting + // someone else doesn't establish an intent to attach a file. + $depth = idx($item, 'quote.depth'); + if ($depth > 0) { + continue; + } + + $id = $item['id']; + $file = idx($objects, $id); + + if (!$file) { + continue; + } + + $attach_phids[] = $file->getPHID(); } - $engine->setTextMetadata($phids_key, $phids); + + $attach_phids = array_fuse($attach_phids); + $attach_phids = array_keys($attach_phids); + + $engine->setTextMetadata($attach_key, $attach_phids); + return $objects; } diff --git a/src/applications/files/phid/PhabricatorFileFilePHIDType.php b/src/applications/files/phid/PhabricatorFileFilePHIDType.php index c847c7f1a5..c583595c4d 100644 --- a/src/applications/files/phid/PhabricatorFileFilePHIDType.php +++ b/src/applications/files/phid/PhabricatorFileFilePHIDType.php @@ -39,6 +39,9 @@ final class PhabricatorFileFilePHIDType extends PhabricatorPHIDType { $handle->setName("F{$id}"); $handle->setFullName("F{$id}: {$name}"); $handle->setURI($uri); + + $icon = FileTypeIcon::getFileIcon($name); + $handle->setIcon($icon); } } diff --git a/src/applications/files/query/PhabricatorFileAttachmentQuery.php b/src/applications/files/query/PhabricatorFileAttachmentQuery.php new file mode 100644 index 0000000000..5fb37dc32e --- /dev/null +++ b/src/applications/files/query/PhabricatorFileAttachmentQuery.php @@ -0,0 +1,131 @@ +objectPHIDs = $object_phids; + return $this; + } + + public function withFilePHIDs(array $file_phids) { + $this->filePHIDs = $file_phids; + return $this; + } + + public function withVisibleFiles($visible_files) { + $this->visibleFiles = $visible_files; + return $this; + } + + public function needFiles($need) { + $this->needFiles = $need; + return $this; + } + + public function newResultObject() { + return new PhabricatorFileAttachment(); + } + + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); + + if ($this->objectPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'attachments.objectPHID IN (%Ls)', + $this->objectPHIDs); + } + + if ($this->filePHIDs !== null) { + $where[] = qsprintf( + $conn, + 'attachments.filePHID IN (%Ls)', + $this->filePHIDs); + } + + return $where; + } + + protected function willFilterPage(array $attachments) { + $viewer = $this->getViewer(); + $object_phids = array(); + + foreach ($attachments as $attachment) { + $object_phid = $attachment->getObjectPHID(); + $object_phids[$object_phid] = $object_phid; + } + + if ($object_phids) { + $objects = id(new PhabricatorObjectQuery()) + ->setViewer($viewer) + ->setParentQuery($this) + ->withPHIDs($object_phids) + ->execute(); + $objects = mpull($objects, null, 'getPHID'); + } else { + $objects = array(); + } + + foreach ($attachments as $key => $attachment) { + $object_phid = $attachment->getObjectPHID(); + $object = idx($objects, $object_phid); + + if (!$object) { + $this->didRejectResult($attachment); + unset($attachments[$key]); + continue; + } + + $attachment->attachObject($object); + } + + if ($this->needFiles) { + $file_phids = array(); + foreach ($attachments as $attachment) { + $file_phid = $attachment->getFilePHID(); + $file_phids[$file_phid] = $file_phid; + } + + if ($file_phids) { + $files = id(new PhabricatorFileQuery()) + ->setViewer($viewer) + ->setParentQuery($this) + ->withPHIDs($file_phids) + ->execute(); + $files = mpull($files, null, 'getPHID'); + } else { + $files = array(); + } + + foreach ($attachments as $key => $attachment) { + $file_phid = $attachment->getFilePHID(); + $file = idx($files, $file_phid); + + if ($this->visibleFiles && !$file) { + $this->didRejectResult($attachment); + unset($attachments[$key]); + continue; + } + + $attachment->attachFile($file); + } + } + + return $attachments; + } + + protected function getPrimaryTableAlias() { + return 'attachments'; + } + + public function getQueryApplicationClass() { + return 'PhabricatorFilesApplication'; + } + +} diff --git a/src/applications/files/query/PhabricatorFileQuery.php b/src/applications/files/query/PhabricatorFileQuery.php index c19574acaa..80e511b1e2 100644 --- a/src/applications/files/query/PhabricatorFileQuery.php +++ b/src/applications/files/query/PhabricatorFileQuery.php @@ -20,6 +20,7 @@ final class PhabricatorFileQuery private $builtinKeys; private $isBuiltin; private $storageEngines; + private $attachedObjectPHIDs; public function withIDs(array $ids) { $this->ids = $ids; @@ -61,6 +62,11 @@ final class PhabricatorFileQuery return $this; } + public function withAttachedObjectPHIDs(array $phids) { + $this->attachedObjectPHIDs = $phids; + return $this; + } + /** * Select files which are transformations of some other file. For example, * you can use this query to find previously generated thumbnails of an image @@ -203,16 +209,12 @@ final class PhabricatorFileQuery // If we have any files left which do need objects, load the edges now. $object_phids = array(); if ($need_objects) { - $edge_type = PhabricatorFileHasObjectEdgeType::EDGECONST; - $file_phids = mpull($need_objects, 'getPHID'); - - $edges = id(new PhabricatorEdgeQuery()) - ->withSourcePHIDs($file_phids) - ->withEdgeTypes(array($edge_type)) - ->execute(); + $attachments_map = $this->newAttachmentsMap($need_objects); foreach ($need_objects as $file) { - $phids = array_keys($edges[$file->getPHID()][$edge_type]); + $file_phid = $file->getPHID(); + $phids = $attachments_map[$file_phid]; + $file->attachObjectPHIDs($phids); if ($is_omnipotent) { @@ -299,6 +301,32 @@ final class PhabricatorFileQuery return $files; } + private function newAttachmentsMap(array $files) { + $file_phids = mpull($files, 'getPHID'); + + $attachments_table = new PhabricatorFileAttachment(); + $attachments_conn = $attachments_table->establishConnection('r'); + + $attachments = queryfx_all( + $attachments_conn, + 'SELECT filePHID, objectPHID FROM %R WHERE filePHID IN (%Ls) + AND attachmentMode IN (%Ls)', + $attachments_table, + $file_phids, + array( + PhabricatorFileAttachment::MODE_ATTACH, + )); + + $attachments_map = array_fill_keys($file_phids, array()); + foreach ($attachments as $row) { + $file_phid = $row['filePHID']; + $object_phid = $row['objectPHID']; + $attachments_map[$file_phid][] = $object_phid; + } + + return $attachments_map; + } + protected function didFilterPage(array $files) { $xform_keys = $this->needTransforms; if ($xform_keys !== null) { @@ -347,9 +375,24 @@ final class PhabricatorFileQuery id(new PhabricatorTransformedFile())->getTableName()); } + if ($this->shouldJoinAttachmentsTable()) { + $joins[] = qsprintf( + $conn, + 'JOIN %R attachments ON attachments.filePHID = f.phid + AND attachmentMode IN (%Ls)', + new PhabricatorFileAttachment(), + array( + PhabricatorFileAttachment::MODE_ATTACH, + )); + } + return $joins; } + private function shouldJoinAttachmentsTable() { + return ($this->attachedObjectPHIDs !== null); + } + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); @@ -482,6 +525,13 @@ final class PhabricatorFileQuery $this->storageEngines); } + if ($this->attachedObjectPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'attachments.objectPHID IN (%Ls)', + $this->attachedObjectPHIDs); + } + return $where; } diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php index c78d4e1941..cc1b701dcd 100644 --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -1416,28 +1416,25 @@ final class PhabricatorFile extends PhabricatorFileDAO * @return this */ public function attachToObject($phid) { - $edge_type = PhabricatorObjectHasFileEdgeType::EDGECONST; + $attachment_table = new PhabricatorFileAttachment(); + $attachment_conn = $attachment_table->establishConnection('w'); - id(new PhabricatorEdgeEditor()) - ->addEdge($phid, $edge_type, $this->getPHID()) - ->save(); - - return $this; - } - - - /** - * Remove the policy edge between this file and some object. - * - * @param phid Object PHID to detach from. - * @return this - */ - public function detachFromObject($phid) { - $edge_type = PhabricatorObjectHasFileEdgeType::EDGECONST; - - id(new PhabricatorEdgeEditor()) - ->removeEdge($phid, $edge_type, $this->getPHID()) - ->save(); + queryfx( + $attachment_conn, + 'INSERT INTO %R (objectPHID, filePHID, attachmentMode, + attacherPHID, dateCreated, dateModified) + VALUES (%s, %s, %s, %ns, %d, %d) + ON DUPLICATE KEY UPDATE + attachmentMode = VALUES(attachmentMode), + attacherPHID = VALUES(attacherPHID), + dateModified = VALUES(dateModified)', + $attachment_table, + $phid, + $this->getPHID(), + PhabricatorFileAttachment::MODE_ATTACH, + null, + PhabricatorTime::getNow(), + PhabricatorTime::getNow()); return $this; } diff --git a/src/applications/files/storage/PhabricatorFileAttachment.php b/src/applications/files/storage/PhabricatorFileAttachment.php new file mode 100644 index 0000000000..c6aa170c79 --- /dev/null +++ b/src/applications/files/storage/PhabricatorFileAttachment.php @@ -0,0 +1,123 @@ + array( + 'objectPHID' => 'phid', + 'filePHID' => 'phid', + 'attacherPHID' => 'phid?', + 'attachmentMode' => 'text32', + ), + self::CONFIG_KEY_SCHEMA => array( + 'key_object' => array( + 'columns' => array('objectPHID', 'filePHID'), + 'unique' => true, + ), + 'key_file' => array( + 'columns' => array('filePHID'), + ), + ), + ) + parent::getConfiguration(); + } + + public static function getModeList() { + return array( + self::MODE_ATTACH, + self::MODE_REFERENCE, + self::MODE_DETACH, + ); + } + + public static function getModeNameMap() { + return array( + self::MODE_ATTACH => pht('Attached'), + self::MODE_REFERENCE => pht('Referenced'), + ); + } + + public function isPolicyAttachment() { + switch ($this->getAttachmentMode()) { + case self::MODE_ATTACH: + return true; + default: + return false; + } + } + + public function attachObject($object) { + $this->object = $object; + return $this; + } + + public function getObject() { + return $this->assertAttached($this->object); + } + + public function attachFile(PhabricatorFile $file = null) { + $this->file = $file; + return $this; + } + + public function getFile() { + return $this->assertAttached($this->file); + } + + public function canDetach() { + switch ($this->getAttachmentMode()) { + case self::MODE_ATTACH: + return true; + } + + return false; + } + + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + ); + } + + public function getPolicy($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return PhabricatorPolicies::getMostOpenPolicy(); + } + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return false; + } + + +/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */ + + + public function getExtendedPolicy($capability, PhabricatorUser $viewer) { + return array( + array($this->getObject(), $capability), + ); + } + +} diff --git a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php index 65dc0a12a2..21cb2daf11 100644 --- a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php +++ b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php @@ -100,7 +100,14 @@ final class PhabricatorFileTestCase extends PhabricatorTestCase { $xactions[] = id(new ManiphestTransaction()) ->setTransactionType( ManiphestTaskDescriptionTransaction::TRANSACTIONTYPE) - ->setNewValue('{'.$file->getMonogram().'}'); + ->setNewValue('{'.$file->getMonogram().'}') + ->setMetadataValue( + 'remarkup.control', + array( + 'attachedFilePHIDs' => array( + $file->getPHID(), + ), + )); id(new ManiphestTransactionEditor()) ->setActor($author) @@ -167,9 +174,10 @@ final class PhabricatorFileTestCase extends PhabricatorTestCase { // Create an object and test object policies. - $object = ManiphestTask::initializeNewTask($author); - $object->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()); - $object->save(); + $object = ManiphestTask::initializeNewTask($author) + ->setTitle(pht('File Visibility Test Task')) + ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) + ->save(); $this->assertTrue( $filter->hasCapability( @@ -185,10 +193,53 @@ final class PhabricatorFileTestCase extends PhabricatorTestCase { PhabricatorPolicyCapability::CAN_VIEW), pht('Object Visible to Others')); + // Reference the file in a comment. This should not affect the file + // policy. + + $file_ref = '{F'.$file->getID().'}'; + + $xactions = array(); + $xactions[] = id(new ManiphestTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) + ->attachComment( + id(new ManiphestTransactionComment()) + ->setContent($file_ref)); + + id(new ManiphestTransactionEditor()) + ->setActor($author) + ->setContentSource($this->newContentSource()) + ->applyTransactions($object, $xactions); + + // Test the referenced file's visibility. + $this->assertEqual( + array( + true, + false, + ), + $this->canViewFile($users, $file), + pht('Referenced File Visibility')); + // Attach the file to the object and test that the association opens a // policy exception for the non-author viewer. - $file->attachToObject($object->getPHID()); + $xactions = array(); + $xactions[] = id(new ManiphestTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) + ->setMetadataValue( + 'remarkup.control', + array( + 'attachedFilePHIDs' => array( + $file->getPHID(), + ), + )) + ->attachComment( + id(new ManiphestTransactionComment()) + ->setContent($file_ref)); + + id(new ManiphestTransactionEditor()) + ->setActor($author) + ->setContentSource($this->newContentSource()) + ->applyTransactions($object, $xactions); // Test the attached file's visibility. $this->assertEqual( @@ -224,18 +275,6 @@ final class PhabricatorFileTestCase extends PhabricatorTestCase { ), $this->canViewFile($users, $xform), pht('Attached Thumbnail Visibility')); - - // Detach the object and make sure it affects the thumbnail. - $file->detachFromObject($object->getPHID()); - - // Test the detached thumbnail's visibility. - $this->assertEqual( - array( - true, - false, - ), - $this->canViewFile($users, $xform), - pht('Detached Thumbnail Visibility')); } private function canViewFile(array $users, PhabricatorFile $file) { diff --git a/src/applications/fund/query/FundInitiativeQuery.php b/src/applications/fund/query/FundInitiativeQuery.php index bbb4ef2746..4b18597873 100644 --- a/src/applications/fund/query/FundInitiativeQuery.php +++ b/src/applications/fund/query/FundInitiativeQuery.php @@ -32,10 +32,6 @@ final class FundInitiativeQuery return new FundInitiative(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/guides/module/PhabricatorGuideInstallModule.php b/src/applications/guides/module/PhabricatorGuideInstallModule.php index 172ff7a17b..2299bce260 100644 --- a/src/applications/guides/module/PhabricatorGuideInstallModule.php +++ b/src/applications/guides/module/PhabricatorGuideInstallModule.php @@ -7,7 +7,7 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { } public function getModuleName() { - return pht('Install Phabricator'); + return pht('Install'); } public function getModulePosition() { @@ -66,7 +66,7 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { $icon_bg = 'bg-sky'; $description = pht( 'Authentication providers allow users to register accounts and '. - 'log in to Phabricator.'); + 'log in.'); } $item = id(new PhabricatorGuideItemView()) @@ -78,7 +78,7 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { $guide_items->addItem($item); - $title = pht('Configure Phabricator'); + $title = pht('Configure'); $href = PhabricatorEnv::getURI('/config/'); // Just load any config value at all; if one exists the install has figured @@ -95,7 +95,7 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { $icon = 'fa-sliders'; $icon_bg = 'bg-sky'; $description = pht( - 'Learn how to configure mail and other options in Phabricator.'); + 'Learn how to configure mail and other options.'); } $item = id(new PhabricatorGuideItemView()) @@ -148,7 +148,7 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { $icon = 'fa-bell'; $icon_bg = 'bg-sky'; $description = pht( - 'Phabricator can deliver notifications in real-time with WebSockets.'); + 'Real-time notifications can be delivered with WebSockets.'); } $item = id(new PhabricatorGuideItemView()) @@ -161,11 +161,12 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule { $guide_items->addItem($item); $intro = pht( - 'Phabricator has been successfully installed. These next guides will '. + '%s has been successfully installed. These next guides will '. 'take you through configuration and new user orientation. '. 'These steps are optional, and you can go through them in any order. '. 'If you want to get back to this guide later on, you can find it in '. - '{icon globe} **Applications** under {icon map-o} **Guides**.'); + '{icon globe} **Applications** under {icon map-o} **Guides**.', + PlatformSymbols::getPlatformServerName()); $intro = new PHUIRemarkupView($viewer, $intro); diff --git a/src/applications/guides/module/PhabricatorGuideQuickStartModule.php b/src/applications/guides/module/PhabricatorGuideQuickStartModule.php index 65b07ffe2c..3486ce4ba0 100644 --- a/src/applications/guides/module/PhabricatorGuideQuickStartModule.php +++ b/src/applications/guides/module/PhabricatorGuideQuickStartModule.php @@ -132,7 +132,7 @@ final class PhabricatorGuideQuickStartModule extends PhabricatorGuideModule { $icon = 'fa-globe'; $icon_bg = 'bg-sky'; $description = - pht('See all the applications included in Phabricator.'); + pht('See all available applications.'); $item = id(new PhabricatorGuideItemView()) ->setTitle($title) @@ -159,7 +159,7 @@ final class PhabricatorGuideQuickStartModule extends PhabricatorGuideModule { $icon = 'fa-group'; $icon_bg = 'bg-sky'; $description = - pht('Invite the rest of your team to get started on Phabricator.'); + pht('Invite the rest of your team to get started.'); } $item = id(new PhabricatorGuideItemView()) @@ -172,11 +172,9 @@ final class PhabricatorGuideQuickStartModule extends PhabricatorGuideModule { } $intro = pht( - 'If you\'re new to Phabricator, these optional steps can help you learn '. - 'the basics. Conceptually, Phabricator is structured as a graph, and '. - 'repositories, tasks, and projects are all independent from each other. '. - 'Feel free to set up Phabricator for how you work best, and explore '. - 'these features at your own pace.'); + 'If you\'re new to this software, these optional steps can help you '. + 'learn the basics. Feel free to set things up for how you work best '. + 'and explore these features at your own pace.'); $intro = new PHUIRemarkupView($viewer, $intro); $intro = id(new PHUIDocumentView()) diff --git a/src/applications/harbormaster/artifact/HarbormasterFileArtifact.php b/src/applications/harbormaster/artifact/HarbormasterFileArtifact.php index 91fc025c21..6b536d8efa 100644 --- a/src/applications/harbormaster/artifact/HarbormasterFileArtifact.php +++ b/src/applications/harbormaster/artifact/HarbormasterFileArtifact.php @@ -10,8 +10,7 @@ final class HarbormasterFileArtifact extends HarbormasterArtifact { public function getArtifactTypeDescription() { return pht( - 'Stores a reference to file data which has been uploaded to '. - 'Phabricator.'); + 'Stores a reference to file data.'); } public function getArtifactParameterSpecification() { diff --git a/src/applications/harbormaster/conduit/HarbormasterBuildEditAPIMethod.php b/src/applications/harbormaster/conduit/HarbormasterBuildEditAPIMethod.php new file mode 100644 index 0000000000..c13f4039ca --- /dev/null +++ b/src/applications/harbormaster/conduit/HarbormasterBuildEditAPIMethod.php @@ -0,0 +1,20 @@ +newSendingDocumentationBoxPage($viewer); + $pages[] = $this->newBuildsDocumentationBoxPage($viewer); + $pages[] = $this->newCommandsDocumentationBoxPage($viewer); + $pages[] = $this->newTargetsDocumentationBoxPage($viewer); + $pages[] = $this->newUnitDocumentationBoxPage($viewer); + $pages[] = $this->newLintDocumentationBoxPage($viewer); + + return $pages; + } + + private function newSendingDocumentationBoxPage(PhabricatorUser $viewer) { + $title = pht('Sending Messages'); + $content = pht(<< + + Object Type + PHID Example + Description + + + Harbormaster Buildable + `PHID-HMBB-...` + %s + + + Harbormaster Build + `PHID-HMBD-...` + %s + + + Harbormaster Build Target + `PHID-HMBT-...` + %s + + + +See below for specifics on sending messages to different object types. +EOREMARKUP + , + pht( + 'Buildables may receive control commands like "abort" and "restart". '. + 'Sending a control command to a Buildable is the same as sending it '. + 'to each Build for the Buildable.'), + pht( + 'Builds may receive control commands like "pause", "resume", "abort", '. + 'and "restart".'), + pht( + 'Build Targets may receive build status and result messages, like '. + '"pass" or "fail".')); + + $content = $this->newRemarkupDocumentationView($content); + + return $this->newDocumentationBoxPage($viewer, $title, $content) + ->setAnchor('sending') + ->setIconIcon('fa-envelope-o'); + } + + private function newBuildsDocumentationBoxPage(PhabricatorUser $viewer) { + $title = pht('Updating Builds'); + + $content = pht(<<newRemarkupDocumentationView($content); + + return $this->newDocumentationBoxPage($viewer, $title, $content) + ->setAnchor('builds') + ->setIconIcon('fa-cubes'); + } + + private function newCommandsDocumentationBoxPage(PhabricatorUser $viewer) { + $messages = HarbormasterBuildMessageTransaction::getAllMessages(); + + $rows = array(); + + $rows[] = ''; + $rows[] = ''.pht('Message Type').''; + $rows[] = ''.pht('Description').''; + $rows[] = ''; + + foreach ($messages as $message) { + $row = array(); + + $row[] = sprintf( + '`%s`', + $message->getHarbormasterBuildMessageType()); + + $row[] = sprintf( + '%s', + $message->getHarbormasterBuildMessageDescription()); + + $rows[] = sprintf( + '%s', + implode("\n", $row)); + } + + $message_table = sprintf( + '%s
', + implode("\n", $rows)); + + $title = pht('Control Commands'); + + $content = pht(<< + + Object Type + PHID Example + + Description + + + Harbormaster Buildable + `PHID-HMBB-...` + {icon check color=green} + Buildables may receive control commands. + + + Harbormaster Build + `PHID-HMBD-...` + {icon check color=green} + Builds may receive control commands. + + + Harbormaster Build Target + `PHID-HMBT-...` + {icon times color=red} + You may **NOT** send control commands to build targets. + + + +You can send these commands: + +%s + +To send a command message, specify the PHID of the object you would like to +receive the message using the `receiver` parameter, and specify the message +type using the `type` parameter. + +EOREMARKUP + , + $message_table); + + $content = $this->newRemarkupDocumentationView($content); + + return $this->newDocumentationBoxPage($viewer, $title, $content) + ->setAnchor('commands') + ->setIconIcon('fa-exclamation-triangle'); + } + + private function newTargetsDocumentationBoxPage(PhabricatorUser $viewer) { $messages = HarbormasterMessageType::getAllMessages(); - $head_type = pht('Constant'); - $head_desc = pht('Description'); - $head_key = pht('Key'); $head_type = pht('Type'); - $head_name = pht('Name'); + $head_desc = pht('Description'); $rows = array(); $rows[] = "| {$head_type} | {$head_desc} |"; @@ -31,6 +235,84 @@ final class HarbormasterSendMessageConduitAPIMethod } $message_table = implode("\n", $rows); + $content = pht(<< + + Object Type + PHID Example + + Description + + + Harbormaster Buildable + `PHID-HMBB-...` + {icon times color=red} + Buildables may **NOT** receive status or result messages. + + + Harbormaster Build + `PHID-HMBD-...` + {icon times color=red} + Builds may **NOT** receive status or result messages. + + + Harbormaster Build Target + `PHID-HMBT-...` + {icon check color=green} + Report build status and results to Build Targets. + + + +The simplest way to use this method to report build results is to call it once +after the build finishes with a `pass` or `fail` message. This will record the +build result, and continue the next step in the build if the build was waiting +for a result. + +When you send a status message about a build target, you can optionally include +detailed `lint` or `unit` results alongside the message. See below for details. + +If you want to report intermediate results but a build hasn't completed yet, +you can use the `work` message. This message doesn't have any direct effects, +but allows you to send additional data to update the progress of the build +target. The target will continue waiting for a completion message, but the UI +will update to show the progress which has been made. + +When sending a message to a build target to report the status or results of +a build, your message must include a `type` which describes the overall state +of the build. For example, use `pass` to tell Harbormaster that a build target +completed successfully. + +Supported message types are: + +%s + +EOREMARKUP + , + $message_table); + + $title = pht('Updating Build Targets'); + + $content = $this->newRemarkupDocumentationView($content); + + return $this->newDocumentationBoxPage($viewer, $title, $content) + ->setAnchor('targets') + ->setIconIcon('fa-bullseye'); + } + + private function newUnitDocumentationBoxPage(PhabricatorUser $viewer) { + $head_key = pht('Key'); + $head_desc = pht('Description'); + $head_name = pht('Name'); + $head_type = pht('Type'); + $rows = array(); $rows[] = "| {$head_key} | {$head_type} | {$head_desc} |"; $rows[] = '|-------------|--------------|--------------|'; @@ -55,6 +337,64 @@ final class HarbormasterSendMessageConduitAPIMethod } $result_table = implode("\n", $rows); + $valid_unit = array( + array( + 'name' => 'PassingTest', + 'result' => ArcanistUnitTestResult::RESULT_PASS, + ), + array( + 'name' => 'FailingTest', + 'result' => ArcanistUnitTestResult::RESULT_FAIL, + ), + ); + + $json = new PhutilJSON(); + $valid_unit = $json->encodeAsList($valid_unit); + + + $title = pht('Reporting Unit Results'); + + $content = pht(<<newRemarkupDocumentationView($content); + + return $this->newDocumentationBoxPage($viewer, $title, $content) + ->setAnchor('unit'); + } + + private function newLintDocumentationBoxPage(PhabricatorUser $viewer) { + + $head_key = pht('Key'); + $head_desc = pht('Description'); + $head_name = pht('Name'); + $head_type = pht('Type'); + $rows = array(); $rows[] = "| {$head_key} | {$head_type} | {$head_desc} |"; $rows[] = '|-------------|--------------|--------------|'; @@ -76,17 +416,6 @@ final class HarbormasterSendMessageConduitAPIMethod } $severity_table = implode("\n", $rows); - $valid_unit = array( - array( - 'name' => 'PassingTest', - 'result' => ArcanistUnitTestResult::RESULT_PASS, - ), - array( - 'name' => 'FailingTest', - 'result' => ArcanistUnitTestResult::RESULT_FAIL, - ), - ); - $valid_lint = array( array( 'name' => pht('Syntax Error'), @@ -109,104 +438,58 @@ final class HarbormasterSendMessageConduitAPIMethod ); $json = new PhutilJSON(); - $valid_unit = $json->encodeAsList($valid_unit); $valid_lint = $json->encodeAsList($valid_lint); - return pht( - "Send a message about the status of a build target to Harbormaster, ". - "notifying the application of build results in an external system.". - "\n\n". - "Sending Messages\n". - "================\n". - "If you run external builds, you can use this method to publish build ". - "results back into Harbormaster after the external system finishes work ". - "or as it makes progress.". - "\n\n". - "The simplest way to use this method is to call it once after the ". - "build finishes with a `pass` or `fail` message. This will record the ". - "build result, and continue the next step in the build if the build was ". - "waiting for a result.". - "\n\n". - "When you send a status message about a build target, you can ". - "optionally include detailed `lint` or `unit` results alongside the ". - "message. See below for details.". - "\n\n". - "If you want to report intermediate results but a build hasn't ". - "completed yet, you can use the `work` message. This message doesn't ". - "have any direct effects, but allows you to send additional data to ". - "update the progress of the build target. The target will continue ". - "waiting for a completion message, but the UI will update to show the ". - "progress which has been made.". - "\n\n". - "Message Types\n". - "=============\n". - "When you send Harbormaster a message, you must include a `type`, ". - "which describes the overall state of the build. For example, use ". - "`pass` to tell Harbormaster that a build completed successfully.". - "\n\n". - "Supported message types are:". - "\n\n". - "%s". - "\n\n". - "Unit Results\n". - "============\n". - "You can report test results alongside a message. The simplest way to ". - "do this is to report all the results alongside a `pass` or `fail` ". - "message, but you can also send a `work` message to report intermediate ". - "results.\n\n". - "To provide unit test results, pass a list of results in the `unit` ". - "parameter. Each result should be a dictionary with these keys:". - "\n\n". - "%s". - "\n\n". - "The `result` parameter recognizes these test results:". - "\n\n". - "%s". - "\n\n". - "This is a simple, valid value for the `unit` parameter. It reports ". - "one passing test and one failing test:\n\n". - "\n\n". - "```lang=json\n". - "%s". - "```". - "\n\n". - "Lint Results\n". - "============\n". - "Like unit test results, you can report lint results alongside a ". - "message. The `lint` parameter should contain results as a list of ". - "dictionaries with these keys:". - "\n\n". - "%s". - "\n\n". - "The `severity` parameter recognizes these severity levels:". - "\n\n". - "%s". - "\n\n". - "This is a simple, valid value for the `lint` parameter. It reports one ". - "error and one warning:". - "\n\n". - "```lang=json\n". - "%s". - "```". - "\n\n", - $message_table, - $unit_table, - $result_table, - $valid_unit, + $title = pht('Reporting Lint Results'); + $content = pht(<<newRemarkupDocumentationView($content); + + return $this->newDocumentationBoxPage($viewer, $title, $content) + ->setAnchor('lint'); } protected function defineParamTypes() { $messages = HarbormasterMessageType::getAllMessages(); + + $more_messages = HarbormasterBuildMessageTransaction::getAllMessages(); + $more_messages = mpull($more_messages, 'getHarbormasterBuildMessageType'); + + $messages = array_merge($messages, $more_messages); + $messages = array_unique($messages); + + sort($messages); + $type_const = $this->formatStringConstants($messages); return array( - 'buildTargetPHID' => 'required phid', + 'receiver' => 'required string|phid', 'type' => 'required '.$type_const, 'unit' => 'optional list', 'lint' => 'optional list', + 'buildTargetPHID' => 'deprecated optional phid', ); } @@ -215,19 +498,90 @@ final class HarbormasterSendMessageConduitAPIMethod } protected function execute(ConduitAPIRequest $request) { - $viewer = $request->getUser(); + $viewer = $request->getViewer(); + + $receiver_name = $request->getValue('receiver'); $build_target_phid = $request->getValue('buildTargetPHID'); - $message_type = $request->getValue('type'); - - $build_target = id(new HarbormasterBuildTargetQuery()) - ->setViewer($viewer) - ->withPHIDs(array($build_target_phid)) - ->executeOne(); - if (!$build_target) { - throw new Exception(pht('No such build target!')); + if ($build_target_phid !== null) { + if ($receiver_name === null) { + $receiver_name = $build_target_phid; + } else { + throw new Exception( + pht( + 'Call specifies both "receiver" and "buildTargetPHID". '. + 'When using the modern "receiver" parameter, omit the '. + 'deprecated "buildTargetPHID" parameter.')); + } } + if (!strlen($receiver_name)) { + throw new Exception( + pht( + 'Call omits required "receiver" parameter. Specify the PHID '. + 'of the object you want to send a message to.')); + } + + $message_type = $request->getValue('type'); + if (!strlen($message_type)) { + throw new Exception( + pht( + 'Call omits required "type" parameter. Specify the type of '. + 'message you want to send.')); + } + + $receiver_object = id(new PhabricatorObjectQuery()) + ->setViewer($viewer) + ->withNames(array($receiver_name)) + ->executeOne(); + if (!$receiver_object) { + throw new Exception( + pht( + 'Unable to load object "%s" to receive message.', + $receiver_name)); + } + + $is_target = ($receiver_object instanceof HarbormasterBuildTarget); + if ($is_target) { + return $this->sendToTarget($request, $message_type, $receiver_object); + } + + if ($request->getValue('unit') !== null) { + throw new Exception( + pht( + 'Call includes "unit" parameter. This parameter must be omitted '. + 'when the receiver is not a Build Target.')); + } + + if ($request->getValue('lint') !== null) { + throw new Exception( + pht( + 'Call includes "lint" parameter. This parameter must be omitted '. + 'when the receiver is not a Build Target.')); + } + + $is_build = ($receiver_object instanceof HarbormasterBuild); + if ($is_build) { + return $this->sendToBuild($request, $message_type, $receiver_object); + } + + $is_buildable = ($receiver_object instanceof HarbormasterBuildable); + if ($is_buildable) { + return $this->sendToBuildable($request, $message_type, $receiver_object); + } + + throw new Exception( + pht( + 'Receiver object (of class "%s") is not a valid receiver.', + get_class($receiver_object))); + } + + private function sendToTarget( + ConduitAPIRequest $request, + $message_type, + HarbormasterBuildTarget $build_target) { + $viewer = $request->getViewer(); + $save = array(); $lint_messages = $request->getValue('lint', array()); @@ -270,4 +624,67 @@ final class HarbormasterSendMessageConduitAPIMethod return null; } + private function sendToBuild( + ConduitAPIRequest $request, + $message_type, + HarbormasterBuild $build) { + $viewer = $request->getViewer(); + + $xaction = + HarbormasterBuildMessageTransaction::getTransactionObjectForMessageType( + $message_type); + if (!$xaction) { + throw new Exception( + pht( + 'Message type "%s" is not supported.', + $message_type)); + } + + // NOTE: This is a slightly weaker check than we perform in the web UI. + // We allow API callers to send a "pause" message to a pausing build, + // for example, even though the message will have no effect. + $xaction->assertCanApplyMessage($viewer, $build); + + $build->sendMessage($viewer, $xaction->getHarbormasterBuildMessageType()); + } + + private function sendToBuildable( + ConduitAPIRequest $request, + $message_type, + HarbormasterBuildable $buildable) { + $viewer = $request->getViewer(); + + $xaction = + HarbormasterBuildMessageTransaction::getTransactionObjectForMessageType( + $message_type); + if (!$xaction) { + throw new Exception( + pht( + 'Message type "%s" is not supported.', + $message_type)); + } + + // Reload the Buildable to load Builds. + $buildable = id(new HarbormasterBuildableQuery()) + ->setViewer($viewer) + ->withIDs(array($buildable->getID())) + ->needBuilds(true) + ->executeOne(); + + $can_send = array(); + foreach ($buildable->getBuilds() as $build) { + if ($xaction->canApplyMessage($viewer, $build)) { + $can_send[] = $build; + } + } + + // NOTE: This doesn't actually apply a transaction to the Buildable, + // but that transaction is purely informational and should probably be + // implemented as a Message. + + foreach ($can_send as $build) { + $build->sendMessage($viewer, $xaction->getHarbormasterBuildMessageType()); + } + } + } diff --git a/src/applications/harbormaster/constants/HarbormasterBuildStatus.php b/src/applications/harbormaster/constants/HarbormasterBuildStatus.php index 3ceb8e0686..009758078f 100644 --- a/src/applications/harbormaster/constants/HarbormasterBuildStatus.php +++ b/src/applications/harbormaster/constants/HarbormasterBuildStatus.php @@ -12,6 +12,11 @@ final class HarbormasterBuildStatus extends Phobject { const STATUS_PAUSED = 'paused'; const STATUS_DEADLOCKED = 'deadlocked'; + const PENDING_PAUSING = 'x-pausing'; + const PENDING_RESUMING = 'x-resuming'; + const PENDING_RESTARTING = 'x-restarting'; + const PENDING_ABORTING = 'x-aborting'; + private $key; private $properties; @@ -56,6 +61,37 @@ final class HarbormasterBuildStatus extends Phobject { return ($this->key === self::STATUS_FAILED); } + public function isAborting() { + return ($this->key === self::PENDING_ABORTING); + } + + public function isRestarting() { + return ($this->key === self::PENDING_RESTARTING); + } + + public function isResuming() { + return ($this->key === self::PENDING_RESUMING); + } + + public function isPausing() { + return ($this->key === self::PENDING_PAUSING); + } + + public function isPending() { + return ($this->key === self::STATUS_PENDING); + } + + public function getIconIcon() { + return $this->getProperty('icon'); + } + + public function getIconColor() { + return $this->getProperty('color'); + } + + public function getName() { + return $this->getProperty('name'); + } /** * Get a human readable name for a build status constant. @@ -185,8 +221,8 @@ final class HarbormasterBuildStatus extends Phobject { ), self::STATUS_PAUSED => array( 'name' => pht('Paused'), - 'icon' => 'fa-minus-circle', - 'color' => 'dark', + 'icon' => 'fa-pause', + 'color' => 'yellow', 'color.ansi' => 'yellow', 'isBuilding' => false, 'isComplete' => false, @@ -199,6 +235,38 @@ final class HarbormasterBuildStatus extends Phobject { 'isBuilding' => false, 'isComplete' => true, ), + self::PENDING_PAUSING => array( + 'name' => pht('Pausing'), + 'icon' => 'fa-exclamation-triangle', + 'color' => 'red', + 'color.ansi' => 'red', + 'isBuilding' => false, + 'isComplete' => false, + ), + self::PENDING_RESUMING => array( + 'name' => pht('Resuming'), + 'icon' => 'fa-exclamation-triangle', + 'color' => 'red', + 'color.ansi' => 'red', + 'isBuilding' => false, + 'isComplete' => false, + ), + self::PENDING_RESTARTING => array( + 'name' => pht('Restarting'), + 'icon' => 'fa-exclamation-triangle', + 'color' => 'red', + 'color.ansi' => 'red', + 'isBuilding' => false, + 'isComplete' => false, + ), + self::PENDING_ABORTING => array( + 'name' => pht('Aborting'), + 'icon' => 'fa-exclamation-triangle', + 'color' => 'red', + 'color.ansi' => 'red', + 'isBuilding' => false, + 'isComplete' => false, + ), ); } diff --git a/src/applications/harbormaster/controller/HarbormasterBuildActionController.php b/src/applications/harbormaster/controller/HarbormasterBuildActionController.php index 6a4a2b1fee..0156e485f1 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildActionController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildActionController.php @@ -22,24 +22,13 @@ final class HarbormasterBuildActionController return new Aphront404Response(); } - switch ($action) { - case HarbormasterBuildCommand::COMMAND_RESTART: - $can_issue = $build->canRestartBuild(); - break; - case HarbormasterBuildCommand::COMMAND_PAUSE: - $can_issue = $build->canPauseBuild(); - break; - case HarbormasterBuildCommand::COMMAND_RESUME: - $can_issue = $build->canResumeBuild(); - break; - case HarbormasterBuildCommand::COMMAND_ABORT: - $can_issue = $build->canAbortBuild(); - break; - default: - return new Aphront400Response(); - } + $xaction = + HarbormasterBuildMessageTransaction::getTransactionObjectForMessageType( + $action); - $build->assertCanIssueCommand($viewer, $action); + if (!$xaction) { + return new Aphront404Response(); + } switch ($via) { case 'buildable': @@ -50,100 +39,29 @@ final class HarbormasterBuildActionController break; } - if ($request->isDialogFormPost() && $can_issue) { - $build->sendMessage($viewer, $action); + try { + $xaction->assertCanSendMessage($viewer, $build); + } catch (HarbormasterMessageException $ex) { + return $this->newDialog() + ->setTitle($ex->getTitle()) + ->appendChild($ex->getBody()) + ->addCancelButton($return_uri); + } + + if ($request->isDialogFormPost()) { + $build->sendMessage($viewer, $xaction->getHarbormasterBuildMessageType()); return id(new AphrontRedirectResponse())->setURI($return_uri); } - switch ($action) { - case HarbormasterBuildCommand::COMMAND_RESTART: - if ($can_issue) { - $title = pht('Really restart build?'); - $body = pht( - 'Progress on this build will be discarded and the build will '. - 'restart. Side effects of the build will occur again. Really '. - 'restart build?'); - $submit = pht('Restart Build'); - } else { - try { - $build->assertCanRestartBuild(); - throw new Exception(pht('Expected to be unable to restart build.')); - } catch (HarbormasterRestartException $ex) { - $title = $ex->getTitle(); - $body = $ex->getBody(); - } - } - break; - case HarbormasterBuildCommand::COMMAND_ABORT: - if ($can_issue) { - $title = pht('Really abort build?'); - $body = pht( - 'Progress on this build will be discarded. Really '. - 'abort build?'); - $submit = pht('Abort Build'); - } else { - $title = pht('Unable to Abort Build'); - $body = pht('You can not abort this build.'); - } - break; - case HarbormasterBuildCommand::COMMAND_PAUSE: - if ($can_issue) { - $title = pht('Really pause build?'); - $body = pht( - 'If you pause this build, work will halt once the current steps '. - 'complete. You can resume the build later.'); - $submit = pht('Pause Build'); - } else { - $title = pht('Unable to Pause Build'); - if ($build->isComplete()) { - $body = pht( - 'This build is already complete. You can not pause a completed '. - 'build.'); - } else if ($build->isPaused()) { - $body = pht( - 'This build is already paused. You can not pause a build which '. - 'has already been paused.'); - } else if ($build->isPausing()) { - $body = pht( - 'This build is already pausing. You can not reissue a pause '. - 'command to a pausing build.'); - } else { - $body = pht( - 'This build can not be paused.'); - } - } - break; - case HarbormasterBuildCommand::COMMAND_RESUME: - if ($can_issue) { - $title = pht('Really resume build?'); - $body = pht( - 'Work will continue on the build. Really resume?'); - $submit = pht('Resume Build'); - } else { - $title = pht('Unable to Resume Build'); - if ($build->isResuming()) { - $body = pht( - 'This build is already resuming. You can not reissue a resume '. - 'command to a resuming build.'); - } else if (!$build->isPaused()) { - $body = pht( - 'This build is not paused. You can only resume a paused '. - 'build.'); - } - } - break; - } + $title = $xaction->newConfirmPromptTitle(); + $body = $xaction->newConfirmPromptBody(); + $submit = $xaction->getHarbormasterBuildMessageName(); - $dialog = $this->newDialog() + return $this->newDialog() ->setTitle($title) ->appendChild($body) - ->addCancelButton($return_uri); - - if ($can_issue) { - $dialog->addSubmitButton($submit); - } - - return $dialog; + ->addCancelButton($return_uri) + ->addSubmitButton($submit); } } diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php index b0f2654ea6..a17948fc8d 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php @@ -32,21 +32,13 @@ final class HarbormasterBuildViewController ->setPolicyObject($build) ->setHeaderIcon('fa-cubes'); - $is_restarting = $build->isRestarting(); + $status = $build->getBuildPendingStatusObject(); - if ($is_restarting) { - $page_header->setStatus( - 'fa-exclamation-triangle', 'red', pht('Restarting')); - } else if ($build->isPausing()) { - $page_header->setStatus( - 'fa-exclamation-triangle', 'red', pht('Pausing')); - } else if ($build->isResuming()) { - $page_header->setStatus( - 'fa-exclamation-triangle', 'red', pht('Resuming')); - } else if ($build->isAborting()) { - $page_header->setStatus( - 'fa-exclamation-triangle', 'red', pht('Aborting')); - } + $status_icon = $status->getIconIcon(); + $status_color = $status->getIconColor(); + $status_name = $status->getName(); + + $page_header->setStatus($status_icon, $status_color, $status_name); $max_generation = (int)$build->getBuildGeneration(); if ($max_generation === 0) { @@ -55,7 +47,7 @@ final class HarbormasterBuildViewController $min_generation = 1; } - if ($is_restarting) { + if ($build->isRestarting()) { $max_generation = $max_generation + 1; } @@ -541,64 +533,32 @@ final class HarbormasterBuildViewController $curtain = $this->newCurtainView($build); - $can_restart = - $build->canRestartBuild() && - $build->canIssueCommand( - $viewer, - HarbormasterBuildCommand::COMMAND_RESTART); + $messages = array( + new HarbormasterBuildMessageRestartTransaction(), + new HarbormasterBuildMessagePauseTransaction(), + new HarbormasterBuildMessageResumeTransaction(), + new HarbormasterBuildMessageAbortTransaction(), + ); - $can_pause = - $build->canPauseBuild() && - $build->canIssueCommand( - $viewer, - HarbormasterBuildCommand::COMMAND_PAUSE); + foreach ($messages as $message) { + $can_send = $message->canSendMessage($viewer, $build); - $can_resume = - $build->canResumeBuild() && - $build->canIssueCommand( - $viewer, - HarbormasterBuildCommand::COMMAND_RESUME); + $message_uri = urisprintf( + '/build/%s/%d/', + $message->getHarbormasterBuildMessageType(), + $id); + $message_uri = $this->getApplicationURI($message_uri); - $can_abort = - $build->canAbortBuild() && - $build->canIssueCommand( - $viewer, - HarbormasterBuildCommand::COMMAND_ABORT); + $action = id(new PhabricatorActionView()) + ->setName($message->getHarbormasterBuildMessageName()) + ->setIcon($message->getIcon()) + ->setHref($message_uri) + ->setDisabled(!$can_send) + ->setWorkflow(true); - $curtain->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Restart Build')) - ->setIcon('fa-repeat') - ->setHref($this->getApplicationURI('/build/restart/'.$id.'/')) - ->setDisabled(!$can_restart) - ->setWorkflow(true)); - - if ($build->canResumeBuild()) { - $curtain->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Resume Build')) - ->setIcon('fa-play') - ->setHref($this->getApplicationURI('/build/resume/'.$id.'/')) - ->setDisabled(!$can_resume) - ->setWorkflow(true)); - } else { - $curtain->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Pause Build')) - ->setIcon('fa-pause') - ->setHref($this->getApplicationURI('/build/pause/'.$id.'/')) - ->setDisabled(!$can_pause) - ->setWorkflow(true)); + $curtain->addAction($action); } - $curtain->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Abort Build')) - ->setIcon('fa-exclamation-triangle') - ->setHref($this->getApplicationURI('/build/abort/'.$id.'/')) - ->setDisabled(!$can_abort) - ->setWorkflow(true)); - return $curtain; } @@ -624,10 +584,6 @@ final class HarbormasterBuildViewController pht('Build Plan'), $handles[$build->getBuildPlanPHID()]->renderLink()); - $properties->addProperty( - pht('Status'), - $this->getStatus($build)); - return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Properties')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) @@ -681,31 +637,6 @@ final class HarbormasterBuildViewController ->setTable($table); } - - private function getStatus(HarbormasterBuild $build) { - $status_view = new PHUIStatusListView(); - - $item = new PHUIStatusItemView(); - - if ($build->isPausing()) { - $status_name = pht('Pausing'); - $icon = PHUIStatusItemView::ICON_RIGHT; - $color = 'dark'; - } else { - $status = $build->getBuildStatus(); - $status_name = - HarbormasterBuildStatus::getBuildStatusName($status); - $icon = HarbormasterBuildStatus::getBuildStatusIcon($status); - $color = HarbormasterBuildStatus::getBuildStatusColor($status); - } - - $item->setTarget($status_name); - $item->setIcon($icon, $color); - $status_view->addItem($item); - - return $status_view; - } - private function buildMessages(array $messages) { $viewer = $this->getRequest()->getUser(); diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableActionController.php b/src/applications/harbormaster/controller/HarbormasterBuildableActionController.php index b701274eb0..1071870eda 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildableActionController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildableActionController.php @@ -22,299 +22,162 @@ final class HarbormasterBuildableActionController return new Aphront404Response(); } - $issuable = array(); - - $builds = $buildable->getBuilds(); - foreach ($builds as $key => $build) { - switch ($action) { - case HarbormasterBuildCommand::COMMAND_RESTART: - if ($build->canRestartBuild()) { - $issuable[$key] = $build; - } - break; - case HarbormasterBuildCommand::COMMAND_PAUSE: - if ($build->canPauseBuild()) { - $issuable[$key] = $build; - } - break; - case HarbormasterBuildCommand::COMMAND_RESUME: - if ($build->canResumeBuild()) { - $issuable[$key] = $build; - } - break; - case HarbormasterBuildCommand::COMMAND_ABORT: - if ($build->canAbortBuild()) { - $issuable[$key] = $build; - } - break; - default: - return new Aphront400Response(); - } - } - - $restricted = false; - foreach ($issuable as $key => $build) { - if (!$build->canIssueCommand($viewer, $action)) { - $restricted = true; - unset($issuable[$key]); - } - } - - $building = false; - foreach ($issuable as $key => $build) { - if ($build->isBuilding()) { - $building = true; - break; - } + $message = + HarbormasterBuildMessageTransaction::getTransactionObjectForMessageType( + $action); + if (!$message) { + return new Aphront404Response(); } $return_uri = '/'.$buildable->getMonogram(); - if ($request->isDialogFormPost() && $issuable) { + + // See T13348. Actions may apply to only a subset of builds, so give the + // user a preview of what will happen. + + $can_send = array(); + + $rows = array(); + $builds = $buildable->getBuilds(); + foreach ($builds as $key => $build) { + $exception = null; + try { + $message->assertCanSendMessage($viewer, $build); + $can_send[$key] = $build; + } catch (HarbormasterMessageException $ex) { + $exception = $ex; + } + + if (!$exception) { + $icon_icon = $message->getIcon(); + $icon_color = 'green'; + + $title = $message->getHarbormasterBuildMessageName(); + $body = $message->getHarbormasterBuildableMessageEffect(); + } else { + $icon_icon = 'fa-times'; + $icon_color = 'red'; + + $title = $ex->getTitle(); + $body = $ex->getBody(); + } + + $icon = id(new PHUIIconView()) + ->setIcon($icon_icon) + ->setColor($icon_color); + + $build_name = phutil_tag( + 'a', + array( + 'href' => $build->getURI(), + 'target' => '_blank', + ), + pht('%s %s', $build->getObjectName(), $build->getName())); + + $rows[] = array( + $icon, + $build_name, + $title, + $body, + ); + } + + $table = id(new AphrontTableView($rows)) + ->setHeaders( + array( + null, + pht('Build'), + pht('Action'), + pht('Details'), + )) + ->setColumnClasses( + array( + null, + null, + 'pri', + 'wide', + )); + + $table = phutil_tag( + 'div', + array( + 'class' => 'mlt mlb', + ), + $table); + + if ($request->isDialogFormPost() && $can_send) { $editor = id(new HarbormasterBuildableTransactionEditor()) ->setActor($viewer) ->setContentSourceFromRequest($request) ->setContinueOnNoEffect(true) ->setContinueOnMissingFields(true); + $xaction_type = HarbormasterBuildableMessageTransaction::TRANSACTIONTYPE; + $xaction = id(new HarbormasterBuildableTransaction()) - ->setTransactionType(HarbormasterBuildableTransaction::TYPE_COMMAND) + ->setTransactionType($xaction_type) ->setNewValue($action); $editor->applyTransactions($buildable, array($xaction)); - $build_editor = id(new HarbormasterBuildTransactionEditor()) - ->setActor($viewer) - ->setContentSourceFromRequest($request) - ->setContinueOnNoEffect(true) - ->setContinueOnMissingFields(true); - - foreach ($issuable as $build) { - $xaction = id(new HarbormasterBuildTransaction()) - ->setTransactionType(HarbormasterBuildTransaction::TYPE_COMMAND) - ->setNewValue($action); - $build_editor->applyTransactions($build, array($xaction)); + foreach ($can_send as $build) { + $build->sendMessage( + $viewer, + $message->getHarbormasterBuildMessageType()); } return id(new AphrontRedirectResponse())->setURI($return_uri); } - $width = AphrontDialogView::WIDTH_DEFAULT; + if (!$builds) { + $title = pht('No Builds'); + $body = pht( + 'This buildable has no builds, so you can not issue any commands.'); + } else { + if ($can_send) { + $title = $message->newBuildableConfirmPromptTitle( + $builds, + $can_send); - switch ($action) { - case HarbormasterBuildCommand::COMMAND_RESTART: - // See T13348. The "Restart Builds" action may restart only a subset - // of builds, so show the user a preview of which builds will actually - // restart. + $body = $message->newBuildableConfirmPromptBody( + $builds, + $can_send); + } else { + $title = pht('Unable to Send Command'); + $body = pht( + 'You can not send this command to any of the current builds '. + 'for this buildable.'); + } - $body = array(); - - if ($issuable) { - $title = pht('Restart Builds'); - $submit = pht('Restart Builds'); - } else { - $title = pht('Unable to Restart Builds'); - } - - if ($builds) { - $width = AphrontDialogView::WIDTH_FORM; - - $body[] = pht('Builds for this buildable:'); - - $rows = array(); - foreach ($builds as $key => $build) { - if (isset($issuable[$key])) { - $icon = id(new PHUIIconView()) - ->setIcon('fa-repeat green'); - $build_note = pht('Will Restart'); - } else { - $icon = null; - - try { - $build->assertCanRestartBuild(); - } catch (HarbormasterRestartException $ex) { - $icon = id(new PHUIIconView()) - ->setIcon('fa-times red'); - $build_note = pht( - '%s: %s', - phutil_tag('strong', array(), pht('Not Restartable')), - $ex->getTitle()); - } - - if (!$icon) { - try { - $build->assertCanIssueCommand($viewer, $action); - } catch (PhabricatorPolicyException $ex) { - $icon = id(new PHUIIconView()) - ->setIcon('fa-lock red'); - $build_note = pht( - '%s: %s', - phutil_tag('strong', array(), pht('Not Restartable')), - pht('You do not have permission to restart this build.')); - } - } - - if (!$icon) { - $icon = id(new PHUIIconView()) - ->setIcon('fa-times red'); - $build_note = pht('Will Not Restart'); - } - } - - $build_name = phutil_tag( - 'a', - array( - 'href' => $build->getURI(), - 'target' => '_blank', - ), - pht('%s %s', $build->getObjectName(), $build->getName())); - - $rows[] = array( - $icon, - $build_name, - $build_note, - ); - } - - $table = id(new AphrontTableView($rows)) - ->setHeaders( - array( - null, - pht('Build'), - pht('Action'), - )) - ->setColumnClasses( - array( - null, - 'pri', - 'wide', - )); - - $table = phutil_tag( - 'div', - array( - 'class' => 'mlt mlb', - ), - $table); - - $body[] = $table; - } - - if ($issuable) { - $warnings = array(); - - if ($restricted) { - $warnings[] = pht( - 'You only have permission to restart some builds.'); - } - - if ($building) { - $warnings[] = pht( - 'Progress on running builds will be discarded.'); - } - - $warnings[] = pht( - 'When a build is restarted, side effects associated with '. - 'the build may occur again.'); - - $body[] = id(new PHUIInfoView()) - ->setSeverity(PHUIInfoView::SEVERITY_WARNING) - ->setErrors($warnings); - - $body[] = pht('Really restart builds?'); - } else { - if ($restricted) { - $body[] = pht('You do not have permission to restart any builds.'); - } else { - $body[] = pht('No builds can be restarted.'); - } - } - - break; - case HarbormasterBuildCommand::COMMAND_PAUSE: - if ($issuable) { - $title = pht('Really pause builds?'); - - if ($restricted) { - $body = pht( - 'You only have permission to pause some builds. Once the '. - 'current steps complete, work will halt on builds you have '. - 'permission to pause. You can resume the builds later.'); - } else { - $body = pht( - 'If you pause all builds, work will halt once the current steps '. - 'complete. You can resume the builds later.'); - } - $submit = pht('Pause Builds'); - } else { - $title = pht('Unable to Pause Builds'); - - if ($restricted) { - $body = pht('You do not have permission to pause any builds.'); - } else { - $body = pht('No builds can be paused.'); - } - } - break; - case HarbormasterBuildCommand::COMMAND_ABORT: - if ($issuable) { - $title = pht('Really abort builds?'); - if ($restricted) { - $body = pht( - 'You only have permission to abort some builds. Work will '. - 'halt immediately on builds you have permission to abort. '. - 'Progress will be discarded, and builds must be completely '. - 'restarted if you want them to complete.'); - } else { - $body = pht( - 'If you abort all builds, work will halt immediately. Work '. - 'will be discarded, and builds must be completely restarted.'); - } - $submit = pht('Abort Builds'); - } else { - $title = pht('Unable to Abort Builds'); - - if ($restricted) { - $body = pht('You do not have permission to abort any builds.'); - } else { - $body = pht('No builds can be aborted.'); - } - } - break; - case HarbormasterBuildCommand::COMMAND_RESUME: - if ($issuable) { - $title = pht('Really resume builds?'); - if ($restricted) { - $body = pht( - 'You only have permission to resume some builds. Work will '. - 'continue on builds you have permission to resume.'); - } else { - $body = pht('Work will continue on all builds. Really resume?'); - } - - $submit = pht('Resume Builds'); - } else { - $title = pht('Unable to Resume Builds'); - if ($restricted) { - $body = pht('You do not have permission to resume any builds.'); - } else { - $body = pht('No builds can be resumed.'); - } - } - break; + $body = array( + pht('Builds for this buildable:'), + $table, + $body, + ); } - $dialog = id(new AphrontDialogView()) - ->setUser($viewer) - ->setWidth($width) + $warnings = $message->newBuildableConfirmPromptWarnings( + $builds, + $can_send); + + if ($warnings) { + $body[] = id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_WARNING) + ->setErrors($warnings); + } + + $submit = $message->getHarbormasterBuildableMessageName(); + + $dialog = $this->newDialog() + ->setWidth(AphrontDialogView::WIDTH_FULL) ->setTitle($title) ->appendChild($body) ->addCancelButton($return_uri); - if ($issuable) { + if ($can_send) { $dialog->addSubmitButton($submit); } - return id(new AphrontDialogResponse())->setDialog($dialog); + return $dialog; } } diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php index aa433be656..d8f6f2f950 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php @@ -87,76 +87,40 @@ final class HarbormasterBuildableViewController $buildable, PhabricatorPolicyCapability::CAN_EDIT); - $can_restart = false; - $can_resume = false; - $can_pause = false; - $can_abort = false; + $messages = array( + new HarbormasterBuildMessageRestartTransaction(), + new HarbormasterBuildMessagePauseTransaction(), + new HarbormasterBuildMessageResumeTransaction(), + new HarbormasterBuildMessageAbortTransaction(), + ); - $command_restart = HarbormasterBuildCommand::COMMAND_RESTART; - $command_resume = HarbormasterBuildCommand::COMMAND_RESUME; - $command_pause = HarbormasterBuildCommand::COMMAND_PAUSE; - $command_abort = HarbormasterBuildCommand::COMMAND_ABORT; + foreach ($messages as $message) { - foreach ($buildable->getBuilds() as $build) { - if ($build->canRestartBuild()) { - if ($build->canIssueCommand($viewer, $command_restart)) { - $can_restart = true; - } - } - if ($build->canResumeBuild()) { - if ($build->canIssueCommand($viewer, $command_resume)) { - $can_resume = true; - } - } - if ($build->canPauseBuild()) { - if ($build->canIssueCommand($viewer, $command_pause)) { - $can_pause = true; - } - } - if ($build->canAbortBuild()) { - if ($build->canIssueCommand($viewer, $command_abort)) { - $can_abort = true; + // Messages are enabled if they can be sent to at least one build. + $can_send = false; + foreach ($buildable->getBuilds() as $build) { + $can_send = $message->canSendMessage($viewer, $build); + if ($can_send) { + break; } } + + $message_uri = urisprintf( + '/buildable/%d/%s/', + $id, + $message->getHarbormasterBuildMessageType()); + $message_uri = $this->getApplicationURI($message_uri); + + $action = id(new PhabricatorActionView()) + ->setName($message->getHarbormasterBuildableMessageName()) + ->setIcon($message->getIcon()) + ->setHref($message_uri) + ->setDisabled(!$can_send || !$can_edit) + ->setWorkflow(true); + + $curtain->addAction($action); } - $restart_uri = "buildable/{$id}/restart/"; - $pause_uri = "buildable/{$id}/pause/"; - $resume_uri = "buildable/{$id}/resume/"; - $abort_uri = "buildable/{$id}/abort/"; - - $curtain->addAction( - id(new PhabricatorActionView()) - ->setIcon('fa-repeat') - ->setName(pht('Restart Builds')) - ->setHref($this->getApplicationURI($restart_uri)) - ->setWorkflow(true) - ->setDisabled(!$can_restart || !$can_edit)); - - $curtain->addAction( - id(new PhabricatorActionView()) - ->setIcon('fa-pause') - ->setName(pht('Pause Builds')) - ->setHref($this->getApplicationURI($pause_uri)) - ->setWorkflow(true) - ->setDisabled(!$can_pause || !$can_edit)); - - $curtain->addAction( - id(new PhabricatorActionView()) - ->setIcon('fa-play') - ->setName(pht('Resume Builds')) - ->setHref($this->getApplicationURI($resume_uri)) - ->setWorkflow(true) - ->setDisabled(!$can_resume || !$can_edit)); - - $curtain->addAction( - id(new PhabricatorActionView()) - ->setIcon('fa-exclamation-triangle') - ->setName(pht('Abort Builds')) - ->setHref($this->getApplicationURI($abort_uri)) - ->setWorkflow(true) - ->setDisabled(!$can_abort || !$can_edit)); - return $curtain; } @@ -198,56 +162,17 @@ final class HarbormasterBuildableViewController ->setUser($viewer); foreach ($buildable->getBuilds() as $build) { $view_uri = $this->getApplicationURI('/build/'.$build->getID().'/'); + $item = id(new PHUIObjectItemView()) ->setObjectName(pht('Build %d', $build->getID())) ->setHeader($build->getName()) ->setHref($view_uri); - $status = $build->getBuildStatus(); - $status_color = HarbormasterBuildStatus::getBuildStatusColor($status); - $status_name = HarbormasterBuildStatus::getBuildStatusName($status); - $item->setStatusIcon('fa-dot-circle-o '.$status_color, $status_name); - $item->addAttribute($status_name); + $status = $build->getBuildPendingStatusObject(); - if ($build->isRestarting()) { - $item->addIcon('fa-repeat', pht('Restarting')); - } else if ($build->isPausing()) { - $item->addIcon('fa-pause', pht('Pausing')); - } else if ($build->isResuming()) { - $item->addIcon('fa-play', pht('Resuming')); - } - - $build_id = $build->getID(); - - $restart_uri = "build/restart/{$build_id}/buildable/"; - $resume_uri = "build/resume/{$build_id}/buildable/"; - $pause_uri = "build/pause/{$build_id}/buildable/"; - $abort_uri = "build/abort/{$build_id}/buildable/"; - - $item->addAction( - id(new PHUIListItemView()) - ->setIcon('fa-repeat') - ->setName(pht('Restart')) - ->setHref($this->getApplicationURI($restart_uri)) - ->setWorkflow(true) - ->setDisabled(!$build->canRestartBuild())); - - if ($build->canResumeBuild()) { - $item->addAction( - id(new PHUIListItemView()) - ->setIcon('fa-play') - ->setName(pht('Resume')) - ->setHref($this->getApplicationURI($resume_uri)) - ->setWorkflow(true)); - } else { - $item->addAction( - id(new PHUIListItemView()) - ->setIcon('fa-pause') - ->setName(pht('Pause')) - ->setHref($this->getApplicationURI($pause_uri)) - ->setWorkflow(true) - ->setDisabled(!$build->canPauseBuild())); - } + $item->setStatusIcon( + $status->getIconIcon().' '.$status->getIconColor(), + $status->getName()); $targets = $build->getBuildTargets(); diff --git a/src/applications/harbormaster/controller/HarbormasterPlanRunController.php b/src/applications/harbormaster/controller/HarbormasterPlanRunController.php index 5d80d421aa..cfa27fec1d 100644 --- a/src/applications/harbormaster/controller/HarbormasterPlanRunController.php +++ b/src/applications/harbormaster/controller/HarbormasterPlanRunController.php @@ -84,7 +84,7 @@ final class HarbormasterPlanRunController extends HarbormasterPlanController { "example, `rX123456` or `D123`).\n\n". "For more detailed output, you can also run manual builds from ". "the command line:\n\n". - " phabricator/ $ ./bin/harbormaster build --plan %s", + " $ ./bin/harbormaster build --plan %s", $plan->getID())) ->appendChild( id(new AphrontFormTextControl()) diff --git a/src/applications/harbormaster/customfield/HarbormasterBuildStepCoreCustomField.php b/src/applications/harbormaster/customfield/HarbormasterBuildStepCoreCustomField.php index 6f8fe28c6e..c83ae46086 100644 --- a/src/applications/harbormaster/customfield/HarbormasterBuildStepCoreCustomField.php +++ b/src/applications/harbormaster/customfield/HarbormasterBuildStepCoreCustomField.php @@ -30,8 +30,8 @@ final class HarbormasterBuildStepCoreCustomField 'After completing this build step Harbormaster can continue the '. 'build normally, or it can pause the build and wait for a message. '. 'If you are using this build step to trigger some work in an '. - 'external system, you may want to have Phabricator wait for that '. - 'system to perform the work and report results back.'. + 'external system, you may want wait for that system to perform '. + 'the work and report results back.'. "\n\n". 'If you select **Continue Build Normally**, the build plan will '. 'proceed once this step finishes.'. diff --git a/src/applications/harbormaster/editor/HarbormasterBuildEditEngine.php b/src/applications/harbormaster/editor/HarbormasterBuildEditEngine.php new file mode 100644 index 0000000000..9f55965d9c --- /dev/null +++ b/src/applications/harbormaster/editor/HarbormasterBuildEditEngine.php @@ -0,0 +1,87 @@ +getViewer(); + return HarbormasterBuild::initializeNewBuild($viewer); + } + + protected function newObjectQuery() { + return new HarbormasterBuildQuery(); + } + + protected function newEditableObjectForDocumentation() { + $object = new DifferentialRevision(); + + $buildable = id(new HarbormasterBuildable()) + ->attachBuildableObject($object); + + return $this->newEditableObject() + ->attachBuildable($buildable); + } + + protected function getObjectCreateTitleText($object) { + return pht('Create Build'); + } + + protected function getObjectCreateButtonText($object) { + return pht('Create Build'); + } + + protected function getObjectEditTitleText($object) { + return pht('Edit Build: %s', $object->getName()); + } + + protected function getObjectEditShortText($object) { + return pht('Edit Build'); + } + + protected function getObjectCreateShortText() { + return pht('Create Build'); + } + + protected function getObjectName() { + return pht('Build'); + } + + protected function getEditorURI() { + return '/harbormaster/build/edit/'; + } + + protected function getObjectCreateCancelURI($object) { + return '/harbormaster/'; + } + + protected function getObjectViewURI($object) { + return $object->getURI(); + } + + protected function buildCustomEditFields($object) { + return array(); + } + +} diff --git a/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php b/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php index 2615f3c5be..146caf9f2c 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php @@ -11,115 +11,4 @@ final class HarbormasterBuildTransactionEditor return pht('Harbormaster Builds'); } - public function getTransactionTypes() { - $types = parent::getTransactionTypes(); - - $types[] = HarbormasterBuildTransaction::TYPE_CREATE; - $types[] = HarbormasterBuildTransaction::TYPE_COMMAND; - - return $types; - } - - protected function getCustomTransactionOldValue( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case HarbormasterBuildTransaction::TYPE_CREATE: - case HarbormasterBuildTransaction::TYPE_COMMAND: - return null; - } - - return parent::getCustomTransactionOldValue($object, $xaction); - } - - protected function getCustomTransactionNewValue( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case HarbormasterBuildTransaction::TYPE_CREATE: - return true; - case HarbormasterBuildTransaction::TYPE_COMMAND: - return $xaction->getNewValue(); - } - - return parent::getCustomTransactionNewValue($object, $xaction); - } - - protected function applyCustomInternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case HarbormasterBuildTransaction::TYPE_CREATE: - return; - case HarbormasterBuildTransaction::TYPE_COMMAND: - return $this->executeBuildCommand($object, $xaction); - } - - return parent::applyCustomInternalTransaction($object, $xaction); - } - - private function executeBuildCommand( - HarbormasterBuild $build, - HarbormasterBuildTransaction $xaction) { - - $command = $xaction->getNewValue(); - - switch ($command) { - case HarbormasterBuildCommand::COMMAND_RESTART: - $issuable = $build->canRestartBuild(); - break; - case HarbormasterBuildCommand::COMMAND_PAUSE: - $issuable = $build->canPauseBuild(); - break; - case HarbormasterBuildCommand::COMMAND_RESUME: - $issuable = $build->canResumeBuild(); - break; - case HarbormasterBuildCommand::COMMAND_ABORT: - $issuable = $build->canAbortBuild(); - break; - default: - throw new Exception(pht('Unknown command %s', $command)); - } - - if (!$issuable) { - return; - } - - $actor = $this->getActor(); - if (!$build->canIssueCommand($actor, $command)) { - return; - } - - id(new HarbormasterBuildCommand()) - ->setAuthorPHID($xaction->getAuthorPHID()) - ->setTargetPHID($build->getPHID()) - ->setCommand($command) - ->save(); - - PhabricatorWorker::scheduleTask( - 'HarbormasterBuildWorker', - array( - 'buildID' => $build->getID(), - ), - array( - 'objectPHID' => $build->getPHID(), - )); - } - - protected function applyCustomExternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case HarbormasterBuildTransaction::TYPE_CREATE: - case HarbormasterBuildTransaction::TYPE_COMMAND: - return; - } - - return parent::applyCustomExternalTransaction($object, $xaction); - } - } diff --git a/src/applications/harbormaster/editor/HarbormasterBuildableEditEngine.php b/src/applications/harbormaster/editor/HarbormasterBuildableEditEngine.php new file mode 100644 index 0000000000..bd60eeb53b --- /dev/null +++ b/src/applications/harbormaster/editor/HarbormasterBuildableEditEngine.php @@ -0,0 +1,84 @@ +getViewer(); + return HarbormasterBuildable::initializeNewBuildable($viewer); + } + + protected function newObjectQuery() { + return new HarbormasterBuildableQuery(); + } + + protected function newEditableObjectForDocumentation() { + $object = new DifferentialRevision(); + + return $this->newEditableObject() + ->attachBuildableObject($object); + } + + protected function getObjectCreateTitleText($object) { + return pht('Create Buildable'); + } + + protected function getObjectCreateButtonText($object) { + return pht('Create Buildable'); + } + + protected function getObjectEditTitleText($object) { + return pht('Edit Buildable: %s', $object->getName()); + } + + protected function getObjectEditShortText($object) { + return pht('Edit Buildable'); + } + + protected function getObjectCreateShortText() { + return pht('Create Buildable'); + } + + protected function getObjectName() { + return pht('Buildable'); + } + + protected function getEditorURI() { + return '/harbormaster/buildable/edit/'; + } + + protected function getObjectCreateCancelURI($object) { + return '/harbormaster/'; + } + + protected function getObjectViewURI($object) { + return $object->getURI(); + } + + protected function buildCustomEditFields($object) { + return array(); + } + +} diff --git a/src/applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php b/src/applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php index 3974851c0d..895c89f858 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php @@ -11,66 +11,4 @@ final class HarbormasterBuildableTransactionEditor return pht('Harbormaster Buildables'); } - public function getTransactionTypes() { - $types = parent::getTransactionTypes(); - - $types[] = HarbormasterBuildableTransaction::TYPE_CREATE; - $types[] = HarbormasterBuildableTransaction::TYPE_COMMAND; - - return $types; - } - - protected function getCustomTransactionOldValue( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case HarbormasterBuildableTransaction::TYPE_CREATE: - case HarbormasterBuildableTransaction::TYPE_COMMAND: - return null; - } - - return parent::getCustomTransactionOldValue($object, $xaction); - } - - protected function getCustomTransactionNewValue( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case HarbormasterBuildableTransaction::TYPE_CREATE: - return true; - case HarbormasterBuildableTransaction::TYPE_COMMAND: - return $xaction->getNewValue(); - } - - return parent::getCustomTransactionNewValue($object, $xaction); - } - - protected function applyCustomInternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case HarbormasterBuildableTransaction::TYPE_CREATE: - case HarbormasterBuildableTransaction::TYPE_COMMAND: - return; - } - - return parent::applyCustomInternalTransaction($object, $xaction); - } - - protected function applyCustomExternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case HarbormasterBuildableTransaction::TYPE_CREATE: - case HarbormasterBuildableTransaction::TYPE_COMMAND: - return; - } - - return parent::applyCustomExternalTransaction($object, $xaction); - } - } diff --git a/src/applications/harbormaster/engine/HarbormasterBuildEngine.php b/src/applications/harbormaster/engine/HarbormasterBuildEngine.php index 447bd53704..fd72e31a0e 100644 --- a/src/applications/harbormaster/engine/HarbormasterBuildEngine.php +++ b/src/applications/harbormaster/engine/HarbormasterBuildEngine.php @@ -49,6 +49,7 @@ final class HarbormasterBuildEngine extends Phobject { } public function continueBuild() { + $viewer = $this->getViewer(); $build = $this->getBuild(); $lock_key = 'harbormaster.build:'.$build->getID(); @@ -68,7 +69,7 @@ final class HarbormasterBuildEngine extends Phobject { $lock->unlock(); - $this->releaseAllArtifacts($build); + $build->releaseAllArtifacts($viewer); throw $ex; } @@ -99,58 +100,68 @@ final class HarbormasterBuildEngine extends Phobject { // If we are no longer building for any reason, release all artifacts. if (!$build->isBuilding()) { - $this->releaseAllArtifacts($build); + $build->releaseAllArtifacts($viewer); } } private function updateBuild(HarbormasterBuild $build) { - if ($build->isAborting()) { - $this->releaseAllArtifacts($build); - $build->setBuildStatus(HarbormasterBuildStatus::STATUS_ABORTED); - $build->save(); + $viewer = $this->getViewer(); + + $content_source = PhabricatorContentSource::newForSource( + PhabricatorDaemonContentSource::SOURCECONST); + + $acting_phid = $viewer->getPHID(); + if (!$acting_phid) { + $acting_phid = id(new PhabricatorHarbormasterApplication())->getPHID(); } - if (($build->getBuildStatus() == HarbormasterBuildStatus::STATUS_PENDING) || - ($build->isRestarting())) { - $this->restartBuild($build); - $build->setBuildStatus(HarbormasterBuildStatus::STATUS_BUILDING); - $build->save(); + $editor = $build->getApplicationTransactionEditor() + ->setActor($viewer) + ->setActingAsPHID($acting_phid) + ->setContentSource($content_source) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true); + + $xactions = array(); + + $messages = $build->getUnprocessedMessagesForApply(); + foreach ($messages as $message) { + $message_type = $message->getType(); + + $message_xaction = + HarbormasterBuildMessageTransaction::getTransactionTypeForMessageType( + $message_type); + + if (!$message_xaction) { + continue; + } + + $xactions[] = $build->getApplicationTransactionTemplate() + ->setAuthorPHID($message->getAuthorPHID()) + ->setTransactionType($message_xaction) + ->setNewValue($message_type); } - if ($build->isResuming()) { - $build->setBuildStatus(HarbormasterBuildStatus::STATUS_BUILDING); - $build->save(); + if (!$xactions) { + if ($build->isPending()) { + // TODO: This should be a transaction. + + $build->restartBuild($viewer); + $build->setBuildStatus(HarbormasterBuildStatus::STATUS_BUILDING); + $build->save(); + } } - if ($build->isPausing() && !$build->isComplete()) { - $build->setBuildStatus(HarbormasterBuildStatus::STATUS_PAUSED); - $build->save(); + if ($xactions) { + $editor->applyTransactions($build, $xactions); + $build->markUnprocessedMessagesAsProcessed(); } - $build->deleteUnprocessedCommands(); - if ($build->getBuildStatus() == HarbormasterBuildStatus::STATUS_BUILDING) { $this->updateBuildSteps($build); } } - private function restartBuild(HarbormasterBuild $build) { - - // We're restarting the build, so release all previous artifacts. - $this->releaseAllArtifacts($build); - - // Increment the build generation counter on the build. - $build->setBuildGeneration($build->getBuildGeneration() + 1); - - // Currently running targets should periodically check their build - // generation (which won't have changed) against the build's generation. - // If it is different, they will automatically stop what they're doing - // and abort. - - // Previously we used to delete targets, logs and artifacts here. Instead, - // leave them around so users can view previous generations of this build. - } - private function updateBuildSteps(HarbormasterBuild $build) { $all_targets = id(new HarbormasterBuildTargetQuery()) ->setViewer($this->getViewer()) @@ -596,29 +607,6 @@ final class HarbormasterBuildEngine extends Phobject { ->publishBuildable($old, $new); } - private function releaseAllArtifacts(HarbormasterBuild $build) { - $targets = id(new HarbormasterBuildTargetQuery()) - ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withBuildPHIDs(array($build->getPHID())) - ->withBuildGenerations(array($build->getBuildGeneration())) - ->execute(); - - if (count($targets) === 0) { - return; - } - - $target_phids = mpull($targets, 'getPHID'); - - $artifacts = id(new HarbormasterBuildArtifactQuery()) - ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withBuildTargetPHIDs($target_phids) - ->withIsReleased(false) - ->execute(); - foreach ($artifacts as $artifact) { - $artifact->releaseArtifact(); - } - } - private function releaseQueuedArtifacts() { foreach ($this->artifactReleaseQueue as $key => $artifact) { $artifact->releaseArtifact(); diff --git a/src/applications/harbormaster/exception/HarbormasterRestartException.php b/src/applications/harbormaster/exception/HarbormasterMessageException.php similarity index 59% rename from src/applications/harbormaster/exception/HarbormasterRestartException.php rename to src/applications/harbormaster/exception/HarbormasterMessageException.php index bd0b86184a..74213d47c4 100644 --- a/src/applications/harbormaster/exception/HarbormasterRestartException.php +++ b/src/applications/harbormaster/exception/HarbormasterMessageException.php @@ -1,6 +1,6 @@ setTitle($title); $this->appendParagraph($body); - parent::__construct($title); + parent::__construct( + pht( + '%s: %s', + $title, + $body)); } public function setTitle($title) { @@ -30,4 +34,13 @@ final class HarbormasterRestartException extends Exception { return $this->body; } + public function newDisplayString() { + $title = $this->getTitle(); + + $body = $this->getBody(); + $body = implode("\n\n", $body); + + return pht('%s: %s', $title, $body); + } + } diff --git a/src/applications/harbormaster/management/HarbormasterManagementRestartWorkflow.php b/src/applications/harbormaster/management/HarbormasterManagementRestartWorkflow.php index 2a38e10409..568bef46ac 100644 --- a/src/applications/harbormaster/management/HarbormasterManagementRestartWorkflow.php +++ b/src/applications/harbormaster/management/HarbormasterManagementRestartWorkflow.php @@ -32,10 +32,10 @@ final class HarbormasterManagementRestartWorkflow if (!$ids && !$active) { throw new PhutilArgumentUsageException( - pht('Use --id or --active to select builds.')); + pht('Use "--id" or "--active" to select builds.')); } if ($ids && $active) { throw new PhutilArgumentUsageException( - pht('Use one of --id or --active to select builds, but not both.')); + pht('Use one of "--id" or "--active" to select builds, but not both.')); } $query = id(new HarbormasterBuildQuery()) @@ -48,50 +48,41 @@ final class HarbormasterManagementRestartWorkflow } $builds = $query->execute(); - $console = PhutilConsole::getConsole(); $count = count($builds); if (!$count) { - $console->writeOut("%s\n", pht('No builds to restart.')); + $this->logSkip( + pht('SKIP'), + pht('No builds to restart.')); return 0; } + $prompt = pht('Restart %s build(s)?', new PhutilNumber($count)); if (!phutil_console_confirm($prompt)) { - $console->writeOut("%s\n", pht('Cancelled.')); - return 1; + throw new ArcanistUserAbortException(); } - $app_phid = id(new PhabricatorHarbormasterApplication())->getPHID(); - $editor = id(new HarbormasterBuildTransactionEditor()) - ->setActor($viewer) - ->setActingAsPHID($app_phid) - ->setContentSource($this->newContentSource()); + $message = new HarbormasterBuildMessageRestartTransaction(); + foreach ($builds as $build) { - $console->writeOut( - " %s %s\n", + $this->logInfo( pht('RESTARTING'), pht('Build %d: %s', $build->getID(), $build->getName())); - if (!$build->canRestartBuild()) { - $console->writeOut( - " %s %s\n", - pht('INVALID'), - pht('Cannot be restarted.')); - continue; - } - $xactions = array(); - $xactions[] = id(new HarbormasterBuildTransaction()) - ->setTransactionType(HarbormasterBuildTransaction::TYPE_COMMAND) - ->setNewValue(HarbormasterBuildCommand::COMMAND_RESTART); + try { - $editor->applyTransactions($build, $xactions); - } catch (Exception $e) { - $message = phutil_console_wrap($e->getMessage(), 2); - $console->writeOut( - " %s \n%s\n", - pht('FAILED'), - $message); - continue; + $message->assertCanSendMessage($viewer, $build); + } catch (HarbormasterMessageException $ex) { + $this->logWarn( + pht('INVALID'), + $ex->newDisplayString()); } - $console->writeOut(" %s \n", pht('SUCCESS')); + + $build->sendMessage( + $viewer, + $message->getHarbormasterBuildMessageType()); + + $this->logOkay( + pht('QUEUED'), + pht('Sent a restart message to build.')); } return 0; diff --git a/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php b/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php index de35c05aa6..59d4c07d2f 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php @@ -40,10 +40,6 @@ final class HarbormasterBuildArtifactQuery return new HarbormasterBuildArtifact(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { $build_targets = array(); diff --git a/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php b/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php index ad27d09582..f62919f989 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php @@ -27,10 +27,6 @@ final class HarbormasterBuildLogQuery return new HarbormasterBuildLog(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { $build_targets = array(); diff --git a/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php b/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php index 134c68ce04..393ca0a493 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php @@ -26,10 +26,6 @@ final class HarbormasterBuildMessageQuery return new HarbormasterBuildMessage(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { $receiver_phids = array_filter(mpull($page, 'getReceiverPHID')); if ($receiver_phids) { diff --git a/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php b/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php index c903fbb37f..c8514b2186 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php @@ -50,10 +50,6 @@ final class HarbormasterBuildPlanQuery return new HarbormasterBuildPlan(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function didFilterPage(array $page) { if ($this->needBuildSteps) { $plan_phids = mpull($page, 'getPHID'); @@ -98,7 +94,7 @@ final class HarbormasterBuildPlanQuery $this->statuses); } - if (strlen($this->datasourceQuery)) { + if (!phutil_nonempty_string($this->datasourceQuery)) { $where[] = qsprintf( $conn, 'plan.name LIKE %>', diff --git a/src/applications/harbormaster/query/HarbormasterBuildQuery.php b/src/applications/harbormaster/query/HarbormasterBuildQuery.php index 770ca4413b..a397f5968a 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildQuery.php @@ -56,10 +56,6 @@ final class HarbormasterBuildQuery return new HarbormasterBuild(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { $buildables = array(); @@ -104,13 +100,13 @@ final class HarbormasterBuildQuery } $build_phids = mpull($page, 'getPHID'); - $commands = id(new HarbormasterBuildCommand())->loadAllWhere( - 'targetPHID IN (%Ls) ORDER BY id ASC', + $messages = id(new HarbormasterBuildMessage())->loadAllWhere( + 'receiverPHID IN (%Ls) AND isConsumed = 0 ORDER BY id ASC', $build_phids); - $commands = mgroup($commands, 'getTargetPHID'); + $messages = mgroup($messages, 'getReceiverPHID'); foreach ($page as $build) { - $unprocessed_commands = idx($commands, $build->getPHID(), array()); - $build->attachUnprocessedCommands($unprocessed_commands); + $unprocessed_messages = idx($messages, $build->getPHID(), array()); + $build->attachUnprocessedMessages($unprocessed_messages); } if ($this->needBuildTargets) { diff --git a/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php b/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php index 992dd4fad1..93010071e2 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php @@ -26,10 +26,6 @@ final class HarbormasterBuildStepQuery return new HarbormasterBuildStep(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php b/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php index 54b9c4ee16..a93aff60bd 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php @@ -69,10 +69,6 @@ final class HarbormasterBuildTargetQuery return new HarbormasterBuildTarget(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php b/src/applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php index f73016a29f..edfe102ca2 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php @@ -26,10 +26,6 @@ final class HarbormasterBuildUnitMessageQuery return new HarbormasterBuildUnitMessage(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/harbormaster/query/HarbormasterBuildableQuery.php b/src/applications/harbormaster/query/HarbormasterBuildableQuery.php index b1a643cac7..cf907a2dc3 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildableQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildableQuery.php @@ -63,10 +63,6 @@ final class HarbormasterBuildableQuery return new HarbormasterBuildable(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { $buildables = array(); diff --git a/src/applications/harbormaster/step/HarbormasterAbortOlderBuildsBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterAbortOlderBuildsBuildStepImplementation.php index d6c2a46734..368d765b44 100644 --- a/src/applications/harbormaster/step/HarbormasterAbortOlderBuildsBuildStepImplementation.php +++ b/src/applications/harbormaster/step/HarbormasterAbortOlderBuildsBuildStepImplementation.php @@ -122,7 +122,7 @@ EOTEXT foreach ($abort_builds as $abort_build) { $abort_build->sendMessage( $viewer, - HarbormasterBuildCommand::COMMAND_ABORT); + HarbormasterBuildMessageAbortTransaction::MESSAGETYPE); } } diff --git a/src/applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php deleted file mode 100644 index fc0c830150..0000000000 --- a/src/applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php +++ /dev/null @@ -1,89 +0,0 @@ -formatSettingForDescription('artifact'), - $this->formatSettingForDescription('path')); - } - - public function execute( - HarbormasterBuild $build, - HarbormasterBuildTarget $build_target) { - - $settings = $this->getSettings(); - $variables = $build_target->getVariables(); - $viewer = PhabricatorUser::getOmnipotentUser(); - - $path = $this->mergeVariables( - 'vsprintf', - $settings['path'], - $variables); - - $artifact = $build_target->loadArtifact($settings['artifact']); - $impl = $artifact->getArtifactImplementation(); - $file = $impl->loadArtifactFile($viewer); - - $fragment = id(new PhragmentFragmentQuery()) - ->setViewer($viewer) - ->withPaths(array($path)) - ->executeOne(); - - if ($fragment === null) { - PhragmentFragment::createFromFile( - $viewer, - $file, - $path, - PhabricatorPolicies::getMostOpenPolicy(), - PhabricatorPolicies::POLICY_USER); - } else { - if ($file->getMimeType() === 'application/zip') { - $fragment->updateFromZIP($viewer, $file); - } else { - $fragment->updateFromFile($viewer, $file); - } - } - } - - public function getArtifactInputs() { - return array( - array( - 'name' => pht('Publishes File'), - 'key' => $this->getSetting('artifact'), - 'type' => HarbormasterFileArtifact::ARTIFACTCONST, - ), - ); - } - - public function getFieldSpecifications() { - return array( - 'path' => array( - 'name' => pht('Path'), - 'type' => 'text', - 'required' => true, - ), - 'artifact' => array( - 'name' => pht('File Artifact'), - 'type' => 'text', - 'required' => true, - ), - ); - } - -} diff --git a/src/applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php index f8bdb7fdfd..0779ccb186 100644 --- a/src/applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php +++ b/src/applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php @@ -8,7 +8,7 @@ final class HarbormasterUploadArtifactBuildStepImplementation } public function getGenericDescription() { - return pht('Upload a file from a host to Phabricator.'); + return pht('Upload a file.'); } public function getBuildStepGroupKey() { diff --git a/src/applications/harbormaster/storage/HarbormasterBuildCommand.php b/src/applications/harbormaster/storage/HarbormasterBuildCommand.php deleted file mode 100644 index 50a40d8e98..0000000000 --- a/src/applications/harbormaster/storage/HarbormasterBuildCommand.php +++ /dev/null @@ -1,27 +0,0 @@ - array( - 'command' => 'text128', - ), - self::CONFIG_KEY_SCHEMA => array( - 'key_target' => array( - 'columns' => array('targetPHID'), - ), - ), - ) + parent::getConfiguration(); - } - -} diff --git a/src/applications/harbormaster/storage/HarbormasterBuildTransaction.php b/src/applications/harbormaster/storage/HarbormasterBuildTransaction.php index e16a51008c..f095bd90d4 100644 --- a/src/applications/harbormaster/storage/HarbormasterBuildTransaction.php +++ b/src/applications/harbormaster/storage/HarbormasterBuildTransaction.php @@ -1,10 +1,7 @@ getAuthorPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_CREATE: - return pht( - '%s created this build.', - $this->renderHandleLink($author_phid)); - case self::TYPE_COMMAND: - switch ($new) { - case HarbormasterBuildCommand::COMMAND_RESTART: - return pht( - '%s restarted this build.', - $this->renderHandleLink($author_phid)); - case HarbormasterBuildCommand::COMMAND_ABORT: - return pht( - '%s aborted this build.', - $this->renderHandleLink($author_phid)); - case HarbormasterBuildCommand::COMMAND_RESUME: - return pht( - '%s resumed this build.', - $this->renderHandleLink($author_phid)); - case HarbormasterBuildCommand::COMMAND_PAUSE: - return pht( - '%s paused this build.', - $this->renderHandleLink($author_phid)); - } - } - return parent::getTitle(); + public function getBaseTransactionClass() { + return 'HarbormasterBuildTransactionType'; } - public function getIcon() { - $author_phid = $this->getAuthorPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_CREATE: - return 'fa-plus'; - case self::TYPE_COMMAND: - switch ($new) { - case HarbormasterBuildCommand::COMMAND_RESTART: - return 'fa-backward'; - case HarbormasterBuildCommand::COMMAND_RESUME: - return 'fa-play'; - case HarbormasterBuildCommand::COMMAND_PAUSE: - return 'fa-pause'; - case HarbormasterBuildCommand::COMMAND_ABORT: - return 'fa-exclamation-triangle'; - } - } - - return parent::getIcon(); - } - - public function getColor() { - $author_phid = $this->getAuthorPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_CREATE: - return 'green'; - case self::TYPE_COMMAND: - switch ($new) { - case HarbormasterBuildCommand::COMMAND_PAUSE: - case HarbormasterBuildCommand::COMMAND_ABORT: - return 'red'; - } - } - return parent::getColor(); - } } diff --git a/src/applications/harbormaster/storage/HarbormasterBuildableTransaction.php b/src/applications/harbormaster/storage/HarbormasterBuildableTransaction.php index 90a26d50c2..e3b937df1e 100644 --- a/src/applications/harbormaster/storage/HarbormasterBuildableTransaction.php +++ b/src/applications/harbormaster/storage/HarbormasterBuildableTransaction.php @@ -1,10 +1,7 @@ getAuthorPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_CREATE: - return pht( - '%s created this buildable.', - $this->renderHandleLink($author_phid)); - case self::TYPE_COMMAND: - switch ($new) { - case HarbormasterBuildCommand::COMMAND_RESTART: - return pht( - '%s restarted this buildable.', - $this->renderHandleLink($author_phid)); - case HarbormasterBuildCommand::COMMAND_RESUME: - return pht( - '%s resumed this buildable.', - $this->renderHandleLink($author_phid)); - case HarbormasterBuildCommand::COMMAND_PAUSE: - return pht( - '%s paused this buildable.', - $this->renderHandleLink($author_phid)); - } - } - return parent::getTitle(); + public function getBaseTransactionClass() { + return 'HarbormasterBuildableTransactionType'; } - public function getIcon() { - $author_phid = $this->getAuthorPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_CREATE: - return 'fa-plus'; - case self::TYPE_COMMAND: - switch ($new) { - case HarbormasterBuildCommand::COMMAND_RESTART: - return 'fa-backward'; - case HarbormasterBuildCommand::COMMAND_RESUME: - return 'fa-play'; - case HarbormasterBuildCommand::COMMAND_PAUSE: - return 'fa-pause'; - } - } - - return parent::getIcon(); - } - - public function getColor() { - $author_phid = $this->getAuthorPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_CREATE: - return 'green'; - case self::TYPE_COMMAND: - switch ($new) { - case HarbormasterBuildCommand::COMMAND_PAUSE: - return 'red'; - } - } - return parent::getColor(); - } } diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuild.php b/src/applications/harbormaster/storage/build/HarbormasterBuild.php index 7af5af092f..24c9689b46 100644 --- a/src/applications/harbormaster/storage/build/HarbormasterBuild.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuild.php @@ -18,7 +18,7 @@ final class HarbormasterBuild extends HarbormasterDAO private $buildable = self::ATTACHABLE; private $buildPlan = self::ATTACHABLE; private $buildTargets = self::ATTACHABLE; - private $unprocessedCommands = self::ATTACHABLE; + private $unprocessedMessages = self::ATTACHABLE; public static function initializeNewBuild(PhabricatorUser $actor) { return id(new HarbormasterBuild()) @@ -28,7 +28,7 @@ final class HarbormasterBuild extends HarbormasterDAO public function delete() { $this->openTransaction(); - $this->deleteUnprocessedCommands(); + $this->deleteUnprocessedMessages(); $result = parent::delete(); $this->saveTransaction(); @@ -207,11 +207,25 @@ final class HarbormasterBuild extends HarbormasterDAO return $this->getBuildStatusObject()->isFailed(); } + public function isPending() { + return $this->getBuildstatusObject()->isPending(); + } + public function getURI() { $id = $this->getID(); return "/harbormaster/build/{$id}/"; } + public function getBuildPendingStatusObject() { + list($pending_status) = $this->getUnprocessedMessageState(); + + if ($pending_status !== null) { + return HarbormasterBuildStatus::newBuildStatusObject($pending_status); + } + + return $this->getBuildStatusObject(); + } + protected function getBuildStatusObject() { $status_key = $this->getBuildStatus(); return HarbormasterBuildStatus::newBuildStatusObject($status_key); @@ -222,263 +236,176 @@ final class HarbormasterBuild extends HarbormasterDAO } -/* -( Build Commands )----------------------------------------------------- */ +/* -( Build Messages )----------------------------------------------------- */ - private function getUnprocessedCommands() { - return $this->assertAttached($this->unprocessedCommands); + private function getUnprocessedMessages() { + return $this->assertAttached($this->unprocessedMessages); } - public function attachUnprocessedCommands(array $commands) { - $this->unprocessedCommands = $commands; - return $this; + public function getUnprocessedMessagesForApply() { + $unprocessed_state = $this->getUnprocessedMessageState(); + list($pending_status, $apply_messages) = $unprocessed_state; + + return $apply_messages; } - public function canRestartBuild() { - try { - $this->assertCanRestartBuild(); - return true; - } catch (HarbormasterRestartException $ex) { - return false; - } - } + private function getUnprocessedMessageState() { + // NOTE: If a build has multiple unprocessed messages, we'll ignore + // messages that are obsoleted by a later or stronger message. + // + // For example, if a build has both "pause" and "abort" messages in queue, + // we just ignore the "pause" message and perform an "abort", since pausing + // first wouldn't affect the final state, so we can just skip it. + // + // Likewise, if a build has both "restart" and "abort" messages, the most + // recent message is controlling: we'll take whichever action a command + // was most recently issued for. - public function assertCanRestartBuild() { - if ($this->isAutobuild()) { - throw new HarbormasterRestartException( - pht('Can Not Restart Autobuild'), - pht( - 'This build can not be restarted because it is an automatic '. - 'build.')); - } + $is_restarting = false; + $is_aborting = false; + $is_pausing = false; + $is_resuming = false; - $restartable = HarbormasterBuildPlanBehavior::BEHAVIOR_RESTARTABLE; - $plan = $this->getBuildPlan(); + $apply_messages = array(); - // See T13526. Users who can't see the "BuildPlan" can end up here with - // no object. This is highly questionable. - if (!$plan) { - throw new HarbormasterRestartException( - pht('No Build Plan Permission'), - pht( - 'You can not restart this build because you do not have '. - 'permission to access the build plan.')); - } - - $option = HarbormasterBuildPlanBehavior::getBehavior($restartable) - ->getPlanOption($plan); - $option_key = $option->getKey(); - - $never_restartable = HarbormasterBuildPlanBehavior::RESTARTABLE_NEVER; - $is_never = ($option_key === $never_restartable); - if ($is_never) { - throw new HarbormasterRestartException( - pht('Build Plan Prevents Restart'), - pht( - 'This build can not be restarted because the build plan is '. - 'configured to prevent the build from restarting.')); - } - - $failed_restartable = HarbormasterBuildPlanBehavior::RESTARTABLE_IF_FAILED; - $is_failed = ($option_key === $failed_restartable); - if ($is_failed) { - if (!$this->isFailed()) { - throw new HarbormasterRestartException( - pht('Only Restartable if Failed'), - pht( - 'This build can not be restarted because the build plan is '. - 'configured to prevent the build from restarting unless it '. - 'has failed, and it has not failed.')); + foreach ($this->getUnprocessedMessages() as $message_object) { + $message_type = $message_object->getType(); + switch ($message_type) { + case HarbormasterBuildMessageRestartTransaction::MESSAGETYPE: + $is_restarting = true; + $is_aborting = false; + $apply_messages = array($message_object); + break; + case HarbormasterBuildMessageAbortTransaction::MESSAGETYPE: + $is_aborting = true; + $is_restarting = false; + $apply_messages = array($message_object); + break; + case HarbormasterBuildMessagePauseTransaction::MESSAGETYPE: + $is_pausing = true; + $is_resuming = false; + $apply_messages = array($message_object); + break; + case HarbormasterBuildMessageResumeTransaction::MESSAGETYPE: + $is_resuming = true; + $is_pausing = false; + $apply_messages = array($message_object); + break; } } - if ($this->isRestarting()) { - throw new HarbormasterRestartException( - pht('Already Restarting'), - pht( - 'This build is already restarting. You can not reissue a restart '. - 'command to a restarting build.')); + $pending_status = null; + if ($is_restarting) { + $pending_status = HarbormasterBuildStatus::PENDING_RESTARTING; + } else if ($is_aborting) { + $pending_status = HarbormasterBuildStatus::PENDING_ABORTING; + } else if ($is_pausing) { + $pending_status = HarbormasterBuildStatus::PENDING_PAUSING; + } else if ($is_resuming) { + $pending_status = HarbormasterBuildStatus::PENDING_RESUMING; } + + return array($pending_status, $apply_messages); } - public function canPauseBuild() { - if ($this->isAutobuild()) { - return false; - } - - return !$this->isComplete() && - !$this->isPaused() && - !$this->isPausing(); - } - - public function canAbortBuild() { - if ($this->isAutobuild()) { - return false; - } - - return !$this->isComplete(); - } - - public function canResumeBuild() { - if ($this->isAutobuild()) { - return false; - } - - return $this->isPaused() && - !$this->isResuming(); + public function attachUnprocessedMessages(array $messages) { + assert_instances_of($messages, 'HarbormasterBuildMessage'); + $this->unprocessedMessages = $messages; + return $this; } public function isPausing() { - $is_pausing = false; - foreach ($this->getUnprocessedCommands() as $command_object) { - $command = $command_object->getCommand(); - switch ($command) { - case HarbormasterBuildCommand::COMMAND_PAUSE: - $is_pausing = true; - break; - case HarbormasterBuildCommand::COMMAND_RESUME: - case HarbormasterBuildCommand::COMMAND_RESTART: - $is_pausing = false; - break; - case HarbormasterBuildCommand::COMMAND_ABORT: - $is_pausing = true; - break; - } - } - - return $is_pausing; + return $this->getBuildPendingStatusObject()->isPausing(); } public function isResuming() { - $is_resuming = false; - foreach ($this->getUnprocessedCommands() as $command_object) { - $command = $command_object->getCommand(); - switch ($command) { - case HarbormasterBuildCommand::COMMAND_RESTART: - case HarbormasterBuildCommand::COMMAND_RESUME: - $is_resuming = true; - break; - case HarbormasterBuildCommand::COMMAND_PAUSE: - $is_resuming = false; - break; - case HarbormasterBuildCommand::COMMAND_ABORT: - $is_resuming = false; - break; - } - } - - return $is_resuming; + return $this->getBuildPendingStatusObject()->isResuming(); } public function isRestarting() { - $is_restarting = false; - foreach ($this->getUnprocessedCommands() as $command_object) { - $command = $command_object->getCommand(); - switch ($command) { - case HarbormasterBuildCommand::COMMAND_RESTART: - $is_restarting = true; - break; - } - } - - return $is_restarting; + return $this->getBuildPendingStatusObject()->isRestarting(); } public function isAborting() { - $is_aborting = false; - foreach ($this->getUnprocessedCommands() as $command_object) { - $command = $command_object->getCommand(); - switch ($command) { - case HarbormasterBuildCommand::COMMAND_ABORT: - $is_aborting = true; - break; - } - } - - return $is_aborting; + return $this->getBuildPendingStatusObject()->isAborting(); } - public function deleteUnprocessedCommands() { - foreach ($this->getUnprocessedCommands() as $key => $command_object) { - $command_object->delete(); - unset($this->unprocessedCommands[$key]); + public function markUnprocessedMessagesAsProcessed() { + foreach ($this->getUnprocessedMessages() as $key => $message_object) { + $message_object + ->setIsConsumed(1) + ->save(); } return $this; } - public function canIssueCommand(PhabricatorUser $viewer, $command) { - try { - $this->assertCanIssueCommand($viewer, $command); - return true; - } catch (Exception $ex) { - return false; + public function deleteUnprocessedMessages() { + foreach ($this->getUnprocessedMessages() as $key => $message_object) { + $message_object->delete(); + unset($this->unprocessedMessages[$key]); + } + + return $this; + } + + public function sendMessage(PhabricatorUser $viewer, $message_type) { + HarbormasterBuildMessage::initializeNewMessage($viewer) + ->setReceiverPHID($this->getPHID()) + ->setType($message_type) + ->save(); + + PhabricatorWorker::scheduleTask( + 'HarbormasterBuildWorker', + array( + 'buildID' => $this->getID(), + ), + array( + 'objectPHID' => $this->getPHID(), + 'containerPHID' => $this->getBuildablePHID(), + )); + } + + public function releaseAllArtifacts(PhabricatorUser $viewer) { + $targets = id(new HarbormasterBuildTargetQuery()) + ->setViewer($viewer) + ->withBuildPHIDs(array($this->getPHID())) + ->withBuildGenerations(array($this->getBuildGeneration())) + ->execute(); + + if (!$targets) { + return; + } + + $target_phids = mpull($targets, 'getPHID'); + + $artifacts = id(new HarbormasterBuildArtifactQuery()) + ->setViewer($viewer) + ->withBuildTargetPHIDs($target_phids) + ->withIsReleased(false) + ->execute(); + foreach ($artifacts as $artifact) { + $artifact->releaseArtifact(); } } - public function assertCanIssueCommand(PhabricatorUser $viewer, $command) { - $plan = $this->getBuildPlan(); + public function restartBuild(PhabricatorUser $viewer) { + // TODO: This should become transactional. - // See T13526. Users without permission to access the build plan can - // currently end up here with no "BuildPlan" object. - if (!$plan) { - return false; - } + // We're restarting the build, so release all previous artifacts. + $this->releaseAllArtifacts($viewer); - $need_edit = true; - switch ($command) { - case HarbormasterBuildCommand::COMMAND_RESTART: - case HarbormasterBuildCommand::COMMAND_PAUSE: - case HarbormasterBuildCommand::COMMAND_RESUME: - case HarbormasterBuildCommand::COMMAND_ABORT: - if ($plan->canRunWithoutEditCapability()) { - $need_edit = false; - } - break; - default: - throw new Exception( - pht( - 'Invalid Harbormaster build command "%s".', - $command)); - } + // Increment the build generation counter on the build. + $this->setBuildGeneration($this->getBuildGeneration() + 1); - // Issuing these commands requires that you be able to edit the build, to - // prevent enemy engineers from sabotaging your builds. See T9614. - if ($need_edit) { - PhabricatorPolicyFilter::requireCapability( - $viewer, - $plan, - PhabricatorPolicyCapability::CAN_EDIT); - } - } + // Currently running targets should periodically check their build + // generation (which won't have changed) against the build's generation. + // If it is different, they will automatically stop what they're doing + // and abort. - public function sendMessage(PhabricatorUser $viewer, $command) { - // TODO: This should not be an editor transaction, but there are plans to - // merge BuildCommand into BuildMessage which should moot this. As this - // exists today, it can race against BuildEngine. - - // This is a bogus content source, but this whole flow should be obsolete - // soon. - $content_source = PhabricatorContentSource::newForSource( - PhabricatorConsoleContentSource::SOURCECONST); - - $editor = id(new HarbormasterBuildTransactionEditor()) - ->setActor($viewer) - ->setContentSource($content_source) - ->setContinueOnNoEffect(true) - ->setContinueOnMissingFields(true); - - $viewer_phid = $viewer->getPHID(); - if (!$viewer_phid) { - $acting_phid = id(new PhabricatorHarbormasterApplication())->getPHID(); - $editor->setActingAsPHID($acting_phid); - } - - $xaction = id(new HarbormasterBuildTransaction()) - ->setTransactionType(HarbormasterBuildTransaction::TYPE_COMMAND) - ->setNewValue($command); - - $editor->applyTransactions($this, array($xaction)); + // Previously we used to delete targets, logs and artifacts here. Instead, + // leave them around so users can view previous generations of this build. } diff --git a/src/applications/harbormaster/view/HarbormasterUnitSummaryView.php b/src/applications/harbormaster/view/HarbormasterUnitSummaryView.php index 66ba2307b6..154e1c96fc 100644 --- a/src/applications/harbormaster/view/HarbormasterUnitSummaryView.php +++ b/src/applications/harbormaster/view/HarbormasterUnitSummaryView.php @@ -5,7 +5,6 @@ final class HarbormasterUnitSummaryView extends AphrontView { private $buildable; private $messages; private $limit; - private $excuse; private $showViewAll; public function setBuildable(HarbormasterBuildable $buildable) { @@ -23,11 +22,6 @@ final class HarbormasterUnitSummaryView extends AphrontView { return $this; } - public function setExcuse($excuse) { - $this->excuse = $excuse; - return $this; - } - public function setShowViewAll($show_view_all) { $this->showViewAll = $show_view_all; return $this; @@ -88,21 +82,6 @@ final class HarbormasterUnitSummaryView extends AphrontView { $table->setLimit($this->limit); } - $excuse = $this->excuse; - if (strlen($excuse)) { - $excuse_icon = id(new PHUIIconView()) - ->setIcon('fa-commenting-o red'); - - $table->setNotice( - array( - $excuse_icon, - ' ', - phutil_tag('strong', array(), pht('Excuse:')), - ' ', - $excuse, - )); - } - $box->setTable($table); return $box; diff --git a/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageAbortTransaction.php b/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageAbortTransaction.php new file mode 100644 index 0000000000..2af5dc4048 --- /dev/null +++ b/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageAbortTransaction.php @@ -0,0 +1,117 @@ +renderAuthor()); + } + + public function getIcon() { + return 'fa-exclamation-triangle'; + } + + public function getColor() { + return 'red'; + } + + public function applyInternalEffects($object, $value) { + $actor = $this->getActor(); + $build = $object; + + $build->setBuildStatus(HarbormasterBuildStatus::STATUS_ABORTED); + } + + public function applyExternalEffects($object, $value) { + $actor = $this->getActor(); + $build = $object; + + $build->releaseAllArtifacts($actor); + } + + protected function newCanApplyMessageAssertion( + PhabricatorUser $viewer, + HarbormasterBuild $build) { + + if ($build->isAutobuild()) { + throw new HarbormasterMessageException( + pht('Unable to Abort Build'), + pht( + 'You can not abort a build that uses an autoplan.')); + } + + if ($build->isComplete()) { + throw new HarbormasterMessageException( + pht('Unable to Abort Build'), + pht( + 'You can not abort this biuld because it is already complete.')); + } + } + + protected function newCanSendMessageAssertion( + PhabricatorUser $viewer, + HarbormasterBuild $build) { + + if ($build->isAborting()) { + throw new HarbormasterMessageException( + pht('Unable to Abort Build'), + pht( + 'You can not abort this build because it is already aborting.')); + } + } + +} diff --git a/src/applications/harbormaster/xaction/build/HarbormasterBuildMessagePauseTransaction.php b/src/applications/harbormaster/xaction/build/HarbormasterBuildMessagePauseTransaction.php new file mode 100644 index 0000000000..326970e63e --- /dev/null +++ b/src/applications/harbormaster/xaction/build/HarbormasterBuildMessagePauseTransaction.php @@ -0,0 +1,126 @@ +renderAuthor()); + } + + public function getIcon() { + return 'fa-pause'; + } + + public function getColor() { + return 'red'; + } + + public function applyInternalEffects($object, $value) { + $actor = $this->getActor(); + $build = $object; + + $build->setBuildStatus(HarbormasterBuildStatus::STATUS_PAUSED); + } + + protected function newCanApplyMessageAssertion( + PhabricatorUser $viewer, + HarbormasterBuild $build) { + + if ($build->isAutobuild()) { + throw new HarbormasterMessageException( + pht('Unable to Pause Build'), + pht('You can not pause a build that uses an autoplan.')); + } + + if ($build->isPaused()) { + throw new HarbormasterMessageException( + pht('Unable to Pause Build'), + pht('You can not pause this build because it is already paused.')); + } + + if ($build->isComplete()) { + throw new HarbormasterMessageException( + pht('Unable to Pause Build'), + pht('You can not pause this build because it has already completed.')); + } + } + + protected function newCanSendMessageAssertion( + PhabricatorUser $viewer, + HarbormasterBuild $build) { + + if ($build->isPausing()) { + throw new HarbormasterMessageException( + pht('Unable to Pause Build'), + pht('You can not pause this build because it is already pausing.')); + } + + if ($build->isRestarting()) { + throw new HarbormasterMessageException( + pht('Unable to Pause Build'), + pht('You can not pause this build because it is already restarting.')); + } + + if ($build->isAborting()) { + throw new HarbormasterMessageException( + pht('Unable to Pause Build'), + pht('You can not pause this build because it is already aborting.')); + } + } +} diff --git a/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageRestartTransaction.php b/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageRestartTransaction.php new file mode 100644 index 0000000000..6bc976dcea --- /dev/null +++ b/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageRestartTransaction.php @@ -0,0 +1,171 @@ +isBuilding()) { + $building = true; + break; + } + } + + $warnings = array(); + + if ($building) { + $warnings[] = pht( + 'Progress on running builds will be discarded.'); + } + + if ($sendable) { + $warnings[] = pht( + 'When a build is restarted, side effects associated with '. + 'the build may occur again.'); + } + + return $warnings; + } + + public function getTitle() { + return pht( + '%s restarted this build.', + $this->renderAuthor()); + } + + public function getIcon() { + return 'fa-repeat'; + } + + public function applyInternalEffects($object, $value) { + $actor = $this->getActor(); + $build = $object; + + $build->restartBuild($actor); + $build->setBuildStatus(HarbormasterBuildStatus::STATUS_BUILDING); + } + + protected function newCanApplyMessageAssertion( + PhabricatorUser $viewer, + HarbormasterBuild $build) { + + if ($build->isAutobuild()) { + throw new HarbormasterMessageException( + pht('Can Not Restart Autobuild'), + pht( + 'This build can not be restarted because it is an automatic '. + 'build.')); + } + + $restartable = HarbormasterBuildPlanBehavior::BEHAVIOR_RESTARTABLE; + $plan = $build->getBuildPlan(); + + // See T13526. Users who can't see the "BuildPlan" can end up here with + // no object. This is highly questionable. + if (!$plan) { + throw new HarbormasterMessageException( + pht('No Build Plan Permission'), + pht( + 'You can not restart this build because you do not have '. + 'permission to access the build plan.')); + } + + $option = HarbormasterBuildPlanBehavior::getBehavior($restartable) + ->getPlanOption($plan); + $option_key = $option->getKey(); + + $never_restartable = HarbormasterBuildPlanBehavior::RESTARTABLE_NEVER; + $is_never = ($option_key === $never_restartable); + if ($is_never) { + throw new HarbormasterMessageException( + pht('Build Plan Prevents Restart'), + pht( + 'This build can not be restarted because the build plan is '. + 'configured to prevent the build from restarting.')); + } + + $failed_restartable = HarbormasterBuildPlanBehavior::RESTARTABLE_IF_FAILED; + $is_failed = ($option_key === $failed_restartable); + if ($is_failed) { + if (!$this->isFailed()) { + throw new HarbormasterMessageException( + pht('Only Restartable if Failed'), + pht( + 'This build can not be restarted because the build plan is '. + 'configured to prevent the build from restarting unless it '. + 'has failed, and it has not failed.')); + } + } + + } + + protected function newCanSendMessageAssertion( + PhabricatorUser $viewer, + HarbormasterBuild $build) { + + if ($build->isRestarting()) { + throw new HarbormasterMessageException( + pht('Already Restarting'), + pht( + 'This build is already restarting. You can not reissue a restart '. + 'command to a restarting build.')); + } + + } + +} diff --git a/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageResumeTransaction.php b/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageResumeTransaction.php new file mode 100644 index 0000000000..1bd7094799 --- /dev/null +++ b/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageResumeTransaction.php @@ -0,0 +1,119 @@ +renderAuthor()); + } + + public function getIcon() { + return 'fa-play'; + } + + public function applyInternalEffects($object, $value) { + $actor = $this->getActor(); + $build = $object; + + $build->setBuildStatus(HarbormasterBuildStatus::STATUS_BUILDING); + } + + protected function newCanApplyMessageAssertion( + PhabricatorUser $viewer, + HarbormasterBuild $build) { + + if ($build->isAutobuild()) { + throw new HarbormasterMessageException( + pht('Unable to Resume Build'), + pht( + 'You can not resume a build that uses an autoplan.')); + } + + if (!$build->isPaused() && !$build->isPausing()) { + throw new HarbormasterMessageException( + pht('Unable to Resume Build'), + pht( + 'You can not resume this build because it is not paused. You can '. + 'only resume a paused build.')); + } + + } + + protected function newCanSendMessageAssertion( + PhabricatorUser $viewer, + HarbormasterBuild $build) { + + if ($build->isResuming()) { + throw new HarbormasterMessageException( + pht('Unable to Resume Build'), + pht( + 'You can not resume this build beacuse it is already resuming.')); + } + + if ($build->isRestarting()) { + throw new HarbormasterMessageException( + pht('Unable to Resume Build'), + pht('You can not resume this build because it is already restarting.')); + } + + if ($build->isAborting()) { + throw new HarbormasterMessageException( + pht('Unable to Resume Build'), + pht('You can not resume this build because it is already aborting.')); + } + + } + +} diff --git a/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageTransaction.php b/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageTransaction.php new file mode 100644 index 0000000000..22fa25a1fd --- /dev/null +++ b/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageTransaction.php @@ -0,0 +1,158 @@ +getPhobjectClassConstant('MESSAGETYPE'); + } + + abstract public function getHarbormasterBuildMessageName(); + abstract public function getHarbormasterBuildMessageDescription(); + abstract public function getHarbormasterBuildableMessageName(); + abstract public function getHarbormasterBuildableMessageEffect(); + + abstract public function newConfirmPromptTitle(); + abstract public function newConfirmPromptBody(); + + abstract public function newBuildableConfirmPromptTitle( + array $builds, + array $sendable); + + abstract public function newBuildableConfirmPromptBody( + array $builds, + array $sendable); + + public function newBuildableConfirmPromptWarnings( + array $builds, + array $sendable) { + return array(); + } + + final public function generateOldValue($object) { + return null; + } + + final public function getTransactionTypeForConduit($xaction) { + return 'message'; + } + + final public function getFieldValuesForConduit($xaction, $data) { + return array( + 'type' => $xaction->getNewValue(), + ); + } + + final public static function getAllMessages() { + $message_xactions = id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->execute(); + + return $message_xactions; + } + + final public static function getTransactionObjectForMessageType( + $message_type) { + $message_xactions = self::getAllMessages(); + + foreach ($message_xactions as $message_xaction) { + $xaction_type = $message_xaction->getHarbormasterBuildMessageType(); + if ($xaction_type === $message_type) { + return $message_xaction; + } + } + + return null; + } + + final public static function getTransactionTypeForMessageType($message_type) { + $message_xaction = self::getTransactionObjectForMessageType($message_type); + + if ($message_xaction) { + return $message_xaction->getTransactionTypeConstant(); + } + + return null; + } + + final public function getTransactionHasEffect($object, $old, $new) { + return $this->canApplyMessage($this->getActor(), $object); + } + + final public function canApplyMessage( + PhabricatorUser $viewer, + HarbormasterBuild $build) { + + try { + $this->assertCanApplyMessage($viewer, $build); + return true; + } catch (HarbormasterMessageException $ex) { + return false; + } + } + + final public function canSendMessage( + PhabricatorUser $viewer, + HarbormasterBuild $build) { + + try { + $this->assertCanSendMessage($viewer, $build); + return true; + } catch (HarbormasterMessageException $ex) { + return false; + } + } + + final public function assertCanApplyMessage( + PhabricatorUser $viewer, + HarbormasterBuild $build) { + $this->newCanApplyMessageAssertion($viewer, $build); + } + + final public function assertCanSendMessage( + PhabricatorUser $viewer, + HarbormasterBuild $build) { + $plan = $build->getBuildPlan(); + + // See T13526. Users without permission to access the build plan can + // currently end up here with no "BuildPlan" object. + if (!$plan) { + throw new HarbormasterMessageException( + pht('No Build Plan Permission'), + pht( + 'You can not issue this command because you do not have '. + 'permission to access the build plan for this build.')); + } + + // Issuing these commands requires that you be able to edit the build, to + // prevent enemy engineers from sabotaging your builds. See T9614. + if (!$plan->canRunWithoutEditCapability()) { + try { + PhabricatorPolicyFilter::requireCapability( + $viewer, + $plan, + PhabricatorPolicyCapability::CAN_EDIT); + } catch (PhabricatorPolicyException $ex) { + throw new HarbormasterMessageException( + pht('Insufficent Build Plan Permission'), + pht( + 'The build plan for this build is configured to prevent '. + 'users who can not edit it from issuing commands to the '. + 'build, and you do not have permission to edit the build '. + 'plan.')); + } + } + + $this->newCanSendMessageAssertion($viewer, $build); + $this->assertCanApplyMessage($viewer, $build); + } + + abstract protected function newCanSendMessageAssertion( + PhabricatorUser $viewer, + HarbormasterBuild $build); + + abstract protected function newCanApplyMessageAssertion( + PhabricatorUser $viewer, + HarbormasterBuild $build); + +} diff --git a/src/applications/harbormaster/xaction/build/HarbormasterBuildTransactionType.php b/src/applications/harbormaster/xaction/build/HarbormasterBuildTransactionType.php new file mode 100644 index 0000000000..2b3f91cb4f --- /dev/null +++ b/src/applications/harbormaster/xaction/build/HarbormasterBuildTransactionType.php @@ -0,0 +1,4 @@ +getNewValue(); + + switch ($new) { + case HarbormasterBuildMessageRestartTransaction::MESSAGETYPE: + return pht( + '%s restarted this buildable.', + $this->renderAuthor()); + case HarbormasterBuildMessageResumeTransaction::MESSAGETYPE: + return pht( + '%s resumed this buildable.', + $this->renderAuthor()); + case HarbormasterBuildMessagePauseTransaction::MESSAGETYPE: + return pht( + '%s paused this buildable.', + $this->renderAuthor()); + case HarbormasterBuildMessageAbortTransaction::MESSAGETYPE: + return pht( + '%s aborted this buildable.', + $this->renderAuthor()); + } + + return parent::getTitle(); + } + + public function getIcon() { + $new = $this->getNewValue(); + + switch ($new) { + case HarbormasterBuildMessageRestartTransaction::MESSAGETYPE: + return 'fa-backward'; + case HarbormasterBuildMessageResumeTransaction::MESSAGETYPE: + return 'fa-play'; + case HarbormasterBuildMessagePauseTransaction::MESSAGETYPE: + return 'fa-pause'; + case HarbormasterBuildMessageAbortTransaction::MESSAGETYPE: + return 'fa-exclamation-triangle'; + } + + return parent::getIcon(); + } + + public function getColor() { + $new = $this->getNewValue(); + + switch ($new) { + case HarbormasterBuildMessagePauseTransaction::MESSAGETYPE: + return 'red'; + } + + return parent::getColor(); + } + +} diff --git a/src/applications/harbormaster/xaction/buildable/HarbormasterBuildableTransactionType.php b/src/applications/harbormaster/xaction/buildable/HarbormasterBuildableTransactionType.php new file mode 100644 index 0000000000..0299313104 --- /dev/null +++ b/src/applications/harbormaster/xaction/buildable/HarbormasterBuildableTransactionType.php @@ -0,0 +1,4 @@ +loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $rules) { $rule_ids = mpull($rules, 'getID'); diff --git a/src/applications/herald/query/HeraldWebhookQuery.php b/src/applications/herald/query/HeraldWebhookQuery.php index ca46880613..77307a71e6 100644 --- a/src/applications/herald/query/HeraldWebhookQuery.php +++ b/src/applications/herald/query/HeraldWebhookQuery.php @@ -26,10 +26,6 @@ final class HeraldWebhookQuery return new HeraldWebhook(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/herald/query/HeraldWebhookRequestQuery.php b/src/applications/herald/query/HeraldWebhookRequestQuery.php index 4c71d48e05..f0a61a2dc5 100644 --- a/src/applications/herald/query/HeraldWebhookRequestQuery.php +++ b/src/applications/herald/query/HeraldWebhookRequestQuery.php @@ -29,10 +29,6 @@ final class HeraldWebhookRequestQuery return new HeraldWebhookRequest(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - public function withLastRequestEpochBetween($epoch_min, $epoch_max) { $this->lastRequestEpochMin = $epoch_min; $this->lastRequestEpochMax = $epoch_max; diff --git a/src/applications/herald/storage/transcript/HeraldTranscript.php b/src/applications/herald/storage/transcript/HeraldTranscript.php index 9539d4653d..f977c44485 100644 --- a/src/applications/herald/storage/transcript/HeraldTranscript.php +++ b/src/applications/herald/storage/transcript/HeraldTranscript.php @@ -66,6 +66,10 @@ final class HeraldTranscript extends HeraldDAO } private static function combineXHeraldRulesHeaders($u, $v) { + if ($u === null) { + return $v; + } + $u = preg_split('/[, ]+/', $u); $v = preg_split('/[, ]+/', $v); diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignController.php b/src/applications/legalpad/controller/LegalpadDocumentSignController.php index fb15e2af8f..7798a2aa9d 100644 --- a/src/applications/legalpad/controller/LegalpadDocumentSignController.php +++ b/src/applications/legalpad/controller/LegalpadDocumentSignController.php @@ -681,7 +681,7 @@ final class LegalpadDocumentSignController extends LegalpadController { $body = pht( "%s:\n\n". "This email address was used to sign a Legalpad document ". - "in Phabricator:\n\n". + "in %s:\n\n". " %s\n\n". "Please verify you own this email address and accept the ". "agreement by clicking this link:\n\n". @@ -690,6 +690,7 @@ final class LegalpadDocumentSignController extends LegalpadController { "verification step.\n\nYou can review the document here:\n\n". " %s\n", $name, + PlatformSymbols::getPlatformServerName(), $doc_name, $link, $doc_link); diff --git a/src/applications/legalpad/editor/LegalpadDocumentEditEngine.php b/src/applications/legalpad/editor/LegalpadDocumentEditEngine.php index 814647b82a..fb3f54275a 100644 --- a/src/applications/legalpad/editor/LegalpadDocumentEditEngine.php +++ b/src/applications/legalpad/editor/LegalpadDocumentEditEngine.php @@ -131,7 +131,7 @@ final class LegalpadDocumentEditEngine ->setKey('requireSignature') ->setOptions( pht('No Signature Required'), - pht('Signature Required to use Phabricator')) + pht('Signature Required to Log In')) ->setAsCheckbox(true) ->setTransactionType( LegalpadDocumentRequireSignatureTransaction::TRANSACTIONTYPE) diff --git a/src/applications/legalpad/editor/LegalpadDocumentEditor.php b/src/applications/legalpad/editor/LegalpadDocumentEditor.php index 4ede196e39..90f50564de 100644 --- a/src/applications/legalpad/editor/LegalpadDocumentEditor.php +++ b/src/applications/legalpad/editor/LegalpadDocumentEditor.php @@ -101,7 +101,7 @@ final class LegalpadDocumentEditor LegalpadDocumentRequireSignatureTransaction::TRANSACTIONTYPE, pht('Invalid'), pht('Only documents with signature type "individual" may '. - 'require signing to use Phabricator.'), + 'require signing to log in.'), null); } diff --git a/src/applications/legalpad/query/LegalpadDocumentQuery.php b/src/applications/legalpad/query/LegalpadDocumentQuery.php index 3d79e9f3a1..854a187fab 100644 --- a/src/applications/legalpad/query/LegalpadDocumentQuery.php +++ b/src/applications/legalpad/query/LegalpadDocumentQuery.php @@ -81,10 +81,6 @@ final class LegalpadDocumentQuery return new LegalpadDocument(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $documents) { if ($this->needDocumentBodies) { $documents = $this->loadDocumentBodies($documents); diff --git a/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php b/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php index 8c4dd31ff1..591174be57 100644 --- a/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php +++ b/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php @@ -185,8 +185,7 @@ final class LegalpadDocumentSearchEngine ->setIcon($icon) ->setTitle(pht('Welcome to %s', $app_name)) ->setDescription( - pht('Create documents and track signatures. Can also be re-used in '. - 'other areas of Phabricator, like CLAs.')) + pht('Create documents and track signatures.')) ->addAction($create_button); return $view; diff --git a/src/applications/macro/query/PhabricatorMacroQuery.php b/src/applications/macro/query/PhabricatorMacroQuery.php index 7635b68b73..70e7f7e688 100644 --- a/src/applications/macro/query/PhabricatorMacroQuery.php +++ b/src/applications/macro/query/PhabricatorMacroQuery.php @@ -100,10 +100,6 @@ final class PhabricatorMacroQuery return new PhabricatorFileImageMacro(); } - protected function loadPage() { - return $this->loadStandardPage(new PhabricatorFileImageMacro()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); @@ -128,7 +124,7 @@ final class PhabricatorMacroQuery $this->authorPHIDs); } - if (strlen($this->nameLike)) { + if (($this->nameLike !== null) && strlen($this->nameLike)) { $where[] = qsprintf( $conn, 'm.name LIKE %~', @@ -142,7 +138,7 @@ final class PhabricatorMacroQuery $this->names); } - if (strlen($this->namePrefix)) { + if (($this->namePrefix !== null) && strlen($this->namePrefix)) { $where[] = qsprintf( $conn, 'm.name LIKE %>', diff --git a/src/applications/macro/xaction/PhabricatorMacroAudioTransaction.php b/src/applications/macro/xaction/PhabricatorMacroAudioTransaction.php index 26dc64c1f3..bb966a16cb 100644 --- a/src/applications/macro/xaction/PhabricatorMacroAudioTransaction.php +++ b/src/applications/macro/xaction/PhabricatorMacroAudioTransaction.php @@ -13,32 +13,14 @@ final class PhabricatorMacroAudioTransaction $object->setAudioPHID($value); } - public function applyExternalEffects($object, $value) { - $old = $this->generateOldValue($object); - $new = $value; - $all = array(); - if ($old) { - $all[] = $old; - } - if ($new) { - $all[] = $new; + public function extractFilePHIDs($object, $value) { + $file_phids = array(); + + if ($value) { + $file_phids[] = $value; } - $files = id(new PhabricatorFileQuery()) - ->setViewer($this->getActor()) - ->withPHIDs($all) - ->execute(); - $files = mpull($files, null, 'getPHID'); - - $old_file = idx($files, $old); - if ($old_file) { - $old_file->detachFromObject($object->getPHID()); - } - - $new_file = idx($files, $new); - if ($new_file) { - $new_file->attachToObject($object->getPHID()); - } + return $file_phids; } public function getTitle() { diff --git a/src/applications/macro/xaction/PhabricatorMacroFileTransaction.php b/src/applications/macro/xaction/PhabricatorMacroFileTransaction.php index fb0c56f1c1..4e9f019071 100644 --- a/src/applications/macro/xaction/PhabricatorMacroFileTransaction.php +++ b/src/applications/macro/xaction/PhabricatorMacroFileTransaction.php @@ -13,32 +13,8 @@ final class PhabricatorMacroFileTransaction $object->setFilePHID($value); } - public function applyExternalEffects($object, $value) { - $old = $this->generateOldValue($object); - $new = $value; - $all = array(); - if ($old) { - $all[] = $old; - } - if ($new) { - $all[] = $new; - } - - $files = id(new PhabricatorFileQuery()) - ->setViewer($this->getActor()) - ->withPHIDs($all) - ->execute(); - $files = mpull($files, null, 'getPHID'); - - $old_file = idx($files, $old); - if ($old_file) { - $old_file->detachFromObject($object->getPHID()); - } - - $new_file = idx($files, $new); - if ($new_file) { - $new_file->attachToObject($object->getPHID()); - } + public function extractFilePHIDs($object, $value) { + return array($value); } public function getTitle() { diff --git a/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php b/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php index 60a41a26ca..9bb3987415 100644 --- a/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php +++ b/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php @@ -218,7 +218,7 @@ The keys you can provide in a specification are: Statuses will appear in the UI in the order specified. Note the status marked `special` as `duplicate` is not settable directly and will not appear in UI -elements, and that any status marked `silly` does not appear if Phabricator +elements, and that any status marked `silly` does not appear if the software is configured with `phabricator.serious-business` set to true. Examining the default configuration and examples below will probably be helpful diff --git a/src/applications/maniphest/storage/ManiphestTask.php b/src/applications/maniphest/storage/ManiphestTask.php index 4f90de3507..46dd8a1547 100644 --- a/src/applications/maniphest/storage/ManiphestTask.php +++ b/src/applications/maniphest/storage/ManiphestTask.php @@ -24,8 +24,6 @@ final class ManiphestTask extends ManiphestDAO PhabricatorPolicyCodexInterface, PhabricatorUnlockableInterface { - const MARKUP_FIELD_DESCRIPTION = 'markup:desc'; - protected $authorPHID; protected $ownerPHID; diff --git a/src/applications/maniphest/xaction/ManiphestTaskOwnerTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskOwnerTransaction.php index 0ebef78976..e1d2749acc 100644 --- a/src/applications/maniphest/xaction/ManiphestTaskOwnerTransaction.php +++ b/src/applications/maniphest/xaction/ManiphestTaskOwnerTransaction.php @@ -112,7 +112,8 @@ final class ManiphestTaskOwnerTransaction foreach ($xactions as $xaction) { $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); - if (!strlen($new)) { + + if (!phutil_nonempty_string($new)) { continue; } diff --git a/src/applications/meta/controller/PhabricatorApplicationEmailCommandsController.php b/src/applications/meta/controller/PhabricatorApplicationEmailCommandsController.php index 43ed2e957e..596ed5755b 100644 --- a/src/applications/meta/controller/PhabricatorApplicationEmailCommandsController.php +++ b/src/applications/meta/controller/PhabricatorApplicationEmailCommandsController.php @@ -35,12 +35,12 @@ final class PhabricatorApplicationEmailCommandsController $content[] = '= '.pht('Mail Commands Overview'); $content[] = pht( - 'After configuring Phabricator to process inbound mail, you can '. + 'After configuring processing for inbound mail, you can '. 'interact with objects (like tasks and revisions) over email. For '. - 'information on configuring Phabricator, see '. + 'information on configuring inbound mail, see '. '**[[ %s | Configuring Inbound Email ]]**.'. "\n\n". - 'In most cases, you can reply to email you receive from Phabricator '. + 'In most cases, you can reply to email you receive from this server '. 'to leave comments. You can also use **mail commands** to take a '. 'greater range of actions (like claiming a task or requesting changes '. 'to a revision) without needing to log in to the web UI.'. @@ -52,12 +52,13 @@ final class PhabricatorApplicationEmailCommandsController 'or end of your mail message. For example, you could write this in a '. 'reply to task email to claim the task:'. "\n\n```\n!claim\n\nI'll take care of this.\n```\n\n\n". - "When Phabricator receives your mail, it will process any commands ". + "When %s receives your mail, it will process any commands ". "first, then post the remaining message body as a comment. You can ". "execute multiple commands at once:". "\n\n```\n!assign alincoln\n!close\n\nI just talked to @alincoln, ". "and he showed me that he fixed this.\n```\n", - PhabricatorEnv::getDoclink('Configuring Inbound Email')); + PhabricatorEnv::getDoclink('Configuring Inbound Email'), + PlatformSymbols::getPlatformServerName()); $content[] = '= '.$spec['header']; $content[] = $spec['summary']; @@ -122,7 +123,7 @@ final class PhabricatorApplicationEmailCommandsController $info_view = null; if (!PhabricatorEnv::getEnvConfig('metamta.reply-handler-domain')) { $error = pht( - "Phabricator is not currently configured to accept inbound mail. ". + "This server is not currently configured to accept inbound mail. ". "You won't be able to interact with objects over email until ". "inbound mail is set up."); $info_view = id(new PHUIInfoView()) diff --git a/src/applications/meta/controller/PhabricatorApplicationUninstallController.php b/src/applications/meta/controller/PhabricatorApplicationUninstallController.php index 09f3e92ee5..e89976c42c 100644 --- a/src/applications/meta/controller/PhabricatorApplicationUninstallController.php +++ b/src/applications/meta/controller/PhabricatorApplicationUninstallController.php @@ -38,7 +38,7 @@ final class PhabricatorApplicationUninstallController ->appendChild( pht( 'To manage prototypes, enable them by setting %s in your '. - 'Phabricator configuration.', + 'configuration.', phutil_tag('tt', array(), 'phabricator.show-prototypes'))); return id(new AphrontDialogResponse())->setDialog($dialog); } @@ -98,9 +98,9 @@ final class PhabricatorApplicationUninstallController ->appendParagraph( pht( 'This is very unusual and will leave you without any '. - 'content on the Phabricator home page. You should only '. - 'do this if you are certain you know what you are doing.')) - ->addSubmitButton(pht('Completely Break Phabricator')); + 'content on the home page. You should only do this if you '. + 'are certain you know what you are doing.')) + ->addSubmitButton(pht('Completely Break Everything')); } else { $dialog ->appendParagraph( @@ -114,8 +114,7 @@ final class PhabricatorApplicationUninstallController ->setTitle(pht('Information')) ->appendChild( pht( - 'This application cannot be uninstalled, '. - 'because it is required for Phabricator to work.')); + 'This application is required and cannot be uninstalled.')); } } return id(new AphrontDialogResponse())->setDialog($dialog); diff --git a/src/applications/meta/query/PhabricatorApplicationQuery.php b/src/applications/meta/query/PhabricatorApplicationQuery.php index 63de1174cf..ff9aa5539a 100644 --- a/src/applications/meta/query/PhabricatorApplicationQuery.php +++ b/src/applications/meta/query/PhabricatorApplicationQuery.php @@ -89,7 +89,7 @@ final class PhabricatorApplicationQuery } } - if (strlen($this->nameContains)) { + if ($this->nameContains !== null) { foreach ($apps as $key => $app) { if (stripos($app->getName(), $this->nameContains) === false) { unset($apps[$key]); diff --git a/src/applications/metamta/adapter/PhabricatorMailPostmarkAdapter.php b/src/applications/metamta/adapter/PhabricatorMailPostmarkAdapter.php index 2381ff04bf..cdc5a830c4 100644 --- a/src/applications/metamta/adapter/PhabricatorMailPostmarkAdapter.php +++ b/src/applications/metamta/adapter/PhabricatorMailPostmarkAdapter.php @@ -33,10 +33,11 @@ final class PhabricatorMailPostmarkAdapter // // "Configuring Outbound Email" should be updated if this changes. // - // These addresses were last updated in January 2019. + // These addresses were last updated in December 2021. '50.31.156.6/32', '50.31.156.77/32', '18.217.206.57/32', + '3.134.147.250/32', ), ); } diff --git a/src/applications/metamta/adapter/PhabricatorMailTestAdapter.php b/src/applications/metamta/adapter/PhabricatorMailTestAdapter.php index a6258a8874..311dd78b99 100644 --- a/src/applications/metamta/adapter/PhabricatorMailTestAdapter.php +++ b/src/applications/metamta/adapter/PhabricatorMailTestAdapter.php @@ -126,12 +126,12 @@ final class PhabricatorMailTestAdapter $guts['headers'] = $header_list; $text_body = $message->getTextBody(); - if (strlen($text_body)) { + if (phutil_nonempty_string($text_body)) { $guts['body'] = $text_body; } $html_body = $message->getHTMLBody(); - if (strlen($html_body)) { + if (phutil_nonempty_string($html_body)) { $guts['html-body'] = $html_body; } diff --git a/src/applications/metamta/applicationpanel/PhabricatorMetaMTAApplicationEmailPanel.php b/src/applications/metamta/applicationpanel/PhabricatorMetaMTAApplicationEmailPanel.php index 2f9ddcf22b..5f6911db0f 100644 --- a/src/applications/metamta/applicationpanel/PhabricatorMetaMTAApplicationEmailPanel.php +++ b/src/applications/metamta/applicationpanel/PhabricatorMetaMTAApplicationEmailPanel.php @@ -264,7 +264,7 @@ final class PhabricatorMetaMTAApplicationEmailPanel pht( 'Used if the "From:" address does not map to a user account. '. 'Setting a default author will allow anyone on the public '. - 'internet to create objects in Phabricator by sending email to '. + 'internet to create objects by sending email to '. 'this address.'))); if ($is_new) { diff --git a/src/applications/metamta/constants/MetaMTAReceivedMailStatus.php b/src/applications/metamta/constants/MetaMTAReceivedMailStatus.php index faacdc2cfc..871d47f97f 100644 --- a/src/applications/metamta/constants/MetaMTAReceivedMailStatus.php +++ b/src/applications/metamta/constants/MetaMTAReceivedMailStatus.php @@ -21,7 +21,7 @@ final class MetaMTAReceivedMailStatus public static function getHumanReadableName($status) { $map = array( self::STATUS_DUPLICATE => pht('Duplicate Message'), - self::STATUS_FROM_PHABRICATOR => pht('Phabricator Mail'), + self::STATUS_FROM_PHABRICATOR => pht('Mail From Self'), self::STATUS_NO_RECEIVERS => pht('No Receivers'), self::STATUS_UNKNOWN_SENDER => pht('Unknown Sender'), self::STATUS_DISABLED_SENDER => pht('Disabled Sender'), diff --git a/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php b/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php index d7d31ba254..e38c7f9801 100644 --- a/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php @@ -119,7 +119,9 @@ final class PhabricatorMetaMTAMailViewController if ($mail->getFrom()) { $from_str = $viewer->renderHandle($mail->getFrom()); } else { - $from_str = pht('Sent by Phabricator'); + $from_str = pht( + 'Sent by %s', + PlatformSymbols::getPlatformServerName()); } $properties->addProperty( pht('From'), @@ -428,7 +430,7 @@ final class PhabricatorMetaMTAMailViewController if ($actor_phid) { $actor_str = $viewer->renderHandle($actor_phid); } else { - $actor_str = pht('Generated by Phabricator'); + $actor_str = pht('Generated by Server'); } $properties->addProperty(pht('Actor'), $actor_str); diff --git a/src/applications/metamta/engine/PhabricatorMailEmailEngine.php b/src/applications/metamta/engine/PhabricatorMailEmailEngine.php index ef7b92a7d3..6c9cf1b356 100644 --- a/src/applications/metamta/engine/PhabricatorMailEmailEngine.php +++ b/src/applications/metamta/engine/PhabricatorMailEmailEngine.php @@ -119,6 +119,8 @@ final class PhabricatorMailEmailEngine } $body_limit = PhabricatorEnv::getEnvConfig('metamta.email-body-limit'); + + $body = phutil_string_cast($body); if (strlen($body) > $body_limit) { $body = id(new PhutilUTF8StringTruncator()) ->setMaximumBytes($body_limit) @@ -143,7 +145,7 @@ final class PhabricatorMailEmailEngine if ($send_html) { $html_body = $mail->getHTMLBody(); - if (strlen($html_body)) { + if (phutil_nonempty_string($html_body)) { // NOTE: We just drop the entire HTML body if it won't fit. Safely // truncating HTML is hard, and we already have the text body to fall // back to. @@ -166,7 +168,7 @@ final class PhabricatorMailEmailEngine if (PhabricatorEnv::getEnvConfig('phabricator.silent')) { $mail->setMessage( pht( - 'Phabricator is running in silent mode. See `%s` '. + 'This software is running in silent mode. See `%s` '. 'in the configuration to change this setting.', 'phabricator.silent')); @@ -226,7 +228,7 @@ final class PhabricatorMailEmailEngine $mail = $this->getMail(); $reply_raw = $mail->getReplyTo(); - if (!strlen($reply_raw)) { + if (!phutil_nonempty_string($reply_raw)) { return null; } @@ -241,7 +243,7 @@ final class PhabricatorMailEmailEngine // If we don't have a display name, fill in a default. if (!strlen($reply_address->getDisplayName())) { - $reply_address->setDisplayName(pht('Phabricator')); + $reply_address->setDisplayName(PlatformSymbols::getPlatformServerName()); } return $reply_address; @@ -301,7 +303,11 @@ final class PhabricatorMailEmailEngine } } - $subject[] = trim($mail->getSubjectPrefix()); + $subject_prefix = $mail->getSubjectPrefix(); + $subject_prefix = phutil_string_cast($subject_prefix); + $subject_prefix = trim($subject_prefix); + + $subject[] = $subject_prefix; // If mail content must be encrypted, we replace the subject with // a generic one. @@ -313,7 +319,7 @@ final class PhabricatorMailEmailEngine $subject[] = $encrypt_subject; } else { $vary_prefix = $mail->getVarySubjectPrefix(); - if (strlen($vary_prefix)) { + if (phutil_nonempty_string($vary_prefix)) { if ($this->shouldVarySubject()) { $subject[] = $vary_prefix; } @@ -323,7 +329,7 @@ final class PhabricatorMailEmailEngine } foreach ($subject as $key => $part) { - if (!strlen($part)) { + if (!phutil_nonempty_string($part)) { unset($subject[$key]); } } @@ -403,7 +409,7 @@ final class PhabricatorMailEmailEngine $headers = array(); $thread_id = $mail->getThreadID(); - if (!strlen($thread_id)) { + if (!phutil_nonempty_string($thread_id)) { return $headers; } @@ -508,8 +514,8 @@ final class PhabricatorMailEmailEngine $address = new PhutilEmailAddress($raw_address); - if (!strlen($address->getDisplayName())) { - $address->setDisplayName(pht('Phabricator')); + if (!phutil_nonempty_string($address->getDisplayName())) { + $address->setDisplayName(PlatformSymbols::getPlatformServerName()); } return $address; diff --git a/src/applications/metamta/management/PhabricatorMailManagementListInboundWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementListInboundWorkflow.php index 02ed82a6b0..c19923026d 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementListInboundWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementListInboundWorkflow.php @@ -6,7 +6,7 @@ final class PhabricatorMailManagementListInboundWorkflow protected function didConstruct() { $this ->setName('list-inbound') - ->setSynopsis(pht('List inbound messages received by Phabricator.')) + ->setSynopsis(pht('List inbound messages.')) ->setExamples( '**list-inbound**') ->setArguments( diff --git a/src/applications/metamta/management/PhabricatorMailManagementListOutboundWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementListOutboundWorkflow.php index 30939dd436..77c363a45b 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementListOutboundWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementListOutboundWorkflow.php @@ -6,7 +6,7 @@ final class PhabricatorMailManagementListOutboundWorkflow protected function didConstruct() { $this ->setName('list-outbound') - ->setSynopsis(pht('List outbound messages sent by Phabricator.')) + ->setSynopsis(pht('List outbound messages.')) ->setExamples('**list-outbound**') ->setArguments( array( diff --git a/src/applications/metamta/parser/PhabricatorMetaMTAEmailBodyParser.php b/src/applications/metamta/parser/PhabricatorMetaMTAEmailBodyParser.php index be0401f066..f20e626573 100644 --- a/src/applications/metamta/parser/PhabricatorMetaMTAEmailBodyParser.php +++ b/src/applications/metamta/parser/PhabricatorMetaMTAEmailBodyParser.php @@ -11,7 +11,7 @@ final class PhabricatorMetaMTAEmailBodyParser extends Phobject { * * Or * - * !assign epriestley + * !assign alincoln * * please, take this task I took; its hard * @@ -20,9 +20,9 @@ final class PhabricatorMetaMTAEmailBodyParser extends Phobject { * commands. For example, this body above might parse as: * * array( - * 'body' => 'please, take this task I took; its hard', + * 'body' => 'please, take this task I took; it's hard', * 'commands' => array( - * array('assign', 'epriestley'), + * array('assign', 'alincoln'), * ), * ) * @@ -81,6 +81,7 @@ final class PhabricatorMetaMTAEmailBodyParser extends Phobject { } private function stripQuotedText($body) { + $body = phutil_string_cast($body); // Look for "On , wrote:". This may be split across multiple // lines. We need to be careful not to remove all of a message like this: diff --git a/src/applications/metamta/query/PhabricatorMetaMTAActor.php b/src/applications/metamta/query/PhabricatorMetaMTAActor.php index cf2060a8f7..676f0933ec 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAActor.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAActor.php @@ -143,7 +143,7 @@ final class PhabricatorMetaMTAActor extends Phobject { 'recipient received the original email message, so we are not '. 'sending them this substantially similar message (for example, '. 'the sender used "Reply All" instead of "Reply" in response to '. - 'mail from Phabricator).'), + 'mail from this server).'), self::REASON_SELF => pht( 'This recipient is the user whose actions caused delivery of '. 'this message, but they have set preferences so they do not '. diff --git a/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php index e6ec09f4ff..b7dd3e5ee4 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php @@ -20,10 +20,6 @@ final class PhabricatorMetaMTAMailPropertiesQuery return new PhabricatorMetaMTAMailProperties(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php index 903b385ceb..d1f0235c90 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php @@ -36,10 +36,6 @@ final class PhabricatorMetaMTAMailQuery return $this; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/metamta/query/PhabricatorMetaMTAMemberQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAMemberQuery.php index 8657981836..f7fb0ba27a 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAMemberQuery.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAMemberQuery.php @@ -60,6 +60,16 @@ final class PhabricatorMetaMTAMemberQuery extends PhabricatorQuery { } $package_map[$package->getPHID()] = $package_owners; } + + // See T13648. We may have packages that no longer exist or can't be + // loaded (for example, because they have been destroyed). Give them + // empty entries in the map so we return a mapping for all input PHIDs. + + foreach ($package_phids as $package_phid) { + if (!isset($package_map[$package_phid])) { + $package_map[$package_phid] = array(); + } + } } $results = array(); diff --git a/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php b/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php index 16950c1577..0342a94a60 100644 --- a/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php +++ b/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php @@ -72,10 +72,10 @@ abstract class PhabricatorObjectMailReceiver extends PhabricatorMailReceiver { MetaMTAReceivedMailStatus::STATUS_NO_PUBLIC_MAIL, pht( 'This mail is addressed to the public email address of an object '. - '("%s"), but public replies are not enabled on this Phabricator '. - 'install. An administrator may have recently disabled this '. - 'setting, or you may have replied to an old message. Try '. - 'replying to a more recent message instead.', + '("%s"), but public replies are not enabled on this server. An '. + 'administrator may have recently disabled this setting, or you '. + 'may have replied to an old message. Try replying to a more '. + 'recent message instead.', $pattern)); } $check_phid = $object->getPHID(); diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php index c620bea30a..d3289bbc69 100644 --- a/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php @@ -263,19 +263,16 @@ final class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO { MetaMTAReceivedMailStatus::STATUS_UNKNOWN_SENDER, pht( 'This email was sent from an email address ("%s") that is not '. - 'associated with a Phabricator account. To interact with '. - 'Phabricator via email, add this address to your account.', + 'associated with a registered user account. To interact via '. + 'email, add this address to your account.', (string)$this->newFromAddress())); } else { throw new PhabricatorMetaMTAReceivedMailProcessingException( MetaMTAReceivedMailStatus::STATUS_NO_RECEIVERS, pht( - 'Phabricator can not process this mail because no application '. + 'This mail can not be processed because no application '. 'knows how to handle it. Check that the address you sent it to '. - 'is correct.'. - "\n\n". - '(No concrete, enabled subclass of PhabricatorMailReceiver can '. - 'accept this mail.)')); + 'is correct.')); } } } catch (PhabricatorMetaMTAReceivedMailProcessingException $ex) { @@ -348,9 +345,13 @@ final class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO { private function getRawEmailAddresses($addresses) { $raw_addresses = array(); - foreach (explode(',', $addresses) as $address) { - $raw_addresses[] = $this->getRawEmailAddress($address); + + if (phutil_nonempty_string($addresses)) { + foreach (explode(',', $addresses) as $address) { + $raw_addresses[] = $this->getRawEmailAddress($address); + } } + return array_filter($raw_addresses); } @@ -436,7 +437,7 @@ final class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO { $status_code, pht( 'Your message does not contain any body text or attachments, so '. - 'Phabricator can not do anything useful with it. Make sure comment '. + 'this server can not do anything useful with it. Make sure comment '. 'text appears at the top of your message: quoted replies, inline '. 'text, and signatures are discarded and ignored.')); } @@ -484,7 +485,7 @@ final class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO { $headers = implode("\n", $headers); $body = pht(<<getRawTextBody(), $headers); @@ -563,21 +565,20 @@ EOBODY if ($sender->getIsDisabled()) { $failure_reason = pht( 'Your account ("%s") is disabled, so you can not interact with '. - 'Phabricator over email.', + 'over email.', $sender->getUsername()); } else if ($sender->getIsStandardUser()) { if (!$sender->getIsApproved()) { $failure_reason = pht( 'Your account ("%s") has not been approved yet. You can not '. - 'interact with Phabricator over email until your account is '. - 'approved.', + 'interact over email until your account is approved.', $sender->getUsername()); } else if (PhabricatorUserEmail::isEmailVerificationRequired() && !$sender->getIsEmailVerified()) { $failure_reason = pht( 'You have not verified the email address for your account ("%s"). '. - 'You must verify your email address before you can interact '. - 'with Phabricator over email.', + 'You must verify your email address before you can interact over '. + 'email.', $sender->getUsername()); } } diff --git a/src/applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php b/src/applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php index 7462aaf558..e66dd8c61b 100644 --- a/src/applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php +++ b/src/applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php @@ -395,7 +395,7 @@ final class PhabricatorMetaMTAMailTestCase extends PhabricatorTestCase { // We expect the HTML body was dropped completely after the text body was // truncated. $this->assertTrue( - !strlen($html_body), + !phutil_nonempty_string($html_body), pht('HTML Body Removed')); @@ -416,7 +416,7 @@ final class PhabricatorMetaMTAMailTestCase extends PhabricatorTestCase { $html_body = $mailer->getHTMLBody(); $this->assertEqual($string_1kb, $text_body); - $this->assertTrue(!strlen($html_body)); + $this->assertTrue(!phutil_nonempty_string($html_body)); } } diff --git a/src/applications/metamta/util/PhabricatorMailUtil.php b/src/applications/metamta/util/PhabricatorMailUtil.php index a5fbc7179e..270e9786f3 100644 --- a/src/applications/metamta/util/PhabricatorMailUtil.php +++ b/src/applications/metamta/util/PhabricatorMailUtil.php @@ -21,11 +21,10 @@ final class PhabricatorMailUtil // If a mailbox prefix is configured and present, strip it off. $prefix_key = 'metamta.single-reply-handler-prefix'; $prefix = PhabricatorEnv::getEnvConfig($prefix_key); - $len = strlen($prefix); - if ($len) { + if (phutil_nonempty_string($prefix)) { $prefix = $prefix.'+'; - $len = $len + 1; + $len = strlen($prefix); if (!strncasecmp($raw_address, $prefix, $len)) { $raw_address = substr($raw_address, $len); diff --git a/src/applications/notification/setup/PhabricatorAphlictSetupCheck.php b/src/applications/notification/setup/PhabricatorAphlictSetupCheck.php index 7a00673ae1..20787c79bd 100644 --- a/src/applications/notification/setup/PhabricatorAphlictSetupCheck.php +++ b/src/applications/notification/setup/PhabricatorAphlictSetupCheck.php @@ -7,7 +7,7 @@ final class PhabricatorAphlictSetupCheck extends PhabricatorSetupCheck { PhabricatorNotificationClient::tryAnyConnection(); } catch (Exception $ex) { $message = pht( - "Phabricator is configured to use a notification server, but is ". + "This server is configured to use a notification server, but is ". "unable to connect to it. You should resolve this issue or disable ". "the notification server. It may be helpful to double check your ". "configuration or restart the server using the command below.\n\n%s", @@ -26,14 +26,14 @@ final class PhabricatorAphlictSetupCheck extends PhabricatorSetupCheck { ->setName(pht('Unable to Connect to Notification Server')) ->setSummary( pht( - 'Phabricator is configured to use a notification server, '. + 'This server is configured to use a notification server, '. 'but is not able to connect to it.')) ->setMessage($message) ->addRelatedPhabricatorConfig('notification.servers') ->addCommand( pht( "(To start the server, run this command.)\n%s", - 'phabricator/ $ ./bin/aphlict start')); + '$ ./bin/aphlict start')); return; } diff --git a/src/applications/nuance/github/NuanceGitHubRawEvent.php b/src/applications/nuance/github/NuanceGitHubRawEvent.php index b28a9222dc..8652dca9ad 100644 --- a/src/applications/nuance/github/NuanceGitHubRawEvent.php +++ b/src/applications/nuance/github/NuanceGitHubRawEvent.php @@ -164,7 +164,7 @@ final class NuanceGitHubRawEvent extends Phobject { $raw = $this->raw; $full = idxv($raw, array('repo', 'name')); - if (strlen($full)) { + if (phutil_nonempty_string($full)) { return $full; } diff --git a/src/applications/nuance/query/NuanceImportCursorDataQuery.php b/src/applications/nuance/query/NuanceImportCursorDataQuery.php index ae451abfb9..133c940b3f 100644 --- a/src/applications/nuance/query/NuanceImportCursorDataQuery.php +++ b/src/applications/nuance/query/NuanceImportCursorDataQuery.php @@ -26,10 +26,6 @@ final class NuanceImportCursorDataQuery return new NuanceImportCursorData(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/nuance/query/NuanceItemCommandQuery.php b/src/applications/nuance/query/NuanceItemCommandQuery.php index 27137cf8f6..a694657120 100644 --- a/src/applications/nuance/query/NuanceItemCommandQuery.php +++ b/src/applications/nuance/query/NuanceItemCommandQuery.php @@ -26,10 +26,6 @@ final class NuanceItemCommandQuery return new NuanceItemCommand(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/nuance/query/NuanceItemQuery.php b/src/applications/nuance/query/NuanceItemQuery.php index 834e81ca72..e2e32c84b4 100644 --- a/src/applications/nuance/query/NuanceItemQuery.php +++ b/src/applications/nuance/query/NuanceItemQuery.php @@ -56,10 +56,6 @@ final class NuanceItemQuery return new NuanceItem(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $items) { $viewer = $this->getViewer(); $source_phids = mpull($items, 'getSourcePHID'); diff --git a/src/applications/nuance/query/NuanceQueueQuery.php b/src/applications/nuance/query/NuanceQueueQuery.php index 10f761d189..acb69665e0 100644 --- a/src/applications/nuance/query/NuanceQueueQuery.php +++ b/src/applications/nuance/query/NuanceQueueQuery.php @@ -20,10 +20,6 @@ final class NuanceQueueQuery return new NuanceQueue(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/nuance/query/NuanceSourceQuery.php b/src/applications/nuance/query/NuanceSourceQuery.php index 907d9c314f..9186d47cdd 100644 --- a/src/applications/nuance/query/NuanceSourceQuery.php +++ b/src/applications/nuance/query/NuanceSourceQuery.php @@ -48,10 +48,6 @@ final class NuanceSourceQuery return 'source'; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $sources) { $all_types = NuanceSourceDefinition::getAllDefinitions(); diff --git a/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php b/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php index 2eaf5781dc..3862cfed92 100644 --- a/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php +++ b/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php @@ -4,7 +4,7 @@ final class NuancePhabricatorFormSourceDefinition extends NuanceSourceDefinition { public function getName() { - return pht('Phabricator Form'); + return pht('Web Form'); } public function getSourceDescription() { diff --git a/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php b/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php index 90e2d5a7dd..70dfd5ea8a 100644 --- a/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php +++ b/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php @@ -23,7 +23,9 @@ final class PhabricatorOAuthServerApplication extends PhabricatorApplication { } public function getFlavorText() { - return pht('Log In with Phabricator'); + return pht( + 'Log In with %s', + PlatformSymbols::getPlatformServerName()); } public function getApplicationGroup() { diff --git a/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php b/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php index 2b454e00ef..6b10dd60c5 100644 --- a/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php +++ b/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php @@ -209,9 +209,10 @@ final class PhabricatorOAuthServerAuthController ->setTitle(pht('Authenticate: %s', $name)) ->appendParagraph( pht( - 'This application ("%s") is authorized to use your Phabricator '. + 'This application ("%s") is authorized to use your %s '. 'credentials. Continue to complete the authentication workflow.', - phutil_tag('strong', array(), $name))) + phutil_tag('strong', array(), $name), + PlatformSymbols::getPlatformServerName())) ->addCancelButton((string)$full_uri, pht('Continue to Application')); return id(new AphrontDialogResponse())->setDialog($dialog); @@ -248,9 +249,10 @@ final class PhabricatorOAuthServerAuthController ->appendParagraph( pht( 'Do you want to authorize the external application "%s" to '. - 'access your Phabricator account data, including your primary '. + 'access your %s account data, including your primary '. 'email address?', - phutil_tag('strong', array(), $name))) + phutil_tag('strong', array(), $name), + PlatformSymbols::getPlatformServerName())) ->appendForm($form) ->addSubmitButton(pht('Authorize Access')) ->addCancelButton((string)$cancel_uri, pht('Do Not Authorize')); @@ -271,8 +273,8 @@ final class PhabricatorOAuthServerAuthController pht( 'This application also requested additional unrecognized '. 'permissions. These permissions may have existed in an older '. - 'version of Phabricator, or may be from a future version of '. - 'Phabricator. They will not be granted.')); + 'version of the software, or may be from a future version of '. + 'the software. They will not be granted.')); $unknown_form = id(new AphrontFormView()) ->setViewer($viewer) diff --git a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientDisableController.php b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientDisableController.php index 2ea9955365..334ef1b0aa 100644 --- a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientDisableController.php +++ b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientDisableController.php @@ -43,7 +43,7 @@ final class PhabricatorOAuthClientDisableController $title = pht('Disable OAuth Application'); $body = pht( 'Really disable the %s OAuth application? Users will no longer be '. - 'able to authenticate against it, nor access Phabricator using '. + 'able to authenticate against it, nor access this server using '. 'tokens generated by this application.', phutil_tag('strong', array(), $client->getName())); $button = pht('Disable Application'); diff --git a/src/applications/oauthserver/panel/PhabricatorOAuthServerAuthorizationsSettingsPanel.php b/src/applications/oauthserver/panel/PhabricatorOAuthServerAuthorizationsSettingsPanel.php index 89a1cc0281..21fb891330 100644 --- a/src/applications/oauthserver/panel/PhabricatorOAuthServerAuthorizationsSettingsPanel.php +++ b/src/applications/oauthserver/panel/PhabricatorOAuthServerAuthorizationsSettingsPanel.php @@ -68,7 +68,7 @@ final class PhabricatorOAuthServerAuthorizationsSettingsPanel ->setTitle(pht('Revoke Authorization?')) ->appendParagraph( pht( - 'This application will no longer be able to access Phabricator '. + 'This application will no longer be able to access this server '. 'on your behalf.')) ->addSubmitButton(pht('Revoke Authorization')) ->addCancelButton($panel_uri); diff --git a/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php b/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php index a746008f55..63a62e8cd9 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php @@ -26,10 +26,6 @@ final class PhabricatorOAuthClientAuthorizationQuery return new PhabricatorOAuthClientAuthorization(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $authorizations) { $client_phids = mpull($authorizations, 'getClientPHID'); diff --git a/src/applications/owners/conduit/OwnersQueryConduitAPIMethod.php b/src/applications/owners/conduit/OwnersQueryConduitAPIMethod.php deleted file mode 100644 index c79f612545..0000000000 --- a/src/applications/owners/conduit/OwnersQueryConduitAPIMethod.php +++ /dev/null @@ -1,166 +0,0 @@ - 'optional string', - 'projectOwner' => 'optional string', - 'userAffiliated' => 'optional string', - 'repositoryCallsign' => 'optional string', - 'path' => 'optional string', - ); - } - - protected function defineReturnType() { - return 'dict dict of package info>'; - } - - protected function defineErrorTypes() { - return array( - 'ERR-INVALID-USAGE' => pht( - 'Provide one of a single owner phid (user/project), a single '. - 'affiliated user phid (user), or a repository/path.'), - 'ERR-INVALID-PARAMETER' => pht('Parameter should be a phid.'), - 'ERR_REP_NOT_FOUND' => pht('The repository callsign is not recognized.'), - ); - } - - protected static function queryAll() { - return id(new PhabricatorOwnersPackage())->loadAll(); - } - - protected static function queryByOwner($owner) { - $is_valid_phid = - phid_get_type($owner) == PhabricatorPeopleUserPHIDType::TYPECONST || - phid_get_type($owner) == PhabricatorProjectProjectPHIDType::TYPECONST; - - if (!$is_valid_phid) { - throw id(new ConduitException('ERR-INVALID-PARAMETER')) - ->setErrorDescription( - pht( - 'Expected user/project PHID for owner, got %s.', - $owner)); - } - - $owners = id(new PhabricatorOwnersOwner())->loadAllWhere( - 'userPHID = %s', - $owner); - - $package_ids = mpull($owners, 'getPackageID'); - $packages = array(); - foreach ($package_ids as $id) { - $packages[] = id(new PhabricatorOwnersPackage())->load($id); - } - return $packages; - } - - private static function queryByPath( - PhabricatorUser $viewer, - $repo_callsign, - $path) { - - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($viewer) - ->withCallsigns(array($repo_callsign)) - ->executeOne(); - - if (!$repository) { - throw id(new ConduitException('ERR_REP_NOT_FOUND')) - ->setErrorDescription( - pht( - 'Repository callsign %s not recognized', - $repo_callsign)); - } - if ($path == null) { - return PhabricatorOwnersPackage::loadPackagesForRepository($repository); - } else { - return PhabricatorOwnersPackage::loadOwningPackages( - $repository, $path); - } - } - - public static function buildPackageInformationDictionaries($packages) { - assert_instances_of($packages, 'PhabricatorOwnersPackage'); - - $result = array(); - foreach ($packages as $package) { - $p_owners = $package->loadOwners(); - $p_paths = $package->loadPaths(); - - $owners = array_values(mpull($p_owners, 'getUserPHID')); - $paths = array(); - foreach ($p_paths as $p) { - $paths[] = array($p->getRepositoryPHID(), $p->getPath()); - } - - $result[$package->getPHID()] = array( - 'phid' => $package->getPHID(), - 'name' => $package->getName(), - 'description' => $package->getDescription(), - 'owners' => $owners, - 'paths' => $paths, - ); - } - return $result; - } - - protected function execute(ConduitAPIRequest $request) { - $is_owner_query = - ($request->getValue('userOwner') || - $request->getValue('projectOwner')) ? - 1 : 0; - - $is_affiliated_query = $request->getValue('userAffiliated') ? 1 : 0; - - $repo = $request->getValue('repositoryCallsign'); - $path = $request->getValue('path'); - $is_path_query = $repo ? 1 : 0; - - if ($is_owner_query + $is_path_query + $is_affiliated_query === 0) { - // if no search terms are provided, return everything - $packages = self::queryAll(); - } else if ($is_owner_query + $is_path_query + $is_affiliated_query > 1) { - // otherwise, exactly one of these should be provided - throw new ConduitException('ERR-INVALID-USAGE'); - } - - if ($is_affiliated_query) { - $query = id(new PhabricatorOwnersPackageQuery()) - ->setViewer($request->getUser()); - - $query->withAuthorityPHIDs(array($request->getValue('userAffiliated'))); - - $packages = $query->execute(); - } else if ($is_owner_query) { - $owner = nonempty( - $request->getValue('userOwner'), - $request->getValue('projectOwner')); - - $packages = self::queryByOwner($owner); - - } else if ($is_path_query) { - $packages = self::queryByPath($request->getUser(), $repo, $path); - } - - return self::buildPackageInformationDictionaries($packages); - } - -} diff --git a/src/applications/owners/controller/PhabricatorOwnersDetailController.php b/src/applications/owners/controller/PhabricatorOwnersDetailController.php index ff634a1ad0..8512107e75 100644 --- a/src/applications/owners/controller/PhabricatorOwnersDetailController.php +++ b/src/applications/owners/controller/PhabricatorOwnersDetailController.php @@ -197,6 +197,12 @@ final class PhabricatorOwnersDetailController $name = idx($spec, 'short', $dominion); $view->addProperty(pht('Dominion'), $name); + $authority_mode = $package->getAuthorityMode(); + $authority_map = PhabricatorOwnersPackage::getAuthorityOptionsMap(); + $spec = idx($authority_map, $authority_mode, array()); + $name = idx($spec, 'short', $authority_mode); + $view->addProperty(pht('Authority'), $name); + $auto = $package->getAutoReview(); $autoreview_map = PhabricatorOwnersPackage::getAutoreviewOptionsMap(); $spec = idx($autoreview_map, $auto, array()); diff --git a/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php b/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php index 13f896d3f0..416f2e38f2 100644 --- a/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php +++ b/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php @@ -90,6 +90,9 @@ EOTEXT $dominion_map = PhabricatorOwnersPackage::getDominionOptionsMap(); $dominion_map = ipull($dominion_map, 'name'); + $authority_map = PhabricatorOwnersPackage::getAuthorityOptionsMap(); + $authority_map = ipull($authority_map, 'name'); + return array( id(new PhabricatorTextEditField()) ->setKey('name') @@ -118,6 +121,16 @@ EOTEXT ->setIsCopyable(true) ->setValue($object->getDominion()) ->setOptions($dominion_map), + id(new PhabricatorSelectEditField()) + ->setKey('authority') + ->setLabel(pht('Authority')) + ->setDescription( + pht('Change package authority rules.')) + ->setTransactionType( + PhabricatorOwnersPackageAuthorityTransaction::TRANSACTIONTYPE) + ->setIsCopyable(true) + ->setValue($object->getAuthorityMode()) + ->setOptions($authority_map), id(new PhabricatorSelectEditField()) ->setKey('autoReview') ->setLabel(pht('Auto Review')) diff --git a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php index 67b4836a5a..a80b972548 100644 --- a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php +++ b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php @@ -10,6 +10,7 @@ final class PhabricatorOwnersPackageQuery private $repositoryPHIDs; private $paths; private $statuses; + private $authorityModes; private $controlMap = array(); private $controlResults; @@ -77,6 +78,11 @@ final class PhabricatorOwnersPackageQuery return $this; } + public function withAuthorityModes(array $modes) { + $this->authorityModes = $modes; + return $this; + } + public function withNameNgrams($ngrams) { return $this->withNgramsConstraint( new PhabricatorOwnersPackageNameNgrams(), @@ -96,10 +102,6 @@ final class PhabricatorOwnersPackageQuery $this->controlResults = array(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $packages) { $package_ids = mpull($packages, 'getID'); @@ -231,6 +233,13 @@ final class PhabricatorOwnersPackageQuery $where[] = qsprintf($conn, '%LO', $clauses); } + if ($this->authorityModes !== null) { + $where[] = qsprintf( + $conn, + 'authorityMode IN (%Ls)', + $this->authorityModes); + } + return $where; } diff --git a/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php b/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php index 728c3f42a8..26889c0e7a 100644 --- a/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php +++ b/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php @@ -168,8 +168,9 @@ final class PhabricatorOwnersPackageSearchEngine ->setIcon($icon) ->setTitle(pht('Welcome to %s', $app_name)) ->setDescription( - pht('Group sections of a codebase into packages for re-use in other '. - 'areas of Phabricator, like Herald rules.')) + pht( + 'Group sections of a codebase into packages for re-use in other '. + 'applications, like Herald rules.')) ->addAction($create_button); return $view; diff --git a/src/applications/owners/storage/PhabricatorOwnersPackage.php b/src/applications/owners/storage/PhabricatorOwnersPackage.php index b9e91ef958..7e58d586b2 100644 --- a/src/applications/owners/storage/PhabricatorOwnersPackage.php +++ b/src/applications/owners/storage/PhabricatorOwnersPackage.php @@ -21,6 +21,7 @@ final class PhabricatorOwnersPackage protected $dominion; protected $properties = array(); protected $auditingState; + protected $authorityMode; private $paths = self::ATTACHABLE; private $owners = self::ATTACHABLE; @@ -41,6 +42,9 @@ final class PhabricatorOwnersPackage const DOMINION_STRONG = 'strong'; const DOMINION_WEAK = 'weak'; + const AUTHORITY_STRONG = 'strong'; + const AUTHORITY_WEAK = 'weak'; + const PROPERTY_IGNORED = 'ignored'; public static function initializeNewPackage(PhabricatorUser $actor) { @@ -58,6 +62,7 @@ final class PhabricatorOwnersPackage ->setAuditingState(PhabricatorOwnersAuditRule::AUDITING_NONE) ->setAutoReview(self::AUTOREVIEW_NONE) ->setDominion(self::DOMINION_STRONG) + ->setAuthorityMode(self::AUTHORITY_STRONG) ->setViewPolicy($view_policy) ->setEditPolicy($edit_policy) ->attachPaths(array()) @@ -115,6 +120,19 @@ final class PhabricatorOwnersPackage ); } + public static function getAuthorityOptionsMap() { + return array( + self::AUTHORITY_STRONG => array( + 'name' => pht('Strong (Package Owns Paths)'), + 'short' => pht('Strong'), + ), + self::AUTHORITY_WEAK => array( + 'name' => pht('Weak (Package Watches Paths)'), + 'short' => pht('Weak'), + ), + ); + } + protected function getConfiguration() { return array( // This information is better available from the history table. @@ -130,6 +148,7 @@ final class PhabricatorOwnersPackage 'status' => 'text32', 'autoReview' => 'text32', 'dominion' => 'text32', + 'authorityMode' => 'text32', ), ) + parent::getConfiguration(); } @@ -568,6 +587,10 @@ final class PhabricatorOwnersPackage return PhabricatorOwnersAuditRule::newFromState($this->getAuditingState()); } + public function getHasStrongAuthority() { + return ($this->getAuthorityMode() === self::AUTHORITY_STRONG); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ @@ -696,6 +719,10 @@ final class PhabricatorOwnersPackage ->setKey('dominion') ->setType('map') ->setDescription(pht('Dominion setting information.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('authority') + ->setType('map') + ->setDescription(pht('Authority setting information.')), id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('ignored') ->setType('map') @@ -747,6 +774,23 @@ final class PhabricatorOwnersPackage 'short' => $dominion_short, ); + + $authority_value = $this->getAuthorityMode(); + $authority_map = self::getAuthorityOptionsMap(); + if (isset($authority_map[$authority_value])) { + $authority_label = $authority_map[$authority_value]['name']; + $authority_short = $authority_map[$authority_value]['short']; + } else { + $authority_label = pht('Unknown ("%s")', $authority_value); + $authority_short = pht('Unknown ("%s")', $authority_value); + } + + $authority = array( + 'value' => $authority_value, + 'label' => $authority_label, + 'short' => $authority_short, + ); + // Force this to always emit as a JSON object even if empty, never as // a JSON list. $ignored = $this->getIgnoredPathAttributes(); @@ -762,6 +806,7 @@ final class PhabricatorOwnersPackage 'review' => $review, 'audit' => $audit, 'dominion' => $dominion, + 'authority' => $authority, 'ignored' => $ignored, ); } diff --git a/src/applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php b/src/applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php index d219abfc1e..8c581215e9 100644 --- a/src/applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php +++ b/src/applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php @@ -14,34 +14,34 @@ final class PhabricatorOwnersPackageTestCase extends PhabricatorTestCase { 'id' => 1, 'excluded' => 1, 'dominion' => PhabricatorOwnersPackage::DOMINION_STRONG, - 'path' => 'src/releeph/', + 'path' => 'src/example/', ), array( 'id' => 2, 'excluded' => 0, 'dominion' => PhabricatorOwnersPackage::DOMINION_STRONG, - 'path' => 'src/releeph/', + 'path' => 'src/example/', ), ); $paths = array( - 'src/' => array('src/a.php' => true, 'src/releeph/b.php' => true), - 'src/releeph/' => array('src/releeph/b.php' => true), + 'src/' => array('src/a.php' => true, 'src/example/b.php' => true), + 'src/example/' => array('src/example/b.php' => true), ); $this->assertEqual( array( 1 => strlen('src/'), - 2 => strlen('src/releeph/'), + 2 => strlen('src/example/'), ), PhabricatorOwnersPackage::findLongestPathsPerPackage($rows, $paths)); $paths = array( - 'src/' => array('src/releeph/b.php' => true), - 'src/releeph/' => array('src/releeph/b.php' => true), + 'src/' => array('src/example/b.php' => true), + 'src/example/' => array('src/example/b.php' => true), ); $this->assertEqual( array( - 2 => strlen('src/releeph/'), + 2 => strlen('src/example/'), ), PhabricatorOwnersPackage::findLongestPathsPerPackage($rows, $paths)); diff --git a/src/applications/owners/xaction/PhabricatorOwnersPackageAuthorityTransaction.php b/src/applications/owners/xaction/PhabricatorOwnersPackageAuthorityTransaction.php new file mode 100644 index 0000000000..f01a6bf5fe --- /dev/null +++ b/src/applications/owners/xaction/PhabricatorOwnersPackageAuthorityTransaction.php @@ -0,0 +1,56 @@ +getAuthorityMode(); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + $map = PhabricatorOwnersPackage::getAuthorityOptionsMap(); + foreach ($xactions as $xaction) { + $new = $xaction->getNewValue(); + + if (empty($map[$new])) { + $valid = array_keys($map); + + $errors[] = $this->newInvalidError( + pht( + 'Authority setting "%s" is not valid. '. + 'Valid settings are: %s.', + $new, + implode(', ', $valid)), + $xaction); + } + } + + return $errors; + } + + public function applyInternalEffects($object, $value) { + $object->setAuthorityMode($value); + } + + public function getTitle() { + $map = PhabricatorOwnersPackage::getAuthorityOptionsMap(); + $map = ipull($map, 'short'); + + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $old = idx($map, $old, $old); + $new = idx($map, $new, $new); + + return pht( + '%s adjusted package authority rules from %s to %s.', + $this->renderAuthor(), + $this->renderValue($old), + $this->renderValue($new)); + } + +} diff --git a/src/applications/packages/query/PhabricatorPackagesPackageQuery.php b/src/applications/packages/query/PhabricatorPackagesPackageQuery.php index 67cd3954c9..c97e3b244c 100644 --- a/src/applications/packages/query/PhabricatorPackagesPackageQuery.php +++ b/src/applications/packages/query/PhabricatorPackagesPackageQuery.php @@ -44,10 +44,6 @@ final class PhabricatorPackagesPackageQuery return new PhabricatorPackagesPackage(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/packages/query/PhabricatorPackagesPublisherQuery.php b/src/applications/packages/query/PhabricatorPackagesPublisherQuery.php index d46535c20d..7d31e5fec6 100644 --- a/src/applications/packages/query/PhabricatorPackagesPublisherQuery.php +++ b/src/applications/packages/query/PhabricatorPackagesPublisherQuery.php @@ -32,10 +32,6 @@ final class PhabricatorPackagesPublisherQuery return new PhabricatorPackagesPublisher(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/packages/query/PhabricatorPackagesVersionQuery.php b/src/applications/packages/query/PhabricatorPackagesVersionQuery.php index 6f417e2f77..447907c4a0 100644 --- a/src/applications/packages/query/PhabricatorPackagesVersionQuery.php +++ b/src/applications/packages/query/PhabricatorPackagesVersionQuery.php @@ -44,10 +44,6 @@ final class PhabricatorPackagesVersionQuery return new PhabricatorPackagesVersion(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/passphrase/query/PassphraseCredentialQuery.php b/src/applications/passphrase/query/PassphraseCredentialQuery.php index 2518ad44e5..98ae9429a8 100644 --- a/src/applications/passphrase/query/PassphraseCredentialQuery.php +++ b/src/applications/passphrase/query/PassphraseCredentialQuery.php @@ -57,10 +57,6 @@ final class PassphraseCredentialQuery return new PassphraseCredential(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { if ($this->needSecrets) { $secret_ids = mpull($page, 'getSecretID'); @@ -148,7 +144,7 @@ final class PassphraseCredentialQuery (int)$this->allowConduit); } - if (strlen($this->nameContains)) { + if (phutil_nonempty_string($this->nameContains)) { $where[] = qsprintf( $conn, 'LOWER(c.name) LIKE %~', diff --git a/src/applications/passphrase/query/PassphraseCredentialSearchEngine.php b/src/applications/passphrase/query/PassphraseCredentialSearchEngine.php index ac4cbec9bf..971094afc4 100644 --- a/src/applications/passphrase/query/PassphraseCredentialSearchEngine.php +++ b/src/applications/passphrase/query/PassphraseCredentialSearchEngine.php @@ -124,8 +124,7 @@ final class PassphraseCredentialSearchEngine ->setIcon($icon) ->setTitle(pht('Welcome to %s', $app_name)) ->setDescription( - pht('Credential management for re-use in other areas of Phabricator '. - 'or general storage of shared secrets.')) + pht('Credential management and general storage of shared secrets.')) ->addAction($create_button); return $view; diff --git a/src/applications/paste/query/PhabricatorPasteQuery.php b/src/applications/paste/query/PhabricatorPasteQuery.php index d90ef3b1d2..e841bff300 100644 --- a/src/applications/paste/query/PhabricatorPasteQuery.php +++ b/src/applications/paste/query/PhabricatorPasteQuery.php @@ -84,10 +84,6 @@ final class PhabricatorPasteQuery return new PhabricatorPaste(); } - protected function loadPage() { - return $this->loadStandardPage(new PhabricatorPaste()); - } - protected function didFilterPage(array $pastes) { if ($this->needRawContent) { $pastes = $this->loadRawContent($pastes); diff --git a/src/applications/paste/xaction/PhabricatorPasteContentTransaction.php b/src/applications/paste/xaction/PhabricatorPasteContentTransaction.php index 8df9ad8057..00c01b0fcb 100644 --- a/src/applications/paste/xaction/PhabricatorPasteContentTransaction.php +++ b/src/applications/paste/xaction/PhabricatorPasteContentTransaction.php @@ -5,6 +5,8 @@ final class PhabricatorPasteContentTransaction const TRANSACTIONTYPE = 'paste.create'; + private $filePHID; + public function generateOldValue($object) { return $object->getFilePHID(); } @@ -14,7 +16,8 @@ final class PhabricatorPasteContentTransaction } public function extractFilePHIDs($object, $value) { - return array($value); + $file_phid = $this->getFilePHID($object, $value); + return array($file_phid); } public function validateTransactions($object, array $xactions) { @@ -31,6 +34,18 @@ final class PhabricatorPasteContentTransaction } public function generateNewValue($object, $value) { + return $this->getFilePHID($object, $value); + } + + private function getFilePHID($object, $value) { + if ($this->filePHID === null) { + $this->filePHID = $this->newFilePHID($object, $value); + } + + return $this->filePHID; + } + + private function newFilePHID($object, $value) { // If this transaction does not really change the paste content, return // the current file PHID so this transaction no-ops. $old_content = $object->getRawContent(); diff --git a/src/applications/people/controller/PhabricatorPeopleApproveController.php b/src/applications/people/controller/PhabricatorPeopleApproveController.php index af08a6fbdc..a89dc85322 100644 --- a/src/applications/people/controller/PhabricatorPeopleApproveController.php +++ b/src/applications/people/controller/PhabricatorPeopleApproveController.php @@ -52,7 +52,7 @@ final class PhabricatorPeopleApproveController ->setTitle(pht('Confirm Approval')) ->appendChild( pht( - 'Allow %s to access this Phabricator install?', + 'Allow %s to access this server?', phutil_tag('strong', array(), $user->getUsername()))) ->addCancelButton($done_uri) ->addSubmitButton(pht('Approve Account')); diff --git a/src/applications/people/controller/PhabricatorPeopleCreateController.php b/src/applications/people/controller/PhabricatorPeopleCreateController.php index c0b232645b..320ed57d3c 100644 --- a/src/applications/people/controller/PhabricatorPeopleCreateController.php +++ b/src/applications/people/controller/PhabricatorPeopleCreateController.php @@ -24,7 +24,7 @@ final class PhabricatorPeopleCreateController $title = pht('Create New User'); $standard_caption = pht( - 'Create a standard user account. These users can log in to Phabricator, '. + 'Create a standard user account. These users can log in, '. 'use the web interface and API, and receive email.'); $standard_admin = pht( diff --git a/src/applications/people/controller/PhabricatorPeopleDisableController.php b/src/applications/people/controller/PhabricatorPeopleDisableController.php index 9f2718086b..6e3f436d6f 100644 --- a/src/applications/people/controller/PhabricatorPeopleDisableController.php +++ b/src/applications/people/controller/PhabricatorPeopleDisableController.php @@ -90,7 +90,7 @@ final class PhabricatorPeopleDisableController $short_title = pht('Disable User'); $body = pht( - 'Disable %s? They will no longer be able to access Phabricator or '. + 'Disable %s? They will no longer be able to access this server or '. 'receive email.', phutil_tag('strong', array(), $user->getUsername())); @@ -100,7 +100,7 @@ final class PhabricatorPeopleDisableController $short_title = pht('Enable User'); $body = pht( - 'Enable %s? They will be able to access Phabricator and receive '. + 'Enable %s? They will be able to access this server and receive '. 'email again.', phutil_tag('strong', array(), $user->getUsername())); diff --git a/src/applications/people/controller/PhabricatorPeopleEmpowerController.php b/src/applications/people/controller/PhabricatorPeopleEmpowerController.php index 22e7c22b68..b7af73ac02 100644 --- a/src/applications/people/controller/PhabricatorPeopleEmpowerController.php +++ b/src/applications/people/controller/PhabricatorPeopleEmpowerController.php @@ -44,7 +44,7 @@ final class PhabricatorPeopleEmpowerController $short = pht('Remove Administrator'); $body = pht( 'Remove %s as an administrator? They will no longer be able to '. - 'perform administrative functions on this Phabricator install.', + 'perform administrative functions on this server.', phutil_tag('strong', array(), $user->getUsername())); $submit = pht('Remove Administrator'); } else { @@ -53,7 +53,7 @@ final class PhabricatorPeopleEmpowerController $body = pht( 'Empower %s as an administrator? They will be able to create users, '. 'approve users, make and remove administrators, delete accounts, and '. - 'perform other administrative functions on this Phabricator install.', + 'perform other administrative functions on this server.', phutil_tag('strong', array(), $user->getUsername())); $submit = pht('Make Administrator'); } diff --git a/src/applications/people/controller/PhabricatorPeopleInviteSendController.php b/src/applications/people/controller/PhabricatorPeopleInviteSendController.php index e611606a79..78ab7573ed 100644 --- a/src/applications/people/controller/PhabricatorPeopleInviteSendController.php +++ b/src/applications/people/controller/PhabricatorPeopleInviteSendController.php @@ -77,8 +77,9 @@ final class PhabricatorPeopleInviteSendController $template = array(); $template[] = pht( - '%s has invited you to join Phabricator.', - $viewer->getFullName()); + '%s has invited you to join %s.', + $viewer->getFullName(), + PlatformSymbols::getPlatformServerName()); if (strlen(trim($message))) { $template[] = $message; @@ -174,7 +175,7 @@ final class PhabricatorPeopleInviteSendController ->setUser($viewer) ->appendRemarkupInstructions( pht( - 'To invite users to Phabricator, enter their email addresses below. '. + 'To invite users, enter their email addresses below. '. 'Separate addresses with commas or newlines.')) ->appendChild( id(new AphrontFormTextAreaControl()) diff --git a/src/applications/people/controller/PhabricatorPeopleNewController.php b/src/applications/people/controller/PhabricatorPeopleNewController.php index 858ebef234..121e464b30 100644 --- a/src/applications/people/controller/PhabricatorPeopleNewController.php +++ b/src/applications/people/controller/PhabricatorPeopleNewController.php @@ -185,7 +185,9 @@ final class PhabricatorPeopleNewController ->addCheckbox( 'welcome', 1, - pht('Send "Welcome to Phabricator" email with login instructions.'), + pht( + 'Send "Welcome to %s" email with login instructions.', + PlatformSymbols::getPlatformServerName()), $welcome_checked)); } @@ -202,15 +204,15 @@ final class PhabricatorPeopleNewController pht( '**Why do bot accounts need an email address?**'. "\n\n". - 'Although bots do not normally receive email from Phabricator, '. - 'they can interact with other systems which require an email '. - 'address. Examples include:'. + 'Although bots do not normally receive email, they can interact '. + 'with other systems which require an email address. Examples '. + 'include:'. "\n\n". " - If the account takes actions which //send// email, we need ". " an address to use in the //From// header.\n". " - If the account creates commits, Git and Mercurial require ". " an email address for authorship.\n". - " - If you send email //to// Phabricator on behalf of the ". + " - If you send email //to// this server on behalf of the ". " account, the address can identify the sender.\n". " - Some internal authentication functions depend on accounts ". " having an email address.\n". diff --git a/src/applications/people/controller/PhabricatorPeopleWelcomeController.php b/src/applications/people/controller/PhabricatorPeopleWelcomeController.php index 94d5e0bb03..924eb97b5d 100644 --- a/src/applications/people/controller/PhabricatorPeopleWelcomeController.php +++ b/src/applications/people/controller/PhabricatorPeopleWelcomeController.php @@ -68,9 +68,10 @@ final class PhabricatorPeopleWelcomeController ->appendRemarkupInstructions( pht( 'This workflow will send this user ("%s") a copy of the "Welcome to '. - 'Phabricator" email that users normally receive when their '. + '%s" email that users normally receive when their '. 'accounts are created by an administrator.', - $user->getUsername())) + $user->getUsername(), + PlatformSymbols::getPlatformServerName())) ->appendRemarkupInstructions( pht( 'The email will contain a link that the user may use to log in '. diff --git a/src/applications/people/mail/PhabricatorPeopleEmailLoginMailEngine.php b/src/applications/people/mail/PhabricatorPeopleEmailLoginMailEngine.php index c25b19c3c1..4904e269a6 100644 --- a/src/applications/people/mail/PhabricatorPeopleEmailLoginMailEngine.php +++ b/src/applications/people/mail/PhabricatorPeopleEmailLoginMailEngine.php @@ -28,9 +28,13 @@ final class PhabricatorPeopleEmailLoginMailEngine $is_set_password = $this->isSetPasswordWorkflow(); if ($is_set_password) { - $subject = pht('[Phabricator] Account Password Link'); + $subject = pht( + '[%s] Account Password Link', + PlatformSymbols::getPlatformServerName()); } else { - $subject = pht('[Phabricator] Account Login Link'); + $subject = pht( + '[%s] Account Login Link', + PlatformSymbols::getPlatformServerName()); } $recipient = $this->getRecipient(); @@ -72,7 +76,7 @@ final class PhabricatorPeopleEmailLoginMailEngine $login_uri); } else if ($is_serious) { $body[] = pht( - "You can use this link to reset your Phabricator password:". + "You can use this link to reset your password:". "\n\n %s\n", $login_uri); } else { @@ -90,8 +94,7 @@ final class PhabricatorPeopleEmailLoginMailEngine } } else { $body[] = pht( - "You can use this login link to regain access to your Phabricator ". - "account:". + "You can use this login link to regain access to your account:". "\n\n". " %s\n", $login_uri); diff --git a/src/applications/people/mail/PhabricatorPeopleUsernameMailEngine.php b/src/applications/people/mail/PhabricatorPeopleUsernameMailEngine.php index e62a6a4859..8ba59afafc 100644 --- a/src/applications/people/mail/PhabricatorPeopleUsernameMailEngine.php +++ b/src/applications/people/mail/PhabricatorPeopleUsernameMailEngine.php @@ -40,9 +40,10 @@ final class PhabricatorPeopleUsernameMailEngine $body = sprintf( "%s\n\n %s\n %s\n", pht( - '%s (%s) has changed your Phabricator username.', + '%s (%s) has changed your %s username.', $sender_username, - $sender_realname), + $sender_realname, + PlatformSymbols::getPlatformServerName()), pht( 'Old Username: %s', $old_username), @@ -51,7 +52,10 @@ final class PhabricatorPeopleUsernameMailEngine $new_username)); return id(new PhabricatorMetaMTAMail()) - ->setSubject(pht('[Phabricator] Username Changed')) + ->setSubject( + pht( + '[%s] Username Changed', + PlatformSymbols::getPlatformServerName())) ->setBody($body); } diff --git a/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php b/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php index ec99a5a484..868cc13da1 100644 --- a/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php +++ b/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php @@ -60,7 +60,9 @@ final class PhabricatorPeopleWelcomeMailEngine $message = array(); - $message[] = pht('Welcome to Phabricator!'); + $message[] = pht( + 'Welcome to %s!', + PlatformSymbols::getPlatformServerName()); $message[] = pht( '%s (%s) has created an account for you.', @@ -80,10 +82,10 @@ final class PhabricatorPeopleWelcomeMailEngine $use_passwords = PhabricatorPasswordAuthProvider::getPasswordProvider(); if ($use_passwords) { $message[] = pht( - 'To log in to Phabricator, follow this link and set a password:'); + 'To log in, follow this link and set a password:'); $message[] = pht(' %s', $uri); $message[] = pht( - 'After you have set a password, you can log in to Phabricator in '. + 'After you have set a password, you can log in again in '. 'the future by going here:'); $message[] = pht(' %s', $base_uri); } else { @@ -91,7 +93,7 @@ final class PhabricatorPeopleWelcomeMailEngine 'To log in to your account for the first time, follow this link:'); $message[] = pht(' %s', $uri); $message[] = pht( - 'After you set up your account, you can log in to Phabricator in '. + 'After you set up your account, you can log in again in '. 'the future by going here:'); $message[] = pht(' %s', $base_uri); } @@ -104,7 +106,11 @@ final class PhabricatorPeopleWelcomeMailEngine $message = implode("\n\n", $message); return id(new PhabricatorMetaMTAMail()) - ->setSubject(pht('[Phabricator] Welcome to Phabricator')) + ->setSubject( + pht( + '[%s] Welcome to %s', + PlatformSymbols::getPlatformServerName(), + PlatformSymbols::getPlatformServerName())) ->setBody($message); } @@ -125,7 +131,9 @@ final class PhabricatorPeopleWelcomeMailEngine $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); if (!$is_serious) { - return pht("Love,\nPhabricator"); + return pht( + "Love,\n%s", + PlatformSymbols::getPlatformServerName()); } return null; diff --git a/src/applications/people/query/PhabricatorPeopleLogQuery.php b/src/applications/people/query/PhabricatorPeopleLogQuery.php index 203f79579a..e87fb33660 100644 --- a/src/applications/people/query/PhabricatorPeopleLogQuery.php +++ b/src/applications/people/query/PhabricatorPeopleLogQuery.php @@ -58,10 +58,6 @@ final class PhabricatorPeopleLogQuery return new PhabricatorUserLog(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/people/query/PhabricatorPeopleQuery.php b/src/applications/people/query/PhabricatorPeopleQuery.php index 5e737aaf90..b74b936ba8 100644 --- a/src/applications/people/query/PhabricatorPeopleQuery.php +++ b/src/applications/people/query/PhabricatorPeopleQuery.php @@ -162,10 +162,6 @@ final class PhabricatorPeopleQuery return new PhabricatorUser(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function didFilterPage(array $users) { if ($this->needProfile) { $user_list = mpull($users, null, 'getPHID'); @@ -341,7 +337,7 @@ final class PhabricatorPeopleQuery (int)$this->isMailingList); } - if (strlen($this->nameLike)) { + if ($this->nameLike !== null) { $where[] = qsprintf( $conn, 'user.username LIKE %~ OR user.realname LIKE %~', diff --git a/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php b/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php index 6e2627a96d..ead44a56dc 100644 --- a/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php +++ b/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php @@ -20,10 +20,6 @@ final class PhabricatorPeopleUserEmailQuery return new PhabricatorUserEmail(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function getPrimaryTableAlias() { return 'email'; } diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index 1560f9f65e..5edfb2d537 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -275,7 +275,8 @@ final class PhabricatorUser $this->setConduitCertificate($this->generateConduitCertificate()); } - if (!strlen($this->getAccountSecret())) { + $secret = $this->getAccountSecret(); + if (($secret === null) || !strlen($secret)) { $this->setAccountSecret(Filesystem::readRandomCharacters(64)); } diff --git a/src/applications/people/storage/PhabricatorUserEmail.php b/src/applications/people/storage/PhabricatorUserEmail.php index 4e43b2fb41..d9866f2c43 100644 --- a/src/applications/people/storage/PhabricatorUserEmail.php +++ b/src/applications/people/storage/PhabricatorUserEmail.php @@ -195,7 +195,9 @@ final class PhabricatorUserEmail $signature = null; if (!$is_serious) { - $signature = pht("Get Well Soon,\nPhabricator"); + $signature = pht( + "Get Well Soon,\n%s", + PlatformSymbols::getPlatformServerName()); } $body = sprintf( @@ -211,7 +213,10 @@ final class PhabricatorUserEmail id(new PhabricatorMetaMTAMail()) ->addRawTos(array($address)) ->setForceDelivery(true) - ->setSubject(pht('[Phabricator] Email Verification')) + ->setSubject( + pht( + '[%s] Email Verification', + PlatformSymbols::getPlatformServerName())) ->setBody($body) ->setRelatedPHID($user->getPHID()) ->saveAndSend(); @@ -242,15 +247,18 @@ final class PhabricatorUserEmail pht('Hi %s', $username), pht( 'This email address (%s) is no longer your primary email address. '. - 'Going forward, Phabricator will send all email to your new primary '. - 'email address (%s).', + 'Going forward, all email will be sent to your new primary email '. + 'address (%s).', $old_address, $new_address)); id(new PhabricatorMetaMTAMail()) ->addRawTos(array($old_address)) ->setForceDelivery(true) - ->setSubject(pht('[Phabricator] Primary Address Changed')) + ->setSubject( + pht( + '[%s] Primary Address Changed', + PlatformSymbols::getPlatformServerName())) ->setBody($body) ->setFrom($user->getPHID()) ->setRelatedPHID($user->getPHID()) @@ -276,13 +284,16 @@ final class PhabricatorUserEmail pht('Hi %s', $username), pht( 'This is now your primary email address (%s). Going forward, '. - 'Phabricator will send all email here.', + 'all email will be sent here.', $new_address)); id(new PhabricatorMetaMTAMail()) ->addRawTos(array($new_address)) ->setForceDelivery(true) - ->setSubject(pht('[Phabricator] Primary Address Changed')) + ->setSubject( + pht( + '[%s] Primary Address Changed', + PlatformSymbols::getPlatformServerName())) ->setBody($body) ->setFrom($user->getPHID()) ->setRelatedPHID($user->getPHID()) diff --git a/src/applications/people/xaction/PhabricatorUserApproveTransaction.php b/src/applications/people/xaction/PhabricatorUserApproveTransaction.php index 1084f0c8fa..213b381d1a 100644 --- a/src/applications/people/xaction/PhabricatorUserApproveTransaction.php +++ b/src/applications/people/xaction/PhabricatorUserApproveTransaction.php @@ -22,14 +22,16 @@ final class PhabricatorUserApproveTransaction $actor = $this->getActor(); $title = pht( - 'Phabricator Account "%s" Approved', + '%s Account "%s" Approved', + PlatformSymbols::getPlatformServerName(), $user->getUsername()); $body = sprintf( "%s\n\n %s\n\n", pht( - 'Your Phabricator account (%s) has been approved by %s. You can '. + 'Your %s account (%s) has been approved by %s. You can '. 'login here:', + PlatformSymbols::getPlatformServerName(), $user->getUsername(), $actor->getUsername()), PhabricatorEnv::getProductionURI('/')); @@ -37,7 +39,11 @@ final class PhabricatorUserApproveTransaction $mail = id(new PhabricatorMetaMTAMail()) ->addTos(array($user->getPHID())) ->addCCs(array($actor->getPHID())) - ->setSubject('[Phabricator] '.$title) + ->setSubject( + pht( + '[%s] %s', + PlatformSymbols::getPlatformServerName(), + $title)) ->setForceDelivery(true) ->setBody($body) ->saveAndSend(); diff --git a/src/applications/phame/controller/blog/PhameBlogManageController.php b/src/applications/phame/controller/blog/PhameBlogManageController.php index 65378d91cb..a30d036c3f 100644 --- a/src/applications/phame/controller/blog/PhameBlogManageController.php +++ b/src/applications/phame/controller/blog/PhameBlogManageController.php @@ -143,11 +143,6 @@ final class PhameBlogManageController extends PhameBlogController { ), $feed_uri)); - $engine = id(new PhabricatorMarkupEngine()) - ->setViewer($viewer) - ->addObject($blog, PhameBlog::MARKUP_FIELD_DESCRIPTION) - ->process(); - $description = $blog->getDescription(); if (strlen($description)) { $description = new PHUIRemarkupView($viewer, $description); diff --git a/src/applications/phame/controller/blog/PhameBlogViewController.php b/src/applications/phame/controller/blog/PhameBlogViewController.php index e9e422aeb1..758fe955ad 100644 --- a/src/applications/phame/controller/blog/PhameBlogViewController.php +++ b/src/applications/phame/controller/blog/PhameBlogViewController.php @@ -103,7 +103,8 @@ final class PhameBlogViewController extends PhameLiveController { ->setDescription($description) ->setImage($blog->getProfileImageURI()); - $crumbs = $this->buildApplicationCrumbs(); + $crumbs = $this->buildApplicationCrumbs() + ->setBorder(false); $page = $this->newPage() ->setTitle($blog->getName()) diff --git a/src/applications/phame/editor/PhameBlogEditor.php b/src/applications/phame/editor/PhameBlogEditor.php index c122d8fa3b..656ede95fe 100644 --- a/src/applications/phame/editor/PhameBlogEditor.php +++ b/src/applications/phame/editor/PhameBlogEditor.php @@ -21,8 +21,10 @@ final class PhameBlogEditor public function getTransactionTypes() { $types = parent::getTransactionTypes(); + $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; + $types[] = PhabricatorTransactions::TYPE_INTERACT_POLICY; return $types; } diff --git a/src/applications/phame/editor/PhamePostEditEngineLock.php b/src/applications/phame/editor/PhamePostEditEngineLock.php new file mode 100644 index 0000000000..2c3ff01f20 --- /dev/null +++ b/src/applications/phame/editor/PhamePostEditEngineLock.php @@ -0,0 +1,29 @@ +setTitle(pht('Edit Locked Post')) + ->appendParagraph( + pht('Comments are disabled for this post. Edit it anyway?')) + ->addSubmitButton(pht('Edit Post')); + } + + public function willBlockUserInteractionWithDialog( + AphrontDialogView $dialog) { + + return $dialog + ->setTitle(pht('Post Locked')) + ->appendParagraph( + pht('You can not interact with this post because it is locked.')); + } + + public function getLockedObjectDisplayText() { + return pht('Comments have been disabled for this post.'); + } + +} diff --git a/src/applications/phame/editor/PhamePostEditor.php b/src/applications/phame/editor/PhamePostEditor.php index d95389e549..61092114d1 100644 --- a/src/applications/phame/editor/PhamePostEditor.php +++ b/src/applications/phame/editor/PhamePostEditor.php @@ -21,6 +21,8 @@ final class PhamePostEditor public function getTransactionTypes() { $types = parent::getTransactionTypes(); + + $types[] = PhabricatorTransactions::TYPE_INTERACT_POLICY; $types[] = PhabricatorTransactions::TYPE_COMMENT; return $types; diff --git a/src/applications/phame/policyrule/PhameInheritBlogPolicyRule.php b/src/applications/phame/policyrule/PhameInheritBlogPolicyRule.php new file mode 100644 index 0000000000..4d8309d259 --- /dev/null +++ b/src/applications/phame/policyrule/PhameInheritBlogPolicyRule.php @@ -0,0 +1,51 @@ +getBlog(), + PhabricatorPolicyCapability::CAN_INTERACT); + } + + public function getValueControlType() { + return self::CONTROL_TYPE_NONE; + } + +} diff --git a/src/applications/phame/query/PhameBlogQuery.php b/src/applications/phame/query/PhameBlogQuery.php index b4018c78eb..b730f3c4aa 100644 --- a/src/applications/phame/query/PhameBlogQuery.php +++ b/src/applications/phame/query/PhameBlogQuery.php @@ -45,10 +45,6 @@ final class PhameBlogQuery extends PhabricatorCursorPagedPolicyAwareQuery { return new PhameBlog(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/phame/query/PhamePostQuery.php b/src/applications/phame/query/PhamePostQuery.php index d7396e553f..61ee4f2a25 100644 --- a/src/applications/phame/query/PhamePostQuery.php +++ b/src/applications/phame/query/PhamePostQuery.php @@ -50,10 +50,6 @@ final class PhamePostQuery extends PhabricatorCursorPagedPolicyAwareQuery { return new PhamePost(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $posts) { // We require blogs to do visibility checks, so load them unconditionally. $blog_phids = mpull($posts, 'getBlogPHID'); diff --git a/src/applications/phame/storage/PhameBlog.php b/src/applications/phame/storage/PhameBlog.php index 71a5186225..95507d61dc 100644 --- a/src/applications/phame/storage/PhameBlog.php +++ b/src/applications/phame/storage/PhameBlog.php @@ -13,8 +13,6 @@ final class PhameBlog extends PhameDAO PhabricatorFulltextInterface, PhabricatorFerretInterface { - const MARKUP_FIELD_DESCRIPTION = 'markup:description'; - protected $name; protected $subtitle; protected $description; @@ -26,6 +24,7 @@ final class PhameBlog extends PhameDAO protected $creatorPHID; protected $viewPolicy; protected $editPolicy; + protected $interactPolicy; protected $status; protected $mailKey; protected $profileImagePHID; @@ -56,10 +55,9 @@ final class PhameBlog extends PhameDAO 'profileImagePHID' => 'phid?', 'headerImagePHID' => 'phid?', - // T6203/NULLABILITY - // These policies should always be non-null. - 'editPolicy' => 'policy?', - 'viewPolicy' => 'policy?', + 'editPolicy' => 'policy', + 'viewPolicy' => 'policy', + 'interactPolicy' => 'policy', ), self::CONFIG_KEY_SCHEMA => array( 'key_phid' => null, @@ -92,7 +90,9 @@ final class PhameBlog extends PhameDAO ->setCreatorPHID($actor->getPHID()) ->setStatus(self::STATUS_ACTIVE) ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) - ->setEditPolicy(PhabricatorPolicies::POLICY_USER); + ->setEditPolicy(PhabricatorPolicies::POLICY_USER) + ->setInteractPolicy(PhabricatorPolicies::POLICY_USER); + return $blog; } @@ -154,7 +154,7 @@ final class PhameBlog extends PhameDAO $href = PhabricatorEnv::getProductionURI( '/config/edit/policy.allow-public/'); return pht( - 'For custom domains to work, this Phabricator instance must be '. + 'For custom domains to work, this this server must be '. 'configured to allow the public access policy. Configure this '. 'setting %s, or ask an administrator to configure this setting. '. 'The domain can be specified later once this setting has been '. @@ -233,6 +233,7 @@ final class PhameBlog extends PhameDAO public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_INTERACT, PhabricatorPolicyCapability::CAN_EDIT, ); } @@ -242,6 +243,8 @@ final class PhameBlog extends PhameDAO switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); + case PhabricatorPolicyCapability::CAN_INTERACT: + return $this->getInteractPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return $this->getEditPolicy(); } diff --git a/src/applications/phame/storage/PhamePost.php b/src/applications/phame/storage/PhamePost.php index 300579b086..21149f07c2 100644 --- a/src/applications/phame/storage/PhamePost.php +++ b/src/applications/phame/storage/PhamePost.php @@ -11,11 +11,11 @@ final class PhamePost extends PhameDAO PhabricatorDestructibleInterface, PhabricatorTokenReceiverInterface, PhabricatorConduitResultInterface, + PhabricatorEditEngineLockableInterface, PhabricatorFulltextInterface, PhabricatorFerretInterface { const MARKUP_FIELD_BODY = 'markup:body'; - const MARKUP_FIELD_SUMMARY = 'markup:summary'; protected $bloggerPHID; protected $title; @@ -28,6 +28,7 @@ final class PhamePost extends PhameDAO protected $blogPHID; protected $mailKey; protected $headerImagePHID; + protected $interactPolicy; private $blog = self::ATTACHABLE; private $headerImageFile = self::ATTACHABLE; @@ -41,7 +42,10 @@ final class PhamePost extends PhameDAO ->setBlogPHID($blog->getPHID()) ->attachBlog($blog) ->setDatePublished(PhabricatorTime::getNow()) - ->setVisibility(PhameConstants::VISIBILITY_PUBLISHED); + ->setVisibility(PhameConstants::VISIBILITY_PUBLISHED) + ->setInteractPolicy( + id(new PhameInheritBlogPolicyRule()) + ->getObjectPolicyFullKey()); return $post; } @@ -141,6 +145,8 @@ final class PhamePost extends PhameDAO // T6203/NULLABILITY // This one probably should be nullable? 'datePublished' => 'epoch', + + 'interactPolicy' => 'policy', ), self::CONFIG_KEY_SCHEMA => array( 'key_phid' => null, @@ -197,6 +203,7 @@ final class PhamePost extends PhameDAO return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, + PhabricatorPolicyCapability::CAN_INTERACT, ); } @@ -221,6 +228,8 @@ final class PhamePost extends PhameDAO } else { return PhabricatorPolicies::POLICY_NOONE; } + case PhabricatorPolicyCapability::CAN_INTERACT: + return $this->getInteractPolicy(); } } @@ -231,6 +240,8 @@ final class PhamePost extends PhameDAO case PhabricatorPolicyCapability::CAN_VIEW: case PhabricatorPolicyCapability::CAN_EDIT: return ($user->getPHID() == $this->getBloggerPHID()); + case PhabricatorPolicyCapability::CAN_INTERACT: + return false; } } @@ -255,8 +266,6 @@ final class PhamePost extends PhameDAO switch ($field) { case self::MARKUP_FIELD_BODY: return $this->getBody(); - case self::MARKUP_FIELD_SUMMARY: - return PhabricatorMarkupEngine::summarize($this->getBody()); } } @@ -385,4 +394,11 @@ final class PhamePost extends PhameDAO return new PhamePostFerretEngine(); } + +/* -( PhabricatorEditEngineLockableInterface )----------------------------- */ + + public function newEditEngineLock() { + return new PhamePostEditEngineLock(); + } + } diff --git a/src/applications/phid/handle/pool/PhabricatorHandleList.php b/src/applications/phid/handle/pool/PhabricatorHandleList.php index c3c6f40324..1418a107c0 100644 --- a/src/applications/phid/handle/pool/PhabricatorHandleList.php +++ b/src/applications/phid/handle/pool/PhabricatorHandleList.php @@ -126,22 +126,27 @@ final class PhabricatorHandleList /* -( Iterator )----------------------------------------------------------- */ + #[\ReturnTypeWillChange] public function rewind() { $this->cursor = 0; } + #[\ReturnTypeWillChange] public function current() { return $this->getHandle($this->phids[$this->cursor]); } + #[\ReturnTypeWillChange] public function key() { return $this->phids[$this->cursor]; } + #[\ReturnTypeWillChange] public function next() { ++$this->cursor; } + #[\ReturnTypeWillChange] public function valid() { return ($this->cursor < $this->count); } @@ -150,6 +155,7 @@ final class PhabricatorHandleList /* -( ArrayAccess )-------------------------------------------------------- */ + #[\ReturnTypeWillChange] public function offsetExists($offset) { // NOTE: We're intentionally not loading handles here so that isset() // checks do not trigger fetches. This gives us better bulk loading @@ -162,6 +168,7 @@ final class PhabricatorHandleList return isset($this->map[$offset]); } + #[\ReturnTypeWillChange] public function offsetGet($offset) { if ($this->handles === null) { $this->loadHandles(); @@ -169,10 +176,12 @@ final class PhabricatorHandleList return $this->handles[$offset]; } + #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { $this->raiseImmutableException(); } + #[\ReturnTypeWillChange] public function offsetUnset($offset) { $this->raiseImmutableException(); } @@ -189,6 +198,7 @@ final class PhabricatorHandleList /* -( Countable )---------------------------------------------------------- */ + #[\ReturnTypeWillChange] public function count() { return $this->count; } diff --git a/src/applications/pholio/query/PholioImageQuery.php b/src/applications/pholio/query/PholioImageQuery.php index 0d64540f91..69d890ab98 100644 --- a/src/applications/pholio/query/PholioImageQuery.php +++ b/src/applications/pholio/query/PholioImageQuery.php @@ -44,10 +44,6 @@ final class PholioImageQuery return new PholioImage(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/phortune/query/PhortuneAccountEmailQuery.php b/src/applications/phortune/query/PhortuneAccountEmailQuery.php index 0bdfdb78dc..0e0a668b8c 100644 --- a/src/applications/phortune/query/PhortuneAccountEmailQuery.php +++ b/src/applications/phortune/query/PhortuneAccountEmailQuery.php @@ -38,10 +38,6 @@ final class PhortuneAccountEmailQuery return new PhortuneAccountEmail(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $addresses) { $accounts = id(new PhortuneAccountQuery()) ->setViewer($this->getViewer()) diff --git a/src/applications/phortune/query/PhortuneAccountQuery.php b/src/applications/phortune/query/PhortuneAccountQuery.php index 70c12d9722..43c7c5f976 100644 --- a/src/applications/phortune/query/PhortuneAccountQuery.php +++ b/src/applications/phortune/query/PhortuneAccountQuery.php @@ -46,10 +46,6 @@ final class PhortuneAccountQuery return new PhortuneAccount(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $accounts) { $query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($accounts, 'getPHID')) diff --git a/src/applications/phortune/query/PhortuneMerchantQuery.php b/src/applications/phortune/query/PhortuneMerchantQuery.php index aef7d8aaf1..2c9aefc74d 100644 --- a/src/applications/phortune/query/PhortuneMerchantQuery.php +++ b/src/applications/phortune/query/PhortuneMerchantQuery.php @@ -32,10 +32,6 @@ final class PhortuneMerchantQuery return new PhortuneMerchant(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $merchants) { $query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($merchants, 'getPHID')) diff --git a/src/applications/phortune/query/PhortunePaymentMethodQuery.php b/src/applications/phortune/query/PhortunePaymentMethodQuery.php index 013fa147ec..b95881d3a7 100644 --- a/src/applications/phortune/query/PhortunePaymentMethodQuery.php +++ b/src/applications/phortune/query/PhortunePaymentMethodQuery.php @@ -38,10 +38,6 @@ final class PhortunePaymentMethodQuery return new PhortunePaymentMethod(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $methods) { $accounts = id(new PhortuneAccountQuery()) ->setViewer($this->getViewer()) diff --git a/src/applications/phortune/query/PhortuneSubscriptionQuery.php b/src/applications/phortune/query/PhortuneSubscriptionQuery.php index de0fee1980..5622578738 100644 --- a/src/applications/phortune/query/PhortuneSubscriptionQuery.php +++ b/src/applications/phortune/query/PhortuneSubscriptionQuery.php @@ -51,10 +51,6 @@ final class PhortuneSubscriptionQuery return new PhortuneSubscription(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $subscriptions) { $accounts = id(new PhortuneAccountQuery()) ->setViewer($this->getViewer()) diff --git a/src/applications/phragment/application/PhabricatorPhragmentApplication.php b/src/applications/phragment/application/PhabricatorPhragmentApplication.php deleted file mode 100644 index 164e0293fd..0000000000 --- a/src/applications/phragment/application/PhabricatorPhragmentApplication.php +++ /dev/null @@ -1,66 +0,0 @@ - array( - '' => 'PhragmentBrowseController', - 'browse/(?P.*)' => 'PhragmentBrowseController', - 'create/(?P.*)' => 'PhragmentCreateController', - 'update/(?P.+)' => 'PhragmentUpdateController', - 'policy/(?P.+)' => 'PhragmentPolicyController', - 'history/(?P.+)' => 'PhragmentHistoryController', - 'zip/(?P.+)' => 'PhragmentZIPController', - 'zip@(?P[^/]+)/(?P.+)' => 'PhragmentZIPController', - 'version/(?P[0-9]*)/' => 'PhragmentVersionController', - 'patch/(?P[0-9x]*)/(?P[0-9]*)/' => 'PhragmentPatchController', - 'revert/(?P[0-9]*)/(?P.*)' => 'PhragmentRevertController', - 'snapshot/' => array( - 'create/(?P.*)' => 'PhragmentSnapshotCreateController', - 'view/(?P[0-9]*)/' => 'PhragmentSnapshotViewController', - 'delete/(?P[0-9]*)/' => 'PhragmentSnapshotDeleteController', - 'promote/' => array( - 'latest/(?P.*)' => 'PhragmentSnapshotPromoteController', - '(?P[0-9]*)/' => 'PhragmentSnapshotPromoteController', - ), - ), - ), - ); - } - - protected function getCustomCapabilities() { - return array( - PhragmentCanCreateCapability::CAPABILITY => array(), - ); - } - -} diff --git a/src/applications/phragment/capability/PhragmentCanCreateCapability.php b/src/applications/phragment/capability/PhragmentCanCreateCapability.php deleted file mode 100644 index c2739c5df1..0000000000 --- a/src/applications/phragment/capability/PhragmentCanCreateCapability.php +++ /dev/null @@ -1,15 +0,0 @@ - 'required string', - 'state' => 'required dict', - ); - } - - protected function defineReturnType() { - return 'nonempty dict'; - } - - protected function defineErrorTypes() { - return array( - 'ERR_BAD_FRAGMENT' => pht('No such fragment exists.'), - ); - } - - protected function execute(ConduitAPIRequest $request) { - $path = $request->getValue('path'); - $state = $request->getValue('state'); - // The state is an array mapping file paths to hashes. - - $patches = array(); - - // We need to get all of the mappings (like phragment.getstate) first - // so that we can detect deletions and creations of files. - $fragment = id(new PhragmentFragmentQuery()) - ->setViewer($request->getUser()) - ->withPaths(array($path)) - ->executeOne(); - if ($fragment === null) { - throw new ConduitException('ERR_BAD_FRAGMENT'); - } - - $mappings = $fragment->getFragmentMappings( - $request->getUser(), - $fragment->getPath()); - - $file_phids = mpull(mpull($mappings, 'getLatestVersion'), 'getFilePHID'); - $files = id(new PhabricatorFileQuery()) - ->setViewer($request->getUser()) - ->withPHIDs($file_phids) - ->execute(); - $files = mpull($files, null, 'getPHID'); - - // Scan all of the files that the caller currently has and iterate - // over that. - foreach ($state as $path => $hash) { - // If $mappings[$path] exists, then the user has the file and it's - // also a fragment. - if (array_key_exists($path, $mappings)) { - $file_phid = $mappings[$path]->getLatestVersion()->getFilePHID(); - if ($file_phid !== null) { - // If the file PHID is present, then we need to check the - // hashes to see if they are the same. - $hash_caller = strtolower($state[$path]); - $hash_current = $files[$file_phid]->getContentHash(); - if ($hash_caller === $hash_current) { - // The user's version is identical to our version, so - // there is no update needed. - } else { - // The hash differs, and the user needs to update. - $patches[] = array( - 'path' => $path, - 'fileOld' => null, - 'fileNew' => $files[$file_phid], - 'hashOld' => $hash_caller, - 'hashNew' => $hash_current, - 'patchURI' => null, - ); - } - } else { - // We have a record of this as a file, but there is no file - // attached to the latest version, so we consider this to be - // a deletion. - $patches[] = array( - 'path' => $path, - 'fileOld' => null, - 'fileNew' => null, - 'hashOld' => $hash_caller, - 'hashNew' => PhragmentPatchUtil::EMPTY_HASH, - 'patchURI' => null, - ); - } - } else { - // If $mappings[$path] does not exist, then the user has a file, - // and we have absolutely no record of it what-so-ever (we haven't - // even recorded a deletion). Assuming most applications will store - // some form of data near their own files, this is probably a data - // file relevant for the application that is not versioned, so we - // don't tell the client to do anything with it. - } - } - - // Check the remaining files that we know about but the caller has - // not reported. - foreach ($mappings as $path => $child) { - if (array_key_exists($path, $state)) { - // We have already evaluated this above. - } else { - $file_phid = $mappings[$path]->getLatestVersion()->getFilePHID(); - if ($file_phid !== null) { - // If the file PHID is present, then this is a new file that - // we know about, but the caller does not. We need to tell - // the caller to create the file. - $hash_current = $files[$file_phid]->getContentHash(); - $patches[] = array( - 'path' => $path, - 'fileOld' => null, - 'fileNew' => $files[$file_phid], - 'hashOld' => PhragmentPatchUtil::EMPTY_HASH, - 'hashNew' => $hash_current, - 'patchURI' => null, - ); - } else { - // We have a record of deleting this file, and the caller hasn't - // reported it, so they've probably deleted it in a previous - // update. - } - } - } - - // Before we can calculate patches, we need to resolve the old versions - // of files so we can draw diffs on them. - $hashes = array(); - foreach ($patches as $patch) { - if ($patch['hashOld'] !== PhragmentPatchUtil::EMPTY_HASH) { - $hashes[] = $patch['hashOld']; - } - } - $old_files = array(); - if (count($hashes) !== 0) { - $old_files = id(new PhabricatorFileQuery()) - ->setViewer($request->getUser()) - ->withContentHashes($hashes) - ->execute(); - } - $old_files = mpull($old_files, null, 'getContentHash'); - foreach ($patches as $key => $patch) { - if ($patch['hashOld'] !== PhragmentPatchUtil::EMPTY_HASH) { - if (array_key_exists($patch['hashOld'], $old_files)) { - $patches[$key]['fileOld'] = $old_files[$patch['hashOld']]; - } else { - // We either can't see or can't read the old file. - $patches[$key]['hashOld'] = PhragmentPatchUtil::EMPTY_HASH; - $patches[$key]['fileOld'] = null; - } - } - } - - // Now run through all of the patch entries, calculate the patches - // and return the results. - foreach ($patches as $key => $patch) { - $data = PhragmentPatchUtil::calculatePatch( - $patches[$key]['fileOld'], - $patches[$key]['fileNew']); - unset($patches[$key]['fileOld']); - unset($patches[$key]['fileNew']); - - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); - $file = PhabricatorFile::newFromFileData( - $data, - array( - 'name' => 'patch.dmp', - 'ttl.relative' => phutil_units('24 hours in seconds'), - )); - unset($unguarded); - - $patches[$key]['patchURI'] = $file->getDownloadURI(); - } - - return $patches; - } - -} diff --git a/src/applications/phragment/conduit/PhragmentQueryFragmentsConduitAPIMethod.php b/src/applications/phragment/conduit/PhragmentQueryFragmentsConduitAPIMethod.php deleted file mode 100644 index 08a1bcba49..0000000000 --- a/src/applications/phragment/conduit/PhragmentQueryFragmentsConduitAPIMethod.php +++ /dev/null @@ -1,85 +0,0 @@ - 'required list', - ); - } - - protected function defineReturnType() { - return 'nonempty dict'; - } - - protected function defineErrorTypes() { - return array( - 'ERR_BAD_FRAGMENT' => pht('No such fragment exists.'), - ); - } - - protected function execute(ConduitAPIRequest $request) { - $paths = $request->getValue('paths'); - - $fragments = id(new PhragmentFragmentQuery()) - ->setViewer($request->getUser()) - ->withPaths($paths) - ->execute(); - $fragments = mpull($fragments, null, 'getPath'); - foreach ($paths as $path) { - if (!array_key_exists($path, $fragments)) { - throw new ConduitException('ERR_BAD_FRAGMENT'); - } - } - - $results = array(); - foreach ($fragments as $path => $fragment) { - $mappings = $fragment->getFragmentMappings( - $request->getUser(), - $fragment->getPath()); - - $file_phids = mpull(mpull($mappings, 'getLatestVersion'), 'getFilePHID'); - $files = id(new PhabricatorFileQuery()) - ->setViewer($request->getUser()) - ->withPHIDs($file_phids) - ->execute(); - $files = mpull($files, null, 'getPHID'); - - $result = array(); - foreach ($mappings as $cpath => $child) { - $file_phid = $child->getLatestVersion()->getFilePHID(); - if (!isset($files[$file_phid])) { - // Skip any files we don't have permission to access. - continue; - } - - $file = $files[$file_phid]; - $cpath = substr($child->getPath(), strlen($fragment->getPath()) + 1); - $result[] = array( - 'phid' => $child->getPHID(), - 'phidVersion' => $child->getLatestVersionPHID(), - 'path' => $cpath, - 'hash' => $file->getContentHash(), - 'version' => $child->getLatestVersion()->getSequence(), - 'uri' => $file->getViewURI(), - ); - } - $results[$path] = $result; - } - return $results; - } - -} diff --git a/src/applications/phragment/controller/PhragmentBrowseController.php b/src/applications/phragment/controller/PhragmentBrowseController.php deleted file mode 100644 index d511835ce9..0000000000 --- a/src/applications/phragment/controller/PhragmentBrowseController.php +++ /dev/null @@ -1,95 +0,0 @@ -getViewer(); - $dblob = $request->getURIData('dblob'); - - $parents = $this->loadParentFragments($dblob); - if ($parents === null) { - return new Aphront404Response(); - } - $current = nonempty(last($parents), null); - - $path = ''; - if ($current !== null) { - $path = $current->getPath(); - } - - $crumbs = $this->buildApplicationCrumbsWithPath($parents); - if ($this->hasApplicationCapability( - PhragmentCanCreateCapability::CAPABILITY)) { - $crumbs->addAction( - id(new PHUIListItemView()) - ->setName(pht('Create Fragment')) - ->setHref($this->getApplicationURI('/create/'.$path)) - ->setIcon('fa-plus-square')); - } - - $current_box = $this->createCurrentFragmentView($current, false); - - $list = id(new PHUIObjectItemListView()) - ->setUser($viewer); - - $fragments = null; - if ($current === null) { - // Find all root fragments. - $fragments = id(new PhragmentFragmentQuery()) - ->setViewer($this->getRequest()->getUser()) - ->needLatestVersion(true) - ->withDepths(array(1)) - ->execute(); - } else { - // Find all child fragments. - $fragments = id(new PhragmentFragmentQuery()) - ->setViewer($this->getRequest()->getUser()) - ->needLatestVersion(true) - ->withLeadingPath($current->getPath().'/') - ->withDepths(array($current->getDepth() + 1)) - ->execute(); - } - - foreach ($fragments as $fragment) { - $item = id(new PHUIObjectItemView()); - $item->setHeader($fragment->getName()); - $item->setHref($fragment->getURI()); - if (!$fragment->isDirectory()) { - $item->addAttribute(pht( - 'Last Updated %s', - phabricator_datetime( - $fragment->getLatestVersion()->getDateCreated(), - $viewer))); - $item->addAttribute(pht( - 'Latest Version %s', - $fragment->getLatestVersion()->getSequence())); - if ($fragment->isDeleted()) { - $item->setDisabled(true); - $item->addAttribute(pht('Deleted')); - } - } else { - $item->addAttribute(pht('Directory')); - } - $list->addItem($item); - } - - $title = pht('Browse Fragments'); - - $view = array( - $this->renderConfigurationWarningIfRequired(), - $current_box, - $list, - ); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($view); - - } - -} diff --git a/src/applications/phragment/controller/PhragmentController.php b/src/applications/phragment/controller/PhragmentController.php deleted file mode 100644 index f9df7429be..0000000000 --- a/src/applications/phragment/controller/PhragmentController.php +++ /dev/null @@ -1,232 +0,0 @@ -setViewer($this->getRequest()->getUser()) - ->needLatestVersion(true) - ->withPaths($combinations) - ->execute(); - foreach ($combinations as $combination) { - $found = false; - foreach ($results as $fragment) { - if ($fragment->getPath() === $combination) { - $fragments[] = $fragment; - $found = true; - break; - } - } - if (!$found) { - return null; - } - } - return $fragments; - } - - protected function buildApplicationCrumbsWithPath(array $fragments) { - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb('/', '/phragment/'); - foreach ($fragments as $parent) { - $crumbs->addTextCrumb( - $parent->getName(), - '/phragment/browse/'.$parent->getPath()); - } - return $crumbs; - } - - protected function createCurrentFragmentView($fragment, $is_history_view) { - if ($fragment === null) { - return null; - } - - $viewer = $this->getRequest()->getUser(); - - $snapshot_phids = array(); - $snapshots = id(new PhragmentSnapshotQuery()) - ->setViewer($viewer) - ->withPrimaryFragmentPHIDs(array($fragment->getPHID())) - ->execute(); - foreach ($snapshots as $snapshot) { - $snapshot_phids[] = $snapshot->getPHID(); - } - - $file = null; - $file_uri = null; - if (!$fragment->isDirectory()) { - $file = id(new PhabricatorFileQuery()) - ->setViewer($viewer) - ->withPHIDs(array($fragment->getLatestVersion()->getFilePHID())) - ->executeOne(); - if ($file !== null) { - $file_uri = $file->getDownloadURI(); - } - } - - $header = id(new PHUIHeaderView()) - ->setHeader($fragment->getName()) - ->setPolicyObject($fragment) - ->setUser($viewer); - - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $fragment, - PhabricatorPolicyCapability::CAN_EDIT); - - $zip_uri = $this->getApplicationURI('zip/'.$fragment->getPath()); - - $actions = id(new PhabricatorActionListView()) - ->setUser($viewer) - ->setObject($fragment); - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Download Fragment')) - ->setHref($this->isCorrectlyConfigured() ? $file_uri : null) - ->setDisabled($file === null || !$this->isCorrectlyConfigured()) - ->setIcon('fa-download')); - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Download Contents as ZIP')) - ->setHref($this->isCorrectlyConfigured() ? $zip_uri : null) - ->setDisabled(!$this->isCorrectlyConfigured()) - ->setIcon('fa-floppy-o')); - if (!$fragment->isDirectory()) { - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Update Fragment')) - ->setHref($this->getApplicationURI('update/'.$fragment->getPath())) - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit) - ->setIcon('fa-refresh')); - } else { - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Convert to File')) - ->setHref($this->getApplicationURI('update/'.$fragment->getPath())) - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit) - ->setIcon('fa-file-o')); - } - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Set Fragment Policies')) - ->setHref($this->getApplicationURI('policy/'.$fragment->getPath())) - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit) - ->setIcon('fa-asterisk')); - if ($is_history_view) { - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('View Child Fragments')) - ->setHref($this->getApplicationURI('browse/'.$fragment->getPath())) - ->setIcon('fa-search-plus')); - } else { - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('View History')) - ->setHref($this->getApplicationURI('history/'.$fragment->getPath())) - ->setIcon('fa-list')); - } - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Create Snapshot')) - ->setHref($this->getApplicationURI( - 'snapshot/create/'.$fragment->getPath())) - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit) - ->setIcon('fa-files-o')); - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Promote Snapshot to Here')) - ->setHref($this->getApplicationURI( - 'snapshot/promote/latest/'.$fragment->getPath())) - ->setWorkflow(true) - ->setDisabled(!$can_edit) - ->setIcon('fa-arrow-circle-up')); - - $properties = id(new PHUIPropertyListView()) - ->setUser($viewer) - ->setObject($fragment) - ->setActionList($actions); - - if (!$fragment->isDirectory()) { - if ($fragment->isDeleted()) { - $properties->addProperty( - pht('Type'), - pht('File (Deleted)')); - } else { - $properties->addProperty( - pht('Type'), - pht('File')); - } - $properties->addProperty( - pht('Latest Version'), - $viewer->renderHandle($fragment->getLatestVersionPHID())); - } else { - $properties->addProperty( - pht('Type'), - pht('Directory')); - } - - if (count($snapshot_phids) > 0) { - $properties->addProperty( - pht('Snapshots'), - $viewer->renderHandleList($snapshot_phids)); - } - - return id(new PHUIObjectBoxView()) - ->setHeader($header) - ->addPropertyList($properties); - } - - public function renderConfigurationWarningIfRequired() { - $alt = PhabricatorEnv::getEnvConfig('security.alternate-file-domain'); - if ($alt === null) { - return id(new PHUIInfoView()) - ->setTitle(pht( - '%s must be configured!', - 'security.alternate-file-domain')) - ->setSeverity(PHUIInfoView::SEVERITY_ERROR) - ->appendChild( - phutil_tag( - 'p', - array(), - pht( - "Because Phragment generates files (such as ZIP archives and ". - "patches) as they are requested, it requires that you configure ". - "the `%s` option. This option on it's own will also provide ". - "additional security when serving files across Phabricator.", - 'security.alternate-file-domain'))); - } - return null; - } - - /** - * We use this to disable the download links if the alternate domain is - * not configured correctly. Although the download links will mostly work - * for logged in users without an alternate domain, the behaviour is - * reasonably non-consistent and will deny public users, even if policies - * are configured otherwise (because the Files app does not support showing - * the info page to viewers who are not logged in). - */ - public function isCorrectlyConfigured() { - $alt = PhabricatorEnv::getEnvConfig('security.alternate-file-domain'); - return $alt !== null; - } - -} diff --git a/src/applications/phragment/controller/PhragmentCreateController.php b/src/applications/phragment/controller/PhragmentCreateController.php deleted file mode 100644 index 946d7cc332..0000000000 --- a/src/applications/phragment/controller/PhragmentCreateController.php +++ /dev/null @@ -1,135 +0,0 @@ -getViewer(); - $dblob = $request->getURIData('dblob'); - - $parent = null; - $parents = $this->loadParentFragments($dblob); - if ($parents === null) { - return new Aphront404Response(); - } - if (count($parents) !== 0) { - $parent = idx($parents, count($parents) - 1, null); - } - - $parent_path = ''; - if ($parent !== null) { - $parent_path = $parent->getPath(); - } - $parent_path = trim($parent_path, '/'); - - $fragment = id(new PhragmentFragment()); - - $error_view = null; - - if ($request->isFormPost()) { - $errors = array(); - - $v_name = $request->getStr('name'); - $v_fileid = $request->getInt('fileID'); - $v_viewpolicy = $request->getStr('viewPolicy'); - $v_editpolicy = $request->getStr('editPolicy'); - - if (strpos($v_name, '/') !== false) { - $errors[] = pht("The fragment name can not contain '/'."); - } - - $file = id(new PhabricatorFileQuery()) - ->setViewer($viewer) - ->withIDs(array($v_fileid)) - ->executeOne(); - if (!$file) { - $errors[] = pht("The specified file doesn't exist."); - } - - if (!count($errors)) { - $depth = 1; - if ($parent !== null) { - $depth = $parent->getDepth() + 1; - } - - PhragmentFragment::createFromFile( - $viewer, - $file, - trim($parent_path.'/'.$v_name, '/'), - $v_viewpolicy, - $v_editpolicy); - - return id(new AphrontRedirectResponse()) - ->setURI('/phragment/browse/'.trim($parent_path.'/'.$v_name, '/')); - } else { - $error_view = id(new PHUIInfoView()) - ->setErrors($errors) - ->setTitle(pht('Errors while creating fragment')); - } - } - - $policies = id(new PhabricatorPolicyQuery()) - ->setViewer($viewer) - ->setObject($fragment) - ->execute(); - - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Parent Path')) - ->setDisabled(true) - ->setValue('/'.trim($parent_path.'/', '/'))) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Name')) - ->setName('name')) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('File ID')) - ->setName('fileID')) - ->appendChild( - id(new AphrontFormPolicyControl()) - ->setUser($viewer) - ->setName('viewPolicy') - ->setPolicyObject($fragment) - ->setPolicies($policies) - ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)) - ->appendChild( - id(new AphrontFormPolicyControl()) - ->setUser($viewer) - ->setName('editPolicy') - ->setPolicyObject($fragment) - ->setPolicies($policies) - ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Create Fragment')) - ->addCancelButton( - $this->getApplicationURI('browse/'.$parent_path))); - - $crumbs = $this->buildApplicationCrumbsWithPath($parents); - $crumbs->addTextCrumb(pht('Create Fragment')); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Create Fragment')) - ->setForm($form); - - if ($error_view) { - $box->setInfoView($error_view); - } - - $title = pht('Create Fragments'); - - $view = array( - $this->renderConfigurationWarningIfRequired(), - $box, - ); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($view); - - } - -} diff --git a/src/applications/phragment/controller/PhragmentHistoryController.php b/src/applications/phragment/controller/PhragmentHistoryController.php deleted file mode 100644 index 4e16deb43d..0000000000 --- a/src/applications/phragment/controller/PhragmentHistoryController.php +++ /dev/null @@ -1,109 +0,0 @@ -getViewer(); - $dblob = $request->getURIData('dblob'); - - $parents = $this->loadParentFragments($dblob); - if ($parents === null) { - return new Aphront404Response(); - } - $current = idx($parents, count($parents) - 1, null); - - $path = $current->getPath(); - - $crumbs = $this->buildApplicationCrumbsWithPath($parents); - if ($this->hasApplicationCapability( - PhragmentCanCreateCapability::CAPABILITY)) { - $crumbs->addAction( - id(new PHUIListItemView()) - ->setName(pht('Create Fragment')) - ->setHref($this->getApplicationURI('/create/'.$path)) - ->setIcon('fa-plus-square')); - } - - $current_box = $this->createCurrentFragmentView($current, true); - - $versions = id(new PhragmentFragmentVersionQuery()) - ->setViewer($viewer) - ->withFragmentPHIDs(array($current->getPHID())) - ->execute(); - - $list = id(new PHUIObjectItemListView()) - ->setUser($viewer); - - $file_phids = mpull($versions, 'getFilePHID'); - $files = id(new PhabricatorFileQuery()) - ->setViewer($viewer) - ->withPHIDs($file_phids) - ->execute(); - $files = mpull($files, null, 'getPHID'); - - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $current, - PhabricatorPolicyCapability::CAN_EDIT); - - $first = true; - foreach ($versions as $version) { - $item = id(new PHUIObjectItemView()); - $item->setHeader(pht('Version %s', $version->getSequence())); - $item->setHref($version->getURI()); - $item->addAttribute(phabricator_datetime( - $version->getDateCreated(), - $viewer)); - - if ($version->getFilePHID() === null) { - $item->setDisabled(true); - $item->addAttribute(pht('Deletion')); - } - - if (!$first && $can_edit) { - $item->addAction(id(new PHUIListItemView()) - ->setIcon('fa-refresh') - ->setRenderNameAsTooltip(true) - ->setWorkflow(true) - ->setName(pht('Revert to Here')) - ->setHref($this->getApplicationURI( - 'revert/'.$version->getID().'/'.$current->getPath()))); - } - - $disabled = !isset($files[$version->getFilePHID()]); - $action = id(new PHUIListItemView()) - ->setIcon('fa-download') - ->setDisabled($disabled || !$this->isCorrectlyConfigured()) - ->setRenderNameAsTooltip(true) - ->setName(pht('Download')); - if (!$disabled && $this->isCorrectlyConfigured()) { - $action->setHref($files[$version->getFilePHID()] - ->getDownloadURI($version->getURI())); - } - $item->addAction($action); - - $list->addItem($item); - - $first = false; - } - - $title = pht('Fragment History'); - - $view = array( - $this->renderConfigurationWarningIfRequired(), - $current_box, - $list, - ); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($view); - - } - -} diff --git a/src/applications/phragment/controller/PhragmentPatchController.php b/src/applications/phragment/controller/PhragmentPatchController.php deleted file mode 100644 index eaa08bc29e..0000000000 --- a/src/applications/phragment/controller/PhragmentPatchController.php +++ /dev/null @@ -1,97 +0,0 @@ -getViewer(); - $aid = $request->getURIData('aid'); - $bid = $request->getURIData('bid'); - - // If "aid" is "x", then it means the user wants to generate - // a patch of an empty file to the version specified by "bid". - - $ids = array($aid, $bid); - if ($aid === 'x') { - $ids = array($bid); - } - - $versions = id(new PhragmentFragmentVersionQuery()) - ->setViewer($viewer) - ->withIDs($ids) - ->execute(); - - $version_a = null; - if ($aid !== 'x') { - $version_a = idx($versions, $aid, null); - if ($version_a === null) { - return new Aphront404Response(); - } - } - - $version_b = idx($versions, $bid, null); - if ($version_b === null) { - return new Aphront404Response(); - } - - $file_phids = array(); - if ($version_a !== null) { - $file_phids[] = $version_a->getFilePHID(); - } - $file_phids[] = $version_b->getFilePHID(); - - $files = id(new PhabricatorFileQuery()) - ->setViewer($viewer) - ->withPHIDs($file_phids) - ->execute(); - $files = mpull($files, null, 'getPHID'); - - $file_a = null; - if ($version_a != null) { - $file_a = idx($files, $version_a->getFilePHID(), null); - } - $file_b = idx($files, $version_b->getFilePHID(), null); - - $patch = PhragmentPatchUtil::calculatePatch($file_a, $file_b); - - if ($patch === null) { - // There are no differences between the two files, so we output - // an empty patch. - $patch = ''; - } - - $a_sequence = 'x'; - if ($version_a !== null) { - $a_sequence = $version_a->getSequence(); - } - - $name = - $version_b->getFragment()->getName().'.'. - $a_sequence.'.'. - $version_b->getSequence().'.patch'; - - $return = $version_b->getURI(); - if ($request->getExists('return')) { - $return = $request->getStr('return'); - } - - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); - $result = PhabricatorFile::newFromFileData( - $patch, - array( - 'name' => $name, - 'mime-type' => 'text/plain', - 'ttl.relative' => phutil_units('24 hours in seconds'), - )); - - $result->attachToObject($version_b->getFragmentPHID()); - unset($unguarded); - - return id(new AphrontRedirectResponse()) - ->setURI($result->getDownloadURI($return)); - } - -} diff --git a/src/applications/phragment/controller/PhragmentPolicyController.php b/src/applications/phragment/controller/PhragmentPolicyController.php deleted file mode 100644 index edcde80990..0000000000 --- a/src/applications/phragment/controller/PhragmentPolicyController.php +++ /dev/null @@ -1,106 +0,0 @@ -getViewer(); - $dblob = $request->getURIData('dblob'); - - $parents = $this->loadParentFragments($dblob); - if ($parents === null) { - return new Aphront404Response(); - } - $fragment = idx($parents, count($parents) - 1, null); - - $error_view = null; - - if ($request->isFormPost()) { - $errors = array(); - - $v_view_policy = $request->getStr('viewPolicy'); - $v_edit_policy = $request->getStr('editPolicy'); - $v_replace_children = $request->getBool('replacePoliciesOnChildren'); - - $fragment->setViewPolicy($v_view_policy); - $fragment->setEditPolicy($v_edit_policy); - - $fragment->save(); - - if ($v_replace_children) { - // If you can edit a fragment, you can forcibly set the policies - // on child fragments, regardless of whether you can see them or not. - $children = id(new PhragmentFragmentQuery()) - ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withLeadingPath($fragment->getPath().'/') - ->execute(); - $children_phids = mpull($children, 'getPHID'); - - $fragment->openTransaction(); - foreach ($children as $child) { - $child->setViewPolicy($v_view_policy); - $child->setEditPolicy($v_edit_policy); - $child->save(); - } - $fragment->saveTransaction(); - } - - return id(new AphrontRedirectResponse()) - ->setURI('/phragment/browse/'.$fragment->getPath()); - } - - $policies = id(new PhabricatorPolicyQuery()) - ->setViewer($viewer) - ->setObject($fragment) - ->execute(); - - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->appendChild( - id(new AphrontFormPolicyControl()) - ->setName('viewPolicy') - ->setPolicyObject($fragment) - ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) - ->setPolicies($policies)) - ->appendChild( - id(new AphrontFormPolicyControl()) - ->setName('editPolicy') - ->setPolicyObject($fragment) - ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) - ->setPolicies($policies)) - ->appendChild( - id(new AphrontFormCheckboxControl()) - ->addCheckbox( - 'replacePoliciesOnChildren', - 'true', - pht( - 'Replace policies on child fragments with '. - 'the policies above.'))) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Save Fragment Policies')) - ->addCancelButton( - $this->getApplicationURI('browse/'.$fragment->getPath()))); - - $crumbs = $this->buildApplicationCrumbsWithPath($parents); - $crumbs->addTextCrumb(pht('Edit Fragment Policies')); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Edit Fragment Policies: %s', $fragment->getPath())) - ->setValidationException(null) - ->setForm($form); - - $title = pht('Edit Fragment Policies'); - - $view = array( - $this->renderConfigurationWarningIfRequired(), - $box, - ); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($view); - - } - -} diff --git a/src/applications/phragment/controller/PhragmentRevertController.php b/src/applications/phragment/controller/PhragmentRevertController.php deleted file mode 100644 index b9aa050327..0000000000 --- a/src/applications/phragment/controller/PhragmentRevertController.php +++ /dev/null @@ -1,78 +0,0 @@ -getViewer(); - $id = $request->getURIData('id'); - $dblob = $request->getURIData('dblob'); - - $fragment = id(new PhragmentFragmentQuery()) - ->setViewer($viewer) - ->withPaths(array($dblob)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); - if ($fragment === null) { - return new Aphront404Response(); - } - - $version = id(new PhragmentFragmentVersionQuery()) - ->setViewer($viewer) - ->withFragmentPHIDs(array($fragment->getPHID())) - ->withIDs(array($id)) - ->executeOne(); - if ($version === null) { - return new Aphront404Response(); - } - - if ($request->isDialogFormPost()) { - $file_phid = $version->getFilePHID(); - - $file = null; - if ($file_phid !== null) { - $file = id(new PhabricatorFileQuery()) - ->setViewer($viewer) - ->withPHIDs(array($file_phid)) - ->executeOne(); - if ($file === null) { - throw new Exception( - pht('The file associated with this version was not found.')); - } - } - - if ($file === null) { - $fragment->deleteFile($viewer); - } else { - $fragment->updateFromFile($viewer, $file); - } - - return id(new AphrontRedirectResponse()) - ->setURI($this->getApplicationURI('/history/'.$dblob)); - } - - return $this->createDialog($fragment, $version); - } - - public function createDialog( - PhragmentFragment $fragment, - PhragmentFragmentVersion $version) { - - $viewer = $this->getViewer(); - - $dialog = id(new AphrontDialogView()) - ->setTitle(pht('Really revert this fragment?')) - ->setUser($this->getViewer()) - ->addSubmitButton(pht('Revert')) - ->addCancelButton(pht('Cancel')) - ->appendParagraph(pht( - 'Reverting this fragment to version %d will create a new version of '. - 'the fragment. It will not delete any version history.', - $version->getSequence())); - return id(new AphrontDialogResponse())->setDialog($dialog); - } - -} diff --git a/src/applications/phragment/controller/PhragmentSnapshotCreateController.php b/src/applications/phragment/controller/PhragmentSnapshotCreateController.php deleted file mode 100644 index c32be32cd7..0000000000 --- a/src/applications/phragment/controller/PhragmentSnapshotCreateController.php +++ /dev/null @@ -1,170 +0,0 @@ -getViewer(); - $dblob = $request->getURIData('dblob'); - - $parents = $this->loadParentFragments($dblob); - if ($parents === null) { - return new Aphront404Response(); - } - $fragment = nonempty(last($parents), null); - if ($fragment === null) { - return new Aphront404Response(); - } - - PhabricatorPolicyFilter::requireCapability( - $viewer, - $fragment, - PhabricatorPolicyCapability::CAN_EDIT); - - $children = id(new PhragmentFragmentQuery()) - ->setViewer($viewer) - ->needLatestVersion(true) - ->withLeadingPath($fragment->getPath().'/') - ->execute(); - - $errors = array(); - if ($request->isFormPost()) { - - $v_name = $request->getStr('name'); - if (strlen($v_name) === 0) { - $errors[] = pht('You must specify a name.'); - } - if (strpos($v_name, '/') !== false) { - $errors[] = pht('Snapshot names can not contain "/".'); - } - - if (!count($errors)) { - $snapshot = null; - - try { - // Create the snapshot. - $snapshot = id(new PhragmentSnapshot()) - ->setPrimaryFragmentPHID($fragment->getPHID()) - ->setName($v_name) - ->save(); - } catch (AphrontDuplicateKeyQueryException $e) { - $errors[] = pht('A snapshot with this name already exists.'); - } - - if (!count($errors)) { - // Add the primary fragment. - id(new PhragmentSnapshotChild()) - ->setSnapshotPHID($snapshot->getPHID()) - ->setFragmentPHID($fragment->getPHID()) - ->setFragmentVersionPHID($fragment->getLatestVersionPHID()) - ->save(); - - // Add all of the child fragments. - foreach ($children as $child) { - id(new PhragmentSnapshotChild()) - ->setSnapshotPHID($snapshot->getPHID()) - ->setFragmentPHID($child->getPHID()) - ->setFragmentVersionPHID($child->getLatestVersionPHID()) - ->save(); - } - - return id(new AphrontRedirectResponse()) - ->setURI('/phragment/snapshot/view/'.$snapshot->getID()); - } - } - } - - $fragment_sequence = '-'; - if ($fragment->getLatestVersion() !== null) { - $fragment_sequence = $fragment->getLatestVersion()->getSequence(); - } - - $rows = array(); - $rows[] = phutil_tag( - 'tr', - array(), - array( - phutil_tag('th', array(), pht('Fragment')), - phutil_tag('th', array(), pht('Version')), - )); - $rows[] = phutil_tag( - 'tr', - array(), - array( - phutil_tag('td', array(), $fragment->getPath()), - phutil_tag('td', array(), $fragment_sequence), - )); - foreach ($children as $child) { - $sequence = '-'; - if ($child->getLatestVersion() !== null) { - $sequence = $child->getLatestVersion()->getSequence(); - } - $rows[] = phutil_tag( - 'tr', - array(), - array( - phutil_tag('td', array(), $child->getPath()), - phutil_tag('td', array(), $sequence), - )); - } - - $table = phutil_tag( - 'table', - array('class' => 'remarkup-table'), - $rows); - - $container = phutil_tag( - 'div', - array('class' => 'phabricator-remarkup'), - array( - phutil_tag( - 'p', - array(), - pht( - 'The snapshot will contain the following fragments at '. - 'the specified versions: ')), - $table, - )); - - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Fragment Path')) - ->setDisabled(true) - ->setValue('/'.$fragment->getPath())) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Snapshot Name')) - ->setName('name')) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Create Snapshot')) - ->addCancelButton( - $this->getApplicationURI('browse/'.$fragment->getPath()))) - ->appendChild( - id(new PHUIFormDividerControl())) - ->appendInstructions($container); - - $crumbs = $this->buildApplicationCrumbsWithPath($parents); - $crumbs->addTextCrumb(pht('Create Snapshot')); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Create Snapshot of %s', $fragment->getName())) - ->setFormErrors($errors) - ->setForm($form); - - $title = pht('Create Snapshot'); - - $view = array( - $this->renderConfigurationWarningIfRequired(), - $box, - ); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($view); - - } - -} diff --git a/src/applications/phragment/controller/PhragmentSnapshotDeleteController.php b/src/applications/phragment/controller/PhragmentSnapshotDeleteController.php deleted file mode 100644 index 8f112585d0..0000000000 --- a/src/applications/phragment/controller/PhragmentSnapshotDeleteController.php +++ /dev/null @@ -1,47 +0,0 @@ -getViewer(); - $id = $request->getURIData('id'); - - $snapshot = id(new PhragmentSnapshotQuery()) - ->setViewer($viewer) - ->requireCapabilities(array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($id)) - ->executeOne(); - if ($snapshot === null) { - return new Aphront404Response(); - } - - if ($request->isDialogFormPost()) { - $fragment_uri = $snapshot->getPrimaryFragment()->getURI(); - - $snapshot->delete(); - - return id(new AphrontRedirectResponse()) - ->setURI($fragment_uri); - } - - return $this->createDialog(); - } - - public function createDialog() { - $viewer = $this->getViewer(); - - $dialog = id(new AphrontDialogView()) - ->setTitle(pht('Really delete this snapshot?')) - ->setUser($this->getViewer()) - ->addSubmitButton(pht('Delete')) - ->addCancelButton(pht('Cancel')) - ->appendParagraph(pht( - 'Deleting this snapshot is a permanent operation. You can not '. - 'recover the state of the snapshot.')); - return id(new AphrontDialogResponse())->setDialog($dialog); - } - -} diff --git a/src/applications/phragment/controller/PhragmentSnapshotPromoteController.php b/src/applications/phragment/controller/PhragmentSnapshotPromoteController.php deleted file mode 100644 index 981d139742..0000000000 --- a/src/applications/phragment/controller/PhragmentSnapshotPromoteController.php +++ /dev/null @@ -1,188 +0,0 @@ -getViewer(); - $id = $request->getURIData('id'); - $dblob = $request->getURIData('dblob'); - - // When the user is promoting a snapshot to the latest version, the - // identifier is a fragment path. - if ($dblob !== null) { - $this->targetFragment = id(new PhragmentFragmentQuery()) - ->setViewer($viewer) - ->requireCapabilities(array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withPaths(array($dblob)) - ->executeOne(); - if ($this->targetFragment === null) { - return new Aphront404Response(); - } - - $this->snapshots = id(new PhragmentSnapshotQuery()) - ->setViewer($viewer) - ->withPrimaryFragmentPHIDs(array($this->targetFragment->getPHID())) - ->execute(); - } - - // When the user is promoting a snapshot to another snapshot, the - // identifier is another snapshot ID. - if ($id !== null) { - $this->targetSnapshot = id(new PhragmentSnapshotQuery()) - ->setViewer($viewer) - ->requireCapabilities(array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($id)) - ->executeOne(); - if ($this->targetSnapshot === null) { - return new Aphront404Response(); - } - - $this->snapshots = id(new PhragmentSnapshotQuery()) - ->setViewer($viewer) - ->withPrimaryFragmentPHIDs(array( - $this->targetSnapshot->getPrimaryFragmentPHID(), - )) - ->execute(); - } - - // If there's no identifier, just 404. - if ($this->snapshots === null) { - return new Aphront404Response(); - } - - // Work out what options the user has. - $this->options = mpull( - $this->snapshots, - 'getName', - 'getID'); - if ($id !== null) { - unset($this->options[$id]); - } - - // If there's no options, show a dialog telling the - // user there are no snapshots to promote. - if (count($this->options) === 0) { - return id(new AphrontDialogResponse())->setDialog( - id(new AphrontDialogView()) - ->setTitle(pht('No snapshots to promote')) - ->appendParagraph(pht( - 'There are no snapshots available to promote.')) - ->setUser($this->getViewer()) - ->addCancelButton(pht('Cancel'))); - } - - // Handle snapshot promotion. - if ($request->isDialogFormPost()) { - $snapshot = id(new PhragmentSnapshotQuery()) - ->setViewer($viewer) - ->withIDs(array($request->getStr('snapshot'))) - ->executeOne(); - if ($snapshot === null) { - return new Aphront404Response(); - } - - $snapshot->openTransaction(); - // Delete all existing child entries. - $children = id(new PhragmentSnapshotChildQuery()) - ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withSnapshotPHIDs(array($snapshot->getPHID())) - ->execute(); - foreach ($children as $child) { - $child->delete(); - } - - if ($id === null) { - // The user is promoting the snapshot to the latest version. - $children = id(new PhragmentFragmentQuery()) - ->setViewer($viewer) - ->needLatestVersion(true) - ->withLeadingPath($this->targetFragment->getPath().'/') - ->execute(); - - // Add the primary fragment. - id(new PhragmentSnapshotChild()) - ->setSnapshotPHID($snapshot->getPHID()) - ->setFragmentPHID($this->targetFragment->getPHID()) - ->setFragmentVersionPHID( - $this->targetFragment->getLatestVersionPHID()) - ->save(); - - // Add all of the child fragments. - foreach ($children as $child) { - id(new PhragmentSnapshotChild()) - ->setSnapshotPHID($snapshot->getPHID()) - ->setFragmentPHID($child->getPHID()) - ->setFragmentVersionPHID($child->getLatestVersionPHID()) - ->save(); - } - } else { - // The user is promoting the snapshot to another snapshot. We just - // copy the other snapshot's child entries and change the snapshot - // PHID to make it identical. - $children = id(new PhragmentSnapshotChildQuery()) - ->setViewer($viewer) - ->withSnapshotPHIDs(array($this->targetSnapshot->getPHID())) - ->execute(); - foreach ($children as $child) { - id(new PhragmentSnapshotChild()) - ->setSnapshotPHID($snapshot->getPHID()) - ->setFragmentPHID($child->getFragmentPHID()) - ->setFragmentVersionPHID($child->getFragmentVersionPHID()) - ->save(); - } - } - $snapshot->saveTransaction(); - - if ($id === null) { - return id(new AphrontRedirectResponse()) - ->setURI($this->targetFragment->getURI()); - } else { - return id(new AphrontRedirectResponse()) - ->setURI($this->targetSnapshot->getURI()); - } - } - - return $this->createDialog($id); - } - - public function createDialog($id) { - $viewer = $this->getViewer(); - - $dialog = id(new AphrontDialogView()) - ->setTitle(pht('Promote which snapshot?')) - ->setUser($this->getViewer()) - ->addSubmitButton(pht('Promote')) - ->addCancelButton(pht('Cancel')); - - if ($id === null) { - // The user is promoting a snapshot to the latest version. - $dialog->appendParagraph(pht( - 'Select the snapshot you want to promote to the latest version:')); - } else { - // The user is promoting a snapshot to another snapshot. - $dialog->appendParagraph(pht( - "Select the snapshot you want to promote to '%s':", - $this->targetSnapshot->getName())); - } - - $dialog->appendChild( - id(new AphrontFormSelectControl()) - ->setUser($viewer) - ->setName('snapshot') - ->setOptions($this->options)); - - return id(new AphrontDialogResponse())->setDialog($dialog); - } - -} diff --git a/src/applications/phragment/controller/PhragmentSnapshotViewController.php b/src/applications/phragment/controller/PhragmentSnapshotViewController.php deleted file mode 100644 index 052b5036fb..0000000000 --- a/src/applications/phragment/controller/PhragmentSnapshotViewController.php +++ /dev/null @@ -1,145 +0,0 @@ -getViewer(); - $id = $request->getURIData('id'); - - $snapshot = id(new PhragmentSnapshotQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->executeOne(); - if ($snapshot === null) { - return new Aphront404Response(); - } - - $box = $this->createSnapshotView($snapshot); - - $fragment = id(new PhragmentFragmentQuery()) - ->setViewer($viewer) - ->withPHIDs(array($snapshot->getPrimaryFragmentPHID())) - ->executeOne(); - if ($fragment === null) { - return new Aphront404Response(); - } - - $parents = $this->loadParentFragments($fragment->getPath()); - if ($parents === null) { - return new Aphront404Response(); - } - - $crumbs = $this->buildApplicationCrumbsWithPath($parents); - $crumbs->addTextCrumb(pht('"%s" Snapshot', $snapshot->getName())); - - $children = id(new PhragmentSnapshotChildQuery()) - ->setViewer($viewer) - ->needFragments(true) - ->needFragmentVersions(true) - ->withSnapshotPHIDs(array($snapshot->getPHID())) - ->execute(); - - $list = id(new PHUIObjectItemListView()) - ->setUser($viewer); - - foreach ($children as $child) { - $item = id(new PHUIObjectItemView()) - ->setHeader($child->getFragment()->getPath()); - - if ($child->getFragmentVersion() !== null) { - $item - ->setHref($child->getFragmentVersion()->getURI()) - ->addAttribute(pht( - 'Version %s', - $child->getFragmentVersion()->getSequence())); - } else { - $item - ->setHref($child->getFragment()->getURI()) - ->addAttribute(pht('Directory')); - } - - $list->addItem($item); - } - - $title = pht('View Snapshot'); - - $view = array( - $this->renderConfigurationWarningIfRequired(), - $box, - $list, - ); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($view); - } - - protected function createSnapshotView($snapshot) { - if ($snapshot === null) { - return null; - } - - $viewer = $this->getRequest()->getUser(); - - $header = id(new PHUIHeaderView()) - ->setHeader(pht('"%s" Snapshot', $snapshot->getName())) - ->setPolicyObject($snapshot) - ->setUser($viewer); - - $zip_uri = $this->getApplicationURI( - 'zip@'.$snapshot->getName(). - '/'.$snapshot->getPrimaryFragment()->getPath()); - - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $snapshot, - PhabricatorPolicyCapability::CAN_EDIT); - - $actions = id(new PhabricatorActionListView()) - ->setUser($viewer) - ->setObject($snapshot); - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Download Snapshot as ZIP')) - ->setHref($this->isCorrectlyConfigured() ? $zip_uri : null) - ->setDisabled(!$this->isCorrectlyConfigured()) - ->setIcon('fa-floppy-o')); - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Delete Snapshot')) - ->setHref($this->getApplicationURI( - 'snapshot/delete/'.$snapshot->getID().'/')) - ->setDisabled(!$can_edit) - ->setWorkflow(true) - ->setIcon('fa-times')); - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Promote Another Snapshot to Here')) - ->setHref($this->getApplicationURI( - 'snapshot/promote/'.$snapshot->getID().'/')) - ->setDisabled(!$can_edit) - ->setWorkflow(true) - ->setIcon('fa-arrow-up')); - - $properties = id(new PHUIPropertyListView()) - ->setUser($viewer) - ->setObject($snapshot) - ->setActionList($actions); - - $properties->addProperty( - pht('Name'), - $snapshot->getName()); - $properties->addProperty( - pht('Fragment'), - $viewer->renderHandle($snapshot->getPrimaryFragmentPHID())); - - return id(new PHUIObjectBoxView()) - ->setHeader($header) - ->addPropertyList($properties); - } -} diff --git a/src/applications/phragment/controller/PhragmentUpdateController.php b/src/applications/phragment/controller/PhragmentUpdateController.php deleted file mode 100644 index 7fb91ccd4e..0000000000 --- a/src/applications/phragment/controller/PhragmentUpdateController.php +++ /dev/null @@ -1,80 +0,0 @@ -getViewer(); - $dblob = $request->getURIData('dblob'); - - $parents = $this->loadParentFragments($dblob); - if ($parents === null) { - return new Aphront404Response(); - } - $fragment = idx($parents, count($parents) - 1, null); - - $error_view = null; - - if ($request->isFormPost()) { - $errors = array(); - - $v_fileid = $request->getInt('fileID'); - - $file = id(new PhabricatorFile())->load($v_fileid); - if ($file === null) { - $errors[] = pht('The specified file doesn\'t exist.'); - } - - if (!count($errors)) { - // If the file is a ZIP archive (has application/zip mimetype) - // then we extract the zip and apply versions for each of the - // individual fragments, creating and deleting files as needed. - if ($file->getMimeType() === 'application/zip') { - $fragment->updateFromZIP($viewer, $file); - } else { - $fragment->updateFromFile($viewer, $file); - } - - return id(new AphrontRedirectResponse()) - ->setURI('/phragment/browse/'.$fragment->getPath()); - } else { - $error_view = id(new PHUIInfoView()) - ->setErrors($errors) - ->setTitle(pht('Errors while updating fragment')); - } - } - - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('File ID')) - ->setName('fileID')) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Update Fragment')) - ->addCancelButton( - $this->getApplicationURI('browse/'.$fragment->getPath()))); - - $crumbs = $this->buildApplicationCrumbsWithPath($parents); - $crumbs->addTextCrumb(pht('Update Fragment')); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Update Fragment: %s', $fragment->getPath())) - ->setValidationException(null) - ->setForm($form); - - $title = pht('Update Fragment'); - - $view = array( - $this->renderConfigurationWarningIfRequired(), - $box, - ); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($view); - - } - -} diff --git a/src/applications/phragment/controller/PhragmentVersionController.php b/src/applications/phragment/controller/PhragmentVersionController.php deleted file mode 100644 index 2b1ae2bdf0..0000000000 --- a/src/applications/phragment/controller/PhragmentVersionController.php +++ /dev/null @@ -1,125 +0,0 @@ -getViewer(); - $id = $request->getURIData('id'); - - $version = id(new PhragmentFragmentVersionQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->executeOne(); - if ($version === null) { - return new Aphront404Response(); - } - - $parents = $this->loadParentFragments($version->getFragment()->getPath()); - if ($parents === null) { - return new Aphront404Response(); - } - $current = idx($parents, count($parents) - 1, null); - - $crumbs = $this->buildApplicationCrumbsWithPath($parents); - $crumbs->addTextCrumb(pht('View Version %d', $version->getSequence())); - - $file = id(new PhabricatorFileQuery()) - ->setViewer($viewer) - ->withPHIDs(array($version->getFilePHID())) - ->executeOne(); - if ($file !== null) { - $file_uri = $file->getDownloadURI(); - } - - $header = id(new PHUIHeaderView()) - ->setHeader(pht( - '%s at version %d', - $version->getFragment()->getName(), - $version->getSequence())) - ->setPolicyObject($version) - ->setUser($viewer); - - $actions = id(new PhabricatorActionListView()) - ->setUser($viewer) - ->setObject($version); - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Download Version')) - ->setDisabled($file === null || !$this->isCorrectlyConfigured()) - ->setHref($this->isCorrectlyConfigured() ? $file_uri : null) - ->setIcon('fa-download')); - - $properties = id(new PHUIPropertyListView()) - ->setUser($viewer) - ->setObject($version) - ->setActionList($actions); - $properties->addProperty( - pht('File'), - $viewer->renderHandle($version->getFilePHID())); - - $box = id(new PHUIObjectBoxView()) - ->setHeader($header) - ->addPropertyList($properties); - - $title = pht('View Version'); - - $view = array( - $this->renderConfigurationWarningIfRequired(), - $box, - $this->renderPreviousVersionList($version), - ); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($view); - } - - private function renderPreviousVersionList( - PhragmentFragmentVersion $version) { - $viewer = $this->getViewer(); - - $previous_versions = id(new PhragmentFragmentVersionQuery()) - ->setViewer($viewer) - ->withFragmentPHIDs(array($version->getFragmentPHID())) - ->withSequenceBefore($version->getSequence()) - ->execute(); - - $list = id(new PHUIObjectItemListView()) - ->setUser($viewer); - - foreach ($previous_versions as $previous_version) { - $item = id(new PHUIObjectItemView()); - $item->setHeader(pht('Version %s', $previous_version->getSequence())); - $item->setHref($previous_version->getURI()); - $item->addAttribute(phabricator_datetime( - $previous_version->getDateCreated(), - $viewer)); - $patch_uri = $this->getApplicationURI( - 'patch/'.$previous_version->getID().'/'.$version->getID()); - $item->addAction(id(new PHUIListItemView()) - ->setIcon('fa-file-o') - ->setName(pht('Get Patch')) - ->setHref($this->isCorrectlyConfigured() ? $patch_uri : null) - ->setDisabled(!$this->isCorrectlyConfigured())); - $list->addItem($item); - } - - $item = id(new PHUIObjectItemView()); - $item->setHeader(pht('Prior to Version 0')); - $item->addAttribute(pht('Prior to any content (empty file)')); - $item->addAction(id(new PHUIListItemView()) - ->setIcon('fa-file-o') - ->setName(pht('Get Patch')) - ->setHref($this->getApplicationURI( - 'patch/x/'.$version->getID()))); - $list->addItem($item); - - return $list; - } - -} diff --git a/src/applications/phragment/controller/PhragmentZIPController.php b/src/applications/phragment/controller/PhragmentZIPController.php deleted file mode 100644 index 167a67857f..0000000000 --- a/src/applications/phragment/controller/PhragmentZIPController.php +++ /dev/null @@ -1,154 +0,0 @@ -getViewer(); - $dblob = $request->getURIData('dblob'); - $snapshot = $request->getURIData('snapshot'); - - $parents = $this->loadParentFragments($dblob); - if ($parents === null) { - return new Aphront404Response(); - } - $fragment = idx($parents, count($parents) - 1, null); - - if ($snapshot !== null) { - $snapshot = id(new PhragmentSnapshotQuery()) - ->setViewer($viewer) - ->withPrimaryFragmentPHIDs(array($fragment->getPHID())) - ->withNames(array($snapshot)) - ->executeOne(); - if ($snapshot === null) { - return new Aphront404Response(); - } - - $cache = id(new PhragmentSnapshotChildQuery()) - ->setViewer($viewer) - ->needFragmentVersions(true) - ->withSnapshotPHIDs(array($snapshot->getPHID())) - ->execute(); - $this->snapshotCache = mpull( - $cache, - 'getFragmentVersion', - 'getFragmentPHID'); - } - - $temp = new TempFile(); - - $zip = null; - try { - $zip = new ZipArchive(); - } catch (Exception $e) { - $dialog = new AphrontDialogView(); - $dialog->setUser($viewer); - - $inst = pht( - 'This system does not have the ZIP PHP extension installed. This '. - 'is required to download ZIPs from Phragment.'); - - $dialog->setTitle(pht('ZIP Extension Not Installed')); - $dialog->appendParagraph($inst); - - $dialog->addCancelButton('/phragment/browse/'.$dblob); - return id(new AphrontDialogResponse())->setDialog($dialog); - } - - if (!$zip->open((string)$temp, ZipArchive::CREATE)) { - throw new Exception(pht('Unable to create ZIP archive!')); - } - - $mappings = $this->getFragmentMappings( - $fragment, $fragment->getPath(), $snapshot); - - $phids = array(); - foreach ($mappings as $path => $file_phid) { - $phids[] = $file_phid; - } - - $files = id(new PhabricatorFileQuery()) - ->setViewer($viewer) - ->withPHIDs($phids) - ->execute(); - $files = mpull($files, null, 'getPHID'); - foreach ($mappings as $path => $file_phid) { - if (!isset($files[$file_phid])) { - // The path is most likely pointing to a deleted fragment, which - // hence no longer has a file associated with it. - unset($mappings[$path]); - continue; - } - $mappings[$path] = $files[$file_phid]; - } - - foreach ($mappings as $path => $file) { - if ($file !== null) { - $zip->addFromString($path, $file->loadFileData()); - } - } - $zip->close(); - - $zip_name = $fragment->getName(); - if (substr($zip_name, -4) !== '.zip') { - $zip_name .= '.zip'; - } - - $data = Filesystem::readFile((string)$temp); - - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); - $file = PhabricatorFile::newFromFileData( - $data, - array( - 'name' => $zip_name, - 'ttl.relative' => phutil_units('24 hours in seconds'), - )); - - $file->attachToObject($fragment->getPHID()); - unset($unguarded); - - $return = $fragment->getURI(); - if ($request->getExists('return')) { - $return = $request->getStr('return'); - } - - return id(new AphrontRedirectResponse()) - ->setIsExternal(true) - ->setURI($file->getDownloadURI($return)); - } - - /** - * Returns a list of mappings like array('some/path.txt' => 'file PHID'); - */ - private function getFragmentMappings( - PhragmentFragment $current, - $base_path, - $snapshot) { - $mappings = $current->getFragmentMappings( - $this->getRequest()->getUser(), - $base_path); - - $result = array(); - foreach ($mappings as $path => $fragment) { - $version = $this->getVersion($fragment, $snapshot); - if ($version !== null) { - $result[$path] = $version->getFilePHID(); - } - } - return $result; - } - - private function getVersion($fragment, $snapshot) { - if ($snapshot === null) { - return $fragment->getLatestVersion(); - } else { - return idx($this->snapshotCache, $fragment->getPHID(), null); - } - } - -} diff --git a/src/applications/phragment/phid/PhragmentFragmentPHIDType.php b/src/applications/phragment/phid/PhragmentFragmentPHIDType.php deleted file mode 100644 index 2947bca98e..0000000000 --- a/src/applications/phragment/phid/PhragmentFragmentPHIDType.php +++ /dev/null @@ -1,44 +0,0 @@ -withPHIDs($phids); - } - - public function loadHandles( - PhabricatorHandleQuery $query, - array $handles, - array $objects) { - - $viewer = $query->getViewer(); - foreach ($handles as $phid => $handle) { - $fragment = $objects[$phid]; - - $handle->setName(pht( - 'Fragment %s: %s', - $fragment->getID(), - $fragment->getName())); - $handle->setURI($fragment->getURI()); - } - } - -} diff --git a/src/applications/phragment/phid/PhragmentFragmentVersionPHIDType.php b/src/applications/phragment/phid/PhragmentFragmentVersionPHIDType.php deleted file mode 100644 index 2fa00b15ce..0000000000 --- a/src/applications/phragment/phid/PhragmentFragmentVersionPHIDType.php +++ /dev/null @@ -1,44 +0,0 @@ -withPHIDs($phids); - } - - public function loadHandles( - PhabricatorHandleQuery $query, - array $handles, - array $objects) { - - $viewer = $query->getViewer(); - foreach ($handles as $phid => $handle) { - $version = $objects[$phid]; - - $handle->setName(pht( - 'Fragment Version %d: %s', - $version->getSequence(), - $version->getFragment()->getName())); - $handle->setURI($version->getURI()); - } - } - -} diff --git a/src/applications/phragment/phid/PhragmentSnapshotPHIDType.php b/src/applications/phragment/phid/PhragmentSnapshotPHIDType.php deleted file mode 100644 index d97026a601..0000000000 --- a/src/applications/phragment/phid/PhragmentSnapshotPHIDType.php +++ /dev/null @@ -1,43 +0,0 @@ -withPHIDs($phids); - } - - public function loadHandles( - PhabricatorHandleQuery $query, - array $handles, - array $objects) { - - $viewer = $query->getViewer(); - foreach ($handles as $phid => $handle) { - $snapshot = $objects[$phid]; - - $handle->setName(pht( - 'Snapshot: %s', - $snapshot->getName())); - $handle->setURI($snapshot->getURI()); - } - } - -} diff --git a/src/applications/phragment/query/PhragmentFragmentQuery.php b/src/applications/phragment/query/PhragmentFragmentQuery.php deleted file mode 100644 index 56444217db..0000000000 --- a/src/applications/phragment/query/PhragmentFragmentQuery.php +++ /dev/null @@ -1,130 +0,0 @@ -ids = $ids; - return $this; - } - - public function withPHIDs(array $phids) { - $this->phids = $phids; - return $this; - } - - public function withPaths(array $paths) { - $this->paths = $paths; - return $this; - } - - public function withLeadingPath($path) { - $this->leadingPath = $path; - return $this; - } - - public function withDepths($depths) { - $this->depths = $depths; - return $this; - } - - public function needLatestVersion($need_latest_version) { - $this->needLatestVersion = $need_latest_version; - return $this; - } - - protected function loadPage() { - $table = new PhragmentFragment(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($data); - } - - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); - - if ($this->ids) { - $where[] = qsprintf( - $conn, - 'id IN (%Ld)', - $this->ids); - } - - if ($this->phids) { - $where[] = qsprintf( - $conn, - 'phid IN (%Ls)', - $this->phids); - } - - if ($this->paths) { - $where[] = qsprintf( - $conn, - 'path IN (%Ls)', - $this->paths); - } - - if ($this->leadingPath) { - $where[] = qsprintf( - $conn, - 'path LIKE %>', - $this->leadingPath); - } - - if ($this->depths) { - $where[] = qsprintf( - $conn, - 'depth IN (%Ld)', - $this->depths); - } - - $where[] = $this->buildPagingClause($conn); - - return $this->formatWhereClause($conn, $where); - } - - protected function didFilterPage(array $page) { - if ($this->needLatestVersion) { - $versions = array(); - - $version_phids = array_filter(mpull($page, 'getLatestVersionPHID')); - if ($version_phids) { - $versions = id(new PhabricatorObjectQuery()) - ->setViewer($this->getViewer()) - ->withPHIDs($version_phids) - ->setParentQuery($this) - ->execute(); - $versions = mpull($versions, null, 'getPHID'); - } - - foreach ($page as $key => $fragment) { - $version_phid = $fragment->getLatestVersionPHID(); - if (empty($versions[$version_phid])) { - continue; - } - $fragment->attachLatestVersion($versions[$version_phid]); - } - } - - return $page; - } - - public function getQueryApplicationClass() { - return 'PhabricatorPhragmentApplication'; - } -} diff --git a/src/applications/phragment/query/PhragmentFragmentVersionQuery.php b/src/applications/phragment/query/PhragmentFragmentVersionQuery.php deleted file mode 100644 index e95c3260a8..0000000000 --- a/src/applications/phragment/query/PhragmentFragmentVersionQuery.php +++ /dev/null @@ -1,123 +0,0 @@ -ids = $ids; - return $this; - } - - public function withPHIDs(array $phids) { - $this->phids = $phids; - return $this; - } - - public function withFragmentPHIDs(array $fragment_phids) { - $this->fragmentPHIDs = $fragment_phids; - return $this; - } - - public function withSequences(array $sequences) { - $this->sequences = $sequences; - return $this; - } - - public function withSequenceBefore($current) { - $this->sequenceBefore = $current; - return $this; - } - - protected function loadPage() { - $table = new PhragmentFragmentVersion(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($data); - } - - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); - - if ($this->ids) { - $where[] = qsprintf( - $conn, - 'id IN (%Ld)', - $this->ids); - } - - if ($this->phids) { - $where[] = qsprintf( - $conn, - 'phid IN (%Ls)', - $this->phids); - } - - if ($this->fragmentPHIDs) { - $where[] = qsprintf( - $conn, - 'fragmentPHID IN (%Ls)', - $this->fragmentPHIDs); - } - - if ($this->sequences) { - $where[] = qsprintf( - $conn, - 'sequence IN (%Ld)', - $this->sequences); - } - - if ($this->sequenceBefore !== null) { - $where[] = qsprintf( - $conn, - 'sequence < %d', - $this->sequenceBefore); - } - - $where[] = $this->buildPagingClause($conn); - - return $this->formatWhereClause($conn, $where); - } - - protected function willFilterPage(array $page) { - $fragments = array(); - - $fragment_phids = array_filter(mpull($page, 'getFragmentPHID')); - if ($fragment_phids) { - $fragments = id(new PhabricatorObjectQuery()) - ->setViewer($this->getViewer()) - ->withPHIDs($fragment_phids) - ->setParentQuery($this) - ->execute(); - $fragments = mpull($fragments, null, 'getPHID'); - } - - foreach ($page as $key => $version) { - $fragment_phid = $version->getFragmentPHID(); - if (empty($fragments[$fragment_phid])) { - unset($page[$key]); - continue; - } - $version->attachFragment($fragments[$fragment_phid]); - } - - return $page; - } - - public function getQueryApplicationClass() { - return 'PhabricatorPhragmentApplication'; - } -} diff --git a/src/applications/phragment/query/PhragmentSnapshotChildQuery.php b/src/applications/phragment/query/PhragmentSnapshotChildQuery.php deleted file mode 100644 index faa3493499..0000000000 --- a/src/applications/phragment/query/PhragmentSnapshotChildQuery.php +++ /dev/null @@ -1,174 +0,0 @@ -ids = $ids; - return $this; - } - - public function withSnapshotPHIDs(array $snapshot_phids) { - $this->snapshotPHIDs = $snapshot_phids; - return $this; - } - - public function withFragmentPHIDs(array $fragment_phids) { - $this->fragmentPHIDs = $fragment_phids; - return $this; - } - - public function withFragmentVersionPHIDs(array $fragment_version_phids) { - $this->fragmentVersionPHIDs = $fragment_version_phids; - return $this; - } - - public function needFragments($need_fragments) { - $this->needFragments = $need_fragments; - return $this; - } - - public function needFragmentVersions($need_fragment_versions) { - $this->needFragmentVersions = $need_fragment_versions; - return $this; - } - - protected function loadPage() { - $table = new PhragmentSnapshotChild(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($data); - } - - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); - - if ($this->ids) { - $where[] = qsprintf( - $conn, - 'id IN (%Ld)', - $this->ids); - } - - if ($this->snapshotPHIDs) { - $where[] = qsprintf( - $conn, - 'snapshotPHID IN (%Ls)', - $this->snapshotPHIDs); - } - - if ($this->fragmentPHIDs) { - $where[] = qsprintf( - $conn, - 'fragmentPHID IN (%Ls)', - $this->fragmentPHIDs); - } - - if ($this->fragmentVersionPHIDs) { - $where[] = qsprintf( - $conn, - 'fragmentVersionPHID IN (%Ls)', - $this->fragmentVersionPHIDs); - } - - $where[] = $this->buildPagingClause($conn); - - return $this->formatWhereClause($conn, $where); - } - - protected function willFilterPage(array $page) { - $snapshots = array(); - - $snapshot_phids = array_filter(mpull($page, 'getSnapshotPHID')); - if ($snapshot_phids) { - $snapshots = id(new PhabricatorObjectQuery()) - ->setViewer($this->getViewer()) - ->withPHIDs($snapshot_phids) - ->setParentQuery($this) - ->execute(); - $snapshots = mpull($snapshots, null, 'getPHID'); - } - - foreach ($page as $key => $child) { - $snapshot_phid = $child->getSnapshotPHID(); - if (empty($snapshots[$snapshot_phid])) { - unset($page[$key]); - continue; - } - $child->attachSnapshot($snapshots[$snapshot_phid]); - } - - return $page; - } - - protected function didFilterPage(array $page) { - if ($this->needFragments) { - $fragments = array(); - - $fragment_phids = array_filter(mpull($page, 'getFragmentPHID')); - if ($fragment_phids) { - $fragments = id(new PhabricatorObjectQuery()) - ->setViewer($this->getViewer()) - ->withPHIDs($fragment_phids) - ->setParentQuery($this) - ->execute(); - $fragments = mpull($fragments, null, 'getPHID'); - } - - foreach ($page as $key => $child) { - $fragment_phid = $child->getFragmentPHID(); - if (empty($fragments[$fragment_phid])) { - unset($page[$key]); - continue; - } - $child->attachFragment($fragments[$fragment_phid]); - } - } - - if ($this->needFragmentVersions) { - $fragment_versions = array(); - - $fragment_version_phids = array_filter(mpull( - $page, - 'getFragmentVersionPHID')); - if ($fragment_version_phids) { - $fragment_versions = id(new PhabricatorObjectQuery()) - ->setViewer($this->getViewer()) - ->withPHIDs($fragment_version_phids) - ->setParentQuery($this) - ->execute(); - $fragment_versions = mpull($fragment_versions, null, 'getPHID'); - } - - foreach ($page as $key => $child) { - $fragment_version_phid = $child->getFragmentVersionPHID(); - if (empty($fragment_versions[$fragment_version_phid])) { - continue; - } - $child->attachFragmentVersion( - $fragment_versions[$fragment_version_phid]); - } - } - - return $page; - } - - public function getQueryApplicationClass() { - return 'PhabricatorPhragmentApplication'; - } -} diff --git a/src/applications/phragment/query/PhragmentSnapshotQuery.php b/src/applications/phragment/query/PhragmentSnapshotQuery.php deleted file mode 100644 index a4805650fc..0000000000 --- a/src/applications/phragment/query/PhragmentSnapshotQuery.php +++ /dev/null @@ -1,111 +0,0 @@ -ids = $ids; - return $this; - } - - public function withPHIDs(array $phids) { - $this->phids = $phids; - return $this; - } - - public function withPrimaryFragmentPHIDs(array $primary_fragment_phids) { - $this->primaryFragmentPHIDs = $primary_fragment_phids; - return $this; - } - - public function withNames(array $names) { - $this->names = $names; - return $this; - } - - protected function loadPage() { - $table = new PhragmentSnapshot(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($data); - } - - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); - - if ($this->ids !== null) { - $where[] = qsprintf( - $conn, - 'id IN (%Ld)', - $this->ids); - } - - if ($this->phids !== null) { - $where[] = qsprintf( - $conn, - 'phid IN (%Ls)', - $this->phids); - } - - if ($this->primaryFragmentPHIDs !== null) { - $where[] = qsprintf( - $conn, - 'primaryFragmentPHID IN (%Ls)', - $this->primaryFragmentPHIDs); - } - - if ($this->names !== null) { - $where[] = qsprintf( - $conn, - 'name IN (%Ls)', - $this->names); - } - - $where[] = $this->buildPagingClause($conn); - - return $this->formatWhereClause($conn, $where); - } - - protected function willFilterPage(array $page) { - $fragments = array(); - - $fragment_phids = array_filter(mpull($page, 'getPrimaryFragmentPHID')); - if ($fragment_phids) { - $fragments = id(new PhabricatorObjectQuery()) - ->setViewer($this->getViewer()) - ->withPHIDs($fragment_phids) - ->setParentQuery($this) - ->execute(); - $fragments = mpull($fragments, null, 'getPHID'); - } - - foreach ($page as $key => $snapshot) { - $fragment_phid = $snapshot->getPrimaryFragmentPHID(); - if (empty($fragments[$fragment_phid])) { - unset($page[$key]); - continue; - } - $snapshot->attachPrimaryFragment($fragments[$fragment_phid]); - } - - return $page; - } - - public function getQueryApplicationClass() { - return 'PhabricatorPhragmentApplication'; - } - -} diff --git a/src/applications/phragment/storage/PhragmentDAO.php b/src/applications/phragment/storage/PhragmentDAO.php deleted file mode 100644 index 5b3a9a20d7..0000000000 --- a/src/applications/phragment/storage/PhragmentDAO.php +++ /dev/null @@ -1,9 +0,0 @@ - true, - self::CONFIG_COLUMN_SCHEMA => array( - 'path' => 'text128', - 'depth' => 'uint32', - 'latestVersionPHID' => 'phid?', - ), - self::CONFIG_KEY_SCHEMA => array( - 'key_path' => array( - 'columns' => array('path'), - 'unique' => true, - ), - ), - ) + parent::getConfiguration(); - } - - public function generatePHID() { - return PhabricatorPHID::generateNewPHID( - PhragmentFragmentPHIDType::TYPECONST); - } - - public function getURI() { - return '/phragment/browse/'.$this->getPath(); - } - - public function getName() { - return basename($this->path); - } - - public function getFile() { - return $this->assertAttached($this->file); - } - - public function attachFile(PhabricatorFile $file) { - return $this->file = $file; - } - - public function isDirectory() { - return $this->latestVersionPHID === null; - } - - public function isDeleted() { - return $this->getLatestVersion()->getFilePHID() === null; - } - - public function getLatestVersion() { - if ($this->latestVersionPHID === null) { - return null; - } - return $this->assertAttached($this->latestVersion); - } - - public function attachLatestVersion(PhragmentFragmentVersion $version) { - return $this->latestVersion = $version; - } - - -/* -( Updating ) --------------------------------------------------------- */ - - - /** - * Create a new fragment from a file. - */ - public static function createFromFile( - PhabricatorUser $viewer, - PhabricatorFile $file = null, - $path = null, - $view_policy = null, - $edit_policy = null) { - - $fragment = id(new PhragmentFragment()); - $fragment->setPath($path); - $fragment->setDepth(count(explode('/', $path))); - $fragment->setLatestVersionPHID(null); - $fragment->setViewPolicy($view_policy); - $fragment->setEditPolicy($edit_policy); - $fragment->save(); - - // Directory fragments have no versions associated with them, so we - // just return the fragment at this point. - if ($file === null) { - return $fragment; - } - - if ($file->getMimeType() === 'application/zip') { - $fragment->updateFromZIP($viewer, $file); - } else { - $fragment->updateFromFile($viewer, $file); - } - - return $fragment; - } - - - /** - * Set the specified file as the next version for the fragment. - */ - public function updateFromFile( - PhabricatorUser $viewer, - PhabricatorFile $file) { - - $existing = id(new PhragmentFragmentVersionQuery()) - ->setViewer($viewer) - ->withFragmentPHIDs(array($this->getPHID())) - ->execute(); - $sequence = count($existing); - - $this->openTransaction(); - $version = id(new PhragmentFragmentVersion()); - $version->setSequence($sequence); - $version->setFragmentPHID($this->getPHID()); - $version->setFilePHID($file->getPHID()); - $version->save(); - - $this->setLatestVersionPHID($version->getPHID()); - $this->save(); - $this->saveTransaction(); - - $file->attachToObject($version->getPHID()); - } - - /** - * Apply the specified ZIP archive onto the fragment, removing - * and creating fragments as needed. - */ - public function updateFromZIP( - PhabricatorUser $viewer, - PhabricatorFile $file) { - - if ($file->getMimeType() !== 'application/zip') { - throw new Exception( - pht("File must have mimetype '%s'.", 'application/zip')); - } - - // First apply the ZIP as normal. - $this->updateFromFile($viewer, $file); - - // Ensure we have ZIP support. - $zip = null; - try { - $zip = new ZipArchive(); - } catch (Exception $e) { - // The server doesn't have php5-zip, so we can't do recursive updates. - return; - } - - $temp = new TempFile(); - Filesystem::writeFile($temp, $file->loadFileData()); - if (!$zip->open($temp)) { - throw new Exception(pht('Unable to open ZIP.')); - } - - // Get all of the paths and their data from the ZIP. - $mappings = array(); - for ($i = 0; $i < $zip->numFiles; $i++) { - $path = trim($zip->getNameIndex($i), '/'); - $stream = $zip->getStream($path); - $data = null; - // If the stream is false, then it is a directory entry. We leave - // $data set to null for directories so we know not to create a - // version entry for them. - if ($stream !== false) { - $data = stream_get_contents($stream); - fclose($stream); - } - $mappings[$path] = $data; - } - - // We need to detect any directories that are in the ZIP folder that - // aren't explicitly noted in the ZIP. This can happen if the file - // entries in the ZIP look like: - // - // * something/blah.png - // * something/other.png - // * test.png - // - // Where there is no explicit "something/" entry. - foreach ($mappings as $path_key => $data) { - if ($data === null) { - continue; - } - $directory = dirname($path_key); - while ($directory !== '.') { - if (!array_key_exists($directory, $mappings)) { - $mappings[$directory] = null; - } - if (dirname($directory) === $directory) { - // dirname() will not reduce this directory any further; to - // prevent infinite loop we just break out here. - break; - } - $directory = dirname($directory); - } - } - - // Adjust the paths relative to this fragment so we can look existing - // fragments up in the DB. - $base_path = $this->getPath(); - $paths = array(); - foreach ($mappings as $p => $data) { - $paths[] = $base_path.'/'.$p; - } - - // FIXME: What happens when a child exists, but the current user - // can't see it. We're going to create a new child with the exact - // same path and then bad things will happen. - $children = id(new PhragmentFragmentQuery()) - ->setViewer($viewer) - ->needLatestVersion(true) - ->withLeadingPath($this->getPath().'/') - ->execute(); - $children = mpull($children, null, 'getPath'); - - // Iterate over the existing fragments. - foreach ($children as $full_path => $child) { - $path = substr($full_path, strlen($base_path) + 1); - if (array_key_exists($path, $mappings)) { - if ($child->isDirectory() && $mappings[$path] === null) { - // Don't create a version entry for a directory - // (unless it's been converted into a file). - continue; - } - - // The file is being updated. - $file = PhabricatorFile::newFromFileData( - $mappings[$path], - array('name' => basename($path))); - $child->updateFromFile($viewer, $file); - } else { - // The file is being deleted. - $child->deleteFile($viewer); - } - } - - // Iterate over the mappings to find new files. - foreach ($mappings as $path => $data) { - if (!array_key_exists($base_path.'/'.$path, $children)) { - // The file is being created. If the data is null, - // then this is explicitly a directory being created. - $file = null; - if ($mappings[$path] !== null) { - $file = PhabricatorFile::newFromFileData( - $mappings[$path], - array('name' => basename($path))); - } - self::createFromFile( - $viewer, - $file, - $base_path.'/'.$path, - $this->getViewPolicy(), - $this->getEditPolicy()); - } - } - } - - /** - * Delete the contents of the specified fragment. - */ - public function deleteFile(PhabricatorUser $viewer) { - $existing = id(new PhragmentFragmentVersionQuery()) - ->setViewer($viewer) - ->withFragmentPHIDs(array($this->getPHID())) - ->execute(); - $sequence = count($existing); - - $this->openTransaction(); - $version = id(new PhragmentFragmentVersion()); - $version->setSequence($sequence); - $version->setFragmentPHID($this->getPHID()); - $version->setFilePHID(null); - $version->save(); - - $this->setLatestVersionPHID($version->getPHID()); - $this->save(); - $this->saveTransaction(); - } - - -/* -( Utility ) ---------------------------------------------------------- */ - - - public function getFragmentMappings( - PhabricatorUser $viewer, - $base_path) { - - $children = id(new PhragmentFragmentQuery()) - ->setViewer($viewer) - ->needLatestVersion(true) - ->withLeadingPath($this->getPath().'/') - ->withDepths(array($this->getDepth() + 1)) - ->execute(); - - if (count($children) === 0) { - $path = substr($this->getPath(), strlen($base_path) + 1); - return array($path => $this); - } else { - $mappings = array(); - foreach ($children as $child) { - $child_mappings = $child->getFragmentMappings( - $viewer, - $base_path); - foreach ($child_mappings as $key => $value) { - $mappings[$key] = $value; - } - } - return $mappings; - } - } - - -/* -( Policy Interface )--------------------------------------------------- */ - - - public function getCapabilities() { - return array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - ); - } - - public function getPolicy($capability) { - switch ($capability) { - case PhabricatorPolicyCapability::CAN_VIEW: - return $this->getViewPolicy(); - case PhabricatorPolicyCapability::CAN_EDIT: - return $this->getEditPolicy(); - } - } - - public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { - return false; - } - -} diff --git a/src/applications/phragment/storage/PhragmentFragmentVersion.php b/src/applications/phragment/storage/PhragmentFragmentVersion.php deleted file mode 100644 index 88e501ee29..0000000000 --- a/src/applications/phragment/storage/PhragmentFragmentVersion.php +++ /dev/null @@ -1,72 +0,0 @@ - true, - self::CONFIG_COLUMN_SCHEMA => array( - 'sequence' => 'uint32', - 'filePHID' => 'phid?', - ), - self::CONFIG_KEY_SCHEMA => array( - 'key_version' => array( - 'columns' => array('fragmentPHID', 'sequence'), - 'unique' => true, - ), - ), - ) + parent::getConfiguration(); - } - - public function generatePHID() { - return PhabricatorPHID::generateNewPHID( - PhragmentFragmentVersionPHIDType::TYPECONST); - } - - public function getURI() { - return '/phragment/version/'.$this->getID().'/'; - } - - public function getFragment() { - return $this->assertAttached($this->fragment); - } - - public function attachFragment(PhragmentFragment $fragment) { - return $this->fragment = $fragment; - } - - public function getFile() { - return $this->assertAttached($this->file); - } - - public function attachFile(PhabricatorFile $file) { - return $this->file = $file; - } - - public function getCapabilities() { - return array( - PhabricatorPolicyCapability::CAN_VIEW, - ); - } - - public function getPolicy($capability) { - return $this->getFragment()->getPolicy($capability); - } - - public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { - return $this->getFragment()->hasAutomaticCapability($capability, $viewer); - } - - public function describeAutomaticCapability($capability) { - return $this->getFragment()->describeAutomaticCapability($capability); - } - -} diff --git a/src/applications/phragment/storage/PhragmentSchemaSpec.php b/src/applications/phragment/storage/PhragmentSchemaSpec.php deleted file mode 100644 index b2be73c172..0000000000 --- a/src/applications/phragment/storage/PhragmentSchemaSpec.php +++ /dev/null @@ -1,9 +0,0 @@ -buildEdgeSchemata(new PhragmentFragment()); - } - -} diff --git a/src/applications/phragment/storage/PhragmentSnapshot.php b/src/applications/phragment/storage/PhragmentSnapshot.php deleted file mode 100644 index 53c5443c7a..0000000000 --- a/src/applications/phragment/storage/PhragmentSnapshot.php +++ /dev/null @@ -1,76 +0,0 @@ - true, - self::CONFIG_COLUMN_SCHEMA => array( - 'name' => 'text128', - ), - self::CONFIG_KEY_SCHEMA => array( - 'key_name' => array( - 'columns' => array('primaryFragmentPHID', 'name'), - 'unique' => true, - ), - ), - ) + parent::getConfiguration(); - } - - public function generatePHID() { - return PhabricatorPHID::generateNewPHID( - PhragmentSnapshotPHIDType::TYPECONST); - } - - public function getURI() { - return '/phragment/snapshot/view/'.$this->getID().'/'; - } - - public function getPrimaryFragment() { - return $this->assertAttached($this->primaryFragment); - } - - public function attachPrimaryFragment(PhragmentFragment $fragment) { - return $this->primaryFragment = $fragment; - } - - public function delete() { - $children = id(new PhragmentSnapshotChild()) - ->loadAllWhere('snapshotPHID = %s', $this->getPHID()); - $this->openTransaction(); - foreach ($children as $child) { - $child->delete(); - } - $result = parent::delete(); - $this->saveTransaction(); - return $result; - } - - -/* -( Policy Interface )--------------------------------------------------- */ - - - public function getCapabilities() { - return $this->getPrimaryFragment()->getCapabilities(); - } - - public function getPolicy($capability) { - return $this->getPrimaryFragment()->getPolicy($capability); - } - - public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { - return $this->getPrimaryFragment() - ->hasAutomaticCapability($capability, $viewer); - } - - public function describeAutomaticCapability($capability) { - return $this->getPrimaryFragment() - ->describeAutomaticCapability($capability); - } -} diff --git a/src/applications/phragment/storage/PhragmentSnapshotChild.php b/src/applications/phragment/storage/PhragmentSnapshotChild.php deleted file mode 100644 index 3f0692ede6..0000000000 --- a/src/applications/phragment/storage/PhragmentSnapshotChild.php +++ /dev/null @@ -1,82 +0,0 @@ - array( - 'fragmentVersionPHID' => 'phid?', - ), - self::CONFIG_KEY_SCHEMA => array( - 'key_child' => array( - 'columns' => array( - 'snapshotPHID', - 'fragmentPHID', - 'fragmentVersionPHID', - ), - 'unique' => true, - ), - ), - ) + parent::getConfiguration(); - } - - public function getSnapshot() { - return $this->assertAttached($this->snapshot); - } - - public function attachSnapshot(PhragmentSnapshot $snapshot) { - return $this->snapshot = $snapshot; - } - - public function getFragment() { - return $this->assertAttached($this->fragment); - } - - public function attachFragment(PhragmentFragment $fragment) { - return $this->fragment = $fragment; - } - - public function getFragmentVersion() { - if ($this->fragmentVersionPHID === null) { - return null; - } - return $this->assertAttached($this->fragmentVersion); - } - - public function attachFragmentVersion(PhragmentFragmentVersion $version) { - return $this->fragmentVersion = $version; - } - - -/* -( Policy Interface )--------------------------------------------------- */ - - - public function getCapabilities() { - return array( - PhabricatorPolicyCapability::CAN_VIEW, - ); - } - - public function getPolicy($capability) { - return $this->getSnapshot()->getPolicy($capability); - } - - public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { - return $this->getSnapshot() - ->hasAutomaticCapability($capability, $viewer); - } - - public function describeAutomaticCapability($capability) { - return $this->getSnapshot() - ->describeAutomaticCapability($capability); - } -} diff --git a/src/applications/phragment/util/PhragmentPatchUtil.php b/src/applications/phragment/util/PhragmentPatchUtil.php deleted file mode 100644 index 5319ba6034..0000000000 --- a/src/applications/phragment/util/PhragmentPatchUtil.php +++ /dev/null @@ -1,53 +0,0 @@ -getContentHash(); - } - if ($new !== null) { - $new_hash = $new->getContentHash(); - } - - $old_content = ''; - $new_content = ''; - - if ($old_hash === $new_hash) { - return null; - } - - if ($old_hash !== self::EMPTY_HASH) { - $old_content = $old->loadFileData(); - } else { - $old_content = ''; - } - - if ($new_hash !== self::EMPTY_HASH) { - $new_content = $new->loadFileData(); - } else { - $new_content = ''; - } - - $dmp = new diff_match_patch(); - $dmp_patches = $dmp->patch_make($old_content, $new_content); - return $dmp->patch_toText($dmp_patches); - } - -} diff --git a/src/applications/phriction/application/PhabricatorPhrictionApplication.php b/src/applications/phriction/application/PhabricatorPhrictionApplication.php index 02f285aef3..edb035a63b 100644 --- a/src/applications/phriction/application/PhabricatorPhrictionApplication.php +++ b/src/applications/phriction/application/PhabricatorPhrictionApplication.php @@ -61,7 +61,7 @@ final class PhabricatorPhrictionApplication extends PhabricatorApplication { 'new/' => 'PhrictionNewController', 'move/(?P[1-9]\d*)/' => 'PhrictionMoveController', - 'preview/(?P.*/)' => 'PhrictionMarkupPreviewController', + 'preview/' => 'PhrictionMarkupPreviewController', 'diff/(?P[1-9]\d*)/' => 'PhrictionDiffController', $this->getEditRoutePattern('document/edit/') diff --git a/src/applications/phriction/controller/PhrictionEditController.php b/src/applications/phriction/controller/PhrictionEditController.php index d86fa1e05b..e6dea7f0ad 100644 --- a/src/applications/phriction/controller/PhrictionEditController.php +++ b/src/applications/phriction/controller/PhrictionEditController.php @@ -316,9 +316,17 @@ final class PhrictionEditController ->setBackground(PHUIObjectBoxView::WHITE_CONFIG) ->setForm($form); + $preview_uri = '/phriction/preview/'; + $preview_uri = new PhutilURI( + $preview_uri, + array( + 'slug' => $document->getSlug(), + )); + $preview_uri = phutil_string_cast($preview_uri); + $preview = id(new PHUIRemarkupPreviewPanel()) ->setHeader($content->getTitle()) - ->setPreviewURI('/phriction/preview/'.$document->getSlug()) + ->setPreviewURI($preview_uri) ->setControlID('document-textarea') ->setPreviewType(PHUIRemarkupPreviewPanel::DOCUMENT); diff --git a/src/applications/phriction/controller/PhrictionMarkupPreviewController.php b/src/applications/phriction/controller/PhrictionMarkupPreviewController.php index b5c97e5c64..a0964c53c4 100644 --- a/src/applications/phriction/controller/PhrictionMarkupPreviewController.php +++ b/src/applications/phriction/controller/PhrictionMarkupPreviewController.php @@ -3,12 +3,29 @@ final class PhrictionMarkupPreviewController extends PhabricatorController { - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); $text = $request->getStr('text'); - $slug = $request->getURIData('slug'); + $slug = $request->getStr('slug'); + + $document = id(new PhrictionDocumentQuery()) + ->setViewer($viewer) + ->withSlugs(array($slug)) + ->needContent(true) + ->executeOne(); + if (!$document) { + $document = PhrictionDocument::initializeNewDocument( + $viewer, + $slug); + + $content = id(new PhrictionContent()) + ->setSlug($slug); + + $document + ->setPHID($document->generatePHID()) + ->attachContent($content); + } $output = PhabricatorMarkupEngine::renderOneObject( id(new PhabricatorMarkupOneOff()) @@ -17,10 +34,7 @@ final class PhrictionMarkupPreviewController ->setContent($text), 'default', $viewer, - array( - 'phriction.isPreview' => true, - 'phriction.slug' => $slug, - )); + $document); return id(new AphrontAjaxResponse()) ->setContent($output); diff --git a/src/applications/phriction/markup/PhrictionRemarkupRule.php b/src/applications/phriction/markup/PhrictionRemarkupRule.php index 11994e0aa5..8394b23218 100644 --- a/src/applications/phriction/markup/PhrictionRemarkupRule.php +++ b/src/applications/phriction/markup/PhrictionRemarkupRule.php @@ -273,13 +273,6 @@ final class PhrictionRemarkupRule extends PhutilRemarkupRule { return null; } - // Handle content when it's a preview for the Phriction editor. - if (is_array($context)) { - if (idx($context, 'phriction.isPreview')) { - return idx($context, 'phriction.slug'); - } - } - if ($context instanceof PhrictionContent) { return $context->getSlug(); } diff --git a/src/applications/phriction/query/PhrictionContentQuery.php b/src/applications/phriction/query/PhrictionContentQuery.php index 6efab5e1c6..8ac92be351 100644 --- a/src/applications/phriction/query/PhrictionContentQuery.php +++ b/src/applications/phriction/query/PhrictionContentQuery.php @@ -32,10 +32,6 @@ final class PhrictionContentQuery return new PhrictionContent(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/phriction/query/PhrictionDocumentQuery.php b/src/applications/phriction/query/PhrictionDocumentQuery.php index e7b5a0529e..298db97dbb 100644 --- a/src/applications/phriction/query/PhrictionDocumentQuery.php +++ b/src/applications/phriction/query/PhrictionDocumentQuery.php @@ -62,10 +62,6 @@ final class PhrictionDocumentQuery return $this; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - public function newResultObject() { return new PhrictionDocument(); } diff --git a/src/applications/phurl/query/PhabricatorPhurlURLQuery.php b/src/applications/phurl/query/PhabricatorPhurlURLQuery.php index 6efbbd5b4c..c30cedf09d 100644 --- a/src/applications/phurl/query/PhabricatorPhurlURLQuery.php +++ b/src/applications/phurl/query/PhabricatorPhurlURLQuery.php @@ -50,10 +50,6 @@ final class PhabricatorPhurlURLQuery return $this; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/policy/config/PhabricatorPolicyConfigOptions.php b/src/applications/policy/config/PhabricatorPolicyConfigOptions.php index eacc32cebe..f7ac362b0f 100644 --- a/src/applications/policy/config/PhabricatorPolicyConfigOptions.php +++ b/src/applications/policy/config/PhabricatorPolicyConfigOptions.php @@ -37,7 +37,7 @@ final class PhabricatorPolicyConfigOptions ->setSummary(pht('Allow users to set object visibility to public.')) ->setDescription( pht( - "Phabricator allows you to set the visibility of objects (like ". + "This software allows you to set the visibility of objects (like ". "repositories and tasks) to 'Public', which means **anyone ". "on the internet can see them, without needing to log in or ". "have an account**.". @@ -59,7 +59,7 @@ final class PhabricatorPolicyConfigOptions ->setSummary(pht( 'Lock specific application policies so they can not be edited.')) ->setDescription(pht( - 'Phabricator has application policies which can dictate whether '. + 'This software has application policies which can dictate whether '. 'users can take certain actions, such as creating new users. '."\n\n". 'This setting allows for "locking" these policies such that no '. 'further edits can be made on a per-policy basis.')) diff --git a/src/applications/policy/editor/PhabricatorPolicyEditEngineExtension.php b/src/applications/policy/editor/PhabricatorPolicyEditEngineExtension.php index 14a4768f21..8bb6fb10ff 100644 --- a/src/applications/policy/editor/PhabricatorPolicyEditEngineExtension.php +++ b/src/applications/policy/editor/PhabricatorPolicyEditEngineExtension.php @@ -66,6 +66,16 @@ final class PhabricatorPolicyEditEngineExtension 'description.conduit' => pht('Change the join policy of the object.'), 'edit' => 'join', ), + PhabricatorTransactions::TYPE_INTERACT_POLICY => array( + 'key' => 'policy.interact', + 'aliases' => array('interact'), + 'capability' => PhabricatorPolicyCapability::CAN_INTERACT, + 'label' => pht('Interact Policy'), + 'description' => pht('Controls who can interact with the object.'), + 'description.conduit' + => pht('Change the interaction policy of the object.'), + 'edit' => 'interact', + ), ); if ($object instanceof PhabricatorPolicyCodexInterface) { diff --git a/src/applications/policy/query/PhabricatorPolicyQuery.php b/src/applications/policy/query/PhabricatorPolicyQuery.php index 018007db28..9bd6ba5994 100644 --- a/src/applications/policy/query/PhabricatorPolicyQuery.php +++ b/src/applications/policy/query/PhabricatorPolicyQuery.php @@ -290,6 +290,10 @@ final class PhabricatorPolicyQuery } public static function isSpecialPolicy($identifier) { + if ($identifier === null) { + return true; + } + if (self::isObjectPolicy($identifier)) { return true; } diff --git a/src/applications/policy/storage/PhabricatorPolicy.php b/src/applications/policy/storage/PhabricatorPolicy.php index 7904f17927..5e5ec14107 100644 --- a/src/applications/policy/storage/PhabricatorPolicy.php +++ b/src/applications/policy/storage/PhabricatorPolicy.php @@ -248,7 +248,7 @@ final class PhabricatorPolicy case PhabricatorPolicies::POLICY_PUBLIC: return pht( 'This object is public and can be viewed by anyone, even if they '. - 'do not have a Phabricator account.'); + 'do not have an account on this server.'); case PhabricatorPolicies::POLICY_USER: return pht('Logged in users can take this action.'); case PhabricatorPolicies::POLICY_ADMIN: @@ -417,12 +417,23 @@ final class PhabricatorPolicy PhabricatorPolicies::POLICY_NOONE => 1, ); - $this_strength = idx($strengths, $this->getPHID(), 0); - $other_strength = idx($strengths, $other->getPHID(), 0); + $this_strength = idx($strengths, $this_policy, 0); + $other_strength = idx($strengths, $other_policy, 0); return ($this_strength > $other_strength); } + public function isStrongerThanOrEqualTo(PhabricatorPolicy $other) { + $this_policy = $this->getPHID(); + $other_policy = $other->getPHID(); + + if ($this_policy === $other_policy) { + return true; + } + + return $this->isStrongerThan($other); + } + public function isValidPolicyForEdit() { return $this->getType() !== PhabricatorPolicyType::TYPE_MASKED; } diff --git a/src/applications/ponder/query/PonderAnswerQuery.php b/src/applications/ponder/query/PonderAnswerQuery.php index 2901f4d6a5..f100f05ae3 100644 --- a/src/applications/ponder/query/PonderAnswerQuery.php +++ b/src/applications/ponder/query/PonderAnswerQuery.php @@ -59,10 +59,6 @@ final class PonderAnswerQuery return new PonderAnswer(); } - protected function loadPage() { - return $this->loadStandardPage(new PonderAnswer()); - } - protected function willFilterPage(array $answers) { $questions = id(new PonderQuestionQuery()) ->setViewer($this->getViewer()) diff --git a/src/applications/ponder/query/PonderQuestionQuery.php b/src/applications/ponder/query/PonderQuestionQuery.php index b2de14e52c..323f34aac5 100644 --- a/src/applications/ponder/query/PonderQuestionQuery.php +++ b/src/applications/ponder/query/PonderQuestionQuery.php @@ -86,10 +86,6 @@ final class PonderQuestionQuery return new PonderQuestion(); } - protected function loadPage() { - return $this->loadStandardPage(new PonderQuestion()); - } - protected function willFilterPage(array $questions) { $phids = mpull($questions, 'getPHID'); diff --git a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php index a31bf8853c..e3a138c702 100644 --- a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php +++ b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php @@ -1435,7 +1435,7 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $task = ManiphestTask::initializeNewTask($viewer); - if (!strlen($name)) { + if ($name === null || $name === '') { $name = pht('Test Task'); } diff --git a/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php b/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php index 438c558e6e..2673902780 100644 --- a/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php +++ b/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php @@ -32,10 +32,6 @@ final class PhabricatorProjectColumnPositionQuery return new PhabricatorProjectColumnPosition(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = array(); diff --git a/src/applications/project/query/PhabricatorProjectColumnQuery.php b/src/applications/project/query/PhabricatorProjectColumnQuery.php index 380dab5208..478d872cb4 100644 --- a/src/applications/project/query/PhabricatorProjectColumnQuery.php +++ b/src/applications/project/query/PhabricatorProjectColumnQuery.php @@ -56,10 +56,6 @@ final class PhabricatorProjectColumnQuery return new PhabricatorProjectColumn(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { $projects = array(); diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php index 22e0b59c94..b02fa647a2 100644 --- a/src/applications/project/query/PhabricatorProjectQuery.php +++ b/src/applications/project/query/PhabricatorProjectQuery.php @@ -236,10 +236,6 @@ final class PhabricatorProjectQuery } } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $projects) { $ancestor_paths = array(); foreach ($projects as $project) { diff --git a/src/applications/project/query/PhabricatorProjectTriggerQuery.php b/src/applications/project/query/PhabricatorProjectTriggerQuery.php index 452e3e53f1..306fcb50fe 100644 --- a/src/applications/project/query/PhabricatorProjectTriggerQuery.php +++ b/src/applications/project/query/PhabricatorProjectTriggerQuery.php @@ -35,10 +35,6 @@ final class PhabricatorProjectTriggerQuery return new PhabricatorProjectTrigger(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php index 860d6e1749..6aebc954df 100644 --- a/src/applications/project/storage/PhabricatorProject.php +++ b/src/applications/project/storage/PhabricatorProject.php @@ -407,11 +407,13 @@ final class PhabricatorProject extends PhabricatorProjectDAO $this->setMailKey(Filesystem::readRandomCharacters(20)); } - if (!strlen($this->getPHID())) { + $phid = $this->getPHID(); + if ($phid === null || $phid === '') { $this->setPHID($this->generatePHID()); } - if (!strlen($this->getProjectPathKey())) { + $path_key = $this->getProjectPathKey(); + if ($path_key === null || $path_key === '') { $hash = PhabricatorHash::digestForIndex($this->getPHID()); $hash = substr($hash, 0, 4); $this->setProjectPathKey($hash); diff --git a/src/applications/project/xaction/PhabricatorProjectImageTransaction.php b/src/applications/project/xaction/PhabricatorProjectImageTransaction.php index f6d10f0961..9ed506d299 100644 --- a/src/applications/project/xaction/PhabricatorProjectImageTransaction.php +++ b/src/applications/project/xaction/PhabricatorProjectImageTransaction.php @@ -13,34 +13,6 @@ final class PhabricatorProjectImageTransaction $object->setProfileImagePHID($value); } - public function applyExternalEffects($object, $value) { - $old = $this->getOldValue(); - $new = $value; - $all = array(); - if ($old) { - $all[] = $old; - } - if ($new) { - $all[] = $new; - } - - $files = id(new PhabricatorFileQuery()) - ->setViewer($this->getActor()) - ->withPHIDs($all) - ->execute(); - $files = mpull($files, null, 'getPHID'); - - $old_file = idx($files, $old); - if ($old_file) { - $old_file->detachFromObject($object->getPHID()); - } - - $new_file = idx($files, $new); - if ($new_file) { - $new_file->attachToObject($object->getPHID()); - } - } - public function getTitle() { $old = $this->getOldValue(); $new = $this->getNewValue(); diff --git a/src/applications/releeph/application/PhabricatorReleephApplication.php b/src/applications/releeph/application/PhabricatorReleephApplication.php deleted file mode 100644 index 2590478f1a..0000000000 --- a/src/applications/releeph/application/PhabricatorReleephApplication.php +++ /dev/null @@ -1,92 +0,0 @@ -[1-9]\d*)' => 'ReleephRequestViewController', - - // TODO: Remove these older routes eventually. - '/RQ(?P[1-9]\d*)' => 'ReleephRequestViewController', - '/releeph/request/(?P[1-9]\d*)/' - => 'ReleephRequestViewController', - - '/releeph/' => array( - '' => 'ReleephProductListController', - '(?:product|project)/' => array( - '(?:query/(?P[^/]+)/)?' => 'ReleephProductListController', - 'create/' => 'ReleephProductCreateController', - '(?P[1-9]\d*)/' => array( - '(?:query/(?P[^/]+)/)?' => 'ReleephProductViewController', - 'edit/' => 'ReleephProductEditController', - 'cutbranch/' => 'ReleephBranchCreateController', - 'action/(?P.+)/' => 'ReleephProductActionController', - 'history/' => 'ReleephProductHistoryController', - ), - ), - - 'branch/' => array( - 'edit/(?P[1-9]\d*)/' - => 'ReleephBranchEditController', - '(?Pclose|re-open)/(?P[1-9]\d*)/' - => 'ReleephBranchAccessController', - 'preview/' => 'ReleephBranchNamePreviewController', - '(?P[1-9]\d*)/' => array( - 'history/' => 'ReleephBranchHistoryController', - '(?:query/(?P[^/]+)/)?' => 'ReleephBranchViewController', - ), - 'pull/(?P[1-9]\d*)/' - => 'ReleephRequestEditController', - ), - - 'request/' => array( - 'create/' => 'ReleephRequestEditController', - 'differentialcreate/' => array( - 'D(?P[1-9]\d*)' => - 'ReleephRequestDifferentialCreateController', - ), - 'edit/(?P[1-9]\d*)/' - => 'ReleephRequestEditController', - 'action/(?P.+)/(?P[1-9]\d*)/' - => 'ReleephRequestActionController', - 'typeahead/' => - 'ReleephRequestTypeaheadController', - 'comment/(?P[1-9]\d*)/' - => 'ReleephRequestCommentController', - ), - ), - ); - } - - public function getMailCommandObjects() { - // TODO: Pull requests don't implement any interfaces which give them - // meaningful commands, so don't expose ReleephRequest here for now. - // Once we add relevant commands, return it here. - return array(); - } - -} diff --git a/src/applications/releeph/commitfinder/ReleephCommitFinder.php b/src/applications/releeph/commitfinder/ReleephCommitFinder.php deleted file mode 100644 index 8c1712c802..0000000000 --- a/src/applications/releeph/commitfinder/ReleephCommitFinder.php +++ /dev/null @@ -1,120 +0,0 @@ -user = $user; - return $this; - } - public function getUser() { - return $this->user; - } - - public function setReleephProject(ReleephProject $rp) { - $this->releephProject = $rp; - return $this; - } - - public function getRequestedObjectPHID() { - return $this->objectPHID; - } - - public function fromPartial($partial_string) { - $this->objectPHID = null; - - // Look for diffs - $matches = array(); - if (preg_match('/^D([1-9]\d*)$/', $partial_string, $matches)) { - $diff_id = $matches[1]; - $diff_rev = id(new DifferentialRevisionQuery()) - ->setViewer($this->getUser()) - ->withIDs(array($diff_id)) - ->needCommitPHIDs(true) - ->executeOne(); - if (!$diff_rev) { - throw new ReleephCommitFinderException( - pht( - '%s does not refer to an existing diff.', - $partial_string)); - } - $commit_phids = $diff_rev->getCommitPHIDs(); - - if (!$commit_phids) { - throw new ReleephCommitFinderException( - pht( - '%s has no commits associated with it yet.', - $partial_string)); - } - - $this->objectPHID = $diff_rev->getPHID(); - - $commits = id(new DiffusionCommitQuery()) - ->setViewer($this->getUser()) - ->withPHIDs($commit_phids) - ->execute(); - $commits = msort($commits, 'getEpoch'); - return head($commits); - } - - // Look for a raw commit number, or r. - $repository = $this->releephProject->getRepository(); - $dr_data = null; - $matches = array(); - if (preg_match('/^r(?P[A-Z]+)(?P\w+)$/', - $partial_string, $matches)) { - $callsign = $matches['callsign']; - if ($callsign != $repository->getCallsign()) { - throw new ReleephCommitFinderException( - pht( - '%s is in a different repository to this Releeph project (%s).', - $partial_string, - $repository->getCallsign())); - } else { - $dr_data = $matches; - } - } else { - $dr_data = array( - 'callsign' => $repository->getCallsign(), - 'commit' => $partial_string, - ); - } - - try { - $dr_data['user'] = $this->getUser(); - $dr = DiffusionRequest::newFromDictionary($dr_data); - } catch (Exception $ex) { - $message = pht( - 'No commit matches %s: %s', - $partial_string, - $ex->getMessage()); - throw new ReleephCommitFinderException($message); - } - - $phabricator_repository_commit = $dr->loadCommit(); - - if (!$phabricator_repository_commit) { - throw new ReleephCommitFinderException( - pht( - "The commit %s doesn't exist in this repository.", - $partial_string)); - } - - // When requesting a single commit, if it has an associated review we - // imply the review was requested instead. This is always correct for now - // and consistent with the older behavior, although it might not be the - // right rule in the future. - $phids = PhabricatorEdgeQuery::loadDestinationPHIDs( - $phabricator_repository_commit->getPHID(), - DiffusionCommitHasRevisionEdgeType::EDGECONST); - if ($phids) { - $this->objectPHID = head($phids); - } - - return $phabricator_repository_commit; - } - -} diff --git a/src/applications/releeph/commitfinder/ReleephCommitFinderException.php b/src/applications/releeph/commitfinder/ReleephCommitFinderException.php deleted file mode 100644 index 8250de5927..0000000000 --- a/src/applications/releeph/commitfinder/ReleephCommitFinderException.php +++ /dev/null @@ -1,3 +0,0 @@ ->'; - } - - protected function execute(ConduitAPIRequest $request) { - $results = array(); - - $projects = id(new ReleephProductQuery()) - ->setViewer($request->getUser()) - ->withActive(1) - ->execute(); - - foreach ($projects as $project) { - $repository = $project->getRepository(); - - $branches = id(new ReleephBranch())->loadAllWhere( - 'releephProjectID = %d AND isActive = 1', - $project->getID()); - - foreach ($branches as $branch) { - $full_branch_name = $branch->getName(); - - $cut_point_commit = id(new PhabricatorRepositoryCommit())->loadOneWhere( - 'phid = %s', - $branch->getCutPointCommitPHID()); - - $results[] = array( - 'project' => $project->getName(), - 'repository' => $repository->getCallsign(), - 'branch' => $branch->getBasename(), - 'fullBranchName' => $full_branch_name, - 'symbolicName' => $branch->getSymbolicName(), - 'cutPoint' => $cut_point_commit->getCommitIdentifier(), - ); - } - } - - return $results; - } - -} diff --git a/src/applications/releeph/conduit/ReleephQueryBranchesConduitAPIMethod.php b/src/applications/releeph/conduit/ReleephQueryBranchesConduitAPIMethod.php deleted file mode 100644 index 400bbee604..0000000000 --- a/src/applications/releeph/conduit/ReleephQueryBranchesConduitAPIMethod.php +++ /dev/null @@ -1,73 +0,0 @@ - 'optional list', - 'phids' => 'optional list', - 'productPHIDs' => 'optional list', - ) + $this->getPagerParamTypes(); - } - - protected function defineReturnType() { - return 'query-results'; - } - - protected function execute(ConduitAPIRequest $request) { - $viewer = $request->getUser(); - - $query = id(new ReleephBranchQuery()) - ->setViewer($viewer); - - $ids = $request->getValue('ids'); - if ($ids !== null) { - $query->withIDs($ids); - } - - $phids = $request->getValue('phids'); - if ($phids !== null) { - $query->withPHIDs($phids); - } - - $product_phids = $request->getValue('productPHIDs'); - if ($product_phids !== null) { - $query->withProductPHIDs($product_phids); - } - - $pager = $this->newPager($request); - $branches = $query->executeWithCursorPager($pager); - - $data = array(); - foreach ($branches as $branch) { - $id = $branch->getID(); - - $uri = '/releeph/branch/'.$id.'/'; - $uri = PhabricatorEnv::getProductionURI($uri); - - $data[] = array( - 'id' => $id, - 'phid' => $branch->getPHID(), - 'uri' => $uri, - 'name' => $branch->getName(), - 'productPHID' => $branch->getProduct()->getPHID(), - ); - } - - return $this->addPagerResults( - array( - 'data' => $data, - ), - $pager); - } - -} diff --git a/src/applications/releeph/conduit/ReleephQueryProductsConduitAPIMethod.php b/src/applications/releeph/conduit/ReleephQueryProductsConduitAPIMethod.php deleted file mode 100644 index fe4d1d1df9..0000000000 --- a/src/applications/releeph/conduit/ReleephQueryProductsConduitAPIMethod.php +++ /dev/null @@ -1,80 +0,0 @@ - 'optional list', - 'phids' => 'optional list', - 'repositoryPHIDs' => 'optional list', - 'isActive' => 'optional bool', - ) + $this->getPagerParamTypes(); - } - - protected function defineReturnType() { - return 'query-results'; - } - - protected function execute(ConduitAPIRequest $request) { - $viewer = $request->getUser(); - - $query = id(new ReleephProductQuery()) - ->setViewer($viewer); - - $ids = $request->getValue('ids'); - if ($ids !== null) { - $query->withIDs($ids); - } - - $phids = $request->getValue('phids'); - if ($phids !== null) { - $query->withPHIDs($phids); - } - - $repository_phids = $request->getValue('repositoryPHIDs'); - if ($repository_phids !== null) { - $query->withRepositoryPHIDs($repository_phids); - } - - $is_active = $request->getValue('isActive'); - if ($is_active !== null) { - $query->withActive($is_active); - } - - $pager = $this->newPager($request); - $products = $query->executeWithCursorPager($pager); - - $data = array(); - foreach ($products as $product) { - $id = $product->getID(); - - $uri = '/releeph/product/'.$id.'/'; - $uri = PhabricatorEnv::getProductionURI($uri); - - $data[] = array( - 'id' => $id, - 'phid' => $product->getPHID(), - 'uri' => $uri, - 'name' => $product->getName(), - 'isActive' => (bool)$product->getIsActive(), - 'repositoryPHID' => $product->getRepositoryPHID(), - ); - } - - return $this->addPagerResults( - array( - 'data' => $data, - ), - $pager); - } - -} diff --git a/src/applications/releeph/conduit/ReleephQueryRequestsConduitAPIMethod.php b/src/applications/releeph/conduit/ReleephQueryRequestsConduitAPIMethod.php deleted file mode 100644 index 5cd5032c2a..0000000000 --- a/src/applications/releeph/conduit/ReleephQueryRequestsConduitAPIMethod.php +++ /dev/null @@ -1,77 +0,0 @@ - 'optional list', - 'requestedCommitPHIDs' => 'optional list', - ); - } - - protected function defineReturnType() { - return 'dict'; - } - - protected function execute(ConduitAPIRequest $conduit_request) { - $revision_phids = $conduit_request->getValue('revisionPHIDs'); - $requested_commit_phids = - $conduit_request->getValue('requestedCommitPHIDs'); - $result = array(); - - if (!$revision_phids && !$requested_commit_phids) { - return $result; - } - - $query = new ReleephRequestQuery(); - $query->setViewer($conduit_request->getUser()); - - if ($revision_phids) { - $query->withRequestedObjectPHIDs($revision_phids); - } else if ($requested_commit_phids) { - $query->withRequestedCommitPHIDs($requested_commit_phids); - } - - $releeph_requests = $query->execute(); - - foreach ($releeph_requests as $releeph_request) { - $branch = $releeph_request->getBranch(); - - $request_commit_phid = $releeph_request->getRequestCommitPHID(); - - $object = $releeph_request->getRequestedObject(); - if ($object instanceof DifferentialRevision) { - $object_phid = $object->getPHID(); - } else { - $object_phid = null; - } - - $status = $releeph_request->getStatus(); - $status_name = ReleephRequestStatus::getStatusDescriptionFor($status); - $url = PhabricatorEnv::getProductionURI('/RQ'.$releeph_request->getID()); - - $result[] = array( - 'branchBasename' => $branch->getBasename(), - 'branchSymbolic' => $branch->getSymbolicName(), - 'requestID' => $releeph_request->getID(), - 'revisionPHID' => $object_phid, - 'status' => $status, - 'status_name' => $status_name, - 'url' => $url, - ); - } - - return $result; - } - -} diff --git a/src/applications/releeph/conduit/ReleephRequestConduitAPIMethod.php b/src/applications/releeph/conduit/ReleephRequestConduitAPIMethod.php deleted file mode 100644 index 0ac3caf15e..0000000000 --- a/src/applications/releeph/conduit/ReleephRequestConduitAPIMethod.php +++ /dev/null @@ -1,169 +0,0 @@ - 'required string', - 'things' => 'required list', - 'fields' => 'dict', - ); - } - - protected function defineReturnType() { - return 'dict'; - } - - protected function defineErrorTypes() { - return array( - 'ERR_BRANCH' => pht('Unknown Releeph branch.'), - 'ERR_FIELD_PARSE' => pht('Unable to parse a Releeph field.'), - ); - } - - protected function execute(ConduitAPIRequest $request) { - $user = $request->getUser(); - - $viewer_handle = id(new PhabricatorHandleQuery()) - ->setViewer($user) - ->withPHIDs(array($user->getPHID())) - ->executeOne(); - - $branch_phid = $request->getValue('branchPHID'); - $releeph_branch = id(new ReleephBranchQuery()) - ->setViewer($user) - ->withPHIDs(array($branch_phid)) - ->executeOne(); - - if (!$releeph_branch) { - throw id(new ConduitException('ERR_BRANCH'))->setErrorDescription( - pht( - 'No %s found with PHID %s!', - 'ReleephBranch', - $branch_phid)); - } - - $releeph_project = $releeph_branch->getProduct(); - - // Find the requested commit identifiers - $requested_commits = array(); - $requested_object_phids = array(); - $things = $request->getValue('things'); - $finder = id(new ReleephCommitFinder()) - ->setUser($user) - ->setReleephProject($releeph_project); - foreach ($things as $thing) { - try { - $requested_commits[$thing] = $finder->fromPartial($thing); - $object_phid = $finder->getRequestedObjectPHID(); - if (!$object_phid) { - $object_phid = $requested_commits[$thing]->getPHID(); - } - $requested_object_phids[$thing] = $object_phid; - } catch (ReleephCommitFinderException $ex) { - throw id(new ConduitException('ERR_NO_MATCHES')) - ->setErrorDescription($ex->getMessage()); - } - } - $requested_commit_phids = mpull($requested_commits, 'getPHID'); - - // Find any existing requests that clash on the commit id, for this branch - $existing_releeph_requests = id(new ReleephRequest())->loadAllWhere( - 'requestCommitPHID IN (%Ls) AND branchID = %d', - $requested_commit_phids, - $releeph_branch->getID()); - $existing_releeph_requests = mpull( - $existing_releeph_requests, - null, - 'getRequestCommitPHID'); - - $selector = $releeph_project->getReleephFieldSelector(); - $fields = $selector->getFieldSpecifications(); - foreach ($fields as $field) { - $field - ->setReleephProject($releeph_project) - ->setReleephBranch($releeph_branch); - } - - $results = array(); - $handles = id(new PhabricatorHandleQuery()) - ->setViewer($user) - ->withPHIDs($requested_commit_phids) - ->execute(); - foreach ($requested_commits as $thing => $commit) { - $phid = $commit->getPHID(); - $name = id($handles[$phid])->getName(); - - $releeph_request = null; - - $existing_releeph_request = idx($existing_releeph_requests, $phid); - if ($existing_releeph_request) { - $releeph_request = $existing_releeph_request; - } else { - $releeph_request = id(new ReleephRequest()) - ->setRequestUserPHID($user->getPHID()) - ->setBranchID($releeph_branch->getID()) - ->setInBranch(0) - ->setRequestedObjectPHID($requested_object_phids[$thing]); - - $xactions = array(); - - $xactions[] = id(new ReleephRequestTransaction()) - ->setTransactionType(ReleephRequestTransaction::TYPE_REQUEST) - ->setNewValue($commit->getPHID()); - - $xactions[] = id(new ReleephRequestTransaction()) - ->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT) - ->setMetadataValue('userPHID', $user->getPHID()) - ->setMetadataValue( - 'isAuthoritative', - $releeph_project->isAuthoritative($user)) - ->setNewValue(ReleephRequest::INTENT_WANT); - - foreach ($fields as $field) { - if (!$field->isEditable()) { - continue; - } - $field->setReleephRequest($releeph_request); - try { - $field->setValueFromConduitAPIRequest($request); - } catch (ReleephFieldParseException $ex) { - throw id(new ConduitException('ERR_FIELD_PARSE')) - ->setErrorDescription($ex->getMessage()); - } - } - - $editor = id(new ReleephRequestTransactionalEditor()) - ->setActor($user) - ->setContinueOnNoEffect(true) - ->setContentSource($request->newContentSource()); - - $editor->applyTransactions($releeph_request, $xactions); - } - - $url = PhabricatorEnv::getProductionURI('/Y'.$releeph_request->getID()); - $results[$thing] = array( - 'thing' => $thing, - 'branch' => $releeph_branch->getDisplayNameWithDetail(), - 'commitName' => $name, - 'commitID' => $commit->getCommitIdentifier(), - 'url' => $url, - 'requestID' => $releeph_request->getID(), - 'requestor' => $viewer_handle->getName(), - 'requestTime' => $releeph_request->getDateCreated(), - 'existing' => $existing_releeph_request !== null, - ); - } - - return $results; - } - -} diff --git a/src/applications/releeph/conduit/work/ReleephWorkCanPushConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkCanPushConduitAPIMethod.php deleted file mode 100644 index 0086209575..0000000000 --- a/src/applications/releeph/conduit/work/ReleephWorkCanPushConduitAPIMethod.php +++ /dev/null @@ -1,34 +0,0 @@ - 'required string', - ); - } - - protected function defineReturnType() { - return 'bool'; - } - - protected function execute(ConduitAPIRequest $request) { - $releeph_project = id(new ReleephProject()) - ->loadOneWhere('phid = %s', $request->getValue('projectPHID')); - $user = $request->getUser(); - return $releeph_project->isAuthoritative($user); - } - -} diff --git a/src/applications/releeph/conduit/work/ReleephWorkGetAuthorInfoConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkGetAuthorInfoConduitAPIMethod.php deleted file mode 100644 index 6db9fd76ee..0000000000 --- a/src/applications/releeph/conduit/work/ReleephWorkGetAuthorInfoConduitAPIMethod.php +++ /dev/null @@ -1,44 +0,0 @@ - 'required string', - 'vcsType' => 'required string', - ); - } - - protected function defineReturnType() { - return 'nonempty string'; - } - - protected function execute(ConduitAPIRequest $request) { - $user = id(new PhabricatorUser()) - ->loadOneWhere('phid = %s', $request->getValue('userPHID')); - - $email = $user->loadPrimaryEmailAddress(); - if (is_numeric($email)) { - $email = $user->getUserName().'@fb.com'; - } - - return sprintf( - '%s <%s>', - $user->getRealName(), - $email); - } - -} diff --git a/src/applications/releeph/conduit/work/ReleephWorkGetBranchCommitMessageConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkGetBranchCommitMessageConduitAPIMethod.php deleted file mode 100644 index 43bdd04161..0000000000 --- a/src/applications/releeph/conduit/work/ReleephWorkGetBranchCommitMessageConduitAPIMethod.php +++ /dev/null @@ -1,104 +0,0 @@ - 'required string', - ); - } - - protected function defineReturnType() { - return 'nonempty string'; - } - - protected function execute(ConduitAPIRequest $request) { - $viewer = $request->getUser(); - - $branch = id(new ReleephBranchQuery()) - ->setViewer($viewer) - ->withPHIDs(array($request->getValue('branchPHID'))) - ->executeOne(); - - $project = $branch->getProduct(); - - $creator_phid = $branch->getCreatedByUserPHID(); - $cut_phid = $branch->getCutPointCommitPHID(); - - $phids = array( - $branch->getPHID(), - $project->getPHID(), - $creator_phid, - $cut_phid, - ); - - $handles = id(new PhabricatorHandleQuery()) - ->setViewer($request->getUser()) - ->withPHIDs($phids) - ->execute(); - - $h_branch = $handles[$branch->getPHID()]; - $h_project = $handles[$project->getPHID()]; - - // Not as customizable as a ReleephRequest's commit message. It doesn't - // really need to be. - // TODO: Yes it does, see FB-specific stuff below. - $commit_message = array(); - $commit_message[] = $h_branch->getFullName(); - $commit_message[] = $h_branch->getURI(); - - $commit_message[] = pht('Cut Point: %s', $handles[$cut_phid]->getName()); - - $cut_point_pr_commit = id(new PhabricatorRepositoryCommit()) - ->loadOneWhere('phid = %s', $cut_phid); - $cut_point_commit_date = strftime( - '%Y-%m-%d %H:%M:%S%z', - $cut_point_pr_commit->getEpoch()); - $commit_message[] = pht('Cut Point Date: %s', $cut_point_commit_date); - - $commit_message[] = pht( - 'Created By: %s', - $handles[$creator_phid]->getName()); - - $project_uri = $project->getURI(); - $commit_message[] = pht( - 'Project: %s', - $h_project->getName().' '.$project_uri); - - /** - * Required for 090-limit_new_branch_creations.sh in - * admin/scripts/git/hosting/hooks/update.d (in the E repo): - * - * http://fburl.com/2372545 - * - * The commit message must have a line saying: - * - * @new-branch: - * - */ - $repo = $project->getRepository(); - switch ($repo->getVersionControlSystem()) { - case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: - $commit_message[] = sprintf( - '@new-branch: %s', - $branch->getName()); - break; - } - - return implode("\n\n", $commit_message); - } - -} diff --git a/src/applications/releeph/conduit/work/ReleephWorkGetBranchConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkGetBranchConduitAPIMethod.php deleted file mode 100644 index 7941710528..0000000000 --- a/src/applications/releeph/conduit/work/ReleephWorkGetBranchConduitAPIMethod.php +++ /dev/null @@ -1,57 +0,0 @@ - 'required string', - ); - } - - protected function defineReturnType() { - return 'dict'; - } - - protected function execute(ConduitAPIRequest $request) { - $branch = id(new ReleephBranchQuery()) - ->setViewer($request->getUser()) - ->withPHIDs(array($request->getValue('branchPHID'))) - ->needCutPointCommits(true) - ->executeOne(); - - $cut_phid = $branch->getCutPointCommitPHID(); - $phids = array($cut_phid); - $handles = id(new PhabricatorHandleQuery()) - ->setViewer($request->getUser()) - ->withPHIDs($phids) - ->execute(); - - $project = $branch->getProject(); - $repo = $project->getRepository(); - $commit = $branch->getCutPointCommit(); - - return array( - 'branchName' => $branch->getName(), - 'branchPHID' => $branch->getPHID(), - 'vcsType' => $repo->getVersionControlSystem(), - 'cutCommitID' => $commit->getCommitIdentifier(), - 'cutCommitName' => $handles[$cut_phid]->getName(), - 'creatorPHID' => $branch->getCreatedByUserPHID(), - 'trunk' => $project->getTrunkBranch(), - ); - } - -} diff --git a/src/applications/releeph/conduit/work/ReleephWorkGetCommitMessageConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkGetCommitMessageConduitAPIMethod.php deleted file mode 100644 index 7f645c4be8..0000000000 --- a/src/applications/releeph/conduit/work/ReleephWorkGetCommitMessageConduitAPIMethod.php +++ /dev/null @@ -1,96 +0,0 @@ -formatStringConstants(array('pick', 'revert')); - - return array( - 'requestPHID' => 'required string', - 'action' => 'required '.$action_const, - ); - } - - protected function defineReturnType() { - return 'dict'; - } - - protected function execute(ConduitAPIRequest $request) { - $viewer = $request->getUser(); - - $releeph_request = id(new ReleephRequestQuery()) - ->setViewer($viewer) - ->withPHIDs(array($request->getValue('requestPHID'))) - ->executeOne(); - - $action = $request->getValue('action'); - - $title = $releeph_request->getSummaryForDisplay(); - - $commit_message = array(); - - $branch = $releeph_request->getBranch(); - $project = $branch->getProduct(); - - $selector = $project->getReleephFieldSelector(); - $fields = $selector->getFieldSpecifications(); - $fields = $selector->sortFieldsForCommitMessage($fields); - - foreach ($fields as $field) { - $field - ->setUser($request->getUser()) - ->setReleephProject($project) - ->setReleephBranch($branch) - ->setReleephRequest($releeph_request); - - $label = null; - $value = null; - - switch ($action) { - case 'pick': - if ($field->shouldAppearOnCommitMessage()) { - $label = $field->renderLabelForCommitMessage(); - $value = $field->renderValueForCommitMessage(); - } - break; - - case 'revert': - if ($field->shouldAppearOnRevertMessage()) { - $label = $field->renderLabelForRevertMessage(); - $value = $field->renderValueForRevertMessage(); - } - break; - } - - if ($label && $value) { - if (strpos($value, "\n") !== false || - substr($value, 0, 2) === ' ') { - $commit_message[] = "{$label}:\n{$value}"; - } else { - $commit_message[] = "{$label}: {$value}"; - } - } - } - - return array( - 'title' => $title, - 'body' => implode("\n\n", $commit_message), - ); - } - -} diff --git a/src/applications/releeph/conduit/work/ReleephWorkNextRequestConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkNextRequestConduitAPIMethod.php deleted file mode 100644 index fb8a8ad0dc..0000000000 --- a/src/applications/releeph/conduit/work/ReleephWorkNextRequestConduitAPIMethod.php +++ /dev/null @@ -1,228 +0,0 @@ - 'required phid', - 'seen' => 'required map', - ); - } - - protected function defineReturnType() { - return ''; - } - - protected function defineErrorTypes() { - return array( - 'ERR-NOT-PUSHER' => pht( - 'You are not listed as a pusher for the Releeph project!'), - ); - } - - protected function execute(ConduitAPIRequest $request) { - $viewer = $request->getUser(); - $seen = $request->getValue('seen'); - - $branch = id(new ReleephBranchQuery()) - ->setViewer($viewer) - ->withPHIDs(array($request->getValue('branchPHID'))) - ->executeOne(); - - $project = $branch->getProduct(); - - $needs_pick = array(); - $needs_revert = array(); - - // Load every request ever made for this branch...?!!! - $releeph_requests = id(new ReleephRequestQuery()) - ->setViewer($viewer) - ->withBranchIDs(array($branch->getID())) - ->execute(); - - foreach ($releeph_requests as $candidate) { - $phid = $candidate->getPHID(); - if (idx($seen, $phid)) { - continue; - } - - $should = $candidate->shouldBeInBranch(); - $in = $candidate->getInBranch(); - if ($should && !$in) { - $needs_pick[] = $candidate; - } - if (!$should && $in) { - $needs_revert[] = $candidate; - } - } - - /** - * Sort both needs_pick and needs_revert in ascending commit order, as - * discovered by Phabricator (using the `id` column to perform that - * ordering). - * - * This is easy for $needs_pick as the ordinal is stored. It is hard for - * reverts, as we have to look that information up. - */ - $needs_pick = $this->sortPicks($needs_pick); - $needs_revert = $this->sortReverts($needs_revert); - - /** - * Do reverts first in reverse order, then the picks in original-commit - * order. - * - * This seems like the correct thing to do, but there may be a better - * algorithm for the releephwork.nextrequest Conduit call that orders - * things better. - * - * We could also button-mash our way through everything that failed (at the - * end of the run) to try failed things again. - */ - $releeph_request = null; - $action = null; - if ($needs_revert) { - $releeph_request = last($needs_revert); - $action = 'revert'; - $commit_id = $releeph_request->getCommitIdentifier(); - $commit_phid = $releeph_request->getCommitPHID(); - } else if ($needs_pick) { - $releeph_request = head($needs_pick); - $action = 'pick'; - $commit = $releeph_request->loadPhabricatorRepositoryCommit(); - $commit_id = $commit->getCommitIdentifier(); - $commit_phid = $commit->getPHID(); - } else { - // Return early if there's nothing to do! - return array(); - } - - // Build the response - $phids = array(); - $phids[] = $commit_phid; - - $diff_phid = null; - $diff_rev_id = null; - - $requested_object = $releeph_request->getRequestedObject(); - if ($requested_object instanceof DifferentialRevision) { - $diff_rev = $requested_object; - } else { - $diff_rev = null; - } - - if ($diff_rev) { - $diff_phid = $diff_rev->getPHID(); - $phids[] = $diff_phid; - $diff_rev_id = $diff_rev->getID(); - } - - $phids[] = $releeph_request->getPHID(); - $handles = id(new PhabricatorHandleQuery()) - ->setViewer($request->getUser()) - ->withPHIDs($phids) - ->execute(); - - $diff_name = null; - if ($diff_rev) { - $diff_name = $handles[$diff_phid]->getName(); - } - - $new_author_phid = null; - if ($diff_rev) { - $new_author_phid = $diff_rev->getAuthorPHID(); - } else { - $pr_commit = $releeph_request->loadPhabricatorRepositoryCommit(); - if ($pr_commit) { - $new_author_phid = $pr_commit->getAuthorPHID(); - } - } - - return array( - 'requestID' => $releeph_request->getID(), - 'requestPHID' => $releeph_request->getPHID(), - 'requestName' => $handles[$releeph_request->getPHID()]->getName(), - 'requestorPHID' => $releeph_request->getRequestUserPHID(), - 'action' => $action, - 'diffRevID' => $diff_rev_id, - 'diffName' => $diff_name, - 'commitIdentifier' => $commit_id, - 'commitPHID' => $commit_phid, - 'commitName' => $handles[$commit_phid]->getName(), - 'needsRevert' => mpull($needs_revert, 'getID'), - 'needsPick' => mpull($needs_pick, 'getID'), - 'newAuthorPHID' => $new_author_phid, - ); - } - - private function sortPicks(array $releeph_requests) { - $surrogate = array(); - foreach ($releeph_requests as $rq) { - // TODO: it's likely that relying on the `id` column to provide - // trunk-commit-order is thoroughly broken. - $ordinal = (int)$rq->loadPhabricatorRepositoryCommit()->getID(); - $surrogate[$ordinal] = $rq; - } - ksort($surrogate); - return $surrogate; - } - - /** - * Sort an array of ReleephRequests, that have been picked into a branch, in - * the order in which they were picked to the branch. - */ - private function sortReverts(array $releeph_requests) { - if (!$releeph_requests) { - return array(); - } - - // ReleephRequests, keyed by - $releeph_requests = mpull($releeph_requests, null, 'getCommitIdentifier'); - - $commits = id(new PhabricatorRepositoryCommit()) - ->loadAllWhere( - 'commitIdentifier IN (%Ls)', - mpull($releeph_requests, 'getCommitIdentifier')); - - // A map of => - $surrogate = mpull($commits, 'getID', 'getCommitIdentifier'); - - $unparsed = array(); - $result = array(); - - foreach ($releeph_requests as $commit_id => $releeph_request) { - $ordinal = idx($surrogate, $commit_id); - if ($ordinal) { - $result[$ordinal] = $releeph_request; - } else { - $unparsed[] = $releeph_request; - } - } - - // Sort $result in ascending order - ksort($result); - - // Unparsed commits we'll just have to guess, based on time - $unparsed = msort($unparsed, 'getDateModified'); - - return array_merge($result, $unparsed); - } - -} diff --git a/src/applications/releeph/conduit/work/ReleephWorkRecordConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkRecordConduitAPIMethod.php deleted file mode 100644 index 9cfbe72c99..0000000000 --- a/src/applications/releeph/conduit/work/ReleephWorkRecordConduitAPIMethod.php +++ /dev/null @@ -1,73 +0,0 @@ -formatStringConstants( - array( - 'pick', - 'revert', - )); - - return array( - 'requestPHID' => 'required string', - 'action' => 'required '.$action_const, - 'commitIdentifier' => 'required string', - ); - } - - protected function defineReturnType() { - return 'void'; - } - - protected function execute(ConduitAPIRequest $request) { - $action = $request->getValue('action'); - $new_commit_id = $request->getValue('commitIdentifier'); - - $releeph_request = id(new ReleephRequest()) - ->loadOneWhere('phid = %s', $request->getValue('requestPHID')); - - $xactions = array(); - - $xactions[] = id(new ReleephRequestTransaction()) - ->setTransactionType(ReleephRequestTransaction::TYPE_COMMIT) - ->setMetadataValue('action', $action) - ->setNewValue($new_commit_id); - - $editor = id(new ReleephRequestTransactionalEditor()) - ->setActor($request->getUser()) - ->setContinueOnNoEffect(true) - ->setContentSource($request->newContentSource()); - - $editor->applyTransactions($releeph_request, $xactions); - } - -} diff --git a/src/applications/releeph/conduit/work/ReleephWorkRecordPickStatusConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkRecordPickStatusConduitAPIMethod.php deleted file mode 100644 index 5de0108a25..0000000000 --- a/src/applications/releeph/conduit/work/ReleephWorkRecordPickStatusConduitAPIMethod.php +++ /dev/null @@ -1,80 +0,0 @@ -formatStringConstants( - array( - 'pick', - 'revert', - )); - - return array( - 'requestPHID' => 'required string', - 'action' => 'required '.$action_const, - 'ok' => 'required bool', - 'dryRun' => 'optional bool', - 'details' => 'optional dict', - ); - } - - protected function defineReturnType() { - return ''; - } - - protected function execute(ConduitAPIRequest $request) { - $action = $request->getValue('action'); - $ok = $request->getValue('ok'); - $dry_run = $request->getValue('dryRun'); - $details = $request->getValue('details', array()); - - switch ($request->getValue('action')) { - case 'pick': - $pick_status = $ok - ? ReleephRequest::PICK_OK - : ReleephRequest::PICK_FAILED; - break; - - case 'revert': - $pick_status = $ok - ? ReleephRequest::REVERT_OK - : ReleephRequest::REVERT_FAILED; - break; - - default: - throw new Exception(pht('Unknown action %s!', $action)); - } - - $releeph_request = id(new ReleephRequest()) - ->loadOneWhere('phid = %s', $request->getValue('requestPHID')); - - $editor = id(new ReleephRequestTransactionalEditor()) - ->setActor($request->getUser()) - ->setContinueOnNoEffect(true) - ->setContentSource($request->newContentSource()); - - $xactions = array(); - - $xactions[] = id(new ReleephRequestTransaction()) - ->setTransactionType(ReleephRequestTransaction::TYPE_PICK_STATUS) - ->setMetadataValue('dryRun', $dry_run) - ->setMetadataValue('details', $details) - ->setNewValue($pick_status); - - $editor->applyTransactions($releeph_request, $xactions); - } - -} diff --git a/src/applications/releeph/config/PhabricatorReleephApplicationConfigOptions.php b/src/applications/releeph/config/PhabricatorReleephApplicationConfigOptions.php deleted file mode 100644 index 08dace52a7..0000000000 --- a/src/applications/releeph/config/PhabricatorReleephApplicationConfigOptions.php +++ /dev/null @@ -1,67 +0,0 @@ -getFieldKey()] = true; - } - - foreach ($default as $key => $enabled) { - $default[$key] = array( - 'disabled' => !$enabled, - ); - } - - $custom_field_type = 'custom:PhabricatorCustomFieldConfigOptionType'; - - return array( - $this->newOption('releeph.fields', $custom_field_type, $default) - ->setCustomData('ReleephFieldSpecification'), - $this->newOption( - 'releeph.default-branch-template', - 'string', - 'releases/%P/%p-%Y%m%d-%v') - ->setDescription( - pht( - 'The default branch template for new branches in unconfigured '. - 'Releeph projects. This is also configurable on a per-project '. - 'basis.')), - ); - } - -} diff --git a/src/applications/releeph/constants/ReleephRequestStatus.php b/src/applications/releeph/constants/ReleephRequestStatus.php deleted file mode 100644 index 2d29a95921..0000000000 --- a/src/applications/releeph/constants/ReleephRequestStatus.php +++ /dev/null @@ -1,32 +0,0 @@ - pht('Requested'), - self::STATUS_REJECTED => pht('Rejected'), - self::STATUS_ABANDONED => pht('Abandoned'), - self::STATUS_PICKED => pht('Pulled'), - self::STATUS_REVERTED => pht('Reverted'), - self::STATUS_NEEDS_PICK => pht('Needs Pull'), - self::STATUS_NEEDS_REVERT => pht('Needs Revert'), - ); - return idx($descriptions, $status, '??'); - } - - public static function getStatusClassSuffixFor($status) { - $description = self::getStatusDescriptionFor($status); - $class = str_replace(' ', '-', strtolower($description)); - return $class; - } - -} diff --git a/src/applications/releeph/controller/ReleephController.php b/src/applications/releeph/controller/ReleephController.php deleted file mode 100644 index f48d2858e8..0000000000 --- a/src/applications/releeph/controller/ReleephController.php +++ /dev/null @@ -1,37 +0,0 @@ -getRequest()->getUser(); - - $nav = new AphrontSideNavFilterView(); - $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); - - if ($for_app) { - $nav->addFilter('project/create/', pht('Create Product')); - } - - id(new ReleephProductSearchEngine()) - ->setViewer($user) - ->addNavigationItems($nav->getMenu()); - - $nav->selectFilter(null); - - return $nav; - } - - public function buildApplicationMenu() { - return $this->buildSideNavView(true)->getMenu(); - } - - - protected function getProductViewURI(ReleephProject $product) { - return $this->getApplicationURI('project/'.$product->getID().'/'); - } - - protected function getBranchViewURI(ReleephBranch $branch) { - return $this->getApplicationURI('branch/'.$branch->getID().'/'); - } - -} diff --git a/src/applications/releeph/controller/branch/ReleephBranchAccessController.php b/src/applications/releeph/controller/branch/ReleephBranchAccessController.php deleted file mode 100644 index 8b675ed2da..0000000000 --- a/src/applications/releeph/controller/branch/ReleephBranchAccessController.php +++ /dev/null @@ -1,73 +0,0 @@ -getViewer(); - $action = $request->getURIData('action'); - $id = $request->getURIData('branchID'); - - $branch = id(new ReleephBranchQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); - if (!$branch) { - return new Aphront404Response(); - } - $this->setBranch($branch); - - switch ($action) { - case 'close': - case 're-open': - break; - default: - return new Aphront404Response(); - } - - $branch_uri = $this->getBranchViewURI($branch); - if ($request->isFormPost()) { - - if ($action == 're-open') { - $is_active = 1; - } else { - $is_active = 0; - } - - id(new ReleephBranchEditor()) - ->setActor($request->getUser()) - ->setReleephBranch($branch) - ->changeBranchAccess($is_active); - - return id(new AphrontReloadResponse())->setURI($branch_uri); - } - - if ($action == 'close') { - $title_text = pht('Really Close Branch?'); - $short = pht('Close Branch'); - $body_text = pht( - 'Really close the branch "%s"?', - phutil_tag('strong', array(), $branch->getBasename())); - $button_text = pht('Close Branch'); - } else { - $title_text = pht('Really Reopen Branch?'); - $short = pht('Reopen Branch'); - $body_text = pht( - 'Really reopen the branch "%s"?', - phutil_tag('strong', array(), $branch->getBasename())); - $button_text = pht('Reopen Branch'); - } - - return $this->newDialog() - ->setTitle($title_text) - ->setShortTitle($short) - ->appendChild($body_text) - ->addSubmitButton($button_text) - ->addCancelButton($branch_uri); - } - -} diff --git a/src/applications/releeph/controller/branch/ReleephBranchController.php b/src/applications/releeph/controller/branch/ReleephBranchController.php deleted file mode 100644 index 62b26b4f94..0000000000 --- a/src/applications/releeph/controller/branch/ReleephBranchController.php +++ /dev/null @@ -1,35 +0,0 @@ -branch = $branch; - return $this; - } - - public function getBranch() { - return $this->branch; - } - - protected function buildApplicationCrumbs() { - $crumbs = parent::buildApplicationCrumbs(); - - $branch = $this->getBranch(); - if ($branch) { - $product = $branch->getProduct(); - - $crumbs->addTextCrumb( - $product->getName(), - $this->getProductViewURI($product)); - - $crumbs->addTextCrumb( - $branch->getName(), - $this->getBranchViewURI($branch)); - } - - return $crumbs; - } - -} diff --git a/src/applications/releeph/controller/branch/ReleephBranchCreateController.php b/src/applications/releeph/controller/branch/ReleephBranchCreateController.php deleted file mode 100644 index e03e432d1f..0000000000 --- a/src/applications/releeph/controller/branch/ReleephBranchCreateController.php +++ /dev/null @@ -1,133 +0,0 @@ -getViewer(); - $id = $request->getURIData('projectID'); - - $product = id(new ReleephProductQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); - if (!$product) { - return new Aphront404Response(); - } - $this->setProduct($product); - - - $cut_point = $request->getStr('cutPoint'); - $symbolic_name = $request->getStr('symbolicName'); - - if (!$cut_point) { - $repository = $product->getRepository(); - switch ($repository->getVersionControlSystem()) { - case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: - break; - case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: - $cut_point = $product->getTrunkBranch(); - break; - } - } - - $e_cut = true; - $errors = array(); - - $branch_date_control = id(new AphrontFormDateControl()) - ->setUser($request->getUser()) - ->setName('templateDate') - ->setLabel(pht('Date')) - ->setCaption(pht('The date used for filling out the branch template.')) - ->setInitialTime(AphrontFormDateControl::TIME_START_OF_DAY); - $branch_date = $branch_date_control->readValueFromRequest($request); - - if ($request->isFormPost()) { - $cut_commit = null; - if (!$cut_point) { - $e_cut = pht('Required'); - $errors[] = pht('You must give a branch cut point'); - } else { - try { - $finder = id(new ReleephCommitFinder()) - ->setUser($request->getUser()) - ->setReleephProject($product); - $cut_commit = $finder->fromPartial($cut_point); - } catch (Exception $e) { - $e_cut = pht('Invalid'); - $errors[] = $e->getMessage(); - } - } - - if (!$errors) { - $branch = id(new ReleephBranchEditor()) - ->setReleephProject($product) - ->setActor($request->getUser()) - ->newBranchFromCommit( - $cut_commit, - $branch_date, - $symbolic_name); - - $branch_uri = $this->getApplicationURI('branch/'.$branch->getID()); - - return id(new AphrontRedirectResponse()) - ->setURI($branch_uri); - } - } - - $product_uri = $this->getProductViewURI($product); - - $form = id(new AphrontFormView()) - ->setUser($request->getUser()) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Symbolic Name')) - ->setName('symbolicName') - ->setValue($symbolic_name) - ->setCaption(pht( - 'Mutable alternate name, for easy reference, (e.g. "LATEST")'))) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Cut point')) - ->setName('cutPoint') - ->setValue($cut_point) - ->setError($e_cut) - ->setCaption(pht( - 'A commit ID for your repo type, or a Diffusion ID like "rE123"'))) - ->appendChild($branch_date_control) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Cut Branch')) - ->addCancelButton($product_uri)); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Branch')) - ->setFormErrors($errors) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($form); - - $title = pht('New Branch'); - - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb($title); - $crumbs->setBorder(true); - - $header = id(new PHUIHeaderView()) - ->setHeader($title) - ->setHeaderIcon('fa-plus-square'); - - $view = id(new PHUITwoColumnView()) - ->setHeader($header) - ->setFooter($box); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($view); - - } -} diff --git a/src/applications/releeph/controller/branch/ReleephBranchEditController.php b/src/applications/releeph/controller/branch/ReleephBranchEditController.php deleted file mode 100644 index 9d34e78668..0000000000 --- a/src/applications/releeph/controller/branch/ReleephBranchEditController.php +++ /dev/null @@ -1,114 +0,0 @@ -getViewer(); - $id = $request->getURIData('branchID'); - - $branch = id(new ReleephBranchQuery()) - ->setViewer($viewer) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($id)) - ->executeOne(); - if (!$branch) { - return new Aphront404Response(); - } - $this->setBranch($branch); - - $symbolic_name = $request->getStr( - 'symbolicName', - $branch->getSymbolicName()); - - if ($request->isFormPost()) { - $existing_with_same_symbolic_name = - id(new ReleephBranch()) - ->loadOneWhere( - 'id != %d AND releephProjectID = %d AND symbolicName = %s', - $branch->getID(), - $branch->getReleephProjectID(), - $symbolic_name); - - $branch->openTransaction(); - $branch->setSymbolicName($symbolic_name); - - if ($existing_with_same_symbolic_name) { - $existing_with_same_symbolic_name - ->setSymbolicName(null) - ->save(); - } - - $branch->save(); - $branch->saveTransaction(); - - return id(new AphrontRedirectResponse()) - ->setURI($this->getBranchViewURI($branch)); - } - - $phids = array(); - - $phids[] = $creator_phid = $branch->getCreatedByUserPHID(); - $phids[] = $cut_commit_phid = $branch->getCutPointCommitPHID(); - - $handles = id(new PhabricatorHandleQuery()) - ->setViewer($request->getUser()) - ->withPHIDs($phids) - ->execute(); - - $form = id(new AphrontFormView()) - ->setUser($request->getUser()) - ->appendChild( - id(new AphrontFormStaticControl()) - ->setLabel(pht('Branch Name')) - ->setValue($branch->getName())) - ->appendChild( - id(new AphrontFormMarkupControl()) - ->setLabel(pht('Cut Point')) - ->setValue($handles[$cut_commit_phid]->renderLink())) - ->appendChild( - id(new AphrontFormMarkupControl()) - ->setLabel(pht('Created By')) - ->setValue($handles[$creator_phid]->renderLink())) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Symbolic Name')) - ->setName('symbolicName') - ->setValue($symbolic_name) - ->setCaption(pht( - 'Mutable alternate name, for easy reference, (e.g. "LATEST")'))) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->addCancelButton($this->getBranchViewURI($branch)) - ->setValue(pht('Save Branch'))); - - $title = pht( - 'Edit Branch: %s', - $branch->getDisplayNameWithDetail()); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Branch')) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($form); - - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb(pht('Edit')); - $crumbs->setBorder(true); - - $header = id(new PHUIHeaderView()) - ->setHeader(pht('Edit Branch')) - ->setHeaderIcon('fa-pencil'); - - $view = id(new PHUITwoColumnView()) - ->setHeader($header) - ->setFooter($box); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($view); - } -} diff --git a/src/applications/releeph/controller/branch/ReleephBranchHistoryController.php b/src/applications/releeph/controller/branch/ReleephBranchHistoryController.php deleted file mode 100644 index 5a07a5c879..0000000000 --- a/src/applications/releeph/controller/branch/ReleephBranchHistoryController.php +++ /dev/null @@ -1,41 +0,0 @@ -getViewer(); - $id = $request->getURIData('branchID'); - - $branch = id(new ReleephBranchQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->executeOne(); - if (!$branch) { - return new Aphront404Response(); - } - $this->setBranch($branch); - - $timeline = $this->buildTransactionTimeline( - $branch, - new ReleephBranchTransactionQuery()); - $timeline - ->setShouldTerminate(true); - - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb(pht('History')); - $crumbs->setBorder(true); - - $title = pht('Branch History'); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($timeline); - - } - -} diff --git a/src/applications/releeph/controller/branch/ReleephBranchNamePreviewController.php b/src/applications/releeph/controller/branch/ReleephBranchNamePreviewController.php deleted file mode 100644 index f2dbd23d8d..0000000000 --- a/src/applications/releeph/controller/branch/ReleephBranchNamePreviewController.php +++ /dev/null @@ -1,47 +0,0 @@ -getBool('isSymbolic'); - $template = $request->getStr('template'); - - if (!$is_symbolic && !$template) { - $template = ReleephBranchTemplate::getDefaultTemplate(); - } - - $repository_phid = $request->getInt('repositoryPHID'); - $fake_commit_handle = - ReleephBranchTemplate::getFakeCommitHandleFor( - $repository_phid, - $request->getUser()); - - list($name, $errors) = id(new ReleephBranchTemplate()) - ->setCommitHandle($fake_commit_handle) - ->setReleephProjectName($request->getStr('projectName')) - ->setSymbolic($is_symbolic) - ->interpolate($template); - - $markup = ''; - - if ($name) { - $markup = phutil_tag( - 'div', - array('class' => 'name'), - $name); - } - - if ($errors) { - $markup .= phutil_tag( - 'div', - array('class' => 'error'), - head($errors)); - } - - return id(new AphrontAjaxResponse()) - ->setContent(array('markup' => $markup)); - } - -} diff --git a/src/applications/releeph/controller/branch/ReleephBranchViewController.php b/src/applications/releeph/controller/branch/ReleephBranchViewController.php deleted file mode 100644 index cb4c3ed297..0000000000 --- a/src/applications/releeph/controller/branch/ReleephBranchViewController.php +++ /dev/null @@ -1,149 +0,0 @@ -getViewer(); - $id = $request->getURIData('branchID'); - $querykey = $request->getURIData('queryKey'); - - $branch = id(new ReleephBranchQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->executeOne(); - if (!$branch) { - return new Aphront404Response(); - } - $this->setBranch($branch); - - $controller = id(new PhabricatorApplicationSearchController()) - ->setPreface($this->renderPreface()) - ->setQueryKey($querykey) - ->setSearchEngine($this->getSearchEngine()) - ->setNavigation($this->buildSideNavView()); - - return $this->delegateToController($controller); - } - - - public function buildSideNavView($for_app = false) { - $user = $this->getRequest()->getUser(); - - $nav = new AphrontSideNavFilterView(); - $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); - - $this->getSearchEngine()->addNavigationItems($nav->getMenu()); - - $nav->selectFilter(null); - - return $nav; - } - - private function getSearchEngine() { - $branch = $this->getBranch(); - return id(new ReleephRequestSearchEngine()) - ->setBranch($branch) - ->setBaseURI($this->getApplicationURI('branch/'.$branch->getID().'/')) - ->setViewer($this->getRequest()->getUser()); - } - - protected function buildApplicationCrumbs() { - $crumbs = parent::buildApplicationCrumbs(); - - $branch = $this->getBranch(); - if ($branch) { - $pull_uri = $this->getApplicationURI('branch/pull/'.$branch->getID().'/'); - $crumbs->addAction( - id(new PHUIListItemView()) - ->setHref($pull_uri) - ->setName(pht('New Pull Request')) - ->setIcon('fa-plus-square') - ->setDisabled(!$branch->isActive())); - } - - return $crumbs; - } - - private function renderPreface() { - $viewer = $this->getRequest()->getUser(); - - $branch = $this->getBranch(); - $id = $branch->getID(); - - $header = id(new PHUIHeaderView()) - ->setHeader($branch->getDisplayName()) - ->setUser($viewer) - ->setPolicyObject($branch); - - if ($branch->getIsActive()) { - $header->setStatus('fa-check', 'bluegrey', pht('Active')); - } else { - $header->setStatus('fa-ban', 'dark', pht('Closed')); - } - - $actions = id(new PhabricatorActionListView()) - ->setUser($viewer) - ->setObject($branch); - - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $branch, - PhabricatorPolicyCapability::CAN_EDIT); - - $edit_uri = $this->getApplicationURI("branch/edit/{$id}/"); - $close_uri = $this->getApplicationURI("branch/close/{$id}/"); - $reopen_uri = $this->getApplicationURI("branch/re-open/{$id}/"); - $history_uri = $this->getApplicationURI("branch/{$id}/history/"); - - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Edit Branch')) - ->setHref($edit_uri) - ->setIcon('fa-pencil') - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit)); - - if ($branch->getIsActive()) { - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Close Branch')) - ->setHref($close_uri) - ->setIcon('fa-times') - ->setDisabled(!$can_edit) - ->setWorkflow(true)); - } else { - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Reopen Branch')) - ->setHref($reopen_uri) - ->setIcon('fa-plus') - ->setUser($viewer) - ->setDisabled(!$can_edit) - ->setWorkflow(true)); - } - - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('View History')) - ->setHref($history_uri) - ->setIcon('fa-list')); - - $properties = id(new PHUIPropertyListView()) - ->setUser($viewer) - ->setObject($branch) - ->setActionList($actions); - - $properties->addProperty( - pht('Branch'), - $branch->getName()); - - return id(new PHUIObjectBoxView()) - ->setHeader($header) - ->addPropertyList($properties); - } - -} diff --git a/src/applications/releeph/controller/product/ReleephProductActionController.php b/src/applications/releeph/controller/product/ReleephProductActionController.php deleted file mode 100644 index da82e2d6ad..0000000000 --- a/src/applications/releeph/controller/product/ReleephProductActionController.php +++ /dev/null @@ -1,85 +0,0 @@ -getViewer(); - $id = $request->getURIData('projectID'); - $action = $request->getURIData('action'); - - $product = id(new ReleephProductQuery()) - ->withIDs(array($id)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->setViewer($viewer) - ->executeOne(); - if (!$product) { - return new Aphront404Response(); - } - - $this->setProduct($product); - - $product_id = $product->getID(); - $product_uri = $this->getProductViewURI($product); - - switch ($action) { - case 'deactivate': - case 'activate': - break; - default: - throw new Aphront404Response(); - } - - if ($request->isFormPost()) { - $type_active = ReleephProductTransaction::TYPE_ACTIVE; - - $xactions = array(); - if ($action == 'activate') { - $xactions[] = id(new ReleephProductTransaction()) - ->setTransactionType($type_active) - ->setNewValue(1); - } else { - $xactions[] = id(new ReleephProductTransaction()) - ->setTransactionType($type_active) - ->setNewValue(0); - } - - $editor = id(new ReleephProductEditor()) - ->setActor($viewer) - ->setContentSourceFromRequest($request) - ->setContinueOnNoEffect(true) - ->setContinueOnMissingFields(true); - - $editor->applyTransactions($product, $xactions); - - return id(new AphrontRedirectResponse())->setURI($product_uri); - } - - if ($action == 'activate') { - $title = pht('Activate Product?'); - $body = pht( - 'Reactivate the product %s?', - phutil_tag('strong', array(), $product->getName())); - $submit = pht('Reactivate Product'); - $short = pht('Deactivate'); - } else { - $title = pht('Really Deactivate Product?'); - $body = pht( - 'Really deactivate the product %s?', - phutil_tag('strong', array(), $product->getName())); - $submit = pht('Deactivate Product'); - $short = pht('Activate'); - } - - return $this->newDialog() - ->setTitle($title) - ->setShortTitle($short) - ->appendParagraph($body) - ->addSubmitButton($submit) - ->addCancelButton($product_uri); - } - -} diff --git a/src/applications/releeph/controller/product/ReleephProductController.php b/src/applications/releeph/controller/product/ReleephProductController.php deleted file mode 100644 index 0e9090ebd4..0000000000 --- a/src/applications/releeph/controller/product/ReleephProductController.php +++ /dev/null @@ -1,30 +0,0 @@ -product = $product; - return $this; - } - - protected function getProduct() { - return $this->product; - } - - protected function buildApplicationCrumbs() { - $crumbs = parent::buildApplicationCrumbs(); - - $product = $this->getProduct(); - if ($product) { - $crumbs->addTextCrumb( - $product->getName(), - $this->getProductViewURI($product)); - } - - return $crumbs; - } - - -} diff --git a/src/applications/releeph/controller/product/ReleephProductCreateController.php b/src/applications/releeph/controller/product/ReleephProductCreateController.php deleted file mode 100644 index 12da2ea3f4..0000000000 --- a/src/applications/releeph/controller/product/ReleephProductCreateController.php +++ /dev/null @@ -1,140 +0,0 @@ -getStr('name')); - $trunk_branch = trim($request->getStr('trunkBranch')); - $repository_phid = $request->getStr('repositoryPHID'); - - $e_name = true; - $e_trunk_branch = true; - $errors = array(); - - if ($request->isFormPost()) { - if (!$name) { - $e_name = pht('Required'); - $errors[] = pht( - 'Your product should have a simple, descriptive name.'); - } - - if (!$trunk_branch) { - $e_trunk_branch = pht('Required'); - $errors[] = pht( - 'You must specify which branch you will be picking from.'); - } - - $pr_repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($request->getUser()) - ->withPHIDs(array($repository_phid)) - ->executeOne(); - - - if (!$errors) { - $releeph_product = id(new ReleephProject()) - ->setName($name) - ->setTrunkBranch($trunk_branch) - ->setRepositoryPHID($pr_repository->getPHID()) - ->setCreatedByUserPHID($request->getUser()->getPHID()) - ->setIsActive(1); - - try { - $releeph_product->save(); - - return id(new AphrontRedirectResponse()) - ->setURI($releeph_product->getURI()); - } catch (AphrontDuplicateKeyQueryException $ex) { - $e_name = pht('Not Unique'); - $errors[] = pht('Another product already uses this name.'); - } - } - } - - $repo_options = $this->getRepositorySelectOptions(); - - $product_name_input = id(new AphrontFormTextControl()) - ->setLabel(pht('Name')) - ->setDisableAutocomplete(true) - ->setName('name') - ->setValue($name) - ->setError($e_name) - ->setCaption(pht('A name like "Thrift" but not "Thrift releases".')); - - $repository_input = id(new AphrontFormSelectControl()) - ->setLabel(pht('Repository')) - ->setName('repositoryPHID') - ->setValue($repository_phid) - ->setOptions($repo_options); - - $branch_name_preview = id(new ReleephBranchPreviewView()) - ->setLabel(pht('Example Branch')) - ->addControl('projectName', $product_name_input) - ->addControl('repositoryPHID', $repository_input) - ->addStatic('template', '') - ->addStatic('isSymbolic', false); - - $form = id(new AphrontFormView()) - ->setUser($request->getUser()) - ->appendChild($product_name_input) - ->appendChild($repository_input) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Trunk')) - ->setName('trunkBranch') - ->setValue($trunk_branch) - ->setError($e_trunk_branch) - ->setCaption(pht( - 'The development branch, from which requests will be picked.'))) - ->appendChild($branch_name_preview) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->addCancelButton('/releeph/project/') - ->setValue(pht('Create Release Product'))); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Product')) - ->setFormErrors($errors) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->setForm($form); - - $title = pht('Create New Product'); - - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb(pht('New Product')); - $crumbs->setBorder(true); - - $header = id(new PHUIHeaderView()) - ->setHeader($title) - ->setHeaderIcon('fa-plus-square'); - - $view = id(new PHUITwoColumnView()) - ->setHeader($header) - ->setFooter($box); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($view); - } - - private function getRepositorySelectOptions() { - $repos = id(new PhabricatorRepositoryQuery()) - ->setViewer($this->getRequest()->getUser()) - ->execute(); - - $repos = msort($repos, 'getName'); - $repos = mpull($repos, null, 'getID'); - - $choices = array(); - - foreach ($repos as $repo_id => $repo) { - $repo_name = $repo->getName(); - $display = $repo->getDisplayName(); - $choices[$repo->getPHID()] = "{$display} ({$repo_name})"; - } - - asort($choices); - return $choices; - } - -} diff --git a/src/applications/releeph/controller/product/ReleephProductEditController.php b/src/applications/releeph/controller/product/ReleephProductEditController.php deleted file mode 100644 index 7938f0d930..0000000000 --- a/src/applications/releeph/controller/product/ReleephProductEditController.php +++ /dev/null @@ -1,275 +0,0 @@ -getViewer(); - $id = $request->getURIData('projectID'); - - $product = id(new ReleephProductQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); - if (!$product) { - return new Aphront404Response(); - } - $this->setProduct($product); - - $e_name = true; - $e_trunk_branch = true; - $e_branch_template = false; - $errors = array(); - - $product_name = $request->getStr('name', $product->getName()); - - $trunk_branch = $request->getStr('trunkBranch', $product->getTrunkBranch()); - $branch_template = $request->getStr('branchTemplate'); - if ($branch_template === null) { - $branch_template = $product->getDetail('branchTemplate'); - } - $pick_failure_instructions = $request->getStr('pickFailureInstructions', - $product->getDetail('pick_failure_instructions')); - $test_paths = $request->getStr('testPaths'); - if ($test_paths !== null) { - $test_paths = array_filter(explode("\n", $test_paths)); - } else { - $test_paths = $product->getDetail('testPaths', array()); - } - - $repository_phid = $product->getRepositoryPHID(); - - if ($request->isFormPost()) { - $pusher_phids = $request->getArr('pushers'); - - if (!$product_name) { - $e_name = pht('Required'); - $errors[] = - pht('Your Releeph product should have a simple descriptive name.'); - } - - if (!$trunk_branch) { - $e_trunk_branch = pht('Required'); - $errors[] = - pht('You must specify which branch you will be picking from.'); - } - - $other_releeph_products = id(new ReleephProject()) - ->loadAllWhere('id != %d', $product->getID()); - $other_releeph_product_names = mpull($other_releeph_products, - 'getName', 'getID'); - - if (in_array($product_name, $other_releeph_product_names)) { - $errors[] = pht('Releeph product name %s is already taken', - $product_name); - } - - foreach ($test_paths as $test_path) { - $result = @preg_match($test_path, ''); - $is_a_valid_regexp = $result !== false; - if (!$is_a_valid_regexp) { - $errors[] = pht('Please provide a valid regular expression: '. - '%s is not valid', $test_path); - } - } - - $product - ->setName($product_name) - ->setTrunkBranch($trunk_branch) - ->setDetail('pushers', $pusher_phids) - ->setDetail('pick_failure_instructions', $pick_failure_instructions) - ->setDetail('branchTemplate', $branch_template) - ->setDetail('testPaths', $test_paths); - - $fake_commit_handle = ReleephBranchTemplate::getFakeCommitHandleFor( - $repository_phid, - $viewer); - - if ($branch_template) { - list($branch_name, $template_errors) = id(new ReleephBranchTemplate()) - ->setCommitHandle($fake_commit_handle) - ->setReleephProjectName($product_name) - ->interpolate($branch_template); - - if ($template_errors) { - $e_branch_template = pht('Whoopsies!'); - foreach ($template_errors as $template_error) { - $errors[] = pht('Template error: %s', $template_error); - } - } - } - - if (!$errors) { - $product->save(); - - return id(new AphrontRedirectResponse())->setURI($product->getURI()); - } - } - - $pusher_phids = $request->getArr( - 'pushers', - $product->getDetail('pushers', array())); - - $form = id(new AphrontFormView()) - ->setUser($request->getUser()) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Name')) - ->setName('name') - ->setValue($product_name) - ->setError($e_name) - ->setCaption(pht('A name like "Thrift" but not "Thrift releases".'))) - ->appendChild( - id(new AphrontFormStaticControl()) - ->setLabel(pht('Repository')) - ->setValue( - $product->getRepository()->getName())) - ->appendChild( - id(new AphrontFormStaticControl()) - ->setLabel(pht('Repository')) - ->setValue( - $product->getRepository()->getName())) - ->appendChild( - id(new AphrontFormStaticControl()) - ->setLabel(pht('Releeph Project PHID')) - ->setValue( - $product->getPHID())) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Trunk')) - ->setValue($trunk_branch) - ->setName('trunkBranch') - ->setError($e_trunk_branch)) - ->appendChild( - id(new AphrontFormTextAreaControl()) - ->setLabel(pht('Pick Instructions')) - ->setValue($pick_failure_instructions) - ->setName('pickFailureInstructions') - ->setCaption( - pht('Instructions for pick failures, which will be used '. - 'in emails generated by failed picks'))) - ->appendChild( - id(new AphrontFormTextAreaControl()) - ->setLabel(pht('Tests paths')) - ->setValue(implode("\n", $test_paths)) - ->setName('testPaths') - ->setCaption( - pht('List of strings that all test files contain in their path '. - 'in this project. One string per line. '. - 'Examples: \'__tests__\', \'/javatests/\'...'))); - - $branch_template_input = id(new AphrontFormTextControl()) - ->setName('branchTemplate') - ->setValue($branch_template) - ->setLabel(pht('Branch Template')) - ->setError($e_branch_template) - ->setCaption( - pht("Leave this blank to use your installation's default.")); - - $branch_template_preview = id(new ReleephBranchPreviewView()) - ->setLabel(pht('Preview')) - ->addControl('template', $branch_template_input) - ->addStatic('repositoryPHID', $repository_phid) - ->addStatic('isSymbolic', false) - ->addStatic('projectName', $product->getName()); - - $form - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setLabel(pht('Pushers')) - ->setName('pushers') - ->setDatasource(new PhabricatorPeopleDatasource()) - ->setValue($pusher_phids)) - ->appendChild($branch_template_input) - ->appendChild($branch_template_preview) - ->appendRemarkupInstructions($this->getBranchHelpText()); - - $form - ->appendChild( - id(new AphrontFormSubmitControl()) - ->addCancelButton('/releeph/product/') - ->setValue(pht('Save'))); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Product')) - ->setFormErrors($errors) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($form); - - $title = pht('Edit Product'); - - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb(pht('Edit Product')); - $crumbs->setBorder(true); - - $header = id(new PHUIHeaderView()) - ->setHeader($title) - ->setHeaderIcon('fa-pencil'); - - $view = id(new PHUITwoColumnView()) - ->setHeader($header) - ->setFooter($box); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($view); - - } - - private function getBranchHelpText() { - return << releases/2012-30-16-rHERGE32cd512a52b7 - -Include a second hierarchy if you share your repository with other products: - - lang=none - releases/%P/%p-release-%Y%m%d-%V - => releases/Tintin/tintin-release-20121116-32cd512a52b7 - -Keep your branch names simple, avoiding strange punctuation, most of which is -forbidden or escaped anyway: - - lang=none, counterexample - releases//..clown-releases..//`date --iso=seconds`-$(sudo halt) - -Include the date early in your template, in an order which sorts properly: - - lang=none - releases/%Y%m%d-%v - => releases/20121116-rHERGE32cd512a52b7 (good!) - - releases/%V-%m.%d.%Y - => releases/32cd512a52b7-11.16.2012 (awful!) - - -EOTEXT; - } - -} diff --git a/src/applications/releeph/controller/product/ReleephProductHistoryController.php b/src/applications/releeph/controller/product/ReleephProductHistoryController.php deleted file mode 100644 index 12d0d0b5c1..0000000000 --- a/src/applications/releeph/controller/product/ReleephProductHistoryController.php +++ /dev/null @@ -1,39 +0,0 @@ -getViewer(); - $id = $request->getURIData('projectID'); - - $product = id(new ReleephProductQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->executeOne(); - if (!$product) { - return new Aphront404Response(); - } - $this->setProduct($product); - - $timeline = $this->buildTransactionTimeline( - $product, - new ReleephProductTransactionQuery()); - $timeline->setShouldTerminate(true); - - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb(pht('History')); - $crumbs->setBorder(true); - - $title = pht('Product History'); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($timeline); - } - -} diff --git a/src/applications/releeph/controller/product/ReleephProductListController.php b/src/applications/releeph/controller/product/ReleephProductListController.php deleted file mode 100644 index 14cc964e03..0000000000 --- a/src/applications/releeph/controller/product/ReleephProductListController.php +++ /dev/null @@ -1,31 +0,0 @@ -getURIData('queryKey'); - $controller = id(new PhabricatorApplicationSearchController()) - ->setQueryKey($query_key) - ->setSearchEngine(new ReleephProductSearchEngine()) - ->setNavigation($this->buildSideNavView()); - - return $this->delegateToController($controller); - } - - protected function buildApplicationCrumbs() { - $crumbs = parent::buildApplicationCrumbs(); - - $crumbs->addAction( - id(new PHUIListItemView()) - ->setName(pht('Create Product')) - ->setHref($this->getApplicationURI('product/create/')) - ->setIcon('fa-plus-square')); - - return $crumbs; - } - -} diff --git a/src/applications/releeph/controller/product/ReleephProductViewController.php b/src/applications/releeph/controller/product/ReleephProductViewController.php deleted file mode 100644 index 963710c3ec..0000000000 --- a/src/applications/releeph/controller/product/ReleephProductViewController.php +++ /dev/null @@ -1,153 +0,0 @@ -getURIData('projectID'); - $query_key = $request->getURIData('queryKey'); - $viewer = $request->getViewer(); - - $product = id(new ReleephProductQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->executeOne(); - if (!$product) { - return new Aphront404Response(); - } - $this->setProduct($product); - - $controller = id(new PhabricatorApplicationSearchController()) - ->setQueryKey($query_key) - ->setPreface($this->renderPreface()) - ->setSearchEngine( - id(new ReleephBranchSearchEngine()) - ->setProduct($product)) - ->setNavigation($this->buildSideNavView()); - - return $this->delegateToController($controller); - } - - public function buildSideNavView($for_app = false) { - $viewer = $this->getRequest()->getUser(); - $product = $this->getProduct(); - - $nav = new AphrontSideNavFilterView(); - $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); - - if ($for_app) { - $nav->addFilter('product/create/', pht('Create Product')); - } - - id(new ReleephBranchSearchEngine()) - ->setProduct($product) - ->setViewer($viewer) - ->addNavigationItems($nav->getMenu()); - - $nav->selectFilter(null); - - return $nav; - } - - protected function buildApplicationCrumbs() { - $crumbs = parent::buildApplicationCrumbs(); - - $product = $this->getProduct(); - if ($product) { - $crumbs->addAction( - id(new PHUIListItemView()) - ->setHref($product->getURI('cutbranch/')) - ->setName(pht('Cut New Branch')) - ->setIcon('fa-plus')); - } - - return $crumbs; - } - - private function renderPreface() { - $viewer = $this->getRequest()->getUser(); - $product = $this->getProduct(); - - $id = $product->getID(); - - $header = id(new PHUIHeaderView()) - ->setHeader($product->getName()) - ->setUser($viewer) - ->setPolicyObject($product); - - if ($product->getIsActive()) { - $header->setStatus('fa-check', 'bluegrey', pht('Active')); - } else { - $header->setStatus('fa-ban', 'dark', pht('Inactive')); - } - - $actions = id(new PhabricatorActionListView()) - ->setUser($viewer) - ->setObject($product); - - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $product, - PhabricatorPolicyCapability::CAN_EDIT); - - $edit_uri = $this->getApplicationURI("product/{$id}/edit/"); - $history_uri = $this->getApplicationURI("product/{$id}/history/"); - - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Edit Product')) - ->setHref($edit_uri) - ->setIcon('fa-pencil') - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit)); - - if ($product->getIsActive()) { - $status_name = pht('Deactivate Product'); - $status_href = "product/{$id}/action/deactivate/"; - $status_icon = 'fa-times'; - } else { - $status_name = pht('Reactivate Product'); - $status_href = "product/{$id}/action/activate/"; - $status_icon = 'fa-plus-circle-o'; - } - - $actions->addAction( - id(new PhabricatorActionView()) - ->setName($status_name) - ->setHref($this->getApplicationURI($status_href)) - ->setIcon($status_icon) - ->setDisabled(!$can_edit) - ->setWorkflow(true)); - - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('View History')) - ->setHref($history_uri) - ->setIcon('fa-list')); - - $properties = id(new PHUIPropertyListView()) - ->setUser($viewer) - ->setObject($product); - - $properties->addProperty( - pht('Repository'), - $product->getRepository()->getName()); - - $properties->setActionList($actions); - - $pushers = $product->getPushers(); - if ($pushers) { - $properties->addProperty( - pht('Pushers'), - $viewer->renderHandleList($pushers)); - } - - return id(new PHUIObjectBoxView()) - ->setHeader($header) - ->addPropertyList($properties); - } - -} diff --git a/src/applications/releeph/controller/request/ReleephRequestActionController.php b/src/applications/releeph/controller/request/ReleephRequestActionController.php deleted file mode 100644 index 1a53b08b8c..0000000000 --- a/src/applications/releeph/controller/request/ReleephRequestActionController.php +++ /dev/null @@ -1,122 +0,0 @@ -getURIData('action'); - $id = $request->getURIData('requestID'); - $viewer = $request->getViewer(); - - $request->validateCSRF(); - - $pull = id(new ReleephRequestQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->executeOne(); - if (!$pull) { - return new Aphront404Response(); - } - - $branch = $pull->getBranch(); - $product = $branch->getProduct(); - $origin_uri = '/'.$pull->getMonogram(); - - $editor = id(new ReleephRequestTransactionalEditor()) - ->setActor($viewer) - ->setContinueOnNoEffect(true) - ->setContentSourceFromRequest($request); - - $xactions = array(); - - switch ($action) { - case 'want': - case 'pass': - static $action_map = array( - 'want' => ReleephRequest::INTENT_WANT, - 'pass' => ReleephRequest::INTENT_PASS, - ); - $intent = $action_map[$action]; - $xactions[] = id(new ReleephRequestTransaction()) - ->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT) - ->setMetadataValue( - 'isAuthoritative', - $product->isAuthoritative($viewer)) - ->setNewValue($intent); - break; - - case 'mark-manually-picked': - case 'mark-manually-reverted': - if ( - $pull->getRequestUserPHID() === $viewer->getPHID() || - $product->isAuthoritative($viewer)) { - - // We're all good! - } else { - throw new Exception( - pht( - "Bug! Only pushers or the requestor can manually change a ". - "request's in-branch status!")); - } - - if ($action === 'mark-manually-picked') { - $in_branch = 1; - $intent = ReleephRequest::INTENT_WANT; - } else { - $in_branch = 0; - $intent = ReleephRequest::INTENT_PASS; - } - - $xactions[] = id(new ReleephRequestTransaction()) - ->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT) - ->setMetadataValue('isManual', true) - ->setMetadataValue('isAuthoritative', true) - ->setNewValue($intent); - - $xactions[] = id(new ReleephRequestTransaction()) - ->setTransactionType(ReleephRequestTransaction::TYPE_MANUAL_IN_BRANCH) - ->setNewValue($in_branch); - - break; - - default: - throw new Exception( - pht('Unknown or unimplemented action %s.', $action)); - } - - $editor->applyTransactions($pull, $xactions); - - if ($request->getBool('render')) { - $field_list = PhabricatorCustomField::getObjectFields( - $pull, - PhabricatorCustomField::ROLE_VIEW); - - $field_list - ->setViewer($viewer) - ->readFieldsFromStorage($pull); - - // TODO: This should be more modern and general. - $engine = id(new PhabricatorMarkupEngine()) - ->setViewer($viewer); - foreach ($field_list->getFields() as $field) { - if ($field->shouldMarkup()) { - $field->setMarkupEngine($engine); - } - } - $engine->process(); - - $pull_box = id(new ReleephRequestView()) - ->setUser($viewer) - ->setCustomFields($field_list) - ->setPullRequest($pull) - ->setIsListView(true); - - return id(new AphrontAjaxResponse())->setContent( - array( - 'markup' => hsprintf('%s', $pull_box), - )); - } - - return id(new AphrontRedirectResponse())->setURI($origin_uri); - } -} diff --git a/src/applications/releeph/controller/request/ReleephRequestCommentController.php b/src/applications/releeph/controller/request/ReleephRequestCommentController.php deleted file mode 100644 index 96500e8bc5..0000000000 --- a/src/applications/releeph/controller/request/ReleephRequestCommentController.php +++ /dev/null @@ -1,64 +0,0 @@ -getURIData('requestID'); - $viewer = $request->getViewer(); - - if (!$request->isFormPost()) { - return new Aphront400Response(); - } - - $pull = id(new ReleephRequestQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->executeOne(); - if (!$pull) { - return new Aphront404Response(); - } - - $is_preview = $request->isPreviewRequest(); - $draft = PhabricatorDraft::buildFromRequest($request); - - $view_uri = $this->getApplicationURI('/'.$pull->getMonogram()); - - $xactions = array(); - $xactions[] = id(new ReleephRequestTransaction()) - ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) - ->attachComment( - id(new ReleephRequestTransactionComment()) - ->setContent($request->getStr('comment'))); - - $editor = id(new ReleephRequestTransactionalEditor()) - ->setActor($viewer) - ->setContinueOnNoEffect($request->isContinueRequest()) - ->setContentSourceFromRequest($request) - ->setIsPreview($is_preview); - - try { - $xactions = $editor->applyTransactions($pull, $xactions); - } catch (PhabricatorApplicationTransactionNoEffectException $ex) { - return id(new PhabricatorApplicationTransactionNoEffectResponse()) - ->setCancelURI($view_uri) - ->setException($ex); - } - - if ($draft) { - $draft->replaceOrDelete(); - } - - if ($request->isAjax() && $is_preview) { - return id(new PhabricatorApplicationTransactionResponse()) - ->setObject($pull) - ->setViewer($viewer) - ->setTransactions($xactions) - ->setIsPreview($is_preview); - } else { - return id(new AphrontRedirectResponse()) - ->setURI($view_uri); - } - } - -} diff --git a/src/applications/releeph/controller/request/ReleephRequestController.php b/src/applications/releeph/controller/request/ReleephRequestController.php deleted file mode 100644 index 98b0bbab60..0000000000 --- a/src/applications/releeph/controller/request/ReleephRequestController.php +++ /dev/null @@ -1,3 +0,0 @@ -getURIData('diffRevID'); - $viewer = $request->getViewer(); - - $diff_rev = id(new DifferentialRevisionQuery()) - ->setViewer($viewer) - ->withIDs(array($revision_id)) - ->executeOne(); - if (!$diff_rev) { - return new Aphront404Response(); - } - $this->revision = $diff_rev; - - $repository = $this->revision->getRepository(); - - $projects = id(new ReleephProject())->loadAllWhere( - 'repositoryPHID = %s AND isActive = 1', - $repository->getPHID()); - if (!$projects) { - throw new Exception( - pht( - "%s belongs to the '%s' repository, ". - "which is not part of any Releeph project!", - 'D'.$this->revision->getID(), - $repository->getMonogram())); - } - - $branches = id(new ReleephBranch())->loadAllWhere( - 'releephProjectID IN (%Ld) AND isActive = 1', - mpull($projects, 'getID')); - if (!$branches) { - throw new Exception(pht( - '%s could be in the Releeph project(s) %s, '. - 'but this project / none of these projects have open branches.', - 'D'.$this->revision->getID(), - implode(', ', mpull($projects, 'getName')))); - } - - if (count($branches) === 1) { - return id(new AphrontRedirectResponse()) - ->setURI($this->buildReleephRequestURI(head($branches))); - } - - $projects = msort( - mpull($projects, null, 'getID'), - 'getName'); - - $branch_groups = mgroup($branches, 'getReleephProjectID'); - - require_celerity_resource('releeph-request-differential-create-dialog'); - $dialog = id(new AphrontDialogView()) - ->setUser($viewer) - ->setTitle(pht('Choose Releeph Branch')) - ->setClass('releeph-request-differential-create-dialog') - ->addCancelButton('/D'.$request->getStr('D')); - - $dialog->appendChild( - pht( - 'This differential revision changes code that is associated '. - 'with multiple Releeph branches. Please select the branch '. - 'where you would like this code to be picked.')); - - foreach ($branch_groups as $project_id => $branches) { - $project = idx($projects, $project_id); - $dialog->appendChild( - phutil_tag( - 'h1', - array(), - $project->getName())); - $branches = msort($branches, 'getBasename'); - foreach ($branches as $branch) { - $uri = $this->buildReleephRequestURI($branch); - $dialog->appendChild( - phutil_tag( - 'a', - array( - 'href' => $uri, - ), - $branch->getDisplayNameWithDetail())); - } - } - - return id(new AphrontDialogResponse()) - ->setDialog($dialog); - } - - private function buildReleephRequestURI(ReleephBranch $branch) { - $uri = $branch->getURI('request/'); - return id(new PhutilURI($uri)) - ->replaceQueryParam('D', $this->revision->getID()); - } - -} diff --git a/src/applications/releeph/controller/request/ReleephRequestEditController.php b/src/applications/releeph/controller/request/ReleephRequestEditController.php deleted file mode 100644 index af7adc2c83..0000000000 --- a/src/applications/releeph/controller/request/ReleephRequestEditController.php +++ /dev/null @@ -1,320 +0,0 @@ -getURIData('action'); - $request_id = $request->getURIData('requestID'); - $branch_id = $request->getURIData('branchID'); - $viewer = $request->getViewer(); - - if ($request_id) { - $pull = id(new ReleephRequestQuery()) - ->setViewer($viewer) - ->withIDs(array($request_id)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); - if (!$pull) { - return new Aphront404Response(); - } - - $branch = $pull->getBranch(); - - $is_edit = true; - } else { - $branch = id(new ReleephBranchQuery()) - ->setViewer($viewer) - ->withIDs(array($branch_id)) - ->executeOne(); - if (!$branch) { - return new Aphront404Response(); - } - - $pull = id(new ReleephRequest()) - ->setRequestUserPHID($viewer->getPHID()) - ->setBranchID($branch->getID()) - ->setInBranch(0) - ->attachBranch($branch); - - $is_edit = false; - } - $this->setBranch($branch); - - $product = $branch->getProduct(); - - $request_identifier = $request->getStr('requestIdentifierRaw'); - $e_request_identifier = true; - - // Load all the ReleephFieldSpecifications - $selector = $branch->getProduct()->getReleephFieldSelector(); - $fields = $selector->getFieldSpecifications(); - foreach ($fields as $field) { - $field - ->setReleephProject($product) - ->setReleephBranch($branch) - ->setReleephRequest($pull); - } - - $field_list = PhabricatorCustomField::getObjectFields( - $pull, - PhabricatorCustomField::ROLE_EDIT); - foreach ($field_list->getFields() as $field) { - $field - ->setReleephProject($product) - ->setReleephBranch($branch) - ->setReleephRequest($pull); - } - $field_list->readFieldsFromStorage($pull); - - - if ($branch_id) { - $cancel_uri = $this->getApplicationURI('branch/'.$branch_id.'/'); - } else { - $cancel_uri = '/'.$pull->getMonogram(); - } - - // Make edits - $errors = array(); - if ($request->isFormPost()) { - $xactions = array(); - - // The commit-identifier being requested... - if (!$is_edit) { - if ($request_identifier === - ReleephRequestTypeaheadControl::PLACEHOLDER) { - - $errors[] = pht('No commit ID was provided.'); - $e_request_identifier = pht('Required'); - } else { - $pr_commit = null; - $finder = id(new ReleephCommitFinder()) - ->setUser($viewer) - ->setReleephProject($product); - try { - $pr_commit = $finder->fromPartial($request_identifier); - } catch (Exception $e) { - $e_request_identifier = pht('Invalid'); - $errors[] = pht( - 'Request %s is probably not a valid commit.', - $request_identifier); - $errors[] = $e->getMessage(); - } - - if (!$errors) { - $object_phid = $finder->getRequestedObjectPHID(); - if (!$object_phid) { - $object_phid = $pr_commit->getPHID(); - } - - $pull->setRequestedObjectPHID($object_phid); - } - } - - if (!$errors) { - $existing = id(new ReleephRequest()) - ->loadOneWhere('requestCommitPHID = %s AND branchID = %d', - $pr_commit->getPHID(), $branch->getID()); - if ($existing) { - return id(new AphrontRedirectResponse()) - ->setURI('/releeph/request/edit/'.$existing->getID(). - '?existing=1'); - } - - $xactions[] = id(new ReleephRequestTransaction()) - ->setTransactionType(ReleephRequestTransaction::TYPE_REQUEST) - ->setNewValue($pr_commit->getPHID()); - - $xactions[] = id(new ReleephRequestTransaction()) - ->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT) - // To help hide these implicit intents... - ->setMetadataValue('isRQCreate', true) - ->setMetadataValue('userPHID', $viewer->getPHID()) - ->setMetadataValue( - 'isAuthoritative', - $product->isAuthoritative($viewer)) - ->setNewValue(ReleephRequest::INTENT_WANT); - } - } - - // TODO: This should happen implicitly while building transactions - // instead. - foreach ($field_list->getFields() as $field) { - $field->readValueFromRequest($request); - } - - if (!$errors) { - foreach ($fields as $field) { - if ($field->isEditable()) { - try { - $data = $request->getRequestData(); - $value = idx($data, $field->getRequiredStorageKey()); - $field->validate($value); - $xactions[] = id(new ReleephRequestTransaction()) - ->setTransactionType(ReleephRequestTransaction::TYPE_EDIT_FIELD) - ->setMetadataValue('fieldClass', get_class($field)) - ->setNewValue($value); - } catch (ReleephFieldParseException $ex) { - $errors[] = $ex->getMessage(); - } - } - } - } - - if (!$errors) { - $editor = id(new ReleephRequestTransactionalEditor()) - ->setActor($viewer) - ->setContinueOnNoEffect(true) - ->setContentSourceFromRequest($request); - $editor->applyTransactions($pull, $xactions); - return id(new AphrontRedirectResponse())->setURI($cancel_uri); - } - } - - $handle_phids = array( - $pull->getRequestUserPHID(), - $pull->getRequestCommitPHID(), - ); - $handle_phids = array_filter($handle_phids); - if ($handle_phids) { - $handles = id(new PhabricatorHandleQuery()) - ->setViewer($viewer) - ->withPHIDs($handle_phids) - ->execute(); - } else { - $handles = array(); - } - - $age_string = ''; - if ($is_edit) { - $age_string = phutil_format_relative_time( - time() - $pull->getDateCreated()).' ago'; - } - - // Warn the user if we've been redirected here because we tried to - // re-request something. - $notice_view = null; - if ($request->getInt('existing')) { - $notice_messages = array( - pht('You are editing an existing pick request!'), - pht( - 'Requested %s by %s', - $age_string, - $handles[$pull->getRequestUserPHID()]->renderLink()), - ); - $notice_view = id(new PHUIInfoView()) - ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) - ->setErrors($notice_messages); - } - - $form = id(new AphrontFormView()) - ->setUser($viewer); - - if ($is_edit) { - $form - ->appendChild( - id(new AphrontFormMarkupControl()) - ->setLabel(pht('Original Commit')) - ->setValue( - $handles[$pull->getRequestCommitPHID()]->renderLink())) - ->appendChild( - id(new AphrontFormMarkupControl()) - ->setLabel(pht('Requestor')) - ->setValue(hsprintf( - '%s %s', - $handles[$pull->getRequestUserPHID()]->renderLink(), - $age_string))); - } else { - $origin = null; - $diff_rev_id = $request->getStr('D'); - if ($diff_rev_id) { - $diff_rev = id(new DifferentialRevisionQuery()) - ->setViewer($viewer) - ->withIDs(array($diff_rev_id)) - ->executeOne(); - $origin = '/D'.$diff_rev->getID(); - $title = sprintf( - 'D%d: %s', - $diff_rev_id, - $diff_rev->getTitle()); - $form - ->addHiddenInput('requestIdentifierRaw', 'D'.$diff_rev_id) - ->appendChild( - id(new AphrontFormStaticControl()) - ->setLabel(pht('Diff')) - ->setValue($title)); - } else { - $origin = $branch->getURI(); - $repo = $product->getRepository(); - $branch_cut_point = id(new PhabricatorRepositoryCommit()) - ->loadOneWhere( - 'phid = %s', - $branch->getCutPointCommitPHID()); - $form->appendChild( - id(new ReleephRequestTypeaheadControl()) - ->setName('requestIdentifierRaw') - ->setLabel(pht('Commit ID')) - ->setRepo($repo) - ->setValue($request_identifier) - ->setError($e_request_identifier) - ->setStartTime($branch_cut_point->getEpoch()) - ->setCaption( - pht( - 'Start typing to autocomplete on commit title, '. - 'or give a Phabricator commit identifier like rFOO1234.'))); - } - } - - $field_list->appendFieldsToForm($form); - - $crumbs = $this->buildApplicationCrumbs(); - - if ($is_edit) { - $title = pht('Edit Pull Request'); - $submit_name = pht('Save'); - $header_icon = 'fa-pencil'; - - $crumbs->addTextCrumb($pull->getMonogram(), '/'.$pull->getMonogram()); - $crumbs->addTextCrumb(pht('Edit')); - } else { - $title = pht('Create Pull Request'); - $submit_name = pht('Create Pull Request'); - $header_icon = 'fa-plus-square'; - - $crumbs->addTextCrumb(pht('New Pull Request')); - } - - $form->appendChild( - id(new AphrontFormSubmitControl()) - ->addCancelButton($cancel_uri, pht('Cancel')) - ->setValue($submit_name)); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Request')) - ->setFormErrors($errors) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($form); - - $crumbs->setBorder(true); - - $header = id(new PHUIHeaderView()) - ->setHeader($title) - ->setHeaderIcon($header_icon); - - $view = id(new PHUITwoColumnView()) - ->setHeader($header) - ->setFooter(array( - $notice_view, - $box, - )); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($view); - - } -} diff --git a/src/applications/releeph/controller/request/ReleephRequestTypeaheadController.php b/src/applications/releeph/controller/request/ReleephRequestTypeaheadController.php deleted file mode 100644 index 3a93de7453..0000000000 --- a/src/applications/releeph/controller/request/ReleephRequestTypeaheadController.php +++ /dev/null @@ -1,90 +0,0 @@ -getStr('q'); - $repo_id = $request->getInt('repo'); - $since = $request->getInt('since'); - $limit = $request->getInt('limit'); - - $now = time(); - $data = array(); - - // Dummy instances used for getting connections, table names, etc. - $pr_commit = new PhabricatorRepositoryCommit(); - $pr_commit_data = new PhabricatorRepositoryCommitData(); - - $conn = $pr_commit->establishConnection('r'); - - $rows = queryfx_all( - $conn, - 'SELECT - rc.phid as commitPHID, - rc.authorPHID, - rcd.authorName, - SUBSTRING(rcd.commitMessage, 1, 100) AS shortMessage, - rc.commitIdentifier, - rc.epoch - FROM %T rc - INNER JOIN %T rcd ON rcd.commitID = rc.id - WHERE repositoryID = %d - AND rc.epoch >= %d - AND ( - rcd.commitMessage LIKE %~ - OR - rc.commitIdentifier LIKE %~ - ) - ORDER BY rc.epoch DESC - LIMIT %d', - $pr_commit->getTableName(), - $pr_commit_data->getTableName(), - $repo_id, - $since, - $query, - $query, - $limit); - - foreach ($rows as $row) { - $full_commit_id = $row['commitIdentifier']; - $short_commit_id = substr($full_commit_id, 0, 12); - $first_line = $this->getFirstLine($row['shortMessage']); - $data[] = array( - $full_commit_id, - $short_commit_id, - $row['authorName'], - phutil_format_relative_time($now - $row['epoch']), - $first_line, - ); - } - - return id(new AphrontAjaxResponse()) - ->setContent($data); - } - - /** - * Split either at the first new line, or a bunch of dashes. - * - * Really just a legacy from old Releeph Daemon commit messages where I used - * to say: - * - * Commit of FOO for BAR - * ------------ - * This does X Y Z - * - */ - private function getFirstLine($commit_message_fragment) { - static $separators = array('-------', "\n"); - $string = ltrim($commit_message_fragment); - $first_line = $string; - foreach ($separators as $separator) { - if ($pos = strpos($string, $separator)) { - $first_line = substr($string, 0, $pos); - break; - } - } - return $first_line; - } - -} diff --git a/src/applications/releeph/controller/request/ReleephRequestViewController.php b/src/applications/releeph/controller/request/ReleephRequestViewController.php deleted file mode 100644 index c404e31579..0000000000 --- a/src/applications/releeph/controller/request/ReleephRequestViewController.php +++ /dev/null @@ -1,101 +0,0 @@ -getURIData('requestID'); - $viewer = $request->getViewer(); - - $pull = id(new ReleephRequestQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->executeOne(); - if (!$pull) { - return new Aphront404Response(); - } - $this->setBranch($pull->getBranch()); - - // Redirect older URIs to new "Y" URIs. - // TODO: Get rid of this eventually. - $actual_path = $request->getRequestURI()->getPath(); - $expect_path = '/'.$pull->getMonogram(); - if ($actual_path != $expect_path) { - return id(new AphrontRedirectResponse())->setURI($expect_path); - } - - // TODO: Break this 1:1 stuff? - $branch = $pull->getBranch(); - - $field_list = PhabricatorCustomField::getObjectFields( - $pull, - PhabricatorCustomField::ROLE_VIEW); - - $field_list - ->setViewer($viewer) - ->readFieldsFromStorage($pull); - - // TODO: This should be more modern and general. - $engine = id(new PhabricatorMarkupEngine()) - ->setViewer($viewer); - foreach ($field_list->getFields() as $field) { - if ($field->shouldMarkup()) { - $field->setMarkupEngine($engine); - } - } - $engine->process(); - - $pull_box = id(new ReleephRequestView()) - ->setUser($viewer) - ->setCustomFields($field_list) - ->setPullRequest($pull); - - $timeline = $this->buildTransactionTimeline( - $pull, - new ReleephRequestTransactionQuery()); - - $add_comment_header = pht('Plea or Yield'); - - $draft = PhabricatorDraft::newFromUserAndKey( - $viewer, - $pull->getPHID()); - - $title = hsprintf( - '%s %s', - $pull->getMonogram(), - $pull->getSummaryForDisplay()); - - $add_comment_form = id(new PhabricatorApplicationTransactionCommentView()) - ->setUser($viewer) - ->setObjectPHID($pull->getPHID()) - ->setDraft($draft) - ->setHeaderText($add_comment_header) - ->setAction($this->getApplicationURI( - '/request/comment/'.$pull->getID().'/')) - ->setSubmitButtonName(pht('Comment')); - - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb($pull->getMonogram(), '/'.$pull->getMonogram()); - $crumbs->setBorder(true); - - $header = id(new PHUIHeaderView()) - ->setHeader($title) - ->setHeaderIcon('fa-flag-checkered'); - - $view = id(new PHUITwoColumnView()) - ->setHeader($header) - ->setFooter(array( - $pull_box, - $timeline, - $add_comment_form, - )); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild($view); - - } - - -} diff --git a/src/applications/releeph/differential/DifferentialReleephRequestFieldSpecification.php b/src/applications/releeph/differential/DifferentialReleephRequestFieldSpecification.php deleted file mode 100644 index 56d371f1d6..0000000000 --- a/src/applications/releeph/differential/DifferentialReleephRequestFieldSpecification.php +++ /dev/null @@ -1,384 +0,0 @@ -" headers in commits created by - * arc-releeph so that RQs committed by arc-releeph have real - * PhabricatorRepositoryCommits associated with them (instead of just the SHA - * of the commit, as seen by the pusher). - * - * 2: If requestors want to commit directly to their release branch, they can - * use this header to (i) indicate on a differential revision that this - * differential revision is for the release branch, and (ii) when they land - * their diff on to the release branch manually, the ReleephRequest is - * automatically updated (instead of having to use the "Mark Manually Picked" - * button.) - * - */ -final class DifferentialReleephRequestFieldSpecification extends Phobject { - - // TODO: This class is essentially dead right now, see T2222. - - const ACTION_PICKS = 'picks'; - const ACTION_REVERTS = 'reverts'; - - private $releephAction; - private $releephPHIDs = array(); - - public function getStorageKey() { - return 'releeph:actions'; - } - - public function getValueForStorage() { - return json_encode(array( - 'releephAction' => $this->releephAction, - 'releephPHIDs' => $this->releephPHIDs, - )); - } - - public function setValueFromStorage($json) { - if ($json) { - $dict = phutil_json_decode($json); - $this->releephAction = idx($dict, 'releephAction'); - $this->releephPHIDs = idx($dict, 'releephPHIDs'); - } - return $this; - } - - public function shouldAppearOnRevisionView() { - return true; - } - - public function renderLabelForRevisionView() { - return pht('Releeph'); - } - - public function getRequiredHandlePHIDs() { - return mpull($this->loadReleephRequests(), 'getPHID'); - } - - public function renderValueForRevisionView() { - static $tense; - - if ($tense === null) { - $tense = array( - self::ACTION_PICKS => array( - 'future' => pht('Will pick'), - 'past' => pht('Picked'), - ), - self::ACTION_REVERTS => array( - 'future' => pht('Will revert'), - 'past' => pht('Reverted'), - ), - ); - } - - $releeph_requests = $this->loadReleephRequests(); - if (!$releeph_requests) { - return null; - } - - if ($this->getRevision()->isClosed()) { - $verb = $tense[$this->releephAction]['past']; - } else { - $verb = $tense[$this->releephAction]['future']; - } - - $parts = hsprintf('%s...', $verb); - foreach ($releeph_requests as $releeph_request) { - $parts->appendHTML(phutil_tag('br')); - $parts->appendHTML( - $this->getHandle($releeph_request->getPHID())->renderLink()); - } - - return $parts; - } - - public function shouldAppearOnCommitMessage() { - return true; - } - - public function getCommitMessageKey() { - return 'releephActions'; - } - - public function setValueFromParsedCommitMessage($dict) { - $this->releephAction = $dict['releephAction']; - $this->releephPHIDs = $dict['releephPHIDs']; - return $this; - } - - public function renderValueForCommitMessage($is_edit) { - $releeph_requests = $this->loadReleephRequests(); - if (!$releeph_requests) { - return null; - } - - $parts = array($this->releephAction); - foreach ($releeph_requests as $releeph_request) { - $parts[] = 'RQ'.$releeph_request->getID(); - } - - return implode(' ', $parts); - } - - /** - * Releeph fields should look like: - * - * Releeph: picks RQ1 RQ2, RQ3 - * Releeph: reverts RQ1 - */ - public function parseValueFromCommitMessage($value) { - /** - * Releeph commit messages look like this (but with more blank lines, - * omitted here): - * - * Make CaptainHaddock more reasonable - * Releeph: picks RQ1 - * Requested By: edward - * Approved By: edward (requestor) - * Request Reason: x - * Summary: Make the Haddock implementation more reasonable. - * Test Plan: none - * Reviewers: user1 - * - * Some of these fields are recognized by Differential (e.g. "Requested - * By"). They are folded up into the "Releeph" field, parsed by this - * class. As such $value includes more than just the first-line: - * - * "picks RQ1\n\nRequested By: edward\n\nApproved By: edward (requestor)" - * - * To hack around this, just consider the first line of $value when - * determining what Releeph actions the parsed commit is performing. - */ - $first_line = head(array_filter(explode("\n", $value))); - - $tokens = preg_split('/\s*,?\s+/', $first_line); - $raw_action = array_shift($tokens); - $action = strtolower($raw_action); - - if (!$action) { - return null; - } - - switch ($action) { - case self::ACTION_REVERTS: - case self::ACTION_PICKS: - break; - - default: - throw new DifferentialFieldParseException( - pht( - "Commit message contains unknown Releeph action '%s'!", - $raw_action)); - break; - } - - $releeph_requests = array(); - foreach ($tokens as $token) { - $match = array(); - if (!preg_match('/^(?:RQ)?(\d+)$/i', $token, $match)) { - $label = $this->renderLabelForCommitMessage(); - throw new DifferentialFieldParseException( - pht( - "Commit message contains unparseable ". - "Releeph request token '%s'!", - $token)); - } - - $id = (int)$match[1]; - $releeph_request = id(new ReleephRequest())->load($id); - - if (!$releeph_request) { - throw new DifferentialFieldParseException( - pht( - 'Commit message references non existent Releeph request: %s!', - $value)); - } - - $releeph_requests[] = $releeph_request; - } - - if (count($releeph_requests) > 1) { - $rqs_seen = array(); - $groups = array(); - foreach ($releeph_requests as $releeph_request) { - $releeph_branch = $releeph_request->getBranch(); - $branch_name = $releeph_branch->getName(); - $rq_id = 'RQ'.$releeph_request->getID(); - - if (idx($rqs_seen, $rq_id)) { - throw new DifferentialFieldParseException( - pht( - 'Commit message refers to %s multiple times!', - $rq_id)); - } - $rqs_seen[$rq_id] = true; - - if (!isset($groups[$branch_name])) { - $groups[$branch_name] = array(); - } - $groups[$branch_name][] = $rq_id; - } - - if (count($groups) > 1) { - $lists = array(); - foreach ($groups as $branch_name => $rq_ids) { - $lists[] = implode(', ', $rq_ids).' in '.$branch_name; - } - throw new DifferentialFieldParseException( - pht( - 'Commit message references multiple Releeph requests, '. - 'but the requests are in different branches: %s', - implode('; ', $lists))); - } - } - - $phids = mpull($releeph_requests, 'getPHID'); - - $data = array( - 'releephAction' => $action, - 'releephPHIDs' => $phids, - ); - return $data; - } - - public function renderLabelForCommitMessage() { - return pht('Releeph'); - } - - public function shouldAppearOnCommitMessageTemplate() { - return false; - } - - public function didParseCommit( - PhabricatorRepository $repo, - PhabricatorRepositoryCommit $commit, - PhabricatorRepositoryCommitData $data) { - - // NOTE: This is currently dead code. See T2222. - - $releeph_requests = $this->loadReleephRequests(); - - if (!$releeph_requests) { - return; - } - - $releeph_branch = head($releeph_requests)->getBranch(); - if (!$this->isCommitOnBranch($repo, $commit, $releeph_branch)) { - return; - } - - foreach ($releeph_requests as $releeph_request) { - if ($this->releephAction === self::ACTION_PICKS) { - $action = 'pick'; - } else { - $action = 'revert'; - } - - $actor_phid = coalesce( - $data->getCommitDetail('committerPHID'), - $data->getCommitDetail('authorPHID')); - - $actor = id(new PhabricatorUser()) - ->loadOneWhere('phid = %s', $actor_phid); - - $xactions = array(); - - $xactions[] = id(new ReleephRequestTransaction()) - ->setTransactionType(ReleephRequestTransaction::TYPE_DISCOVERY) - ->setMetadataValue('action', $action) - ->setMetadataValue('authorPHID', - $data->getCommitDetail('authorPHID')) - ->setMetadataValue('committerPHID', - $data->getCommitDetail('committerPHID')) - ->setNewValue($commit->getPHID()); - - $editor = id(new ReleephRequestTransactionalEditor()) - ->setActor($actor) - ->setContinueOnNoEffect(true) - ->setContentSource( - PhabricatorContentSource::newForSource( - PhabricatorUnknownContentSource::SOURCECONST)); - - $editor->applyTransactions($releeph_request, $xactions); - } - } - - private function loadReleephRequests() { - if (!$this->releephPHIDs) { - return array(); - } - - return id(new ReleephRequestQuery()) - ->setViewer($this->getViewer()) - ->withPHIDs($this->releephPHIDs) - ->execute(); - } - - private function isCommitOnBranch( - PhabricatorRepository $repo, - PhabricatorRepositoryCommit $commit, - ReleephBranch $releeph_branch) { - - switch ($repo->getVersionControlSystem()) { - case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: - list($output) = $repo->execxLocalCommand( - 'branch --all --no-color --contains %s', - $commit->getCommitIdentifier()); - - $remote_prefix = 'remotes/origin/'; - $branches = array(); - foreach (array_filter(explode("\n", $output)) as $line) { - $tokens = explode(' ', $line); - $ref = last($tokens); - if (strncmp($ref, $remote_prefix, strlen($remote_prefix)) === 0) { - $branch = substr($ref, strlen($remote_prefix)); - $branches[$branch] = $branch; - } - } - - return idx($branches, $releeph_branch->getName()); - break; - - case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: - $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest( - DiffusionRequest::newFromDictionary(array( - 'user' => $this->getUser(), - 'repository' => $repo, - 'commit' => $commit->getCommitIdentifier(), - ))); - $path_changes = $change_query->loadChanges(); - $commit_paths = mpull($path_changes, 'getPath'); - - $branch_path = $releeph_branch->getName(); - - $in_branch = array(); - $ex_branch = array(); - foreach ($commit_paths as $path) { - if (strncmp($path, $branch_path, strlen($branch_path)) === 0) { - $in_branch[] = $path; - } else { - $ex_branch[] = $path; - } - } - - if ($in_branch && $ex_branch) { - $error = pht( - 'CONFUSION: commit %s in %s contains %d path change(s) that were '. - 'part of a Releeph branch, but also has %d path change(s) not '. - 'part of a Releeph branch!', - $commit->getCommitIdentifier(), - $repo->getDisplayName(), - count($in_branch), - count($ex_branch)); - phlog($error); - } - - return !empty($in_branch); - break; - } - } - -} diff --git a/src/applications/releeph/editor/ReleephBranchEditor.php b/src/applications/releeph/editor/ReleephBranchEditor.php deleted file mode 100644 index 0bc084223b..0000000000 --- a/src/applications/releeph/editor/ReleephBranchEditor.php +++ /dev/null @@ -1,86 +0,0 @@ -releephProject = $rp; - return $this; - } - - public function setReleephBranch(ReleephBranch $branch) { - $this->releephBranch = $branch; - return $this; - } - - public function newBranchFromCommit( - PhabricatorRepositoryCommit $cut_point, - $branch_date, - $symbolic_name = null) { - - $template = $this->releephProject->getDetail('branchTemplate'); - if (!$template) { - $template = ReleephBranchTemplate::getRequiredDefaultTemplate(); - } - - $cut_point_handle = id(new PhabricatorHandleQuery()) - ->setViewer($this->requireActor()) - ->withPHIDs(array($cut_point->getPHID())) - ->executeOne(); - - list($name, $errors) = id(new ReleephBranchTemplate()) - ->setCommitHandle($cut_point_handle) - ->setBranchDate($branch_date) - ->setReleephProjectName($this->releephProject->getName()) - ->interpolate($template); - - $basename = last(explode('/', $name)); - - $table = id(new ReleephBranch()); - $transaction = $table->openTransaction(); - $branch = id(new ReleephBranch()) - ->setName($name) - ->setBasename($basename) - ->setReleephProjectID($this->releephProject->getID()) - ->setCreatedByUserPHID($this->requireActor()->getPHID()) - ->setCutPointCommitPHID($cut_point->getPHID()) - ->setIsActive(1) - ->setDetail('branchDate', $branch_date) - ->save(); - - /** - * Steal the symbolic name from any other branch that has it (in this - * project). - */ - if ($symbolic_name) { - $others = id(new ReleephBranch())->loadAllWhere( - 'releephProjectID = %d', - $this->releephProject->getID()); - foreach ($others as $other) { - if ($other->getSymbolicName() == $symbolic_name) { - $other - ->setSymbolicName(null) - ->save(); - } - } - $branch - ->setSymbolicName($symbolic_name) - ->save(); - } - - $table->saveTransaction(); - return $branch; - } - - // aka "close" and "reopen" - public function changeBranchAccess($is_active) { - $branch = $this->releephBranch; - - $branch - ->setIsActive((int)$is_active) - ->save(); - } - -} diff --git a/src/applications/releeph/editor/ReleephProductEditor.php b/src/applications/releeph/editor/ReleephProductEditor.php deleted file mode 100644 index 4c3d69c97b..0000000000 --- a/src/applications/releeph/editor/ReleephProductEditor.php +++ /dev/null @@ -1,61 +0,0 @@ -getTransactionType()) { - case ReleephProductTransaction::TYPE_ACTIVE: - return (int)$object->getIsActive(); - } - } - - protected function getCustomTransactionNewValue( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case ReleephProductTransaction::TYPE_ACTIVE: - return (int)$xaction->getNewValue(); - } - } - - protected function applyCustomInternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - $new = $xaction->getNewValue(); - - switch ($xaction->getTransactionType()) { - case ReleephProductTransaction::TYPE_ACTIVE: - $object->setIsActive($new); - break; - } - } - - protected function applyCustomExternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - return; - } - -} diff --git a/src/applications/releeph/editor/ReleephRequestTransactionalEditor.php b/src/applications/releeph/editor/ReleephRequestTransactionalEditor.php deleted file mode 100644 index da488d9c72..0000000000 --- a/src/applications/releeph/editor/ReleephRequestTransactionalEditor.php +++ /dev/null @@ -1,307 +0,0 @@ -getTransactionType()) { - case ReleephRequestTransaction::TYPE_REQUEST: - return $object->getRequestCommitPHID(); - - case ReleephRequestTransaction::TYPE_EDIT_FIELD: - $field = newv($xaction->getMetadataValue('fieldClass'), array()); - $value = $field->setReleephRequest($object)->getValue(); - return $value; - - case ReleephRequestTransaction::TYPE_USER_INTENT: - $user_phid = $xaction->getAuthorPHID(); - return idx($object->getUserIntents(), $user_phid); - - case ReleephRequestTransaction::TYPE_PICK_STATUS: - return (int)$object->getPickStatus(); - break; - - case ReleephRequestTransaction::TYPE_COMMIT: - return $object->getCommitIdentifier(); - - case ReleephRequestTransaction::TYPE_DISCOVERY: - return $object->getCommitPHID(); - - case ReleephRequestTransaction::TYPE_MANUAL_IN_BRANCH: - return $object->getInBranch(); - } - } - - protected function getCustomTransactionNewValue( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case ReleephRequestTransaction::TYPE_REQUEST: - case ReleephRequestTransaction::TYPE_USER_INTENT: - case ReleephRequestTransaction::TYPE_EDIT_FIELD: - case ReleephRequestTransaction::TYPE_PICK_STATUS: - case ReleephRequestTransaction::TYPE_COMMIT: - case ReleephRequestTransaction::TYPE_DISCOVERY: - case ReleephRequestTransaction::TYPE_MANUAL_IN_BRANCH: - return $xaction->getNewValue(); - } - } - - protected function applyCustomInternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - $new = $xaction->getNewValue(); - - switch ($xaction->getTransactionType()) { - case ReleephRequestTransaction::TYPE_REQUEST: - $object->setRequestCommitPHID($new); - break; - - case ReleephRequestTransaction::TYPE_USER_INTENT: - $user_phid = $xaction->getAuthorPHID(); - $intents = $object->getUserIntents(); - $intents[$user_phid] = $new; - $object->setUserIntents($intents); - break; - - case ReleephRequestTransaction::TYPE_EDIT_FIELD: - $field = newv($xaction->getMetadataValue('fieldClass'), array()); - $field - ->setReleephRequest($object) - ->setValue($new); - break; - - case ReleephRequestTransaction::TYPE_PICK_STATUS: - $object->setPickStatus($new); - break; - - case ReleephRequestTransaction::TYPE_COMMIT: - $this->setInBranchFromAction($object, $xaction); - $object->setCommitIdentifier($new); - break; - - case ReleephRequestTransaction::TYPE_DISCOVERY: - $this->setInBranchFromAction($object, $xaction); - $object->setCommitPHID($new); - break; - - case ReleephRequestTransaction::TYPE_MANUAL_IN_BRANCH: - $object->setInBranch((int)$new); - break; - } - } - - protected function applyCustomExternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - return; - } - - protected function filterTransactions( - PhabricatorLiskDAO $object, - array $xactions) { - - // Remove TYPE_DISCOVERY xactions that are the result of a reparse. - $previously_discovered_commits = array(); - $discovery_xactions = idx( - mgroup($xactions, 'getTransactionType'), - ReleephRequestTransaction::TYPE_DISCOVERY); - if ($discovery_xactions) { - $previous_xactions = id(new ReleephRequestTransactionQuery()) - ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withObjectPHIDs(array($object->getPHID())) - ->execute(); - - foreach ($previous_xactions as $xaction) { - if ($xaction->getTransactionType() === - ReleephRequestTransaction::TYPE_DISCOVERY) { - - $commit_phid = $xaction->getNewValue(); - $previously_discovered_commits[$commit_phid] = true; - } - } - } - - foreach ($xactions as $key => $xaction) { - if ($xaction->getTransactionType() === - ReleephRequestTransaction::TYPE_DISCOVERY && - idx($previously_discovered_commits, $xaction->getNewValue())) { - - unset($xactions[$key]); - } - } - - return parent::filterTransactions($object, $xactions); - } - - protected function shouldSendMail( - PhabricatorLiskDAO $object, - array $xactions) { - - // Avoid sending emails that only talk about commit discovery. - $types = array_unique(mpull($xactions, 'getTransactionType')); - if ($types === array(ReleephRequestTransaction::TYPE_DISCOVERY)) { - return false; - } - - // Don't email people when we discover that something picks or reverts OK. - if ($types === array(ReleephRequestTransaction::TYPE_PICK_STATUS)) { - if (!mfilter($xactions, 'isBoringPickStatus', true /* negate */)) { - // If we effectively call "isInterestingPickStatus" and get nothing... - return false; - } - } - - return true; - } - - protected function buildReplyHandler(PhabricatorLiskDAO $object) { - return id(new ReleephRequestReplyHandler()) - ->setActor($this->getActor()) - ->setMailReceiver($object); - } - - protected function getMailSubjectPrefix() { - return '[Releeph]'; - } - - protected function buildMailTemplate(PhabricatorLiskDAO $object) { - $id = $object->getID(); - $title = $object->getSummaryForDisplay(); - return id(new PhabricatorMetaMTAMail()) - ->setSubject("RQ{$id}: {$title}"); - } - - protected function getMailTo(PhabricatorLiskDAO $object) { - $to_phids = array(); - - $product = $object->getBranch()->getProduct(); - foreach ($product->getPushers() as $phid) { - $to_phids[] = $phid; - } - - foreach ($object->getUserIntents() as $phid => $intent) { - $to_phids[] = $phid; - } - - return $to_phids; - } - - protected function getMailCC(PhabricatorLiskDAO $object) { - return array(); - } - - protected function buildMailBody( - PhabricatorLiskDAO $object, - array $xactions) { - - $body = parent::buildMailBody($object, $xactions); - - $rq = $object; - $releeph_branch = $rq->getBranch(); - $releeph_project = $releeph_branch->getProduct(); - - /** - * If any of the events we are emailing about were about a pick failure - * (and/or a revert failure?), include pick failure instructions. - */ - $has_pick_failure = false; - foreach ($xactions as $xaction) { - if ($xaction->getTransactionType() === - ReleephRequestTransaction::TYPE_PICK_STATUS && - $xaction->getNewValue() === ReleephRequest::PICK_FAILED) { - - $has_pick_failure = true; - break; - } - } - if ($has_pick_failure) { - $instructions = $releeph_project->getDetail('pick_failure_instructions'); - if ($instructions) { - $body->addRemarkupSection( - pht('PICK FAILURE INSTRUCTIONS'), - $instructions); - } - } - - $name = sprintf('RQ%s: %s', $rq->getID(), $rq->getSummaryForDisplay()); - $body->addTextSection( - pht('RELEEPH REQUEST'), - $name."\n". - PhabricatorEnv::getProductionURI('/RQ'.$rq->getID())); - - $project_and_branch = sprintf( - '%s - %s', - $releeph_project->getName(), - $releeph_branch->getDisplayNameWithDetail()); - - $body->addTextSection( - pht('RELEEPH BRANCH'), - $project_and_branch."\n". - PhabricatorEnv::getProductionURI($releeph_branch->getURI())); - - return $body; - } - - private function setInBranchFromAction( - ReleephRequest $rq, - ReleephRequestTransaction $xaction) { - - $action = $xaction->getMetadataValue('action'); - switch ($action) { - case 'pick': - $rq->setInBranch(1); - break; - - case 'revert': - $rq->setInBranch(0); - break; - - default: - $id = $rq->getID(); - $type = $xaction->getTransactionType(); - $new = $xaction->getNewValue(); - phlog( - pht( - "Unknown discovery action '%s' for xaction of type %s ". - "with new value %s mentioning %s!", - $action, - $type, - $new, - 'RQ'.$id)); - break; - } - - return $this; - } - -} diff --git a/src/applications/releeph/field/exception/ReleephFieldParseException.php b/src/applications/releeph/field/exception/ReleephFieldParseException.php deleted file mode 100644 index fcb513df13..0000000000 --- a/src/applications/releeph/field/exception/ReleephFieldParseException.php +++ /dev/null @@ -1,12 +0,0 @@ -getName(); - parent::__construct("{$name}: {$message}"); - } - -} diff --git a/src/applications/releeph/field/selector/ReleephDefaultFieldSelector.php b/src/applications/releeph/field/selector/ReleephDefaultFieldSelector.php deleted file mode 100644 index af18bfd3d6..0000000000 --- a/src/applications/releeph/field/selector/ReleephDefaultFieldSelector.php +++ /dev/null @@ -1,71 +0,0 @@ - - } - - abstract public function getFieldSpecifications(); - - public function sortFieldsForCommitMessage(array $fields) { - assert_instances_of($fields, 'ReleephFieldSpecification'); - return $fields; - } - - protected static function selectFields(array $fields, array $classes) { - assert_instances_of($fields, 'ReleephFieldSpecification'); - - $map = array(); - foreach ($fields as $field) { - $map[get_class($field)] = $field; - } - - $result = array(); - foreach ($classes as $class) { - $field = idx($map, $class); - if (!$field) { - throw new Exception( - pht( - "Tried to select a in instance of '%s' but that field ". - "is not configured for this project!", - $class)); - } - - if (idx($result, $class)) { - throw new Exception( - pht( - "You have asked to select the field '%s' more than once!", - $class)); - } - - $result[$class] = $field; - } - - return $result; - } - -} diff --git a/src/applications/releeph/field/specification/ReleephAuthorFieldSpecification.php b/src/applications/releeph/field/specification/ReleephAuthorFieldSpecification.php deleted file mode 100644 index 577280fddb..0000000000 --- a/src/applications/releeph/field/specification/ReleephAuthorFieldSpecification.php +++ /dev/null @@ -1,33 +0,0 @@ -getReleephRequest(); - $commit = $pull->loadPhabricatorRepositoryCommit(); - if (!$commit) { - return array(); - } - - $author_phid = $commit->getAuthorPHID(); - if (!$author_phid) { - return array(); - } - - return array($author_phid); - } - - public function renderPropertyViewValue(array $handles) { - return $this->renderHandleList($handles); - } - -} diff --git a/src/applications/releeph/field/specification/ReleephBranchCommitFieldSpecification.php b/src/applications/releeph/field/specification/ReleephBranchCommitFieldSpecification.php deleted file mode 100644 index 74f747238d..0000000000 --- a/src/applications/releeph/field/specification/ReleephBranchCommitFieldSpecification.php +++ /dev/null @@ -1,28 +0,0 @@ -getReleephRequest(); - - if ($pull->getCommitPHID()) { - return array($pull->getCommitPHID()); - } - - return array(); - } - - public function renderPropertyViewValue(array $handles) { - return $this->renderHandleList($handles); - } - -} diff --git a/src/applications/releeph/field/specification/ReleephCommitMessageFieldSpecification.php b/src/applications/releeph/field/specification/ReleephCommitMessageFieldSpecification.php deleted file mode 100644 index 5122d1e7e1..0000000000 --- a/src/applications/releeph/field/specification/ReleephCommitMessageFieldSpecification.php +++ /dev/null @@ -1,54 +0,0 @@ -renderCommonLabel(); - } - - public function renderValueForCommitMessage() { - return $this->renderCommonValue( - DifferentialReleephRequestFieldSpecification::ACTION_PICKS); - } - - public function shouldAppearOnRevertMessage() { - return true; - } - - public function renderLabelForRevertMessage() { - return $this->renderCommonLabel(); - } - - public function renderValueForRevertMessage() { - return $this->renderCommonValue( - DifferentialReleephRequestFieldSpecification::ACTION_REVERTS); - } - - private function renderCommonLabel() { - return id(new DifferentialReleephRequestFieldSpecification()) - ->renderLabelForCommitMessage(); - } - - private function renderCommonValue($action) { - $rq = 'RQ'.$this->getReleephRequest()->getID(); - return "{$action} {$rq}"; - } - -} diff --git a/src/applications/releeph/field/specification/ReleephDependsOnFieldSpecification.php b/src/applications/releeph/field/specification/ReleephDependsOnFieldSpecification.php deleted file mode 100644 index acb5d7d11d..0000000000 --- a/src/applications/releeph/field/specification/ReleephDependsOnFieldSpecification.php +++ /dev/null @@ -1,34 +0,0 @@ -getDependentRevisionPHIDs(); - } - - public function renderPropertyViewValue(array $handles) { - return $this->renderHandleList($handles); - } - - private function getDependentRevisionPHIDs() { - $requested_object = $this->getObject()->getRequestedObjectPHID(); - if (!($requested_object instanceof DifferentialRevision)) { - return array(); - } - - $revision = $requested_object; - - return PhabricatorEdgeQuery::loadDestinationPHIDs( - $revision->getPHID(), - DifferentialRevisionDependsOnRevisionEdgeType::EDGECONST); - } -} diff --git a/src/applications/releeph/field/specification/ReleephDiffChurnFieldSpecification.php b/src/applications/releeph/field/specification/ReleephDiffChurnFieldSpecification.php deleted file mode 100644 index 3b472bbae7..0000000000 --- a/src/applications/releeph/field/specification/ReleephDiffChurnFieldSpecification.php +++ /dev/null @@ -1,90 +0,0 @@ -getObject()->getRequestedObject(); - if (!($requested_object instanceof DifferentialRevision)) { - return null; - } - $diff_rev = $requested_object; - - $xactions = id(new DifferentialTransactionQuery()) - ->setViewer($this->getViewer()) - ->withObjectPHIDs(array($diff_rev->getPHID())) - ->execute(); - - $rejections = 0; - $comments = 0; - $updates = 0; - - foreach ($xactions as $xaction) { - switch ($xaction->getTransactionType()) { - case PhabricatorTransactions::TYPE_COMMENT: - $comments++; - break; - case DifferentialRevisionUpdateTransaction::TRANSACTIONTYPE: - $updates++; - break; - case DifferentialTransaction::TYPE_ACTION: - switch ($xaction->getNewValue()) { - case DifferentialAction::ACTION_REJECT: - $rejections++; - break; - } - break; - } - } - - $points = - self::REJECTIONS_WEIGHT * $rejections + - self::COMMENTS_WEIGHT * $comments + - self::UPDATES_WEIGHT * $updates; - - if ($points === 0) { - $points = 0.15 * self::MAX_POINTS; - $blurb = pht('Silent diff'); - } else { - $parts = array(); - if ($rejections) { - $parts[] = pht('%s rejection(s)', new PhutilNumber($rejections)); - } - if ($comments) { - $parts[] = pht('%s comment(s)', new PhutilNumber($comments)); - } - if ($updates) { - $parts[] = pht('%s update(s)', new PhutilNumber($updates)); - } - - if (count($parts) === 0) { - $blurb = ''; - } else if (count($parts) === 1) { - $blurb = head($parts); - } else { - $last = array_pop($parts); - $blurb = pht('%s and %s', implode(', ', $parts), $last); - } - } - - return id(new AphrontProgressBarView()) - ->setValue($points) - ->setMax(self::MAX_POINTS) - ->setCaption($blurb) - ->render(); - } - -} diff --git a/src/applications/releeph/field/specification/ReleephDiffMessageFieldSpecification.php b/src/applications/releeph/field/specification/ReleephDiffMessageFieldSpecification.php deleted file mode 100644 index cde4ab95dd..0000000000 --- a/src/applications/releeph/field/specification/ReleephDiffMessageFieldSpecification.php +++ /dev/null @@ -1,42 +0,0 @@ - 'phabricator-remarkup', - ), - $this->getMarkupEngineOutput()); - } - - public function shouldMarkup() { - return true; - } - - public function getMarkupText($field) { - $commit_data = $this - ->getReleephRequest() - ->loadPhabricatorRepositoryCommitData(); - if ($commit_data) { - return $commit_data->getCommitMessage(); - } else { - return ''; - } - } - -} diff --git a/src/applications/releeph/field/specification/ReleephDiffSizeFieldSpecification.php b/src/applications/releeph/field/specification/ReleephDiffSizeFieldSpecification.php deleted file mode 100644 index 20c8d5e226..0000000000 --- a/src/applications/releeph/field/specification/ReleephDiffSizeFieldSpecification.php +++ /dev/null @@ -1,118 +0,0 @@ -getObject()->getRequestedObject(); - if (!($requested_object instanceof DifferentialRevision)) { - return null; - } - $diff_rev = $requested_object; - - $diffs = id(new DifferentialDiff())->loadAllWhere( - 'revisionID = %d AND creationMethod != %s', - $diff_rev->getID(), - 'commit'); - - $all_changesets = array(); - $most_recent_changesets = null; - foreach ($diffs as $diff) { - $changesets = id(new DifferentialChangeset())->loadAllWhere( - 'diffID = %d', - $diff->getID()); - $all_changesets += $changesets; - $most_recent_changesets = $changesets; - } - - // The score is based on all changesets for all versions of this diff - $all_changes = $this->countLinesAndPaths($all_changesets); - $points = - self::LINES_WEIGHT * $all_changes['code']['lines'] + - self::PATHS_WEIGHT * count($all_changes['code']['paths']); - - // The blurb is just based on the most recent version of the diff - $mr_changes = $this->countLinesAndPaths($most_recent_changesets); - - $test_tag = ''; - if ($mr_changes['tests']['paths']) { - Javelin::initBehavior('phabricator-tooltips'); - require_celerity_resource('aphront-tooltip-css'); - - $test_blurb = pht( - "%d line(s) and %d path(s) contain changes to test code:\n", - $mr_changes['tests']['lines'], - count($mr_changes['tests']['paths'])); - foreach ($mr_changes['tests']['paths'] as $mr_test_path) { - $test_blurb .= sprintf("%s\n", $mr_test_path); - } - - $test_tag = javelin_tag( - 'span', - array( - 'sigil' => 'has-tooltip', - 'meta' => array( - 'tip' => $test_blurb, - 'align' => 'E', - 'size' => 'auto', - ), - 'style' => '', - ), - ' + tests'); - } - - $blurb = hsprintf('%s%s.', - pht( - '%d line(s) and %d path(s) over %d diff(s)', - $mr_changes['code']['lines'], - $mr_changes['code']['paths'], - count($diffs)), - $test_tag); - - return id(new AphrontProgressBarView()) - ->setValue($points) - ->setMax(self::MAX_POINTS) - ->setCaption($blurb) - ->render(); - } - - private function countLinesAndPaths(array $changesets) { - assert_instances_of($changesets, 'DifferentialChangeset'); - $lines = 0; - $paths_touched = array(); - $test_lines = 0; - $test_paths_touched = array(); - - foreach ($changesets as $ch) { - if ($this->getReleephProject()->isTestFile($ch->getFilename())) { - $test_lines += $ch->getAddLines() + $ch->getDelLines(); - $test_paths_touched[] = $ch->getFilename(); - } else { - $lines += $ch->getAddLines() + $ch->getDelLines(); - $paths_touched[] = $ch->getFilename(); - } - } - return array( - 'code' => array( - 'lines' => $lines, - 'paths' => array_unique($paths_touched), - ), - 'tests' => array( - 'lines' => $test_lines, - 'paths' => array_unique($test_paths_touched), - ), - ); - } -} diff --git a/src/applications/releeph/field/specification/ReleephFieldSpecification.php b/src/applications/releeph/field/specification/ReleephFieldSpecification.php deleted file mode 100644 index ff95ed6514..0000000000 --- a/src/applications/releeph/field/specification/ReleephFieldSpecification.php +++ /dev/null @@ -1,265 +0,0 @@ -requestValue = $request->getStr($this->getRequiredStorageKey()); - return $this; - } - - public function shouldAppearInPropertyView() { - return true; - } - - public function renderPropertyViewLabel() { - return $this->getName(); - } - - public function renderPropertyViewValue(array $handles) { - $key = $this->getRequiredStorageKey(); - $value = $this->getReleephRequest()->getDetail($key); - if ($value === '') { - return null; - } - return $value; - } - - abstract public function getName(); - -/* -( Storage )------------------------------------------------------------ */ - - public function getStorageKey() { - return null; - } - - public function getRequiredStorageKey() { - $key = $this->getStorageKey(); - if ($key === null) { - throw new PhabricatorCustomFieldImplementationIncompleteException($this); - } - if (strpos($key, '.') !== false) { - /** - * Storage keys are reused for form controls, and periods in form control - * names break HTML forms. - */ - throw new Exception(pht("You can't use '%s' in storage keys!", '.')); - } - return $key; - } - - public function shouldAppearInEditView() { - return $this->isEditable(); - } - - final public function isEditable() { - return $this->getStorageKey() !== null; - } - - final public function getValue() { - if ($this->requestValue !== null) { - return $this->requestValue; - } - - $key = $this->getRequiredStorageKey(); - return $this->getReleephRequest()->getDetail($key); - } - - final public function setValue($value) { - $key = $this->getRequiredStorageKey(); - return $this->getReleephRequest()->setDetail($key, $value); - } - - /** - * @throws ReleephFieldParseException, to show an error. - */ - public function validate($value) { - return; - } - - /** - * Turn values as they are stored in a ReleephRequest into a text that can be - * rendered as a transactions old/new values. - */ - public function normalizeForTransactionView( - PhabricatorApplicationTransaction $xaction, - $value) { - - return $value; - } - - -/* -( Conduit )------------------------------------------------------------ */ - - public function getKeyForConduit() { - return $this->getRequiredStorageKey(); - } - - public function getValueForConduit() { - return $this->getValue(); - } - - public function setValueFromConduitAPIRequest(ConduitAPIRequest $request) { - $value = idx( - $request->getValue('fields', array()), - $this->getRequiredStorageKey()); - $this->validate($value); - $this->setValue($value); - return $this; - } - - -/* -( Arcanist )----------------------------------------------------------- */ - - public function renderHelpForArcanist() { - return ''; - } - - -/* -( Context )------------------------------------------------------------ */ - - private $releephProject; - private $releephBranch; - private $releephRequest; - private $user; - - final public function setReleephProject(ReleephProject $rp) { - $this->releephProject = $rp; - return $this; - } - - final public function setReleephBranch(ReleephBranch $rb) { - $this->releephRequest = $rb; - return $this; - } - - final public function setReleephRequest(ReleephRequest $rr) { - $this->releephRequest = $rr; - return $this; - } - - final public function setUser(PhabricatorUser $user) { - $this->user = $user; - return $this; - } - - final public function getReleephProject() { - if (!$this->releephProject) { - return $this->getReleephBranch()->getProduct(); - } - return $this->releephProject; - } - - final public function getReleephBranch() { - if (!$this->releephBranch) { - return $this->getReleephRequest()->getBranch(); - } - return $this->releephBranch; - } - - final public function getReleephRequest() { - if (!$this->releephRequest) { - return $this->getObject(); - } - return $this->releephRequest; - } - - final public function getUser() { - if (!$this->user) { - return $this->getViewer(); - } - return $this->user; - } - -/* -( Commit Messages )---------------------------------------------------- */ - - public function shouldAppearOnCommitMessage() { - return false; - } - - public function renderLabelForCommitMessage() { - throw new PhabricatorCustomFieldImplementationIncompleteException($this); - } - - public function renderValueForCommitMessage() { - throw new PhabricatorCustomFieldImplementationIncompleteException($this); - } - - public function shouldAppearOnRevertMessage() { - return false; - } - - public function renderLabelForRevertMessage() { - return $this->renderLabelForCommitMessage(); - } - - public function renderValueForRevertMessage() { - return $this->renderValueForCommitMessage(); - } - - -/* -( Markup Interface )--------------------------------------------------- */ - - const MARKUP_FIELD_GENERIC = 'releeph:generic-markup-field'; - - private $engine; - - /** - * @{class:ReleephFieldSpecification} implements much of - * @{interface:PhabricatorMarkupInterface} for you. If you return true from - * `shouldMarkup()`, and implement `getMarkupText()` then your text will be - * rendered through the Phabricator markup pipeline. - * - * Output is retrievable with `getMarkupEngineOutput()`. - */ - public function shouldMarkup() { - return false; - } - - public function getMarkupText($field) { - throw new PhabricatorCustomFieldImplementationIncompleteException($this); - } - - final public function getMarkupEngineOutput() { - return $this->engine->getOutput($this, self::MARKUP_FIELD_GENERIC); - } - - final public function setMarkupEngine(PhabricatorMarkupEngine $engine) { - $this->engine = $engine; - $engine->addObject($this, self::MARKUP_FIELD_GENERIC); - return $this; - } - - final public function getMarkupFieldKey($field) { - $content = sprintf( - '%s:%s:%s:%s', - $this->getReleephRequest()->getPHID(), - $this->getStorageKey(), - $field, - $this->getMarkupText($field)); - - return PhabricatorMarkupEngine::digestRemarkupContent($this, $content); - } - - final public function newMarkupEngine($field) { - return PhabricatorMarkupEngine::newDifferentialMarkupEngine(); - } - - final public function didMarkupText( - $field, - $output, - PhutilMarkupEngine $engine) { - - return $output; - } - - final public function shouldUseMarkupCache($field) { - return true; - } - -} diff --git a/src/applications/releeph/field/specification/ReleephIntentFieldSpecification.php b/src/applications/releeph/field/specification/ReleephIntentFieldSpecification.php deleted file mode 100644 index 833dce0b39..0000000000 --- a/src/applications/releeph/field/specification/ReleephIntentFieldSpecification.php +++ /dev/null @@ -1,142 +0,0 @@ -getReleephRequest(); - $intents = $pull->getUserIntents(); - return array_keys($intents); - } - - public function renderPropertyViewValue(array $handles) { - $pull = $this->getReleephRequest(); - - $intents = $pull->getUserIntents(); - $product = $this->getReleephProject(); - - if (!$intents) { - return null; - } - - $pushers = array(); - $others = array(); - - foreach ($intents as $phid => $intent) { - if ($product->isAuthoritativePHID($phid)) { - $pushers[$phid] = $intent; - } else { - $others[$phid] = $intent; - } - } - - $intents = $pushers + $others; - - $view = id(new PHUIStatusListView()); - foreach ($intents as $phid => $intent) { - switch ($intent) { - case ReleephRequest::INTENT_WANT: - $icon = PHUIStatusItemView::ICON_ACCEPT; - $color = 'green'; - $label = pht('Want'); - break; - case ReleephRequest::INTENT_PASS: - $icon = PHUIStatusItemView::ICON_REJECT; - $color = 'red'; - $label = pht('Pass'); - break; - default: - $icon = PHUIStatusItemView::ICON_QUESTION; - $color = 'bluegrey'; - $label = pht('Unknown Intent (%s)', $intent); - break; - } - - $target = $handles[$phid]->renderLink(); - if ($product->isAuthoritativePHID($phid)) { - $target = phutil_tag('strong', array(), $target); - } - - $view->addItem( - id(new PHUIStatusItemView()) - ->setIcon($icon, $color, $label) - ->setTarget($target)); - } - - return $view; - } - - public function shouldAppearOnCommitMessage() { - return true; - } - - public function shouldAppearOnRevertMessage() { - return true; - } - - public function renderLabelForCommitMessage() { - return pht('Approved By'); - } - - public function renderLabelForRevertMessage() { - return pht('Rejected By'); - } - - public function renderValueForCommitMessage() { - return $this->renderIntentsForCommitMessage(ReleephRequest::INTENT_WANT); - } - - public function renderValueForRevertMessage() { - return $this->renderIntentsForCommitMessage(ReleephRequest::INTENT_PASS); - } - - private function renderIntentsForCommitMessage($print_intent) { - $intents = $this->getReleephRequest()->getUserIntents(); - - $requestor = $this->getReleephRequest()->getRequestUserPHID(); - $pusher_phids = $this->getReleephProject()->getPushers(); - - $phids = array_unique($pusher_phids + array_keys($intents)); - $handles = id(new PhabricatorHandleQuery()) - ->setViewer($this->getUser()) - ->withPHIDs($phids) - ->execute(); - - $tokens = array(); - foreach ($phids as $phid) { - $intent = idx($intents, $phid); - if ($intent == $print_intent) { - $name = $handles[$phid]->getName(); - $is_pusher = in_array($phid, $pusher_phids); - $is_requestor = $phid == $requestor; - - if ($is_pusher) { - if ($is_requestor) { - $token = pht('%s (pusher and requestor)', $name); - } else { - $token = "{$name} (pusher)"; - } - } else { - if ($is_requestor) { - $token = pht('%s (requestor)', $name); - } else { - $token = $name; - } - } - - $tokens[] = $token; - } - } - - return implode(', ', $tokens); - } - -} diff --git a/src/applications/releeph/field/specification/ReleephLevelFieldSpecification.php b/src/applications/releeph/field/specification/ReleephLevelFieldSpecification.php deleted file mode 100644 index 395636c983..0000000000 --- a/src/applications/releeph/field/specification/ReleephLevelFieldSpecification.php +++ /dev/null @@ -1,137 +0,0 @@ -getNameForLevel($this->getValue()); - } - - public function renderEditControl(array $handles) { - $control_name = $this->getRequiredStorageKey(); - $all_levels = $this->getLevels(); - - $level = $this->getValue(); - if (!$level) { - $level = $this->getDefaultLevel(); - } - - $control = id(new AphrontFormRadioButtonControl()) - ->setLabel(pht('Level')) - ->setName($control_name) - ->setValue($level); - - if ($this->error) { - $control->setError($this->error); - } else if ($this->getDefaultLevel()) { - $control->setError(true); - } - - foreach ($all_levels as $level) { - $name = $this->getNameForLevel($level); - $description = $this->getDescriptionForLevel($level); - $control->addButton($level, $name, $description); - } - - return $control; - } - - public function renderHelpForArcanist() { - $text = ''; - $levels = $this->getLevels(); - $default = $this->getDefaultLevel(); - foreach ($levels as $level) { - $name = $this->getNameForLevel($level); - $description = $this->getDescriptionForLevel($level); - $default_marker = ' '; - if ($level === $default) { - $default_marker = '*'; - } - $text .= " {$default_marker} **{$name}**\n"; - $text .= phutil_console_wrap($description."\n", 8); - } - return $text; - } - - public function validate($value) { - if ($value === null) { - $this->error = pht('Required'); - $label = $this->getName(); - throw new ReleephFieldParseException( - $this, - pht('You must provide a %s level.', $label)); - } - - $levels = $this->getLevels(); - if (!in_array($value, $levels)) { - $label = $this->getName(); - throw new ReleephFieldParseException( - $this, - pht( - "Level '%s' is not a valid %s level in this project.", - $value, - $label)); - } - } - - public function setValueFromConduitAPIRequest(ConduitAPIRequest $request) { - $key = $this->getRequiredStorageKey(); - $label = $this->getName(); - $name = idx($request->getValue('fields', array()), $key); - - if (!$name) { - $level = $this->getDefaultLevel(); - if (!$level) { - throw new ReleephFieldParseException( - $this, - pht( - 'No value given for %s, and no default is given for this level!', - $label)); - } - } else { - $level = $this->getLevelByName($name); - } - - if (!$level) { - throw new ReleephFieldParseException( - $this, - pht("Unknown %s level name '%s'", $label, $name)); - } - $this->setValue($level); - return $this; - } - - private $nameMap = array(); - - public function getLevelByName($name) { - // Build this once - if (!$this->nameMap) { - foreach ($this->getLevels() as $level) { - $level_name = $this->getNameForLevel($level); - $this->nameMap[$level_name] = $level; - } - } - return idx($this->nameMap, $name); - } - -} diff --git a/src/applications/releeph/field/specification/ReleephOriginalCommitFieldSpecification.php b/src/applications/releeph/field/specification/ReleephOriginalCommitFieldSpecification.php deleted file mode 100644 index 57c4209ca7..0000000000 --- a/src/applications/releeph/field/specification/ReleephOriginalCommitFieldSpecification.php +++ /dev/null @@ -1,25 +0,0 @@ -getReleephRequest()->getRequestCommitPHID(), - ); - } - - - public function renderPropertyViewValue(array $handles) { - return $this->renderHandleList($handles); - } - -} diff --git a/src/applications/releeph/field/specification/ReleephReasonFieldSpecification.php b/src/applications/releeph/field/specification/ReleephReasonFieldSpecification.php deleted file mode 100644 index 023ea081a1..0000000000 --- a/src/applications/releeph/field/specification/ReleephReasonFieldSpecification.php +++ /dev/null @@ -1,86 +0,0 @@ - 'phabricator-remarkup', - ), - $this->getMarkupEngineOutput()); - } - - private $error = true; - - public function renderEditControl(array $handles) { - return id(new AphrontFormTextAreaControl()) - ->setLabel(pht('Reason')) - ->setName('reason') - ->setError($this->error) - ->setValue($this->getValue()); - } - - public function validate($reason) { - if (!$reason) { - $this->error = pht('Required'); - throw new ReleephFieldParseException( - $this, - pht('You must give a reason for your request.')); - } - } - - public function renderHelpForArcanist() { - $text = pht( - 'Fully explain why you are requesting this code be included '. - 'in the next release.')."\n"; - return phutil_console_wrap($text, 8); - } - - public function shouldAppearOnCommitMessage() { - return true; - } - - public function renderLabelForCommitMessage() { - return pht('Request Reason'); - } - - public function renderValueForCommitMessage() { - return $this->getValue(); - } - - public function shouldMarkup() { - return true; - } - - public function getMarkupText($field) { - $reason = $this->getValue(); - if ($reason) { - return $reason; - } else { - return ''; - } - } - -} diff --git a/src/applications/releeph/field/specification/ReleephRequestorFieldSpecification.php b/src/applications/releeph/field/specification/ReleephRequestorFieldSpecification.php deleted file mode 100644 index d479bb8020..0000000000 --- a/src/applications/releeph/field/specification/ReleephRequestorFieldSpecification.php +++ /dev/null @@ -1,50 +0,0 @@ -getReleephRequest()->getRequestUserPHID(); - if ($phid) { - $phids[] = $phid; - } - - return $phids; - } - - public function renderPropertyViewValue(array $handles) { - return $this->renderHandleList($handles); - } - - public function shouldAppearOnCommitMessage() { - return true; - } - - public function shouldAppearOnRevertMessage() { - return true; - } - - public function renderLabelForCommitMessage() { - return pht('Requested By'); - } - - public function renderValueForCommitMessage() { - $phid = $this->getReleephRequest()->getRequestUserPHID(); - $handle = id(new PhabricatorHandleQuery()) - ->setViewer($this->getUser()) - ->withPHIDs(array($phid)) - ->executeOne(); - return $handle->getName(); - } - -} diff --git a/src/applications/releeph/field/specification/ReleephRevisionFieldSpecification.php b/src/applications/releeph/field/specification/ReleephRevisionFieldSpecification.php deleted file mode 100644 index 45f6bdd7e1..0000000000 --- a/src/applications/releeph/field/specification/ReleephRevisionFieldSpecification.php +++ /dev/null @@ -1,29 +0,0 @@ -getObject()->getRequestedObjectPHID(); - if (!($requested_object instanceof DifferentialRevision)) { - return array(); - } - - return array( - $requested_object->getPHID(), - ); - } - - public function renderPropertyViewValue(array $handles) { - return $this->renderHandleList($handles); - } - -} diff --git a/src/applications/releeph/field/specification/ReleephSeverityFieldSpecification.php b/src/applications/releeph/field/specification/ReleephSeverityFieldSpecification.php deleted file mode 100644 index c73a17e149..0000000000 --- a/src/applications/releeph/field/specification/ReleephSeverityFieldSpecification.php +++ /dev/null @@ -1,53 +0,0 @@ - 'HOTFIX', - self::RELEASE => 'RELEASE', - ); - return idx($names, $level, $level); - } - - public function getDescriptionForLevel($level) { - static $descriptions; - - if ($descriptions === null) { - $descriptions = array( - self::HOTFIX => pht('Needs merging and fixing right now.'), - self::RELEASE => pht('Required for the currently rolling release.'), - ); - } - - return idx($descriptions, $level); - } - -} diff --git a/src/applications/releeph/field/specification/ReleephSummaryFieldSpecification.php b/src/applications/releeph/field/specification/ReleephSummaryFieldSpecification.php deleted file mode 100644 index 2341b0d2b5..0000000000 --- a/src/applications/releeph/field/specification/ReleephSummaryFieldSpecification.php +++ /dev/null @@ -1,53 +0,0 @@ -setLabel(pht('Summary')) - ->setName('summary') - ->setError($this->error) - ->setValue($this->getValue()) - ->setCaption(pht('Leave this blank to use the original commit title')); - } - - public function renderHelpForArcanist() { - $text = pht( - 'A one-line title summarizing this request. '. - 'Leave blank to use the original commit title.')."\n"; - return phutil_console_wrap($text, 8); - } - - public function validate($summary) { - if ($summary && strlen($summary) > self::MAX_SUMMARY_LENGTH) { - $this->error = pht('Too long!'); - throw new ReleephFieldParseException( - $this, - pht( - 'Please keep your summary to under %d characters.', - self::MAX_SUMMARY_LENGTH)); - } - } - -} diff --git a/src/applications/releeph/mail/ReleephRequestMailReceiver.php b/src/applications/releeph/mail/ReleephRequestMailReceiver.php deleted file mode 100644 index a64c7f483f..0000000000 --- a/src/applications/releeph/mail/ReleephRequestMailReceiver.php +++ /dev/null @@ -1,27 +0,0 @@ -setViewer($viewer) - ->withIDs(array($id)) - ->executeOne(); - } - - protected function getTransactionReplyHandler() { - return new ReleephRequestReplyHandler(); - } - -} diff --git a/src/applications/releeph/mail/ReleephRequestReplyHandler.php b/src/applications/releeph/mail/ReleephRequestReplyHandler.php deleted file mode 100644 index 9648941941..0000000000 --- a/src/applications/releeph/mail/ReleephRequestReplyHandler.php +++ /dev/null @@ -1,16 +0,0 @@ -withPHIDs($phids); - } - - public function loadHandles( - PhabricatorHandleQuery $query, - array $handles, - array $objects) { - - foreach ($handles as $phid => $handle) { - $branch = $objects[$phid]; - - $handle->setURI($branch->getURI()); - $handle->setName($branch->getBasename()); - $handle->setFullName($branch->getName()); - } - } - -} diff --git a/src/applications/releeph/phid/ReleephProductPHIDType.php b/src/applications/releeph/phid/ReleephProductPHIDType.php deleted file mode 100644 index c7979cfba3..0000000000 --- a/src/applications/releeph/phid/ReleephProductPHIDType.php +++ /dev/null @@ -1,40 +0,0 @@ -withPHIDs($phids); - } - - public function loadHandles( - PhabricatorHandleQuery $query, - array $handles, - array $objects) { - - foreach ($handles as $phid => $handle) { - $product = $objects[$phid]; - - $handle->setName($product->getName()); - $handle->setURI($product->getURI()); - } - } - -} diff --git a/src/applications/releeph/phid/ReleephRequestPHIDType.php b/src/applications/releeph/phid/ReleephRequestPHIDType.php deleted file mode 100644 index 7bd853f984..0000000000 --- a/src/applications/releeph/phid/ReleephRequestPHIDType.php +++ /dev/null @@ -1,44 +0,0 @@ -withPHIDs($phids); - } - - public function loadHandles( - PhabricatorHandleQuery $query, - array $handles, - array $objects) { - - foreach ($handles as $phid => $handle) { - $request = $objects[$phid]; - - $id = $request->getID(); - $title = $request->getSummaryForDisplay(); - - $handle->setURI("/RQ{$id}"); - $handle->setName($title); - $handle->setFullName("RQ{$id}: {$title}"); - } - } - -} diff --git a/src/applications/releeph/query/ReleephBranchQuery.php b/src/applications/releeph/query/ReleephBranchQuery.php deleted file mode 100644 index 97e47bdcaf..0000000000 --- a/src/applications/releeph/query/ReleephBranchQuery.php +++ /dev/null @@ -1,152 +0,0 @@ -ids = $ids; - return $this; - } - - public function withPHIDs(array $phids) { - $this->phids = $phids; - return $this; - } - - public function needCutPointCommits($need_commits) { - $this->needCutPointCommits = $need_commits; - return $this; - } - - public function withStatus($status) { - $this->status = $status; - return $this; - } - - public function withProductPHIDs($product_phids) { - $this->productPHIDs = $product_phids; - return $this; - } - - protected function loadPage() { - $table = new ReleephBranch(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($data); - } - - protected function willExecute() { - if ($this->productPHIDs !== null) { - $products = id(new ReleephProductQuery()) - ->setViewer($this->getViewer()) - ->withPHIDs($this->productPHIDs) - ->execute(); - - if (!$products) { - throw new PhabricatorEmptyQueryException(); - } - - $this->productIDs = mpull($products, 'getID'); - } - } - - protected function willFilterPage(array $branches) { - $project_ids = mpull($branches, 'getReleephProjectID'); - - $projects = id(new ReleephProductQuery()) - ->withIDs($project_ids) - ->setViewer($this->getViewer()) - ->execute(); - - foreach ($branches as $key => $branch) { - $project_id = $project_ids[$key]; - if (isset($projects[$project_id])) { - $branch->attachProject($projects[$project_id]); - } else { - unset($branches[$key]); - } - } - - if ($this->needCutPointCommits) { - $commit_phids = mpull($branches, 'getCutPointCommitPHID'); - $commits = id(new DiffusionCommitQuery()) - ->setViewer($this->getViewer()) - ->withPHIDs($commit_phids) - ->execute(); - $commits = mpull($commits, null, 'getPHID'); - - foreach ($branches as $branch) { - $commit = idx($commits, $branch->getCutPointCommitPHID()); - $branch->attachCutPointCommit($commit); - } - } - - return $branches; - } - - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); - - if ($this->ids !== null) { - $where[] = qsprintf( - $conn, - 'id IN (%Ld)', - $this->ids); - } - - if ($this->phids !== null) { - $where[] = qsprintf( - $conn, - 'phid IN (%Ls)', - $this->phids); - } - - if ($this->productIDs !== null) { - $where[] = qsprintf( - $conn, - 'releephProjectID IN (%Ld)', - $this->productIDs); - } - - $status = $this->status; - switch ($status) { - case self::STATUS_ALL: - break; - case self::STATUS_OPEN: - $where[] = qsprintf( - $conn, - 'isActive = 1'); - break; - default: - throw new Exception(pht("Unknown status constant '%s'!", $status)); - } - - $where[] = $this->buildPagingClause($conn); - - return $this->formatWhereClause($conn, $where); - } - - public function getQueryApplicationClass() { - return 'PhabricatorReleephApplication'; - } - -} diff --git a/src/applications/releeph/query/ReleephBranchSearchEngine.php b/src/applications/releeph/query/ReleephBranchSearchEngine.php deleted file mode 100644 index 441f70e992..0000000000 --- a/src/applications/releeph/query/ReleephBranchSearchEngine.php +++ /dev/null @@ -1,200 +0,0 @@ -product = $product; - return $this; - } - - public function getProduct() { - return $this->product; - } - - public function buildSavedQueryFromRequest(AphrontRequest $request) { - $saved = new PhabricatorSavedQuery(); - - $saved->setParameter('active', $request->getStr('active')); - - return $saved; - } - - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { - $query = id(new ReleephBranchQuery()) - ->needCutPointCommits(true) - ->withProductPHIDs(array($this->getProduct()->getPHID())); - - $active = $saved->getParameter('active'); - $value = idx($this->getActiveValues(), $active); - if ($value !== null) { - $query->withStatus($value); - } - - return $query; - } - - public function buildSearchForm( - AphrontFormView $form, - PhabricatorSavedQuery $saved_query) { - - $form->appendChild( - id(new AphrontFormSelectControl()) - ->setName('active') - ->setLabel(pht('Show Branches')) - ->setValue($saved_query->getParameter('active')) - ->setOptions($this->getActiveOptions())); - } - - protected function getURI($path) { - return '/releeph/product/'.$this->getProduct()->getID().'/'.$path; - } - - protected function getBuiltinQueryNames() { - $names = array( - 'open' => pht('Open'), - 'all' => pht('All'), - ); - - return $names; - } - - public function buildSavedQueryFromBuiltin($query_key) { - - $query = $this->newSavedQuery(); - $query->setQueryKey($query_key); - - switch ($query_key) { - case 'open': - return $query - ->setParameter('active', 'open'); - case 'all': - return $query; - } - - return parent::buildSavedQueryFromBuiltin($query_key); - } - - private function getActiveOptions() { - return array( - 'open' => pht('Open Branches'), - 'all' => pht('Open and Closed Branches'), - ); - } - - private function getActiveValues() { - return array( - 'open' => ReleephBranchQuery::STATUS_OPEN, - 'all' => ReleephBranchQuery::STATUS_ALL, - ); - } - - protected function renderResultList( - array $branches, - PhabricatorSavedQuery $query, - array $handles) { - - - assert_instances_of($branches, 'ReleephBranch'); - - $viewer = $this->getRequest()->getUser(); - - $products = mpull($branches, 'getProduct'); - $repo_phids = mpull($products, 'getRepositoryPHID'); - - if ($repo_phids) { - $repos = id(new PhabricatorRepositoryQuery()) - ->setViewer($viewer) - ->withPHIDs($repo_phids) - ->execute(); - $repos = mpull($repos, null, 'getPHID'); - } else { - $repos = array(); - } - - $requests = array(); - if ($branches) { - $requests = id(new ReleephRequestQuery()) - ->setViewer($viewer) - ->withBranchIDs(mpull($branches, 'getID')) - ->withStatus(ReleephRequestQuery::STATUS_OPEN) - ->execute(); - $requests = mgroup($requests, 'getBranchID'); - } - - $list = id(new PHUIObjectItemListView()) - ->setUser($viewer); - foreach ($branches as $branch) { - $diffusion_href = null; - $repo = idx($repos, $branch->getProduct()->getRepositoryPHID()); - if ($repo) { - $drequest = DiffusionRequest::newFromDictionary( - array( - 'user' => $viewer, - 'repository' => $repo, - )); - - $diffusion_href = $drequest->generateURI( - array( - 'action' => 'branch', - 'branch' => $branch->getName(), - )); - } - - $branch_link = $branch->getName(); - if ($diffusion_href) { - $branch_link = phutil_tag( - 'a', - array( - 'href' => $diffusion_href, - ), - $branch_link); - } - - $item = id(new PHUIObjectItemView()) - ->setHeader($branch->getDisplayName()) - ->setHref($this->getApplicationURI('branch/'.$branch->getID().'/')) - ->addAttribute($branch_link); - - if (!$branch->getIsActive()) { - $item->setDisabled(true); - } - - $commit = $branch->getCutPointCommit(); - if ($commit) { - $item->addIcon( - 'none', - phabricator_datetime($commit->getEpoch(), $viewer)); - } - - $open_count = count(idx($requests, $branch->getID(), array())); - if ($open_count) { - $item->setStatusIcon('fa-code-fork orange'); - $item->addIcon( - 'fa-code-fork', - pht( - '%s Open Pull Request(s)', - new PhutilNumber($open_count))); - } - - $list->addItem($item); - } - - return id(new PhabricatorApplicationSearchResultView()) - ->setObjectList($list); - } -} diff --git a/src/applications/releeph/query/ReleephProductQuery.php b/src/applications/releeph/query/ReleephProductQuery.php deleted file mode 100644 index 118b9919a8..0000000000 --- a/src/applications/releeph/query/ReleephProductQuery.php +++ /dev/null @@ -1,144 +0,0 @@ -active = $active; - return $this; - } - - public function setOrder($order) { - switch ($order) { - case self::ORDER_ID: - $this->setOrderVector(array('id')); - break; - case self::ORDER_NAME: - $this->setOrderVector(array('name')); - break; - default: - throw new Exception(pht('Order "%s" not supported.', $order)); - } - return $this; - } - - public function withIDs(array $ids) { - $this->ids = $ids; - return $this; - } - - public function withPHIDs(array $phids) { - $this->phids = $phids; - return $this; - } - - public function withRepositoryPHIDs(array $repository_phids) { - $this->repositoryPHIDs = $repository_phids; - return $this; - } - - protected function loadPage() { - $table = new ReleephProject(); - $conn_r = $table->establishConnection('r'); - - $rows = queryfx_all( - $conn_r, - 'SELECT * FROM %T %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($rows); - } - - protected function willFilterPage(array $projects) { - assert_instances_of($projects, 'ReleephProject'); - - $repository_phids = mpull($projects, 'getRepositoryPHID'); - - $repositories = id(new PhabricatorRepositoryQuery()) - ->setViewer($this->getViewer()) - ->withPHIDs($repository_phids) - ->execute(); - $repositories = mpull($repositories, null, 'getPHID'); - - foreach ($projects as $key => $project) { - $repo = idx($repositories, $project->getRepositoryPHID()); - if (!$repo) { - unset($projects[$key]); - continue; - } - $project->attachRepository($repo); - } - - return $projects; - } - - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); - - if ($this->active !== null) { - $where[] = qsprintf( - $conn, - 'isActive = %d', - (int)$this->active); - } - - if ($this->ids !== null) { - $where[] = qsprintf( - $conn, - 'id IN (%Ls)', - $this->ids); - } - - if ($this->phids !== null) { - $where[] = qsprintf( - $conn, - 'phid IN (%Ls)', - $this->phids); - } - - if ($this->repositoryPHIDs !== null) { - $where[] = qsprintf( - $conn, - 'repositoryPHID IN (%Ls)', - $this->repositoryPHIDs); - } - - $where[] = $this->buildPagingClause($conn); - - return $this->formatWhereClause($conn, $where); - } - - public function getOrderableColumns() { - return parent::getOrderableColumns() + array( - 'name' => array( - 'column' => 'name', - 'unique' => true, - 'reverse' => true, - 'type' => 'string', - ), - ); - } - - protected function newPagingMapFromPartialObject($object) { - return array( - 'id' => (int)$object->getID(), - 'name' => $object->getName(), - ); - } - - public function getQueryApplicationClass() { - return 'PhabricatorReleephApplication'; - } - -} diff --git a/src/applications/releeph/query/ReleephProductSearchEngine.php b/src/applications/releeph/query/ReleephProductSearchEngine.php deleted file mode 100644 index d58ee735f1..0000000000 --- a/src/applications/releeph/query/ReleephProductSearchEngine.php +++ /dev/null @@ -1,134 +0,0 @@ -setParameter('active', $request->getStr('active')); - - return $saved; - } - - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { - $query = id(new ReleephProductQuery()) - ->setOrder(ReleephProductQuery::ORDER_NAME); - - $active = $saved->getParameter('active'); - $value = idx($this->getActiveValues(), $active); - if ($value !== null) { - $query->withActive($value); - } - - return $query; - } - - public function buildSearchForm( - AphrontFormView $form, - PhabricatorSavedQuery $saved_query) { - - $form->appendChild( - id(new AphrontFormSelectControl()) - ->setName('active') - ->setLabel(pht('Show Products')) - ->setValue($saved_query->getParameter('active')) - ->setOptions($this->getActiveOptions())); - } - - protected function getURI($path) { - return '/releeph/project/'.$path; - } - - protected function getBuiltinQueryNames() { - return array( - 'active' => pht('Active'), - 'all' => pht('All'), - ); - } - - public function buildSavedQueryFromBuiltin($query_key) { - $query = $this->newSavedQuery(); - $query->setQueryKey($query_key); - - switch ($query_key) { - case 'active': - return $query - ->setParameter('active', 'active'); - case 'all': - return $query; - } - - return parent::buildSavedQueryFromBuiltin($query_key); - } - - private function getActiveOptions() { - return array( - 'all' => pht('Active and Inactive Products'), - 'active' => pht('Active Products'), - 'inactive' => pht('Inactive Products'), - ); - } - - private function getActiveValues() { - return array( - 'all' => null, - 'active' => 1, - 'inactive' => 0, - ); - } - - protected function renderResultList( - array $products, - PhabricatorSavedQuery $query, - array $handles) { - - assert_instances_of($products, 'ReleephProject'); - $viewer = $this->requireViewer(); - - $list = id(new PHUIObjectItemListView()) - ->setUser($viewer); - - foreach ($products as $product) { - $id = $product->getID(); - - $item = id(new PHUIObjectItemView()) - ->setHeader($product->getName()) - ->setHref($this->getApplicationURI("product/{$id}/")); - - if (!$product->getIsActive()) { - $item->setDisabled(true); - $item->addIcon('none', pht('Inactive')); - } - - $repo = $product->getRepository(); - $item->addAttribute( - phutil_tag( - 'a', - array( - 'href' => $repo->getURI(), - ), - $repo->getMonogram())); - - $list->addItem($item); - } - - $result = new PhabricatorApplicationSearchResultView(); - $result->setObjectList($list); - - return $result; - } - -} diff --git a/src/applications/releeph/query/ReleephProductTransactionQuery.php b/src/applications/releeph/query/ReleephProductTransactionQuery.php deleted file mode 100644 index 2a595da443..0000000000 --- a/src/applications/releeph/query/ReleephProductTransactionQuery.php +++ /dev/null @@ -1,10 +0,0 @@ -ids = $ids; - return $this; - } - - public function withPHIDs(array $phids) { - $this->phids = $phids; - return $this; - } - - public function withBranchIDs(array $branch_ids) { - $this->branchIDs = $branch_ids; - return $this; - } - - public function withStatus($status) { - $this->status = $status; - return $this; - } - - public function withRequestedCommitPHIDs(array $requested_commit_phids) { - $this->requestedCommitPHIDs = $requested_commit_phids; - return $this; - } - - public function withRequestorPHIDs(array $phids) { - $this->requestorPHIDs = $phids; - return $this; - } - - public function withSeverities(array $severities) { - $this->severities = $severities; - return $this; - } - - public function withRequestedObjectPHIDs(array $phids) { - $this->requestedObjectPHIDs = $phids; - return $this; - } - - protected function loadPage() { - $table = new ReleephRequest(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($data); - } - - protected function willFilterPage(array $requests) { - // Load requested objects: you must be able to see an object to see - // requests for it. - $object_phids = mpull($requests, 'getRequestedObjectPHID'); - $objects = id(new PhabricatorObjectQuery()) - ->setViewer($this->getViewer()) - ->setParentQuery($this) - ->withPHIDs($object_phids) - ->execute(); - - foreach ($requests as $key => $request) { - $object_phid = $request->getRequestedObjectPHID(); - $object = idx($objects, $object_phid); - if (!$object) { - unset($requests[$key]); - continue; - } - $request->attachRequestedObject($object); - } - - if ($this->severities) { - $severities = array_fuse($this->severities); - foreach ($requests as $key => $request) { - - // NOTE: Facebook uses a custom field here. - if (ReleephDefaultFieldSelector::isFacebook()) { - $severity = $request->getDetail('severity'); - } else { - $severity = $request->getDetail('releeph:severity'); - } - - if (empty($severities[$severity])) { - unset($requests[$key]); - } - } - } - - $branch_ids = array_unique(mpull($requests, 'getBranchID')); - $branches = id(new ReleephBranchQuery()) - ->withIDs($branch_ids) - ->setViewer($this->getViewer()) - ->execute(); - $branches = mpull($branches, null, 'getID'); - foreach ($requests as $key => $request) { - $branch = idx($branches, $request->getBranchID()); - if (!$branch) { - unset($requests[$key]); - continue; - } - $request->attachBranch($branch); - } - - // TODO: These should be serviced by the query, but are not currently - // denormalized anywhere. For now, filter them here instead. Note that - // we must perform this filtering *after* querying and attaching branches, - // because request status depends on the product. - - $keep_status = array_fuse($this->getKeepStatusConstants()); - if ($keep_status) { - foreach ($requests as $key => $request) { - if (empty($keep_status[$request->getStatus()])) { - unset($requests[$key]); - } - } - } - - return $requests; - } - - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); - - if ($this->ids !== null) { - $where[] = qsprintf( - $conn, - 'id IN (%Ld)', - $this->ids); - } - - if ($this->phids !== null) { - $where[] = qsprintf( - $conn, - 'phid IN (%Ls)', - $this->phids); - } - - if ($this->branchIDs !== null) { - $where[] = qsprintf( - $conn, - 'branchID IN (%Ld)', - $this->branchIDs); - } - - if ($this->requestedCommitPHIDs !== null) { - $where[] = qsprintf( - $conn, - 'requestCommitPHID IN (%Ls)', - $this->requestedCommitPHIDs); - } - - if ($this->requestorPHIDs !== null) { - $where[] = qsprintf( - $conn, - 'requestUserPHID IN (%Ls)', - $this->requestorPHIDs); - } - - if ($this->requestedObjectPHIDs !== null) { - $where[] = qsprintf( - $conn, - 'requestedObjectPHID IN (%Ls)', - $this->requestedObjectPHIDs); - } - - $where[] = $this->buildPagingClause($conn); - - return $this->formatWhereClause($conn, $where); - } - - private function getKeepStatusConstants() { - switch ($this->status) { - case self::STATUS_ALL: - return array(); - case self::STATUS_OPEN: - return array( - ReleephRequestStatus::STATUS_REQUESTED, - ReleephRequestStatus::STATUS_NEEDS_PICK, - ReleephRequestStatus::STATUS_NEEDS_REVERT, - ); - case self::STATUS_REQUESTED: - return array( - ReleephRequestStatus::STATUS_REQUESTED, - ); - case self::STATUS_NEEDS_PULL: - return array( - ReleephRequestStatus::STATUS_NEEDS_PICK, - ); - case self::STATUS_REJECTED: - return array( - ReleephRequestStatus::STATUS_REJECTED, - ); - case self::STATUS_ABANDONED: - return array( - ReleephRequestStatus::STATUS_ABANDONED, - ); - case self::STATUS_PULLED: - return array( - ReleephRequestStatus::STATUS_PICKED, - ); - case self::STATUS_NEEDS_REVERT: - return array( - ReleephRequestStatus::STATUS_NEEDS_REVERT, - ); - case self::STATUS_REVERTED: - return array( - ReleephRequestStatus::STATUS_REVERTED, - ); - default: - throw new Exception(pht("Unknown status '%s'!", $this->status)); - } - } - - public function getQueryApplicationClass() { - return 'PhabricatorReleephApplication'; - } - -} diff --git a/src/applications/releeph/query/ReleephRequestSearchEngine.php b/src/applications/releeph/query/ReleephRequestSearchEngine.php deleted file mode 100644 index 7f866405c1..0000000000 --- a/src/applications/releeph/query/ReleephRequestSearchEngine.php +++ /dev/null @@ -1,225 +0,0 @@ -branch = $branch; - return $this; - } - - public function getBranch() { - return $this->branch; - } - - public function setBaseURI($base_uri) { - $this->baseURI = $base_uri; - return $this; - } - - public function buildSavedQueryFromRequest(AphrontRequest $request) { - $saved = new PhabricatorSavedQuery(); - - $saved->setParameter('status', $request->getStr('status')); - $saved->setParameter('severity', $request->getStr('severity')); - $saved->setParameter( - 'requestorPHIDs', - $this->readUsersFromRequest($request, 'requestors')); - - return $saved; - } - - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { - $query = id(new ReleephRequestQuery()) - ->withBranchIDs(array($this->getBranch()->getID())); - - $status = $saved->getParameter('status'); - $status = idx($this->getStatusValues(), $status); - if ($status) { - $query->withStatus($status); - } - - $severity = $saved->getParameter('severity'); - if ($severity) { - $query->withSeverities(array($severity)); - } - - $requestor_phids = $saved->getParameter('requestorPHIDs'); - if ($requestor_phids) { - $query->withRequestorPHIDs($requestor_phids); - } - - return $query; - } - - public function buildSearchForm( - AphrontFormView $form, - PhabricatorSavedQuery $saved_query) { - - $requestor_phids = $saved_query->getParameter('requestorPHIDs', array()); - - $form - ->appendChild( - id(new AphrontFormSelectControl()) - ->setName('status') - ->setLabel(pht('Status')) - ->setValue($saved_query->getParameter('status')) - ->setOptions($this->getStatusOptions())) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setName('severity') - ->setLabel(pht('Severity')) - ->setValue($saved_query->getParameter('severity')) - ->setOptions($this->getSeverityOptions())) - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setDatasource(new PhabricatorPeopleDatasource()) - ->setName('requestors') - ->setLabel(pht('Requestors')) - ->setValue($requestor_phids)); - } - - protected function getURI($path) { - return $this->baseURI.$path; - } - - protected function getBuiltinQueryNames() { - $names = array( - 'open' => pht('Open Requests'), - 'all' => pht('All Requests'), - ); - - if ($this->requireViewer()->isLoggedIn()) { - $names['requested'] = pht('Requested'); - } - - return $names; - } - - public function buildSavedQueryFromBuiltin($query_key) { - - $query = $this->newSavedQuery(); - $query->setQueryKey($query_key); - - switch ($query_key) { - case 'open': - return $query->setParameter('status', 'open'); - case 'all': - return $query; - case 'requested': - return $query->setParameter( - 'requestorPHIDs', - array($this->requireViewer()->getPHID())); - } - - return parent::buildSavedQueryFromBuiltin($query_key); - } - - private function getStatusOptions() { - return array( - '' => pht('(All Requests)'), - 'open' => pht('Open Requests'), - 'requested' => pht('Pull Requested'), - 'needs-pull' => pht('Needs Pull'), - 'rejected' => pht('Rejected'), - 'abandoned' => pht('Abandoned'), - 'pulled' => pht('Pulled'), - 'needs-revert' => pht('Needs Revert'), - 'reverted' => pht('Reverted'), - ); - } - - private function getStatusValues() { - return array( - 'open' => ReleephRequestQuery::STATUS_OPEN, - 'requested' => ReleephRequestQuery::STATUS_REQUESTED, - 'needs-pull' => ReleephRequestQuery::STATUS_NEEDS_PULL, - 'rejected' => ReleephRequestQuery::STATUS_REJECTED, - 'abandoned' => ReleephRequestQuery::STATUS_ABANDONED, - 'pulled' => ReleephRequestQuery::STATUS_PULLED, - 'needs-revert' => ReleephRequestQuery::STATUS_NEEDS_REVERT, - 'reverted' => ReleephRequestQuery::STATUS_REVERTED, - ); - } - - private function getSeverityOptions() { - if (ReleephDefaultFieldSelector::isFacebook()) { - return array( - '' => pht('(All Severities)'), - 11 => pht('HOTFIX'), - 12 => pht('PIGGYBACK'), - 13 => pht('RELEASE'), - 14 => pht('DAILY'), - 15 => pht('PARKING'), - ); - } else { - return array( - '' => pht('(All Severities)'), - ReleephSeverityFieldSpecification::HOTFIX => pht('Hotfix'), - ReleephSeverityFieldSpecification::RELEASE => pht('Release'), - ); - } - } - - protected function renderResultList( - array $requests, - PhabricatorSavedQuery $query, - array $handles) { - - assert_instances_of($requests, 'ReleephRequest'); - $viewer = $this->requireViewer(); - - // TODO: This is generally a bit sketchy, but we don't do this kind of - // thing elsewhere at the moment. For the moment it shouldn't be hugely - // costly, and we can batch things later. Generally, this commits fewer - // sins than the old code did. - - $engine = id(new PhabricatorMarkupEngine()) - ->setViewer($viewer); - - $list = array(); - foreach ($requests as $pull) { - $field_list = PhabricatorCustomField::getObjectFields( - $pull, - PhabricatorCustomField::ROLE_VIEW); - - $field_list - ->setViewer($viewer) - ->readFieldsFromStorage($pull); - - foreach ($field_list->getFields() as $field) { - if ($field->shouldMarkup()) { - $field->setMarkupEngine($engine); - } - } - - $list[] = id(new ReleephRequestView()) - ->setUser($viewer) - ->setCustomFields($field_list) - ->setPullRequest($pull) - ->setIsListView(true); - } - - // This is quite sketchy, but the list has not actually rendered yet, so - // this still allows us to batch the markup rendering. - $engine->process(); - - return id(new PhabricatorApplicationSearchResultView()) - ->setContent($list); - } -} diff --git a/src/applications/releeph/query/ReleephRequestTransactionQuery.php b/src/applications/releeph/query/ReleephRequestTransactionQuery.php deleted file mode 100644 index 80c4f694f7..0000000000 --- a/src/applications/releeph/query/ReleephRequestTransactionQuery.php +++ /dev/null @@ -1,10 +0,0 @@ - true, - self::CONFIG_SERIALIZATION => array( - 'details' => self::SERIALIZATION_JSON, - ), - self::CONFIG_COLUMN_SCHEMA => array( - 'basename' => 'text64', - 'isActive' => 'bool', - 'symbolicName' => 'text64?', - 'name' => 'text128', - ), - self::CONFIG_KEY_SCHEMA => array( - 'releephProjectID' => array( - 'columns' => array('releephProjectID', 'symbolicName'), - 'unique' => true, - ), - 'releephProjectID_2' => array( - 'columns' => array('releephProjectID', 'basename'), - 'unique' => true, - ), - 'releephProjectID_name' => array( - 'columns' => array('releephProjectID', 'name'), - 'unique' => true, - ), - ), - ) + parent::getConfiguration(); - } - - public function generatePHID() { - return PhabricatorPHID::generateNewPHID(ReleephBranchPHIDType::TYPECONST); - } - - public function getDetail($key, $default = null) { - return idx($this->getDetails(), $key, $default); - } - - public function setDetail($key, $value) { - $this->details[$key] = $value; - return $this; - } - - protected function willWriteData(array &$data) { - // If symbolicName is omitted, set it to the basename. - // - // This means that we can enforce symbolicName as a UNIQUE column in the - // DB. We'll interpret symbolicName === basename as meaning "no symbolic - // name". - // - // SYMBOLIC_NAME_NOTE - if (!$data['symbolicName']) { - $data['symbolicName'] = $data['basename']; - } - parent::willWriteData($data); - } - - public function getSymbolicName() { - // See SYMBOLIC_NAME_NOTE above for why this is needed - if ($this->symbolicName == $this->getBasename()) { - return ''; - } - return $this->symbolicName; - } - - public function setSymbolicName($name) { - if ($name) { - parent::setSymbolicName($name); - } else { - parent::setSymbolicName($this->getBasename()); - } - return $this; - } - - public function getDisplayName() { - if ($sn = $this->getSymbolicName()) { - return $sn; - } - return $this->getBasename(); - } - - public function getDisplayNameWithDetail() { - $n = $this->getBasename(); - if ($sn = $this->getSymbolicName()) { - return "{$sn} ({$n})"; - } else { - return $n; - } - } - - public function getURI($path = null) { - $components = array( - '/releeph/branch', - $this->getID(), - $path, - ); - return implode('/', $components); - } - - public function isActive() { - return $this->getIsActive(); - } - - public function attachProject(ReleephProject $project) { - $this->project = $project; - return $this; - } - - public function getProject() { - return $this->assertAttached($this->project); - } - - public function getProduct() { - return $this->getProject(); - } - - public function attachCutPointCommit( - PhabricatorRepositoryCommit $commit = null) { - $this->cutPointCommit = $commit; - return $this; - } - - public function getCutPointCommit() { - return $this->assertAttached($this->cutPointCommit); - } - - -/* -( PhabricatorApplicationTransactionInterface )------------------------- */ - - - public function getApplicationTransactionEditor() { - return new ReleephBranchEditor(); - } - - public function getApplicationTransactionTemplate() { - return new ReleephBranchTransaction(); - } - - -/* -( PhabricatorPolicyInterface )----------------------------------------- */ - - - public function getCapabilities() { - return $this->getProduct()->getCapabilities(); - } - - public function getPolicy($capability) { - return $this->getProduct()->getPolicy($capability); - } - - public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { - return $this->getProduct()->hasAutomaticCapability($capability, $viewer); - } - - public function describeAutomaticCapability($capability) { - return pht( - 'Release branches have the same policies as the product they are a '. - 'part of.'); - } - - -} diff --git a/src/applications/releeph/storage/ReleephBranchTransaction.php b/src/applications/releeph/storage/ReleephBranchTransaction.php deleted file mode 100644 index 5dc2811c86..0000000000 --- a/src/applications/releeph/storage/ReleephBranchTransaction.php +++ /dev/null @@ -1,14 +0,0 @@ -getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_ACTIVE: - if ($new) { - return 'green'; - } else { - return 'black'; - } - break; - } - - return parent::getColor(); - } - - public function getIcon() { - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_ACTIVE: - if ($new) { - return 'fa-pencil'; - } else { - return 'fa-times'; - } - break; - } - - return parent::getIcon(); - } - - public function getTitle() { - $author_phid = $this->getAuthorPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_ACTIVE: - if ($new) { - return pht( - '%s activated this product.', - $this->renderHandleLink($author_phid)); - } else { - return pht( - '%s deactivated this product.', - $this->renderHandleLink($author_phid)); - } - break; - } - - return parent::getTitle(); - } - - public function getTitleForFeed() { - $author_phid = $this->getAuthorPHID(); - $object_phid = $this->getObjectPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_ACTIVE: - if ($new) { - return pht( - '%s activated release product %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - } else { - return pht( - '%s deactivated release product %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - } - break; - } - - return parent::getTitleForFeed(); - } - - public function getNoEffectDescription() { - switch ($this->getTransactionType()) { - case self::TYPE_ACTIVE: - return pht('The product is already in that state.'); - } - - return parent::getNoEffectDescription(); - } - -} diff --git a/src/applications/releeph/storage/ReleephProject.php b/src/applications/releeph/storage/ReleephProject.php deleted file mode 100644 index db47ac5d39..0000000000 --- a/src/applications/releeph/storage/ReleephProject.php +++ /dev/null @@ -1,145 +0,0 @@ - true, - self::CONFIG_SERIALIZATION => array( - 'details' => self::SERIALIZATION_JSON, - ), - self::CONFIG_COLUMN_SCHEMA => array( - 'name' => 'text128', - 'trunkBranch' => 'text255', - 'isActive' => 'bool', - ), - self::CONFIG_KEY_SCHEMA => array( - 'projectName' => array( - 'columns' => array('name'), - 'unique' => true, - ), - ), - ) + parent::getConfiguration(); - } - - public function generatePHID() { - return PhabricatorPHID::generateNewPHID(ReleephProductPHIDType::TYPECONST); - } - - public function getDetail($key, $default = null) { - return idx($this->details, $key, $default); - } - - public function getURI($path = null) { - $components = array( - '/releeph/product', - $this->getID(), - $path, - ); - return implode('/', $components); - } - - public function setDetail($key, $value) { - $this->details[$key] = $value; - return $this; - } - - public function getPushers() { - return $this->getDetail('pushers', array()); - } - - public function isPusher(PhabricatorUser $user) { - // TODO Deprecate this once `isPusher` is out of the Facebook codebase. - return $this->isAuthoritative($user); - } - - public function isAuthoritative(PhabricatorUser $user) { - return $this->isAuthoritativePHID($user->getPHID()); - } - - public function isAuthoritativePHID($phid) { - $pushers = $this->getPushers(); - if (!$pushers) { - return true; - } else { - return in_array($phid, $pushers); - } - } - - public function attachRepository(PhabricatorRepository $repository) { - $this->repository = $repository; - return $this; - } - - public function getRepository() { - return $this->assertAttached($this->repository); - } - - public function getReleephFieldSelector() { - return new ReleephDefaultFieldSelector(); - } - - public function isTestFile($filename) { - $test_paths = $this->getDetail('testPaths', array()); - - foreach ($test_paths as $test_path) { - if (preg_match($test_path, $filename)) { - return true; - } - } - return false; - } - - -/* -( PhabricatorApplicationTransactionInterface )------------------------- */ - - - public function getApplicationTransactionEditor() { - return new ReleephProductEditor(); - } - - public function getApplicationTransactionTemplate() { - return new ReleephProductTransaction(); - } - - -/* -( PhabricatorPolicyInterface )----------------------------------------- */ - - - public function getCapabilities() { - return array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - ); - } - - public function getPolicy($capability) { - return PhabricatorPolicies::POLICY_USER; - } - - public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { - return false; - } - -} diff --git a/src/applications/releeph/storage/ReleephRequest.php b/src/applications/releeph/storage/ReleephRequest.php deleted file mode 100644 index c21f9b28c8..0000000000 --- a/src/applications/releeph/storage/ReleephRequest.php +++ /dev/null @@ -1,354 +0,0 @@ -getPusherIntent() == self::INTENT_WANT && - /** - * We use "!= pass" instead of "== want" in case the requestor intent is - * not present. In other words, only revert if the requestor explicitly - * passed. - */ - $this->getRequestorIntent() != self::INTENT_PASS; - } - - /** - * Will return INTENT_WANT if any pusher wants this request, and no pusher - * passes on this request. - */ - public function getPusherIntent() { - $product = $this->getBranch()->getProduct(); - - if (!$product->getPushers()) { - return self::INTENT_WANT; - } - - $found_pusher_want = false; - foreach ($this->userIntents as $phid => $intent) { - if ($product->isAuthoritativePHID($phid)) { - if ($intent == self::INTENT_PASS) { - return self::INTENT_PASS; - } - - $found_pusher_want = true; - } - } - - if ($found_pusher_want) { - return self::INTENT_WANT; - } else { - return null; - } - } - - public function getRequestorIntent() { - return idx($this->userIntents, $this->requestUserPHID); - } - - public function getStatus() { - return $this->calculateStatus(); - } - - public function getMonogram() { - return 'Y'.$this->getID(); - } - - public function getBranch() { - return $this->assertAttached($this->branch); - } - - public function attachBranch(ReleephBranch $branch) { - $this->branch = $branch; - return $this; - } - - public function getRequestedObject() { - return $this->assertAttached($this->requestedObject); - } - - public function attachRequestedObject($object) { - $this->requestedObject = $object; - return $this; - } - - private function calculateStatus() { - if ($this->shouldBeInBranch()) { - if ($this->getInBranch()) { - return ReleephRequestStatus::STATUS_PICKED; - } else { - return ReleephRequestStatus::STATUS_NEEDS_PICK; - } - } else { - if ($this->getInBranch()) { - return ReleephRequestStatus::STATUS_NEEDS_REVERT; - } else { - $intent_pass = self::INTENT_PASS; - $intent_want = self::INTENT_WANT; - - $has_been_in_branch = $this->getCommitIdentifier(); - // Regardless of why we reverted something, always say reverted if it - // was once in the branch. - if ($has_been_in_branch) { - return ReleephRequestStatus::STATUS_REVERTED; - } else if ($this->getPusherIntent() === $intent_pass) { - // Otherwise, if it has never been in the branch, explicitly say why: - return ReleephRequestStatus::STATUS_REJECTED; - } else if ($this->getRequestorIntent() === $intent_want) { - return ReleephRequestStatus::STATUS_REQUESTED; - } else { - return ReleephRequestStatus::STATUS_ABANDONED; - } - } - } - } - - -/* -( Lisk mechanics )----------------------------------------------------- */ - - protected function getConfiguration() { - return array( - self::CONFIG_AUX_PHID => true, - self::CONFIG_SERIALIZATION => array( - 'details' => self::SERIALIZATION_JSON, - 'userIntents' => self::SERIALIZATION_JSON, - ), - self::CONFIG_COLUMN_SCHEMA => array( - 'requestCommitPHID' => 'phid?', - 'commitIdentifier' => 'text40?', - 'commitPHID' => 'phid?', - 'pickStatus' => 'uint32?', - 'inBranch' => 'bool', - 'mailKey' => 'bytes20', - 'userIntents' => 'text?', - ), - self::CONFIG_KEY_SCHEMA => array( - 'key_phid' => null, - 'phid' => array( - 'columns' => array('phid'), - 'unique' => true, - ), - 'requestIdentifierBranch' => array( - 'columns' => array('requestCommitPHID', 'branchID'), - 'unique' => true, - ), - 'branchID' => array( - 'columns' => array('branchID'), - ), - 'key_requestedObject' => array( - 'columns' => array('requestedObjectPHID'), - ), - ), - ) + parent::getConfiguration(); - } - - public function generatePHID() { - return PhabricatorPHID::generateNewPHID( - ReleephRequestPHIDType::TYPECONST); - } - - public function save() { - if (!$this->getMailKey()) { - $this->setMailKey(Filesystem::readRandomCharacters(20)); - } - return parent::save(); - } - - -/* -( Helpful accessors )--------------------------------------------------- */ - - - public function getDetail($key, $default = null) { - return idx($this->getDetails(), $key, $default); - } - - public function setDetail($key, $value) { - $this->details[$key] = $value; - return $this; - } - - - /** - * Get the commit PHIDs this request is requesting. - * - * NOTE: For now, this always returns one PHID. - * - * @return list Commit PHIDs requested by this request. - */ - public function getCommitPHIDs() { - return array( - $this->requestCommitPHID, - ); - } - - public function getReason() { - // Backward compatibility: reason used to be called comments - $reason = $this->getDetail('reason'); - if (!$reason) { - return $this->getDetail('comments'); - } - return $reason; - } - - /** - * Allow a null summary, and fall back to the title of the commit. - */ - public function getSummaryForDisplay() { - $summary = $this->getDetail('summary'); - - if (!strlen($summary)) { - $commit = $this->loadPhabricatorRepositoryCommit(); - if ($commit) { - $summary = $commit->getSummary(); - } - } - - if (!strlen($summary)) { - $summary = pht('None'); - } - - return $summary; - } - -/* -( Loading external objects )------------------------------------------- */ - - public function loadPhabricatorRepositoryCommit() { - return id(new PhabricatorRepositoryCommit())->loadOneWhere( - 'phid = %s', - $this->getRequestCommitPHID()); - } - - public function loadPhabricatorRepositoryCommitData() { - $commit = $this->loadPhabricatorRepositoryCommit(); - if ($commit) { - return id(new PhabricatorRepositoryCommitData())->loadOneWhere( - 'commitID = %d', - $commit->getID()); - } - } - - -/* -( State change helpers )----------------------------------------------- */ - - public function setUserIntent(PhabricatorUser $user, $intent) { - $this->userIntents[$user->getPHID()] = $intent; - return $this; - } - - -/* -( Migrating to status-less ReleephRequests )--------------------------- */ - - protected function didReadData() { - if ($this->userIntents === null) { - $this->userIntents = array(); - } - } - - public function setStatus($value) { - throw new Exception(pht('`%s` is now deprecated!', 'status')); - } - -/* -( PhabricatorApplicationTransactionInterface )------------------------- */ - - - public function getApplicationTransactionEditor() { - return new ReleephRequestTransactionalEditor(); - } - - public function getApplicationTransactionTemplate() { - return new ReleephRequestTransaction(); - } - - -/* -( PhabricatorPolicyInterface )----------------------------------------- */ - - - public function getCapabilities() { - return array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - ); - } - - public function getPolicy($capability) { - return $this->getBranch()->getPolicy($capability); - } - - public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { - return $this->getBranch()->hasAutomaticCapability($capability, $viewer); - } - - public function describeAutomaticCapability($capability) { - return pht( - 'Pull requests have the same policies as the branches they are '. - 'requested against.'); - } - - - -/* -( PhabricatorCustomFieldInterface )------------------------------------ */ - - - public function getCustomFieldSpecificationForRole($role) { - return PhabricatorEnv::getEnvConfig('releeph.fields'); - } - - public function getCustomFieldBaseClass() { - return 'ReleephFieldSpecification'; - } - - public function getCustomFields() { - return $this->assertAttached($this->customFields); - } - - public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) { - $this->customFields = $fields; - return $this; - } - - -} diff --git a/src/applications/releeph/storage/ReleephRequestTransaction.php b/src/applications/releeph/storage/ReleephRequestTransaction.php deleted file mode 100644 index 6c7f98b3db..0000000000 --- a/src/applications/releeph/storage/ReleephRequestTransaction.php +++ /dev/null @@ -1,275 +0,0 @@ -getTransactionType()) { - default; - break; - } - return parent::hasChangeDetails(); - } - - public function getRequiredHandlePHIDs() { - $phids = parent::getRequiredHandlePHIDs(); - $phids[] = $this->getObjectPHID(); - - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_REQUEST: - case self::TYPE_DISCOVERY: - $phids[] = $new; - break; - - case self::TYPE_EDIT_FIELD: - self::searchForPHIDs($this->getOldValue(), $phids); - self::searchForPHIDs($this->getNewValue(), $phids); - break; - } - - return $phids; - } - - public function getTitle() { - $author_phid = $this->getAuthorPHID(); - $object_phid = $this->getObjectPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_REQUEST: - return pht( - '%s requested %s', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($new)); - break; - - case self::TYPE_USER_INTENT: - return $this->getIntentTitle(); - break; - - case self::TYPE_EDIT_FIELD: - $field = newv($this->getMetadataValue('fieldClass'), array()); - $name = $field->getName(); - - $markup = $name; - if ($this->getRenderingTarget() === - parent::TARGET_HTML) { - - $markup = hsprintf('%s', $name); - } - - return pht( - '%s changed the %s to "%s"', - $this->renderHandleLink($author_phid), - $markup, - $field->normalizeForTransactionView($this, $new)); - break; - - case self::TYPE_PICK_STATUS: - switch ($new) { - case ReleephRequest::PICK_OK: - return pht('%s found this request picks without error', - $this->renderHandleLink($author_phid)); - - case ReleephRequest::REVERT_OK: - return pht('%s found this request reverts without error', - $this->renderHandleLink($author_phid)); - - case ReleephRequest::PICK_FAILED: - return pht("%s couldn't pick this request", - $this->renderHandleLink($author_phid)); - - case ReleephRequest::REVERT_FAILED: - return pht("%s couldn't revert this request", - $this->renderHandleLink($author_phid)); - } - break; - - case self::TYPE_COMMIT: - $action_type = $this->getMetadataValue('action'); - switch ($action_type) { - case 'pick': - return pht( - '%s picked this request and committed the result upstream', - $this->renderHandleLink($author_phid)); - break; - - case 'revert': - return pht( - '%s reverted this request and committed the result upstream', - $this->renderHandleLink($author_phid)); - break; - } - break; - - case self::TYPE_MANUAL_IN_BRANCH: - $action = $new ? pht('picked') : pht('reverted'); - return pht( - '%s marked this request as manually %s', - $this->renderHandleLink($author_phid), - $action); - break; - - case self::TYPE_DISCOVERY: - return pht('%s discovered this commit as %s', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($new)); - break; - - default: - return parent::getTitle(); - break; - } - } - - public function getActionName() { - switch ($this->getTransactionType()) { - case self::TYPE_REQUEST: - return pht('Requested'); - - case self::TYPE_COMMIT: - $action_type = $this->getMetadataValue('action'); - switch ($action_type) { - case 'pick': - return pht('Picked'); - - case 'revert': - return pht('Reverted'); - } - } - - return parent::getActionName(); - } - - public function getColor() { - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_USER_INTENT: - switch ($new) { - case ReleephRequest::INTENT_WANT: - return PhabricatorTransactions::COLOR_GREEN; - case ReleephRequest::INTENT_PASS: - return PhabricatorTransactions::COLOR_RED; - } - } - return parent::getColor(); - } - - private static function searchForPHIDs($thing, array &$phids) { - /** - * To implement something like getRequiredHandlePHIDs() in a - * ReleephFieldSpecification, we'd have to provide the field with its - * ReleephRequest (so that it could load the PHIDs from the - * ReleephRequest's storage, and return them.) - * - * We don't have fields initialized with their ReleephRequests, but we can - * make a good guess at what handles will be needed for rendering the field - * in this transaction by inspecting the old and new values. - */ - if (!is_array($thing)) { - $thing = array($thing); - } - - foreach ($thing as $value) { - if (phid_get_type($value) !== - PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN) { - - $phids[] = $value; - } - } - } - - private function getIntentTitle() { - $author_phid = $this->getAuthorPHID(); - $object_phid = $this->getObjectPHID(); - - $new = $this->getNewValue(); - $is_pusher = $this->getMetadataValue('isPusher'); - - switch ($new) { - case ReleephRequest::INTENT_WANT: - if ($is_pusher) { - return pht( - '%s approved this request', - $this->renderHandleLink($author_phid)); - } else { - return pht( - '%s wanted this request', - $this->renderHandleLink($author_phid)); - } - - case ReleephRequest::INTENT_PASS: - if ($is_pusher) { - return pht( - '%s rejected this request', - $this->renderHandleLink($author_phid)); - } else { - return pht( - '%s passed on this request', - $this->renderHandleLink($author_phid)); - } - } - } - - public function shouldHide() { - $type = $this->getTransactionType(); - - if ($type === self::TYPE_USER_INTENT && - $this->getMetadataValue('isRQCreate')) { - - return true; - } - - if ($this->isBoringPickStatus()) { - return true; - } - - // ReleephSummaryFieldSpecification is usually blank when an RQ is created, - // creating a transaction change from null to "". Hide these! - if ($type === self::TYPE_EDIT_FIELD) { - if ($this->getOldValue() === null && $this->getNewValue() === '') { - return true; - } - } - return parent::shouldHide(); - } - - public function isBoringPickStatus() { - $type = $this->getTransactionType(); - if ($type === self::TYPE_PICK_STATUS) { - $new = $this->getNewValue(); - if ($new === ReleephRequest::PICK_OK || - $new === ReleephRequest::REVERT_OK) { - - return true; - } - } - return false; - } - -} diff --git a/src/applications/releeph/storage/ReleephRequestTransactionComment.php b/src/applications/releeph/storage/ReleephRequestTransactionComment.php deleted file mode 100644 index 71844b7367..0000000000 --- a/src/applications/releeph/storage/ReleephRequestTransactionComment.php +++ /dev/null @@ -1,10 +0,0 @@ -isListView = $is_list_view; - return $this; - } - - public function getIsListView() { - return $this->isListView; - } - - public function setCustomFields(PhabricatorCustomFieldList $custom_fields) { - $this->customFields = $custom_fields; - return $this; - } - - public function getCustomFields() { - return $this->customFields; - } - - public function setPullRequest(ReleephRequest $pull_request) { - $this->pullRequest = $pull_request; - return $this; - } - - public function getPullRequest() { - return $this->pullRequest; - } - - public function render() { - $viewer = $this->getUser(); - - $field_list = $this->getCustomFields(); - $pull = $this->getPullRequest(); - - $header = $this->buildHeader($pull); - - $action_list = $this->buildActionList($pull); - - $property_list = id(new PHUIPropertyListView()) - ->setUser($viewer) - ->setActionList($action_list); - - $field_list->appendFieldsToPropertyList( - $pull, - $viewer, - $property_list); - - $warnings = $this->getWarnings($pull); - - if ($this->getIsListView()) { - Javelin::initBehavior('releeph-request-state-change'); - } - - return id(new PHUIObjectBoxView()) - ->setHeader($header) - ->setFormErrors($warnings) - ->addSigil('releeph-request-box') - ->setMetadata(array('uri' => '/'.$pull->getMonogram())) - ->appendChild($property_list); - } - - private function buildHeader(ReleephRequest $pull) { - $header_text = $pull->getSummaryForDisplay(); - if ($this->getIsListView()) { - $header_text = phutil_tag( - 'a', - array( - 'href' => '/'.$pull->getMonogram(), - ), - $header_text); - } - - $header = id(new PHUIHeaderView()) - ->setHeader($header_text) - ->setUser($this->getUser()) - ->setPolicyObject($pull); - - switch ($pull->getStatus()) { - case ReleephRequestStatus::STATUS_REQUESTED: - $icon = 'open'; - $color = null; - break; - case ReleephRequestStatus::STATUS_REJECTED: - $icon = 'reject'; - $color = 'red'; - break; - case ReleephRequestStatus::STATUS_PICKED: - $icon = 'accept'; - $color = 'green'; - break; - case ReleephRequestStatus::STATUS_REVERTED: - case ReleephRequestStatus::STATUS_ABANDONED: - $icon = 'reject'; - $color = 'dark'; - break; - case ReleephRequestStatus::STATUS_NEEDS_PICK: - $icon = 'warning'; - $color = 'green'; - break; - case ReleephRequestStatus::STATUS_NEEDS_REVERT: - $icon = 'warning'; - $color = 'red'; - break; - default: - $icon = 'question'; - $color = null; - break; - } - $text = ReleephRequestStatus::getStatusDescriptionFor($pull->getStatus()); - $header->setStatus($icon, $color, $text); - - return $header; - } - - private function buildActionList(ReleephRequest $pull) { - $viewer = $this->getUser(); - $id = $pull->getID(); - - $edit_uri = '/releeph/request/edit/'.$id.'/'; - - $view = id(new PhabricatorActionListView()) - ->setUser($viewer); - - $product = $pull->getBranch()->getProduct(); - $viewer_is_pusher = $product->isAuthoritativePHID($viewer->getPHID()); - $viewer_is_requestor = ($pull->getRequestUserPHID() == $viewer->getPHID()); - - if ($viewer_is_pusher) { - $yes_text = pht('Approve Pull'); - $no_text = pht('Reject Pull'); - $yes_icon = 'fa-check'; - $no_icon = 'fa-times'; - } else if ($viewer_is_requestor) { - $yes_text = pht('Request Pull'); - $no_text = pht('Cancel Pull'); - $yes_icon = 'fa-check'; - $no_icon = 'fa-times'; - } else { - $yes_text = pht('Support Pull'); - $no_text = pht('Discourage Pull'); - $yes_icon = 'fa-thumbs-o-up'; - $no_icon = 'fa-thumbs-o-down'; - } - - $yes_href = '/releeph/request/action/want/'.$id.'/'; - $no_href = '/releeph/request/action/pass/'.$id.'/'; - - $intents = $pull->getUserIntents(); - $current_intent = idx($intents, $viewer->getPHID()); - - $yes_disabled = ($current_intent == ReleephRequest::INTENT_WANT); - $no_disabled = ($current_intent == ReleephRequest::INTENT_PASS); - - $use_workflow = (!$this->getIsListView()); - - $view->addAction( - id(new PhabricatorActionView()) - ->setName($yes_text) - ->setHref($yes_href) - ->setWorkflow($use_workflow) - ->setRenderAsForm($use_workflow) - ->setDisabled($yes_disabled) - ->addSigil('releeph-request-state-change') - ->addSigil('want') - ->setIcon($yes_icon)); - - $view->addAction( - id(new PhabricatorActionView()) - ->setName($no_text) - ->setHref($no_href) - ->setWorkflow($use_workflow) - ->setRenderAsForm($use_workflow) - ->setDisabled($no_disabled) - ->addSigil('releeph-request-state-change') - ->addSigil('pass') - ->setIcon($no_icon)); - - - if ($viewer_is_pusher || $viewer_is_requestor) { - - $pulled_href = '/releeph/request/action/mark-manually-picked/'.$id.'/'; - $revert_href = '/releeph/request/action/mark-manually-reverted/'.$id.'/'; - - if ($pull->getInBranch()) { - $view->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Mark as Reverted')) - ->setHref($revert_href) - ->setWorkflow($use_workflow) - ->setRenderAsForm($use_workflow) - ->addSigil('releeph-request-state-change') - ->addSigil('mark-manually-reverted') - ->setIcon($no_icon)); - } else { - $view->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Mark as Pulled')) - ->setHref($pulled_href) - ->setWorkflow($use_workflow) - ->setRenderAsForm($use_workflow) - ->addSigil('releeph-request-state-change') - ->addSigil('mark-manually-picked') - ->setIcon('fa-exclamation-triangle')); - } - } - - - if (!$this->getIsListView()) { - $view->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Edit Pull Request')) - ->setIcon('fa-pencil') - ->setHref($edit_uri)); - } - - return $view; - } - - private function getWarnings(ReleephRequest $pull) { - $warnings = array(); - - switch ($pull->getStatus()) { - case ReleephRequestStatus::STATUS_NEEDS_PICK: - if ($pull->getPickStatus() == ReleephRequest::PICK_FAILED) { - $warnings[] = pht('Last pull failed!'); - } - break; - case ReleephRequestStatus::STATUS_NEEDS_REVERT: - if ($pull->getPickStatus() == ReleephRequest::REVERT_FAILED) { - $warnings[] = pht('Last revert failed!'); - } - break; - } - - return $warnings; - } - -} diff --git a/src/applications/releeph/view/branch/ReleephBranchPreviewView.php b/src/applications/releeph/view/branch/ReleephBranchPreviewView.php deleted file mode 100644 index 462ed73db3..0000000000 --- a/src/applications/releeph/view/branch/ReleephBranchPreviewView.php +++ /dev/null @@ -1,62 +0,0 @@ -setID($celerity_id); - $this->dynamics[$param_name] = $celerity_id; - return $this; - } - - public function addStatic($param_name, $value) { - $this->statics[$param_name] = $value; - return $this; - } - - protected function getCustomControlClass() { - require_celerity_resource('releeph-preview-branch'); - return 'releeph-preview-branch'; - } - - protected function renderInput() { - static $required_params = array( - 'repositoryPHID', - 'projectName', - 'isSymbolic', - 'template', - ); - - $all_params = array_merge($this->statics, $this->dynamics); - foreach ($required_params as $param_name) { - if (idx($all_params, $param_name) === null) { - throw new Exception( - pht( - "'%s' is not set as either a static or dynamic!", - $param_name)); - } - } - - $output_id = celerity_generate_unique_node_id(); - - Javelin::initBehavior('releeph-preview-branch', array( - 'uri' => '/releeph/branch/preview/', - 'outputID' => $output_id, - 'params' => array( - 'static' => $this->statics, - 'dynamic' => $this->dynamics, - ), - )); - - return phutil_tag( - 'div', - array( - 'id' => $output_id, - ), - ''); - } - -} diff --git a/src/applications/releeph/view/branch/ReleephBranchTemplate.php b/src/applications/releeph/view/branch/ReleephBranchTemplate.php deleted file mode 100644 index f29e7a9fc2..0000000000 --- a/src/applications/releeph/view/branch/ReleephBranchTemplate.php +++ /dev/null @@ -1,195 +0,0 @@ -setViewer($viewer) - ->withPHIDs(array($repository_phid)) - ->executeOne(); - - $fake_handle = 'SOFAKE'; - if ($repository) { - $fake_handle = id(new PhabricatorObjectHandle()) - ->setName($repository->formatCommitName('100000000000')); - } - return $fake_handle; - } - - public function setCommitHandle(PhabricatorObjectHandle $handle) { - $this->commitHandle = $handle; - return $this; - } - - public function setBranchDate($branch_date) { - $this->branchDate = $branch_date; - return $this; - } - - public function setReleephProjectName($project_name) { - $this->projectName = $project_name; - return $this; - } - - public function setSymbolic($is_symbolic) { - $this->isSymbolic = $is_symbolic; - return $this; - } - - public function interpolate($template) { - if (!$this->projectName) { - return array('', array()); - } - - list($name, $name_errors) = $this->interpolateInner( - $template, - $this->isSymbolic); - - if ($this->isSymbolic) { - return array($name, $name_errors); - } else { - $validate_errors = $this->validateAsBranchName($name); - $errors = array_merge($name_errors, $validate_errors); - return array($name, $errors); - } - } - - /* - * xsprintf() would be useful here, but that's for formatting concrete lists - * of things in a certain way... - * - * animal_printf('%A %A %A', $dog1, $dog2, $dog3); - * - * ...rather than interpolating percent-control-strings like strftime does. - */ - private function interpolateInner($template, $is_symbolic) { - $name = $template; - $errors = array(); - - $safe_project_name = str_replace(' ', '-', $this->projectName); - $short_commit_id = last( - preg_split('/r[A-Z]+/', $this->commitHandle->getName())); - - $interpolations = array(); - for ($ii = 0; $ii < strlen($name); $ii++) { - $char = substr($name, $ii, 1); - $prev = null; - if ($ii > 0) { - $prev = substr($name, $ii - 1, 1); - } - $next = substr($name, $ii + 1, 1); - if ($next && $char == '%' && $prev != '%') { - $interpolations[$ii] = $next; - } - } - - $variable_interpolations = array(); - - $reverse_interpolations = $interpolations; - krsort($reverse_interpolations); - - if ($this->branchDate) { - $branch_date = $this->branchDate; - } else { - $branch_date = $this->commitHandle->getTimestamp(); - } - - foreach ($reverse_interpolations as $position => $code) { - $replacement = null; - switch ($code) { - case 'v': - $replacement = $this->commitHandle->getName(); - $is_variable = true; - break; - - case 'V': - $replacement = $short_commit_id; - $is_variable = true; - break; - - case 'P': - $replacement = $safe_project_name; - $is_variable = false; - break; - - case 'p': - $replacement = strtolower($safe_project_name); - $is_variable = false; - break; - - default: - // Format anything else using strftime() - $replacement = strftime("%{$code}", $branch_date); - $is_variable = true; - break; - } - - if ($is_variable) { - $variable_interpolations[] = $code; - } - $name = substr_replace($name, $replacement, $position, 2); - } - - if (!$is_symbolic && !$variable_interpolations) { - $errors[] = pht("Include additional interpolations that aren't static!"); - } - - return array($name, $errors); - } - - private function validateAsBranchName($name) { - $errors = array(); - - if (preg_match('{^/}', $name) || preg_match('{/$}', $name)) { - $errors[] = pht("Branches cannot begin or end with '%s'", '/'); - } - - if (preg_match('{//+}', $name)) { - $errors[] = pht("Branches cannot contain multiple consecutive '%s'", '/'); - } - - $parts = array_filter(explode('/', $name)); - foreach ($parts as $index => $part) { - $part_error = null; - if (preg_match('{^\.}', $part) || preg_match('{\.$}', $part)) { - $errors[] = pht("Path components cannot begin or end with '%s'", '.'); - } else if (preg_match('{^(?!\w)}', $part)) { - $errors[] = pht('Path components must begin with an alphanumeric.'); - } else if (!preg_match('{^\w ([\w-_%\.]* [\w-_%])?$}x', $part)) { - $errors[] = pht( - "Path components may only contain alphanumerics ". - "or '%s', '%s' or '%s'.", - '-', - '_', - '.'); - } - } - - return $errors; - } -} diff --git a/src/applications/releeph/view/request/ReleephRequestTypeaheadControl.php b/src/applications/releeph/view/request/ReleephRequestTypeaheadControl.php deleted file mode 100644 index e8ed53f946..0000000000 --- a/src/applications/releeph/view/request/ReleephRequestTypeaheadControl.php +++ /dev/null @@ -1,60 +0,0 @@ -repo = $repo; - return $this; - } - - public function setStartTime($epoch) { - $this->startTime = $epoch; - return $this; - } - - protected function getCustomControlClass() { - return 'releeph-request-typeahead'; - } - - protected function renderInput() { - $id = celerity_generate_unique_node_id(); - - $div = phutil_tag( - 'div', - array( - 'style' => 'position: relative;', - 'id' => $id, - ), - phutil_tag( - 'input', - array( - 'autocomplete' => 'off', - 'type' => 'text', - 'name' => $this->getName(), - ), - '')); - - require_celerity_resource('releeph-request-typeahead-css'); - - Javelin::initBehavior('releeph-request-typeahead', array( - 'id' => $id, - 'src' => '/releeph/request/typeahead/', - 'placeholder' => self::PLACEHOLDER, - 'value' => $this->getValue(), - 'aux' => array( - 'repo' => $this->repo->getID(), - 'callsign' => $this->repo->getCallsign(), - 'since' => $this->startTime, - 'limit' => 16, - ), - )); - - return $div; - } - -} diff --git a/src/applications/remarkup/RemarkupValue.php b/src/applications/remarkup/RemarkupValue.php new file mode 100644 index 0000000000..84546ee2b9 --- /dev/null +++ b/src/applications/remarkup/RemarkupValue.php @@ -0,0 +1,27 @@ +corpus = $corpus; + return $this; + } + + public function getCorpus() { + return $this->corpus; + } + + public function setMetadata(array $metadata) { + $this->metadata = $metadata; + return $this; + } + + public function getMetadata() { + return $this->metadata; + } + +} diff --git a/src/applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php b/src/applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php index f9169cd08c..ae181f9860 100644 --- a/src/applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php +++ b/src/applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php @@ -11,7 +11,7 @@ final class RemarkupProcessConduitAPIMethod extends ConduitAPIMethod { } public function getMethodDescription() { - return pht('Process text through remarkup in Phabricator context.'); + return pht('Process text through remarkup.'); } protected function defineReturnType() { diff --git a/src/applications/repository/config/PhabricatorRepositoryConfigOptions.php b/src/applications/repository/config/PhabricatorRepositoryConfigOptions.php index 26e0e9d5d1..f01515501a 100644 --- a/src/applications/repository/config/PhabricatorRepositoryConfigOptions.php +++ b/src/applications/repository/config/PhabricatorRepositoryConfigOptions.php @@ -28,9 +28,10 @@ final class PhabricatorRepositoryConfigOptions ->setDescription( pht( 'The default location in which to store working copies and other '. - 'data about repositories. Phabricator will control and manage '. + 'data about repositories. %s will control and manage '. 'data here, so you should **not** choose an existing directory '. - 'full of data you care about.')), + 'full of data you care about.', + PlatformSymbols::getPlatformServerName())), ); } diff --git a/src/applications/repository/daemon/PhabricatorMercurialGraphStream.php b/src/applications/repository/daemon/PhabricatorMercurialGraphStream.php index 205b267ed5..6d990b025b 100644 --- a/src/applications/repository/daemon/PhabricatorMercurialGraphStream.php +++ b/src/applications/repository/daemon/PhabricatorMercurialGraphStream.php @@ -16,13 +16,23 @@ final class PhabricatorMercurialGraphStream private $local = array(); private $localParents = array(); - public function __construct(PhabricatorRepository $repository, $commit) { + public function __construct(PhabricatorRepository $repository, + $start_commit = null) { + $this->repository = $repository; + $command = 'log --template %s --rev %s'; + $template = '{rev}\1{node}\1{date}\1{parents}\2'; + if ($start_commit !== null) { + $revset = hgsprintf('reverse(ancestors(%s))', $start_commit); + } else { + $revset = 'reverse(all())'; + } + $future = $repository->getLocalCommandFuture( - 'log --template %s --rev %s', - '{rev}\1{node}\1{date}\1{parents}\2', - hgsprintf('reverse(ancestors(%s))', $commit)); + $command, + $template, + $revset); $this->iterator = new LinesOfALargeExecFuture($future); $this->iterator->setDelimiter("\2"); diff --git a/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php b/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php index 3081a9c5a3..8f5b346ecd 100644 --- a/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php +++ b/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php @@ -780,8 +780,7 @@ final class PhabricatorRepositoryDiscoveryEngine } private function markUnreachableCommits(PhabricatorRepository $repository) { - // For now, this is only supported for Git. - if (!$repository->isGit()) { + if (!$repository->isGit() && !$repository->isHg()) { return; } @@ -799,7 +798,11 @@ final class PhabricatorRepositoryDiscoveryEngine } // We can share a single graph stream across all the checks we need to do. - $stream = new PhabricatorGitGraphStream($repository); + if ($repository->isGit()) { + $stream = new PhabricatorGitGraphStream($repository); + } else if ($repository->isHg()) { + $stream = new PhabricatorMercurialGraphStream($repository); + } foreach ($old_refs as $old_ref) { $identifier = $old_ref->getCommitIdentifier(); @@ -812,7 +815,7 @@ final class PhabricatorRepositoryDiscoveryEngine private function markUnreachableFrom( PhabricatorRepository $repository, - PhabricatorGitGraphStream $stream, + PhabricatorRepositoryGraphStream $stream, $identifier) { $unreachable = array(); diff --git a/src/applications/repository/engine/PhabricatorRepositoryMirrorEngine.php b/src/applications/repository/engine/PhabricatorRepositoryMirrorEngine.php index ed856957f0..32689d3a77 100644 --- a/src/applications/repository/engine/PhabricatorRepositoryMirrorEngine.php +++ b/src/applications/repository/engine/PhabricatorRepositoryMirrorEngine.php @@ -16,7 +16,7 @@ final class PhabricatorRepositoryMirrorEngine if (PhabricatorEnv::getEnvConfig('phabricator.silent')) { $this->log( - pht('Phabricator is running in silent mode; declining to mirror.')); + pht('This software is running in silent mode; declining to mirror.')); return; } diff --git a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php index 7d216421d7..12aee9bc51 100644 --- a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php +++ b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php @@ -723,7 +723,6 @@ final class PhabricatorRepositoryPullEngine // This behavior has been reverted, but users who updated between Feb 1, // 2012 and Mar 1, 2012 will have the erroring version. Do a dumb test // against stdout to check for this possibility. - // See: https://github.com/phacility/phabricator/issues/101/ // NOTE: Mercurial has translated versions, which translate this error // string. In a translated version, the string will be something else, diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementCacheWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementCacheWorkflow.php index a78dff1a73..81955f36f3 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementCacheWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementCacheWorkflow.php @@ -47,7 +47,7 @@ final class PhabricatorRepositoryManagementCacheWorkflow ->loadPathIDs(); if (empty($path_map[$path_name])) { throw new PhutilArgumentUsageException( - pht('Path "%s" is not known to Phabricator.', $path_name)); + pht('Path "%s" is not unknown.', $path_name)); } $path_id = $path_map[$path_name]; diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementMarkReachableWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementMarkReachableWorkflow.php index 84dea48948..1f235c3fb6 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementMarkReachableWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementMarkReachableWorkflow.php @@ -48,11 +48,11 @@ final class PhabricatorRepositoryManagementMarkReachableWorkflow } private function markReachable(PhabricatorRepository $repository) { - if (!$repository->isGit()) { + if (!$repository->isGit() && !$repository->isHg()) { throw new PhutilArgumentUsageException( pht( - 'Only Git repositories are supported, this repository ("%s") is '. - 'not a Git repository.', + 'Only Git and Mercurial repositories are supported, unable to '. + 'operate on this repository ("%s").', $repository->getDisplayName())); } @@ -65,7 +65,12 @@ final class PhabricatorRepositoryManagementMarkReachableWorkflow $flag = PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE; - $graph = new PhabricatorGitGraphStream($repository); + if ($repository->isGit()) { + $graph = new PhabricatorGitGraphStream($repository); + } else if ($repository->isHg()) { + $graph = new PhabricatorMercurialGraphStream($repository); + } + foreach ($commits as $commit) { $identifier = $commit->getCommitIdentifier(); diff --git a/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php b/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php index cd693be605..08f0ada159 100644 --- a/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php @@ -26,10 +26,6 @@ final class PhabricatorRepositoryGitLFSRefQuery return new PhabricatorRepositoryGitLFSRef(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php b/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php index 7de97de4d6..2b05b542d5 100644 --- a/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php @@ -66,10 +66,6 @@ final class PhabricatorRepositoryIdentityQuery return 'identity'; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php b/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php index ce14a6f831..8d4f14e0ce 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php @@ -40,10 +40,6 @@ final class PhabricatorRepositoryPullEventQuery return new PhabricatorRepositoryPullEvent(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $events) { // If a pull targets an invalid repository or fails before authenticating, // it may not have an associated repository. diff --git a/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php b/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php index f3e5fc62b4..d1ce937b86 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php @@ -38,10 +38,6 @@ final class PhabricatorRepositoryPushEventQuery return new PhabricatorRepositoryPushEvent(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $events) { $repository_phids = mpull($events, 'getRepositoryPHID'); $repositories = id(new PhabricatorRepositoryQuery()) diff --git a/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php b/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php index d4734f61e6..16897a1e4b 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php @@ -64,10 +64,6 @@ final class PhabricatorRepositoryPushLogQuery return new PhabricatorRepositoryPushLog(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $logs) { $event_phids = mpull($logs, 'getPushEventPHID'); $events = id(new PhabricatorObjectQuery()) diff --git a/src/applications/repository/query/PhabricatorRepositoryQuery.php b/src/applications/repository/query/PhabricatorRepositoryQuery.php index 9150c692b6..05b011e85a 100644 --- a/src/applications/repository/query/PhabricatorRepositoryQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryQuery.php @@ -633,7 +633,7 @@ final class PhabricatorRepositoryQuery $this->uuids); } - if (strlen($this->datasourceQuery)) { + if (phutil_nonempty_string($this->datasourceQuery)) { // This handles having "rP" match callsigns starting with "P...". $query = trim($this->datasourceQuery); if (preg_match('/^r/', $query)) { diff --git a/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php b/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php index b3c960e025..5e894333f6 100644 --- a/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php @@ -50,10 +50,6 @@ final class PhabricatorRepositoryRefCursorQuery return new PhabricatorRepositoryRefCursor(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $refs) { $repository_phids = mpull($refs, 'getRepositoryPHID'); diff --git a/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php b/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php index 542cb5cdc0..cc568ef8e1 100644 --- a/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php +++ b/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php @@ -34,10 +34,6 @@ final class PhabricatorRepositorySyncEventQuery return new PhabricatorRepositorySyncEvent(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $events) { $repository_phids = mpull($events, 'getRepositoryPHID'); $repository_phids = array_filter($repository_phids); diff --git a/src/applications/repository/query/PhabricatorRepositoryURIQuery.php b/src/applications/repository/query/PhabricatorRepositoryURIQuery.php index 71252a6fb7..5b75e1ef63 100644 --- a/src/applications/repository/query/PhabricatorRepositoryURIQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryURIQuery.php @@ -34,10 +34,6 @@ final class PhabricatorRepositoryURIQuery return new PhabricatorRepositoryURI(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 8c98dd8756..c68eb22668 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -193,7 +193,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO public function getMonogram() { $callsign = $this->getCallsign(); - if (strlen($callsign)) { + if (phutil_nonempty_string($callsign)) { return "r{$callsign}"; } @@ -203,7 +203,8 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO public function getDisplayName() { $slug = $this->getRepositorySlug(); - if (strlen($slug)) { + + if (phutil_nonempty_string($slug)) { return $slug; } @@ -281,9 +282,11 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO public function getSubversionBaseURI($commit = null) { $subpath = $this->getDetail('svn-subpath'); - if (!strlen($subpath)) { + + if (!phutil_nonempty_string($subpath)) { $subpath = null; } + return $this->getSubversionPathURI($subpath, $commit); } @@ -301,7 +304,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO $uri = rtrim($uri, '/'); - if (strlen($path)) { + if (phutil_nonempty_string($path)) { $path = rawurlencode($path); $path = str_replace('%2F', '/', $path); $uri = $uri.'/'.ltrim($path, '/'); @@ -499,7 +502,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO public function passthruRemoteCommand($pattern /* , $arg, ... */) { $args = func_get_args(); - return $this->newRemoteCommandPassthru($args)->execute(); + return $this->newRemoteCommandPassthru($args)->resolve(); } private function newRemoteCommandFuture(array $argv) { @@ -540,7 +543,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO public function passthruLocalCommand($pattern /* , $arg, ... */) { $args = func_get_args(); - return $this->newLocalCommandPassthru($args)->execute(); + return $this->newLocalCommandPassthru($args)->resolve(); } private function newLocalCommandFuture(array $argv) { @@ -574,12 +577,12 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO public function getURI() { $short_name = $this->getRepositorySlug(); - if (strlen($short_name)) { + if (phutil_nonempty_string($short_name)) { return "/source/{$short_name}/"; } $callsign = $this->getCallsign(); - if (strlen($callsign)) { + if (phutil_nonempty_string($callsign)) { return "/diffusion/{$callsign}/"; } @@ -593,7 +596,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO public function getCommitURI($identifier) { $callsign = $this->getCallsign(); - if (strlen($callsign)) { + if (phutil_nonempty_string($callsign)) { return "/r{$callsign}{$identifier}"; } @@ -736,25 +739,26 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO return $this->getCommitURI($commit); } - if (strlen($path)) { + if (phutil_nonempty_string($path)) { $path = ltrim($path, '/'); $path = str_replace(array(';', '$'), array(';;', '$$'), $path); $path = phutil_escape_uri($path); } $raw_branch = $branch; - if (strlen($branch)) { + if (phutil_nonempty_string($branch)) { $branch = phutil_escape_uri_path_component($branch); $path = "{$branch}/{$path}"; } $raw_commit = $commit; - if (strlen($commit)) { + if (phutil_nonempty_scalar($commit)) { $commit = str_replace('$', '$$', $commit); $commit = ';'.phutil_escape_uri($commit); } - if (strlen($line)) { + $line = phutil_string_cast($line); + if (phutil_nonempty_string($line)) { $line = '$'.phutil_escape_uri($line); } @@ -862,7 +866,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO public function getDefaultBranch() { $default = $this->getDetail('default-branch'); - if (strlen($default)) { + if (phutil_nonempty_string($default)) { return $default; } @@ -1943,7 +1947,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO 'point at this host, or the "device.id" configuration file on '. 'this host may be incorrect.'. "\n\n". - 'Requests routed within the cluster by Phabricator are always '. + 'Requests routed within the cluster are always '. 'expected to be sent to a node which can serve the request. To '. 'prevent loops, this request will not be proxied again.'. "\n\n". @@ -2205,7 +2209,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO throw new PhutilAggregateException( pht( 'Unable to read device public key while attempting to make '. - 'authenticated method call within the Phabricator cluster. '. + 'authenticated method call within the cluster. '. 'Use `%s` to register keys for this device. Exception: %s', 'bin/almanac register', $ex->getMessage()), @@ -2220,7 +2224,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO throw new PhutilAggregateException( pht( 'Unable to read device private key while attempting to make '. - 'authenticated method call within the Phabricator cluster. '. + 'authenticated method call within the cluster. '. 'Use `%s` to register keys for this device. Exception: %s', 'bin/almanac register', $ex->getMessage()), @@ -2269,10 +2273,9 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO $never_proxy); if (!$client) { - $result = id(new ConduitCall($method, $params)) - ->setUser($viewer) - ->execute(); - $future = new ImmediateFuture($result); + $conduit_call = id(new ConduitCall($method, $params)) + ->setUser($viewer); + $future = new MethodCallFuture($conduit_call, 'execute'); } else { $future = $client->callMethod($method, $params); } diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommit.php b/src/applications/repository/storage/PhabricatorRepositoryCommit.php index 31413ea0c2..9e20a36676 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryCommit.php +++ b/src/applications/repository/storage/PhabricatorRepositoryCommit.php @@ -674,9 +674,9 @@ final class PhabricatorRepositoryCommit return array( 'buildable.commit' => pht('The commit identifier, if applicable.'), 'repository.callsign' => - pht('The callsign of the repository in Phabricator.'), + pht('The callsign of the repository.'), 'repository.phid' => - pht('The PHID of the repository in Phabricator.'), + pht('The PHID of the repository.'), 'repository.vcs' => pht('The version control system, either "svn", "hg" or "git".'), 'repository.uri' => @@ -714,8 +714,8 @@ final class PhabricatorRepositoryCommit if (!$path) { throw new Exception( pht( - 'This commit ("%s") is associated with a repository ("%s") that '. - 'with a remote URI ("%s") that does not appear to be hosted on '. + 'This commit ("%s") is associated with a repository ("%s") which '. + 'has a remote URI ("%s") that does not appear to be hosted on '. 'GitHub. Repositories must be hosted on GitHub to be built with '. 'CircleCI.', $commit_phid, diff --git a/src/applications/repository/storage/PhabricatorRepositoryURI.php b/src/applications/repository/storage/PhabricatorRepositoryURI.php index 98c08baa00..26db694a66 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryURI.php +++ b/src/applications/repository/storage/PhabricatorRepositoryURI.php @@ -501,7 +501,7 @@ final class PhabricatorRepositoryURI 'color' => 'green', 'label' => pht('Observe'), 'note' => pht( - 'Phabricator will observe changes to this URI and copy them.'), + 'Changes to this URI will be observed and pulled.'), 'short' => pht('Copy from a remote.'), ), self::IO_MIRROR => array( @@ -509,7 +509,7 @@ final class PhabricatorRepositoryURI 'color' => 'green', 'label' => pht('Mirror'), 'note' => pht( - 'Phabricator will push a copy of any changes to this URI.'), + 'A copy of any changes will be pushed to this URI.'), 'short' => pht('Push a copy to a remote.'), ), self::IO_NONE => array( @@ -517,7 +517,7 @@ final class PhabricatorRepositoryURI 'color' => 'grey', 'label' => pht('No I/O'), 'note' => pht( - 'Phabricator will not push or pull any changes to this URI.'), + 'No changes will be pushed or pulled from this URI.'), 'short' => pht('Do not perform any I/O.'), ), self::IO_READ => array( @@ -525,8 +525,7 @@ final class PhabricatorRepositoryURI 'color' => 'blue', 'label' => pht('Read Only'), 'note' => pht( - 'Phabricator will serve a read-only copy of the repository from '. - 'this URI.'), + 'A read-only copy of the repository will be served from this URI.'), 'short' => pht('Serve repository in read-only mode.'), ), self::IO_READWRITE => array( @@ -534,8 +533,7 @@ final class PhabricatorRepositoryURI 'color' => 'blue', 'label' => pht('Read/Write'), 'note' => pht( - 'Phabricator will serve a read/write copy of the repository from '. - 'this URI.'), + 'A read/write copy of the repository will be served from this URI.'), 'short' => pht('Serve repository in read/write mode.'), ), ); diff --git a/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php b/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php index ea83aa9d56..ca755bbaee 100644 --- a/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php +++ b/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php @@ -100,55 +100,6 @@ final class PhabricatorRepositoryTestCase } - public function testFilterMercurialDebugOutput() { - $map = array( - '' => '', - - "quack\n" => "quack\n", - - "ignoring untrusted configuration option x.y = z\nquack\n" => - "quack\n", - - "ignoring untrusted configuration option x.y = z\n". - "ignoring untrusted configuration option x.y = z\n". - "quack\n" => - "quack\n", - - "ignoring untrusted configuration option x.y = z\n". - "ignoring untrusted configuration option x.y = z\n". - "ignoring untrusted configuration option x.y = z\n". - "quack\n" => - "quack\n", - - "quack\n". - "ignoring untrusted configuration option x.y = z\n". - "ignoring untrusted configuration option x.y = z\n". - "ignoring untrusted configuration option x.y = z\n" => - "quack\n", - - "ignoring untrusted configuration option x.y = z\n". - "ignoring untrusted configuration option x.y = z\n". - "duck\n". - "ignoring untrusted configuration option x.y = z\n". - "ignoring untrusted configuration option x.y = z\n". - "bread\n". - "ignoring untrusted configuration option x.y = z\n". - "quack\n" => - "duck\nbread\nquack\n", - - "ignoring untrusted configuration option x.y = z\n". - "duckignoring untrusted configuration option x.y = z\n". - "quack" => - 'duckquack', - ); - - foreach ($map as $input => $expect) { - $actual = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( - $input); - $this->assertEqual($expect, $actual, $input); - } - } - public function testRepositoryShortNameValidation() { $good = array( 'sensible-repository', diff --git a/src/applications/repository/xaction/PhabricatorRepositoryVCSTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryVCSTransaction.php index d5944d5425..84ee86dc76 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryVCSTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryVCSTransaction.php @@ -48,7 +48,7 @@ final class PhabricatorRepositoryVCSTransaction $errors[] = $this->newInvalidError( pht( 'Specified version control system must be a VCS '. - 'recognized by Phabricator. Valid systems are: %s.', + 'recognized by this software. Valid systems are: %s.', implode(', ', array_keys($vcs_map))), $xaction); } diff --git a/src/applications/search/controller/PhabricatorSearchDeleteController.php b/src/applications/search/controller/PhabricatorSearchDeleteController.php index 9cbabd3a2f..fe3836017f 100644 --- a/src/applications/search/controller/PhabricatorSearchDeleteController.php +++ b/src/applications/search/controller/PhabricatorSearchDeleteController.php @@ -42,6 +42,19 @@ final class PhabricatorSearchDeleteController } $named_query = $engine->getBuiltinQuery($key); + + // After loading a global query, make sure the viewer actually has + // permission to view and edit it. + + PhabricatorPolicyFilter::requireCapability( + $viewer, + $named_query, + PhabricatorPolicyCapability::CAN_VIEW); + + PhabricatorPolicyFilter::requireCapability( + $viewer, + $named_query, + PhabricatorPolicyCapability::CAN_EDIT); } $builtin = null; diff --git a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php index aecc44699c..e9607f9539 100644 --- a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php +++ b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php @@ -60,7 +60,7 @@ abstract class PhabricatorSearchEngineAPIMethod PhabricatorEnv::getDoclink('Conduit API: Using Search Endpoints')); } - final public function getMethodDocumentation() { + final protected function newDocumentationPages(PhabricatorUser $viewer) { $viewer = $this->getViewer(); $engine = $this->newSearchEngine() @@ -70,17 +70,18 @@ abstract class PhabricatorSearchEngineAPIMethod $out = array(); - $out[] = $this->buildQueriesBox($engine); - $out[] = $this->buildConstraintsBox($engine); - $out[] = $this->buildOrderBox($engine, $query); - $out[] = $this->buildFieldsBox($engine); - $out[] = $this->buildAttachmentsBox($engine); - $out[] = $this->buildPagingBox($engine); + $out[] = $this->buildQueriesDocumentationPage($viewer, $engine); + $out[] = $this->buildConstraintsDocumentationPage($viewer, $engine); + $out[] = $this->buildOrderDocumentationPage($viewer, $engine, $query); + $out[] = $this->buildFieldsDocumentationPage($viewer, $engine); + $out[] = $this->buildAttachmentsDocumentationPage($viewer, $engine); + $out[] = $this->buildPagingDocumentationPage($viewer, $engine); return $out; } - private function buildQueriesBox( + private function buildQueriesDocumentationPage( + PhabricatorUser $viewer, PhabricatorApplicationSearchEngine $engine) { $viewer = $this->getViewer(); @@ -140,15 +141,18 @@ EOTEXT null, )); - return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Builtin and Saved Queries')) - ->setCollapsed(true) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($this->newRemarkupDocumentationView($info)) - ->appendChild($table); + $title = pht('Prebuilt Queries'); + $content = array( + $this->newRemarkupDocumentationView($info), + $table, + ); + + return $this->newDocumentationBoxPage($viewer, $title, $content) + ->setAnchor('queries'); } - private function buildConstraintsBox( + private function buildConstraintsDocumentationPage( + PhabricatorUser $viewer, PhabricatorApplicationSearchEngine $engine) { $info = pht(<<setHeaderText(pht('Custom Query Constraints')) - ->setCollapsed(true) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($this->newRemarkupDocumentationView($info)) - ->appendChild($table) - ->appendChild($constant_lists); + + $title = pht('Constraints'); + $content = array( + $this->newRemarkupDocumentationView($info), + $table, + $constant_lists, + ); + + return $this->newDocumentationBoxPage($viewer, $title, $content) + ->setAnchor('constraints') + ->setIconIcon('fa-filter'); } - private function buildOrderBox( + private function buildOrderDocumentationPage( + PhabricatorUser $viewer, PhabricatorApplicationSearchEngine $engine, $query) { @@ -388,18 +397,21 @@ EOTEXT 'wide', )); + $title = pht('Result Ordering'); + $content = array( + $this->newRemarkupDocumentationView($orders_info), + $orders_table, + $this->newRemarkupDocumentationView($columns_info), + $columns_table, + ); - return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Result Ordering')) - ->setCollapsed(true) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($this->newRemarkupDocumentationView($orders_info)) - ->appendChild($orders_table) - ->appendChild($this->newRemarkupDocumentationView($columns_info)) - ->appendChild($columns_table); + return $this->newDocumentationBoxPage($viewer, $title, $content) + ->setAnchor('ordering') + ->setIconIcon('fa-sort-numeric-asc'); } - private function buildFieldsBox( + private function buildFieldsDocumentationPage( + PhabricatorUser $viewer, PhabricatorApplicationSearchEngine $engine) { $info = pht(<<setHeaderText(pht('Object Fields')) - ->setCollapsed(true) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($this->newRemarkupDocumentationView($info)) - ->appendChild($table); + $title = pht('Object Fields'); + $content = array( + $this->newRemarkupDocumentationView($info), + $table, + ); + + return $this->newDocumentationBoxPage($viewer, $title, $content) + ->setAnchor('fields') + ->setIconIcon('fa-cube'); } - private function buildAttachmentsBox( + private function buildAttachmentsDocumentationPage( + PhabricatorUser $viewer, PhabricatorApplicationSearchEngine $engine) { $info = pht(<<setHeaderText(pht('Attachments')) - ->setCollapsed(true) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($this->newRemarkupDocumentationView($info)) - ->appendChild($table); + $title = pht('Attachments'); + $content = array( + $this->newRemarkupDocumentationView($info), + $table, + ); + + return $this->newDocumentationBoxPage($viewer, $title, $content) + ->setAnchor('attachments') + ->setIconIcon('fa-cubes'); } - private function buildPagingBox( + private function buildPagingDocumentationPage( + PhabricatorUser $viewer, PhabricatorApplicationSearchEngine $engine) { $info = pht(<<setHeaderText(pht('Paging and Limits')) - ->setCollapsed(true) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($this->newRemarkupDocumentationView($info)); + $title = pht('Paging and Limits'); + $content = array( + $this->newRemarkupDocumentationView($info), + ); + + return $this->newDocumentationBoxPage($viewer, $title, $content) + ->setAnchor('paging') + ->setIconIcon('fa-clone'); } } diff --git a/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php b/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php index 6c4fad31a3..862f694fc8 100644 --- a/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php +++ b/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php @@ -26,10 +26,6 @@ final class PhabricatorNamedQueryConfigQuery return new PhabricatorNamedQueryConfig(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/search/query/PhabricatorNamedQueryQuery.php b/src/applications/search/query/PhabricatorNamedQueryQuery.php index 3decff5494..0ed92646e6 100644 --- a/src/applications/search/query/PhabricatorNamedQueryQuery.php +++ b/src/applications/search/query/PhabricatorNamedQueryQuery.php @@ -32,10 +32,6 @@ final class PhabricatorNamedQueryQuery return new PhabricatorNamedQuery(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php index 16b5d793a4..9f26a81424 100644 --- a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php +++ b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php @@ -40,10 +40,6 @@ final class PhabricatorProfileMenuItemConfigurationQuery return new PhabricatorProfileMenuItemConfiguration(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php index 8f7f633e7e..a888bb3175 100644 --- a/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php @@ -398,8 +398,9 @@ final class PhabricatorEmailAddressesSettingsPanel ->setTitle(pht('Change primary email address?')) ->appendParagraph( pht( - 'If you change your primary address, Phabricator will send all '. + 'If you change your primary address, %s will send all '. 'email to %s.', + PlatformSymbols::getPlatformServerName(), $address)) ->appendParagraph( pht( diff --git a/src/applications/settings/panel/PhabricatorExternalEditorSettingsPanel.php b/src/applications/settings/panel/PhabricatorExternalEditorSettingsPanel.php index c331d3d762..3a49b6c3c3 100644 --- a/src/applications/settings/panel/PhabricatorExternalEditorSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorExternalEditorSettingsPanel.php @@ -129,7 +129,7 @@ final class PhabricatorExternalEditorSettingsPanel $protocol_table = id(new AphrontTableView($protocol_rows)) ->setNoDataString( pht( - 'Phabricator is not configured to allow any editor protocols.')) + 'No allowed editor protocols are configured.')) ->setHeaders( array( pht('Protocol'), diff --git a/src/applications/settings/panel/PhabricatorNotificationsSettingsPanel.php b/src/applications/settings/panel/PhabricatorNotificationsSettingsPanel.php index d0165dc3f1..ae5679d9dd 100644 --- a/src/applications/settings/panel/PhabricatorNotificationsSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorNotificationsSettingsPanel.php @@ -68,8 +68,9 @@ final class PhabricatorNotificationsSettingsPanel 'span', array(), array( - pht('This browser has not yet granted permission to send desktop '. - 'notifications for this Phabricator instance.'), + pht( + 'Your browser has not yet granted this server permission to send '. + 'desktop notifications.'), phutil_tag('br'), phutil_tag('br'), javelin_tag( @@ -83,13 +84,13 @@ final class PhabricatorNotificationsSettingsPanel $granted_status = phutil_tag( 'span', array(), - pht('This browser has been granted permission to send desktop '. - 'notifications for this Phabricator instance.')); + pht('Your browser has granted this server permission to send desktop '. + 'notifications.')); $denied_status = phutil_tag( 'span', array(), pht('This browser has denied permission to send desktop notifications '. - 'for this Phabricator instance. Consult your browser settings / '. + 'to this server. Consult your browser settings / '. 'documentation to figure out how to clear this setting, do so, '. 'and then re-visit this page to grant permission.')); @@ -146,7 +147,7 @@ final class PhabricatorNotificationsSettingsPanel ->setOptions(PhabricatorNotificationsSetting::getOptionsMap()) ->setCaption( pht( - 'Phabricator can send real-time notifications to your web browser '. + 'This server can send real-time notifications to your web browser '. 'or to your desktop. Select where you want to receive these '. 'real-time updates.')) ->initBehavior( diff --git a/src/applications/settings/setting/PhabricatorAccessibilitySetting.php b/src/applications/settings/setting/PhabricatorAccessibilitySetting.php index 774bfcd895..226feeae5f 100644 --- a/src/applications/settings/setting/PhabricatorAccessibilitySetting.php +++ b/src/applications/settings/setting/PhabricatorAccessibilitySetting.php @@ -19,8 +19,8 @@ final class PhabricatorAccessibilitySetting protected function getControlInstructions() { return pht( - 'If you have difficulty reading the Phabricator UI, this setting '. - 'may make Phabricator more accessible.'); + 'If you have difficulty reading the UI, this setting '. + 'may help.'); } public function getSettingDefaultValue() { diff --git a/src/applications/settings/setting/PhabricatorDarkConsoleSetting.php b/src/applications/settings/setting/PhabricatorDarkConsoleSetting.php index 28ee90788e..7851bbdb76 100644 --- a/src/applications/settings/setting/PhabricatorDarkConsoleSetting.php +++ b/src/applications/settings/setting/PhabricatorDarkConsoleSetting.php @@ -27,7 +27,7 @@ final class PhabricatorDarkConsoleSetting protected function getControlInstructions() { return pht( 'DarkConsole is a debugging console for developing and troubleshooting '. - 'Phabricator applications. After enabling DarkConsole, press the '. + 'applications. After enabling DarkConsole, press the '. '{nav `} key on your keyboard to toggle it on or off.'); } diff --git a/src/applications/settings/setting/PhabricatorEditorSetting.php b/src/applications/settings/setting/PhabricatorEditorSetting.php index a0f1b43c95..bafc2ae518 100644 --- a/src/applications/settings/setting/PhabricatorEditorSetting.php +++ b/src/applications/settings/setting/PhabricatorEditorSetting.php @@ -21,8 +21,8 @@ final class PhabricatorEditorSetting return pht( "Many text editors can be configured as URI handlers for special ". "protocols like `editor://`. If you have installed and configured ". - "such an editor, Phabricator can generate links that you can click ". - "to open files locally.". + "such an editor, some applications can generate links that you can ". + "click to open files locally.". "\n\n". "Provide a URI pattern for building external editor URIs in your ". "environment. For example, if you use TextMate on macOS, the pattern ". @@ -42,7 +42,7 @@ final class PhabricatorEditorSetting } public function validateTransactionValue($value) { - if (!strlen($value)) { + if (!phutil_nonempty_string($value)) { return; } diff --git a/src/applications/settings/setting/PhabricatorEmailFormatSetting.php b/src/applications/settings/setting/PhabricatorEmailFormatSetting.php index 333d85c6f4..a8a0b07d94 100644 --- a/src/applications/settings/setting/PhabricatorEmailFormatSetting.php +++ b/src/applications/settings/setting/PhabricatorEmailFormatSetting.php @@ -22,8 +22,8 @@ final class PhabricatorEmailFormatSetting protected function getControlInstructions() { return pht( - 'You can opt to receive plain text email from Phabricator instead '. - 'of HTML email. Plain text email works better with some clients.'); + 'You can opt to receive plain text email instead of HTML email. '. + 'Plain text email works better with some clients.'); } public function getSettingDefaultValue() { diff --git a/src/applications/settings/setting/PhabricatorEmailNotificationsSetting.php b/src/applications/settings/setting/PhabricatorEmailNotificationsSetting.php index dfbedb3a12..2c63e01d72 100644 --- a/src/applications/settings/setting/PhabricatorEmailNotificationsSetting.php +++ b/src/applications/settings/setting/PhabricatorEmailNotificationsSetting.php @@ -22,7 +22,7 @@ final class PhabricatorEmailNotificationsSetting protected function getControlInstructions() { return pht( - 'If you disable **Email Notifications**, Phabricator will never '. + 'If you disable **Email Notifications**, this server will never '. 'send email to notify you about events. This preference overrides '. 'all your other settings.'. "\n\n". diff --git a/src/applications/settings/setting/PhabricatorEmailSelfActionsSetting.php b/src/applications/settings/setting/PhabricatorEmailSelfActionsSetting.php index f910c2b039..1c64cbdf32 100644 --- a/src/applications/settings/setting/PhabricatorEmailSelfActionsSetting.php +++ b/src/applications/settings/setting/PhabricatorEmailSelfActionsSetting.php @@ -22,7 +22,7 @@ final class PhabricatorEmailSelfActionsSetting protected function getControlInstructions() { return pht( - 'If you disable **Self Actions**, Phabricator will not notify '. + 'If you disable **Self Actions**, this server will not notify '. 'you about actions you take.'); } diff --git a/src/applications/settings/setting/PhabricatorEmailStampsSetting.php b/src/applications/settings/setting/PhabricatorEmailStampsSetting.php index 39403f40a0..52497f3c42 100644 --- a/src/applications/settings/setting/PhabricatorEmailStampsSetting.php +++ b/src/applications/settings/setting/PhabricatorEmailStampsSetting.php @@ -22,7 +22,7 @@ final class PhabricatorEmailStampsSetting protected function getControlInstructions() { return pht(<<getBuiltinKey()) { - case self::BUILTIN_GLOBAL_DEFAULT: - // NOTE: Without this policy exception, the logged-out viewer can not - // see global preferences. - return true; + $builtin_key = $this->getBuiltinKey(); + + $is_global = ($builtin_key === self::BUILTIN_GLOBAL_DEFAULT); + $is_view = ($capability === PhabricatorPolicyCapability::CAN_VIEW); + + if ($is_global && $is_view) { + // NOTE: Without this policy exception, the logged-out viewer can not + // see global preferences. + return true; } return false; diff --git a/src/applications/slowvote/constants/SlowvotePollResponseVisibility.php b/src/applications/slowvote/constants/SlowvotePollResponseVisibility.php new file mode 100644 index 0000000000..1daf237e72 --- /dev/null +++ b/src/applications/slowvote/constants/SlowvotePollResponseVisibility.php @@ -0,0 +1,75 @@ +key = $key; + return $object; + } + + public function getKey() { + return $this->key; + } + + public static function getAll() { + $map = self::getMap(); + + $result = array(); + foreach ($map as $key => $spec) { + $result[$key] = self::newResponseVisibilityObject($key); + } + + return $result; + } + + public function getName() { + $name = $this->getProperty('name'); + + if ($name === null) { + $name = pht('Unknown ("%s")', $this->getKey()); + } + + return $name; + } + + public function getNameForEdit() { + $name = $this->getProperty('name.edit'); + + if ($name === null) { + $name = pht('Unknown ("%s")', $this->getKey()); + } + + return $name; + } + + private function getProperty($key, $default = null) { + $spec = idx(self::getMap(), $this->getKey(), array()); + return idx($spec, $key, $default); + } + + private static function getMap() { + return array( + self::RESPONSES_VISIBLE => array( + 'name' => pht('Always Visible'), + 'name.edit' => pht('Anyone can see the responses'), + ), + self::RESPONSES_VOTERS => array( + 'name' => pht('Voters'), + 'name.edit' => pht('Require a vote to see the responses'), + ), + self::RESPONSES_OWNER => array( + 'name' => pht('Owner'), + 'name.edit' => pht('Only the poll owner can see the responses'), + ), + ); + } + +} diff --git a/src/applications/slowvote/constants/SlowvotePollStatus.php b/src/applications/slowvote/constants/SlowvotePollStatus.php new file mode 100644 index 0000000000..dd2153feec --- /dev/null +++ b/src/applications/slowvote/constants/SlowvotePollStatus.php @@ -0,0 +1,76 @@ +key = $key; + return $object; + } + + public function getKey() { + return $this->key; + } + + public static function getAll() { + $map = self::getMap(); + + $result = array(); + foreach ($map as $key => $spec) { + $result[$key] = self::newStatusObject($key); + } + + return $result; + } + + public function getName() { + $name = $this->getProperty('name'); + + if ($name === null) { + $name = pht('Unknown ("%s")', $this->getKey()); + } + + return $name; + } + + public function getHeaderTagIcon() { + return $this->getProperty('header.tag.icon'); + } + + public function getHeaderTagColor() { + return $this->getProperty('header.tag.color'); + } + + public function getTransactionIcon() { + return $this->getProperty('transaction.icon'); + } + + private function getProperty($key, $default = null) { + $spec = idx(self::getMap(), $this->getKey(), array()); + return idx($spec, $key, $default); + } + + private static function getMap() { + return array( + self::STATUS_OPEN => array( + 'name' => pht('Open'), + 'header.tag.icon' => 'fa-square-o', + 'header.tag.color' => 'bluegrey', + 'transaction.icon' => 'fa-pencil', + ), + self::STATUS_CLOSED => array( + 'name' => pht('Closed'), + 'header.tag.icon' => 'fa-ban', + 'header.tag.color' => 'indigo', + 'transaction.icon' => 'fa-ban', + ), + ); + } + +} diff --git a/src/applications/slowvote/constants/SlowvotePollVotingMethod.php b/src/applications/slowvote/constants/SlowvotePollVotingMethod.php new file mode 100644 index 0000000000..f0f18641e3 --- /dev/null +++ b/src/applications/slowvote/constants/SlowvotePollVotingMethod.php @@ -0,0 +1,70 @@ +key = $key; + return $object; + } + + public function getKey() { + return $this->key; + } + + public static function getAll() { + $map = self::getMap(); + + $result = array(); + foreach ($map as $key => $spec) { + $result[$key] = self::newVotingMethodObject($key); + } + + return $result; + } + + public function getName() { + $name = $this->getProperty('name'); + + if ($name === null) { + $name = pht('Unknown ("%s")', $this->getKey()); + } + + return $name; + } + + public function getNameForEdit() { + $name = $this->getProperty('name.edit'); + + if ($name === null) { + $name = pht('Unknown ("%s")', $this->getKey()); + } + + return $name; + } + + private function getProperty($key, $default = null) { + $spec = idx(self::getMap(), $this->getKey(), array()); + return idx($spec, $key, $default); + } + + private static function getMap() { + return array( + self::METHOD_PLURALITY => array( + 'name' => pht('Plurality'), + 'name.edit' => pht('Plurality (Single Choice)'), + ), + self::METHOD_APPROVAL => array( + 'name' => pht('Approval'), + 'name.edit' => pht('Approval (Multiple Choice)'), + ), + ); + } + +} diff --git a/src/applications/slowvote/controller/PhabricatorSlowvoteCloseController.php b/src/applications/slowvote/controller/PhabricatorSlowvoteCloseController.php index a4da0f7f7d..733c201aae 100644 --- a/src/applications/slowvote/controller/PhabricatorSlowvoteCloseController.php +++ b/src/applications/slowvote/controller/PhabricatorSlowvoteCloseController.php @@ -20,20 +20,20 @@ final class PhabricatorSlowvoteCloseController return new Aphront404Response(); } - $close_uri = '/V'.$poll->getID(); + $close_uri = $poll->getURI(); if ($request->isFormPost()) { - if ($poll->getIsClosed()) { - $new_status = 0; + if ($poll->isClosed()) { + $new_status = SlowvotePollStatus::STATUS_OPEN; } else { - $new_status = 1; + $new_status = SlowvotePollStatus::STATUS_CLOSED; } $xactions = array(); $xactions[] = id(new PhabricatorSlowvoteTransaction()) ->setTransactionType( - PhabricatorSlowvoteCloseTransaction::TRANSACTIONTYPE) + PhabricatorSlowvoteStatusTransaction::TRANSACTIONTYPE) ->setNewValue($new_status); id(new PhabricatorSlowvoteEditor()) @@ -46,7 +46,7 @@ final class PhabricatorSlowvoteCloseController return id(new AphrontRedirectResponse())->setURI($close_uri); } - if ($poll->getIsClosed()) { + if ($poll->isClosed()) { $title = pht('Reopen Poll'); $content = pht('Are you sure you want to reopen the poll?'); $submit = pht('Reopen'); diff --git a/src/applications/slowvote/controller/PhabricatorSlowvoteEditController.php b/src/applications/slowvote/controller/PhabricatorSlowvoteEditController.php index 44927fedea..48639ffde6 100644 --- a/src/applications/slowvote/controller/PhabricatorSlowvoteEditController.php +++ b/src/applications/slowvote/controller/PhabricatorSlowvoteEditController.php @@ -49,7 +49,7 @@ final class PhabricatorSlowvoteEditController if ($request->isFormPost()) { $v_question = $request->getStr('question'); $v_description = $request->getStr('description'); - $v_responses = (int)$request->getInt('responses'); + $v_responses = $request->getStr('responses'); $v_shuffle = (int)$request->getBool('shuffle'); $v_view_policy = $request->getStr('viewPolicy'); $v_projects = $request->getArr('projects'); @@ -57,7 +57,7 @@ final class PhabricatorSlowvoteEditController $v_space = $request->getStr('spacePHID'); if ($is_new) { - $poll->setMethod($request->getInt('method')); + $poll->setMethod($request->getStr('method')); } if (!strlen($v_question)) { @@ -189,21 +189,33 @@ final class PhabricatorSlowvoteEditController } } - $poll_type_options = array( - PhabricatorSlowvotePoll::METHOD_PLURALITY => - pht('Plurality (Single Choice)'), - PhabricatorSlowvotePoll::METHOD_APPROVAL => - pht('Approval (Multiple Choice)'), - ); + $vote_type_map = SlowvotePollVotingMethod::getAll(); + $vote_type_options = mpull($vote_type_map, 'getNameForEdit'); - $response_type_options = array( - PhabricatorSlowvotePoll::RESPONSES_VISIBLE - => pht('Allow anyone to see the responses'), - PhabricatorSlowvotePoll::RESPONSES_VOTERS - => pht('Require a vote to see the responses'), - PhabricatorSlowvotePoll::RESPONSES_OWNER - => pht('Only I can see the responses'), - ); + $method = $poll->getMethod(); + if (!isset($vote_type_options[$method])) { + $method_object = + SlowvotePollVotingMethod::newVotingMethodObject( + $method); + + $vote_type_options = array( + $method => $method_object->getNameForEdit(), + ) + $vote_type_options; + } + + $response_type_map = SlowvotePollResponseVisibility::getAll(); + $response_type_options = mpull($response_type_map, 'getNameForEdit'); + + $visibility = $poll->getResponseVisibility(); + if (!isset($response_type_options[$visibility])) { + $visibility_object = + SlowvotePollResponseVisibility::newResponseVisibilityObject( + $visibility); + + $response_type_options = array( + $visibility => $visibility_object->getNameForEdit(), + ) + $response_type_options; + } if ($is_new) { $form->appendChild( @@ -211,12 +223,12 @@ final class PhabricatorSlowvoteEditController ->setLabel(pht('Vote Type')) ->setName('method') ->setValue($poll->getMethod()) - ->setOptions($poll_type_options)); + ->setOptions($vote_type_options)); } else { $form->appendChild( id(new AphrontFormStaticControl()) ->setLabel(pht('Vote Type')) - ->setValue(idx($poll_type_options, $poll->getMethod()))); + ->setValue(idx($vote_type_options, $poll->getMethod()))); } if ($is_new) { diff --git a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php index 1ffab17791..7ed83fb13f 100644 --- a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php +++ b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php @@ -35,9 +35,11 @@ final class PhabricatorSlowvotePollController )); } - $header_icon = $poll->getIsClosed() ? 'fa-ban' : 'fa-square-o'; - $header_name = $poll->getIsClosed() ? pht('Closed') : pht('Open'); - $header_color = $poll->getIsClosed() ? 'indigo' : 'bluegrey'; + $status = $poll->getStatusObject(); + + $header_icon = $status->getHeaderTagIcon(); + $header_color = $status->getHeaderTagColor(); + $header_name = $status->getName(); $header = id(new PHUIHeaderView()) ->setHeader($poll->getQuestion()) @@ -50,7 +52,7 @@ final class PhabricatorSlowvotePollController $subheader = $this->buildSubheaderView($poll); $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb('V'.$poll->getID()); + $crumbs->addTextCrumb($poll->getMonogram()); $crumbs->setBorder(true); $timeline = $this->buildTransactionTimeline( @@ -71,7 +73,11 @@ final class PhabricatorSlowvotePollController ->setMainColumn($poll_content); return $this->newPage() - ->setTitle('V'.$poll->getID().' '.$poll->getQuestion()) + ->setTitle( + pht( + '%s %s', + $poll->getMonogram(), + $poll->getQuestion())) ->setCrumbs($crumbs) ->setPageObjectPHIDs(array($poll->getPHID())) ->appendChild($view); @@ -87,7 +93,7 @@ final class PhabricatorSlowvotePollController $curtain = $this->newCurtainView($poll); - $is_closed = $poll->getIsClosed(); + $is_closed = $poll->isClosed(); $close_poll_text = $is_closed ? pht('Reopen Poll') : pht('Close Poll'); $close_poll_icon = $is_closed ? 'fa-check' : 'fa-ban'; diff --git a/src/applications/slowvote/controller/PhabricatorSlowvoteVoteController.php b/src/applications/slowvote/controller/PhabricatorSlowvoteVoteController.php index e1a1b9df34..8c70c2833b 100644 --- a/src/applications/slowvote/controller/PhabricatorSlowvoteVoteController.php +++ b/src/applications/slowvote/controller/PhabricatorSlowvoteVoteController.php @@ -21,7 +21,7 @@ final class PhabricatorSlowvoteVoteController return new Aphront404Response(); } - if ($poll->getIsClosed()) { + if ($poll->isClosed()) { return new Aphront400Response(); } @@ -35,7 +35,7 @@ final class PhabricatorSlowvoteVoteController $votes = array_fuse($votes); $method = $poll->getMethod(); - $is_plurality = ($method == PhabricatorSlowvotePoll::METHOD_PLURALITY); + $is_plurality = ($method == SlowvotePollVotingMethod::METHOD_PLURALITY); if (!$votes) { if ($is_plurality) { diff --git a/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php b/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php index 0b7335326c..d2ae9f1a35 100644 --- a/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php +++ b/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php @@ -7,7 +7,7 @@ final class PhabricatorSlowvoteQuery private $phids; private $authorPHIDs; private $withVotesByViewer; - private $isClosed; + private $statuses; private $needOptions; private $needChoices; @@ -33,8 +33,8 @@ final class PhabricatorSlowvoteQuery return $this; } - public function withIsClosed($with_closed) { - $this->isClosed = $with_closed; + public function withStatuses(array $statuses) { + $this->statuses = $statuses; return $this; } @@ -57,10 +57,6 @@ final class PhabricatorSlowvoteQuery return new PhabricatorSlowvotePoll(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $polls) { assert_instances_of($polls, 'PhabricatorSlowvotePoll'); @@ -141,12 +137,13 @@ final class PhabricatorSlowvoteQuery $this->authorPHIDs); } - if ($this->isClosed !== null) { + if ($this->statuses !== null) { $where[] = qsprintf( $conn, - 'p.isClosed = %d', - (int)$this->isClosed); + 'p.status IN (%Ls)', + $this->statuses); } + return $where; } diff --git a/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php b/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php index 44bfb917c2..8b93f75faf 100644 --- a/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php +++ b/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php @@ -26,14 +26,8 @@ final class PhabricatorSlowvoteSearchEngine $query->withAuthorPHIDs($map['authorPHIDs']); } - $statuses = $map['statuses']; - if (count($statuses) == 1) { - $status = head($statuses); - if ($status == 'open') { - $query->withIsClosed(false); - } else { - $query->withIsClosed(true); - } + if ($map['statuses']) { + $query->withStatuses($map['statuses']); } return $query; @@ -41,6 +35,9 @@ final class PhabricatorSlowvoteSearchEngine protected function buildCustomSearchFields() { + $status_options = SlowvotePollStatus::getAll(); + $status_options = mpull($status_options, 'getName'); + return array( id(new PhabricatorUsersSearchField()) ->setKey('authorPHIDs') @@ -61,11 +58,7 @@ final class PhabricatorSlowvoteSearchEngine id(new PhabricatorSearchCheckboxesField()) ->setKey('statuses') ->setLabel(pht('Statuses')) - ->setOptions( - array( - 'open' => pht('Open'), - 'closed' => pht('Closed'), - )), + ->setOptions($status_options), ); } @@ -137,12 +130,12 @@ final class PhabricatorSlowvoteSearchEngine $item = id(new PHUIObjectItemView()) ->setUser($viewer) ->setObject($poll) - ->setObjectName('V'.$poll->getID()) + ->setObjectName($poll->getMonogram()) ->setHeader($poll->getQuestion()) - ->setHref('/V'.$poll->getID()) + ->setHref($poll->getURI()) ->addIcon('none', $date_created); - if ($poll->getIsClosed()) { + if ($poll->isClosed()) { $item->setStatusIcon('fa-ban grey'); $item->setDisabled(true); } else { diff --git a/src/applications/slowvote/storage/PhabricatorSlowvotePoll.php b/src/applications/slowvote/storage/PhabricatorSlowvotePoll.php index 215549f7db..9ee33c81bb 100644 --- a/src/applications/slowvote/storage/PhabricatorSlowvotePoll.php +++ b/src/applications/slowvote/storage/PhabricatorSlowvotePoll.php @@ -1,6 +1,7 @@ getPolicy( PhabricatorSlowvoteDefaultViewCapability::CAPABILITY); + $default_responses = SlowvotePollResponseVisibility::RESPONSES_VISIBLE; + $default_method = SlowvotePollVotingMethod::METHOD_PLURALITY; + return id(new PhabricatorSlowvotePoll()) ->setAuthorPHID($actor->getPHID()) ->setViewPolicy($view_policy) - ->setSpacePHID($actor->getDefaultSpacePHID()); + ->setSpacePHID($actor->getDefaultSpacePHID()) + ->setStatus(SlowvotePollStatus::STATUS_OPEN) + ->setMethod($default_method) + ->setResponseVisibility($default_responses); } protected function getConfiguration() { @@ -54,26 +53,27 @@ final class PhabricatorSlowvotePoll extends PhabricatorSlowvoteDAO self::CONFIG_AUX_PHID => true, self::CONFIG_COLUMN_SCHEMA => array( 'question' => 'text255', - 'responseVisibility' => 'uint32', + 'responseVisibility' => 'text32', 'shuffle' => 'bool', - 'method' => 'uint32', + 'method' => 'text32', 'description' => 'text', - 'isClosed' => 'bool', - 'mailKey' => 'bytes20', + 'status' => 'text32', ), self::CONFIG_KEY_SCHEMA => array( - 'key_phid' => null, - 'phid' => array( - 'columns' => array('phid'), - 'unique' => true, - ), ), ) + parent::getConfiguration(); } - public function generatePHID() { - return PhabricatorPHID::generateNewPHID( - PhabricatorSlowvotePollPHIDType::TYPECONST); + public function getPHIDType() { + return PhabricatorSlowvotePollPHIDType::TYPECONST; + } + + public function getStatusObject() { + return SlowvotePollStatus::newStatusObject($this->getStatus()); + } + + public function isClosed() { + return ($this->getStatus() == SlowvotePollStatus::STATUS_CLOSED); } public function getOptions() { @@ -117,13 +117,6 @@ final class PhabricatorSlowvotePoll extends PhabricatorSlowvoteDAO return '/'.$this->getMonogram(); } - public function save() { - if (!$this->getMailKey()) { - $this->setMailKey(Filesystem::readRandomCharacters(20)); - } - return parent::save(); - } - /* -( PhabricatorApplicationTransactionInterface )------------------------- */ diff --git a/src/applications/slowvote/storage/PhabricatorSlowvoteTransaction.php b/src/applications/slowvote/storage/PhabricatorSlowvoteTransaction.php index 1781733acf..ef86a95760 100644 --- a/src/applications/slowvote/storage/PhabricatorSlowvoteTransaction.php +++ b/src/applications/slowvote/storage/PhabricatorSlowvoteTransaction.php @@ -30,7 +30,7 @@ final class PhabricatorSlowvoteTransaction case PhabricatorSlowvoteQuestionTransaction::TRANSACTIONTYPE: case PhabricatorSlowvoteDescriptionTransaction::TRANSACTIONTYPE: case PhabricatorSlowvoteShuffleTransaction::TRANSACTIONTYPE: - case PhabricatorSlowvoteCloseTransaction::TRANSACTIONTYPE: + case PhabricatorSlowvoteStatusTransaction::TRANSACTIONTYPE: $tags[] = self::MAILTAG_DETAILS; break; case PhabricatorSlowvoteResponsesTransaction::TRANSACTIONTYPE: diff --git a/src/applications/slowvote/view/SlowvoteEmbedView.php b/src/applications/slowvote/view/SlowvoteEmbedView.php index 1183f3b1cd..323801de47 100644 --- a/src/applications/slowvote/view/SlowvoteEmbedView.php +++ b/src/applications/slowvote/view/SlowvoteEmbedView.php @@ -75,16 +75,16 @@ final class SlowvoteEmbedView extends AphrontView { $description, ); + $quip = pht('Voting improves cardiovascular endurance.'); + $vis = $poll->getResponseVisibility(); if ($this->areResultsVisible()) { - if ($vis == PhabricatorSlowvotePoll::RESPONSES_OWNER) { + if ($vis == SlowvotePollResponseVisibility::RESPONSES_OWNER) { $quip = pht('Only you can see the results.'); - } else { - $quip = pht('Voting improves cardiovascular endurance.'); } - } else if ($vis == PhabricatorSlowvotePoll::RESPONSES_VOTERS) { + } else if ($vis == SlowvotePollResponseVisibility::RESPONSES_VOTERS) { $quip = pht('You must vote to see the results.'); - } else if ($vis == PhabricatorSlowvotePoll::RESPONSES_OWNER) { + } else if ($vis == SlowvotePollResponseVisibility::RESPONSES_OWNER) { $quip = pht('Only the author can see the results.'); } @@ -95,7 +95,7 @@ final class SlowvoteEmbedView extends AphrontView { ), $quip); - if ($poll->getIsClosed()) { + if ($poll->isClosed()) { $submit = null; } else { $submit = phutil_tag( @@ -224,11 +224,11 @@ final class SlowvoteEmbedView extends AphrontView { private function renderControl(PhabricatorSlowvoteOption $option, $selected) { $types = array( - PhabricatorSlowvotePoll::METHOD_PLURALITY => 'radio', - PhabricatorSlowvotePoll::METHOD_APPROVAL => 'checkbox', + SlowvotePollVotingMethod::METHOD_PLURALITY => 'radio', + SlowvotePollVotingMethod::METHOD_APPROVAL => 'checkbox', ); - $closed = $this->getPoll()->getIsClosed(); + $closed = $this->getPoll()->isClosed(); return phutil_tag( 'input', @@ -301,13 +301,17 @@ final class SlowvoteEmbedView extends AphrontView { $percent = sprintf('%d%%', $count ? 100 * $choices / $count : 0); - switch ($poll->getMethod()) { - case PhabricatorSlowvotePoll::METHOD_PLURALITY: + $method = $poll->getMethod(); + switch ($method) { + case SlowvotePollVotingMethod::METHOD_PLURALITY: $status = pht('%s (%d / %d)', $percent, $choices, $count); break; - case PhabricatorSlowvotePoll::METHOD_APPROVAL: + case SlowvotePollVotingMethod::METHOD_APPROVAL: $status = pht('%s Approval (%d / %d)', $percent, $choices, $count); break; + default: + $status = pht('Unknown ("%s")', $method); + break; } return phutil_tag( @@ -321,15 +325,19 @@ final class SlowvoteEmbedView extends AphrontView { private function areResultsVisible() { $poll = $this->getPoll(); - $vis = $poll->getResponseVisibility(); - if ($vis == PhabricatorSlowvotePoll::RESPONSES_VISIBLE) { + $visibility = $poll->getResponseVisibility(); + if ($visibility == SlowvotePollResponseVisibility::RESPONSES_VISIBLE) { return true; - } else if ($vis == PhabricatorSlowvotePoll::RESPONSES_OWNER) { - return ($poll->getAuthorPHID() == $this->getUser()->getPHID()); - } else { - $choices = mgroup($poll->getChoices(), 'getAuthorPHID'); - return (bool)idx($choices, $this->getUser()->getPHID()); } + + $viewer = $this->getViewer(); + + if ($visibility == SlowvotePollResponseVisibility::RESPONSES_OWNER) { + return ($poll->getAuthorPHID() === $viewer->getPHID()); + } + + $choices = mgroup($poll->getChoices(), 'getAuthorPHID'); + return (bool)idx($choices, $viewer->getPHID()); } } diff --git a/src/applications/slowvote/xaction/PhabricatorSlowvoteCloseTransaction.php b/src/applications/slowvote/xaction/PhabricatorSlowvoteCloseTransaction.php deleted file mode 100644 index ce3eaf5879..0000000000 --- a/src/applications/slowvote/xaction/PhabricatorSlowvoteCloseTransaction.php +++ /dev/null @@ -1,60 +0,0 @@ -getIsClosed(); - } - - public function generateNewValue($object, $value) { - return (bool)$value; - } - - public function applyInternalEffects($object, $value) { - $object->setIsClosed((int)$value); - } - - public function getTitle() { - $new = $this->getNewValue(); - - if ($new) { - return pht( - '%s closed this poll.', - $this->renderAuthor()); - } else { - return pht( - '%s reopened this poll.', - $this->renderAuthor()); - } - } - - public function getTitleForFeed() { - $new = $this->getNewValue(); - - if ($new) { - return pht( - '%s closed %s.', - $this->renderAuthor(), - $this->renderObject()); - } else { - return pht( - '%s reopened %s.', - $this->renderAuthor(), - $this->renderObject()); - } - } - - public function getIcon() { - $new = $this->getNewValue(); - - if ($new) { - return 'fa-ban'; - } else { - return 'fa-pencil'; - } - } - -} diff --git a/src/applications/slowvote/xaction/PhabricatorSlowvoteResponsesTransaction.php b/src/applications/slowvote/xaction/PhabricatorSlowvoteResponsesTransaction.php index 93035cbd6a..ad18d1055e 100644 --- a/src/applications/slowvote/xaction/PhabricatorSlowvoteResponsesTransaction.php +++ b/src/applications/slowvote/xaction/PhabricatorSlowvoteResponsesTransaction.php @@ -6,7 +6,11 @@ final class PhabricatorSlowvoteResponsesTransaction const TRANSACTIONTYPE = 'vote:responses'; public function generateOldValue($object) { - return (int)$object->getResponseVisibility(); + return (string)$object->getResponseVisibility(); + } + + public function generateNewValue($object, $value) { + return (string)$value; } public function applyInternalEffects($object, $value) { @@ -14,18 +18,38 @@ final class PhabricatorSlowvoteResponsesTransaction } public function getTitle() { - // TODO: This could be more detailed + $old_name = $this->getOldResponseVisibilityObject()->getName(); + $new_name = $this->getNewResponseVisibilityObject()->getName(); + return pht( - '%s changed who can see the responses.', - $this->renderAuthor()); + '%s changed who can see the responses from %s to %s.', + $this->renderAuthor(), + $this->renderValue($old_name), + $this->renderValue($new_name)); } public function getTitleForFeed() { - // TODO: This could be more detailed + $old_name = $this->getOldResponseVisibilityObject()->getName(); + $new_name = $this->getNewResponseVisibilityObject()->getName(); + return pht( - '%s changed who can see the responses of %s.', + '%s changed who can see the responses of %s from %s to %s.', $this->renderAuthor(), - $this->renderObject()); + $this->renderObject(), + $this->renderValue($old_name), + $this->renderValue($new_name)); + } + + private function getOldResponseVisibilityObject() { + return $this->newResponseVisibilityObject($this->getOldValue()); + } + + private function getNewResponseVisibilityObject() { + return $this->newResponseVisibilityObject($this->getNewValue()); + } + + private function newResponseVisibilityObject($value) { + return SlowvotePollResponseVisibility::newResponseVisibilityObject($value); } } diff --git a/src/applications/slowvote/xaction/PhabricatorSlowvoteStatusTransaction.php b/src/applications/slowvote/xaction/PhabricatorSlowvoteStatusTransaction.php new file mode 100644 index 0000000000..033d2ba2ab --- /dev/null +++ b/src/applications/slowvote/xaction/PhabricatorSlowvoteStatusTransaction.php @@ -0,0 +1,60 @@ +getStatus(); + } + + public function generateNewValue($object, $value) { + return (string)$value; + } + + public function applyInternalEffects($object, $value) { + $object->setStatus($value); + } + + public function getTitle() { + $old_name = $this->getOldStatusObject()->getName(); + $new_name = $this->getNewStatusObject()->getName(); + + return pht( + '%s changed the status of this poll from %s to %s.', + $this->renderAuthor(), + $this->renderValue($old_name), + $this->renderValue($new_name)); + } + + public function getTitleForFeed() { + $old_name = $this->getOldStatusObject()->getName(); + $new_name = $this->getNewStatusObject()->getName(); + + + return pht( + '%s changed the status of %s from %s to %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderValue($old_name), + $this->renderValue($new_name)); + } + + public function getIcon() { + return $this->getNewStatusObject()->getTransactionIcon(); + } + + private function getOldStatusObject() { + return $this->newStatusObject($this->getOldValue()); + } + + private function getNewStatusObject() { + return $this->newStatusObject($this->getNewValue()); + } + + private function newStatusObject($value) { + return SlowvotePollStatus::newStatusObject($value); + } + +} diff --git a/src/applications/slowvote/xaction/PhabricatorSlowvoteVotingMethodTransaction.php b/src/applications/slowvote/xaction/PhabricatorSlowvoteVotingMethodTransaction.php new file mode 100644 index 0000000000..ac8eaa15dc --- /dev/null +++ b/src/applications/slowvote/xaction/PhabricatorSlowvoteVotingMethodTransaction.php @@ -0,0 +1,55 @@ +getMethod(); + } + + public function generateNewValue($object, $value) { + return (string)$value; + } + + public function applyInternalEffects($object, $value) { + $object->setMethod($value); + } + + public function getTitle() { + $old_name = $this->getOldVotingMethodObject()->getName(); + $new_name = $this->getNewVotingMethodObject()->getName(); + + return pht( + '%s changed the voting method from %s to %s.', + $this->renderAuthor(), + $this->renderValue($old_name), + $this->renderValue($new_name)); + } + + public function getTitleForFeed() { + $old_name = $this->getOldVotingMethodObject()->getName(); + $new_name = $this->getNewVotingMethodObject()->getName(); + + return pht( + '%s changed the voting method of %s from %s to %s.', + $this->renderAuthor(), + $this->renderObject(), + $this->renderValue($old_name), + $this->renderValue($new_name)); + } + + private function getOldVotingMethodObject() { + return $this->newVotingMethodObject($this->getOldValue()); + } + + private function getNewVotingMethodObject() { + return $this->newVotingMethodObject($this->getNewValue()); + } + + private function newVotingMethodObject($value) { + return SlowvotePollVotingMethod::newVotingMethodObject($value); + } + +} diff --git a/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php b/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php index ee11dcdd06..388b6ab4d8 100644 --- a/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php +++ b/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php @@ -36,8 +36,8 @@ final class PhabricatorSpacesNamespaceQuery return 'PhabricatorSpacesApplication'; } - protected function loadPage() { - return $this->loadStandardPage(new PhabricatorSpacesNamespace()); + public function newResultObject() { + return new PhabricatorSpacesNamespace(); } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { diff --git a/src/applications/subscriptions/command/PhabricatorSubscriptionsSubscribeEmailCommand.php b/src/applications/subscriptions/command/PhabricatorSubscriptionsSubscribeEmailCommand.php index 0c361ad2c6..226fba34e5 100644 --- a/src/applications/subscriptions/command/PhabricatorSubscriptionsSubscribeEmailCommand.php +++ b/src/applications/subscriptions/command/PhabricatorSubscriptionsSubscribeEmailCommand.php @@ -26,8 +26,7 @@ final class PhabricatorSubscriptionsSubscribeEmailCommand 'command has no effect if you do not specify any subscribers.'. "\n\n". 'Users who are CC\'d on the email itself are also automatically '. - 'subscribed if Phabricator knows which accounts are linked to their '. - 'email addresses.', + 'subscribed if their addresses are associated with a known account.', '!subscribe alincoln #ios'); } diff --git a/src/applications/system/controller/PhabricatorSystemReadOnlyController.php b/src/applications/system/controller/PhabricatorSystemReadOnlyController.php index 7541e9adf4..e83fe67b0e 100644 --- a/src/applications/system/controller/PhabricatorSystemReadOnlyController.php +++ b/src/applications/system/controller/PhabricatorSystemReadOnlyController.php @@ -16,7 +16,7 @@ final class PhabricatorSystemReadOnlyController case PhabricatorEnv::READONLY_CONFIG: $title = pht('Administrative Read-Only Mode'); $body[] = pht( - 'An administrator has placed Phabricator into read-only mode.'); + 'An administrator has placed this server into read-only mode.'); $body[] = pht( 'This mode may be used to perform temporary maintenance, test '. 'configuration, or archive an installation permanently.'); @@ -24,8 +24,9 @@ final class PhabricatorSystemReadOnlyController 'Read-only mode was enabled by the explicit action of a human '. 'administrator, so you can get more information about why it '. 'has been turned on by rolling your chair away from your desk and '. - 'yelling "Hey! Why is Phabricator in read-only mode??!" using '. - 'your very loudest outside voice.'); + 'yelling "Hey! Why is %s in read-only mode??!" using '. + 'your very loudest outside voice.', + PlatformSymbols::getPlatformServerSymbol()); $body[] = pht( 'This mode is active because it is enabled in the configuration '. 'option "%s".', @@ -35,9 +36,9 @@ final class PhabricatorSystemReadOnlyController case PhabricatorEnv::READONLY_MASTERLESS: $title = pht('No Writable Database'); $body[] = pht( - 'Phabricator is currently configured with no writable ("master") '. + 'This server is currently configured with no writable ("master") '. 'database, so it can not write new information anywhere. '. - 'Phabricator will run in read-only mode until an administrator '. + 'This server will run in read-only mode until an administrator '. 'reconfigures it with a writable database.'); $body[] = pht( 'This usually occurs when an administrator is actively working on '. @@ -52,17 +53,17 @@ final class PhabricatorSystemReadOnlyController case PhabricatorEnv::READONLY_UNREACHABLE: $title = pht('Unable to Reach Master'); $body[] = pht( - 'Phabricator was unable to connect to the writable ("master") '. + 'This server was unable to connect to the writable ("master") '. 'database while handling this request, and automatically degraded '. 'into read-only mode.'); $body[] = pht( 'This may happen if there is a temporary network anomaly on the '. 'server side, like cosmic radiation or spooky ghosts. If this '. 'failure was caused by a transient service interruption, '. - 'Phabricator will recover momentarily.'); + 'this server will recover momentarily.'); $body[] = pht( 'This may also indicate that a more serious failure has occurred. '. - 'If this interruption does not resolve on its own, Phabricator '. + 'If this interruption does not resolve on its own, this server '. 'will soon detect the persistent disruption and degrade into '. 'read-only mode until the issue is resolved.'); $button = pht('Quite Unsettling'); @@ -70,13 +71,13 @@ final class PhabricatorSystemReadOnlyController case PhabricatorEnv::READONLY_SEVERED: $title = pht('Severed From Master'); $body[] = pht( - 'Phabricator has consistently been unable to reach the writable '. + 'This server has consistently been unable to reach the writable '. '("master") database while processing recent requests.'); $body[] = pht( 'This likely indicates a severe misconfiguration or major service '. 'interruption.'); $body[] = pht( - 'Phabricator will periodically retry the connection and recover '. + 'This server will periodically retry the connection and recover '. 'once service is restored. Most causes of persistent service '. 'interruption will require administrative intervention in order '. 'to restore service.'); diff --git a/src/applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php b/src/applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php index bd6b4e361a..b66ad82b80 100644 --- a/src/applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php +++ b/src/applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php @@ -109,9 +109,8 @@ EOBANNER; 'projects and users.'. "\n\n". 'These tattered edges are an expected consequence of destroying '. - 'objects, and the Phabricator upstream will not help you fix '. - 'them. We strongly recommend disabling or archiving objects '. - 'instead.'))); + 'objects, and the upstream will not help you fix them. We '. + 'strongly recommend disabling or archiving objects instead.'))); $phids = mpull($named_objects, 'getPHID'); $handles = PhabricatorUser::getOmnipotentUser()->loadHandles($phids); diff --git a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php index b4a86428c1..0e6ad9eb54 100644 --- a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php +++ b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php @@ -26,10 +26,6 @@ final class PhabricatorTokenGivenQuery return new PhabricatorTokenGiven(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php b/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php index aaeee3aea7..82eaf08e83 100644 --- a/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php +++ b/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php @@ -13,11 +13,11 @@ final class TransactionSearchConduitAPIMethod 'or an entire object type.'); } - public function getMethodDocumentation() { + protected function newDocumentationPages(PhabricatorUser $viewer) { $markup = pht(<<newRemarkupDocumentationView($markup); - return id(new PHUIObjectBoxView()) - ->setCollapsed(true) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->setHeaderText(pht('Method Details')) - ->appendChild($markup); + return array( + $this->newDocumentationBoxPage($viewer, pht('Method Details'), $markup) + ->setAnchor('details'), + ); } protected function defineParamTypes() { diff --git a/src/applications/transactions/constants/PhabricatorTransactions.php b/src/applications/transactions/constants/PhabricatorTransactions.php index aa83227625..3e31bdb1db 100644 --- a/src/applications/transactions/constants/PhabricatorTransactions.php +++ b/src/applications/transactions/constants/PhabricatorTransactions.php @@ -7,6 +7,7 @@ final class PhabricatorTransactions extends Phobject { const TYPE_VIEW_POLICY = 'core:view-policy'; const TYPE_EDIT_POLICY = 'core:edit-policy'; const TYPE_JOIN_POLICY = 'core:join-policy'; + const TYPE_INTERACT_POLICY = 'core:interact-policy'; const TYPE_EDGE = 'core:edge'; const TYPE_CUSTOMFIELD = 'core:customfield'; const TYPE_TOKEN = 'token:give'; @@ -17,6 +18,7 @@ final class PhabricatorTransactions extends Phobject { const TYPE_SUBTYPE = 'core:subtype'; const TYPE_HISTORY = 'core:history'; const TYPE_MFA = 'core:mfa'; + const TYPE_FILE = 'core:file'; const COLOR_RED = 'red'; const COLOR_ORANGE = 'orange'; diff --git a/src/applications/transactions/data/PhabricatorTransactionChange.php b/src/applications/transactions/data/PhabricatorTransactionChange.php index 2fc59ce5e5..11aa5876a5 100644 --- a/src/applications/transactions/data/PhabricatorTransactionChange.php +++ b/src/applications/transactions/data/PhabricatorTransactionChange.php @@ -3,6 +3,7 @@ abstract class PhabricatorTransactionChange extends Phobject { private $transaction; + private $metadata = array(); private $oldValue; private $newValue; @@ -34,4 +35,13 @@ abstract class PhabricatorTransactionChange extends Phobject { return $this->newValue; } + final public function setMetadata(array $metadata) { + $this->metadata = $metadata; + return $this; + } + + final public function getMetadata() { + return $this->metadata; + } + } diff --git a/src/applications/transactions/edges/PhabricatorObjectHasFileEdgeType.php b/src/applications/transactions/edges/PhabricatorObjectHasFileEdgeType.php deleted file mode 100644 index 6ed89ff91b..0000000000 --- a/src/applications/transactions/edges/PhabricatorObjectHasFileEdgeType.php +++ /dev/null @@ -1,115 +0,0 @@ -setIsDefault(true) ->setIsEdit(true); - if (!strlen($first->getName())) { + $first_name = $first->getName(); + + if ($first_name === null || $first_name === '') { $first->setName($this->getObjectCreateShortText()); } } else { @@ -1906,6 +1908,11 @@ abstract class PhabricatorEditEngine $comment_text = $request->getStr('comment'); + $comment_metadata = $request->getStr('comment_metadata'); + if (strlen($comment_metadata)) { + $comment_metadata = phutil_json_decode($comment_metadata); + } + $actions = $request->getStr('editengine.actions'); if ($actions) { $actions = phutil_json_decode($actions); @@ -1921,10 +1928,9 @@ abstract class PhabricatorEditEngine $viewer->getPHID(), $current_version); - $is_empty = (!strlen($comment_text) && !$actions); - $draft ->setProperty('comment', $comment_text) + ->setProperty('metadata', $comment_metadata) ->setProperty('actions', $actions) ->save(); @@ -2006,6 +2012,7 @@ abstract class PhabricatorEditEngine if (strlen($comment_text) || !$xactions) { $xactions[] = id(clone $template) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) + ->setMetadataValue('remarkup.control', $comment_metadata) ->attachComment( id(clone $comment_template) ->setContent($comment_text)); diff --git a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php index 3a6dd4b70c..2028f10d90 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php @@ -38,9 +38,7 @@ abstract class PhabricatorEditEngineAPIMethod PhabricatorEnv::getDoclink('Conduit API: Using Edit Endpoints')); } - final public function getMethodDocumentation() { - $viewer = $this->getViewer(); - + final protected function newDocumentationPages(PhabricatorUser $viewer) { $engine = $this->newEditEngine() ->setViewer($viewer); @@ -48,16 +46,15 @@ abstract class PhabricatorEditEngineAPIMethod $out = array(); - $out[] = $this->buildEditTypesBoxes($engine, $types); - - return $out; + return $this->buildEditTypesDocumentationPages($viewer, $engine, $types); } - private function buildEditTypesBoxes( + private function buildEditTypesDocumentationPages( + PhabricatorUser $viewer, PhabricatorEditEngine $engine, array $types) { - $boxes = array(); + $pages = array(); $summary_info = pht( 'This endpoint supports these types of transactions. See below for '. @@ -83,12 +80,14 @@ abstract class PhabricatorEditEngineAPIMethod 'wide', )); - $boxes[] = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Transaction Types')) - ->setCollapsed(true) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($this->buildRemarkup($summary_info)) - ->appendChild($summary_table); + $title = pht('Transaction Summary'); + $content = array( + $this->buildRemarkup($summary_info), + $summary_table, + ); + + $pages[] = $this->newDocumentationBoxPage($viewer, $title, $content) + ->setAnchor('types'); foreach ($types as $type) { $section = array(); @@ -130,15 +129,18 @@ abstract class PhabricatorEditEngineAPIMethod 'wide', )); - $boxes[] = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Transaction Type: %s', $type->getEditType())) - ->setCollapsed(true) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($this->buildRemarkup($section)) - ->appendChild($type_table); + $title = $type->getEditType(); + $content = array( + $this->buildRemarkup($section), + $type_table, + ); + + $pages[] = $this->newDocumentationBoxPage($viewer, $title, $content) + ->setAnchor($type->getEditType()) + ->setIconIcon('fa-pencil'); } - return $boxes; + return $pages; } diff --git a/src/applications/transactions/editfield/PhabricatorRemarkupEditField.php b/src/applications/transactions/editfield/PhabricatorRemarkupEditField.php index 039f0b368f..a2e0debd39 100644 --- a/src/applications/transactions/editfield/PhabricatorRemarkupEditField.php +++ b/src/applications/transactions/editfield/PhabricatorRemarkupEditField.php @@ -7,6 +7,10 @@ final class PhabricatorRemarkupEditField return new PhabricatorRemarkupControl(); } + protected function newHTTPParameterType() { + return new AphrontRemarkupHTTPParameterType(); + } + protected function newConduitParameterType() { return new ConduitStringParameterType(); } @@ -15,4 +19,55 @@ final class PhabricatorRemarkupEditField return new BulkRemarkupParameterType(); } + public function getValueForTransaction() { + $value = $this->getValue(); + + if ($value instanceof RemarkupValue) { + $value = $value->getCorpus(); + } + + return $value; + } + + public function getValueForDefaults() { + $value = parent::getValueForDefaults(); + + if ($value instanceof RemarkupValue) { + $value = $value->getCorpus(); + } + + return $value; + } + + protected function getDefaultValueFromConfiguration($value) { + + // See T13685. After changes to file attachment handling, the database + // was briefly poisoned with "array()" values as defaults. + + try { + $value = phutil_string_cast($value); + } catch (Exception $ex) { + $value = ''; + } catch (Throwable $ex) { + $value = ''; + } + + return $value; + } + + public function getMetadata() { + $defaults = array(); + + $value = $this->getValue(); + if ($value instanceof RemarkupValue) { + $defaults['remarkup.control'] = $value->getMetadata(); + } + + $metadata = parent::getMetadata(); + $metadata = $metadata + $defaults; + + return $metadata; + } + + } diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionCommentEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionCommentEditor.php index b2405d90c4..dea9e9e4f7 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionCommentEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionCommentEditor.php @@ -78,12 +78,6 @@ final class PhabricatorApplicationTransactionCommentEditor $comment->setViewPolicy(PhabricatorPolicies::POLICY_PUBLIC); $comment->setEditPolicy($this->getActingAsPHID()); - $file_phids = PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles( - $actor, - array( - $comment->getContent(), - )); - $xaction->openTransaction(); $xaction->beginReadLocking(); if ($xaction->getID()) { @@ -132,18 +126,6 @@ final class PhabricatorApplicationTransactionCommentEditor $xaction->endReadLocking(); $xaction->saveTransaction(); - // Add links to any files newly referenced by the edit. - if ($file_phids) { - $editor = new PhabricatorEdgeEditor(); - foreach ($file_phids as $file_phid) { - $editor->addEdge( - $xaction->getObjectPHID(), - PhabricatorObjectHasFileEdgeType::EDGECONST , - $file_phid); - } - $editor->save(); - } - return $this; } diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index 437a7a1437..77b5fbfbb6 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -226,7 +226,7 @@ abstract class PhabricatorApplicationTransactionEditor public function getHeraldRuleMonograms() { // Convert the stored "<123>, <456>" string into a list: "H123", "H456". - $list = $this->heraldHeader; + $list = phutil_string_cast($this->heraldHeader); $list = preg_split('/[, ]+/', $list); foreach ($list as $key => $item) { @@ -332,6 +332,8 @@ abstract class PhabricatorApplicationTransactionEditor $types[] = PhabricatorTransactions::TYPE_CREATE; $types[] = PhabricatorTransactions::TYPE_HISTORY; + $types[] = PhabricatorTransactions::TYPE_FILE; + if ($this->object instanceof PhabricatorEditEngineSubtypeInterface) { $types[] = PhabricatorTransactions::TYPE_SUBTYPE; } @@ -388,6 +390,101 @@ abstract class PhabricatorApplicationTransactionEditor $new = $this->getTransactionNewValue($object, $xaction); $xaction->setNewValue($new); + + // Apply an optional transformation to convert "external" tranaction + // values (provided by APIs) into "internal" values. + + $old = $xaction->getOldValue(); + $new = $xaction->getNewValue(); + + $type = $xaction->getTransactionType(); + $xtype = $this->getModularTransactionType($object, $type); + if ($xtype) { + $xtype = clone $xtype; + $xtype->setStorage($xaction); + + + // TODO: Provide a modular hook for modern transactions to do a + // transformation. + list($old, $new) = array($old, $new); + + return; + } else { + switch ($type) { + case PhabricatorTransactions::TYPE_FILE: + list($old, $new) = $this->newFileTransactionInternalValues( + $object, + $xaction, + $old, + $new); + break; + } + } + + $xaction->setOldValue($old); + $xaction->setNewValue($new); + } + + private function newFileTransactionInternalValues( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction, + $old, + $new) { + + $old_map = array(); + + if (!$this->getIsNewObject()) { + $phid = $object->getPHID(); + + $attachment_table = new PhabricatorFileAttachment(); + $attachment_conn = $attachment_table->establishConnection('w'); + + $rows = queryfx_all( + $attachment_conn, + 'SELECT filePHID, attachmentMode FROM %R WHERE objectPHID = %s', + $attachment_table, + $phid); + $old_map = ipull($rows, 'attachmentMode', 'filePHID'); + } + + $mode_ref = PhabricatorFileAttachment::MODE_REFERENCE; + $mode_detach = PhabricatorFileAttachment::MODE_DETACH; + + $new_map = $old_map; + + foreach ($new as $file_phid => $attachment_mode) { + $is_ref = ($attachment_mode === $mode_ref); + $is_detach = ($attachment_mode === $mode_detach); + + if ($is_detach) { + unset($new_map[$file_phid]); + continue; + } + + $old_mode = idx($old_map, $file_phid); + + // If we're adding a reference to a file but it is already attached, + // don't touch it. + + if ($is_ref) { + if ($old_mode !== null) { + continue; + } + } + + $new_map[$file_phid] = $attachment_mode; + } + + foreach (array_keys($old_map + $new_map) as $key) { + if (isset($old_map[$key]) && isset($new_map[$key])) { + if ($old_map[$key] === $new_map[$key]) { + unset($old_map[$key]); + unset($new_map[$key]); + } + } + } + + return array($old_map, $new_map); } private function getTransactionOldValue( @@ -396,7 +493,7 @@ abstract class PhabricatorApplicationTransactionEditor $type = $xaction->getTransactionType(); - $xtype = $this->getModularTransactionType($type); + $xtype = $this->getModularTransactionType($object, $type); if ($xtype) { $xtype = clone $xtype; $xtype->setStorage($xaction); @@ -428,6 +525,11 @@ abstract class PhabricatorApplicationTransactionEditor return null; } return $object->getJoinPolicy(); + case PhabricatorTransactions::TYPE_INTERACT_POLICY: + if ($this->getIsNewObject()) { + return null; + } + return $object->getInteractPolicy(); case PhabricatorTransactions::TYPE_SPACE: if ($this->getIsNewObject()) { return null; @@ -476,6 +578,8 @@ abstract class PhabricatorApplicationTransactionEditor return $xaction->getOldValue(); case PhabricatorTransactions::TYPE_COMMENT: return null; + case PhabricatorTransactions::TYPE_FILE: + return null; default: return $this->getCustomTransactionOldValue($object, $xaction); } @@ -487,7 +591,7 @@ abstract class PhabricatorApplicationTransactionEditor $type = $xaction->getTransactionType(); - $xtype = $this->getModularTransactionType($type); + $xtype = $this->getModularTransactionType($object, $type); if ($xtype) { $xtype = clone $xtype; $xtype->setStorage($xaction); @@ -502,10 +606,12 @@ abstract class PhabricatorApplicationTransactionEditor case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_JOIN_POLICY: + case PhabricatorTransactions::TYPE_INTERACT_POLICY: case PhabricatorTransactions::TYPE_TOKEN: case PhabricatorTransactions::TYPE_INLINESTATE: case PhabricatorTransactions::TYPE_SUBTYPE: case PhabricatorTransactions::TYPE_HISTORY: + case PhabricatorTransactions::TYPE_FILE: return $xaction->getNewValue(); case PhabricatorTransactions::TYPE_MFA: return true; @@ -606,7 +712,7 @@ abstract class PhabricatorApplicationTransactionEditor } $type = $xaction->getTransactionType(); - $xtype = $this->getModularTransactionType($type); + $xtype = $this->getModularTransactionType($object, $type); if ($xtype) { return $xtype->getTransactionHasEffect( $object, @@ -639,7 +745,7 @@ abstract class PhabricatorApplicationTransactionEditor $type = $xaction->getTransactionType(); - $xtype = $this->getModularTransactionType($type); + $xtype = $this->getModularTransactionType($object, $type); if ($xtype) { $xtype = clone $xtype; $xtype->setStorage($xaction); @@ -658,11 +764,13 @@ abstract class PhabricatorApplicationTransactionEditor case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_JOIN_POLICY: + case PhabricatorTransactions::TYPE_INTERACT_POLICY: case PhabricatorTransactions::TYPE_SUBSCRIBERS: case PhabricatorTransactions::TYPE_INLINESTATE: case PhabricatorTransactions::TYPE_EDGE: case PhabricatorTransactions::TYPE_SPACE: case PhabricatorTransactions::TYPE_COMMENT: + case PhabricatorTransactions::TYPE_FILE: return $this->applyBuiltinInternalTransaction($object, $xaction); } @@ -675,7 +783,7 @@ abstract class PhabricatorApplicationTransactionEditor $type = $xaction->getTransactionType(); - $xtype = $this->getModularTransactionType($type); + $xtype = $this->getModularTransactionType($object, $type); if ($xtype) { $xtype = clone $xtype; $xtype->setStorage($xaction); @@ -722,9 +830,11 @@ abstract class PhabricatorApplicationTransactionEditor case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_JOIN_POLICY: + case PhabricatorTransactions::TYPE_INTERACT_POLICY: case PhabricatorTransactions::TYPE_INLINESTATE: case PhabricatorTransactions::TYPE_SPACE: case PhabricatorTransactions::TYPE_COMMENT: + case PhabricatorTransactions::TYPE_FILE: return $this->applyBuiltinExternalTransaction($object, $xaction); } @@ -776,6 +886,9 @@ abstract class PhabricatorApplicationTransactionEditor case PhabricatorTransactions::TYPE_JOIN_POLICY: $object->setJoinPolicy($xaction->getNewValue()); break; + case PhabricatorTransactions::TYPE_INTERACT_POLICY: + $object->setInteractPolicy($xaction->getNewValue()); + break; case PhabricatorTransactions::TYPE_SPACE: $object->setSpacePHID($xaction->getNewValue()); break; @@ -846,6 +959,81 @@ abstract class PhabricatorApplicationTransactionEditor case PhabricatorTransactions::TYPE_HISTORY: $this->sendHistory = true; break; + case PhabricatorTransactions::TYPE_FILE: + $this->applyFileTransaction($object, $xaction); + break; + } + } + + private function applyFileTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + $old_map = $xaction->getOldValue(); + $new_map = $xaction->getNewValue(); + + $add_phids = array(); + $rem_phids = array(); + + foreach ($new_map as $phid => $mode) { + $add_phids[$phid] = $mode; + } + + foreach ($old_map as $phid => $mode) { + if (!isset($new_map[$phid])) { + $rem_phids[] = $phid; + } + } + + $now = PhabricatorTime::getNow(); + $object_phid = $object->getPHID(); + $attacher_phid = $this->getActingAsPHID(); + + $attachment_table = new PhabricatorFileAttachment(); + $attachment_conn = $attachment_table->establishConnection('w'); + + $add_sql = array(); + foreach ($add_phids as $add_phid => $add_mode) { + $add_sql[] = qsprintf( + $attachment_conn, + '(%s, %s, %s, %ns, %d, %d)', + $object_phid, + $add_phid, + $add_mode, + $attacher_phid, + $now, + $now); + } + + $rem_sql = array(); + foreach ($rem_phids as $rem_phid) { + $rem_sql[] = qsprintf( + $attachment_conn, + '%s', + $rem_phid); + } + + foreach (PhabricatorLiskDAO::chunkSQL($add_sql) as $chunk) { + queryfx( + $attachment_conn, + 'INSERT INTO %R (objectPHID, filePHID, attachmentMode, + attacherPHID, dateCreated, dateModified) + VALUES %LQ + ON DUPLICATE KEY UPDATE + attachmentMode = VALUES(attachmentMode), + attacherPHID = VALUES(attacherPHID), + dateModified = VALUES(dateModified)', + $attachment_table, + $chunk); + } + + foreach (PhabricatorLiskDAO::chunkSQL($rem_sql) as $chunk) { + queryfx( + $attachment_conn, + 'DELETE FROM %R WHERE objectPHID = %s AND filePHID in (%LQ)', + $attachment_table, + $object_phid, + $chunk); } } @@ -932,7 +1120,7 @@ abstract class PhabricatorApplicationTransactionEditor continue; } - $xtype = $this->getModularTransactionType($type); + $xtype = $this->getModularTransactionType($object, $type); if (!$xtype) { continue; } @@ -1129,7 +1317,6 @@ abstract class PhabricatorApplicationTransactionEditor } $xactions = $this->sortTransactions($xactions); - $file_phids = $this->extractFilePHIDs($object, $xactions); if ($is_preview) { $this->loadHandles($xactions); @@ -1218,10 +1405,6 @@ abstract class PhabricatorApplicationTransactionEditor } } - if ($file_phids) { - $this->attachFiles($object, $file_phids); - } - foreach ($xactions as $xaction) { $this->applyExternalEffects($object, $xaction); } @@ -1678,7 +1861,7 @@ abstract class PhabricatorApplicationTransactionEditor foreach ($xactions as $xaction) { $type = $xaction->getTransactionType(); - $xtype = $this->getModularTransactionType($type); + $xtype = $this->getModularTransactionType($object, $type); if (!$xtype) { $capabilities = $this->getLegacyRequiredCapabilities($xaction); } else { @@ -1779,6 +1962,8 @@ abstract class PhabricatorApplicationTransactionEditor // Signing a transaction group with MFA does not require permissions // on its own. return null; + case PhabricatorTransactions::TYPE_FILE: + return null; case PhabricatorTransactions::TYPE_EDGE: return $this->getLegacyRequiredEdgeCapabilities($xaction); default: @@ -1951,11 +2136,11 @@ abstract class PhabricatorApplicationTransactionEditor PhabricatorApplicationTransaction $u, PhabricatorApplicationTransaction $v) { + $object = $this->object; $type = $u->getTransactionType(); - $xtype = $this->getModularTransactionType($type); + $xtype = $this->getModularTransactionType($object, $type); if ($xtype) { - $object = $this->object; return $xtype->mergeTransactions($object, $u, $v); } @@ -2055,9 +2240,94 @@ abstract class PhabricatorApplicationTransactionEditor $xactions[] = $xaction; } + $file_xaction = $this->newFileTransaction( + $object, + $xactions, + $changes); + if ($file_xaction) { + $xactions[] = $file_xaction; + } + return $xactions; } + + private function newFileTransaction( + PhabricatorLiskDAO $object, + array $xactions, + array $remarkup_changes) { + + assert_instances_of( + $remarkup_changes, + 'PhabricatorTransactionRemarkupChange'); + + $new_map = array(); + + $viewer = $this->getActor(); + + $old_blocks = mpull($remarkup_changes, 'getOldValue'); + foreach ($old_blocks as $key => $old_block) { + $old_blocks[$key] = phutil_string_cast($old_block); + } + + $new_blocks = mpull($remarkup_changes, 'getNewValue'); + foreach ($new_blocks as $key => $new_block) { + $new_blocks[$key] = phutil_string_cast($new_block); + } + + $old_refs = PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles( + $viewer, + $old_blocks); + $old_refs = array_fuse($old_refs); + + $new_refs = PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles( + $viewer, + $new_blocks); + $new_refs = array_fuse($new_refs); + + $add_refs = array_diff_key($new_refs, $old_refs); + foreach ($add_refs as $file_phid) { + $new_map[$file_phid] = PhabricatorFileAttachment::MODE_REFERENCE; + } + + foreach ($remarkup_changes as $remarkup_change) { + $metadata = $remarkup_change->getMetadata(); + + $attached_phids = idx($metadata, 'attachedFilePHIDs', array()); + foreach ($attached_phids as $file_phid) { + + // If the blocks don't include a new embedded reference to this file, + // do not actually attach it. A common way for this to happen is for + // a user to upload a file, then change their mind and remove the + // reference. We do not want to attach the file if they decided against + // referencing it. + + if (!isset($new_map[$file_phid])) { + continue; + } + + $new_map[$file_phid] = PhabricatorFileAttachment::MODE_ATTACH; + } + } + + $file_phids = $this->extractFilePHIDs($object, $xactions); + foreach ($file_phids as $file_phid) { + $new_map[$file_phid] = PhabricatorFileAttachment::MODE_ATTACH; + } + + if (!$new_map) { + return null; + } + + $xaction = $object->getApplicationTransactionTemplate() + ->setTransactionType(PhabricatorTransactions::TYPE_FILE) + ->setMetadataValue('attach.implicit', true) + ->setNewValue($new_map); + + return $xaction; + } + + private function getRemarkupChanges(array $xactions) { $changes = array(); @@ -2596,7 +2866,7 @@ abstract class PhabricatorApplicationTransactionEditor $errors = array(); - $xtype = $this->getModularTransactionType($type); + $xtype = $this->getModularTransactionType($object, $type); if ($xtype) { $errors[] = $xtype->validateTransactions($object, $xactions); } @@ -2656,11 +2926,113 @@ abstract class PhabricatorApplicationTransactionEditor idx($groups, $field->getFieldKey(), array())); } break; + case PhabricatorTransactions::TYPE_FILE: + $errors[] = $this->validateFileTransactions( + $object, + $xactions, + $type); + break; } return array_mergev($errors); } + private function validateFileTransactions( + PhabricatorLiskDAO $object, + array $xactions, + $transaction_type) { + + $errors = array(); + + $mode_map = PhabricatorFileAttachment::getModeList(); + $mode_map = array_fuse($mode_map); + + $file_phids = array(); + foreach ($xactions as $xaction) { + $new = $xaction->getNewValue(); + + if (!is_array($new)) { + $errors[] = new PhabricatorApplicationTransactionValidationError( + $transaction_type, + pht('Invalid'), + pht( + 'File attachment transaction must have a map of files to '. + 'attachment modes, found "%s".', + phutil_describe_type($new)), + $xaction); + continue; + } + + foreach ($new as $file_phid => $attachment_mode) { + $file_phids[$file_phid] = $file_phid; + + if (is_string($attachment_mode) && isset($mode_map[$attachment_mode])) { + continue; + } + + if (!is_string($attachment_mode)) { + $errors[] = new PhabricatorApplicationTransactionValidationError( + $transaction_type, + pht('Invalid'), + pht( + 'File attachment mode (for file "%s") is invalid. Expected '. + 'a string, found "%s".', + $file_phid, + phutil_describe_type($attachment_mode)), + $xaction); + } else { + $errors[] = new PhabricatorApplicationTransactionValidationError( + $transaction_type, + pht('Invalid'), + pht( + 'File attachment mode "%s" (for file "%s") is invalid. Valid '. + 'modes are: %s.', + $attachment_mode, + $file_phid, + pht_list($mode_map)), + $xaction); + } + } + } + + if ($file_phids) { + $file_map = id(new PhabricatorFileQuery()) + ->setViewer($this->getActor()) + ->withPHIDs($file_phids) + ->execute(); + $file_map = mpull($file_map, null, 'getPHID'); + } else { + $file_map = array(); + } + + foreach ($xactions as $xaction) { + $new = $xaction->getNewValue(); + + if (!is_array($new)) { + continue; + } + + foreach ($new as $file_phid => $attachment_mode) { + if (isset($file_map[$file_phid])) { + continue; + } + + $errors[] = new PhabricatorApplicationTransactionValidationError( + $transaction_type, + pht('Invalid'), + pht( + 'File "%s" is invalid: it could not be loaded, or you do not '. + 'have permission to view it. You must be able to see a file to '. + 'attach it to an object.', + $file_phid), + $xaction); + } + } + + return $errors; + } + + public function validatePolicyTransaction( PhabricatorLiskDAO $object, array $xactions, @@ -2909,7 +3281,7 @@ abstract class PhabricatorApplicationTransactionEditor * @return bool True if the field will be an empty text field after edits. */ protected function validateIsEmptyTextField($field_value, array $xactions) { - if (strlen($field_value) && empty($xactions)) { + if (($field_value !== null && strlen($field_value)) && empty($xactions)) { return false; } @@ -3637,8 +4009,9 @@ abstract class PhabricatorApplicationTransactionEditor private function getMailDiffSectionHeader($xaction) { $type = $xaction->getTransactionType(); + $object = $this->object; - $xtype = $this->getModularTransactionType($type); + $xtype = $this->getModularTransactionType($object, $type); if ($xtype) { return $xtype->getMailDiffSectionHeader(); } @@ -4031,20 +4404,12 @@ abstract class PhabricatorApplicationTransactionEditor PhabricatorLiskDAO $object, array $xactions) { - $changes = $this->getRemarkupChanges($xactions); - $blocks = mpull($changes, 'getNewValue'); - $phids = array(); - if ($blocks) { - $phids[] = PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles( - $this->getActor(), - $blocks); - } foreach ($xactions as $xaction) { $type = $xaction->getTransactionType(); - $xtype = $this->getModularTransactionType($type); + $xtype = $this->getModularTransactionType($object, $type); if ($xtype) { $phids[] = $xtype->extractFilePHIDs($object, $xaction->getNewValue()); } else { @@ -4055,20 +4420,8 @@ abstract class PhabricatorApplicationTransactionEditor } $phids = array_unique(array_filter(array_mergev($phids))); - if (!$phids) { - return array(); - } - // Only let a user attach files they can actually see, since this would - // otherwise let you access any file by attaching it to an object you have - // view permission on. - - $files = id(new PhabricatorFileQuery()) - ->setViewer($this->getActor()) - ->withPHIDs($phids) - ->execute(); - - return mpull($files, 'getPHID'); + return $phids; } /** @@ -4081,28 +4434,6 @@ abstract class PhabricatorApplicationTransactionEditor } - /** - * @task files - */ - private function attachFiles( - PhabricatorLiskDAO $object, - array $file_phids) { - - if (!$file_phids) { - return; - } - - $editor = new PhabricatorEdgeEditor(); - - $src = $object->getPHID(); - $type = PhabricatorObjectHasFileEdgeType::EDGECONST; - foreach ($file_phids as $dst) { - $editor->addEdge($src, $type, $dst); - } - - $editor->save(); - } - private function applyInverseEdgeTransactions( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction, @@ -4549,20 +4880,11 @@ abstract class PhabricatorApplicationTransactionEditor } } - $phid = $object->getPHID(); - - $attached_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( - $phid, - PhabricatorObjectHasFileEdgeType::EDGECONST); - if (!$attached_phids) { - return; - } - $omnipotent_viewer = PhabricatorUser::getOmnipotentUser(); $files = id(new PhabricatorFileQuery()) ->setViewer($omnipotent_viewer) - ->withPHIDs($attached_phids) + ->withAttachedObjectPHIDs(array($object->getPHID())) ->execute(); foreach ($files as $file) { $view_policy = $file->getViewPolicy(); @@ -4633,9 +4955,11 @@ abstract class PhabricatorApplicationTransactionEditor $proxy_phids); } - private function getModularTransactionTypes() { + private function getModularTransactionTypes( + PhabricatorLiskDAO $object) { + if ($this->modularTypes === null) { - $template = $this->object->getApplicationTransactionTemplate(); + $template = $object->getApplicationTransactionTemplate(); if ($template instanceof PhabricatorModularTransaction) { $xtypes = $template->newModularTransactionTypes(); foreach ($xtypes as $key => $xtype) { @@ -4653,8 +4977,8 @@ abstract class PhabricatorApplicationTransactionEditor return $this->modularTypes; } - private function getModularTransactionType($type) { - $types = $this->getModularTransactionTypes(); + private function getModularTransactionType($object, $type) { + $types = $this->getModularTransactionTypes($object); return idx($types, $type); } @@ -5214,7 +5538,7 @@ abstract class PhabricatorApplicationTransactionEditor foreach ($xactions as $xaction) { $type = $xaction->getTransactionType(); - $xtype = $this->getModularTransactionType($type); + $xtype = $this->getModularTransactionType($object, $type); if ($xtype) { $xtype = clone $xtype; $xtype->setStorage($xaction); @@ -5261,8 +5585,9 @@ abstract class PhabricatorApplicationTransactionEditor private function getTitleForTextMail( PhabricatorApplicationTransaction $xaction) { $type = $xaction->getTransactionType(); + $object = $this->object; - $xtype = $this->getModularTransactionType($type); + $xtype = $this->getModularTransactionType($object, $type); if ($xtype) { $xtype = clone $xtype; $xtype->setStorage($xaction); @@ -5278,8 +5603,9 @@ abstract class PhabricatorApplicationTransactionEditor private function getTitleForHTMLMail( PhabricatorApplicationTransaction $xaction) { $type = $xaction->getTransactionType(); + $object = $this->object; - $xtype = $this->getModularTransactionType($type); + $xtype = $this->getModularTransactionType($object, $type); if ($xtype) { $xtype = clone $xtype; $xtype->setStorage($xaction); @@ -5296,8 +5622,9 @@ abstract class PhabricatorApplicationTransactionEditor private function getBodyForTextMail( PhabricatorApplicationTransaction $xaction) { $type = $xaction->getTransactionType(); + $object = $this->object; - $xtype = $this->getModularTransactionType($type); + $xtype = $this->getModularTransactionType($object, $type); if ($xtype) { $xtype = clone $xtype; $xtype->setStorage($xaction); diff --git a/src/applications/transactions/engineextension/PhabricatorSubtypeEditEngineExtension.php b/src/applications/transactions/engineextension/PhabricatorSubtypeEditEngineExtension.php index e73d476d74..3a301d8410 100644 --- a/src/applications/transactions/engineextension/PhabricatorSubtypeEditEngineExtension.php +++ b/src/applications/transactions/engineextension/PhabricatorSubtypeEditEngineExtension.php @@ -21,7 +21,7 @@ final class PhabricatorSubtypeEditEngineExtension public function supportsObject( PhabricatorEditEngine $engine, PhabricatorApplicationTransactionInterface $object) { - return $engine->supportsSubtypes(); + return ($object instanceof PhabricatorEditEngineSubtypeInterface); } public function buildCustomEditFields( diff --git a/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php b/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php index 4f6f45bea7..eb4d4d8174 100644 --- a/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php +++ b/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php @@ -46,10 +46,6 @@ abstract class PhabricatorApplicationTransactionCommentQuery return $this->newApplicationTransactionCommentTemplate(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); $alias = $this->getPrimaryTableAlias(); diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index 43878a0bec..e32fe92983 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -244,6 +244,18 @@ abstract class PhabricatorApplicationTransaction ->setNewValue($new_value); } + $metadata = $this->getMetadataValue('remarkup.control'); + + if (!is_array($metadata)) { + $metadata = array(); + } + + foreach ($changes as $change) { + if (!$change->getMetadata()) { + $change->setMetadata($metadata); + } + } + return $changes; } @@ -334,6 +346,9 @@ abstract class PhabricatorApplicationTransaction $phids[] = $old; $phids[] = $new; break; + case PhabricatorTransactions::TYPE_FILE: + $phids[] = array_keys($old + $new); + break; case PhabricatorTransactions::TYPE_EDGE: $record = PhabricatorEdgeChangeRecord::newFromTransaction($this); $phids[] = $record->getChangedPHIDs(); @@ -350,6 +365,7 @@ abstract class PhabricatorApplicationTransaction case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_JOIN_POLICY: + case PhabricatorTransactions::TYPE_INTERACT_POLICY: if (!PhabricatorPolicyQuery::isSpecialPolicy($old)) { $phids[] = array($old); } @@ -479,6 +495,7 @@ abstract class PhabricatorApplicationTransaction case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_JOIN_POLICY: + case PhabricatorTransactions::TYPE_INTERACT_POLICY: return 'fa-lock'; case PhabricatorTransactions::TYPE_EDGE: switch ($this->getMetadataValue('edge:type')) { @@ -581,6 +598,13 @@ abstract class PhabricatorApplicationTransaction return true; } + // Always hide file attach/detach transactions. + if ($xaction_type === PhabricatorTransactions::TYPE_FILE) { + if ($this->getMetadataValue('attach.implicit')) { + return true; + } + } + // Hide creation transactions if the old value is empty. These are // transactions like "alice set the task title to: ...", which are // essentially never interesting. @@ -590,6 +614,7 @@ abstract class PhabricatorApplicationTransaction case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_JOIN_POLICY: + case PhabricatorTransactions::TYPE_INTERACT_POLICY: case PhabricatorTransactions::TYPE_SPACE: break; case PhabricatorTransactions::TYPE_SUBTYPE: @@ -602,7 +627,7 @@ abstract class PhabricatorApplicationTransaction } if (!is_array($old)) { - if (!strlen($old)) { + if ($old === '' || $old === null) { return true; } @@ -634,6 +659,7 @@ abstract class PhabricatorApplicationTransaction case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_JOIN_POLICY: + case PhabricatorTransactions::TYPE_INTERACT_POLICY: case PhabricatorTransactions::TYPE_SPACE: if ($this->getIsCreateTransaction()) { break; @@ -707,7 +733,7 @@ abstract class PhabricatorApplicationTransaction switch ($this->getTransactionType()) { case PhabricatorTransactions::TYPE_TOKEN: return true; - case PhabricatorTransactions::TYPE_EDGE: + case PhabricatorTransactions::TYPE_EDGE: $edge_type = $this->getMetadataValue('edge:type'); switch ($edge_type) { case PhabricatorObjectMentionsObjectEdgeType::EDGECONST: @@ -887,6 +913,10 @@ abstract class PhabricatorApplicationTransaction return pht( 'This %s already has that join policy.', $this->getApplicationObjectTypeName()); + case PhabricatorTransactions::TYPE_INTERACT_POLICY: + return pht( + 'This %s already has that interact policy.', + $this->getApplicationObjectTypeName()); case PhabricatorTransactions::TYPE_SUBSCRIBERS: return pht( 'All users are already subscribed to this %s.', @@ -964,6 +994,19 @@ abstract class PhabricatorApplicationTransaction $this->renderPolicyName($old, 'old'), $this->renderPolicyName($new, 'new')); } + case PhabricatorTransactions::TYPE_INTERACT_POLICY: + if ($this->getIsCreateTransaction()) { + return pht( + '%s created this object with interact policy "%s".', + $this->renderHandleLink($author_phid), + $this->renderPolicyName($new, 'new')); + } else { + return pht( + '%s changed the interact policy from "%s" to "%s".', + $this->renderHandleLink($author_phid), + $this->renderPolicyName($old, 'old'), + $this->renderPolicyName($new, 'new')); + } case PhabricatorTransactions::TYPE_SPACE: if ($this->getIsCreateTransaction()) { return pht( @@ -1008,6 +1051,124 @@ abstract class PhabricatorApplicationTransaction '%s updated subscribers...', $this->renderHandleLink($author_phid)); } + break; + case PhabricatorTransactions::TYPE_FILE: + $add = array_diff_key($new, $old); + $add = array_keys($add); + + $rem = array_diff_key($old, $new); + $rem = array_keys($rem); + + $mod = array(); + foreach ($old + $new as $key => $ignored) { + if (!isset($old[$key])) { + continue; + } + + if (!isset($new[$key])) { + continue; + } + + if ($old[$key] === $new[$key]) { + continue; + } + + $mod[] = $key; + } + + // Specialize the specific case of only modifying files and upgrading + // references to attachments. This is accessible via the UI and can + // be shown more clearly than the generic default transaction shows + // it. + + $mode_reference = PhabricatorFileAttachment::MODE_REFERENCE; + $mode_attach = PhabricatorFileAttachment::MODE_ATTACH; + + $is_refattach = false; + if ($mod && !$add && !$rem) { + $all_refattach = true; + foreach ($mod as $phid) { + if (idx($old, $phid) !== $mode_reference) { + $all_refattach = false; + break; + } + if (idx($new, $phid) !== $mode_attach) { + $all_refattach = false; + break; + } + } + $is_refattach = $all_refattach; + } + + if ($is_refattach) { + return pht( + '%s attached %s referenced file(s): %s.', + $this->renderHandleLink($author_phid), + phutil_count($mod), + $this->renderHandleList($mod)); + } else if ($add && $rem && $mod) { + return pht( + '%s updated %s attached file(s), added %s: %s; removed %s: %s; '. + 'modified %s: %s.', + $this->renderHandleLink($author_phid), + new PhutilNumber(count($add) + count($rem)), + phutil_count($add), + $this->renderHandleList($add), + phutil_count($rem), + $this->renderHandleList($rem), + phutil_count($mod), + $this->renderHandleList($mod)); + } else if ($add && $rem) { + return pht( + '%s updated %s attached file(s), added %s: %s; removed %s: %s.', + $this->renderHandleLink($author_phid), + new PhutilNumber(count($add) + count($rem)), + phutil_count($add), + $this->renderHandleList($add), + phutil_count($rem), + $this->renderHandleList($rem)); + } else if ($add && $mod) { + return pht( + '%s updated %s attached file(s), added %s: %s; modified %s: %s.', + $this->renderHandleLink($author_phid), + new PhutilNumber(count($add) + count($mod)), + phutil_count($add), + $this->renderHandleList($add), + phutil_count($mod), + $this->renderHandleList($mod)); + } else if ($rem && $mod) { + return pht( + '%s updated %s attached file(s), removed %s: %s; modified %s: %s.', + $this->renderHandleLink($author_phid), + new PhutilNumber(count($rem) + count($mod)), + phutil_count($rem), + $this->renderHandleList($rem), + phutil_count($mod), + $this->renderHandleList($mod)); + } else if ($add) { + return pht( + '%s attached %s file(s): %s.', + $this->renderHandleLink($author_phid), + phutil_count($add), + $this->renderHandleList($add)); + } else if ($rem) { + return pht( + '%s removed %s attached file(s): %s.', + $this->renderHandleLink($author_phid), + phutil_count($rem), + $this->renderHandleList($rem)); + } else if ($mod) { + return pht( + '%s modified %s attached file(s): %s.', + $this->renderHandleLink($author_phid), + phutil_count($mod), + $this->renderHandleList($mod)); + } else { + return pht( + '%s attached files...', + $this->renderHandleLink($author_phid)); + } + break; case PhabricatorTransactions::TYPE_EDGE: $record = PhabricatorEdgeChangeRecord::newFromTransaction($this); @@ -1204,6 +1365,11 @@ abstract class PhabricatorApplicationTransaction '%s changed the join policy for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); + case PhabricatorTransactions::TYPE_INTERACT_POLICY: + return pht( + '%s changed the interact policy for %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); case PhabricatorTransactions::TYPE_SUBSCRIBERS: return pht( '%s updated subscribers of %s.', @@ -1426,6 +1592,7 @@ abstract class PhabricatorApplicationTransaction case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_JOIN_POLICY: + case PhabricatorTransactions::TYPE_INTERACT_POLICY: return pht('Changed Policy'); case PhabricatorTransactions::TYPE_SUBSCRIBERS: return pht('Changed Subscribers'); @@ -1440,6 +1607,8 @@ abstract class PhabricatorApplicationTransaction public function hasChangeDetails() { switch ($this->getTransactionType()) { + case PhabricatorTransactions::TYPE_FILE: + return true; case PhabricatorTransactions::TYPE_CUSTOMFIELD: $field = $this->getTransactionCustomField(); if ($field) { @@ -1455,6 +1624,11 @@ abstract class PhabricatorApplicationTransaction } public function renderChangeDetailsForMail(PhabricatorUser $viewer) { + switch ($this->getTransactionType()) { + case PhabricatorTransactions::TYPE_FILE: + return false; + } + $view = $this->renderChangeDetails($viewer); if ($view instanceof PhabricatorApplicationTransactionTextDiffDetailView) { return $view->renderForMail(); @@ -1464,6 +1638,8 @@ abstract class PhabricatorApplicationTransaction public function renderChangeDetails(PhabricatorUser $viewer) { switch ($this->getTransactionType()) { + case PhabricatorTransactions::TYPE_FILE: + return $this->newFileTransactionChangeDetails($viewer); case PhabricatorTransactions::TYPE_CUSTOMFIELD: $field = $this->getTransactionCustomField(); if ($field) { @@ -1730,6 +1906,66 @@ abstract class PhabricatorApplicationTransaction ->addInt(-$this->getActionStrength()); } + private function newFileTransactionChangeDetails(PhabricatorUser $viewer) { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $phids = array_keys($old + $new); + $handles = $viewer->loadHandles($phids); + + $names = array( + PhabricatorFileAttachment::MODE_REFERENCE => pht('Referenced'), + PhabricatorFileAttachment::MODE_ATTACH => pht('Attached'), + ); + + $rows = array(); + foreach ($old + $new as $phid => $ignored) { + $handle = $handles[$phid]; + + $old_mode = idx($old, $phid); + $new_mode = idx($new, $phid); + + if ($old_mode === null) { + $old_name = pht('None'); + } else if (isset($names[$old_mode])) { + $old_name = $names[$old_mode]; + } else { + $old_name = pht('Unknown ("%s")', $old_mode); + } + + if ($new_mode === null) { + $new_name = pht('Detached'); + } else if (isset($names[$new_mode])) { + $new_name = $names[$new_mode]; + } else { + $new_name = pht('Unknown ("%s")', $new_mode); + } + + $rows[] = array( + $handle->renderLink(), + $old_name, + $new_name, + ); + } + + $table = id(new AphrontTableView($rows)) + ->setHeaders( + array( + pht('File'), + pht('Old Mode'), + pht('New Mode'), + )) + ->setColumnClasses( + array( + 'pri', + )); + + return id(new PHUIBoxView()) + ->addMargin(PHUI::MARGIN_SMALL) + ->appendChild($table); + } + + /* -( PhabricatorPolicyInterface Implementation )-------------------------- */ @@ -1797,5 +2033,4 @@ abstract class PhabricatorApplicationTransaction $this->saveTransaction(); } - } diff --git a/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php b/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php index b1919a0ee0..ae55b9a4bf 100644 --- a/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php +++ b/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php @@ -185,7 +185,7 @@ final class PhabricatorEditEngineConfiguration $fields = $this->reorderFields($fields); $preamble = $this->getPreamble(); - if (strlen($preamble)) { + if ($preamble !== null && strlen($preamble)) { $fields = array( 'config.preamble' => id(new PhabricatorInstructionsEditField()) ->setKey('config.preamble') diff --git a/src/applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php b/src/applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php index d21e2105fb..fafb4d35af 100644 --- a/src/applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php +++ b/src/applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php @@ -67,7 +67,7 @@ browser's address bar. It will usually look something like this: %s ``` -However, `your.install.com` will be the domain where your copy of Phabricator +However, `your.install.com` will be the domain where your copy of this software is installed, and `application/` will be the URI for an application. Some applications have multiple forms for creating objects or URIs that look a little different than this example, so the URI may not look exactly like this. diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php index 8ec7ef0d8f..2e469c5e0a 100644 --- a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php +++ b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php @@ -294,18 +294,43 @@ final class PhabricatorApplicationTransactionCommentView } private function renderCommentPanel() { + $viewer = $this->getViewer(); + + $remarkup_control = id(new PhabricatorRemarkupControl()) + ->setViewer($viewer) + ->setID($this->getCommentID()) + ->addClass('phui-comment-fullwidth-control') + ->addClass('phui-comment-textarea-control') + ->setCanPin(true) + ->setName('comment'); + $draft_comment = ''; + $draft_metadata = array(); $draft_key = null; - if ($this->getDraft()) { - $draft_comment = $this->getDraft()->getDraft(); - $draft_key = $this->getDraft()->getDraftKey(); + + $legacy_draft = $this->getDraft(); + if ($legacy_draft) { + $draft_comment = $legacy_draft->getDraft(); + $draft_key = $legacy_draft->getDraftKey(); } $versioned_draft = $this->getVersionedDraft(); if ($versioned_draft) { - $draft_comment = $versioned_draft->getProperty('comment', ''); + $draft_comment = $versioned_draft->getProperty( + 'comment', + $draft_comment); + $draft_metadata = $versioned_draft->getProperty( + 'metadata', + $draft_metadata); } + $remarkup_control->setValue($draft_comment); + + if (!is_array($draft_metadata)) { + $draft_metadata = array(); + } + $remarkup_control->setRemarkupMetadata($draft_metadata); + if (!$this->getObjectPHID()) { throw new PhutilInvalidStateException('setObjectPHID', 'render'); } @@ -314,7 +339,7 @@ final class PhabricatorApplicationTransactionCommentView $version_value = $this->getCurrentVersion(); $form = id(new AphrontFormView()) - ->setUser($this->getUser()) + ->setUser($viewer) ->addSigil('transaction-append') ->setWorkflow(true) ->setFullWidth($this->fullWidth) @@ -465,15 +490,7 @@ final class PhabricatorApplicationTransactionCommentView ->setValue($this->getSubmitButtonName()); $form - ->appendChild( - id(new PhabricatorRemarkupControl()) - ->setID($this->getCommentID()) - ->addClass('phui-comment-fullwidth-control') - ->addClass('phui-comment-textarea-control') - ->setCanPin(true) - ->setName('comment') - ->setUser($this->getUser()) - ->setValue($draft_comment)) + ->appendChild($remarkup_control) ->appendChild( id(new AphrontFormSubmitControl()) ->addClass('phui-comment-fullwidth-control') diff --git a/src/applications/uiexample/examples/PHUIBadgeExample.php b/src/applications/uiexample/examples/PHUIBadgeExample.php index 703595b76c..40f549e305 100644 --- a/src/applications/uiexample/examples/PHUIBadgeExample.php +++ b/src/applications/uiexample/examples/PHUIBadgeExample.php @@ -19,7 +19,7 @@ final class PHUIBadgeExample extends PhabricatorUIExample { $badges1 = array(); $badges1[] = id(new PHUIBadgeView()) ->setIcon('fa-users') - ->setHeader(pht('Phacility High Command')) + ->setHeader(pht('High Command')) ->setHref('/') ->setSource('Projects (automatic)') ->addByline(pht('Dec 31, 1969')) @@ -56,7 +56,7 @@ final class PHUIBadgeExample extends PhabricatorUIExample { $badges2 = array(); $badges2[] = id(new PHUIBadgeView()) ->setIcon('fa-user') - ->setHeader(pht('Phabricator User')) + ->setHeader(pht('User')) ->setSubhead(pht('Confirmed your account.')) ->setQuality(PhabricatorBadgesQuality::POOR) ->setSource(pht('People (automatic)')) @@ -111,9 +111,9 @@ final class PHUIBadgeExample extends PhabricatorUIExample { $badges2[] = id(new PHUIBadgeView()) ->setIcon('fa-compass') ->setHeader(pht('Lead Developer')) - ->setSubhead(pht('Lead Developer of Phabricator')) + ->setSubhead(pht('Lead Developer of Software')) ->setQuality(PhabricatorBadgesQuality::HEIRLOOM) - ->setSource(pht('Direct Award (epriestley)')) + ->setSource(pht('Direct Award')) ->addByline(pht('Dec 31, 1969')) ->addByline('1 Awarded (0.4%)'); diff --git a/src/applications/uiexample/examples/PHUIHovercardUIExample.php b/src/applications/uiexample/examples/PHUIHovercardUIExample.php index b88fdc6639..2e68976ae0 100644 --- a/src/applications/uiexample/examples/PHUIHovercardUIExample.php +++ b/src/applications/uiexample/examples/PHUIHovercardUIExample.php @@ -39,7 +39,7 @@ final class PHUIHovercardUIExample extends PhabricatorUIExample { $task_handle = $this->createBasicDummyHandle( 'T123', ManiphestTaskPHIDType::TYPECONST, - pht('Improve Mobile Experience for Phabricator')); + pht('Improve Mobile Experience')); $tag = id(new PHUITagView()) ->setType(PHUITagView::TYPE_STATE) diff --git a/src/applications/uiexample/examples/PhabricatorProjectBuiltinsExample.php b/src/applications/uiexample/examples/PhabricatorProjectBuiltinsExample.php index 2cc89b56d5..9eb90f6de1 100644 --- a/src/applications/uiexample/examples/PhabricatorProjectBuiltinsExample.php +++ b/src/applications/uiexample/examples/PhabricatorProjectBuiltinsExample.php @@ -7,7 +7,7 @@ final class PhabricatorProjectBuiltinsExample extends PhabricatorUIExample { } public function getDescription() { - return pht('Builtin Project Images that ship with Phabricator.'); + return pht('Builtin Project Images.'); } public function getCategory() { diff --git a/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php b/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php index 3e9643a7fc..2001fec0c7 100644 --- a/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php +++ b/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php @@ -20,10 +20,6 @@ final class PhabricatorXHProfSampleQuery return new PhabricatorXHProfSample(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); diff --git a/src/docs/book/phorge.book b/src/docs/book/phorge.book index d423482813..0be0d5526d 100644 --- a/src/docs/book/phorge.book +++ b/src/docs/book/phorge.book @@ -244,10 +244,6 @@ "name": "PHPAST", "include": "(^src/applications/phpast/)" }, - "phragment": { - "name": "Phragment", - "include": "(^src/applications/phragment/)" - }, "phrequent": { "name": "Phrequent", "include": "(^src/applications/phrequent/)" @@ -272,10 +268,6 @@ "name": "Projects", "include": "(^src/applications/project/)" }, - "releeph": { - "name": "Releeph", - "include": "(^src/applications/releeph/)" - }, "remarkup": { "name": "Remarkup", "include": [ diff --git a/src/docs/user/configuration/advanced_configuration.diviner b/src/docs/user/configuration/advanced_configuration.diviner index 8c1389d992..5a2d565386 100644 --- a/src/docs/user/configuration/advanced_configuration.diviner +++ b/src/docs/user/configuration/advanced_configuration.diviner @@ -21,7 +21,7 @@ to lowest priority: - **Database**: Values are stored in the database and edited from the web UI by administrators. They have the highest priority and override other settings. - - **Local**: Values are stored in `conf/local/config.json` and edited by + - **Local**: Values are stored in `conf/local/local.json` and edited by running `bin/config`. - **Config Files**: Values are stored in a config file in `conf/`. The file to use is selected by writing to `conf/local/ENVIRONMENT`, or setting the diff --git a/src/docs/user/configuration/configuring_inbound_email.diviner b/src/docs/user/configuration/configuring_inbound_email.diviner index 00861cc93e..dfebfde7aa 100644 --- a/src/docs/user/configuration/configuring_inbound_email.diviner +++ b/src/docs/user/configuration/configuring_inbound_email.diviner @@ -42,9 +42,9 @@ Some available approaches for delivering mail to Phorge are: | Receive Mail With | Setup | Cost | Notes | |--------|-------|------|-------| -| Mailgun | Easy | Cheap | Recommended | | Postmark | Easy | Cheap | Recommended | | SendGrid | Easy | Cheap | | +| Mailgun | Easy | Cheap | Discouraged | | Local MTA | Difficult | Free | Discouraged | The remainder of this document walks through configuring Phorge to @@ -183,6 +183,9 @@ like this: example domain with your actual domain. - Configure a mailer in `cluster.mailers` with your Mailgun API key. +Use of Mailgun is discouraged because of concerns that they may not be a +trustworthy custodian of sensitive data. See for +discussion and context. Postmark Setup ============== diff --git a/src/docs/user/configuration/configuring_outbound_email.diviner b/src/docs/user/configuration/configuring_outbound_email.diviner index 327bf974ac..07cacb6b25 100644 --- a/src/docs/user/configuration/configuring_outbound_email.diviner +++ b/src/docs/user/configuration/configuring_outbound_email.diviner @@ -17,7 +17,6 @@ including a local mailer or various third-party services. Options include: | Send Mail With | Setup | Cost | Inbound | Media | Notes | |----------------|-------|------|---------|-------|-------| | Postmark | Easy | Cheap | Yes | Email | Recommended | -| Mailgun | Easy | Cheap | Yes | Email | Recommended | | Amazon SES | Easy | Cheap | No | Email | | | SendGrid | Medium | Cheap | Yes | Email | | | Twilio | Easy | Cheap | No | SMS | Recommended | @@ -26,14 +25,15 @@ including a local mailer or various third-party services. Options include: | Local SMTP | Hard | Free | No | Email | sendmail, postfix, etc | | Custom | Hard | Free | No | All | Write a custom mailer. | | Drop in a Hole | Easy | Free | No | All | Drops mail in a deep, dark hole. | +| Mailgun | Easy | Cheap | Yes | Email | Discouraged | See below for details on how to select and configure mail delivery for each mailer. -For email, Postmark or Mailgun are recommended because they make it easy to -set up inbound and outbound mail and have good track records in our production -services. Other services will also generally work well, but they may be more -difficult to set up. +For email, Postmark is recommended because it makes it easy to set up inbound +and outbound mail and has a good track record in our production services. Other +services will also generally work well, but they may be more difficult to set +up. For SMS, Twilio or SNS are recommended. They're also your only upstream options. @@ -87,8 +87,8 @@ A valid `cluster.mailers` configuration looks something like this: ```lang=json [ { - "key": "mycompany-mailgun", - "type": "mailgun", + "key": "mycompany-postmark", + "type": "postmark", "options": { "domain": "mycompany.com", "api-key": "..." @@ -118,12 +118,12 @@ The supported keys for each mailer are: The `type` field can be used to select these mailer services: - - `mailgun`: Use Mailgun. - `ses`: Use Amazon SES. - `sendgrid`: Use SendGrid. - `postmark`: Use Postmark. - `twilio`: Use Twilio. - `sns`: Use Amazon SNS. + - `mailgun`: Use Mailgun. It also supports these local mailers: @@ -204,11 +204,12 @@ The option accepts a list of CIDR ranges, like `1.2.3.4/16` (IPv4) or [ "50.31.156.6/32", "50.31.156.77/32", - "18.217.206.57/32" + "18.217.206.57/32", + "3.134.147.250/32" ] ``` -The default address ranges were last updated in January 2019, and were +The default address ranges were last updated in December 2021, and were documented at: @@ -220,6 +221,10 @@ Mailer: Mailgun | Inbound | Yes |---------| +Use of Mailgun is discouraged because of concerns that they may not be a +trustworthy custodian of sensitive data. See for +discussion and context. + Mailgun is a third-party email delivery service. You can learn more at . Mailgun is easy to configure and works well. @@ -339,7 +344,7 @@ Since you'll be sending the mail yourself, you are subject to things like SPF rules, blackholes, and MTA configuration which are beyond the scope of this document. If you can already send outbound email from the command line or know how to configure it, this option is straightforward. If you have no idea how to -do any of this, strongly consider using Postmark or Mailgun instead. +do any of this, strongly consider using Postmark instead. To use this mailer, set `type` to `sendmail`, then configure these `options`: @@ -416,7 +421,7 @@ highest priority group and go through them randomly, then fall back to the next group. For example, if you have two SMTP servers and you want to balance requests -between them and then fall back to Mailgun if both fail, configure priorities +between them and then fall back to Postmark if both fail, configure priorities like this: ```lang=json @@ -434,8 +439,8 @@ like this: "options": "..." }, { - "key": "mailgun-fallback", - "type": "mailgun", + "key": "postmark-fallback", + "type": "postmark", "options": "..." } } @@ -450,7 +455,7 @@ example, there are no other priority groups. If it still hasn't sent the mail, Phorge will try servers which are not in any priority group, in the configured order. In this example there is -only one such server, so it will try to send via Mailgun. +only one such server, so it will try to send via Postmark. Message-ID Headers diff --git a/src/docs/user/installation_guide.diviner b/src/docs/user/installation_guide.diviner index 0073dc6afb..ded0e4c93a 100644 --- a/src/docs/user/installation_guide.diviner +++ b/src/docs/user/installation_guide.diviner @@ -102,22 +102,7 @@ your own risk and expect that your skills may be tested. Installing Required Components ============================== -If you are installing on Ubuntu or an RedHat derivative, there are install -scripts available which should handle most of the things discussed in this -document for you: - - - **RedHat Derivatives**: - [[ https://we.phorge.it/diffusion/P/browse/master/scripts/install/install_rhel-derivs.sh - | install_rhel-derivs.sh ]] - - **Ubuntu**: - [[ https://we.phorge.it/diffusion/P/browse/master/scripts/install/install_ubuntu.sh - | install_ubuntu.sh ]] - -If those work for you, you can skip directly to the -@{article:Configuration Guide}. These scripts are also available in the -`scripts/install` directory in the project itself. - -Otherwise, here's a general description of what you need to install: +Here's a general description of what you need to install: - git (usually called "git" in package management systems) - Apache (usually "httpd" or "apache2") (or nginx) diff --git a/src/docs/user/userguide/phame.diviner b/src/docs/user/userguide/phame.diviner index 2889f5240a..82d76bd395 100644 --- a/src/docs/user/userguide/phame.diviner +++ b/src/docs/user/userguide/phame.diviner @@ -84,10 +84,7 @@ External Blogs WARNING: This feature is still a prototype and has some known issues. -You can host a Phame blog on an external domain, like `blog.mycompany.com`. The -Phacility corporate blog is an example of an external Phame blog: - -> https://blog.phacility.com/ +You can host a Phame blog on an external domain, like `blog.mycompany.com`. External blogs are public (they do not require login) and are only supported if your Phorge install is also public. You can make an install public by diff --git a/src/infrastructure/cluster/PhabricatorDatabaseRef.php b/src/infrastructure/cluster/PhabricatorDatabaseRef.php index 478f95750b..1eb232ad86 100644 --- a/src/infrastructure/cluster/PhabricatorDatabaseRef.php +++ b/src/infrastructure/cluster/PhabricatorDatabaseRef.php @@ -322,6 +322,7 @@ final class PhabricatorDatabaseRef $default_user = PhabricatorEnv::getEnvConfig('mysql.user'); $default_pass = PhabricatorEnv::getEnvConfig('mysql.pass'); + $default_pass = phutil_string_cast($default_pass); $default_pass = new PhutilOpaqueEnvelope($default_pass); $config = PhabricatorEnv::getEnvConfig('cluster.databases'); @@ -352,7 +353,7 @@ final class PhabricatorDatabaseRef $ref->setConnectionMessage( pht( 'No permission to run "SHOW SLAVE STATUS". Grant this user '. - '"REPLICATION CLIENT" permission to allow Phabricator to '. + '"REPLICATION CLIENT" permission to allow this server to '. 'monitor replica health.')); } catch (AphrontInvalidCredentialsQueryException $ex) { $ref->setConnectionStatus(self::STATUS_AUTH); diff --git a/src/infrastructure/cluster/PhabricatorDatabaseRefParser.php b/src/infrastructure/cluster/PhabricatorDatabaseRefParser.php index db498e8a82..83eaa096b8 100644 --- a/src/infrastructure/cluster/PhabricatorDatabaseRefParser.php +++ b/src/infrastructure/cluster/PhabricatorDatabaseRefParser.php @@ -90,7 +90,7 @@ final class PhabricatorDatabaseRefParser if (!is_array($partition)) { throw new Exception( pht( - 'Phabricator is configured with multiple master databases, '. + 'This server is configured with multiple master databases, '. 'but master "%s" is missing a "partition" configuration key to '. 'define application partitioning.', $ref->getRefKey())); diff --git a/src/infrastructure/contentsource/PhabricatorContentSource.php b/src/infrastructure/contentsource/PhabricatorContentSource.php index 3a5ea19f57..ee77052113 100644 --- a/src/infrastructure/contentsource/PhabricatorContentSource.php +++ b/src/infrastructure/contentsource/PhabricatorContentSource.php @@ -42,7 +42,7 @@ abstract class PhabricatorContentSource extends Phobject { } else { throw new Exception( pht( - 'Content source type "%s" is not known to Phabricator!', + 'Content source type "%s" is unknown.', $source)); } } diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php index 78c8caa5a9..a96ebefda1 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php @@ -256,6 +256,10 @@ abstract class PhabricatorStandardCustomFieldPHIDs } protected function decodeValue($value) { + if ($value === null) { + return array(); + } + $value = json_decode($value); if (!is_array($value)) { $value = array(); diff --git a/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementFloodWorkflow.php b/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementFloodWorkflow.php index fb067ddaac..5719f82b18 100644 --- a/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementFloodWorkflow.php +++ b/src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementFloodWorkflow.php @@ -10,7 +10,7 @@ final class PhabricatorWorkerManagementFloodWorkflow ->setSynopsis( pht( 'Flood the queue with test tasks. This command is intended for '. - 'use when developing and debugging Phabricator.')) + 'use during development and debugging.')) ->setArguments( array( array( diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php index 32a9419a33..b359157e06 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php @@ -38,10 +38,6 @@ final class PhabricatorWorkerBulkJobQuery return new PhabricatorWorkerBulkJob(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { $map = PhabricatorWorkerBulkJobType::getAllJobTypes(); diff --git a/src/infrastructure/diff/engine/PhabricatorInlineCommentAdjustmentEngine.php b/src/infrastructure/diff/engine/PhabricatorInlineCommentAdjustmentEngine.php index f1888cc363..0589cc0234 100644 --- a/src/infrastructure/diff/engine/PhabricatorInlineCommentAdjustmentEngine.php +++ b/src/infrastructure/diff/engine/PhabricatorInlineCommentAdjustmentEngine.php @@ -141,9 +141,7 @@ final class PhabricatorInlineCommentAdjustmentEngine } } - // Find the smallest "new" changeset ID. We'll consider everything - // larger than this to be "newer", and everything smaller to be "older". - $first_new_id = min(mpull($new, 'getID')); + $new_id_map = mpull($new, null, 'getID'); $results = array(); foreach ($inlines as $inline) { @@ -163,7 +161,7 @@ final class PhabricatorInlineCommentAdjustmentEngine $target_id = null; - if ($changeset_id >= $first_new_id) { + if (isset($new_id_map[$changeset_id])) { $name_map = $name_map_new; $is_new = true; } else { diff --git a/src/infrastructure/edges/conduit/EdgeSearchConduitAPIMethod.php b/src/infrastructure/edges/conduit/EdgeSearchConduitAPIMethod.php index 45adad542e..4704aa60b7 100644 --- a/src/infrastructure/edges/conduit/EdgeSearchConduitAPIMethod.php +++ b/src/infrastructure/edges/conduit/EdgeSearchConduitAPIMethod.php @@ -11,9 +11,7 @@ final class EdgeSearchConduitAPIMethod return pht('Read edge relationships between objects.'); } - public function getMethodDocumentation() { - $viewer = $this->getViewer(); - + protected function newDocumentationPages(PhabricatorUser $viewer) { $rows = array(); foreach ($this->getConduitEdgeTypeMap() as $key => $type) { $inverse_constant = $type->getInverseEdgeConstant(); @@ -48,17 +46,11 @@ final class EdgeSearchConduitAPIMethod 'wide', )); - return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Edge Types')) - ->setTable($types_table); - } - public function getMethodStatus() { - return self::METHOD_STATUS_UNSTABLE; - } - - public function getMethodStatusDescription() { - return pht('This method is new and experimental.'); + return array( + $this->newDocumentationBoxPage($viewer, pht('Edge Types'), $types_table) + ->setAnchor('types'), + ); } protected function defineParamTypes() { diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php index 3eaba5cd10..ca478ed0a5 100644 --- a/src/infrastructure/env/PhabricatorEnv.php +++ b/src/infrastructure/env/PhabricatorEnv.php @@ -554,17 +554,17 @@ final class PhabricatorEnv extends Phobject { switch ($reason) { case self::READONLY_MASTERLESS: return pht( - 'Phabricator is in read-only mode (no writable database '. + 'This server is in read-only mode (no writable database '. 'is configured).'); case self::READONLY_UNREACHABLE: return pht( - 'Phabricator is in read-only mode (unreachable master).'); + 'This server is in read-only mode (unreachable master).'); case self::READONLY_SEVERED: return pht( - 'Phabricator is in read-only mode (major interruption).'); + 'This server is in read-only mode (major interruption).'); } - return pht('Phabricator is in read-only mode.'); + return pht('This server is in read-only mode.'); } public static function getReadOnlyURI() { @@ -884,7 +884,7 @@ final class PhabricatorEnv extends Phobject { if (!$cluster_addresses) { throw new Exception( pht( - 'Phabricator is not configured to serve cluster requests. '. + 'This server is not configured to serve cluster requests. '. 'Set `cluster.addresses` in the configuration to whitelist '. 'cluster hosts before sending requests that use a cluster '. 'authentication mechanism.')); diff --git a/src/infrastructure/export/format/PhabricatorExcelExportFormat.php b/src/infrastructure/export/format/PhabricatorExcelExportFormat.php index e7135bd9db..dd33813384 100644 --- a/src/infrastructure/export/format/PhabricatorExcelExportFormat.php +++ b/src/infrastructure/export/format/PhabricatorExcelExportFormat.php @@ -33,8 +33,7 @@ EOHELP return pht(<< array( + array( + '%s modified an attached file: %3$s.', + '%s modified attached files: %3$s.', + ), + ), + + '%s attached %s referenced file(s): %s.' => array( + array( + '%s attached a referenced file: %3$s.', + '%s attached referenced files: %3$s.', + ), + ), + + '%s removed %s attached file(s): %s.' => array( + array( + '%s removed an attached file: %3$s.', + '%s removed attached files: %3$s.', + ), + ), + ); } diff --git a/src/infrastructure/markup/PhabricatorMarkupEngine.php b/src/infrastructure/markup/PhabricatorMarkupEngine.php index 5422870b68..cedd0398e3 100644 --- a/src/infrastructure/markup/PhabricatorMarkupEngine.php +++ b/src/infrastructure/markup/PhabricatorMarkupEngine.php @@ -42,10 +42,12 @@ final class PhabricatorMarkupEngine extends Phobject { private $objects = array(); private $viewer; private $contextObject; - private $version = 20; + private $version = 21; private $engineCaches = array(); private $auxiliaryConfig = array(); + private static $engineStack = array(); + /* -( Markup Pipeline )---------------------------------------------------- */ @@ -103,6 +105,24 @@ final class PhabricatorMarkupEngine extends Phobject { * @task markup */ public function process() { + self::$engineStack[] = $this; + + try { + $result = $this->execute(); + } finally { + array_pop(self::$engineStack); + } + + return $result; + } + + public static function isRenderingEmbeddedContent() { + // See T13678. This prevents cycles when rendering embedded content that + // itself has remarkup fields. + return (count(self::$engineStack) > 1); + } + + private function execute() { $keys = array(); foreach ($this->objects as $key => $info) { if (!isset($info['markup'])) { @@ -504,6 +524,7 @@ final class PhabricatorMarkupEngine extends Phobject { $rules = array(); $rules[] = new PhutilRemarkupEscapeRemarkupRule(); + $rules[] = new PhutilRemarkupEvalRule(); $rules[] = new PhutilRemarkupMonospaceRule(); @@ -582,6 +603,14 @@ final class PhabricatorMarkupEngine extends Phobject { $engine->setConfig('viewer', $viewer); foreach ($content_blocks as $content_block) { + if ($content_block === null) { + continue; + } + + if (!strlen($content_block)) { + continue; + } + $engine->markupText($content_block); $phids = $engine->getTextMetadata( PhabricatorMentionRemarkupRule::KEY_MENTIONED, @@ -603,7 +632,7 @@ final class PhabricatorMarkupEngine extends Phobject { foreach ($content_blocks as $content_block) { $engine->markupText($content_block); $phids = $engine->getTextMetadata( - PhabricatorEmbedFileRemarkupRule::KEY_EMBED_FILE_PHIDS, + PhabricatorEmbedFileRemarkupRule::KEY_ATTACH_INTENT_FILE_PHIDS, array()); foreach ($phids as $phid) { $files[$phid] = $phid; diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupBlockRule.php index feac6cffa9..ac3a308d06 100644 --- a/src/infrastructure/markup/blockrule/PhutilRemarkupBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupBlockRule.php @@ -43,6 +43,14 @@ abstract class PhutilRemarkupBlockRule extends Phobject { return; } + public function willMarkupChildBlocks() { + return; + } + + public function didMarkupChildBlocks() { + return; + } + final public function setEngine(PhutilRemarkupEngine $engine) { $this->engine = $engine; $this->updateRules(); diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupListBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupListBlockRule.php index 5bdcab2b8f..b7d82edfef 100644 --- a/src/infrastructure/markup/blockrule/PhutilRemarkupListBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupListBlockRule.php @@ -446,7 +446,7 @@ final class PhutilRemarkupListBlockRule extends PhutilRemarkupBlockRule { } $start_attr = null; - if (ctype_digit($starts_at) && $starts_at > 1) { + if (ctype_digit(phutil_string_cast($starts_at)) && $starts_at > 1) { $start_attr = hsprintf(' start="%d"', $starts_at); } diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupQuotedBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupQuotedBlockRule.php index 9f2bd7a297..90c9a2c33a 100644 --- a/src/infrastructure/markup/blockrule/PhutilRemarkupQuotedBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupQuotedBlockRule.php @@ -7,6 +7,22 @@ abstract class PhutilRemarkupQuotedBlockRule return true; } + public function willMarkupChildBlocks() { + $engine = $this->getEngine(); + + $depth = $engine->getQuoteDepth(); + $depth = $depth + 1; + $engine->setQuoteDepth($depth); + } + + public function didMarkupChildBlocks() { + $engine = $this->getEngine(); + + $depth = $engine->getQuoteDepth(); + $depth = $depth - 1; + $engine->setQuoteDepth($depth); + } + final protected function normalizeQuotedBody($text) { $text = phutil_split_lines($text, true); foreach ($text as $key => $line) { diff --git a/src/infrastructure/markup/markuprule/PhutilRemarkupDocumentLinkRule.php b/src/infrastructure/markup/markuprule/PhutilRemarkupDocumentLinkRule.php index 2170d9ae5e..ded57d4c77 100644 --- a/src/infrastructure/markup/markuprule/PhutilRemarkupDocumentLinkRule.php +++ b/src/infrastructure/markup/markuprule/PhutilRemarkupDocumentLinkRule.php @@ -46,7 +46,7 @@ final class PhutilRemarkupDocumentLinkRule extends PhutilRemarkupRule { $is_anchor = false; if (strncmp($link, '/', 1) == 0) { - $base = $engine->getConfig('uri.base'); + $base = phutil_string_cast($engine->getConfig('uri.base')); $base = rtrim($base, '/'); $link = $base.$link; } else if (strncmp($link, '#', 1) == 0) { @@ -134,7 +134,7 @@ final class PhutilRemarkupDocumentLinkRule extends PhutilRemarkupRule { public function markupDocumentLink(array $matches) { $uri = trim($matches[1]); - $name = trim(idx($matches, 2)); + $name = trim(idx($matches, 2, '')); if (!$this->isFlatText($uri)) { return $matches[0]; diff --git a/src/infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php b/src/infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php new file mode 100644 index 0000000000..cb67041c62 --- /dev/null +++ b/src/infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php @@ -0,0 +1,100 @@ +isFlatText($expression)) { + return $matches[0]; + } + + $engine = $this->getEngine(); + $token = $engine->storeText($expression); + + $list_key = self::KEY_EVAL; + $expression_list = $engine->getTextMetadata($list_key, array()); + + $expression_list[] = array( + 'token' => $token, + 'expression' => $expression, + 'original' => $matches[0], + ); + + $engine->setTextMetadata($list_key, $expression_list); + + return $token; + } + + public function didMarkupText() { + $engine = $this->getEngine(); + + $list_key = self::KEY_EVAL; + $expression_list = $engine->getTextMetadata($list_key, array()); + + foreach ($expression_list as $expression_item) { + $token = $expression_item['token']; + $expression = $expression_item['expression']; + + $result = $this->evaluateExpression($expression); + + if ($result === null) { + $result = $expression_item['original']; + } + + $engine->overwriteStoredText($token, $result); + } + } + + private function evaluateExpression($expression) { + static $string_map; + + if ($string_map === null) { + $string_map = array( + 'strings' => array( + 'platform' => array( + 'server' => array( + 'name' => PlatformSymbols::getPlatformServerName(), + 'path' => pht('phabricator/'), + ), + 'client' => array( + 'name' => PlatformSymbols::getPlatformClientName(), + 'path' => pht('arcanist/'), + ), + ), + ), + ); + } + + $parts = explode('.', $expression); + + $cursor = $string_map; + foreach ($parts as $part) { + if (isset($cursor[$part])) { + $cursor = $cursor[$part]; + } else { + break; + } + } + + if (is_string($cursor)) { + return $cursor; + } + + return null; + } + +} diff --git a/src/infrastructure/markup/remarkup/PhutilRemarkupEngine.php b/src/infrastructure/markup/remarkup/PhutilRemarkupEngine.php index a0a379aaf9..bedbf0bab7 100644 --- a/src/infrastructure/markup/remarkup/PhutilRemarkupEngine.php +++ b/src/infrastructure/markup/remarkup/PhutilRemarkupEngine.php @@ -42,6 +42,14 @@ final class PhutilRemarkupEngine extends PhutilMarkupEngine { return $this->mode & self::MODE_HTML_MAIL; } + public function getQuoteDepth() { + return $this->getConfig('runtime.quote.depth', 0); + } + + public function setQuoteDepth($depth) { + return $this->setConfig('runtime.quote.depth', $depth); + } + public function setBlockRules(array $rules) { assert_instances_of($rules, 'PhutilRemarkupBlockRule'); @@ -255,18 +263,24 @@ final class PhutilRemarkupEngine extends PhutilMarkupEngine { } private function markupBlock(array $block) { + $rule = $block['rule']; + + $rule->willMarkupChildBlocks(); + $children = array(); foreach ($block['children'] as $child) { $children[] = $this->markupBlock($child); } + $rule->didMarkupChildBlocks(); + if ($children) { $children = $this->flattenOutput($children); } else { $children = null; } - return $block['rule']->markupText($block['text'], $children); + return $rule->markupText($block['text'], $children); } private function flattenOutput(array $output) { diff --git a/src/infrastructure/markup/render.php b/src/infrastructure/markup/render.php index 7294fc0ecf..84c3616fe8 100644 --- a/src/infrastructure/markup/render.php +++ b/src/infrastructure/markup/render.php @@ -105,6 +105,10 @@ function phutil_tag_div($class, $content = null) { } function phutil_escape_html($string) { + if ($string === null) { + return ''; + } + if ($string instanceof PhutilSafeHTML) { return $string; } else if ($string instanceof PhutilSafeHTMLProducerInterface) { diff --git a/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php b/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php index 7fb997b2c5..b0399527b0 100644 --- a/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php +++ b/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php @@ -126,6 +126,12 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule { return $this->renderObjectTagForMail($name, $href, $handle); } + // See T13678. If we're already rendering embedded content, render a + // default reference instead to avoid cycles. + if (PhabricatorMarkupEngine::isRenderingEmbeddedContent()) { + return $this->renderDefaultObjectEmbed($object, $handle); + } + return $this->renderObjectEmbed($object, $handle, $options); } @@ -133,6 +139,12 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule { $object, PhabricatorObjectHandle $handle, $options) { + return $this->renderDefaultObjectEmbed($object, $handle); + } + + final protected function renderDefaultObjectEmbed( + $object, + PhabricatorObjectHandle $handle) { $name = $handle->getFullName(); $href = $handle->getURI(); @@ -306,6 +318,7 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule { 'id' => $matches[1], 'options' => idx($matches, 2), 'original' => $matches[0], + 'quote.depth' => $engine->getQuoteDepth(), )); } @@ -325,6 +338,7 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule { 'id' => $matches[1], 'anchor' => idx($matches, 2), 'original' => $matches[0], + 'quote.depth' => $engine->getQuoteDepth(), )); } diff --git a/src/infrastructure/query/PhabricatorQuery.php b/src/infrastructure/query/PhabricatorQuery.php index 4315ef79ae..f4ce35adaf 100644 --- a/src/infrastructure/query/PhabricatorQuery.php +++ b/src/infrastructure/query/PhabricatorQuery.php @@ -87,7 +87,7 @@ abstract class PhabricatorQuery extends Phobject { foreach ($this->flattenSubclause($part) as $subpart) { $result[] = $subpart; } - } else if (strlen($part)) { + } else if (($part !== null) && strlen($part)) { $result[] = $part; } } diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php index 34a8e1813c..42ccad3316 100644 --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -265,6 +265,24 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery return $this->ferretMetadata; } + protected function loadPage() { + $object = $this->newResultObject(); + + if (!$object instanceof PhabricatorLiskDAO) { + throw new Exception( + pht( + 'Query class ("%s") did not return the correct type of object '. + 'from "newResultObject()" (expected a subclass of '. + '"PhabricatorLiskDAO", found "%s"). Return an object of the '. + 'expected type (this is common), or implement a custom '. + '"loadPage()" method (this is unusual in modern code).', + get_class($this), + phutil_describe_type($object))); + } + + return $this->loadStandardPage($object); + } + protected function loadStandardPage(PhabricatorLiskDAO $table) { $rows = $this->loadStandardPageRows($table); return $table->loadAllFromArray($rows); @@ -369,10 +387,13 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery $this->setLimit($limit + 1); - if (strlen($pager->getAfterID())) { - $this->setExternalCursorString($pager->getAfterID()); - } else if ($pager->getBeforeID()) { - $this->setExternalCursorString($pager->getBeforeID()); + $after_id = phutil_string_cast($pager->getAfterID()); + $before_id = phutil_string_cast($pager->getBeforeID()); + + if (phutil_nonempty_string($after_id)) { + $this->setExternalCursorString($after_id); + } else if (phutil_nonempty_string($before_id)) { + $this->setExternalCursorString($before_id); $this->setIsQueryOrderReversed(true); } diff --git a/src/infrastructure/storage/connection/mysql/AphrontBaseMySQLDatabaseConnection.php b/src/infrastructure/storage/connection/mysql/AphrontBaseMySQLDatabaseConnection.php index db52eebe8e..6ff9b3ba90 100644 --- a/src/infrastructure/storage/connection/mysql/AphrontBaseMySQLDatabaseConnection.php +++ b/src/infrastructure/storage/connection/mysql/AphrontBaseMySQLDatabaseConnection.php @@ -77,6 +77,7 @@ abstract class AphrontBaseMySQLDatabaseConnection } public function escapeStringForLikeClause($value) { + $value = phutil_string_cast($value); $value = addcslashes($value, '\%_'); $value = $this->escapeUTF8String($value); return $value; diff --git a/src/infrastructure/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php b/src/infrastructure/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php index 6a0bc759a7..887f4318d2 100644 --- a/src/infrastructure/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php +++ b/src/infrastructure/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php @@ -57,6 +57,13 @@ final class AphrontMySQLiDatabaseConnection } } + // See T13588. In PHP 8.1, the default "report mode" for MySQLi has + // changed, which causes MySQLi to raise exceptions. Disable exceptions + // to align behavior with older default behavior under MySQLi, which + // this code expects. Plausibly, this code could be updated to use + // MySQLi exceptions to handle errors under a wider range of PHP versions. + mysqli_report(MYSQLI_REPORT_OFF); + $conn = mysqli_init(); $timeout = $this->getConfiguration('timeout'); diff --git a/src/infrastructure/storage/lisk/LiskDAO.php b/src/infrastructure/storage/lisk/LiskDAO.php index 13b2f8d319..2e81b4641a 100644 --- a/src/infrastructure/storage/lisk/LiskDAO.php +++ b/src/infrastructure/storage/lisk/LiskDAO.php @@ -193,6 +193,8 @@ abstract class LiskDAO extends Phobject private static $connections = array(); + private static $liskMetadata = array(); + protected $id; protected $phid; protected $dateCreated; @@ -403,10 +405,11 @@ abstract class LiskDAO extends Phobject * @task config */ public function getConfigOption($option_name) { - static $options = null; + $options = $this->getLiskMetadata('config'); - if (!isset($options)) { + if ($options === null) { $options = $this->getConfiguration(); + $this->setLiskMetadata('config', $options); } return idx($options, $option_name); @@ -439,7 +442,7 @@ abstract class LiskDAO extends Phobject return $this->loadOneWhere( '%C = %d', - $this->getIDKeyForUse(), + $this->getIDKey(), $id); } @@ -554,7 +557,7 @@ abstract class LiskDAO extends Phobject $result = $this->loadOneWhere( '%C = %d', - $this->getIDKeyForUse(), + $this->getIDKey(), $this->getID()); if (!$result) { @@ -579,9 +582,10 @@ abstract class LiskDAO extends Phobject * @task load */ public function loadFromArray(array $row) { - static $valid_properties = array(); + $valid_map = $this->getLiskMetadata('validMap', array()); $map = array(); + $updated = false; foreach ($row as $k => $v) { // We permit (but ignore) extra properties in the array because a // common approach to building the array is to issue a raw SELECT query @@ -594,14 +598,15 @@ abstract class LiskDAO extends Phobject // path (assigning an invalid property which we've already seen) costs // an empty() plus an isset(). - if (empty($valid_properties[$k])) { - if (isset($valid_properties[$k])) { + if (empty($valid_map[$k])) { + if (isset($valid_map[$k])) { // The value is set but empty, which means it's false, so we've // already determined it's not valid. We don't need to check again. continue; } - $valid_properties[$k] = $this->hasProperty($k); - if (!$valid_properties[$k]) { + $valid_map[$k] = $this->hasProperty($k); + $updated = true; + if (!$valid_map[$k]) { continue; } } @@ -609,6 +614,10 @@ abstract class LiskDAO extends Phobject $map[$k] = $v; } + if ($updated) { + $this->setLiskMetadata('validMap', $valid_map); + } + $this->willReadData($map); foreach ($map as $prop => $value) { @@ -686,10 +695,7 @@ abstract class LiskDAO extends Phobject * @task save */ public function setID($id) { - static $id_key = null; - if ($id_key === null) { - $id_key = $this->getIDKeyForUse(); - } + $id_key = $this->getIDKey(); $this->$id_key = $id; return $this; } @@ -704,10 +710,7 @@ abstract class LiskDAO extends Phobject * @task info */ public function getID() { - static $id_key = null; - if ($id_key === null) { - $id_key = $this->getIDKeyForUse(); - } + $id_key = $this->getIDKey(); return $this->$id_key; } @@ -742,9 +745,10 @@ abstract class LiskDAO extends Phobject * @task info */ protected function getAllLiskProperties() { - static $properties = null; - if (!isset($properties)) { - $class = new ReflectionClass(get_class($this)); + $properties = $this->getLiskMetadata('properties'); + + if ($properties === null) { + $class = new ReflectionClass(static::class); $properties = array(); foreach ($class->getProperties(ReflectionProperty::IS_PROTECTED) as $p) { $properties[strtolower($p->getName())] = $p->getName(); @@ -763,7 +767,10 @@ abstract class LiskDAO extends Phobject if ($id_key != 'phid' && !$this->getConfigOption(self::CONFIG_AUX_PHID)) { unset($properties['phid']); } + + $this->setLiskMetadata('properties', $properties); } + return $properties; } @@ -777,10 +784,7 @@ abstract class LiskDAO extends Phobject * @task info */ protected function checkProperty($property) { - static $properties = null; - if ($properties === null) { - $properties = $this->getAllLiskProperties(); - } + $properties = $this->getAllLiskProperties(); $property = strtolower($property); if (empty($properties[$property])) { @@ -996,7 +1000,7 @@ abstract class LiskDAO extends Phobject 'UPDATE %R SET %LQ WHERE %C = '.(is_int($id) ? '%d' : '%s'), $this, $map, - $this->getIDKeyForUse(), + $this->getIDKey(), $id); // We can't detect a missing object because updating an object without // changing any values doesn't affect rows. We could jiggle timestamps @@ -1023,7 +1027,7 @@ abstract class LiskDAO extends Phobject $conn->query( 'DELETE FROM %R WHERE %C = %d', $this, - $this->getIDKeyForUse(), + $this->getIDKey(), $this->getID()); $this->didDelete(); @@ -1051,7 +1055,7 @@ abstract class LiskDAO extends Phobject // If we are using autoincrement IDs, let MySQL assign the value for the // ID column, if it is empty. If the caller has explicitly provided a // value, use it. - $id_key = $this->getIDKeyForUse(); + $id_key = $this->getIDKey(); if (empty($data[$id_key])) { unset($data[$id_key]); } @@ -1059,7 +1063,7 @@ abstract class LiskDAO extends Phobject case self::IDS_COUNTER: // If we are using counter IDs, assign a new ID if we don't already have // one. - $id_key = $this->getIDKeyForUse(); + $id_key = $this->getIDKey(); if (empty($data[$id_key])) { $counter_name = $this->getTableName(); $id = self::loadNextCounterValue($conn, $counter_name); @@ -1175,19 +1179,6 @@ abstract class LiskDAO extends Phobject return 'id'; } - - protected function getIDKeyForUse() { - $id_key = $this->getIDKey(); - if (!$id_key) { - throw new Exception( - pht( - 'This DAO does not have a single-part primary key. The method you '. - 'called requires a single-part primary key.')); - } - return $id_key; - } - - /** * Generate a new PHID, used by CONFIG_AUX_PHID. * @@ -1592,22 +1583,12 @@ abstract class LiskDAO extends Phobject * @task util */ public function __call($method, $args) { - // NOTE: PHP has a bug that static variables defined in __call() are shared - // across all children classes. Call a different method to work around this - // bug. - return $this->call($method, $args); - } + $dispatch_map = $this->getLiskMetadata('dispatchMap', array()); - /** - * @task util - */ - final protected function call($method, $args) { // NOTE: This method is very performance-sensitive (many thousands of calls // per page on some pages), and thus has some silliness in the name of // optimizations. - static $dispatch_map = array(); - if ($method[0] === 'g') { if (isset($dispatch_map[$method])) { $property = $dispatch_map[$method]; @@ -1620,6 +1601,7 @@ abstract class LiskDAO extends Phobject throw new Exception(pht('Bad getter call: %s', $method)); } $dispatch_map[$method] = $property; + $this->setLiskMetadata('dispatchMap', $dispatch_map); } return $this->readField($property); @@ -1632,12 +1614,14 @@ abstract class LiskDAO extends Phobject if (substr($method, 0, 3) !== 'set') { throw new Exception(pht("Unable to resolve method '%s'!", $method)); } + $property = substr($method, 3); $property = $this->checkProperty($property); if (!$property) { throw new Exception(pht('Bad setter call: %s', $method)); } $dispatch_map[$method] = $property; + $this->setLiskMetadata('dispatchMap', $dispatch_map); } $this->writeField($property, $args[0]); @@ -1909,4 +1893,20 @@ abstract class LiskDAO extends Phobject } + private function getLiskMetadata($key, $default = null) { + if (isset(self::$liskMetadata[static::class][$key])) { + return self::$liskMetadata[static::class][$key]; + } + + if (!isset(self::$liskMetadata[static::class])) { + self::$liskMetadata[static::class] = array(); + } + + return idx(self::$liskMetadata[static::class], $key, $default); + } + + private function setLiskMetadata($key, $value) { + self::$liskMetadata[static::class][$key] = $value; + } + } diff --git a/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php b/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php index 85adb4fc29..93f623338f 100644 --- a/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php +++ b/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php @@ -138,7 +138,7 @@ abstract class PhabricatorLiskDAO extends LiskDAO { throw new PhabricatorClusterImproperWriteException( pht( 'Unable to establish a write-mode connection (to application '. - 'database "%s") because Phabricator is in read-only mode. Whatever '. + 'database "%s") because this server is in read-only mode. Whatever '. 'you are trying to do does not function correctly in read-only mode.', $database)); } diff --git a/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php b/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php index a6a0d74593..d9d16dba2e 100644 --- a/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php +++ b/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php @@ -89,6 +89,17 @@ final class PhabricatorStorageManagementAPI extends Phobject { return $this->namespace.'_'.$fragment; } + public function getInternalDatabaseName($name) { + $namespace = $this->getNamespace(); + + $prefix = $namespace.'_'; + if (strncmp($name, $prefix, strlen($prefix))) { + return null; + } + + return substr($name, strlen($prefix)); + } + public function getDisplayName() { return $this->getRef()->getDisplayName(); } diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDatabasesWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDatabasesWorkflow.php index ef6735ec8f..00689ab5fe 100644 --- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDatabasesWorkflow.php +++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDatabasesWorkflow.php @@ -7,7 +7,7 @@ final class PhabricatorStorageManagementDatabasesWorkflow $this ->setName('databases') ->setExamples('**databases** [__options__]') - ->setSynopsis(pht('List Phabricator databases.')); + ->setSynopsis(pht('List databases.')); } protected function isReadOnlyWorkflow() { diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDestroyWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDestroyWorkflow.php index 9b718e231d..d9ad8b8375 100644 --- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDestroyWorkflow.php +++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDestroyWorkflow.php @@ -47,9 +47,10 @@ final class PhabricatorStorageManagementDestroyWorkflow } else { $warning = pht( 'Are you completely sure you really want to permanently destroy '. - 'all storage for Phabricator data on host "%s"? This operation '. + 'all storage for %s data on host "%s"? This operation '. 'can not be undone and your data will not be recoverable if '. 'you proceed.', + PlatformSymbols::getPlatformServerName(), $host_display); echo tsprintf( diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDumpWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDumpWorkflow.php index ebe1c77f40..8cc5b1ae63 100644 --- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDumpWorkflow.php +++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDumpWorkflow.php @@ -44,6 +44,16 @@ final class PhabricatorStorageManagementDumpWorkflow 'With __--output__, overwrite the output file if it already '. 'exists.'), ), + array( + 'name' => 'database', + 'param' => 'database-name', + 'help' => pht( + 'Dump only tables in the named database (or databases, if '. + 'the flag is repeated). Specify database names without the '. + 'namespace prefix (that is: use "differential", not '. + '"phabricator_differential").'), + 'repeat' => true, + ), )); } @@ -58,6 +68,8 @@ final class PhabricatorStorageManagementDumpWorkflow $is_noindex = $args->getArg('no-indexes'); $is_replica = $args->getArg('for-replica'); + $database_filter = $args->getArg('database'); + if ($is_compress) { if ($output_file === null) { throw new PhutilArgumentUsageException( @@ -128,11 +140,60 @@ final class PhabricatorStorageManagementDumpWorkflow $schemata = $actual_map[$ref_key]; $expect = $expect_map[$ref_key]; + if ($database_filter) { + $internal_names = array(); + + $expect_databases = $expect->getDatabases(); + foreach ($expect_databases as $expect_database) { + $database_name = $expect_database->getName(); + + $internal_name = $api->getInternalDatabaseName($database_name); + if ($internal_name !== null) { + $internal_names[$internal_name] = $database_name; + } + } + + ksort($internal_names); + + $seen = array(); + foreach ($database_filter as $filter) { + if (!isset($internal_names[$filter])) { + throw new PhutilArgumentUsageException( + pht( + 'Database "%s" is unknown. This script can only dump '. + 'databases known to the current version of this software. '. + 'Valid databases are: %s.', + $filter, + implode(', ', array_keys($internal_names)))); + } + + if (isset($seen[$filter])) { + throw new PhutilArgumentUsageException( + pht( + 'Database "%s" is specified more than once. Specify each '. + 'database at most once.', + $filter)); + } + + $seen[$filter] = true; + } + + $dump_databases = array_select_keys($internal_names, $database_filter); + $dump_databases = array_fuse($dump_databases); + } else { + $dump_databases = array_keys($schemata->getDatabases()); + $dump_databases = array_fuse($dump_databases); + } + $with_caches = $is_replica; $with_indexes = !$is_noindex; $targets = array(); foreach ($schemata->getDatabases() as $database_name => $database) { + if (!isset($dump_databases[$database_name])) { + continue; + } + $expect_database = $expect->getDatabase($database_name); foreach ($database->getTables() as $table_name => $table) { diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementQuickstartWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementQuickstartWorkflow.php index e181063ac4..b492764c99 100644 --- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementQuickstartWorkflow.php +++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementQuickstartWorkflow.php @@ -10,7 +10,7 @@ final class PhabricatorStorageManagementQuickstartWorkflow ->setSynopsis( pht( 'Generate a new quickstart database dump. This command is mostly '. - 'useful when developing Phabricator.')) + 'useful for internal development.')) ->setArguments( array( array( diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementUpgradeWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementUpgradeWorkflow.php index e0b8d884c5..6a268cd440 100644 --- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementUpgradeWorkflow.php +++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementUpgradeWorkflow.php @@ -46,8 +46,8 @@ final class PhabricatorStorageManagementUpgradeWorkflow phutil_console_wrap( pht( 'Before running storage upgrades, you should take down the '. - 'Phabricator web interface and stop any running Phabricator '. - 'daemons (you can disable this warning with %s).', + 'web interface and stop any running daemons (you can disable '. + 'this warning with %s).', '--force'))); if (!phutil_console_confirm(pht('Are you ready to continue?'))) { diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php index 71f6374b2a..38a7b7788c 100644 --- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php +++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php @@ -47,7 +47,7 @@ abstract class PhabricatorStorageManagementWorkflow throw new PhutilArgumentUsageException( pht( - 'Phabricator is configured in cluster mode, with multiple database '. + 'This server is configured in cluster mode, with multiple database '. 'hosts. Use "--host" to specify which host you want to operate on.')); } @@ -98,7 +98,7 @@ abstract class PhabricatorStorageManagementWorkflow } else { throw new PhutilArgumentUsageException( pht( - 'Phabricator is currently in read-only mode. Use --force to '. + 'This server is currently in read-only mode. Use --force to '. 'override this mode.')); } } @@ -189,7 +189,7 @@ abstract class PhabricatorStorageManagementWorkflow "You can exit this workflow, update MySQL now, and then run this ". "workflow again. This is recommended, but may cause a lot of downtime ". "right now.\n\n". - "You can exit this workflow, continue using Phabricator without ". + "You can exit this workflow, continue using this software without ". "applying adjustments, update MySQL at a later date, and then run ". "this workflow again. This is also a good approach, and will let you ". "delay downtime until later.\n\n". @@ -826,8 +826,8 @@ abstract class PhabricatorStorageManagementWorkflow $message = array(); if ($all_surplus) { $message[] = pht( - 'You have surplus schemata (extra tables or columns which Phabricator '. - 'does not expect). For information on resolving these '. + 'You have surplus schemata (extra tables or columns which this '. + 'software does not expect). For information on resolving these '. 'issues, see the "Surplus Schemata" section in the "Managing Storage '. 'Adjustments" article in the documentation.'); } else if ($all_access) { @@ -844,27 +844,29 @@ abstract class PhabricatorStorageManagementWorkflow $message[] = pht( 'Some of these errors are caused by access control problems. '. 'The user you are connecting with does not have permission to see '. - 'all of the database or tables that Phabricator uses. You need to '. + 'all of the database or tables that this software uses. You need to '. 'GRANT the user more permission, or use a different user.'); } if ($any_surplus) { $message[] = pht( 'Some of these errors are caused by surplus schemata (extra '. - 'tables or columns which Phabricator does not expect). These are '. + 'tables or columns which this software does not expect). These are '. 'not serious. For information on resolving these issues, see the '. '"Surplus Schemata" section in the "Managing Storage Adjustments" '. 'article in the documentation.'); } $message[] = pht( - 'If you are not developing Phabricator itself, report this issue to '. - 'the upstream.'); + 'If you are not developing %s itself, report this issue to '. + 'the upstream.', + PlatformSymbols::getPlatformServerName()); $message[] = pht( - 'If you are developing Phabricator, these errors usually indicate '. + 'If you are developing %s, these errors usually indicate '. 'that your schema specifications do not agree with the schemata your '. - 'code actually builds.'); + 'code actually builds.', + PlatformSymbols::getPlatformServerName()); } $message = implode("\n\n", $message); diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php index 8adcfa64df..f68635860a 100644 --- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php +++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php @@ -93,7 +93,6 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList { 'db.conpherence' => array(), 'db.config' => array(), 'db.token' => array(), - 'db.releeph' => array(), 'db.phlux' => array(), 'db.phortune' => array(), 'db.phrequent' => array(), @@ -104,7 +103,6 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList { 'db.policy' => array(), 'db.nuance' => array(), 'db.passphrase' => array(), - 'db.phragment' => array(), 'db.dashboard' => array(), 'db.system' => array(), 'db.fund' => array(), diff --git a/src/infrastructure/util/PhabricatorHash.php b/src/infrastructure/util/PhabricatorHash.php index d717778eb3..8916b37a0e 100644 --- a/src/infrastructure/util/PhabricatorHash.php +++ b/src/infrastructure/util/PhabricatorHash.php @@ -22,7 +22,7 @@ final class PhabricatorHash extends Phobject { if (!$key) { throw new Exception( pht( - "Set a '%s' in your Phabricator configuration!", + "Set a '%s' in your configuration!", 'security.hmac-key')); } @@ -224,7 +224,7 @@ final class PhabricatorHash extends Phobject { $cache_key = "hmac.key({$hmac_name})"; $hmac_key = $cache->getKey($cache_key); - if (!strlen($hmac_key)) { + if (($hmac_key === null) || !strlen($hmac_key)) { $hmac_key = self::readHMACKey($hmac_name); if ($hmac_key === null) { diff --git a/src/infrastructure/util/PhabricatorMetronome.php b/src/infrastructure/util/PhabricatorMetronome.php index 24f58127f6..d56fa55b74 100644 --- a/src/infrastructure/util/PhabricatorMetronome.php +++ b/src/infrastructure/util/PhabricatorMetronome.php @@ -49,7 +49,7 @@ final class PhabricatorMetronome } public function setOffsetFromSeed($seed) { - $offset = PhabricatorHash::digestToRange($seed, 0, PHP_INT_MAX); + $offset = PhabricatorHash::digestToRange($seed, 0, 0x7FFFFFFF); return $this->setOffset($offset); } diff --git a/src/infrastructure/util/password/PhabricatorPasswordHasher.php b/src/infrastructure/util/password/PhabricatorPasswordHasher.php index f0f30045c0..fe35d2c296 100644 --- a/src/infrastructure/util/password/PhabricatorPasswordHasher.php +++ b/src/infrastructure/util/password/PhabricatorPasswordHasher.php @@ -307,7 +307,7 @@ abstract class PhabricatorPasswordHasher extends Phobject { throw new PhabricatorPasswordHasherUnavailableException( pht( 'Attempting to compare a password saved with the "%s" hash. No such '. - 'hasher is known to Phabricator.', + 'hasher is known.', $name)); } diff --git a/src/view/form/control/AphrontFormTextAreaControl.php b/src/view/form/control/AphrontFormTextAreaControl.php index 2665c6aaa7..e548916fbb 100644 --- a/src/view/form/control/AphrontFormTextAreaControl.php +++ b/src/view/form/control/AphrontFormTextAreaControl.php @@ -73,7 +73,7 @@ class AphrontFormTextAreaControl extends AphrontFormControl { // NOTE: This needs to be string cast, because if we pass `null` the // tag will be self-closed and some browsers aren't thrilled about that. - $value = (string)$this->getValue(); + $value = phutil_string_cast($this->getValue()); // NOTE: We also need to prefix the string with a newline, because browsers // ignore a newline immediately after a