1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-01 17:38:24 +01:00

(stable) Promote 2020 Week 5

This commit is contained in:
epriestley 2020-01-30 13:21:02 -08:00
commit cc11dff7d3
49 changed files with 2013 additions and 233 deletions

20
externals/porter-stemmer/LICENSE vendored Normal file
View file

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2005-2016 Richard Heyes (http://www.phpguru.org/)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

42
externals/porter-stemmer/README.md vendored Normal file
View file

@ -0,0 +1,42 @@
# Porter Stemmer by Richard Heyes
# Installation (with composer)
```json
{
"require": {
"camspiers/porter-stemmer": "1.0.0"
}
}
```
$ composer install
# Usage
```php
$stem = Porter::Stem($word);
```
# License
The MIT License (MIT)
Copyright (c) 2005-2016 Richard Heyes (http://www.phpguru.org/)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

426
externals/porter-stemmer/src/Porter.php vendored Normal file
View file

@ -0,0 +1,426 @@
<?php
# vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4:
/**
* Copyright (c) 2005-2016 Richard Heyes (http://www.phpguru.org/)
*
* Portions Copyright 2003-2007 Jon Abernathy <jon@chuggnutt.com>
*
* Originally available under the GPL 2 or greater. Relicensed with permission
* of original authors under the MIT License in 2016.
*
* All rights reserved.
*
* @package PorterStemmer
* @author Richard Heyes
* @author Jon Abernathy <jon@chuggnutt.com>
* @copyright 2005-2016 Richard Heyes (http://www.phpguru.org/)
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
/**
* PHP 5 Implementation of the Porter Stemmer algorithm. Certain elements
* were borrowed from the (broken) implementation by Jon Abernathy.
*
* See http://tartarus.org/~martin/PorterStemmer/ for a description of the
* algorithm.
*
* Usage:
*
* $stem = PorterStemmer::Stem($word);
*
* How easy is that?
*
* @package PorterStemmer
* @author Richard Heyes
* @author Jon Abernathy <jon@chuggnutt.com>
* @copyright 2005-2016 Richard Heyes (http://www.phpguru.org/)
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
class Porter
{
/**
* Regex for matching a consonant
*
* @var string
*/
private static $regex_consonant = '(?:[bcdfghjklmnpqrstvwxz]|(?<=[aeiou])y|^y)';
/**
* Regex for matching a vowel
*
* @var string
*/
private static $regex_vowel = '(?:[aeiou]|(?<![aeiou])y)';
/**
* Stems a word. Simple huh?
*
* @param string $word Word to stem
*
* @return string Stemmed word
*/
public static function Stem($word)
{
if (strlen($word) <= 2) {
return $word;
}
$word = self::step1ab($word);
$word = self::step1c($word);
$word = self::step2($word);
$word = self::step3($word);
$word = self::step4($word);
$word = self::step5($word);
return $word;
}
/**
* Step 1
*/
private static function step1ab($word)
{
// Part a
if (substr($word, -1) == 's') {
self::replace($word, 'sses', 'ss')
OR self::replace($word, 'ies', 'i')
OR self::replace($word, 'ss', 'ss')
OR self::replace($word, 's', '');
}
// Part b
if (substr($word, -2, 1) != 'e' OR !self::replace($word, 'eed', 'ee', 0)) { // First rule
$v = self::$regex_vowel;
// ing and ed
if ( preg_match("#$v+#", substr($word, 0, -3)) && self::replace($word, 'ing', '')
OR preg_match("#$v+#", substr($word, 0, -2)) && self::replace($word, 'ed', '')) { // Note use of && and OR, for precedence reasons
// If one of above two test successful
if ( !self::replace($word, 'at', 'ate')
AND !self::replace($word, 'bl', 'ble')
AND !self::replace($word, 'iz', 'ize')) {
// Double consonant ending
if ( self::doubleConsonant($word)
AND substr($word, -2) != 'll'
AND substr($word, -2) != 'ss'
AND substr($word, -2) != 'zz') {
$word = substr($word, 0, -1);
} elseif (self::m($word) == 1 AND self::cvc($word)) {
$word .= 'e';
}
}
}
}
return $word;
}
/**
* Step 1c
*
* @param string $word Word to stem
*/
private static function step1c($word)
{
$v = self::$regex_vowel;
if (substr($word, -1) == 'y' && preg_match("#$v+#", substr($word, 0, -1))) {
self::replace($word, 'y', 'i');
}
return $word;
}
/**
* Step 2
*
* @param string $word Word to stem
*/
private static function step2($word)
{
switch (substr($word, -2, 1)) {
case 'a':
self::replace($word, 'ational', 'ate', 0)
OR self::replace($word, 'tional', 'tion', 0);
break;
case 'c':
self::replace($word, 'enci', 'ence', 0)
OR self::replace($word, 'anci', 'ance', 0);
break;
case 'e':
self::replace($word, 'izer', 'ize', 0);
break;
case 'g':
self::replace($word, 'logi', 'log', 0);
break;
case 'l':
self::replace($word, 'entli', 'ent', 0)
OR self::replace($word, 'ousli', 'ous', 0)
OR self::replace($word, 'alli', 'al', 0)
OR self::replace($word, 'bli', 'ble', 0)
OR self::replace($word, 'eli', 'e', 0);
break;
case 'o':
self::replace($word, 'ization', 'ize', 0)
OR self::replace($word, 'ation', 'ate', 0)
OR self::replace($word, 'ator', 'ate', 0);
break;
case 's':
self::replace($word, 'iveness', 'ive', 0)
OR self::replace($word, 'fulness', 'ful', 0)
OR self::replace($word, 'ousness', 'ous', 0)
OR self::replace($word, 'alism', 'al', 0);
break;
case 't':
self::replace($word, 'biliti', 'ble', 0)
OR self::replace($word, 'aliti', 'al', 0)
OR self::replace($word, 'iviti', 'ive', 0);
break;
}
return $word;
}
/**
* Step 3
*
* @param string $word String to stem
*/
private static function step3($word)
{
switch (substr($word, -2, 1)) {
case 'a':
self::replace($word, 'ical', 'ic', 0);
break;
case 's':
self::replace($word, 'ness', '', 0);
break;
case 't':
self::replace($word, 'icate', 'ic', 0)
OR self::replace($word, 'iciti', 'ic', 0);
break;
case 'u':
self::replace($word, 'ful', '', 0);
break;
case 'v':
self::replace($word, 'ative', '', 0);
break;
case 'z':
self::replace($word, 'alize', 'al', 0);
break;
}
return $word;
}
/**
* Step 4
*
* @param string $word Word to stem
*/
private static function step4($word)
{
switch (substr($word, -2, 1)) {
case 'a':
self::replace($word, 'al', '', 1);
break;
case 'c':
self::replace($word, 'ance', '', 1)
OR self::replace($word, 'ence', '', 1);
break;
case 'e':
self::replace($word, 'er', '', 1);
break;
case 'i':
self::replace($word, 'ic', '', 1);
break;
case 'l':
self::replace($word, 'able', '', 1)
OR self::replace($word, 'ible', '', 1);
break;
case 'n':
self::replace($word, 'ant', '', 1)
OR self::replace($word, 'ement', '', 1)
OR self::replace($word, 'ment', '', 1)
OR self::replace($word, 'ent', '', 1);
break;
case 'o':
if (substr($word, -4) == 'tion' OR substr($word, -4) == 'sion') {
self::replace($word, 'ion', '', 1);
} else {
self::replace($word, 'ou', '', 1);
}
break;
case 's':
self::replace($word, 'ism', '', 1);
break;
case 't':
self::replace($word, 'ate', '', 1)
OR self::replace($word, 'iti', '', 1);
break;
case 'u':
self::replace($word, 'ous', '', 1);
break;
case 'v':
self::replace($word, 'ive', '', 1);
break;
case 'z':
self::replace($word, 'ize', '', 1);
break;
}
return $word;
}
/**
* Step 5
*
* @param string $word Word to stem
*/
private static function step5($word)
{
// Part a
if (substr($word, -1) == 'e') {
if (self::m(substr($word, 0, -1)) > 1) {
self::replace($word, 'e', '');
} elseif (self::m(substr($word, 0, -1)) == 1) {
if (!self::cvc(substr($word, 0, -1))) {
self::replace($word, 'e', '');
}
}
}
// Part b
if (self::m($word) > 1 AND self::doubleConsonant($word) AND substr($word, -1) == 'l') {
$word = substr($word, 0, -1);
}
return $word;
}
/**
* Replaces the first string with the second, at the end of the string
*
* If third arg is given, then the preceding string must match that m
* count at least.
*
* @param string $str String to check
* @param string $check Ending to check for
* @param string $repl Replacement string
* @param int $m Optional minimum number of m() to meet
*
* @return bool Whether the $check string was at the end of the $str
* string. True does not necessarily mean that it was
* replaced.
*/
private static function replace(&$str, $check, $repl, $m = null)
{
$len = 0 - strlen($check);
if (substr($str, $len) == $check) {
$substr = substr($str, 0, $len);
if (is_null($m) OR self::m($substr) > $m) {
$str = $substr . $repl;
}
return true;
}
return false;
}
/**
* What, you mean it's not obvious from the name?
*
* m() measures the number of consonant sequences in $str. if c is
* a consonant sequence and v a vowel sequence, and <..> indicates arbitrary
* presence,
*
* <c><v> gives 0
* <c>vc<v> gives 1
* <c>vcvc<v> gives 2
* <c>vcvcvc<v> gives 3
*
* @param string $str The string to return the m count for
*
* @return int The m count
*/
private static function m($str)
{
$c = self::$regex_consonant;
$v = self::$regex_vowel;
$str = preg_replace("#^$c+#", '', $str);
$str = preg_replace("#$v+$#", '', $str);
preg_match_all("#($v+$c+)#", $str, $matches);
return count($matches[1]);
}
/**
* Returns true/false as to whether the given string contains two
* of the same consonant next to each other at the end of the string.
*
* @param string $str String to check
*
* @return bool Result
*/
private static function doubleConsonant($str)
{
$c = self::$regex_consonant;
return preg_match("#$c{2}$#", $str, $matches) AND $matches[0][0] == $matches[0][1];
}
/**
* Checks for ending CVC sequence where second C is not W, X or Y
*
* @param string $str String to check
*
* @return bool Result
*/
private static function cvc($str)
{
$c = self::$regex_consonant;
$v = self::$regex_vowel;
return preg_match("#($c$v$c)$#", $str, $matches)
AND strlen($matches[1]) == 3
AND $matches[1][2] != 'w'
AND $matches[1][2] != 'x'
AND $matches[1][2] != 'y';
}
}

View file

@ -9,7 +9,7 @@ return array(
'names' => array( 'names' => array(
'conpherence.pkg.css' => '3c8a0668', 'conpherence.pkg.css' => '3c8a0668',
'conpherence.pkg.js' => '020aebcf', 'conpherence.pkg.js' => '020aebcf',
'core.pkg.css' => 'b88ac037', 'core.pkg.css' => 'ad8fc332',
'core.pkg.js' => '705aec2c', 'core.pkg.js' => '705aec2c',
'differential.pkg.css' => '607c84be', 'differential.pkg.css' => '607c84be',
'differential.pkg.js' => '1b97518d', 'differential.pkg.js' => '1b97518d',
@ -112,7 +112,7 @@ return array(
'rsrc/css/application/tokens/tokens.css' => 'ce5a50bd', 'rsrc/css/application/tokens/tokens.css' => 'ce5a50bd',
'rsrc/css/application/uiexample/example.css' => 'b4795059', 'rsrc/css/application/uiexample/example.css' => 'b4795059',
'rsrc/css/core/core.css' => '1b29ed61', 'rsrc/css/core/core.css' => '1b29ed61',
'rsrc/css/core/remarkup.css' => 'f06cc20e', 'rsrc/css/core/remarkup.css' => 'c286eaef',
'rsrc/css/core/syntax.css' => '220b85f9', 'rsrc/css/core/syntax.css' => '220b85f9',
'rsrc/css/core/z-index.css' => '99c0f5eb', 'rsrc/css/core/z-index.css' => '99c0f5eb',
'rsrc/css/diviner/diviner-shared.css' => '4bd263b0', 'rsrc/css/diviner/diviner-shared.css' => '4bd263b0',
@ -153,9 +153,9 @@ return array(
'rsrc/css/phui/phui-feed-story.css' => 'a0c05029', 'rsrc/css/phui/phui-feed-story.css' => 'a0c05029',
'rsrc/css/phui/phui-fontkit.css' => '1ec937e5', 'rsrc/css/phui/phui-fontkit.css' => '1ec937e5',
'rsrc/css/phui/phui-form-view.css' => '01b796c0', 'rsrc/css/phui/phui-form-view.css' => '01b796c0',
'rsrc/css/phui/phui-form.css' => '159e2d9c', 'rsrc/css/phui/phui-form.css' => '1f177cb7',
'rsrc/css/phui/phui-head-thing.css' => 'd7f293df', 'rsrc/css/phui/phui-head-thing.css' => 'd7f293df',
'rsrc/css/phui/phui-header-view.css' => 'be09cc83', 'rsrc/css/phui/phui-header-view.css' => '36c86a58',
'rsrc/css/phui/phui-hovercard.css' => '6ca90fa0', 'rsrc/css/phui/phui-hovercard.css' => '6ca90fa0',
'rsrc/css/phui/phui-icon-set-selector.css' => '7aa5f3ec', 'rsrc/css/phui/phui-icon-set-selector.css' => '7aa5f3ec',
'rsrc/css/phui/phui-icon.css' => '4cbc684a', 'rsrc/css/phui/phui-icon.css' => '4cbc684a',
@ -176,7 +176,7 @@ return array(
'rsrc/css/phui/phui-status.css' => 'e5ff8be0', 'rsrc/css/phui/phui-status.css' => 'e5ff8be0',
'rsrc/css/phui/phui-tag-view.css' => '8519160a', 'rsrc/css/phui/phui-tag-view.css' => '8519160a',
'rsrc/css/phui/phui-timeline-view.css' => '1e348e4b', 'rsrc/css/phui/phui-timeline-view.css' => '1e348e4b',
'rsrc/css/phui/phui-two-column-view.css' => '01e6991e', 'rsrc/css/phui/phui-two-column-view.css' => '0a876b9e',
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'e86de308', 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'e86de308',
'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98', 'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98',
'rsrc/css/phui/workboards/phui-workcard.css' => '913441b6', 'rsrc/css/phui/workboards/phui-workcard.css' => '913441b6',
@ -794,7 +794,7 @@ return array(
'phabricator-object-selector-css' => 'ee77366f', 'phabricator-object-selector-css' => 'ee77366f',
'phabricator-phtize' => '2f1db1ed', 'phabricator-phtize' => '2f1db1ed',
'phabricator-prefab' => '5793d835', 'phabricator-prefab' => '5793d835',
'phabricator-remarkup-css' => 'f06cc20e', 'phabricator-remarkup-css' => 'c286eaef',
'phabricator-search-results-css' => '9ea70ace', 'phabricator-search-results-css' => '9ea70ace',
'phabricator-shaped-request' => 'abf88db8', 'phabricator-shaped-request' => 'abf88db8',
'phabricator-slowvote-css' => '1694baed', 'phabricator-slowvote-css' => '1694baed',
@ -840,10 +840,10 @@ return array(
'phui-feed-story-css' => 'a0c05029', 'phui-feed-story-css' => 'a0c05029',
'phui-font-icon-base-css' => 'd7994e06', 'phui-font-icon-base-css' => 'd7994e06',
'phui-fontkit-css' => '1ec937e5', 'phui-fontkit-css' => '1ec937e5',
'phui-form-css' => '159e2d9c', 'phui-form-css' => '1f177cb7',
'phui-form-view-css' => '01b796c0', 'phui-form-view-css' => '01b796c0',
'phui-head-thing-view-css' => 'd7f293df', 'phui-head-thing-view-css' => 'd7f293df',
'phui-header-view-css' => 'be09cc83', 'phui-header-view-css' => '36c86a58',
'phui-hovercard' => '074f0783', 'phui-hovercard' => '074f0783',
'phui-hovercard-view-css' => '6ca90fa0', 'phui-hovercard-view-css' => '6ca90fa0',
'phui-icon-set-selector-css' => '7aa5f3ec', 'phui-icon-set-selector-css' => '7aa5f3ec',
@ -873,7 +873,7 @@ return array(
'phui-tag-view-css' => '8519160a', 'phui-tag-view-css' => '8519160a',
'phui-theme-css' => '35883b37', 'phui-theme-css' => '35883b37',
'phui-timeline-view-css' => '1e348e4b', 'phui-timeline-view-css' => '1e348e4b',
'phui-two-column-view-css' => '01e6991e', 'phui-two-column-view-css' => '0a876b9e',
'phui-workboard-color-css' => 'e86de308', 'phui-workboard-color-css' => 'e86de308',
'phui-workboard-view-css' => '74fc9d98', 'phui-workboard-view-css' => '74fc9d98',
'phui-workcard-view-css' => '913441b6', 'phui-workcard-view-css' => '913441b6',

View file

@ -603,6 +603,7 @@ phutil_register_library_map(array(
'DifferentialRevisionActionTransaction' => 'applications/differential/xaction/DifferentialRevisionActionTransaction.php', 'DifferentialRevisionActionTransaction' => 'applications/differential/xaction/DifferentialRevisionActionTransaction.php',
'DifferentialRevisionAffectedFilesHeraldField' => 'applications/differential/herald/DifferentialRevisionAffectedFilesHeraldField.php', 'DifferentialRevisionAffectedFilesHeraldField' => 'applications/differential/herald/DifferentialRevisionAffectedFilesHeraldField.php',
'DifferentialRevisionAuthorHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorHeraldField.php', 'DifferentialRevisionAuthorHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorHeraldField.php',
'DifferentialRevisionAuthorPackagesHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorPackagesHeraldField.php',
'DifferentialRevisionAuthorProjectsHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorProjectsHeraldField.php', 'DifferentialRevisionAuthorProjectsHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorProjectsHeraldField.php',
'DifferentialRevisionBuildableTransaction' => 'applications/differential/xaction/DifferentialRevisionBuildableTransaction.php', 'DifferentialRevisionBuildableTransaction' => 'applications/differential/xaction/DifferentialRevisionBuildableTransaction.php',
'DifferentialRevisionCloseDetailsController' => 'applications/differential/controller/DifferentialRevisionCloseDetailsController.php', 'DifferentialRevisionCloseDetailsController' => 'applications/differential/controller/DifferentialRevisionCloseDetailsController.php',
@ -736,12 +737,14 @@ phutil_register_library_map(array(
'DiffusionCommitAuditorsHeraldField' => 'applications/diffusion/herald/DiffusionCommitAuditorsHeraldField.php', 'DiffusionCommitAuditorsHeraldField' => 'applications/diffusion/herald/DiffusionCommitAuditorsHeraldField.php',
'DiffusionCommitAuditorsTransaction' => 'applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php', 'DiffusionCommitAuditorsTransaction' => 'applications/diffusion/xaction/DiffusionCommitAuditorsTransaction.php',
'DiffusionCommitAuthorHeraldField' => 'applications/diffusion/herald/DiffusionCommitAuthorHeraldField.php', 'DiffusionCommitAuthorHeraldField' => 'applications/diffusion/herald/DiffusionCommitAuthorHeraldField.php',
'DiffusionCommitAuthorPackagesHeraldField' => 'applications/diffusion/herald/DiffusionCommitAuthorPackagesHeraldField.php',
'DiffusionCommitAuthorProjectsHeraldField' => 'applications/diffusion/herald/DiffusionCommitAuthorProjectsHeraldField.php', 'DiffusionCommitAuthorProjectsHeraldField' => 'applications/diffusion/herald/DiffusionCommitAuthorProjectsHeraldField.php',
'DiffusionCommitAutocloseHeraldField' => 'applications/diffusion/herald/DiffusionCommitAutocloseHeraldField.php', 'DiffusionCommitAutocloseHeraldField' => 'applications/diffusion/herald/DiffusionCommitAutocloseHeraldField.php',
'DiffusionCommitBranchesController' => 'applications/diffusion/controller/DiffusionCommitBranchesController.php', 'DiffusionCommitBranchesController' => 'applications/diffusion/controller/DiffusionCommitBranchesController.php',
'DiffusionCommitBranchesHeraldField' => 'applications/diffusion/herald/DiffusionCommitBranchesHeraldField.php', 'DiffusionCommitBranchesHeraldField' => 'applications/diffusion/herald/DiffusionCommitBranchesHeraldField.php',
'DiffusionCommitBuildableTransaction' => 'applications/diffusion/xaction/DiffusionCommitBuildableTransaction.php', 'DiffusionCommitBuildableTransaction' => 'applications/diffusion/xaction/DiffusionCommitBuildableTransaction.php',
'DiffusionCommitCommitterHeraldField' => 'applications/diffusion/herald/DiffusionCommitCommitterHeraldField.php', 'DiffusionCommitCommitterHeraldField' => 'applications/diffusion/herald/DiffusionCommitCommitterHeraldField.php',
'DiffusionCommitCommitterPackagesHeraldField' => 'applications/diffusion/herald/DiffusionCommitCommitterPackagesHeraldField.php',
'DiffusionCommitCommitterProjectsHeraldField' => 'applications/diffusion/herald/DiffusionCommitCommitterProjectsHeraldField.php', 'DiffusionCommitCommitterProjectsHeraldField' => 'applications/diffusion/herald/DiffusionCommitCommitterProjectsHeraldField.php',
'DiffusionCommitConcernTransaction' => 'applications/diffusion/xaction/DiffusionCommitConcernTransaction.php', 'DiffusionCommitConcernTransaction' => 'applications/diffusion/xaction/DiffusionCommitConcernTransaction.php',
'DiffusionCommitController' => 'applications/diffusion/controller/DiffusionCommitController.php', 'DiffusionCommitController' => 'applications/diffusion/controller/DiffusionCommitController.php',
@ -907,10 +910,12 @@ phutil_register_library_map(array(
'DiffusionPhpExternalSymbolsSource' => 'applications/diffusion/symbol/DiffusionPhpExternalSymbolsSource.php', 'DiffusionPhpExternalSymbolsSource' => 'applications/diffusion/symbol/DiffusionPhpExternalSymbolsSource.php',
'DiffusionPreCommitContentAffectedFilesHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentAffectedFilesHeraldField.php', 'DiffusionPreCommitContentAffectedFilesHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentAffectedFilesHeraldField.php',
'DiffusionPreCommitContentAuthorHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentAuthorHeraldField.php', 'DiffusionPreCommitContentAuthorHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentAuthorHeraldField.php',
'DiffusionPreCommitContentAuthorPackagesHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentAuthorPackagesHeraldField.php',
'DiffusionPreCommitContentAuthorProjectsHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentAuthorProjectsHeraldField.php', 'DiffusionPreCommitContentAuthorProjectsHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentAuthorProjectsHeraldField.php',
'DiffusionPreCommitContentAuthorRawHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentAuthorRawHeraldField.php', 'DiffusionPreCommitContentAuthorRawHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentAuthorRawHeraldField.php',
'DiffusionPreCommitContentBranchesHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentBranchesHeraldField.php', 'DiffusionPreCommitContentBranchesHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentBranchesHeraldField.php',
'DiffusionPreCommitContentCommitterHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentCommitterHeraldField.php', 'DiffusionPreCommitContentCommitterHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentCommitterHeraldField.php',
'DiffusionPreCommitContentCommitterPackagesHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentCommitterPackagesHeraldField.php',
'DiffusionPreCommitContentCommitterProjectsHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentCommitterProjectsHeraldField.php', 'DiffusionPreCommitContentCommitterProjectsHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentCommitterProjectsHeraldField.php',
'DiffusionPreCommitContentCommitterRawHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentCommitterRawHeraldField.php', 'DiffusionPreCommitContentCommitterRawHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentCommitterRawHeraldField.php',
'DiffusionPreCommitContentDiffContentAddedHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentDiffContentAddedHeraldField.php', 'DiffusionPreCommitContentDiffContentAddedHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitContentDiffContentAddedHeraldField.php',
@ -1527,6 +1532,7 @@ phutil_register_library_map(array(
'HeraldApplicationActionGroup' => 'applications/herald/action/HeraldApplicationActionGroup.php', 'HeraldApplicationActionGroup' => 'applications/herald/action/HeraldApplicationActionGroup.php',
'HeraldApplyTranscript' => 'applications/herald/storage/transcript/HeraldApplyTranscript.php', 'HeraldApplyTranscript' => 'applications/herald/storage/transcript/HeraldApplyTranscript.php',
'HeraldBasicFieldGroup' => 'applications/herald/field/HeraldBasicFieldGroup.php', 'HeraldBasicFieldGroup' => 'applications/herald/field/HeraldBasicFieldGroup.php',
'HeraldBoolFieldValue' => 'applications/herald/value/HeraldBoolFieldValue.php',
'HeraldBuildableState' => 'applications/herald/state/HeraldBuildableState.php', 'HeraldBuildableState' => 'applications/herald/state/HeraldBuildableState.php',
'HeraldCallWebhookAction' => 'applications/herald/action/HeraldCallWebhookAction.php', 'HeraldCallWebhookAction' => 'applications/herald/action/HeraldCallWebhookAction.php',
'HeraldCommentAction' => 'applications/herald/action/HeraldCommentAction.php', 'HeraldCommentAction' => 'applications/herald/action/HeraldCommentAction.php',
@ -4382,6 +4388,9 @@ phutil_register_library_map(array(
'PhabricatorProjectSubprojectsProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectSubprojectsProfileMenuItem.php', 'PhabricatorProjectSubprojectsProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectSubprojectsProfileMenuItem.php',
'PhabricatorProjectSubtypeDatasource' => 'applications/project/typeahead/PhabricatorProjectSubtypeDatasource.php', 'PhabricatorProjectSubtypeDatasource' => 'applications/project/typeahead/PhabricatorProjectSubtypeDatasource.php',
'PhabricatorProjectSubtypesConfigType' => 'applications/project/config/PhabricatorProjectSubtypesConfigType.php', 'PhabricatorProjectSubtypesConfigType' => 'applications/project/config/PhabricatorProjectSubtypesConfigType.php',
'PhabricatorProjectTagsAddedField' => 'applications/project/herald/PhabricatorProjectTagsAddedField.php',
'PhabricatorProjectTagsField' => 'applications/project/herald/PhabricatorProjectTagsField.php',
'PhabricatorProjectTagsRemovedField' => 'applications/project/herald/PhabricatorProjectTagsRemovedField.php',
'PhabricatorProjectTestDataGenerator' => 'applications/project/lipsum/PhabricatorProjectTestDataGenerator.php', 'PhabricatorProjectTestDataGenerator' => 'applications/project/lipsum/PhabricatorProjectTestDataGenerator.php',
'PhabricatorProjectTransaction' => 'applications/project/storage/PhabricatorProjectTransaction.php', 'PhabricatorProjectTransaction' => 'applications/project/storage/PhabricatorProjectTransaction.php',
'PhabricatorProjectTransactionEditor' => 'applications/project/editor/PhabricatorProjectTransactionEditor.php', 'PhabricatorProjectTransactionEditor' => 'applications/project/editor/PhabricatorProjectTransactionEditor.php',
@ -5658,6 +5667,12 @@ phutil_register_library_map(array(
'PhutilRemarkupTableBlockRule' => 'infrastructure/markup/blockrule/PhutilRemarkupTableBlockRule.php', 'PhutilRemarkupTableBlockRule' => 'infrastructure/markup/blockrule/PhutilRemarkupTableBlockRule.php',
'PhutilRemarkupTestInterpreterRule' => 'infrastructure/markup/blockrule/PhutilRemarkupTestInterpreterRule.php', 'PhutilRemarkupTestInterpreterRule' => 'infrastructure/markup/blockrule/PhutilRemarkupTestInterpreterRule.php',
'PhutilRemarkupUnderlineRule' => 'infrastructure/markup/markuprule/PhutilRemarkupUnderlineRule.php', 'PhutilRemarkupUnderlineRule' => 'infrastructure/markup/markuprule/PhutilRemarkupUnderlineRule.php',
'PhutilSearchQueryCompiler' => 'applications/search/compiler/PhutilSearchQueryCompiler.php',
'PhutilSearchQueryCompilerSyntaxException' => 'applications/search/compiler/PhutilSearchQueryCompilerSyntaxException.php',
'PhutilSearchQueryCompilerTestCase' => 'applications/search/compiler/__tests__/PhutilSearchQueryCompilerTestCase.php',
'PhutilSearchQueryToken' => 'applications/search/compiler/PhutilSearchQueryToken.php',
'PhutilSearchStemmer' => 'applications/search/compiler/PhutilSearchStemmer.php',
'PhutilSearchStemmerTestCase' => 'applications/search/compiler/__tests__/PhutilSearchStemmerTestCase.php',
'PhutilSlackAuthAdapter' => 'applications/auth/adapter/PhutilSlackAuthAdapter.php', 'PhutilSlackAuthAdapter' => 'applications/auth/adapter/PhutilSlackAuthAdapter.php',
'PhutilTwitchAuthAdapter' => 'applications/auth/adapter/PhutilTwitchAuthAdapter.php', 'PhutilTwitchAuthAdapter' => 'applications/auth/adapter/PhutilTwitchAuthAdapter.php',
'PhutilTwitterAuthAdapter' => 'applications/auth/adapter/PhutilTwitterAuthAdapter.php', 'PhutilTwitterAuthAdapter' => 'applications/auth/adapter/PhutilTwitterAuthAdapter.php',
@ -6586,6 +6601,7 @@ phutil_register_library_map(array(
'DifferentialRevisionActionTransaction' => 'DifferentialRevisionTransactionType', 'DifferentialRevisionActionTransaction' => 'DifferentialRevisionTransactionType',
'DifferentialRevisionAffectedFilesHeraldField' => 'DifferentialRevisionHeraldField', 'DifferentialRevisionAffectedFilesHeraldField' => 'DifferentialRevisionHeraldField',
'DifferentialRevisionAuthorHeraldField' => 'DifferentialRevisionHeraldField', 'DifferentialRevisionAuthorHeraldField' => 'DifferentialRevisionHeraldField',
'DifferentialRevisionAuthorPackagesHeraldField' => 'DifferentialRevisionHeraldField',
'DifferentialRevisionAuthorProjectsHeraldField' => 'DifferentialRevisionHeraldField', 'DifferentialRevisionAuthorProjectsHeraldField' => 'DifferentialRevisionHeraldField',
'DifferentialRevisionBuildableTransaction' => 'DifferentialRevisionTransactionType', 'DifferentialRevisionBuildableTransaction' => 'DifferentialRevisionTransactionType',
'DifferentialRevisionCloseDetailsController' => 'DifferentialController', 'DifferentialRevisionCloseDetailsController' => 'DifferentialController',
@ -6719,12 +6735,14 @@ phutil_register_library_map(array(
'DiffusionCommitAuditorsHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitAuditorsHeraldField' => 'DiffusionCommitHeraldField',
'DiffusionCommitAuditorsTransaction' => 'DiffusionCommitTransactionType', 'DiffusionCommitAuditorsTransaction' => 'DiffusionCommitTransactionType',
'DiffusionCommitAuthorHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitAuthorHeraldField' => 'DiffusionCommitHeraldField',
'DiffusionCommitAuthorPackagesHeraldField' => 'DiffusionCommitHeraldField',
'DiffusionCommitAuthorProjectsHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitAuthorProjectsHeraldField' => 'DiffusionCommitHeraldField',
'DiffusionCommitAutocloseHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitAutocloseHeraldField' => 'DiffusionCommitHeraldField',
'DiffusionCommitBranchesController' => 'DiffusionController', 'DiffusionCommitBranchesController' => 'DiffusionController',
'DiffusionCommitBranchesHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitBranchesHeraldField' => 'DiffusionCommitHeraldField',
'DiffusionCommitBuildableTransaction' => 'DiffusionCommitTransactionType', 'DiffusionCommitBuildableTransaction' => 'DiffusionCommitTransactionType',
'DiffusionCommitCommitterHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitCommitterHeraldField' => 'DiffusionCommitHeraldField',
'DiffusionCommitCommitterPackagesHeraldField' => 'DiffusionCommitHeraldField',
'DiffusionCommitCommitterProjectsHeraldField' => 'DiffusionCommitHeraldField', 'DiffusionCommitCommitterProjectsHeraldField' => 'DiffusionCommitHeraldField',
'DiffusionCommitConcernTransaction' => 'DiffusionCommitAuditTransaction', 'DiffusionCommitConcernTransaction' => 'DiffusionCommitAuditTransaction',
'DiffusionCommitController' => 'DiffusionController', 'DiffusionCommitController' => 'DiffusionController',
@ -6893,10 +6911,12 @@ phutil_register_library_map(array(
'DiffusionPhpExternalSymbolsSource' => 'DiffusionExternalSymbolsSource', 'DiffusionPhpExternalSymbolsSource' => 'DiffusionExternalSymbolsSource',
'DiffusionPreCommitContentAffectedFilesHeraldField' => 'DiffusionPreCommitContentHeraldField', 'DiffusionPreCommitContentAffectedFilesHeraldField' => 'DiffusionPreCommitContentHeraldField',
'DiffusionPreCommitContentAuthorHeraldField' => 'DiffusionPreCommitContentHeraldField', 'DiffusionPreCommitContentAuthorHeraldField' => 'DiffusionPreCommitContentHeraldField',
'DiffusionPreCommitContentAuthorPackagesHeraldField' => 'DiffusionPreCommitContentHeraldField',
'DiffusionPreCommitContentAuthorProjectsHeraldField' => 'DiffusionPreCommitContentHeraldField', 'DiffusionPreCommitContentAuthorProjectsHeraldField' => 'DiffusionPreCommitContentHeraldField',
'DiffusionPreCommitContentAuthorRawHeraldField' => 'DiffusionPreCommitContentHeraldField', 'DiffusionPreCommitContentAuthorRawHeraldField' => 'DiffusionPreCommitContentHeraldField',
'DiffusionPreCommitContentBranchesHeraldField' => 'DiffusionPreCommitContentHeraldField', 'DiffusionPreCommitContentBranchesHeraldField' => 'DiffusionPreCommitContentHeraldField',
'DiffusionPreCommitContentCommitterHeraldField' => 'DiffusionPreCommitContentHeraldField', 'DiffusionPreCommitContentCommitterHeraldField' => 'DiffusionPreCommitContentHeraldField',
'DiffusionPreCommitContentCommitterPackagesHeraldField' => 'DiffusionPreCommitContentHeraldField',
'DiffusionPreCommitContentCommitterProjectsHeraldField' => 'DiffusionPreCommitContentHeraldField', 'DiffusionPreCommitContentCommitterProjectsHeraldField' => 'DiffusionPreCommitContentHeraldField',
'DiffusionPreCommitContentCommitterRawHeraldField' => 'DiffusionPreCommitContentHeraldField', 'DiffusionPreCommitContentCommitterRawHeraldField' => 'DiffusionPreCommitContentHeraldField',
'DiffusionPreCommitContentDiffContentAddedHeraldField' => 'DiffusionPreCommitContentHeraldField', 'DiffusionPreCommitContentDiffContentAddedHeraldField' => 'DiffusionPreCommitContentHeraldField',
@ -7622,6 +7642,7 @@ phutil_register_library_map(array(
'HeraldApplicationActionGroup' => 'HeraldActionGroup', 'HeraldApplicationActionGroup' => 'HeraldActionGroup',
'HeraldApplyTranscript' => 'Phobject', 'HeraldApplyTranscript' => 'Phobject',
'HeraldBasicFieldGroup' => 'HeraldFieldGroup', 'HeraldBasicFieldGroup' => 'HeraldFieldGroup',
'HeraldBoolFieldValue' => 'HeraldFieldValue',
'HeraldBuildableState' => 'HeraldState', 'HeraldBuildableState' => 'HeraldState',
'HeraldCallWebhookAction' => 'HeraldAction', 'HeraldCallWebhookAction' => 'HeraldAction',
'HeraldCommentAction' => 'HeraldAction', 'HeraldCommentAction' => 'HeraldAction',
@ -7673,7 +7694,7 @@ phutil_register_library_map(array(
'HeraldPreCommitContentAdapter' => 'HeraldPreCommitAdapter', 'HeraldPreCommitContentAdapter' => 'HeraldPreCommitAdapter',
'HeraldPreCommitRefAdapter' => 'HeraldPreCommitAdapter', 'HeraldPreCommitRefAdapter' => 'HeraldPreCommitAdapter',
'HeraldPreventActionGroup' => 'HeraldActionGroup', 'HeraldPreventActionGroup' => 'HeraldActionGroup',
'HeraldProjectsField' => 'HeraldField', 'HeraldProjectsField' => 'PhabricatorProjectTagsField',
'HeraldRecursiveConditionsException' => 'Exception', 'HeraldRecursiveConditionsException' => 'Exception',
'HeraldRelatedFieldGroup' => 'HeraldFieldGroup', 'HeraldRelatedFieldGroup' => 'HeraldFieldGroup',
'HeraldRemarkupFieldValue' => 'HeraldFieldValue', 'HeraldRemarkupFieldValue' => 'HeraldFieldValue',
@ -10937,6 +10958,9 @@ phutil_register_library_map(array(
'PhabricatorProjectSubprojectsProfileMenuItem' => 'PhabricatorProfileMenuItem', 'PhabricatorProjectSubprojectsProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorProjectSubtypeDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorProjectSubtypeDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorProjectSubtypesConfigType' => 'PhabricatorJSONConfigType', 'PhabricatorProjectSubtypesConfigType' => 'PhabricatorJSONConfigType',
'PhabricatorProjectTagsAddedField' => 'PhabricatorProjectTagsField',
'PhabricatorProjectTagsField' => 'HeraldField',
'PhabricatorProjectTagsRemovedField' => 'PhabricatorProjectTagsField',
'PhabricatorProjectTestDataGenerator' => 'PhabricatorTestDataGenerator', 'PhabricatorProjectTestDataGenerator' => 'PhabricatorTestDataGenerator',
'PhabricatorProjectTransaction' => 'PhabricatorModularTransaction', 'PhabricatorProjectTransaction' => 'PhabricatorModularTransaction',
'PhabricatorProjectTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorProjectTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
@ -12483,6 +12507,12 @@ phutil_register_library_map(array(
'PhutilRemarkupTableBlockRule' => 'PhutilRemarkupBlockRule', 'PhutilRemarkupTableBlockRule' => 'PhutilRemarkupBlockRule',
'PhutilRemarkupTestInterpreterRule' => 'PhutilRemarkupBlockInterpreter', 'PhutilRemarkupTestInterpreterRule' => 'PhutilRemarkupBlockInterpreter',
'PhutilRemarkupUnderlineRule' => 'PhutilRemarkupRule', 'PhutilRemarkupUnderlineRule' => 'PhutilRemarkupRule',
'PhutilSearchQueryCompiler' => 'Phobject',
'PhutilSearchQueryCompilerSyntaxException' => 'Exception',
'PhutilSearchQueryCompilerTestCase' => 'PhutilTestCase',
'PhutilSearchQueryToken' => 'Phobject',
'PhutilSearchStemmer' => 'Phobject',
'PhutilSearchStemmerTestCase' => 'PhutilTestCase',
'PhutilSlackAuthAdapter' => 'PhutilOAuthAuthAdapter', 'PhutilSlackAuthAdapter' => 'PhutilOAuthAuthAdapter',
'PhutilTwitchAuthAdapter' => 'PhutilOAuthAuthAdapter', 'PhutilTwitchAuthAdapter' => 'PhutilOAuthAuthAdapter',
'PhutilTwitterAuthAdapter' => 'PhutilOAuth1AuthAdapter', 'PhutilTwitterAuthAdapter' => 'PhutilOAuth1AuthAdapter',

View file

@ -1,3 +1,17 @@
<?php <?php
final class PhabricatorAuthHighSecurityToken extends Phobject {} final class PhabricatorAuthHighSecurityToken
extends Phobject {
private $isUnchallengedToken = false;
public function setIsUnchallengedToken($is_unchallenged_token) {
$this->isUnchallengedToken = $is_unchallenged_token;
return $this;
}
public function getIsUnchallengedToken() {
return $this->isUnchallengedToken;
}
}

View file

@ -493,7 +493,8 @@ final class PhabricatorAuthSessionEngine extends Phobject {
// adds an auth factor, existing sessions won't get a free pass into hisec, // adds an auth factor, existing sessions won't get a free pass into hisec,
// since they never actually got marked as hisec. // since they never actually got marked as hisec.
if (!$factors) { if (!$factors) {
return $this->issueHighSecurityToken($session, true); return $this->issueHighSecurityToken($session, true)
->setIsUnchallengedToken(true);
} }
$this->request = $request; $this->request = $request;

View file

@ -0,0 +1,32 @@
<?php
final class DifferentialRevisionAuthorPackagesHeraldField
extends DifferentialRevisionHeraldField {
const FIELDCONST = 'differential.revision.author.packages';
public function getHeraldFieldName() {
return pht("Author's packages");
}
public function getHeraldFieldValue($object) {
$adapter = $this->getAdapter();
$viewer = $adapter->getViewer();
$packages = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withAuthorityPHIDs(array($object->getAuthorPHID()))
->execute();
return mpull($packages, 'getPHID');
}
protected function getHeraldFieldStandardType() {
return self::STANDARD_PHID_LIST;
}
protected function getDatasource() {
return new PhabricatorOwnersPackageDatasource();
}
}

View file

@ -354,6 +354,69 @@ final class DifferentialHunkParser extends Phobject {
return $this; return $this;
} }
public function generateVisibleBlocksMask($lines_context) {
// See T13468. This is similar to "generateVisibleLinesMask()", but
// attempts to work around a series of bugs which cancel each other
// out but make a mess of the intermediate steps.
$old = $this->getOldLines();
$new = $this->getNewLines();
$length = max(count($old), count($new));
$visible_lines = array();
for ($ii = 0; $ii < $length; $ii++) {
$old_visible = (isset($old[$ii]) && $old[$ii]['type']);
$new_visible = (isset($new[$ii]) && $new[$ii]['type']);
$visible_lines[$ii] = ($old_visible || $new_visible);
}
$mask = array();
$reveal_cursor = -1;
for ($ii = 0; $ii < $length; $ii++) {
// If this line isn't visible, it isn't going to reveal anything.
if (!$visible_lines[$ii]) {
// If it hasn't been revealed by a nearby line, mark it as masked.
if (empty($mask[$ii])) {
$mask[$ii] = false;
}
continue;
}
// If this line is visible, reveal all the lines nearby.
// First, compute the minimum and maximum offsets we want to reveal.
$min_reveal = max($ii - $lines_context, 0);
$max_reveal = min($ii + $lines_context, $length - 1);
// Naively, we'd do more work than necessary when revealing context for
// several adjacent visible lines: we would mark all the overlapping
// lines as revealed several times.
// To avoid duplicating work, keep track of the largest line we've
// revealed to. Since we reveal context by marking every consecutive
// line, we don't need to touch any line above it.
$min_reveal = max($min_reveal, $reveal_cursor);
// Reveal the remaining unrevealed lines.
for ($jj = $min_reveal; $jj <= $max_reveal; $jj++) {
$mask[$jj] = true;
}
// Move the cursor to the next line which may still need to be revealed.
$reveal_cursor = $max_reveal + 1;
}
$this->setVisibleLinesMask($mask);
return $mask;
}
public function generateVisibleLinesMask($lines_context) { public function generateVisibleLinesMask($lines_context) {
$old = $this->getOldLines(); $old = $this->getOldLines();
$new = $this->getNewLines(); $new = $this->getNewLines();
@ -361,6 +424,7 @@ final class DifferentialHunkParser extends Phobject {
$visible = false; $visible = false;
$last = 0; $last = 0;
$mask = array(); $mask = array();
for ($cursor = -$lines_context; $cursor < $max_length; $cursor++) { for ($cursor = -$lines_context; $cursor < $max_length; $cursor++) {
$offset = $cursor + $lines_context; $offset = $cursor + $lines_context;
if ((isset($old[$offset]) && $old[$offset]['type']) || if ((isset($old[$offset]) && $old[$offset]['type']) ||

View file

@ -10,7 +10,7 @@ final class DiffusionCommitAuthorHeraldField
} }
public function getHeraldFieldValue($object) { public function getHeraldFieldValue($object) {
return $object->getCommitData()->getCommitDetail('authorPHID'); return $this->getAdapter()->getAuthorPHID();
} }
protected function getHeraldFieldStandardType() { protected function getHeraldFieldStandardType() {

View file

@ -0,0 +1,37 @@
<?php
final class DiffusionCommitAuthorPackagesHeraldField
extends DiffusionCommitHeraldField {
const FIELDCONST = 'diffusion.commit.author.packages';
public function getHeraldFieldName() {
return pht("Author's packages");
}
public function getHeraldFieldValue($object) {
$adapter = $this->getAdapter();
$viewer = $adapter->getViewer();
$author_phid = $adapter->getAuthorPHID();
if (!$author_phid) {
return array();
}
$packages = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withAuthorityPHIDs(array($author_phid))
->execute();
return mpull($packages, 'getPHID');
}
protected function getHeraldFieldStandardType() {
return self::STANDARD_PHID_LIST;
}
protected function getDatasource() {
return new PhabricatorOwnersPackageDatasource();
}
}

View file

@ -11,17 +11,16 @@ final class DiffusionCommitAuthorProjectsHeraldField
public function getHeraldFieldValue($object) { public function getHeraldFieldValue($object) {
$adapter = $this->getAdapter(); $adapter = $this->getAdapter();
$viewer = $adapter->getViewer();
$phid = $object->getCommitData()->getCommitDetail('authorPHID'); $author_phid = $adapter->getAuthorPHID();
if (!$phid) { if (!$author_phid) {
return array(); return array();
} }
$viewer = $adapter->getViewer();
$projects = id(new PhabricatorProjectQuery()) $projects = id(new PhabricatorProjectQuery())
->setViewer($viewer) ->setViewer($viewer)
->withMemberPHIDs(array($phid)) ->withMemberPHIDs(array($author_phid))
->execute(); ->execute();
return mpull($projects, 'getPHID'); return mpull($projects, 'getPHID');

View file

@ -10,7 +10,7 @@ final class DiffusionCommitCommitterHeraldField
} }
public function getHeraldFieldValue($object) { public function getHeraldFieldValue($object) {
return $object->getCommitData()->getCommitDetail('committerPHID'); return $this->getAdapter()->getCommitterPHID();
} }
protected function getHeraldFieldStandardType() { protected function getHeraldFieldStandardType() {

View file

@ -0,0 +1,37 @@
<?php
final class DiffusionCommitCommitterPackagesHeraldField
extends DiffusionCommitHeraldField {
const FIELDCONST = 'diffusion.commit.committer.packages';
public function getHeraldFieldName() {
return pht("Committer's packages");
}
public function getHeraldFieldValue($object) {
$adapter = $this->getAdapter();
$viewer = $adapter->getViewer();
$committer_phid = $adapter->getAuthorPHID();
if (!$committer_phid) {
return array();
}
$packages = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withAuthorityPHIDs(array($committer_phid))
->execute();
return mpull($packages, 'getPHID');
}
protected function getHeraldFieldStandardType() {
return self::STANDARD_PHID_LIST;
}
protected function getDatasource() {
return new PhabricatorOwnersPackageDatasource();
}
}

View file

@ -11,17 +11,16 @@ final class DiffusionCommitCommitterProjectsHeraldField
public function getHeraldFieldValue($object) { public function getHeraldFieldValue($object) {
$adapter = $this->getAdapter(); $adapter = $this->getAdapter();
$viewer = $adapter->getViewer();
$phid = $object->getCommitData()->getCommitDetail('committerPHID'); $committer_phid = $adapter->getCommitterPHID();
if (!$phid) { if (!$committer_phid) {
return array(); return array();
} }
$viewer = $adapter->getViewer();
$projects = id(new PhabricatorProjectQuery()) $projects = id(new PhabricatorProjectQuery())
->setViewer($viewer) ->setViewer($viewer)
->withMemberPHIDs(array($phid)) ->withMemberPHIDs(array($committer_phid))
->execute(); ->execute();
return mpull($projects, 'getPHID'); return mpull($projects, 'getPHID');

View file

@ -0,0 +1,37 @@
<?php
final class DiffusionPreCommitContentAuthorPackagesHeraldField
extends DiffusionPreCommitContentHeraldField {
const FIELDCONST = 'diffusion.pre.commit.author.packages';
public function getHeraldFieldName() {
return pht("Author's packages");
}
public function getHeraldFieldValue($object) {
$adapter = $this->getAdapter();
$viewer = $adapter->getViewer();
$author_phid = $adapter->getAuthorPHID();
if (!$author_phid) {
return array();
}
$packages = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withAuthorityPHIDs(array($author_phid))
->execute();
return mpull($packages, 'getPHID');
}
protected function getHeraldFieldStandardType() {
return self::STANDARD_PHID_LIST;
}
protected function getDatasource() {
return new PhabricatorOwnersPackageDatasource();
}
}

View file

@ -11,17 +11,16 @@ final class DiffusionPreCommitContentAuthorProjectsHeraldField
public function getHeraldFieldValue($object) { public function getHeraldFieldValue($object) {
$adapter = $this->getAdapter(); $adapter = $this->getAdapter();
$viewer = $adapter->getViewer();
$phid = $adapter->getAuthorPHID(); $author_phid = $adapter->getAuthorPHID();
if (!$phid) { if (!$author_phid) {
return array(); return array();
} }
$viewer = $adapter->getViewer();
$projects = id(new PhabricatorProjectQuery()) $projects = id(new PhabricatorProjectQuery())
->setViewer($viewer) ->setViewer($viewer)
->withMemberPHIDs(array($phid)) ->withMemberPHIDs(array($author_phid))
->execute(); ->execute();
return mpull($projects, 'getPHID'); return mpull($projects, 'getPHID');

View file

@ -0,0 +1,37 @@
<?php
final class DiffusionPreCommitContentCommitterPackagesHeraldField
extends DiffusionPreCommitContentHeraldField {
const FIELDCONST = 'diffusion.pre.commit.committer.packages';
public function getHeraldFieldName() {
return pht("Committer's packages");
}
public function getHeraldFieldValue($object) {
$adapter = $this->getAdapter();
$viewer = $adapter->getViewer();
$committer_phid = $adapter->getCommitterPHID();
if (!$committer_phid) {
return array();
}
$packages = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withAuthorityPHIDs(array($committer_phid))
->execute();
return mpull($packages, 'getPHID');
}
protected function getHeraldFieldStandardType() {
return self::STANDARD_PHID_LIST;
}
protected function getDatasource() {
return new PhabricatorOwnersPackageDatasource();
}
}

View file

@ -11,17 +11,16 @@ final class DiffusionPreCommitContentCommitterProjectsHeraldField
public function getHeraldFieldValue($object) { public function getHeraldFieldValue($object) {
$adapter = $this->getAdapter(); $adapter = $this->getAdapter();
$viewer = $adapter->getViewer();
$phid = $adapter->getCommitterPHID(); $committer_phid = $adapter->getCommitterPHID();
if (!$phid) { if (!$committer_phid) {
return array(); return array();
} }
$viewer = $adapter->getViewer();
$projects = id(new PhabricatorProjectQuery()) $projects = id(new PhabricatorProjectQuery())
->setViewer($viewer) ->setViewer($viewer)
->withMemberPHIDs(array($phid)) ->withMemberPHIDs(array($committer_phid))
->execute(); ->execute();
return mpull($projects, 'getPHID'); return mpull($projects, 'getPHID');

View file

@ -35,18 +35,6 @@ final class HeraldCommitAdapter
} }
public function newTestAdapter(PhabricatorUser $viewer, $object) { public function newTestAdapter(PhabricatorUser $viewer, $object) {
$object = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withPHIDs(array($object->getPHID()))
->needCommitData(true)
->executeOne();
if (!$object) {
throw new Exception(
pht(
'Failed to reload commit ("%s") to fetch commit data.',
$object->getPHID()));
}
return id(clone $this) return id(clone $this)
->setObject($object); ->setObject($object);
} }
@ -56,7 +44,23 @@ final class HeraldCommitAdapter
} }
public function setObject($object) { public function setObject($object) {
$this->commit = $object; $viewer = $this->getViewer();
$commit_phid = $object->getPHID();
$commit = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withPHIDs(array($commit_phid))
->needCommitData(true)
->needIdentities(true)
->executeOne();
if (!$commit) {
throw new Exception(
pht(
'Failed to reload commit ("%s") to fetch commit data.',
$commit_phid));
}
$this->commit = $commit;
return $this; return $this;
} }
@ -352,6 +356,22 @@ final class HeraldCommitAdapter
return $this->getObject()->getRepository(); return $this->getObject()->getRepository();
} }
public function getAuthorPHID() {
return $this->getObject()->getEffectiveAuthorPHID();
}
public function getCommitterPHID() {
$commit = $this->getObject();
if ($commit->hasCommitterIdentity()) {
$identity = $commit->getCommitterIdentity();
return $identity->getCurrentEffectiveUserPHID();
}
return null;
}
/* -( HarbormasterBuildableAdapterInterface )------------------------------ */ /* -( HarbormasterBuildableAdapterInterface )------------------------------ */

View file

@ -8,6 +8,7 @@ final class HeraldPreCommitContentAdapter extends HeraldPreCommitAdapter {
private $revision = false; private $revision = false;
private $affectedPackages; private $affectedPackages;
private $identityCache = array();
public function getAdapterContentName() { public function getAdapterContentName() {
return pht('Commit Hook: Commit Content'); return pht('Commit Hook: Commit Content');
@ -166,10 +167,39 @@ final class HeraldPreCommitContentAdapter extends HeraldPreCommitAdapter {
} }
} }
private function lookupUser($author) { private function lookupUser($raw_identity) {
return id(new DiffusionResolveUserQuery()) // See T13480. After the move to repository identities, we want to look
->withName($author) // users up in the identity table. If you push a commit which is authored
->execute(); // by "A Duck <duck@example.org>" and that identity is bound to user
// "@mallard" in the identity table, Herald should see the author of the
// commit as "@mallard" when evaluating pre-commit content rules.
if (!array_key_exists($raw_identity, $this->identityCache)) {
$repository = $this->getHookEngine()->getRepository();
$viewer = $this->getHookEngine()->getViewer();
$identity_engine = id(new DiffusionRepositoryIdentityEngine())
->setViewer($viewer);
// We must provide a "sourcePHID" when resolving identities, but don't
// have a legitimate one yet. Just use the repository PHID as a
// reasonable value. This won't actually be written to storage.
$source_phid = $repository->getPHID();
$identity_engine->setSourcePHID($source_phid);
// If the identity doesn't exist yet, we don't want to create it if
// we haven't seen it before. It will be created later when we actually
// import the commit.
$identity_engine->setDryRun(true);
$author_identity = $identity_engine->newResolvedIdentity($raw_identity);
$effective_phid = $author_identity->getCurrentEffectiveUserPHID();
$this->identityCache[$raw_identity] = $effective_phid;
}
return $this->identityCache[$raw_identity];
} }
private function getCommitFields() { private function getCommitFields() {

View file

@ -59,7 +59,7 @@ final class PhabricatorDocumentEngineBlocks
->parseHunksForLineData($changeset->getHunks()) ->parseHunksForLineData($changeset->getHunks())
->reparseHunksForSpecialAttributes(); ->reparseHunksForSpecialAttributes();
$hunk_parser->generateVisibleLinesMask(2); $hunk_parser->generateVisibleBlocksMask(2);
$mask = $hunk_parser->getVisibleLinesMask(); $mask = $hunk_parser->getVisibleLinesMask();
$old_lines = $hunk_parser->getOldLines(); $old_lines = $hunk_parser->getOldLines();
@ -72,14 +72,7 @@ final class PhabricatorDocumentEngineBlocks
$old_line = idx($old_lines, $ii); $old_line = idx($old_lines, $ii);
$new_line = idx($new_lines, $ii); $new_line = idx($new_lines, $ii);
$is_visible = !empty($mask[$ii + 1]); $is_visible = !empty($mask[$ii]);
// TODO: There's currently a bug where one-line files get incorrectly
// masked. This causes images to completely fail to render. Just ignore
// the mask if it came back empty.
if (!$mask) {
$is_visible = true;
}
if ($old_line) { if ($old_line) {
$old_hash = rtrim($old_line['text'], "\n"); $old_hash = rtrim($old_line['text'], "\n");

View file

@ -942,7 +942,6 @@ abstract class HeraldAdapter extends Phobject {
public function renderRuleAsText( public function renderRuleAsText(
HeraldRule $rule, HeraldRule $rule,
PhabricatorHandleList $handles,
PhabricatorUser $viewer) { PhabricatorUser $viewer) {
require_celerity_resource('herald-css'); require_celerity_resource('herald-css');
@ -973,7 +972,7 @@ abstract class HeraldAdapter extends Phobject {
), ),
array( array(
$icon, $icon,
$this->renderConditionAsText($condition, $handles, $viewer), $this->renderConditionAsText($condition, $viewer),
)); ));
} }
@ -1004,7 +1003,7 @@ abstract class HeraldAdapter extends Phobject {
), ),
array( array(
$icon, $icon,
$this->renderActionAsText($viewer, $action, $handles), $this->renderActionAsText($viewer, $action),
)); ));
} }
@ -1018,7 +1017,6 @@ abstract class HeraldAdapter extends Phobject {
private function renderConditionAsText( private function renderConditionAsText(
HeraldCondition $condition, HeraldCondition $condition,
PhabricatorHandleList $handles,
PhabricatorUser $viewer) { PhabricatorUser $viewer) {
$field_type = $condition->getFieldName(); $field_type = $condition->getFieldName();
@ -1033,7 +1031,7 @@ abstract class HeraldAdapter extends Phobject {
$condition_type = $condition->getFieldCondition(); $condition_type = $condition->getFieldCondition();
$condition_name = idx($this->getConditionNameMap(), $condition_type); $condition_name = idx($this->getConditionNameMap(), $condition_type);
$value = $this->renderConditionValueAsText($condition, $handles, $viewer); $value = $this->renderConditionValueAsText($condition, $viewer);
return array( return array(
$field_name, $field_name,
@ -1046,36 +1044,23 @@ abstract class HeraldAdapter extends Phobject {
private function renderActionAsText( private function renderActionAsText(
PhabricatorUser $viewer, PhabricatorUser $viewer,
HeraldActionRecord $action, HeraldActionRecord $action_record) {
PhabricatorHandleList $handles) {
$impl = $this->getActionImplementation($action->getAction()); $action_type = $action_record->getAction();
if ($impl) { $action_value = $action_record->getTarget();
$impl->setViewer($viewer);
$value = $action->getTarget(); $action = $this->getActionImplementation($action_type);
return $impl->renderActionDescription($value); if (!$action) {
return pht('Unknown Action ("%s")', $action_type);
} }
$rule_global = HeraldRuleTypeConfig::RULE_TYPE_GLOBAL; $action->setViewer($viewer);
$action_type = $action->getAction(); return $action->renderActionDescription($action_value);
$default = pht('(Unknown Action "%s") equals', $action_type);
$action_name = idx(
$this->getActionNameMap($rule_global),
$action_type,
$default);
$target = $this->renderActionTargetAsText($action, $handles);
return hsprintf(' %s %s', $action_name, $target);
} }
private function renderConditionValueAsText( private function renderConditionValueAsText(
HeraldCondition $condition, HeraldCondition $condition,
PhabricatorHandleList $handles,
PhabricatorUser $viewer) { PhabricatorUser $viewer) {
$field = $this->requireFieldImplementation($condition->getFieldName()); $field = $this->requireFieldImplementation($condition->getFieldName());
@ -1086,76 +1071,26 @@ abstract class HeraldAdapter extends Phobject {
$condition->getValue()); $condition->getValue());
} }
private function renderActionTargetAsText( public function renderFieldTranscriptValue(
HeraldActionRecord $action, PhabricatorUser $viewer,
PhabricatorHandleList $handles) { $field_type,
$field_value) {
// TODO: This should be driven through HeraldAction. $field = $this->getFieldImplementation($field_type);
if ($field) {
return $field->renderTranscriptValue(
$viewer,
$field_value);
}
$target = $action->getTarget(); return phutil_tag(
if (!is_array($target)) { 'em',
$target = array($target); array(),
} pht(
foreach ($target as $index => $val) { 'Unable to render value for unknown field type ("%s").',
switch ($action->getAction()) { $field_type));
default:
$handle = $handles->getHandleIfExists($val);
if ($handle) {
$target[$index] = $handle->renderLink();
}
break;
}
}
$target = phutil_implode_html(', ', $target);
return $target;
} }
/**
* Given a @{class:HeraldRule}, this function extracts all the phids that
* we'll want to load as handles later.
*
* This function performs a somewhat hacky approach to figuring out what
* is and is not a phid - try to get the phid type and if the type is
* *not* unknown assume its a valid phid.
*
* Don't try this at home. Use more strongly typed data at home.
*
* Think of the children.
*/
public static function getHandlePHIDs(HeraldRule $rule) {
$phids = array($rule->getAuthorPHID());
foreach ($rule->getConditions() as $condition) {
$value = $condition->getValue();
if (!is_array($value)) {
$value = array($value);
}
foreach ($value as $val) {
if (phid_get_type($val) !=
PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN) {
$phids[] = $val;
}
}
}
foreach ($rule->getActions() as $action) {
$target = $action->getTarget();
if (!is_array($target)) {
$target = array($target);
}
foreach ($target as $val) {
if (phid_get_type($val) !=
PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN) {
$phids[] = $val;
}
}
}
if ($rule->isObjectRule()) {
$phids[] = $rule->getTriggerObjectPHID();
}
return $phids;
}
/* -( Applying Effects )--------------------------------------------------- */ /* -( Applying Effects )--------------------------------------------------- */

View file

@ -143,12 +143,11 @@ final class HeraldRuleViewController extends HeraldController {
private function buildDescriptionView(HeraldRule $rule) { private function buildDescriptionView(HeraldRule $rule) {
$viewer = $this->getRequest()->getUser(); $viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView()) $view = id(new PHUIPropertyListView())
->setUser($viewer); ->setViewer($viewer);
$adapter = HeraldAdapter::getAdapterForContentType($rule->getContentType()); $adapter = HeraldAdapter::getAdapterForContentType($rule->getContentType());
if ($adapter) { if ($adapter) {
$handles = $viewer->loadHandles(HeraldAdapter::getHandlePHIDs($rule)); $rule_text = $adapter->renderRuleAsText($rule, $viewer);
$rule_text = $adapter->renderRuleAsText($rule, $handles, $viewer);
$view->addTextContent($rule_text); $view->addTextContent($rule_text);
return $view; return $view;
} }

View file

@ -447,52 +447,64 @@ final class HeraldTranscriptController extends HeraldController {
} }
private function buildObjectTranscriptPanel(HeraldTranscript $xscript) { private function buildObjectTranscriptPanel(HeraldTranscript $xscript) {
$viewer = $this->getViewer();
$adapter = $this->getAdapter(); $adapter = $this->getAdapter();
$field_names = $adapter->getFieldNameMap(); $field_names = $adapter->getFieldNameMap();
$object_xscript = $xscript->getObjectTranscript(); $object_xscript = $xscript->getObjectTranscript();
$data = array(); $rows = array();
if ($object_xscript) { if ($object_xscript) {
$phid = $object_xscript->getPHID(); $phid = $object_xscript->getPHID();
$handles = $this->handles; $handles = $this->handles;
$data += array( $rows[] = array(
pht('Object Name') => $object_xscript->getName(), pht('Object Name'),
pht('Object Type') => $object_xscript->getType(), $object_xscript->getName(),
pht('Object PHID') => $phid, );
pht('Object Link') => $handles[$phid]->renderLink(),
$rows[] = array(
pht('Object Type'),
$object_xscript->getType(),
);
$rows[] = array(
pht('Object PHID'),
$phid,
);
$rows[] = array(
pht('Object Link'),
$handles[$phid]->renderLink(),
); );
} }
$data += $xscript->getMetadataMap(); foreach ($xscript->getMetadataMap() as $key => $value) {
$rows[] = array(
if ($object_xscript) { $key,
foreach ($object_xscript->getFields() as $field => $value) { $value,
$field = idx($field_names, $field, '['.$field.'?]'); );
$data['Field: '.$field] = $value;
}
} }
$rows = array(); if ($object_xscript) {
foreach ($data as $name => $value) { foreach ($object_xscript->getFields() as $field_type => $value) {
if (!($value instanceof PhutilSafeHTML)) { if (isset($field_names[$field_type])) {
if (!is_scalar($value) && !is_null($value)) { $field_name = pht('Field: %s', $field_names[$field_type]);
$value = implode("\n", $value); } else {
$field_name = pht('Unknown Field ("%s")', $field_type);
} }
if (strlen($value) > 256) { $field_value = $adapter->renderFieldTranscriptValue(
$value = phutil_tag( $viewer,
'textarea', $field_type,
array( $value);
'class' => 'herald-field-value-transcript',
), $rows[] = array(
$value); $field_name,
} $field_value,
);
} }
$rows[] = array($name, $value);
} }
$property_list = new PHUIPropertyListView(); $property_list = new PHUIPropertyListView();

View file

@ -30,6 +30,12 @@ final class HeraldRuleEditor
return true; return true;
} }
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorTransactions::TYPE_EDGE;
return $types;
}
protected function getMailTo(PhabricatorLiskDAO $object) { protected function getMailTo(PhabricatorLiskDAO $object) {
$phids = array(); $phids = array();

View file

@ -23,7 +23,7 @@ final class HeraldAlwaysField extends HeraldField {
} }
public function getHeraldFieldValueType($condition) { public function getHeraldFieldValueType($condition) {
return new HeraldEmptyFieldValue(); return new HeraldBoolFieldValue();
} }
public function supportsObject($object) { public function supportsObject($object) {

View file

@ -105,11 +105,16 @@ abstract class HeraldField extends Phobject {
} }
public function getHeraldFieldValueType($condition) { public function getHeraldFieldValueType($condition) {
// NOTE: The condition type may be "null" to indicate that the caller
// wants a generic field value type. This is used when rendering field
// values in the object transcript.
$standard_type = $this->getHeraldFieldStandardType(); $standard_type = $this->getHeraldFieldStandardType();
switch ($standard_type) { switch ($standard_type) {
case self::STANDARD_BOOL: case self::STANDARD_BOOL:
case self::STANDARD_PHID_BOOL: case self::STANDARD_PHID_BOOL:
return new HeraldEmptyFieldValue(); return new HeraldBoolFieldValue();
case self::STANDARD_TEXT: case self::STANDARD_TEXT:
case self::STANDARD_TEXT_LIST: case self::STANDARD_TEXT_LIST:
case self::STANDARD_TEXT_MAP: case self::STANDARD_TEXT_MAP:
@ -176,6 +181,14 @@ abstract class HeraldField extends Phobject {
return $value_type->renderEditorValue($value); return $value_type->renderEditorValue($value);
} }
public function renderTranscriptValue(
PhabricatorUser $viewer,
$field_value) {
$value_type = $this->getHeraldFieldValueType($condition_type = null);
$value_type->setViewer($viewer);
return $value_type->renderTranscriptValue($field_value);
}
public function getPHIDsAffectedByCondition(HeraldCondition $condition) { public function getPHIDsAffectedByCondition(HeraldCondition $condition) {
try { try {
$standard_type = $this->getHeraldFieldStandardType(); $standard_type = $this->getHeraldFieldStandardType();
@ -241,6 +254,51 @@ abstract class HeraldField extends Phobject {
return false; return false;
} }
final protected function getAppliedTransactionsOfTypes(array $types) {
$types = array_fuse($types);
$xactions = $this->getAdapter()->getAppliedTransactions();
$result = array();
foreach ($xactions as $key => $xaction) {
$xaction_type = $xaction->getTransactionType();
if (isset($types[$xaction_type])) {
$result[$key] = $xaction;
}
}
return $result;
}
final protected function getAppliedEdgeTransactionOfType($edge_type) {
$edge_xactions = $this->getAppliedTransactionsOfTypes(
array(
PhabricatorTransactions::TYPE_EDGE,
));
$results = array();
foreach ($edge_xactions as $edge_xaction) {
$xaction_edge_type = $edge_xaction->getMetadataValue('edge:type');
if ($xaction_edge_type == $edge_type) {
$results[] = $edge_xaction;
}
}
if (count($results) > 1) {
throw new Exception(
pht(
'Found more than one ("%s") applied edge transactions with given '.
'edge type ("%s"); expected zero or one.',
phutil_count($results),
$edge_type));
}
if ($results) {
return head($results);
}
return null;
}
public function isFieldAvailable() { public function isFieldAvailable() {
return true; return true;
} }

View file

@ -0,0 +1,30 @@
<?php
final class HeraldBoolFieldValue
extends HeraldFieldValue {
public function getFieldValueKey() {
return 'bool';
}
public function getControlType() {
return self::CONTROL_NONE;
}
public function renderFieldValue($value) {
return null;
}
public function renderEditorValue($value) {
return null;
}
public function renderTranscriptValue($value) {
if ($value) {
return pht('true');
} else {
return pht('false');
}
}
}

View file

@ -36,4 +36,8 @@ abstract class HeraldFieldValue extends Phobject {
return array(); return array();
} }
public function renderTranscriptValue($value) {
return $this->renderFieldValue($value);
}
} }

View file

@ -19,4 +19,25 @@ final class HeraldTextFieldValue
return $value; return $value;
} }
public function renderTranscriptValue($value) {
if (is_array($value)) {
$value = implode('', $value);
}
if (!strlen($value)) {
return phutil_tag('em', array(), pht('None'));
}
if (strlen($value) > 256) {
$value = phutil_tag(
'textarea',
array(
'class' => 'herald-field-value-transcript',
),
$value);
}
return $value;
}
} }

View file

@ -64,17 +64,7 @@ final class HeraldTokenizerFieldValue
} }
public function renderFieldValue($value) { public function renderFieldValue($value) {
$viewer = $this->getViewer(); return $this->renderValueAsList($value, $for_transcript = false);
$value = (array)$value;
if ($this->valueMap !== null) {
foreach ($value as $k => $v) {
$value[$k] = idx($this->valueMap, $v, $v);
}
return implode(', ', $value);
}
return $viewer->renderHandleList((array)$value)->setAsInline(true);
} }
public function renderEditorValue($value) { public function renderEditorValue($value) {
@ -87,4 +77,33 @@ final class HeraldTokenizerFieldValue
return $datasource->getWireTokens($value); return $datasource->getWireTokens($value);
} }
public function renderTranscriptValue($value) {
return $this->renderValueAsList($value, $for_transcript = true);
}
private function renderValueAsList($value, $for_transcript) {
$viewer = $this->getViewer();
$value = (array)$value;
if (!$value) {
return phutil_tag('em', array(), pht('None'));
}
if ($this->valueMap !== null) {
foreach ($value as $k => $v) {
$value[$k] = idx($this->valueMap, $v, $v);
}
return implode(', ', $value);
}
$list = $viewer->renderHandleList($value);
if (!$for_transcript) {
$list->setAsInline(true);
}
return $list;
}
} }

View file

@ -1,6 +1,7 @@
<?php <?php
final class HeraldProjectsField extends HeraldField { final class HeraldProjectsField
extends PhabricatorProjectTagsField {
const FIELDCONST = 'projects'; const FIELDCONST = 'projects';
@ -8,26 +9,10 @@ final class HeraldProjectsField extends HeraldField {
return pht('Project tags'); return pht('Project tags');
} }
public function getFieldGroupKey() {
return HeraldSupportFieldGroup::FIELDGROUPKEY;
}
public function supportsObject($object) {
return ($object instanceof PhabricatorProjectInterface);
}
public function getHeraldFieldValue($object) { public function getHeraldFieldValue($object) {
return PhabricatorEdgeQuery::loadDestinationPHIDs( return PhabricatorEdgeQuery::loadDestinationPHIDs(
$object->getPHID(), $object->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
} }
protected function getHeraldFieldStandardType() {
return self::STANDARD_PHID_LIST;
}
protected function getDatasource() {
return new PhabricatorProjectDatasource();
}
} }

View file

@ -0,0 +1,23 @@
<?php
final class PhabricatorProjectTagsAddedField
extends PhabricatorProjectTagsField {
const FIELDCONST = 'projects.added';
public function getHeraldFieldName() {
return pht('Project tags added');
}
public function getHeraldFieldValue($object) {
$xaction = $this->getProjectTagsTransaction();
if (!$xaction) {
return array();
}
$record = PhabricatorEdgeChangeRecord::newFromTransaction($xaction);
return $record->getAddedPHIDs();
}
}

View file

@ -0,0 +1,27 @@
<?php
abstract class PhabricatorProjectTagsField
extends HeraldField {
public function getFieldGroupKey() {
return HeraldSupportFieldGroup::FIELDGROUPKEY;
}
public function supportsObject($object) {
return ($object instanceof PhabricatorProjectInterface);
}
protected function getHeraldFieldStandardType() {
return self::STANDARD_PHID_LIST;
}
protected function getDatasource() {
return new PhabricatorProjectDatasource();
}
final protected function getProjectTagsTransaction() {
return $this->getAppliedEdgeTransactionOfType(
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
}
}

View file

@ -0,0 +1,23 @@
<?php
final class PhabricatorProjectTagsRemovedField
extends PhabricatorProjectTagsField {
const FIELDCONST = 'projects.removed';
public function getHeraldFieldName() {
return pht('Project tags removed');
}
public function getHeraldFieldValue($object) {
$xaction = $this->getProjectTagsTransaction();
if (!$xaction) {
return array();
}
$record = PhabricatorEdgeChangeRecord::newFromTransaction($xaction);
return $record->getRemovedPHIDs();
}
}

View file

@ -275,11 +275,39 @@ final class PhabricatorRepositoryPullEngine
private function executeGitUpdate() { private function executeGitUpdate() {
$repository = $this->getRepository(); $repository = $this->getRepository();
// See T13479. We previously used "--show-toplevel", but this stopped
// working in Git 2.25.0 when run in a bare repository.
// NOTE: As of Git 2.21.1, "git rev-parse" can not parse "--" in its
// argument list, so we can not specify arguments unambiguously. Any
// version of Git which does not recognize the "--git-dir" flag will
// treat this as a request to parse the literal refname "--git-dir".
list($err, $stdout) = $repository->execLocalCommand( list($err, $stdout) = $repository->execLocalCommand(
'rev-parse --show-toplevel'); 'rev-parse --git-dir');
$repository_root = null;
$path = $repository->getLocalPath();
if (!$err) {
$repository_root = Filesystem::resolvePath(
rtrim($stdout, "\n"),
$path);
// If we're in a bare Git repository, the "--git-dir" will be the
// root directory. If we're in a working copy, the "--git-dir" will
// be the ".git/" directory.
// Test if the result is the root directory. If it is, we're in good
// shape and appear to be inside a bare repository. If not, take the
// parent directory to get out of the ".git/" folder.
if (!Filesystem::pathsAreEquivalent($repository_root, $path)) {
$repository_root = dirname($repository_root);
}
}
$message = null; $message = null;
$path = $repository->getLocalPath();
if ($err) { if ($err) {
// Try to raise a more tailored error message in the more common case // Try to raise a more tailored error message in the more common case
// of the user creating an empty directory. (We could try to remove it, // of the user creating an empty directory. (We could try to remove it,
@ -313,15 +341,14 @@ final class PhabricatorRepositoryPullEngine
$path); $path);
} }
} else { } else {
$repo_path = rtrim($stdout, "\n");
if (empty($repo_path)) { // Prior to Git 2.25.0, we used "--show-toplevel", which had a weird
// This can mean one of two things: we're in a bare repository, or // case here when the working copy was inside another working copy.
// we're inside a git repository inside another git repository. Since // The switch to "--git-dir" seems to have resolved this; we now seem
// the first is dramatically more likely now that we perform bare // to find the nearest git directory and thus the correct repository
// clones and I don't have a great way to test for the latter, assume // root.
// we're OK.
} else if (!Filesystem::pathsAreEquivalent($repo_path, $path)) { if (!Filesystem::pathsAreEquivalent($repository_root, $path)) {
$err = true; $err = true;
$message = pht( $message = pht(
'Expected to find a Git repository at "%s", but the actual Git '. 'Expected to find a Git repository at "%s", but the actual Git '.
@ -329,7 +356,7 @@ final class PhabricatorRepositoryPullEngine
'misconfigured. This directory should be writable by the daemons '. 'misconfigured. This directory should be writable by the daemons '.
'and not inside another Git repository.', 'and not inside another Git repository.',
$path, $path,
$repo_path); $repository_root);
} }
} }

View file

@ -0,0 +1,374 @@
<?php
final class PhutilSearchQueryCompiler
extends Phobject {
private $operators = '+ -><()~*:""&|';
private $query;
private $stemmer;
private $enableFunctions = false;
const OPERATOR_NOT = 'not';
const OPERATOR_AND = 'and';
const OPERATOR_SUBSTRING = 'sub';
const OPERATOR_EXACT = 'exact';
public function setOperators($operators) {
$this->operators = $operators;
return $this;
}
public function getOperators() {
return $this->operators;
}
public function setStemmer(PhutilSearchStemmer $stemmer) {
$this->stemmer = $stemmer;
return $this;
}
public function getStemmer() {
return $this->stemmer;
}
public function setEnableFunctions($enable_functions) {
$this->enableFunctions = $enable_functions;
return $this;
}
public function getEnableFunctions() {
return $this->enableFunctions;
}
public function compileQuery(array $tokens) {
assert_instances_of($tokens, 'PhutilSearchQueryToken');
$result = array();
foreach ($tokens as $token) {
$result[] = $this->renderToken($token);
}
return $this->compileRenderedTokens($result);
}
public function compileLiteralQuery(array $tokens) {
assert_instances_of($tokens, 'PhutilSearchQueryToken');
$result = array();
foreach ($tokens as $token) {
if (!$token->isQuoted()) {
continue;
}
$result[] = $this->renderToken($token);
}
return $this->compileRenderedTokens($result);
}
public function compileStemmedQuery(array $tokens) {
assert_instances_of($tokens, 'PhutilSearchQueryToken');
$result = array();
foreach ($tokens as $token) {
if ($token->isQuoted()) {
continue;
}
$result[] = $this->renderToken($token, $this->getStemmer());
}
return $this->compileRenderedTokens($result);
}
private function compileRenderedTokens(array $list) {
if (!$list) {
return null;
}
$list = array_unique($list);
return implode(' ', $list);
}
public function newTokens($query) {
$results = $this->tokenizeQuery($query);
$tokens = array();
foreach ($results as $result) {
$tokens[] = PhutilSearchQueryToken::newFromDictionary($result);
}
return $tokens;
}
private function tokenizeQuery($query) {
$maximum_bytes = 1024;
$query_bytes = strlen($query);
if ($query_bytes > $maximum_bytes) {
throw new PhutilSearchQueryCompilerSyntaxException(
pht(
'Query is too long (%s bytes, maximum is %s bytes).',
new PhutilNumber($query_bytes),
new PhutilNumber($maximum_bytes)));
}
$query = phutil_utf8v($query);
$length = count($query);
$enable_functions = $this->getEnableFunctions();
$mode = 'scan';
$current_operator = array();
$current_token = array();
$current_function = null;
$is_quoted = false;
$tokens = array();
if ($enable_functions) {
$operator_characters = '[~=+-]';
} else {
$operator_characters = '[+-]';
}
for ($ii = 0; $ii < $length; $ii++) {
$character = $query[$ii];
if ($mode == 'scan') {
if (preg_match('/^\s\z/u', $character)) {
continue;
}
$mode = 'function';
}
if ($mode == 'function') {
$mode = 'operator';
if ($enable_functions) {
$found = false;
for ($jj = $ii; $jj < $length; $jj++) {
if (preg_match('/^[a-zA-Z]\z/u', $query[$jj])) {
continue;
}
if ($query[$jj] == ':') {
$found = $jj;
}
break;
}
if ($found !== false) {
$function = array_slice($query, $ii, ($jj - $ii));
$current_function = implode('', $function);
if (!strlen($current_function)) {
$current_function = null;
}
$ii = $jj;
continue;
}
}
}
if ($mode == 'operator') {
if (preg_match('/^\s\z/u', $character)) {
continue;
}
if (preg_match('/^'.$operator_characters.'\z/', $character)) {
$current_operator[] = $character;
continue;
}
$mode = 'quote';
}
if ($mode == 'quote') {
if (preg_match('/^"\z/', $character)) {
$is_quoted = true;
$mode = 'token';
continue;
}
$mode = 'token';
}
if ($mode == 'token') {
$capture = false;
$was_quoted = $is_quoted;
if ($is_quoted) {
if (preg_match('/^"\z/', $character)) {
$capture = true;
$mode = 'scan';
$is_quoted = false;
}
} else {
if (preg_match('/^\s\z/u', $character)) {
$capture = true;
$mode = 'scan';
}
if (preg_match('/^"\z/', $character)) {
$capture = true;
$mode = 'token';
$is_quoted = true;
}
}
if ($capture) {
$token = array(
'operator' => $current_operator,
'quoted' => $was_quoted,
'value' => $current_token,
);
if ($enable_functions) {
$token['function'] = $current_function;
}
$tokens[] = $token;
$current_operator = array();
$current_token = array();
$current_function = null;
continue;
} else {
$current_token[] = $character;
}
}
}
if ($is_quoted) {
throw new PhutilSearchQueryCompilerSyntaxException(
pht(
'Query contains unmatched double quotes.'));
}
if ($mode == 'operator') {
throw new PhutilSearchQueryCompilerSyntaxException(
pht(
'Query contains operator ("%s") with no search term.',
implode('', $current_operator)));
}
$token = array(
'operator' => $current_operator,
'quoted' => false,
'value' => $current_token,
);
if ($enable_functions) {
$token['function'] = $current_function;
}
$tokens[] = $token;
$results = array();
foreach ($tokens as $token) {
$value = implode('', $token['value']);
$operator_string = implode('', $token['operator']);
if (!strlen($value)) {
continue;
}
$is_quoted = $token['quoted'];
switch ($operator_string) {
case '-':
$operator = self::OPERATOR_NOT;
break;
case '~':
$operator = self::OPERATOR_SUBSTRING;
break;
case '=':
$operator = self::OPERATOR_EXACT;
break;
case '+':
$operator = self::OPERATOR_AND;
break;
case '':
// See T12995. If this query term contains Chinese, Japanese or
// Korean characters, treat the term as a substring term by default.
// These languages do not separate words with spaces, so the term
// search mode is normally useless.
if ($enable_functions && !$is_quoted && phutil_utf8_is_cjk($value)) {
$operator = self::OPERATOR_SUBSTRING;
} else {
$operator = self::OPERATOR_AND;
}
break;
default:
throw new PhutilSearchQueryCompilerSyntaxException(
pht(
'Query has an invalid sequence of operators ("%s").',
$operator_string));
}
$result = array(
'operator' => $operator,
'quoted' => $is_quoted,
'value' => $value,
);
if ($enable_functions) {
$result['function'] = $token['function'];
}
$results[] = $result;
}
return $results;
}
private function renderToken(
PhutilSearchQueryToken $token,
PhutilSearchStemmer $stemmer = null) {
$value = $token->getValue();
if ($stemmer) {
$value = $stemmer->stemToken($value);
}
$value = $this->quoteToken($value);
$operator = $token->getOperator();
$prefix = $this->getOperatorPrefix($operator);
$value = $prefix.$value;
return $value;
}
private function getOperatorPrefix($operator) {
$operators = $this->operators;
switch ($operator) {
case self::OPERATOR_AND:
$prefix = $operators[0];
break;
case self::OPERATOR_NOT:
$prefix = $operators[2];
break;
default:
throw new PhutilSearchQueryCompilerSyntaxException(
pht(
'Unsupported operator prefix "%s".',
$operator));
}
if ($prefix == ' ') {
$prefix = null;
}
return $prefix;
}
private function quoteToken($value) {
$operators = $this->operators;
$open_quote = $this->operators[10];
$close_quote = $this->operators[11];
return $open_quote.$value.$close_quote;
}
}

View file

@ -0,0 +1,4 @@
<?php
final class PhutilSearchQueryCompilerSyntaxException
extends Exception {}

View file

@ -0,0 +1,37 @@
<?php
final class PhutilSearchQueryToken extends Phobject {
private $isQuoted;
private $value;
private $operator;
private $function;
public static function newFromDictionary(array $dictionary) {
$token = new self();
$token->isQuoted = $dictionary['quoted'];
$token->operator = $dictionary['operator'];
$token->value = $dictionary['value'];
$token->function = idx($dictionary, 'function');
return $token;
}
public function isQuoted() {
return $this->isQuoted;
}
public function getValue() {
return $this->value;
}
public function getOperator() {
return $this->operator;
}
public function getFunction() {
return $this->function;
}
}

View file

@ -0,0 +1,74 @@
<?php
final class PhutilSearchStemmer
extends Phobject {
public function stemToken($token) {
$token = $this->normalizeToken($token);
return $this->applyStemmer($token);
}
public function stemCorpus($corpus) {
$corpus = $this->normalizeCorpus($corpus);
$tokens = preg_split('/[^a-zA-Z0-9\x7F-\xFF._]+/', $corpus);
$words = array();
foreach ($tokens as $key => $token) {
$token = trim($token, '._');
if (strlen($token) < 3) {
continue;
}
$words[$token] = $token;
}
$stems = array();
foreach ($words as $word) {
$stems[] = $this->applyStemmer($word);
}
return implode(' ', $stems);
}
private function normalizeToken($token) {
return phutil_utf8_strtolower($token);
}
private function normalizeCorpus($corpus) {
return phutil_utf8_strtolower($corpus);
}
/**
* @phutil-external-symbol class Porter
*/
private function applyStemmer($normalized_token) {
// If the token has internal punctuation, handle it literally. This
// deals with things like domain names, Conduit API methods, and other
// sorts of informal tokens.
if (preg_match('/[._]/', $normalized_token)) {
return $normalized_token;
}
static $loaded;
if ($loaded === null) {
$root = dirname(phutil_get_library_root('phabricator'));
require_once $root.'/externals/porter-stemmer/src/Porter.php';
$loaded = true;
}
$stem = Porter::stem($normalized_token);
// If the stem is too short, it won't be a candidate for indexing. These
// tokens are also likely to be acronyms (like "DNS") rather than real
// English words.
if (strlen($stem) < 3) {
return $normalized_token;
}
return $stem;
}
}

View file

@ -0,0 +1,220 @@
<?php
final class PhutilSearchQueryCompilerTestCase
extends PhutilTestCase {
public function testCompileQueries() {
$tests = array(
'' => null,
'cat dog' => '+"cat" +"dog"',
'cat -dog' => '+"cat" -"dog"',
'cat-dog' => '+"cat-dog"',
// If there are spaces after an operator, the operator applies to the
// next search term.
'cat - dog' => '+"cat" -"dog"',
// Double quotes serve as delimiters even if there is no whitespace
// between terms.
'"cat"dog' => '+"cat" +"dog"',
// This query is too long.
str_repeat('x', 2048) => false,
// Multiple operators are not permitted.
'++cat' => false,
'+-cat' => false,
'--cat' => false,
// Stray operators are not permitted.
'+' => false,
'cat +' => false,
// Double quotes must be paired.
'"' => false,
'cat "' => false,
'"cat' => false,
'A"' => false,
'A"B"' => '+"A" +"B"',
);
$this->assertCompileQueries($tests);
// Test that we compile queries correctly if the operators have been
// swapped to use "AND" by default.
$operator_tests = array(
'cat dog' => '"cat" "dog"',
'cat -dog' => '"cat" -"dog"',
);
$this->assertCompileQueries($operator_tests, ' |-><()~*:""&\'');
// Test that we compile queries correctly if the quote operators have
// been swapped to differ.
$quote_tests = array(
'cat dog' => '+[cat] +[dog]',
'cat -dog' => '+[cat] -[dog]',
);
$this->assertCompileQueries($quote_tests, '+ -><()~*:[]&|');
}
public function testCompileQueriesWithStemming() {
$stemming_tests = array(
'cat dog' => array(
null,
'+"cat" +"dog"',
),
'cats dogs' => array(
null,
'+"cat" +"dog"',
),
'cats "dogs"' => array(
'+"dogs"',
'+"cat"',
),
'"blessed blade" of the windseeker' => array(
'+"blessed blade"',
'+"of" +"the" +"windseek"',
),
'mailing users for mentions on tasks' => array(
null,
'+"mail" +"user" +"for" +"mention" +"on" +"task"',
),
);
$stemmer = new PhutilSearchStemmer();
$this->assertCompileQueries($stemming_tests, null, $stemmer);
}
public function testCompileQueriesWithFunctions() {
$op_and = PhutilSearchQueryCompiler::OPERATOR_AND;
$op_sub = PhutilSearchQueryCompiler::OPERATOR_SUBSTRING;
$op_exact = PhutilSearchQueryCompiler::OPERATOR_EXACT;
$mao = "\xE7\x8C\xAB";
$function_tests = array(
'cat' => array(
array(null, $op_and, 'cat'),
),
':cat' => array(
array(null, $op_and, 'cat'),
),
'title:cat' => array(
array('title', $op_and, 'cat'),
),
'title:cat:dog' => array(
array('title', $op_and, 'cat:dog'),
),
'title:~cat' => array(
array('title', $op_sub, 'cat'),
),
'cat title:="Meow Meow"' => array(
array(null, $op_and, 'cat'),
array('title', $op_exact, 'Meow Meow'),
),
'title:cat title:dog' => array(
array('title', $op_and, 'cat'),
array('title', $op_and, 'dog'),
),
'~"core and seven years ag"' => array(
array(null, $op_sub, 'core and seven years ag'),
),
$mao => array(
array(null, $op_sub, $mao),
),
'+'.$mao => array(
array(null, $op_and, $mao),
),
'~'.$mao => array(
array(null, $op_sub, $mao),
),
'"'.$mao.'"' => array(
array(null, $op_and, $mao),
),
);
$this->assertCompileFunctionQueries($function_tests);
}
private function assertCompileQueries(
array $tests,
$operators = null,
PhutilSearchStemmer $stemmer = null) {
foreach ($tests as $input => $expect) {
$caught = null;
$query = null;
$literal_query = null;
$stemmed_query = null;
try {
$compiler = new PhutilSearchQueryCompiler();
if ($operators !== null) {
$compiler->setOperators($operators);
}
if ($stemmer !== null) {
$compiler->setStemmer($stemmer);
}
$tokens = $compiler->newTokens($input);
if ($stemmer) {
$literal_query = $compiler->compileLiteralQuery($tokens);
$stemmed_query = $compiler->compileStemmedQuery($tokens);
} else {
$query = $compiler->compileQuery($tokens);
}
} catch (PhutilSearchQueryCompilerSyntaxException $ex) {
$caught = $ex;
}
if ($caught !== null) {
$query = false;
$literal_query = false;
$stemmed_query = false;
}
if (!$stemmer) {
$this->assertEqual(
$expect,
$query,
pht('Compilation of query: %s', $input));
} else {
$this->assertEqual(
$expect,
($literal_query === false)
? false
: array($literal_query, $stemmed_query),
pht('Stemmed compilation of query: %s', $input));
}
}
}
private function assertCompileFunctionQueries(array $tests) {
foreach ($tests as $input => $expect) {
$compiler = id(new PhutilSearchQueryCompiler())
->setEnableFunctions(true);
$tokens = $compiler->newTokens($input);
$result = array();
foreach ($tokens as $token) {
$result[] = array(
$token->getFunction(),
$token->getOperator(),
$token->getValue(),
);
}
$this->assertEqual(
$expect,
$result,
pht('Function compilation of query: %s', $input));
}
}
}

View file

@ -0,0 +1,85 @@
<?php
final class PhutilSearchStemmerTestCase
extends PhutilTestCase {
public function testStemTokens() {
$tests = array(
// Various real-world cases collected from users before we implemented
// stemming.
'tokens' => 'token',
'panels' => 'panel',
'renames' => 'renam',
'rename' => 'renam',
'components' => 'compon',
'component' => 'compon',
'implementation' => 'implement',
'implements' => 'implement',
'implementing' => 'implement',
'implementer' => 'implement',
'deleting' => 'delet',
'deletion' => 'delet',
'delete' => 'delet',
'erratically' => 'errat',
'erratic' => 'errat',
// Stems should be normalized.
'DOG' => 'dog',
// If stemming would bring a token under 3 characters, it should not
// be stemmed.
'dns' => 'dns',
'nis' => 'nis',
// Complex tokens with internal punctuation should be left untouched;
// these are usually things like domain names, API calls, informal tags,
// etc.
'apples' => 'appl',
'bananas' => 'banana',
'apples_bananas' => 'apples_bananas',
'apples_bananas.apples_bananas' => 'apples_bananas.apples_bananas',
);
$stemmer = new PhutilSearchStemmer();
foreach ($tests as $input => $expect) {
$stem = $stemmer->stemToken($input);
$this->assertEqual(
$expect,
$stem,
pht('Token stem of "%s".', $input));
}
}
public function testStemDocuments() {
$tests = array(
'The wild boar meandered erratically.' =>
'the wild boar meander errat',
'Fool me onc, shame on you. Fool me twice, shame on me.' =>
'fool onc shame you twice',
'Fireball is a seventh-level spell which deals 2d16 points of damage '.
'in a 1-meter radius around a target.' =>
'firebal seventh level spell which deal 2d16 point damag meter '.
'radiu around target',
'apples-bananas' => 'appl banana',
'apples_bananas' => 'apples_bananas',
'apples.bananas' => 'apples.bananas',
'oddly-proportioned' => 'oddli proport',
);
$stemmer = new PhutilSearchStemmer();
foreach ($tests as $input => $expect) {
$stem = $stemmer->stemCorpus($input);
$this->assertEqual(
$expect,
$stem,
pht('Corpus stem of: %s', $input));
}
}
}

View file

@ -5152,12 +5152,14 @@ abstract class PhabricatorApplicationTransactionEditor
'an MFA check.')); 'an MFA check.'));
} }
id(new PhabricatorAuthSessionEngine()) $token = id(new PhabricatorAuthSessionEngine())
->setWorkflowKey($workflow_key) ->setWorkflowKey($workflow_key)
->requireHighSecurityToken($actor, $request, $cancel_uri); ->requireHighSecurityToken($actor, $request, $cancel_uri);
foreach ($xactions as $xaction) { if (!$token->getIsUnchallengedToken()) {
$xaction->setIsMFATransaction(true); foreach ($xactions as $xaction) {
$xaction->setIsMFATransaction(true);
}
} }
} }

View file

@ -523,7 +523,7 @@ final class PhabricatorStartup {
"'{$required_version}'."); "'{$required_version}'.");
} }
if (get_magic_quotes_gpc()) { if (@get_magic_quotes_gpc()) {
self::didFatal( self::didFatal(
"Your server is configured with PHP 'magic_quotes_gpc' enabled. This ". "Your server is configured with PHP 'magic_quotes_gpc' enabled. This ".
"feature is 'highly discouraged' by PHP's developers and you must ". "feature is 'highly discouraged' by PHP's developers and you must ".

View file

@ -155,7 +155,6 @@
.phabricator-remarkup .remarkup-list-with-checkmarks .remarkup-checked-item { .phabricator-remarkup .remarkup-list-with-checkmarks .remarkup-checked-item {
color: {$lightgreytext}; color: {$lightgreytext};
text-decoration: line-through;
} }
.phabricator-remarkup ul.remarkup-list ol.remarkup-list, .phabricator-remarkup ul.remarkup-list ol.remarkup-list,

View file

@ -152,7 +152,7 @@ textarea[disabled],
} }
.aphront-space-select-control-knob { .aphront-space-select-control-knob {
margin: 0 8px 0 0; margin: 0 8px 4px 0;
float: left; float: left;
} }
@ -162,7 +162,6 @@ textarea[disabled],
} }
.device .aphront-space-select-control-knob { .device .aphront-space-select-control-knob {
margin-bottom: 8px;
float: none; float: none;
} }

View file

@ -203,6 +203,7 @@ body .phui-header-shell.phui-bleed-header
margin-right: 8px; margin-right: 8px;
-webkit-font-smoothing: auto; -webkit-font-smoothing: auto;
border-color: transparent; border-color: transparent;
line-height: 28px;
} }

View file

@ -50,7 +50,7 @@
} }
.phui-two-column-header .phui-header-subheader { .phui-two-column-header .phui-header-subheader {
margin-top: 12px; margin-top: 8px;
} }
.phui-two-column-subheader { .phui-two-column-subheader {