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:
commit
cc11dff7d3
49 changed files with 2013 additions and 233 deletions
20
externals/porter-stemmer/LICENSE
vendored
Normal file
20
externals/porter-stemmer/LICENSE
vendored
Normal 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
42
externals/porter-stemmer/README.md
vendored
Normal 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
426
externals/porter-stemmer/src/Porter.php
vendored
Normal 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';
|
||||||
|
}
|
||||||
|
}
|
|
@ -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',
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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']) ||
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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');
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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');
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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');
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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');
|
||||||
|
|
|
@ -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 )------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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 )--------------------------------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
30
src/applications/herald/value/HeraldBoolFieldValue.php
Normal file
30
src/applications/herald/value/HeraldBoolFieldValue.php
Normal 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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -36,4 +36,8 @@ abstract class HeraldFieldValue extends Phobject {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function renderTranscriptValue($value) {
|
||||||
|
return $this->renderFieldValue($value);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
374
src/applications/search/compiler/PhutilSearchQueryCompiler.php
Normal file
374
src/applications/search/compiler/PhutilSearchQueryCompiler.php
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhutilSearchQueryCompilerSyntaxException
|
||||||
|
extends Exception {}
|
37
src/applications/search/compiler/PhutilSearchQueryToken.php
Normal file
37
src/applications/search/compiler/PhutilSearchQueryToken.php
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
74
src/applications/search/compiler/PhutilSearchStemmer.php
Normal file
74
src/applications/search/compiler/PhutilSearchStemmer.php
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 ".
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Add table
Reference in a new issue