diff --git a/.arcconfig b/.arcconfig index 88f04f72c5..b381a3ac53 100644 --- a/.arcconfig +++ b/.arcconfig @@ -1,5 +1,5 @@ { - "phabricator.uri": "https://secure.phabricator.com/", + "phabricator.uri": "https://we.phorge.it/", "load": ["src/"], "history.immutable": false } diff --git a/.arclint b/.arclint index 29258b5d3e..7fb4c0f4a6 100644 --- a/.arclint +++ b/.arclint @@ -2,7 +2,8 @@ "exclude": [ "(^externals/)", "(^webroot/rsrc/externals/(?!javelin/))", - "(/__tests__/data/)" + "(/__tests__/data/)", + "(^support/aphlict/server/package-lock.json)" ], "linters": { "chmod": { diff --git a/.gitignore b/.gitignore index d9f44b6bab..4e3a12e5e9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ # Diviner /docs/ /.divinercache/ +/webroot/rsrc/externals/javelin/docs/.divinercache/ /src/.cache/ # libphutil diff --git a/README.md b/README.md index 5190845da4..19dc39cd82 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,24 @@ -Effective June 1, 2021: Phabricator is no longer actively maintained. +**Phorge** is a collection of web applications which help software companies build better software. -**Phabricator** is a collection of web applications for software development. +Phorge is a community-maintained fork of [Phabricator](http://phabricator.org). + +Phorge includes applications for: + + - reviewing and auditing source code; + - hosting and browsing repositories; + - tracking bugs; + - managing projects; + - conversing with team members; + - assembling a party to venture forth; + - writing stuff down and reading it later; + - hiding stuff from coworkers; and + - also some other things. + + +Phorge is developed and maintained by [The Phorge Team](https://phorge.it). + +---------- **LICENSE** -Phabricator is released under the Apache 2.0 license except as otherwise noted. +Phorge is released under the Apache 2.0 license except as otherwise noted. diff --git a/conf/aphlict/README b/conf/aphlict/README index 2786ea5658..0704bf6068 100644 --- a/conf/aphlict/README +++ b/conf/aphlict/README @@ -8,7 +8,7 @@ be read by default. To specify a path when starting Aphlict, use the `--config` flag: - phabricator/ $ ./bin/aphlict start --config path/to/config.json + phorge/ $ ./bin/aphlict start --config path/to/config.json Specifying a configuration file explicitly overrides default configuration. diff --git a/externals/JsShrink/jsShrink.php b/externals/JsShrink/jsShrink.php index 239e6d7978..34a5a5b8e8 100644 --- a/externals/JsShrink/jsShrink.php +++ b/externals/JsShrink/jsShrink.php @@ -35,7 +35,7 @@ function jsShrinkCallback($match) { list(, $context, $regexp, $result, $word, $operator) = $match; if ($word != '') { $result = ($last == 'word' ? "\n" : ($last == 'return' ? " " : "")) . $result; - $last = ($word == 'return' || $word == 'throw' || $word == 'break' ? 'return' : 'word'); + $last = ($word == 'return' || $word == 'throw' || $word == 'break' || $word == 'async' ? 'return' : 'word'); } elseif ($operator) { $result = ($last == $operator[0] ? "\n" : "") . $result; $last = $operator[0]; diff --git a/externals/cldr/cldr_windows_timezones.xml b/externals/cldr/cldr_windows_timezones.xml index 47b689d8af..2fae6c3c18 100644 --- a/externals/cldr/cldr_windows_timezones.xml +++ b/externals/cldr/cldr_windows_timezones.xml @@ -9,7 +9,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -30,7 +30,6 @@ For terms of use, see http://www.unicode.org/copyright.html - @@ -49,7 +48,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -58,28 +57,32 @@ For terms of use, see http://www.unicode.org/copyright.html - + - + - - + + - - + + + + + + @@ -93,8 +96,8 @@ For terms of use, see http://www.unicode.org/copyright.html - - + + @@ -104,7 +107,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -129,7 +132,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -145,6 +148,10 @@ For terms of use, see http://www.unicode.org/copyright.html + + + + @@ -195,13 +202,8 @@ For terms of use, see http://www.unicode.org/copyright.html - - - - - @@ -216,7 +218,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -235,6 +237,10 @@ For terms of use, see http://www.unicode.org/copyright.html + + + + @@ -260,14 +266,8 @@ For terms of use, see http://www.unicode.org/copyright.html - - - - - - - - + + @@ -285,6 +285,7 @@ For terms of use, see http://www.unicode.org/copyright.html + @@ -295,9 +296,17 @@ For terms of use, see http://www.unicode.org/copyright.html - + + + + + + + + + @@ -358,17 +367,13 @@ For terms of use, see http://www.unicode.org/copyright.html - - - - - + @@ -415,28 +420,40 @@ For terms of use, see http://www.unicode.org/copyright.html - - - - - + + + + + + + + + + + + + + + + + @@ -449,9 +466,9 @@ For terms of use, see http://www.unicode.org/copyright.html - + - + @@ -463,9 +480,7 @@ For terms of use, see http://www.unicode.org/copyright.html - - @@ -499,10 +514,18 @@ For terms of use, see http://www.unicode.org/copyright.html + + + + + + + + @@ -514,7 +537,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -530,6 +553,10 @@ For terms of use, see http://www.unicode.org/copyright.html + + + + @@ -548,7 +575,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -627,10 +654,6 @@ For terms of use, see http://www.unicode.org/copyright.html - - - - @@ -647,6 +670,10 @@ For terms of use, see http://www.unicode.org/copyright.html + + + + @@ -682,7 +709,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -715,7 +742,6 @@ For terms of use, see http://www.unicode.org/copyright.html - @@ -749,12 +775,15 @@ For terms of use, see http://www.unicode.org/copyright.html + + + + + + - - - diff --git a/externals/cowsay/INSTALL b/externals/cowsay/INSTALL deleted file mode 100644 index d3844ca565..0000000000 --- a/externals/cowsay/INSTALL +++ /dev/null @@ -1,15 +0,0 @@ -================= -Installing cowsay -================= - -If you really want to get things installed a nice and pretty way, - - sh install.sh - -It will ask approximately one question. If you can't answer it, -you need serious help. - -If the install goes well, you can start cowing immediately! Just -be sure to read the manual page first... - -$Id: INSTALL,v 1.1 1999/08/14 08:03:17 tony Exp $ diff --git a/externals/cowsay/MANIFEST b/externals/cowsay/MANIFEST deleted file mode 100644 index a3ff805fed..0000000000 --- a/externals/cowsay/MANIFEST +++ /dev/null @@ -1,11 +0,0 @@ -ChangeLog Changes to recent versions. -INSTALL Instructions for installing cowsay. -LICENSE The license for use and redistribution of cowsay. -MANIFEST This file. -README Read this first. Really. -Wrap.pm.diff Diff for Text/Wrap.pm. -cows/* Support files used by cowsay. -cowsay Main cowsay executable. -cowsay.1 Main cowsay manual page. -install.sh cowsay installation script. -pgp_public_key.txt Verify the signature file with this key. diff --git a/externals/cowsay/README b/externals/cowsay/README index b5de08c405..50b746c1ec 100644 --- a/externals/cowsay/README +++ b/externals/cowsay/README @@ -36,3 +36,11 @@ this directory. -- Tony Monroe (tony@nog.net) $Id: README,v 1.3 2000/05/28 06:24:46 tony Exp $ + +---- + +In September 2015, Phabricator imported the Perl version of cowsay +in the core, to use its nice template files. Anyway, the logic now +is not in Perl. It was re-implemented in PHP, here: + +src/infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php diff --git a/externals/cowsay/Wrap.pm.diff b/externals/cowsay/Wrap.pm.diff deleted file mode 100644 index 1da064079e..0000000000 --- a/externals/cowsay/Wrap.pm.diff +++ /dev/null @@ -1,47 +0,0 @@ -*** Wrap.pm.in Thu May 22 00:21:42 1997 ---- Wrap.pm Fri Nov 12 10:00:15 1999 -*************** -*** 3,9 **** - require Exporter; - - @ISA = (Exporter); -! @EXPORT = qw(wrap); - @EXPORT_OK = qw($columns); - - $VERSION = 97.011701; ---- 3,9 ---- - require Exporter; - - @ISA = (Exporter); -! @EXPORT = qw(wrap fill); - @EXPORT_OK = qw($columns); - - $VERSION = 97.011701; -*************** -*** 66,71 **** ---- 66,90 ---- - - print "-----------$r---------\n" if $debug;; - return $r; -+ } -+ -+ ## Copied up from below. -+ sub fill -+ { -+ my ($ip, $xp, @raw) = @_; -+ my @para; -+ my $pp; -+ -+ for $pp (split(/\n\s+/, join("\n",@raw))) { -+ $pp =~ s/\s+/ /g; -+ my $x = wrap($ip, $xp, $pp); -+ push(@para, $x); -+ } -+ -+ # if paragraph_indent is the same as line_indent, -+ # separate paragraphs with blank lines -+ -+ return join ($ip eq $xp ? "\n\n" : "\n", @para); - } - - 1; diff --git a/externals/cowsay/cowsay b/externals/cowsay/cowsay deleted file mode 100755 index 900ca46014..0000000000 --- a/externals/cowsay/cowsay +++ /dev/null @@ -1,187 +0,0 @@ -#%BANGPERL% - -## -## Cowsay 3.03 -## -## This file is part of cowsay. (c) 1999-2000 Tony Monroe. -## - -use Text::Tabs qw(expand); -use Text::Wrap qw(wrap fill $columns); -use File::Basename; -use Getopt::Std; -use Cwd; - -$version = "3.03"; -$progname = basename($0); -$eyes = "oo"; -$tongue = " "; -$cowpath = $ENV{'COWPATH'} || '%PREFIX%/share/cows'; -@message = (); -$thoughts = ""; - -## Yeah, this is rude, I know. But hopefully it gets around a nasty -## little version dependency. - -$Text::Wrap::initial_tab = 8; -$Text::Wrap::subsequent_tab = 8; -$Text::Wrap::tabstop = 8; - -## One of these days, we'll get it ported to Windows. Yeah, right. - -if (($^O eq "MSWin32") or ($^O eq "Windows_NT")) { ## Many perls, eek! - $pathsep = ';'; -} else { - $pathsep = ':'; -} - -%opts = ( - 'e' => 'oo', - 'f' => 'default.cow', - 'n' => 0, - 'T' => ' ', - 'W' => 40, -); - -getopts('bde:f:ghlLnNpstT:wW:y', \%opts); - -&display_usage if $opts{'h'}; -&list_cowfiles if $opts{'l'}; - -$borg = $opts{'b'}; -$dead = $opts{'d'}; -$greedy = $opts{'g'}; -$paranoid = $opts{'p'}; -$stoned = $opts{'s'}; -$tired = $opts{'t'}; -$wired = $opts{'w'}; -$young = $opts{'y'}; -$eyes = substr($opts{'e'}, 0, 2); -$tongue = substr($opts{'T'}, 0, 2); -$the_cow = ""; - -&slurp_input; -$Text::Wrap::columns = $opts{'W'}; -@message = ($opts{'n'} ? expand(@message) : - split("\n", fill("", "", @message))); -&construct_balloon; -&construct_face; -&get_cow; -print @balloon_lines; -print $the_cow; - -sub list_cowfiles { - my $basedir; - my @dirfiles; - chop($basedir = cwd); - for my $d (split(/$pathsep/, $cowpath)) { - print "Cow files in $d:\n"; - opendir(COWDIR, $d) || die "$0: Cannot open $d\n"; - for my $file (readdir COWDIR) { - if ($file =~ s/\.cow$//) { - push(@dirfiles, $file); - } - } - closedir(COWDIR); - print wrap("", "", sort @dirfiles), "\n"; - @dirfiles = (); - chdir($basedir); - } - exit(0); -} - -sub slurp_input { - unless ($ARGV[0]) { - chomp(@message = ); - } else { - &display_usage if $opts{'n'}; - @message = join(' ', @ARGV); - } -} - -sub maxlength { - my ($l, $m); - $m = -1; - for my $i (@_) { - $l = length $i; - $m = $l if ($l > $m); - } - return $m; -} - -sub construct_balloon { - my $max = &maxlength(@message); - my $max2 = $max + 2; ## border space fudge. - my $format = "%s %-${max}s %s\n"; - my @border; ## up-left, up-right, down-left, down-right, left, right - if ($0 =~ /think/i) { - $thoughts = 'o'; - @border = qw[ ( ) ( ) ( ) ]; - } elsif (@message < 2) { - $thoughts = '\\'; - @border = qw[ < > ]; - } else { - $thoughts = '\\'; - if ($V and $V gt v5.6.0) { # Thanks, perldelta. - @border = qw[ / \\ \\ / | | ]; - } else { - @border = qw[ / \ \ / | | ]; - } - } - push(@balloon_lines, - " " . ("_" x $max2) . " \n" , - sprintf($format, $border[0], $message[0], $border[1]), - (@message < 2 ? "" : - map { sprintf($format, $border[4], $_, $border[5]) } - @message[1 .. $#message - 1]), - (@message < 2 ? "" : - sprintf($format, $border[2], $message[$#message], $border[3])), - " " . ("-" x $max2) . " \n" - ); -} - -sub construct_face { - if ($borg) { $eyes = "=="; } - if ($dead) { $eyes = "xx"; $tongue = "U "; } - if ($greedy) { $eyes = "\$\$"; } - if ($paranoid) { $eyes = "@@"; } - if ($stoned) { $eyes = "**"; $tongue = "U "; } - if ($tired) { $eyes = "--"; } - if ($wired) { $eyes = "OO"; } - if ($young) { $eyes = ".."; } -} - -sub get_cow { -## -## Get a cow from the specified cowfile; otherwise use the default cow -## which was defined above in $the_cow. -## - my $f = $opts{'f'}; - my $full = ""; - if ($opts{'f'} =~ m,/,) { - $full = $opts{'f'}; - } else { - for my $d (split(/:/, $cowpath)) { - if (-f "$d/$f") { - $full = "$d/$f"; - last; - } elsif (-f "$d/$f.cow") { - $full = "$d/$f.cow"; - last; - } - } - if ($full eq "") { - die "$progname: Could not find $f cowfile!\n"; - } - } - do $full; - die "$progname: $@\n" if $@; -} - -sub display_usage { - die </dev/null 2>&1; then - echo Found a good perl in $perl - goodperls="$goodperls $perl" - fi -done -echo The following perl executables will run cowsay: -echo $goodperls -echo I recommend the latest stable perl you can find. -set $goodperls -if [ -z "$1" ]; then - echo Ack! You do not have Perl 5 installed correctly! - echo Get thee to CPAN! - exit 1 -fi -usethisperl=$1 -echo I will be using $1 because I know it will work. - -echo Now I need an installation prefix. I will use /usr/local unless -printf "you give me a better idea here: " -if [ -n "$backdoor" ]; then - prefix=$backdoor - printf "%s (specified on command line)\n" $prefix -else - read prefix -fi - -PREFIX=${prefix:-/usr/local} - -echo Okay, time to install this puppy. - -echo s,%BANGPERL%,!$usethisperl,\; > install.pl -echo s,%PREFIX%,$PREFIX,\; >> install.pl -set -x -mkdir -p $PREFIX/bin || (mkdir $PREFIX; mkdir $PREFIX/bin) -$usethisperl -p install.pl cowsay > $PREFIX/bin/cowsay -chmod a+x $PREFIX/bin/cowsay -ln -s cowsay $PREFIX/bin/cowthink -mkdir -p $PREFIX/man/man1 || ($mkdir $PREFIX; mkdir $PREFIX/man; mkdir $PREFIX/man/man1) -$usethisperl -p install.pl cowsay.1 > $PREFIX/man/man1/cowsay.1 -chmod a+r $PREFIX/man/man1/cowsay.1 -ln -s cowsay.1 $PREFIX/man/man1/cowthink.1 -mkdir -p $PREFIX/share/cows || (mkdir $PREFIX; mkdir $PREFIX/share; mkdir $PREFIX/share/cows) -tar -cf - $filelist | (cd $PREFIX/share && tar -xvf -) -set +x - -echo Okay, let us see if the install actually worked. - -if [ ! -f $PREFIX/share/cows/default.cow ]; then - echo The default cow file did not make it across! - echo Ooops, it failed...sorry! - exit 1 -fi - -echo Installation complete! Enjoy the cows! diff --git a/externals/cowsay/pgp_public_key.txt b/externals/cowsay/pgp_public_key.txt deleted file mode 100644 index 135735e6f9..0000000000 --- a/externals/cowsay/pgp_public_key.txt +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: 2.6.2 - -mQCNAzNS+H4AAAEEAMilCcxLMIqMNXhZoeHjbeJGnHoKP0JpKDejz66qBlDwP+du -FvVBHkyNIuU3orKe7l/aXIR8TcpjrxdcmrjjzwuB1aV48V4swT7v9UXvv9YP41Pd -1pXYXNelXHmE0eKSfcnkkFmYTJBXPaPNTJ3rpZpZOEW3SfgrrheqQm6w/6IJAAUR -tBpUb255IE1vbnJvZSA8dG9ueUBub2cubmV0PrQdVG9ueSBNb25yb2UgPHRtb25y -b2VAbm9nLm5ldD6JAJUDBRA2bDA5F6pCbrD/ogkBASOZA/9XtYSpcPjpi62MTEZl -l+wV1svtlUlPvKkDE7FB9bwnMbF1FgGhTe/H4/8qDu20m5eGxMX58Ri7HPXWjjJ/ -CrUWMzGVbg1JBkgh+d9QvEywmR0j4WezaifW1nCbxk0GrW1PjJyGb3kx7vrIF1Km -xt6VwxTgfJzjIioBJFb4EiDyaLQnVG9ueSBNb25yb2UgPHRtb25yb2VAQ1NVQS5C -ZXJrZWxleS5FRFU+ -=WpM4 ------END PGP PUBLIC KEY BLOCK----- diff --git a/externals/mimemailparser/MimeMailParser.class.php b/externals/mimemailparser/MimeMailParser.class.php index 914f50888e..dfd7a2fa11 100644 --- a/externals/mimemailparser/MimeMailParser.class.php +++ b/externals/mimemailparser/MimeMailParser.class.php @@ -31,6 +31,11 @@ class MimeMailParser { */ public $attachment_streams; + /** + * Parts of an email + */ + private $parts = array(); + /** * Inialize some stuff * @return @@ -303,10 +308,10 @@ public function getAttachments() { * @param $part Array */ private function getPartHeaders($part) { - if (isset($part['headers'])) { + if (isset($part['headers']) && $part['headers']) { return $part['headers']; } - return false; + throw new Exception('MimeMailParser::getHeaders() could not parse any email headers.'); } /** diff --git a/externals/pear-figlet/Text/Figlet.php b/externals/pear-figlet/Text/Figlet.php index cc121facb3..167d3e1aa3 100644 --- a/externals/pear-figlet/Text/Figlet.php +++ b/externals/pear-figlet/Text/Figlet.php @@ -140,20 +140,23 @@ function loadFont($filename, $loadgerman = true) if (!$compressed) { /* ZIPed font */ if (fread($fp, 2) == 'PK') { - if (!function_exists('zip_open')) { - return self::raiseError('Cannot load ZIP compressed fonts since' - . ' ZIP PHP extension is not available.', - 5); - } - fclose($fp); - if (!($fp = zip_open($filename))) { - return self::raiseError('Cannot open figlet font file ' . $filename, 2); + $zip = new ZipArchive(); + + $zip_flags = 0; + if(defined('ZipArchive::RDONLY')) { + $zip_flags = ZipArchive::RDONLY; // Flag available since PHP 7.4, unnecessary before + } + + $open_result = $zip->open($filename, $zip_flags); + if ($open_result !== true) { + return self::raiseError('Cannot open figlet font file ' . + $filename . ', got error: ' . $open_result, 2); } - $name = zip_entry_name(zip_read($fp)); - zip_close($fp); + $name = $zip->getNameIndex(0); + $zip->close(); if (!($fp = fopen('zip://' . realpath($filename) . '#' . $name, 'rb'))) { return self::raiseError('Cannot open figlet font file ' . $filename, 2); @@ -231,7 +234,7 @@ function loadFont($filename, $loadgerman = true) $i = hexdec(substr($i, 2)); } else { // If octal - if ($i{0} === '0' && $i !== '0' || substr($i, 0, 2) == '-0') { + if ($i[0] === '0' && $i !== '0' || substr($i, 0, 2) == '-0') { $i = octdec($i); } } @@ -274,7 +277,7 @@ function lineEcho($str, $inhtml = false) $lt = hexdec(substr($str, $i+2, 4)); $i += 5; } else { - $lt = ord($str{$i}); + $lt = ord($str[$i]); } $hb = preg_quote($this->hardblank, '/'); diff --git a/externals/phpmailer/class.smtp.php b/externals/phpmailer/class.smtp.php index c2ca1cb3b8..1b238eec95 100644 --- a/externals/phpmailer/class.smtp.php +++ b/externals/phpmailer/class.smtp.php @@ -1,814 +1,814 @@ -smtp_conn = 0; - $this->error = null; - $this->helo_rply = null; - - $this->do_debug = 0; - } - - ///////////////////////////////////////////////// - // CONNECTION FUNCTIONS - ///////////////////////////////////////////////// - - /** - * Connect to the server specified on the port specified. - * If the port is not specified use the default SMTP_PORT. - * If tval is specified then a connection will try and be - * established with the server for that number of seconds. - * If tval is not specified the default is 30 seconds to - * try on the connection. - * - * SMTP CODE SUCCESS: 220 - * SMTP CODE FAILURE: 421 - * @access public - * @return bool - */ - public function Connect($host, $port = 0, $tval = 30) { - // set the error val to null so there is no confusion - $this->error = null; - - // make sure we are __not__ connected - if($this->connected()) { - // already connected, generate error - $this->error = array("error" => "Already connected to a server"); - return false; - } - - if(empty($port)) { - $port = $this->SMTP_PORT; - } - - // connect to the smtp server - $this->smtp_conn = @fsockopen($host, // the host of the server - $port, // the port to use - $errno, // error number if any - $errstr, // error message if any - $tval); // give up after ? secs - // verify we connected properly - if(empty($this->smtp_conn)) { - $this->error = array("error" => "Failed to connect to server", - "errno" => $errno, - "errstr" => $errstr); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '
'; - } - return false; - } - - // SMTP server can take longer to respond, give longer timeout for first read - // Windows does not have support for this timeout function - if(substr(PHP_OS, 0, 3) != "WIN") - socket_set_timeout($this->smtp_conn, $tval, 0); - - // get any announcement - $announce = $this->get_lines(); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $announce . $this->CRLF . '
'; - } - - return true; - } - - /** - * Initiate a TLS communication with the server. - * - * SMTP CODE 220 Ready to start TLS - * SMTP CODE 501 Syntax error (no parameters allowed) - * SMTP CODE 454 TLS not available due to temporary reason - * @access public - * @return bool success - */ - public function StartTLS() { - $this->error = null; # to avoid confusion - - if(!$this->connected()) { - $this->error = array("error" => "Called StartTLS() without being connected"); - return false; - } - - fputs($this->smtp_conn,"STARTTLS" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; - } - - if($code != 220) { - $this->error = - array("error" => "STARTTLS not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - - // Begin encrypted connection - if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { - return false; - } - - return true; - } - - /** - * Performs SMTP authentication. Must be run after running the - * Hello() method. Returns true if successfully authenticated. - * @access public - * @return bool - */ - public function Authenticate($username, $password) { - // Start authentication - fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 334) { - $this->error = - array("error" => "AUTH not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - - // Send encoded username - fputs($this->smtp_conn, base64_encode($username) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 334) { - $this->error = - array("error" => "Username not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - - // Send encoded password - fputs($this->smtp_conn, base64_encode($password) . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($code != 235) { - $this->error = - array("error" => "Password not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - - return true; - } - - /** - * Returns true if connected to a server otherwise false - * @access public - * @return bool - */ - public function Connected() { - if(!empty($this->smtp_conn)) { - $sock_status = socket_get_status($this->smtp_conn); - if($sock_status["eof"]) { - // the socket is valid but we are not connected - if($this->do_debug >= 1) { - echo "SMTP -> NOTICE:" . $this->CRLF . "EOF caught while checking if connected"; - } - $this->Close(); - return false; - } - return true; // everything looks good - } - return false; - } - - /** - * Closes the socket and cleans up the state of the class. - * It is not considered good to use this function without - * first trying to use QUIT. - * @access public - * @return void - */ - public function Close() { - $this->error = null; // so there is no confusion - $this->helo_rply = null; - if(!empty($this->smtp_conn)) { - // close the connection and cleanup - fclose($this->smtp_conn); - $this->smtp_conn = 0; - } - } - - ///////////////////////////////////////////////// - // SMTP COMMANDS - ///////////////////////////////////////////////// - - /** - * Issues a data command and sends the msg_data to the server - * finializing the mail transaction. $msg_data is the message - * that is to be send with the headers. Each header needs to be - * on a single line followed by a with the message headers - * and the message body being seperated by and additional . - * - * Implements rfc 821: DATA - * - * SMTP CODE INTERMEDIATE: 354 - * [data] - * . - * SMTP CODE SUCCESS: 250 - * SMTP CODE FAILURE: 552,554,451,452 - * SMTP CODE FAILURE: 451,554 - * SMTP CODE ERROR : 500,501,503,421 - * @access public - * @return bool - */ - public function Data($msg_data) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Data() without being connected"); - return false; - } - - fputs($this->smtp_conn,"DATA" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; - } - - if($code != 354) { - $this->error = - array("error" => "DATA command not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - - /* the server is ready to accept data! - * according to rfc 821 we should not send more than 1000 - * including the CRLF - * characters on a single line so we will break the data up - * into lines by \r and/or \n then if needed we will break - * each of those into smaller lines to fit within the limit. - * in addition we will be looking for lines that start with - * a period '.' and append and additional period '.' to that - * line. NOTE: this does not count towards limit. - */ - - // normalize the line breaks so we know the explode works - $msg_data = str_replace("\r\n","\n",$msg_data); - $msg_data = str_replace("\r","\n",$msg_data); - $lines = explode("\n",$msg_data); - - /* we need to find a good way to determine is headers are - * in the msg_data or if it is a straight msg body - * currently I am assuming rfc 822 definitions of msg headers - * and if the first field of the first line (':' sperated) - * does not contain a space then it _should_ be a header - * and we can process all lines before a blank "" line as - * headers. - */ - - $field = substr($lines[0],0,strpos($lines[0],":")); - $in_headers = false; - if(!empty($field) && !strstr($field," ")) { - $in_headers = true; - } - - $max_line_length = 998; // used below; set here for ease in change - - while(list(,$line) = @each($lines)) { - $lines_out = null; - if($line == "" && $in_headers) { - $in_headers = false; - } - // ok we need to break this line up into several smaller lines - while(strlen($line) > $max_line_length) { - $pos = strrpos(substr($line,0,$max_line_length)," "); - - // Patch to fix DOS attack - if(!$pos) { - $pos = $max_line_length - 1; - $lines_out[] = substr($line,0,$pos); - $line = substr($line,$pos); - } else { - $lines_out[] = substr($line,0,$pos); - $line = substr($line,$pos + 1); - } - - /* if processing headers add a LWSP-char to the front of new line - * rfc 822 on long msg headers - */ - if($in_headers) { - $line = "\t" . $line; - } - } - $lines_out[] = $line; - - // send the lines to the server - while(list(,$line_out) = @each($lines_out)) { - if(strlen($line_out) > 0) - { - if(substr($line_out, 0, 1) == ".") { - $line_out = "." . $line_out; - } - } - fputs($this->smtp_conn,$line_out . $this->CRLF); - } - } - - // message data has been sent - fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; - } - - if($code != 250) { - $this->error = - array("error" => "DATA not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - return true; - } - - /** - * Sends the HELO command to the smtp server. - * This makes sure that we and the server are in - * the same known state. - * - * Implements from rfc 821: HELO - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE ERROR : 500, 501, 504, 421 - * @access public - * @return bool - */ - public function Hello($host = '') { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Hello() without being connected"); - return false; - } - - // if hostname for HELO was not specified send default - if(empty($host)) { - // determine appropriate default to send to server - $host = "localhost"; - } - - // Send extended hello first (RFC 2821) - if(!$this->SendHello("EHLO", $host)) { - if(!$this->SendHello("HELO", $host)) { - return false; - } - } - - return true; - } - - /** - * Sends a HELO/EHLO command. - * @access private - * @return bool - */ - private function SendHello($hello, $host) { - fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER: " . $rply . $this->CRLF . '
'; - } - - if($code != 250) { - $this->error = - array("error" => $hello . " not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - - $this->helo_rply = $rply; - - return true; - } - - /** - * Starts a mail transaction from the email address specified in - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more Recipient - * commands may be called followed by a Data command. - * - * Implements rfc 821: MAIL FROM: - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552,451,452 - * SMTP CODE SUCCESS: 500,501,421 - * @access public - * @return bool - */ - public function Mail($from) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Mail() without being connected"); - return false; - } - - $useVerp = ($this->do_verp ? "XVERP" : ""); - fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; - } - - if($code != 250) { - $this->error = - array("error" => "MAIL not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - return true; - } - - /** - * Sends the quit command to the server and then closes the socket - * if there is no error or the $close_on_error argument is true. - * - * Implements from rfc 821: QUIT - * - * SMTP CODE SUCCESS: 221 - * SMTP CODE ERROR : 500 - * @access public - * @return bool - */ - public function Quit($close_on_error = true) { - $this->error = null; // so there is no confusion - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Quit() without being connected"); - return false; - } - - // send the quit command to the server - fputs($this->smtp_conn,"quit" . $this->CRLF); - - // get any good-bye messages - $byemsg = $this->get_lines(); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $byemsg . $this->CRLF . '
'; - } - - $rval = true; - $e = null; - - $code = substr($byemsg,0,3); - if($code != 221) { - // use e as a tmp var cause Close will overwrite $this->error - $e = array("error" => "SMTP server rejected quit command", - "smtp_code" => $code, - "smtp_rply" => substr($byemsg,4)); - $rval = false; - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $e["error"] . ": " . $byemsg . $this->CRLF . '
'; - } - } - - if(empty($e) || $close_on_error) { - $this->Close(); - } - - return $rval; - } - - /** - * Sends the command RCPT to the SMTP server with the TO: argument of $to. - * Returns true if the recipient was accepted false if it was rejected. - * - * Implements from rfc 821: RCPT TO: - * - * SMTP CODE SUCCESS: 250,251 - * SMTP CODE FAILURE: 550,551,552,553,450,451,452 - * SMTP CODE ERROR : 500,501,503,421 - * @access public - * @return bool - */ - public function Recipient($to) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Recipient() without being connected"); - return false; - } - - fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; - } - - if($code != 250 && $code != 251) { - $this->error = - array("error" => "RCPT not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - return true; - } - - /** - * Sends the RSET command to abort and transaction that is - * currently in progress. Returns true if successful false - * otherwise. - * - * Implements rfc 821: RSET - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE ERROR : 500,501,504,421 - * @access public - * @return bool - */ - public function Reset() { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called Reset() without being connected"); - return false; - } - - fputs($this->smtp_conn,"RSET" . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; - } - - if($code != 250) { - $this->error = - array("error" => "RSET failed", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - - return true; - } - - /** - * Starts a mail transaction from the email address specified in - * $from. Returns true if successful or false otherwise. If True - * the mail transaction is started and then one or more Recipient - * commands may be called followed by a Data command. This command - * will send the message to the users terminal if they are logged - * in and send them an email. - * - * Implements rfc 821: SAML FROM: - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE SUCCESS: 552,451,452 - * SMTP CODE SUCCESS: 500,501,502,421 - * @access public - * @return bool - */ - public function SendAndMail($from) { - $this->error = null; // so no confusion is caused - - if(!$this->connected()) { - $this->error = array( - "error" => "Called SendAndMail() without being connected"); - return false; - } - - fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF); - - $rply = $this->get_lines(); - $code = substr($rply,0,3); - - if($this->do_debug >= 2) { - echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; - } - - if($code != 250) { - $this->error = - array("error" => "SAML not accepted from server", - "smtp_code" => $code, - "smtp_msg" => substr($rply,4)); - if($this->do_debug >= 1) { - echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; - } - return false; - } - return true; - } - - /** - * This is an optional command for SMTP that this class does not - * support. This method is here to make the RFC821 Definition - * complete for this class and __may__ be implimented in the future - * - * Implements from rfc 821: TURN - * - * SMTP CODE SUCCESS: 250 - * SMTP CODE FAILURE: 502 - * SMTP CODE ERROR : 500, 503 - * @access public - * @return bool - */ - public function Turn() { - $this->error = array("error" => "This method, TURN, of the SMTP ". - "is not implemented"); - if($this->do_debug >= 1) { - echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF . '
'; - } - return false; - } - - /** - * Get the current error - * @access public - * @return array - */ - public function getError() { - return $this->error; - } - - ///////////////////////////////////////////////// - // INTERNAL FUNCTIONS - ///////////////////////////////////////////////// - - /** - * Read in as many lines as possible - * either before eof or socket timeout occurs on the operation. - * With SMTP we can tell if we have more lines to read if the - * 4th character is '-' symbol. If it is a space then we don't - * need to read anything else. - * @access private - * @return string - */ - private function get_lines() { - $data = ""; - while($str = @fgets($this->smtp_conn,515)) { - if($this->do_debug >= 4) { - echo "SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '
'; - echo "SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '
'; - } - $data .= $str; - if($this->do_debug >= 4) { - echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '
'; - } - // if 4th character is a space, we are done reading, break the loop - if(substr($str,3,1) == " ") { break; } - } - return $data; - } - -} - +smtp_conn = 0; + $this->error = null; + $this->helo_rply = null; + + $this->do_debug = 0; + } + + ///////////////////////////////////////////////// + // CONNECTION FUNCTIONS + ///////////////////////////////////////////////// + + /** + * Connect to the server specified on the port specified. + * If the port is not specified use the default SMTP_PORT. + * If tval is specified then a connection will try and be + * established with the server for that number of seconds. + * If tval is not specified the default is 30 seconds to + * try on the connection. + * + * SMTP CODE SUCCESS: 220 + * SMTP CODE FAILURE: 421 + * @access public + * @return bool + */ + public function Connect($host, $port = 0, $tval = 30) { + // set the error val to null so there is no confusion + $this->error = null; + + // make sure we are __not__ connected + if($this->connected()) { + // already connected, generate error + $this->error = array("error" => "Already connected to a server"); + return false; + } + + if(empty($port)) { + $port = $this->SMTP_PORT; + } + + // connect to the smtp server + $this->smtp_conn = @fsockopen($host, // the host of the server + $port, // the port to use + $errno, // error number if any + $errstr, // error message if any + $tval); // give up after ? secs + // verify we connected properly + if(empty($this->smtp_conn)) { + $this->error = array("error" => "Failed to connect to server", + "errno" => $errno, + "errstr" => $errstr); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": $errstr ($errno)" . $this->CRLF . '
'; + } + return false; + } + + // SMTP server can take longer to respond, give longer timeout for first read + // Windows does not have support for this timeout function + if(substr(PHP_OS, 0, 3) != "WIN") + socket_set_timeout($this->smtp_conn, $tval, 0); + + // get any announcement + $announce = $this->get_lines(); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $announce . $this->CRLF . '
'; + } + + return true; + } + + /** + * Initiate a TLS communication with the server. + * + * SMTP CODE 220 Ready to start TLS + * SMTP CODE 501 Syntax error (no parameters allowed) + * SMTP CODE 454 TLS not available due to temporary reason + * @access public + * @return bool success + */ + public function StartTLS() { + $this->error = null; # to avoid confusion + + if(!$this->connected()) { + $this->error = array("error" => "Called StartTLS() without being connected"); + return false; + } + + fputs($this->smtp_conn,"STARTTLS" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 220) { + $this->error = + array("error" => "STARTTLS not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + // Begin encrypted connection + if(!stream_socket_enable_crypto($this->smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { + return false; + } + + return true; + } + + /** + * Performs SMTP authentication. Must be run after running the + * Hello() method. Returns true if successfully authenticated. + * @access public + * @return bool + */ + public function Authenticate($username, $password) { + // Start authentication + fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 334) { + $this->error = + array("error" => "AUTH not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + // Send encoded username + fputs($this->smtp_conn, base64_encode($username) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 334) { + $this->error = + array("error" => "Username not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + // Send encoded password + fputs($this->smtp_conn, base64_encode($password) . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($code != 235) { + $this->error = + array("error" => "Password not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + return true; + } + + /** + * Returns true if connected to a server otherwise false + * @access public + * @return bool + */ + public function Connected() { + if(!empty($this->smtp_conn)) { + $sock_status = socket_get_status($this->smtp_conn); + if($sock_status["eof"]) { + // the socket is valid but we are not connected + if($this->do_debug >= 1) { + echo "SMTP -> NOTICE:" . $this->CRLF . "EOF caught while checking if connected"; + } + $this->Close(); + return false; + } + return true; // everything looks good + } + return false; + } + + /** + * Closes the socket and cleans up the state of the class. + * It is not considered good to use this function without + * first trying to use QUIT. + * @access public + * @return void + */ + public function Close() { + $this->error = null; // so there is no confusion + $this->helo_rply = null; + if(!empty($this->smtp_conn)) { + // close the connection and cleanup + fclose($this->smtp_conn); + $this->smtp_conn = 0; + } + } + + ///////////////////////////////////////////////// + // SMTP COMMANDS + ///////////////////////////////////////////////// + + /** + * Issues a data command and sends the msg_data to the server + * finializing the mail transaction. $msg_data is the message + * that is to be send with the headers. Each header needs to be + * on a single line followed by a with the message headers + * and the message body being seperated by and additional . + * + * Implements rfc 821: DATA + * + * SMTP CODE INTERMEDIATE: 354 + * [data] + * . + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 552,554,451,452 + * SMTP CODE FAILURE: 451,554 + * SMTP CODE ERROR : 500,501,503,421 + * @access public + * @return bool + */ + public function Data($msg_data) { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Data() without being connected"); + return false; + } + + fputs($this->smtp_conn,"DATA" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 354) { + $this->error = + array("error" => "DATA command not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + /* the server is ready to accept data! + * according to rfc 821 we should not send more than 1000 + * including the CRLF + * characters on a single line so we will break the data up + * into lines by \r and/or \n then if needed we will break + * each of those into smaller lines to fit within the limit. + * in addition we will be looking for lines that start with + * a period '.' and append and additional period '.' to that + * line. NOTE: this does not count towards limit. + */ + + // normalize the line breaks so we know the explode works + $msg_data = str_replace("\r\n","\n",$msg_data); + $msg_data = str_replace("\r","\n",$msg_data); + $lines = explode("\n",$msg_data); + + /* we need to find a good way to determine is headers are + * in the msg_data or if it is a straight msg body + * currently I am assuming rfc 822 definitions of msg headers + * and if the first field of the first line (':' sperated) + * does not contain a space then it _should_ be a header + * and we can process all lines before a blank "" line as + * headers. + */ + + $field = substr($lines[0],0,strpos($lines[0],":")); + $in_headers = false; + if(!empty($field) && !strstr($field," ")) { + $in_headers = true; + } + + $max_line_length = 998; // used below; set here for ease in change + + foreach($lines as $line) { + $lines_out = null; + if($line == "" && $in_headers) { + $in_headers = false; + } + // ok we need to break this line up into several smaller lines + while(strlen($line) > $max_line_length) { + $pos = strrpos(substr($line,0,$max_line_length)," "); + + // Patch to fix DOS attack + if(!$pos) { + $pos = $max_line_length - 1; + $lines_out[] = substr($line,0,$pos); + $line = substr($line,$pos); + } else { + $lines_out[] = substr($line,0,$pos); + $line = substr($line,$pos + 1); + } + + /* if processing headers add a LWSP-char to the front of new line + * rfc 822 on long msg headers + */ + if($in_headers) { + $line = "\t" . $line; + } + } + $lines_out[] = $line; + + // send the lines to the server + foreach($lines_out as $line_out) { + if(strlen($line_out) > 0) + { + if(substr($line_out, 0, 1) == ".") { + $line_out = "." . $line_out; + } + } + fputs($this->smtp_conn,$line_out . $this->CRLF); + } + } + + // message data has been sent + fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 250) { + $this->error = + array("error" => "DATA not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + return true; + } + + /** + * Sends the HELO command to the smtp server. + * This makes sure that we and the server are in + * the same known state. + * + * Implements from rfc 821: HELO + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500, 501, 504, 421 + * @access public + * @return bool + */ + public function Hello($host = '') { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Hello() without being connected"); + return false; + } + + // if hostname for HELO was not specified send default + if(empty($host)) { + // determine appropriate default to send to server + $host = "localhost"; + } + + // Send extended hello first (RFC 2821) + if(!$this->SendHello("EHLO", $host)) { + if(!$this->SendHello("HELO", $host)) { + return false; + } + } + + return true; + } + + /** + * Sends a HELO/EHLO command. + * @access private + * @return bool + */ + private function SendHello($hello, $host) { + fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER: " . $rply . $this->CRLF . '
'; + } + + if($code != 250) { + $this->error = + array("error" => $hello . " not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + $this->helo_rply = $rply; + + return true; + } + + /** + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. + * + * Implements rfc 821: MAIL FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,421 + * @access public + * @return bool + */ + public function Mail($from) { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Mail() without being connected"); + return false; + } + + $useVerp = ($this->do_verp ? "XVERP" : ""); + fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 250) { + $this->error = + array("error" => "MAIL not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + return true; + } + + /** + * Sends the quit command to the server and then closes the socket + * if there is no error or the $close_on_error argument is true. + * + * Implements from rfc 821: QUIT + * + * SMTP CODE SUCCESS: 221 + * SMTP CODE ERROR : 500 + * @access public + * @return bool + */ + public function Quit($close_on_error = true) { + $this->error = null; // so there is no confusion + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Quit() without being connected"); + return false; + } + + // send the quit command to the server + fputs($this->smtp_conn,"quit" . $this->CRLF); + + // get any good-bye messages + $byemsg = $this->get_lines(); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $byemsg . $this->CRLF . '
'; + } + + $rval = true; + $e = null; + + $code = substr($byemsg,0,3); + if($code != 221) { + // use e as a tmp var cause Close will overwrite $this->error + $e = array("error" => "SMTP server rejected quit command", + "smtp_code" => $code, + "smtp_rply" => substr($byemsg,4)); + $rval = false; + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $e["error"] . ": " . $byemsg . $this->CRLF . '
'; + } + } + + if(empty($e) || $close_on_error) { + $this->Close(); + } + + return $rval; + } + + /** + * Sends the command RCPT to the SMTP server with the TO: argument of $to. + * Returns true if the recipient was accepted false if it was rejected. + * + * Implements from rfc 821: RCPT TO: + * + * SMTP CODE SUCCESS: 250,251 + * SMTP CODE FAILURE: 550,551,552,553,450,451,452 + * SMTP CODE ERROR : 500,501,503,421 + * @access public + * @return bool + */ + public function Recipient($to) { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Recipient() without being connected"); + return false; + } + + fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 250 && $code != 251) { + $this->error = + array("error" => "RCPT not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + return true; + } + + /** + * Sends the RSET command to abort and transaction that is + * currently in progress. Returns true if successful false + * otherwise. + * + * Implements rfc 821: RSET + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500,501,504,421 + * @access public + * @return bool + */ + public function Reset() { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Reset() without being connected"); + return false; + } + + fputs($this->smtp_conn,"RSET" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 250) { + $this->error = + array("error" => "RSET failed", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + + return true; + } + + /** + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. This command + * will send the message to the users terminal if they are logged + * in and send them an email. + * + * Implements rfc 821: SAML FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,502,421 + * @access public + * @return bool + */ + public function SendAndMail($from) { + $this->error = null; // so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called SendAndMail() without being connected"); + return false; + } + + fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $rply . $this->CRLF . '
'; + } + + if($code != 250) { + $this->error = + array("error" => "SAML not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . ": " . $rply . $this->CRLF . '
'; + } + return false; + } + return true; + } + + /** + * This is an optional command for SMTP that this class does not + * support. This method is here to make the RFC821 Definition + * complete for this class and __may__ be implimented in the future + * + * Implements from rfc 821: TURN + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 502 + * SMTP CODE ERROR : 500, 503 + * @access public + * @return bool + */ + public function Turn() { + $this->error = array("error" => "This method, TURN, of the SMTP ". + "is not implemented"); + if($this->do_debug >= 1) { + echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF . '
'; + } + return false; + } + + /** + * Get the current error + * @access public + * @return array + */ + public function getError() { + return $this->error; + } + + ///////////////////////////////////////////////// + // INTERNAL FUNCTIONS + ///////////////////////////////////////////////// + + /** + * Read in as many lines as possible + * either before eof or socket timeout occurs on the operation. + * With SMTP we can tell if we have more lines to read if the + * 4th character is '-' symbol. If it is a space then we don't + * need to read anything else. + * @access private + * @return string + */ + private function get_lines() { + $data = ""; + while($str = @fgets($this->smtp_conn,515)) { + if($this->do_debug >= 4) { + echo "SMTP -> get_lines(): \$data was \"$data\"" . $this->CRLF . '
'; + echo "SMTP -> get_lines(): \$str is \"$str\"" . $this->CRLF . '
'; + } + $data .= $str; + if($this->do_debug >= 4) { + echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF . '
'; + } + // if 4th character is a space, we are done reading, break the loop + if(substr($str,3,1) == " ") { break; } + } + return $data; + } + +} + ?> \ No newline at end of file diff --git a/resources/celerity/map.php b/resources/celerity/map.php index c29df70b1e..300a2a59db 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,19 +7,20 @@ */ return array( 'names' => array( - 'conpherence.pkg.css' => '0e3cf785', + 'conpherence.pkg.css' => '2f25eb4f', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => 'b816811e', - 'core.pkg.js' => 'd2de90d9', + 'core.pkg.css' => 'cf2d74fd', + 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', - 'differential.pkg.css' => 'ffb69e3d', - 'differential.pkg.js' => 'c60bec1b', - 'diffusion.pkg.css' => '42c75c37', + 'differential.pkg.css' => '6d3700f0', + 'differential.pkg.js' => '46fcb3af', + 'diffusion.pkg.css' => '354279ea', 'diffusion.pkg.js' => '78c9885d', 'maniphest.pkg.css' => '35995d6d', 'maniphest.pkg.js' => 'c9308721', 'rsrc/audio/basic/alert.mp3' => '17889334', 'rsrc/audio/basic/bing.mp3' => 'a817a0c3', + 'rsrc/audio/basic/coin.mp3' => '44ef365f', 'rsrc/audio/basic/pock.mp3' => '0fa843d0', 'rsrc/audio/basic/tap.mp3' => '02d16994', 'rsrc/audio/basic/ting.mp3' => 'a6b6540e', @@ -38,22 +39,21 @@ 'rsrc/css/aphront/typeahead.css' => '8779483d', 'rsrc/css/application/almanac/almanac.css' => '2e050f4f', 'rsrc/css/application/auth/auth.css' => 'c2f23d74', - 'rsrc/css/application/base/main-menu-view.css' => 'bcec20f0', + 'rsrc/css/application/base/main-menu-view.css' => '33820efe', 'rsrc/css/application/base/notification-menu.css' => '4df1ee30', 'rsrc/css/application/base/phui-theme.css' => '35883b37', - 'rsrc/css/application/base/standard-page-view.css' => 'a374f94c', - 'rsrc/css/application/chatlog/chatlog.css' => 'abdc76ee', + 'rsrc/css/application/base/standard-page-view.css' => 'e08c7462', 'rsrc/css/application/conduit/conduit-api.css' => 'ce2cfc41', 'rsrc/css/application/config/config-options.css' => '16c920ae', 'rsrc/css/application/config/config-template.css' => '20babf50', - 'rsrc/css/application/config/setup-issue.css' => '5eed85b2', + 'rsrc/css/application/config/setup-issue.css' => '93231115', 'rsrc/css/application/config/unhandled-exception.css' => '9ecfc00d', 'rsrc/css/application/conpherence/color.css' => 'b17746b0', 'rsrc/css/application/conpherence/durable-column.css' => '2d57072b', 'rsrc/css/application/conpherence/header-pane.css' => 'c9a3db8e', 'rsrc/css/application/conpherence/menu.css' => '67f4680d', - 'rsrc/css/application/conpherence/message-pane.css' => 'd244db1e', - 'rsrc/css/application/conpherence/notification.css' => '6a3d4e58', + 'rsrc/css/application/conpherence/message-pane.css' => '50b1345e', + 'rsrc/css/application/conpherence/notification.css' => '85c48def', 'rsrc/css/application/conpherence/participant-pane.css' => '69e0058a', 'rsrc/css/application/conpherence/transaction.css' => '3a3f5e7e', 'rsrc/css/application/contentsource/content-source-view.css' => 'cdf0d579', @@ -63,21 +63,21 @@ 'rsrc/css/application/diff/diff-tree-view.css' => 'e2d3e222', 'rsrc/css/application/diff/inline-comment-summary.css' => '81eb368d', 'rsrc/css/application/differential/add-comment.css' => '7e5900d9', - 'rsrc/css/application/differential/changeset-view.css' => '60c3d405', + 'rsrc/css/application/differential/changeset-view.css' => '1b0476bc', 'rsrc/css/application/differential/core.css' => '7300a73e', 'rsrc/css/application/differential/phui-inline-comment.css' => '9863a85e', 'rsrc/css/application/differential/revision-comment.css' => '7dbc8d1d', 'rsrc/css/application/differential/revision-history.css' => '237a2979', 'rsrc/css/application/differential/revision-list.css' => '93d2df7d', 'rsrc/css/application/differential/table-of-contents.css' => 'bba788b9', - 'rsrc/css/application/diffusion/diffusion-icons.css' => '23b31a1b', + 'rsrc/css/application/diffusion/diffusion-icons.css' => 'e812add2', 'rsrc/css/application/diffusion/diffusion-readme.css' => 'b68a76e4', 'rsrc/css/application/diffusion/diffusion-repository.css' => 'b89e8c6c', 'rsrc/css/application/diffusion/diffusion.css' => 'e46232d6', 'rsrc/css/application/feed/feed.css' => 'd8b6e3f8', 'rsrc/css/application/files/global-drag-and-drop.css' => '1d2713a4', 'rsrc/css/application/flag/flag.css' => '2b77be8d', - 'rsrc/css/application/harbormaster/harbormaster.css' => '8dfe16b2', + 'rsrc/css/application/harbormaster/harbormaster.css' => '9346e08b', 'rsrc/css/application/herald/herald-test.css' => '7e7bbdae', 'rsrc/css/application/herald/herald.css' => '648d39e2', 'rsrc/css/application/maniphest/report.css' => '3d53188b', @@ -100,7 +100,7 @@ 'rsrc/css/application/policy/policy-edit.css' => '8794e2ed', 'rsrc/css/application/policy/policy-transaction-detail.css' => 'c02b8384', 'rsrc/css/application/policy/policy.css' => 'ceb56a08', - 'rsrc/css/application/ponder/ponder-view.css' => '05a09d0a', + 'rsrc/css/application/ponder/ponder-view.css' => 'b04bbaff', 'rsrc/css/application/project/project-card-view.css' => 'a9f2c2dd', 'rsrc/css/application/project/project-triggers.css' => 'cd9c8bb9', 'rsrc/css/application/project/project-view.css' => '567858b3', @@ -109,8 +109,8 @@ 'rsrc/css/application/slowvote/slowvote.css' => '1694baed', 'rsrc/css/application/tokens/tokens.css' => 'ce5a50bd', 'rsrc/css/application/uiexample/example.css' => 'b4795059', - 'rsrc/css/core/core.css' => 'b3ebd90d', - 'rsrc/css/core/remarkup.css' => '5baa3bd9', + 'rsrc/css/core/core.css' => '531ad849', + 'rsrc/css/core/remarkup.css' => 'd91c2ee8', 'rsrc/css/core/syntax.css' => '548567f6', 'rsrc/css/core/z-index.css' => 'ac3bfcd4', 'rsrc/css/diviner/diviner-shared.css' => '4bd263b0', @@ -121,10 +121,10 @@ 'rsrc/css/fuel/fuel-handle-list.css' => '2c4cbeca', 'rsrc/css/fuel/fuel-map.css' => 'd6e31510', 'rsrc/css/fuel/fuel-menu.css' => '21f5d199', - 'rsrc/css/layout/phabricator-source-code-view.css' => '03d7ac28', + 'rsrc/css/layout/phabricator-source-code-view.css' => 'e382316a', 'rsrc/css/phui/button/phui-button-bar.css' => 'a4aa75c4', 'rsrc/css/phui/button/phui-button-simple.css' => '1ff278aa', - 'rsrc/css/phui/button/phui-button.css' => 'ea704902', + 'rsrc/css/phui/button/phui-button.css' => '55025b10', 'rsrc/css/phui/calendar/phui-calendar-day.css' => '9597d706', 'rsrc/css/phui/calendar/phui-calendar-list.css' => 'ccd7e4e2', 'rsrc/css/phui/calendar/phui-calendar-month.css' => 'cb758c42', @@ -133,18 +133,18 @@ 'rsrc/css/phui/object-item/phui-oi-color.css' => 'b517bfa0', 'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => 'da15d3dc', 'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '490e2e2e', - 'rsrc/css/phui/object-item/phui-oi-list-view.css' => 'af98a277', - 'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => '6a30fa46', + 'rsrc/css/phui/object-item/phui-oi-list-view.css' => '9275ff55', + 'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => '9b03a61f', 'rsrc/css/phui/phui-action-list.css' => '1b0085b2', 'rsrc/css/phui/phui-action-panel.css' => '6c386cbf', 'rsrc/css/phui/phui-badge.css' => '666e25ad', - 'rsrc/css/phui/phui-basic-nav-view.css' => '56ebd66d', + 'rsrc/css/phui/phui-basic-nav-view.css' => 'a5693cf0', 'rsrc/css/phui/phui-big-info-view.css' => '362ad37b', 'rsrc/css/phui/phui-box.css' => '5ed3b8cb', 'rsrc/css/phui/phui-bulk-editor.css' => '374d5e30', 'rsrc/css/phui/phui-chart.css' => '14df9ae3', 'rsrc/css/phui/phui-cms.css' => '8c05c41e', - 'rsrc/css/phui/phui-comment-form.css' => '68a2d99a', + 'rsrc/css/phui/phui-comment-form.css' => '3c6679a3', 'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0', 'rsrc/css/phui/phui-crumbs-view.css' => '614f43cf', 'rsrc/css/phui/phui-curtain-object-ref-view.css' => '51d93266', @@ -154,39 +154,39 @@ 'rsrc/css/phui/phui-document.css' => '52b748a5', 'rsrc/css/phui/phui-feed-story.css' => 'a0c05029', 'rsrc/css/phui/phui-fontkit.css' => '1ec937e5', - 'rsrc/css/phui/phui-form-view.css' => '01b796c0', + 'rsrc/css/phui/phui-form-view.css' => '57edecb7', 'rsrc/css/phui/phui-form.css' => '1f177cb7', 'rsrc/css/phui/phui-formation-view.css' => 'd2dec8ed', 'rsrc/css/phui/phui-head-thing.css' => 'd7f293df', 'rsrc/css/phui/phui-header-view.css' => '36c86a58', - 'rsrc/css/phui/phui-hovercard.css' => '6ca90fa0', - 'rsrc/css/phui/phui-icon-set-selector.css' => '7aa5f3ec', - 'rsrc/css/phui/phui-icon.css' => '4cbc684a', + 'rsrc/css/phui/phui-hovercard.css' => '39fd2e14', + 'rsrc/css/phui/phui-icon-set-selector.css' => '19e0253b', + 'rsrc/css/phui/phui-icon.css' => '084ac612', 'rsrc/css/phui/phui-image-mask.css' => '62c7f4d2', 'rsrc/css/phui/phui-info-view.css' => 'a10a909b', 'rsrc/css/phui/phui-invisible-character-view.css' => 'c694c4a4', 'rsrc/css/phui/phui-left-right.css' => '68513c34', 'rsrc/css/phui/phui-lightbox.css' => '4ebf22da', - 'rsrc/css/phui/phui-list.css' => '0c04affd', + 'rsrc/css/phui/phui-list.css' => 'ccf73664', 'rsrc/css/phui/phui-object-box.css' => 'b8d7eea0', 'rsrc/css/phui/phui-pager.css' => 'd022c7ad', 'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8', 'rsrc/css/phui/phui-policy-section-view.css' => '139fdc64', - 'rsrc/css/phui/phui-property-list-view.css' => '5adf7078', + 'rsrc/css/phui/phui-property-list-view.css' => '9a155095', 'rsrc/css/phui/phui-remarkup-preview.css' => '91767007', 'rsrc/css/phui/phui-segment-bar-view.css' => '5166b370', 'rsrc/css/phui/phui-spacing.css' => 'b05cadc3', 'rsrc/css/phui/phui-status.css' => '293b5dad', 'rsrc/css/phui/phui-tag-view.css' => 'fb811341', - 'rsrc/css/phui/phui-timeline-view.css' => '2d32d7a9', + 'rsrc/css/phui/phui-timeline-view.css' => '7f8659ec', 'rsrc/css/phui/phui-two-column-view.css' => 'f96d319f', - 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'e86de308', + 'rsrc/css/phui/workboards/phui-workboard-color.css' => '3a1c21ff', 'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98', - 'rsrc/css/phui/workboards/phui-workcard.css' => '913441b6', - 'rsrc/css/phui/workboards/phui-workpanel.css' => '3ae89b20', - 'rsrc/css/sprite-login.css' => '18b368a6', + 'rsrc/css/phui/workboards/phui-workcard.css' => '62056e3b', + 'rsrc/css/phui/workboards/phui-workpanel.css' => 'bc06f022', + 'rsrc/css/sprite-login.css' => '07052ee0', 'rsrc/css/sprite-tokens.css' => 'f1896dc5', - 'rsrc/css/syntax/syntax-default.css' => '055fc231', + 'rsrc/css/syntax/syntax-default.css' => 'c0307dc6', 'rsrc/externals/d3/d3.min.js' => '9d068042', 'rsrc/externals/font/fontawesome/fontawesome-webfont.eot' => '23f8c698', 'rsrc/externals/font/fontawesome/fontawesome-webfont.ttf' => '70983df0', @@ -257,7 +257,7 @@ 'rsrc/externals/javelin/lib/URI.js' => '2e255291', 'rsrc/externals/javelin/lib/Vector.js' => 'e9c80beb', 'rsrc/externals/javelin/lib/WebSocket.js' => 'fdc13e4e', - 'rsrc/externals/javelin/lib/Workflow.js' => '945ff654', + 'rsrc/externals/javelin/lib/Workflow.js' => 'cc1553f3', 'rsrc/externals/javelin/lib/__tests__/Cookie.js' => 'ca686f71', 'rsrc/externals/javelin/lib/__tests__/DOM.js' => '4566e249', 'rsrc/externals/javelin/lib/__tests__/JSON.js' => '710377ae', @@ -265,7 +265,7 @@ 'rsrc/externals/javelin/lib/__tests__/behavior.js' => '8426ebeb', 'rsrc/externals/javelin/lib/behavior.js' => '1b6acc2a', 'rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js' => '89a1ae3a', - 'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => 'a4356cde', + 'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => '0507519c', 'rsrc/externals/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js' => 'a241536a', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '22ee68a5', 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js' => '23387297', @@ -320,7 +320,7 @@ 'rsrc/image/icon/tango/upload.png' => '3fe6b92d', 'rsrc/image/icon/unsubscribe.png' => 'db04378a', 'rsrc/image/lightblue-header.png' => 'e6d483c6', - 'rsrc/image/logo/light-eye.png' => '72337472', + 'rsrc/image/logo/project-logo.png' => '019d256f', 'rsrc/image/main_texture.png' => '894d03c4', 'rsrc/image/menu_texture.png' => '896c9ade', 'rsrc/image/people/harding.png' => '95b2db63', @@ -342,8 +342,8 @@ 'rsrc/image/phrequent_active.png' => 'de66dc50', 'rsrc/image/phrequent_inactive.png' => '79c61baf', 'rsrc/image/resize.png' => '9cc83373', - 'rsrc/image/sprite-login-X2.png' => '604545f6', - 'rsrc/image/sprite-login.png' => '7a001a9a', + 'rsrc/image/sprite-login-X2.png' => '02896524', + 'rsrc/image/sprite-login.png' => 'e0508107', 'rsrc/image/sprite-tokens-X2.png' => '21621dd9', 'rsrc/image/sprite-tokens.png' => 'bede2580', 'rsrc/image/texture/card-gradient.png' => 'e6892cb4', @@ -387,12 +387,12 @@ 'rsrc/js/application/diff/DiffTreeView.js' => '5d83623b', 'rsrc/js/application/differential/behavior-diff-radios.js' => '925fe8cd', 'rsrc/js/application/differential/behavior-populate.js' => 'b86ef6c2', - 'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => '94243d89', + 'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => '6c798a10', 'rsrc/js/application/diffusion/ExternalEditorLinkEngine.js' => '48a8641f', 'rsrc/js/application/diffusion/behavior-audit-preview.js' => 'b7b73831', 'rsrc/js/application/diffusion/behavior-commit-branches.js' => '4b671572', 'rsrc/js/application/diffusion/behavior-commit-graph.js' => 'ac10c917', - 'rsrc/js/application/diffusion/behavior-locate-file.js' => '87428eb2', + 'rsrc/js/application/diffusion/behavior-locate-file.js' => '4c77f259', 'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'c715c123', 'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '6a85bc5a', 'rsrc/js/application/drydock/drydock-live-operation-status.js' => '47a0728b', @@ -423,7 +423,7 @@ 'rsrc/js/application/projects/WorkboardCard.js' => '0392a5d8', 'rsrc/js/application/projects/WorkboardCardTemplate.js' => '84f82dad', 'rsrc/js/application/projects/WorkboardColumn.js' => 'c3d24e63', - 'rsrc/js/application/projects/WorkboardController.js' => 'b9d0c2f3', + 'rsrc/js/application/projects/WorkboardController.js' => '7474d31f', 'rsrc/js/application/projects/WorkboardDropEffect.js' => '8e0aa661', 'rsrc/js/application/projects/WorkboardHeader.js' => '111bfd2d', 'rsrc/js/application/projects/WorkboardHeaderTemplate.js' => 'ebe83a6b', @@ -453,7 +453,7 @@ 'rsrc/js/core/DragAndDropFileUpload.js' => '4370900d', 'rsrc/js/core/DraggableList.js' => '0169e425', 'rsrc/js/core/Favicon.js' => '7930776a', - 'rsrc/js/core/FileUpload.js' => 'ab85e184', + 'rsrc/js/core/FileUpload.js' => '331676ea', 'rsrc/js/core/Hovercard.js' => '6199f752', 'rsrc/js/core/HovercardList.js' => 'de4b4919', 'rsrc/js/core/KeyboardShortcut.js' => '1a844c06', @@ -461,6 +461,7 @@ 'rsrc/js/core/MultirowRowManager.js' => '5b54c823', 'rsrc/js/core/Notification.js' => 'a9b91e3f', 'rsrc/js/core/Prefab.js' => '5793d835', + 'rsrc/js/core/RemarkupMetadata.js' => 'e40c4991', 'rsrc/js/core/ShapedRequest.js' => '995f5102', 'rsrc/js/core/TextAreaUtils.js' => 'f340a484', 'rsrc/js/core/Title.js' => '43bc9360', @@ -470,11 +471,11 @@ 'rsrc/js/core/behavior-badge-view.js' => '92cdd7b6', 'rsrc/js/core/behavior-bulk-editor.js' => 'aa6d2308', 'rsrc/js/core/behavior-choose-control.js' => '04f8a1e3', - 'rsrc/js/core/behavior-copy.js' => 'cf32921f', + 'rsrc/js/core/behavior-copy.js' => '96b63a02', 'rsrc/js/core/behavior-detect-timezone.js' => '78bc5d94', 'rsrc/js/core/behavior-device.js' => 'ac2b1e01', - 'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '3277c62d', - 'rsrc/js/core/behavior-fancy-datepicker.js' => '36821f8d', + 'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '6bc7ccf7', + 'rsrc/js/core/behavior-fancy-datepicker.js' => 'b545d0a0', 'rsrc/js/core/behavior-form.js' => '55d7b788', 'rsrc/js/core/behavior-gesture.js' => 'b58d1a2a', 'rsrc/js/core/behavior-global-drag-and-drop.js' => '1cab0e9a', @@ -489,7 +490,7 @@ 'rsrc/js/core/behavior-more.js' => '506aa3f4', 'rsrc/js/core/behavior-object-selector.js' => '98ef467f', 'rsrc/js/core/behavior-oncopy.js' => 'da8f5259', - 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '54262396', + 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '6d347847', 'rsrc/js/core/behavior-read-only-warning.js' => 'b9109f8f', 'rsrc/js/core/behavior-redirect.js' => '407ee861', 'rsrc/js/core/behavior-refresh-csrf.js' => '46116c01', @@ -499,7 +500,7 @@ 'rsrc/js/core/behavior-reveal-content.js' => 'b105a3a6', 'rsrc/js/core/behavior-scrollbar.js' => '92388bae', 'rsrc/js/core/behavior-search-typeahead.js' => '1cb7d027', - 'rsrc/js/core/behavior-select-content.js' => 'e8240b50', + 'rsrc/js/core/behavior-select-content.js' => 'c538cbfc', 'rsrc/js/core/behavior-select-on-click.js' => '66365ee2', 'rsrc/js/core/behavior-setup-check-https.js' => '01384686', 'rsrc/js/core/behavior-time-typeahead.js' => '5803b9e7', @@ -552,14 +553,14 @@ 'conpherence-durable-column-view' => '2d57072b', 'conpherence-header-pane-css' => 'c9a3db8e', 'conpherence-menu-css' => '67f4680d', - 'conpherence-message-pane-css' => 'd244db1e', - 'conpherence-notification-css' => '6a3d4e58', + 'conpherence-message-pane-css' => '50b1345e', + 'conpherence-notification-css' => '85c48def', 'conpherence-participant-pane-css' => '69e0058a', 'conpherence-thread-manager' => 'aec8e38c', 'conpherence-transaction-css' => '3a3f5e7e', 'd3' => '9d068042', 'diff-tree-view-css' => 'e2d3e222', - 'differential-changeset-view-css' => '60c3d405', + 'differential-changeset-view-css' => '1b0476bc', 'differential-core-view-css' => '7300a73e', 'differential-revision-add-comment-css' => '7e5900d9', 'differential-revision-comment-css' => '7dbc8d1d', @@ -567,7 +568,7 @@ 'differential-revision-list-css' => '93d2df7d', 'differential-table-of-contents-css' => 'bba788b9', 'diffusion-css' => 'e46232d6', - 'diffusion-icons-css' => '23b31a1b', + 'diffusion-icons-css' => 'e812add2', 'diffusion-readme-css' => 'b68a76e4', 'diffusion-repository-css' => 'b89e8c6c', 'diviner-shared-css' => '4bd263b0', @@ -578,7 +579,7 @@ 'fuel-map-css' => 'd6e31510', 'fuel-menu-css' => '21f5d199', 'global-drag-and-drop-css' => '1d2713a4', - 'harbormaster-css' => '8dfe16b2', + 'harbormaster-css' => '9346e08b', 'herald-css' => '648d39e2', 'herald-rule-editor' => '2633bef7', 'herald-test-css' => '7e7bbdae', @@ -589,7 +590,7 @@ 'javelin-behavior-aphlict-listen' => '4e61fa88', 'javelin-behavior-aphlict-status' => 'c3703a16', 'javelin-behavior-aphront-basic-tokenizer' => '3b4899b0', - 'javelin-behavior-aphront-drag-and-drop-textarea' => '3277c62d', + 'javelin-behavior-aphront-drag-and-drop-textarea' => '6bc7ccf7', 'javelin-behavior-aphront-form-disable-on-submit' => '55d7b788', 'javelin-behavior-aphront-more' => '506aa3f4', 'javelin-behavior-audio-source' => '3dc5ad43', @@ -619,7 +620,7 @@ 'javelin-behavior-differential-populate' => 'b86ef6c2', 'javelin-behavior-diffusion-commit-branches' => '4b671572', 'javelin-behavior-diffusion-commit-graph' => 'ac10c917', - 'javelin-behavior-diffusion-locate-file' => '87428eb2', + 'javelin-behavior-diffusion-locate-file' => '4c77f259', 'javelin-behavior-diffusion-pull-lastmodified' => 'c715c123', 'javelin-behavior-document-engine' => '243d6c22', 'javelin-behavior-doorkeeper-tag' => '6a85bc5a', @@ -628,7 +629,7 @@ 'javelin-behavior-editengine-reorder-configs' => '4842f137', 'javelin-behavior-editengine-reorder-fields' => '0ad8d31f', 'javelin-behavior-event-all-day' => '0b1bc990', - 'javelin-behavior-fancy-datepicker' => '36821f8d', + 'javelin-behavior-fancy-datepicker' => 'b545d0a0', 'javelin-behavior-global-drag-and-drop' => '1cab0e9a', 'javelin-behavior-harbormaster-log' => 'b347a301', 'javelin-behavior-herald-rule-editor' => '0922e81d', @@ -644,7 +645,7 @@ 'javelin-behavior-owners-path-editor' => 'ff688a7a', 'javelin-behavior-passphrase-credential-control' => '48fe33d0', 'javelin-behavior-phabricator-autofocus' => '65bb0011', - 'javelin-behavior-phabricator-clipboard-copy' => 'cf32921f', + 'javelin-behavior-phabricator-clipboard-copy' => '96b63a02', 'javelin-behavior-phabricator-gesture' => 'b58d1a2a', 'javelin-behavior-phabricator-gesture-example' => '242dedd0', 'javelin-behavior-phabricator-keyboard-pager' => '1325b731', @@ -653,7 +654,7 @@ 'javelin-behavior-phabricator-notification-example' => '29819b75', 'javelin-behavior-phabricator-object-selector' => '98ef467f', 'javelin-behavior-phabricator-oncopy' => 'da8f5259', - 'javelin-behavior-phabricator-remarkup-assist' => '54262396', + 'javelin-behavior-phabricator-remarkup-assist' => '6d347847', 'javelin-behavior-phabricator-reveal-content' => 'b105a3a6', 'javelin-behavior-phabricator-search-typeahead' => '1cb7d027', 'javelin-behavior-phabricator-show-older-transactions' => '8b5c7d65', @@ -687,7 +688,7 @@ 'javelin-behavior-repository-crossreference' => '44d48cd1', 'javelin-behavior-scrollbar' => '92388bae', 'javelin-behavior-search-reorder-queries' => 'b86f297f', - 'javelin-behavior-select-content' => 'e8240b50', + 'javelin-behavior-select-content' => 'c538cbfc', 'javelin-behavior-select-on-click' => '66365ee2', 'javelin-behavior-setup-check-https' => '01384686', 'javelin-behavior-stripe-payment-form' => '02cb4398', @@ -706,7 +707,7 @@ 'javelin-chart-function-label' => '81de1dab', 'javelin-color' => '78f811c9', 'javelin-cookie' => '05d290ef', - 'javelin-diffusion-locate-file-source' => '94243d89', + 'javelin-diffusion-locate-file-source' => '6c798a10', 'javelin-dom' => 'e4c7622a', 'javelin-dynval' => '202a2e85', 'javelin-event' => 'c03f2fb4', @@ -731,7 +732,7 @@ 'javelin-sound' => 'd4cc2d2a', 'javelin-stratcom' => '0889b835', 'javelin-tokenizer' => '89a1ae3a', - 'javelin-typeahead' => 'a4356cde', + 'javelin-typeahead' => '0507519c', 'javelin-typeahead-composite-source' => '22ee68a5', 'javelin-typeahead-normalizer' => 'a241536a', 'javelin-typeahead-ondemand-source' => '23387297', @@ -751,12 +752,12 @@ 'javelin-workboard-card' => '0392a5d8', 'javelin-workboard-card-template' => '84f82dad', 'javelin-workboard-column' => 'c3d24e63', - 'javelin-workboard-controller' => 'b9d0c2f3', + 'javelin-workboard-controller' => '7474d31f', 'javelin-workboard-drop-effect' => '8e0aa661', 'javelin-workboard-header' => '111bfd2d', 'javelin-workboard-header-template' => 'ebe83a6b', 'javelin-workboard-order-template' => '03e8891f', - 'javelin-workflow' => '945ff654', + 'javelin-workflow' => 'cc1553f3', 'maniphest-report-css' => '3d53188b', 'maniphest-task-edit-css' => '272daa84', 'maniphest-task-summary-css' => '61d1667e', @@ -769,9 +770,8 @@ 'people-profile-css' => '2ea2daa1', 'phabricator-action-list-view-css' => '1b0085b2', 'phabricator-busy' => '5202e831', - 'phabricator-chatlog-css' => 'abdc76ee', 'phabricator-content-source-view-css' => 'cdf0d579', - 'phabricator-core-css' => 'b3ebd90d', + 'phabricator-core-css' => '531ad849', 'phabricator-countdown-css' => 'bff8012f', 'phabricator-darklog' => '3b869402', 'phabricator-darkmessage' => '26cd4b73', @@ -787,11 +787,11 @@ 'phabricator-fatal-config-template-css' => '20babf50', 'phabricator-favicon' => '7930776a', 'phabricator-feed-css' => 'd8b6e3f8', - 'phabricator-file-upload' => 'ab85e184', + 'phabricator-file-upload' => '331676ea', 'phabricator-flag-css' => '2b77be8d', 'phabricator-keyboard-shortcut' => '1a844c06', 'phabricator-keyboard-shortcut-manager' => '81debc48', - 'phabricator-main-menu-view' => 'bcec20f0', + 'phabricator-main-menu-view' => '33820efe', 'phabricator-nav-view-css' => '423f92cc', 'phabricator-notification' => 'a9b91e3f', 'phabricator-notification-css' => '30240bd2', @@ -799,12 +799,13 @@ 'phabricator-object-selector-css' => 'ee77366f', 'phabricator-phtize' => '2f1db1ed', 'phabricator-prefab' => '5793d835', - 'phabricator-remarkup-css' => '5baa3bd9', + 'phabricator-remarkup-css' => 'd91c2ee8', + 'phabricator-remarkup-metadata' => 'e40c4991', 'phabricator-search-results-css' => '9ea70ace', 'phabricator-shaped-request' => '995f5102', 'phabricator-slowvote-css' => '1694baed', - 'phabricator-source-code-view-css' => '03d7ac28', - 'phabricator-standard-page-view' => 'a374f94c', + 'phabricator-source-code-view-css' => 'e382316a', + 'phabricator-standard-page-view' => 'e08c7462', 'phabricator-textareautils' => 'f340a484', 'phabricator-title' => '43bc9360', 'phabricator-tooltip' => '83754533', @@ -822,12 +823,12 @@ 'phriction-document-css' => '03380da0', 'phui-action-panel-css' => '6c386cbf', 'phui-badge-view-css' => '666e25ad', - 'phui-basic-nav-view-css' => '56ebd66d', + 'phui-basic-nav-view-css' => 'a5693cf0', 'phui-big-info-view-css' => '362ad37b', 'phui-box-css' => '5ed3b8cb', 'phui-bulk-editor-css' => '374d5e30', 'phui-button-bar-css' => 'a4aa75c4', - 'phui-button-css' => 'ea704902', + 'phui-button-css' => '55025b10', 'phui-button-simple-css' => '1ff278aa', 'phui-calendar-css' => 'f11073aa', 'phui-calendar-day-css' => '9597d706', @@ -835,7 +836,7 @@ 'phui-calendar-month-css' => 'cb758c42', 'phui-chart-css' => '14df9ae3', 'phui-cms-css' => '8c05c41e', - 'phui-comment-form-css' => '68a2d99a', + 'phui-comment-form-css' => '3c6679a3', 'phui-comment-panel-css' => 'ec4e31c0', 'phui-crumbs-view-css' => '614f43cf', 'phui-curtain-object-ref-view-css' => '51d93266', @@ -847,45 +848,45 @@ 'phui-font-icon-base-css' => '303c9b87', 'phui-fontkit-css' => '1ec937e5', 'phui-form-css' => '1f177cb7', - 'phui-form-view-css' => '01b796c0', + 'phui-form-view-css' => '57edecb7', 'phui-formation-view-css' => 'd2dec8ed', 'phui-head-thing-view-css' => 'd7f293df', 'phui-header-view-css' => '36c86a58', 'phui-hovercard' => '6199f752', 'phui-hovercard-list' => 'de4b4919', - 'phui-hovercard-view-css' => '6ca90fa0', - 'phui-icon-set-selector-css' => '7aa5f3ec', - 'phui-icon-view-css' => '4cbc684a', + 'phui-hovercard-view-css' => '39fd2e14', + 'phui-icon-set-selector-css' => '19e0253b', + 'phui-icon-view-css' => '084ac612', 'phui-image-mask-css' => '62c7f4d2', 'phui-info-view-css' => 'a10a909b', 'phui-inline-comment-view-css' => '9863a85e', 'phui-invisible-character-view-css' => 'c694c4a4', 'phui-left-right-css' => '68513c34', 'phui-lightbox-css' => '4ebf22da', - 'phui-list-view-css' => '0c04affd', + 'phui-list-view-css' => 'ccf73664', 'phui-object-box-css' => 'b8d7eea0', 'phui-oi-big-ui-css' => 'fa74cc35', 'phui-oi-color-css' => 'b517bfa0', 'phui-oi-drag-ui-css' => 'da15d3dc', 'phui-oi-flush-ui-css' => '490e2e2e', - 'phui-oi-list-view-css' => 'af98a277', - 'phui-oi-simple-ui-css' => '6a30fa46', + 'phui-oi-list-view-css' => '9275ff55', + 'phui-oi-simple-ui-css' => '9b03a61f', 'phui-pager-css' => 'd022c7ad', 'phui-pinboard-view-css' => '1f08f5d8', 'phui-policy-section-view-css' => '139fdc64', - 'phui-property-list-view-css' => '5adf7078', + 'phui-property-list-view-css' => '9a155095', 'phui-remarkup-preview-css' => '91767007', 'phui-segment-bar-view-css' => '5166b370', 'phui-spacing-css' => 'b05cadc3', 'phui-status-list-view-css' => '293b5dad', 'phui-tag-view-css' => 'fb811341', 'phui-theme-css' => '35883b37', - 'phui-timeline-view-css' => '2d32d7a9', + 'phui-timeline-view-css' => '7f8659ec', 'phui-two-column-view-css' => 'f96d319f', - 'phui-workboard-color-css' => 'e86de308', + 'phui-workboard-color-css' => '3a1c21ff', 'phui-workboard-view-css' => '74fc9d98', - 'phui-workcard-view-css' => '913441b6', - 'phui-workpanel-view-css' => '3ae89b20', + 'phui-workcard-view-css' => '62056e3b', + 'phui-workpanel-view-css' => 'bc06f022', 'phuix-action-list-view' => 'c68f183f', 'phuix-action-view' => 'a8f573a9', 'phuix-autocomplete' => '2fbe234d', @@ -899,14 +900,14 @@ 'policy-css' => 'ceb56a08', 'policy-edit-css' => '8794e2ed', 'policy-transaction-detail-css' => 'c02b8384', - 'ponder-view-css' => '05a09d0a', + 'ponder-view-css' => 'b04bbaff', 'project-card-view-css' => 'a9f2c2dd', 'project-triggers-css' => 'cd9c8bb9', 'project-view-css' => '567858b3', - 'setup-issue-css' => '5eed85b2', - 'sprite-login-css' => '18b368a6', + 'setup-issue-css' => '93231115', + 'sprite-login-css' => '07052ee0', 'sprite-tokens-css' => 'f1896dc5', - 'syntax-default-css' => '055fc231', + 'syntax-default-css' => 'c0307dc6', 'syntax-highlighting-css' => '548567f6', 'tokens-css' => 'ce5a50bd', 'trigger-rule' => '41b7b4f6', @@ -965,6 +966,12 @@ 'javelin-dom', 'javelin-workflow', ), + '0507519c' => array( + 'javelin-install', + 'javelin-dom', + 'javelin-vector', + 'javelin-util', + ), '05d290ef' => array( 'javelin-install', 'javelin-util', @@ -1049,6 +1056,9 @@ 'javelin-util', 'phabricator-keyboard-shortcut-manager', ), + '1b0476bc' => array( + 'phui-inline-comment-view-css', + ), '1b6acc2a' => array( 'javelin-magical-init', 'javelin-util', @@ -1195,18 +1205,19 @@ 'javelin-install', 'javelin-util', ), - '3277c62d' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-json', - 'phabricator-drag-and-drop-file-upload', - 'phabricator-textareautils', - ), '32db8374' => array( 'javelin-behavior', 'javelin-stratcom', 'javelin-dom', ), + '331676ea' => array( + 'javelin-install', + 'javelin-dom', + 'phabricator-notification', + ), + '33820efe' => array( + 'phui-theme-css', + ), 34450586 => array( 'javelin-color', 'javelin-install', @@ -1222,13 +1233,6 @@ 'aphront-typeahead-control-css', 'phui-tag-view-css', ), - '36821f8d' => array( - 'javelin-behavior', - 'javelin-util', - 'javelin-dom', - 'javelin-stratcom', - 'javelin-vector', - ), '3829a3cf' => array( 'javelin-behavior', 'javelin-uri', @@ -1248,9 +1252,6 @@ 'trigger-rule', 'trigger-rule-type', ), - '3ae89b20' => array( - 'phui-workcard-view-css', - ), '3b4899b0' => array( 'javelin-behavior', 'phabricator-prefab', @@ -1375,6 +1376,13 @@ 'javelin-install', 'javelin-dom', ), + '4c77f259' => array( + 'javelin-behavior', + 'javelin-diffusion-locate-file-source', + 'javelin-dom', + 'javelin-typeahead', + 'javelin-uri', + ), '4dffaeb2' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1420,17 +1428,6 @@ '541f81c3' => array( 'javelin-install', ), - 54262396 => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - 'phabricator-phtize', - 'phabricator-textareautils', - 'javelin-workflow', - 'javelin-vector', - 'phuix-autocomplete', - 'javelin-mask', - ), '548567f6' => array( 'syntax-default-css', ), @@ -1519,9 +1516,6 @@ '5faf27b9' => array( 'phuix-form-control-view', ), - '60c3d405' => array( - 'phui-inline-comment-view-css', - ), '60cd9241' => array( 'javelin-behavior', ), @@ -1556,9 +1550,6 @@ '6a18c42e' => array( 'javelin-install', ), - '6a30fa46' => array( - 'phui-oi-list-view-css', - ), '6a85bc5a' => array( 'javelin-behavior', 'javelin-dom', @@ -1566,6 +1557,19 @@ 'javelin-workflow', 'javelin-magical-init', ), + '6bc7ccf7' => array( + 'javelin-behavior', + 'javelin-dom', + 'phabricator-drag-and-drop-file-upload', + 'phabricator-textareautils', + 'phabricator-remarkup-metadata', + ), + '6c798a10' => array( + 'javelin-install', + 'javelin-dom', + 'javelin-typeahead-preloaded-source', + 'javelin-util', + ), '6cfa0008' => array( 'javelin-dom', 'javelin-dynval', @@ -1574,6 +1578,18 @@ 'javelin-install', 'javelin-util', ), + '6d347847' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + 'phabricator-phtize', + 'phabricator-textareautils', + 'phabricator-remarkup-metadata', + 'javelin-workflow', + 'javelin-vector', + 'phuix-autocomplete', + 'javelin-mask', + ), 70245195 => array( 'javelin-behavior', 'javelin-stratcom', @@ -1599,6 +1615,16 @@ 'javelin-behavior', 'javelin-dom', ), + '7474d31f' => array( + 'javelin-install', + 'javelin-dom', + 'javelin-util', + 'javelin-vector', + 'javelin-stratcom', + 'javelin-workflow', + 'phabricator-drag-and-drop-file-upload', + 'javelin-workboard-board', + ), '78bc5d94' => array( 'javelin-behavior', 'javelin-uri', @@ -1654,13 +1680,6 @@ '84f82dad' => array( 'javelin-install', ), - '87428eb2' => array( - 'javelin-behavior', - 'javelin-diffusion-locate-file-source', - 'javelin-dom', - 'javelin-typeahead', - 'javelin-uri', - ), '876506b6' => array( 'javelin-view', 'javelin-install', @@ -1745,23 +1764,6 @@ 'phabricator-prefab', 'javelin-json', ), - '94243d89' => array( - 'javelin-install', - 'javelin-dom', - 'javelin-typeahead-preloaded-source', - 'javelin-util', - ), - '945ff654' => array( - 'javelin-stratcom', - 'javelin-request', - 'javelin-dom', - 'javelin-vector', - 'javelin-install', - 'javelin-util', - 'javelin-mask', - 'javelin-uri', - 'javelin-routable', - ), '9623adc1' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1769,6 +1771,12 @@ 'javelin-dom', 'javelin-router', ), + '96b63a02' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + 'phabricator-notification', + ), '98ef467f' => array( 'javelin-behavior', 'javelin-dom', @@ -1785,6 +1793,9 @@ 'javelin-install', 'javelin-util', ), + '9b03a61f' => array( + 'phui-oi-list-view-css', + ), '9c01e364' => array( 'javelin-behavior', 'javelin-dom', @@ -1818,12 +1829,6 @@ 'javelin-workflow', 'phabricator-draggable-list', ), - 'a4356cde' => array( - 'javelin-install', - 'javelin-dom', - 'javelin-vector', - 'javelin-util', - ), 'a43ae2ae' => array( 'javelin-install', 'javelin-dom', @@ -1880,11 +1885,6 @@ 'javelin-json', 'phuix-form-control-view', ), - 'ab85e184' => array( - 'javelin-install', - 'javelin-dom', - 'phabricator-notification', - ), 'ac10c917' => array( 'javelin-behavior', 'javelin-dom', @@ -1953,6 +1953,13 @@ 'b517bfa0' => array( 'phui-oi-list-view-css', ), + 'b545d0a0' => array( + 'javelin-behavior', + 'javelin-util', + 'javelin-dom', + 'javelin-stratcom', + 'javelin-vector', + ), 'b557770a' => array( 'javelin-install', 'javelin-util', @@ -2000,18 +2007,8 @@ 'javelin-uri', 'phabricator-notification', ), - 'b9d0c2f3' => array( - 'javelin-install', - 'javelin-dom', - 'javelin-util', - 'javelin-vector', - 'javelin-stratcom', - 'javelin-workflow', - 'phabricator-drag-and-drop-file-upload', - 'javelin-workboard-board', - ), - 'bcec20f0' => array( - 'phui-theme-css', + 'bc06f022' => array( + 'phui-workcard-view-css', ), 'c03f2fb4' => array( 'javelin-install', @@ -2032,6 +2029,11 @@ 'javelin-workboard-card', 'javelin-workboard-header', ), + 'c538cbfc' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + ), 'c687e867' => array( 'javelin-behavior', 'javelin-dom', @@ -2051,6 +2053,17 @@ 'javelin-workflow', 'javelin-json', ), + 'cc1553f3' => array( + 'javelin-stratcom', + 'javelin-request', + 'javelin-dom', + 'javelin-vector', + 'javelin-install', + 'javelin-util', + 'javelin-mask', + 'javelin-uri', + 'javelin-routable', + ), 'cc2c5de5' => array( 'javelin-install', 'phuix-button-view', @@ -2062,11 +2075,6 @@ 'phuix-formation-column-view', 'phuix-formation-flank-view', ), - 'cf32921f' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - ), 'd12d214f' => array( 'javelin-install', 'javelin-dom', @@ -2126,6 +2134,11 @@ 'javelin-dom', 'phuix-dropdown-menu', ), + 'e40c4991' => array( + 'javelin-install', + 'javelin-dom', + 'javelin-json', + ), 'e4c7622a' => array( 'javelin-magical-init', 'javelin-install', @@ -2140,11 +2153,6 @@ 'javelin-dom', 'phabricator-draggable-list', ), - 'e8240b50' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - ), 'e9a2940f' => array( 'javelin-behavior', 'javelin-request', diff --git a/resources/sprite/login_1x/Facebook.png b/resources/sprite/login_1x/Facebook.png index 8c7201b05e..397046ffba 100644 Binary files a/resources/sprite/login_1x/Facebook.png and b/resources/sprite/login_1x/Facebook.png differ diff --git a/resources/sprite/login_1x/Google.png b/resources/sprite/login_1x/Google.png index e0989b9c56..4e476c811d 100644 Binary files a/resources/sprite/login_1x/Google.png and b/resources/sprite/login_1x/Google.png differ diff --git a/resources/sprite/login_1x/Phorge.png b/resources/sprite/login_1x/Phorge.png new file mode 100644 index 0000000000..20b6c1b215 Binary files /dev/null and b/resources/sprite/login_1x/Phorge.png differ diff --git a/resources/sprite/login_2x/Facebook.png b/resources/sprite/login_2x/Facebook.png index b3cac2bee5..c5b04bdc8e 100644 Binary files a/resources/sprite/login_2x/Facebook.png and b/resources/sprite/login_2x/Facebook.png differ diff --git a/resources/sprite/login_2x/Google.png b/resources/sprite/login_2x/Google.png index f16ad8c55b..6a206317ad 100644 Binary files a/resources/sprite/login_2x/Google.png and b/resources/sprite/login_2x/Google.png differ diff --git a/resources/sprite/login_2x/Phorge.png b/resources/sprite/login_2x/Phorge.png new file mode 100644 index 0000000000..9bcb696250 Binary files /dev/null and b/resources/sprite/login_2x/Phorge.png differ diff --git a/resources/sprite/manifest/login.json b/resources/sprite/manifest/login.json index 59312820e1..e82fd25fdb 100644 --- a/resources/sprite/manifest/login.json +++ b/resources/sprite/manifest/login.json @@ -24,7 +24,7 @@ "login-Facebook": { "name": "login-Facebook", "rule": ".login-Facebook", - "hash": "1b12a5a5cfe103d4d96213cf7d1ce18a" + "hash": "ad9b8fcc4c338bfe4196a7f636c7d4c1" }, "login-Generic": { "name": "login-Generic", @@ -39,7 +39,7 @@ "login-Google": { "name": "login-Google", "rule": ".login-Google", - "hash": "72e7b0e1005c92f059f4d5881592ee72" + "hash": "9a25ee35dfde4135db2e321e01b82c61" }, "login-HTTP": { "name": "login-HTTP", @@ -71,6 +71,11 @@ "rule": ".login-Phabricator", "hash": "54f5ddae4b9d138c438ec00ed42544d2" }, + "login-Phorge": { + "name": "login-Phorge", + "rule": ".login-Phorge", + "hash": "0cc36ea8b7b98c57c23e547400b955f1" + }, "login-Slack": { "name": "login-Slack", "rule": ".login-Slack", diff --git a/resources/sql/autopatches/20140722.appname.php b/resources/sql/autopatches/20140722.appname.php index dd8e929357..2a337c35aa 100644 --- a/resources/sql/autopatches/20140722.appname.php +++ b/resources/sql/autopatches/20140722.appname.php @@ -4,7 +4,6 @@ 'Audit', 'Auth', 'Calendar', - 'ChatLog', 'Conduit', 'Config', 'Conpherence', diff --git a/resources/sql/autopatches/20210802.legalpad_document_signature.01.phid.sql b/resources/sql/autopatches/20210802.legalpad_document_signature.01.phid.sql new file mode 100644 index 0000000000..92e50cce98 --- /dev/null +++ b/resources/sql/autopatches/20210802.legalpad_document_signature.01.phid.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_legalpad.legalpad_documentsignature + ADD phid VARBINARY(64) NOT NULL; diff --git a/resources/sql/autopatches/20210802.legalpad_document_signature.02.phid-populate.php b/resources/sql/autopatches/20210802.legalpad_document_signature.02.phid-populate.php new file mode 100644 index 0000000000..1a2ce50293 --- /dev/null +++ b/resources/sql/autopatches/20210802.legalpad_document_signature.02.phid-populate.php @@ -0,0 +1,79 @@ +establishConnection('w'); +$table_name = $docsig_table->getTableName(); + +$chunk_size = 4096; + +$temporary_table = 'tmp_20210802_docsig_id_map'; + +try { + queryfx( + $conn, + 'CREATE TEMPORARY TABLE %T ( + docsig_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + docsig_phid VARBINARY(64) NOT NULL)', + $temporary_table); +} catch (AphrontAccessDeniedQueryException $ex) { + throw new PhutilProxyException( + pht( + 'Failed to "CREATE TEMPORARY TABLE". You may need to "GRANT" the '. + 'current MySQL user this permission.'), + $ex); +} + +$table_iterator = id(new LiskRawMigrationIterator($conn, $table_name)) + ->setPageSize($chunk_size); + +$chunk_iterator = new PhutilChunkedIterator($table_iterator, $chunk_size); +foreach ($chunk_iterator as $chunk) { + + $map = array(); + foreach ($chunk as $docsig_row) { + $phid = $docsig_row['phid']; + + if (strlen($phid)) { + continue; + } + + $phid = PhabricatorPHID::generateNewPHID($phid_type); + $id = $docsig_row['id']; + + $map[(int)$id] = $phid; + } + + if (!$map) { + continue; + } + + $sql = array(); + foreach ($map as $docsig_id => $docsig_phid) { + $sql[] = qsprintf( + $conn, + '(%d, %s)', + $docsig_id, + $docsig_phid); + } + + queryfx( + $conn, + 'TRUNCATE TABLE %T', + $temporary_table); + + queryfx( + $conn, + 'INSERT INTO %T (docsig_id, docsig_phid) VALUES %LQ', + $temporary_table, + $sql); + + queryfx( + $conn, + 'UPDATE %T c JOIN %T x ON c.id = x.docsig_id + SET c.phid = x.docsig_phid', + $table_name, + $temporary_table); +} diff --git a/resources/sql/autopatches/20230902.repository.01.rebuild-index.php b/resources/sql/autopatches/20230902.repository.01.rebuild-index.php new file mode 100644 index 0000000000..aba27ce78c --- /dev/null +++ b/resources/sql/autopatches/20230902.repository.01.rebuild-index.php @@ -0,0 +1,6 @@ +openTransaction(); -$channel_table->openTransaction(); - -$event_table->beginReadLocking(); -$channel_table->beginReadLocking(); - -$events = new LiskMigrationIterator($event_table); -$conn_w = $channel_table->establishConnection('w'); - -foreach ($events as $event) { - if ($event->getChannelID()) { - continue; - } - - $event_row = queryfx_one( - $conn_w, - 'SELECT channel FROM %T WHERE id = %d', - $event->getTableName(), - $event->getID()); - $event_channel = $event_row['channel']; - - $matched = queryfx_one( - $conn_w, - 'SELECT * FROM %T WHERE - channelName = %s AND serviceName = %s AND serviceType = %s', - $channel_table->getTableName(), - $event_channel, - '', - ''); - - if (!$matched) { - $matched = id(new PhabricatorChatLogChannel()) - ->setChannelName($event_channel) - ->setServiceType('') - ->setServiceName('') - ->setViewPolicy(PhabricatorPolicies::POLICY_USER) - ->setEditPolicy(PhabricatorPolicies::POLICY_USER) - ->save(); - $matched_id = $matched->getID(); - } else { - $matched_id = $matched['id']; - } - - queryfx( - $event->establishConnection('w'), - 'UPDATE %T SET channelID = %d WHERE id = %d', - $event->getTableName(), - $matched_id, - $event->getID()); -} - -$event_table->endReadLocking(); -$channel_table->endReadLocking(); - -$event_table->saveTransaction(); -$channel_table->saveTransaction(); - -echo "\n".pht('Done.')."\n"; +/* This file is intentionally left empty, see T15126 */ diff --git a/resources/sql/patches/20130222.dropchannel.sql b/resources/sql/patches/20130222.dropchannel.sql index 00e6a13f18..04b0e849bc 100644 --- a/resources/sql/patches/20130222.dropchannel.sql +++ b/resources/sql/patches/20130222.dropchannel.sql @@ -1,2 +1 @@ -ALTER TABLE `{$NAMESPACE}_chatlog`.`chatlog_event` - DROP channel; +/* This file is intentionally left empty, see T15126 */ diff --git a/resources/sql/quickstart.sql b/resources/sql/quickstart.sql index b2796d31e6..2df0d4f252 100644 --- a/resources/sql/quickstart.sql +++ b/resources/sql/quickstart.sql @@ -1477,45 +1477,6 @@ CREATE TABLE `edgedata` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; -CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_chatlog` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; - -USE `{$NAMESPACE}_chatlog`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `chatlog_channel` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `serviceName` varchar(64) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `serviceType` varchar(32) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `channelName` varchar(64) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `viewPolicy` varbinary(64) NOT NULL, - `editPolicy` varbinary(64) NOT NULL, - `dateCreated` int(10) unsigned NOT NULL, - `dateModified` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `key_channel` (`channelName`,`serviceType`,`serviceName`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - -USE `{$NAMESPACE}_chatlog`; - - SET NAMES utf8 ; - - SET character_set_client = {$CHARSET} ; - -CREATE TABLE `chatlog_event` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `epoch` int(10) unsigned NOT NULL, - `author` varchar(64) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `type` varchar(4) CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `message` longtext CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} NOT NULL, - `loggedByPHID` varbinary(64) NOT NULL, - `channelID` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`), - KEY `channel` (`epoch`) -) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE={$COLLATE_TEXT}; - CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{$NAMESPACE}_conduit` /*!40100 DEFAULT CHARACTER SET {$CHARSET} COLLATE {$COLLATE_TEXT} */; USE `{$NAMESPACE}_conduit`; diff --git a/resources/sshd/phabricator-ssh-hook.sh b/resources/sshd/phorge-ssh-hook.sh similarity index 66% rename from resources/sshd/phabricator-ssh-hook.sh rename to resources/sshd/phorge-ssh-hook.sh index fc7de81dad..4e5843986e 100755 --- a/resources/sshd/phabricator-ssh-hook.sh +++ b/resources/sshd/phorge-ssh-hook.sh @@ -3,8 +3,8 @@ # NOTE: Replace this with the username that you expect users to connect with. VCSUSER="vcs-user" -# NOTE: Replace this with the path to your Phabricator directory. -ROOT="/path/to/phabricator" +# NOTE: Replace this with the path to your Phorge directory. +ROOT="/path/to/phorge" if [ "$1" != "$VCSUSER" ]; then diff --git a/resources/sshd/sshd_config.phabricator.example b/resources/sshd/sshd_config.phorge.example similarity index 84% rename from resources/sshd/sshd_config.phabricator.example rename to resources/sshd/sshd_config.phorge.example index 506d32bbbf..34c0d14d33 100644 --- a/resources/sshd/sshd_config.phabricator.example +++ b/resources/sshd/sshd_config.phorge.example @@ -3,7 +3,7 @@ # NOTE: Edit these to the correct values for your setup. -AuthorizedKeysCommand /usr/libexec/phabricator-ssh-hook.sh +AuthorizedKeysCommand /usr/libexec/phorge-ssh-hook.sh AuthorizedKeysCommandUser vcs-user AllowUsers vcs-user @@ -21,4 +21,4 @@ PasswordAuthentication no ChallengeResponseAuthentication no AuthorizedKeysFile none -PidFile /var/run/sshd-phabricator.pid +PidFile /var/run/sshd-phorge.pid diff --git a/scripts/celerity/generate_sprites.php b/scripts/celerity/generate_sprites.php index ccdd194b36..a85f9266ae 100755 --- a/scripts/celerity/generate_sprites.php +++ b/scripts/celerity/generate_sprites.php @@ -3,80 +3,6 @@ require_once dirname(dirname(__FILE__)).'/__init_script__.php'; -$args = new PhutilArgumentParser($argv); -$args->setTagline(pht('regenerate CSS sprite sheets')); -$args->setSynopsis(<<parseStandardArguments(); -$args->parse( - array( - array( - 'name' => 'force', - 'help' => pht('Force regeneration even if sources have not changed.'), - ), - )); - -$root = dirname(phutil_get_library_root('phabricator')); -$webroot = $root.'/webroot/rsrc'; -$webroot = Filesystem::readablePath($webroot); - -$generator = new CeleritySpriteGenerator(); - -$sheets = array( - 'tokens' => $generator->buildTokenSheet(), - 'login' => $generator->buildLoginSheet(), -); - -list($err) = exec_manual('optipng'); -if ($err) { - $have_optipng = false; - echo phutil_console_format( - " %s %s\n%s\n", - pht('WARNING'), - pht('`%s` not found in PATH.', 'optipng'), - pht('Sprites will not be optimized! Install `%s`!', 'optipng')); -} else { - $have_optipng = true; -} - -foreach ($sheets as $name => $sheet) { - - $sheet->setBasePath($root); - - $manifest_path = $root.'/resources/sprite/manifest/'.$name.'.json'; - if (!$args->getArg('force')) { - if (Filesystem::pathExists($manifest_path)) { - $data = Filesystem::readFile($manifest_path); - $data = phutil_json_decode($data); - if (!$sheet->needsRegeneration($data)) { - continue; - } - } - } - - $sheet - ->generateCSS($webroot."/css/sprite-{$name}.css") - ->generateManifest($root."/resources/sprite/manifest/{$name}.json"); - - foreach ($sheet->getScales() as $scale) { - if ($scale == 1) { - $sheet_name = "sprite-{$name}.png"; - } else { - $sheet_name = "sprite-{$name}-X{$scale}.png"; - } - - $full_path = "{$webroot}/image/{$sheet_name}"; - $sheet->generateImage($full_path, $scale); - - if ($have_optipng) { - echo pht('Optimizing...')."\n"; - phutil_passthru('optipng -o7 -clobber %s', $full_path); - } - } -} - -echo pht('Done.')."\n"; +echo pht('This script was replaced with `%s`!.', './bin/celerity sprites'); +echo "\n"; +exit(13); diff --git a/scripts/init/lib.php b/scripts/init/lib.php index 4c544da9d0..a12c394f3b 100644 --- a/scripts/init/lib.php +++ b/scripts/init/lib.php @@ -13,7 +13,7 @@ function init_phabricator_script(array $options) { if (!$ok) { echo 'FATAL ERROR: Unable to load the "Arcanist" library. '. - 'Put "arcanist/" next to "phabricator/" on disk.'; + 'Put "arcanist/" next to "phorge/" on disk.'; echo "\n"; exit(1); diff --git a/scripts/install/update_phabricator.sh b/scripts/install/update_phorge.sh similarity index 66% rename from scripts/install/update_phabricator.sh rename to scripts/install/update_phorge.sh index 3831acd963..c84583506b 100755 --- a/scripts/install/update_phabricator.sh +++ b/scripts/install/update_phorge.sh @@ -3,32 +3,35 @@ set -e set -x -# This is an example script for updating Phabricator, similar to the one used to -# update . It might not work perfectly on your -# system, but hopefully it should be easy to adapt. This script is not intended -# to work without modifications. +# This is an example script for updating Phabricator. It might not work +# perfectly on your system, but hopefully it should be easy to adapt. This +# script is not intended to work without modifications. # NOTE: This script assumes you are running it from a directory which contains -# arcanist/ and phabricator/. +# arcanist/ and phorge/. If you named them differently, you can change them +# here: +NAME_MAIN="phorge" +NAME_ARC="arcanist" + ROOT=`pwd` # You can hard-code the path here instead. ### UPDATE WORKING COPIES ###################################################### -cd $ROOT/arcanist +cd $ROOT/$NAME_ARC git pull -cd $ROOT/phabricator +cd $ROOT/$NAME_MAIN git pull ### CYCLE WEB SERVER AND DAEMONS ############################################### # Stop daemons. -$ROOT/phabricator/bin/phd stop +$ROOT/$NAME_MAIN/bin/phd stop # If running the notification server, stop it. -# $ROOT/phabricator/bin/aphlict stop +# $ROOT/$NAME_MAIN/bin/aphlict stop # Stop the webserver (apache, nginx, lighttpd, etc). This command will differ # depending on which system and webserver you are running: replace it with an @@ -40,14 +43,14 @@ sudo /etc/init.d/httpd stop # Upgrade the database schema. You may want to add the "--force" flag to allow # this script to run noninteractively. -$ROOT/phabricator/bin/storage upgrade +$ROOT/$NAME_MAIN/bin/storage upgrade # Restart the webserver. As above, this depends on your system and webserver. # NOTE: If you're running php-fpm, restart it here too. sudo /etc/init.d/httpd start # Restart daemons. -$ROOT/phabricator/bin/phd start +$ROOT/$NAME_MAIN/bin/phd start # If running the notification server, start it. -# $ROOT/phabricator/bin/aphlict start +# $ROOT/$NAME_MAIN/bin/aphlict start diff --git a/scripts/mail/mail_handler.php b/scripts/mail/mail_handler.php index bf6f315f3a..5631310a2b 100755 --- a/scripts/mail/mail_handler.php +++ b/scripts/mail/mail_handler.php @@ -55,7 +55,9 @@ } $headers = $parser->getHeaders(); -$headers['subject'] = phutil_decode_mime_header($headers['subject']); +if (array_key_exists('subject', $headers)) { + $headers['subject'] = phutil_decode_mime_header($headers['subject']); +} $headers['from'] = phutil_decode_mime_header($headers['from']); if ($args->getArg('process-duplicates')) { diff --git a/scripts/repository/commit_hook.php b/scripts/repository/commit_hook.php index ccbfde08ff..08f4600b9b 100755 --- a/scripts/repository/commit_hook.php +++ b/scripts/repository/commit_hook.php @@ -119,12 +119,14 @@ exit($err); } else if ($repository->isGit() || $repository->isHg()) { $username = getenv(DiffusionCommitHookEngine::ENV_USER); - if (!strlen($username)) { - throw new Exception( - pht( - 'No Direct Pushes: You are pushing directly to a hosted repository. '. - 'This will not work. See "No Direct Pushes" in the documentation '. - 'for more information.')); + if ($username !== false) { + if (!phutil_nonempty_string($username)) { + throw new Exception( + pht( + 'No Direct Pushes: You are pushing directly to a hosted repository. '. + 'This will not work. See "No Direct Pushes" in the documentation '. + 'for more information.')); + } } if ($repository->isHg()) { @@ -181,18 +183,24 @@ $engine->setOriginalArgv(array_slice($argv, 2)); $remote_address = getenv(DiffusionCommitHookEngine::ENV_REMOTE_ADDRESS); -if (strlen($remote_address)) { - $engine->setRemoteAddress($remote_address); +if ($remote_address !== false) { + if (phutil_nonempty_string($remote_address)) { + $engine->setRemoteAddress($remote_address); + } } $remote_protocol = getenv(DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL); -if (strlen($remote_protocol)) { - $engine->setRemoteProtocol($remote_protocol); +if ($remote_protocol !== false) { + if (phutil_nonempty_string($remote_protocol)) { + $engine->setRemoteProtocol($remote_protocol); + } } $request_identifier = getenv(DiffusionCommitHookEngine::ENV_REQUEST); -if (strlen($request_identifier)) { - $engine->setRequestIdentifier($request_identifier); +if ($request_identifier !== false) { + if (phutil_nonempty_string($request_identifier)) { + $engine->setRequestIdentifier($request_identifier); + } } try { diff --git a/scripts/ssh/ssh-auth.php b/scripts/ssh/ssh-auth.php index 45e9a823db..2b3b52cf4b 100755 --- a/scripts/ssh/ssh-auth.php +++ b/scripts/ssh/ssh-auth.php @@ -36,7 +36,7 @@ $authstruct = null; -if (strlen($authstruct_raw)) { +if (phutil_nonempty_string($authstruct_raw)) { try { $authstruct = phutil_json_decode($authstruct_raw); } catch (Exception $ex) { @@ -82,13 +82,13 @@ // Strip out newlines and other nonsense from the key type and key body. $type = $ssh_key->getKeyType(); $type = preg_replace('@[\x00-\x20]+@', '', $type); - if (!strlen($type)) { + if (!phutil_nonempty_string($type)) { continue; } $key = $ssh_key->getKeyBody(); $key = preg_replace('@[\x00-\x20]+@', '', $key); - if (!strlen($key)) { + if (!phutil_nonempty_string($key)) { continue; } @@ -135,7 +135,7 @@ $cmd = csprintf('%s %Ls', $bin, $key_argv); - if (strlen($instance)) { + if (phutil_nonempty_string($instance)) { $cmd = csprintf('PHABRICATOR_INSTANCE=%s %C', $instance, $cmd); } diff --git a/scripts/ssh/ssh-exec.php b/scripts/ssh/ssh-exec.php index 70c95d28da..7b5b6adb8f 100755 --- a/scripts/ssh/ssh-exec.php +++ b/scripts/ssh/ssh-exec.php @@ -103,7 +103,7 @@ '--phabricator-ssh-device', $user_name, $device_name)); - } else if (strlen($user_name)) { + } else if (phutil_nonempty_string($user_name)) { $user = id(new PhabricatorPeopleQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withUsernames(array($user_name)) @@ -117,7 +117,7 @@ id(new PhabricatorAuthSessionEngine()) ->willServeRequestForUser($user); - } else if (strlen($device_name)) { + } else if (phutil_nonempty_string($device_name)) { if (!$remote_address) { throw new Exception( pht( diff --git a/scripts/symbols/generate_ctags_symbols.php b/scripts/symbols/generate_ctags_symbols.php index e93b0c5cbc..13972385ab 100755 --- a/scripts/symbols/generate_ctags_symbols.php +++ b/scripts/symbols/generate_ctags_symbols.php @@ -39,7 +39,7 @@ $futures = array(); foreach (explode("\n", trim($input)) as $file) { - if (!strlen($file)) { + if (!phutil_nonempty_string($file)) { continue; } diff --git a/scripts/symbols/generate_php_symbols.php b/scripts/symbols/generate_php_symbols.php index af87d580d8..cc8cab3817 100755 --- a/scripts/symbols/generate_php_symbols.php +++ b/scripts/symbols/generate_php_symbols.php @@ -27,7 +27,7 @@ $futures = array(); foreach (explode("\n", trim($input)) as $file) { - if (!strlen($file)) { + if (!phutil_nonempty_string($file)) { continue; } diff --git a/src/__phutil_library_init__.php b/src/__phutil_library_init__.php index f150c1f94f..55c0912aba 100644 --- a/src/__phutil_library_init__.php +++ b/src/__phutil_library_init__.php @@ -1,3 +1,3 @@ 'applications/celerity/postprocessor/CelerityDefaultPostprocessor.php', 'CelerityHighContrastPostprocessor' => 'applications/celerity/postprocessor/CelerityHighContrastPostprocessor.php', 'CelerityLargeFontPostprocessor' => 'applications/celerity/postprocessor/CelerityLargeFontPostprocessor.php', + 'CelerityManagementGenerateSpritesWorkflow' => 'applications/celerity/management/CelerityManagementGenerateSpritesWorkflow.php', 'CelerityManagementMapWorkflow' => 'applications/celerity/management/CelerityManagementMapWorkflow.php', 'CelerityManagementSyntaxWorkflow' => 'applications/celerity/management/CelerityManagementSyntaxWorkflow.php', 'CelerityManagementWorkflow' => 'applications/celerity/management/CelerityManagementWorkflow.php', @@ -338,9 +339,6 @@ 'CelerityResourcesOnDisk' => 'applications/celerity/resources/CelerityResourcesOnDisk.php', 'CeleritySpriteGenerator' => 'applications/celerity/CeleritySpriteGenerator.php', 'CelerityStaticResourceResponse' => 'applications/celerity/CelerityStaticResourceResponse.php', - 'ChatLogConduitAPIMethod' => 'applications/chatlog/conduit/ChatLogConduitAPIMethod.php', - 'ChatLogQueryConduitAPIMethod' => 'applications/chatlog/conduit/ChatLogQueryConduitAPIMethod.php', - 'ChatLogRecordConduitAPIMethod' => 'applications/chatlog/conduit/ChatLogRecordConduitAPIMethod.php', 'ConduitAPIDocumentationPage' => 'applications/conduit/data/ConduitAPIDocumentationPage.php', 'ConduitAPIMethod' => 'applications/conduit/method/ConduitAPIMethod.php', 'ConduitAPIMethodTestCase' => 'applications/conduit/method/__tests__/ConduitAPIMethodTestCase.php', @@ -465,6 +463,7 @@ 'DifferentialBlockHeraldAction' => 'applications/differential/herald/DifferentialBlockHeraldAction.php', 'DifferentialBlockingReviewerDatasource' => 'applications/differential/typeahead/DifferentialBlockingReviewerDatasource.php', 'DifferentialBranchField' => 'applications/differential/customfield/DifferentialBranchField.php', + 'DifferentialBranchFieldTestCase' => 'applications/differential/customfield/__tests__/DifferentialBranchFieldTestCase.php', 'DifferentialBuildableEngine' => 'applications/differential/harbormaster/DifferentialBuildableEngine.php', 'DifferentialChangeDetailMailView' => 'applications/differential/mail/DifferentialChangeDetailMailView.php', 'DifferentialChangeHeraldFieldGroup' => 'applications/differential/herald/DifferentialChangeHeraldFieldGroup.php', @@ -486,6 +485,7 @@ 'DifferentialChangesetRenderer' => 'applications/differential/render/DifferentialChangesetRenderer.php', 'DifferentialChangesetSearchConduitAPIMethod' => 'applications/differential/conduit/DifferentialChangesetSearchConduitAPIMethod.php', 'DifferentialChangesetSearchEngine' => 'applications/differential/query/DifferentialChangesetSearchEngine.php', + 'DifferentialChangesetTestCase' => 'applications/differential/storage/__tests__/DifferentialChangesetTestCase.php', 'DifferentialChangesetTestRenderer' => 'applications/differential/render/DifferentialChangesetTestRenderer.php', 'DifferentialChangesetTwoUpRenderer' => 'applications/differential/render/DifferentialChangesetTwoUpRenderer.php', 'DifferentialChangesetTwoUpTestRenderer' => 'applications/differential/render/DifferentialChangesetTwoUpTestRenderer.php', @@ -1051,6 +1051,7 @@ 'DiffusionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSSHWorkflow.php', 'DiffusionSearchQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionSearchQueryConduitAPIMethod.php', 'DiffusionServeController' => 'applications/diffusion/controller/DiffusionServeController.php', + 'DiffusionServeControllerTestCase' => 'applications/diffusion/controller/__tests__/DiffusionServeControllerTestCase.php', 'DiffusionServiceRef' => 'applications/diffusion/ref/DiffusionServiceRef.php', 'DiffusionSetPasswordSettingsPanel' => 'applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php', 'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php', @@ -1494,13 +1495,13 @@ 'HarbormasterBuildableTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildableTransactionQuery.php', 'HarbormasterBuildableTransactionType' => 'applications/harbormaster/xaction/buildable/HarbormasterBuildableTransactionType.php', 'HarbormasterBuildableViewController' => 'applications/harbormaster/controller/HarbormasterBuildableViewController.php', - 'HarbormasterBuildkiteBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterBuildkiteBuildStepImplementation.php', + 'HarbormasterBuildkiteBuildStepImplementation' => 'applications/harbormaster/integration/buildkite/HarbormasterBuildkiteBuildStepImplementation.php', 'HarbormasterBuildkiteBuildableInterface' => 'applications/harbormaster/interface/HarbormasterBuildkiteBuildableInterface.php', - 'HarbormasterBuildkiteHookController' => 'applications/harbormaster/controller/HarbormasterBuildkiteHookController.php', + 'HarbormasterBuildkiteHookHandler' => 'applications/harbormaster/integration/buildkite/HarbormasterBuildkiteHookHandler.php', 'HarbormasterBuiltinBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterBuiltinBuildStepGroup.php', - 'HarbormasterCircleCIBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterCircleCIBuildStepImplementation.php', + 'HarbormasterCircleCIBuildStepImplementation' => 'applications/harbormaster/integration/circleci/HarbormasterCircleCIBuildStepImplementation.php', 'HarbormasterCircleCIBuildableInterface' => 'applications/harbormaster/interface/HarbormasterCircleCIBuildableInterface.php', - 'HarbormasterCircleCIHookController' => 'applications/harbormaster/controller/HarbormasterCircleCIHookController.php', + 'HarbormasterCircleCIHookHandler' => 'applications/harbormaster/integration/circleci/HarbormasterCircleCIHookHandler.php', 'HarbormasterConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterConduitAPIMethod.php', 'HarbormasterControlBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterControlBuildStepGroup.php', 'HarbormasterController' => 'applications/harbormaster/controller/HarbormasterController.php', @@ -1514,6 +1515,8 @@ 'HarbormasterExternalBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterExternalBuildStepGroup.php', 'HarbormasterFileArtifact' => 'applications/harbormaster/artifact/HarbormasterFileArtifact.php', 'HarbormasterHTTPRequestBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php', + 'HarbormasterHookController' => 'applications/harbormaster/controller/HarbormasterHookController.php', + 'HarbormasterHookHandler' => 'applications/harbormaster/integration/HarbormasterHookHandler.php', 'HarbormasterHostArtifact' => 'applications/harbormaster/artifact/HarbormasterHostArtifact.php', 'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseWorkingCopyBuildStepImplementation.php', 'HarbormasterLintMessagesController' => 'applications/harbormaster/controller/HarbormasterLintMessagesController.php', @@ -1736,6 +1739,7 @@ 'LegalpadDocumentQuery' => 'applications/legalpad/query/LegalpadDocumentQuery.php', 'LegalpadDocumentRemarkupRule' => 'applications/legalpad/remarkup/LegalpadDocumentRemarkupRule.php', 'LegalpadDocumentRequireSignatureTransaction' => 'applications/legalpad/xaction/LegalpadDocumentRequireSignatureTransaction.php', + 'LegalpadDocumentSearchConduitAPIMethod' => 'applications/legalpad/conduit/LegalpadDocumentSearchConduitAPIMethod.php', 'LegalpadDocumentSearchEngine' => 'applications/legalpad/query/LegalpadDocumentSearchEngine.php', 'LegalpadDocumentSignController' => 'applications/legalpad/controller/LegalpadDocumentSignController.php', 'LegalpadDocumentSignature' => 'applications/legalpad/storage/LegalpadDocumentSignature.php', @@ -1755,6 +1759,7 @@ 'LegalpadRequireSignatureHeraldAction' => 'applications/legalpad/herald/LegalpadRequireSignatureHeraldAction.php', 'LegalpadSchemaSpec' => 'applications/legalpad/storage/LegalpadSchemaSpec.php', 'LegalpadSignatureNeededByObjectEdgeType' => 'applications/legalpad/edge/LegalpadSignatureNeededByObjectEdgeType.php', + 'LegalpadSignatureSearchConduitAPIMethod' => 'applications/legalpad/conduit/LegalpadSignatureSearchConduitAPIMethod.php', 'LegalpadTransaction' => 'applications/legalpad/storage/LegalpadTransaction.php', 'LegalpadTransactionComment' => 'applications/legalpad/storage/LegalpadTransactionComment.php', 'LegalpadTransactionQuery' => 'applications/legalpad/query/LegalpadTransactionQuery.php', @@ -2800,15 +2805,6 @@ 'PhabricatorChartInterval' => 'applications/fact/chart/PhabricatorChartInterval.php', 'PhabricatorChartRenderingEngine' => 'applications/fact/engine/PhabricatorChartRenderingEngine.php', 'PhabricatorChartStackedAreaDataset' => 'applications/fact/chart/PhabricatorChartStackedAreaDataset.php', - 'PhabricatorChatLogApplication' => 'applications/chatlog/application/PhabricatorChatLogApplication.php', - 'PhabricatorChatLogChannel' => 'applications/chatlog/storage/PhabricatorChatLogChannel.php', - 'PhabricatorChatLogChannelListController' => 'applications/chatlog/controller/PhabricatorChatLogChannelListController.php', - 'PhabricatorChatLogChannelLogController' => 'applications/chatlog/controller/PhabricatorChatLogChannelLogController.php', - 'PhabricatorChatLogChannelQuery' => 'applications/chatlog/query/PhabricatorChatLogChannelQuery.php', - 'PhabricatorChatLogController' => 'applications/chatlog/controller/PhabricatorChatLogController.php', - 'PhabricatorChatLogDAO' => 'applications/chatlog/storage/PhabricatorChatLogDAO.php', - 'PhabricatorChatLogEvent' => 'applications/chatlog/storage/PhabricatorChatLogEvent.php', - 'PhabricatorChatLogQuery' => 'applications/chatlog/query/PhabricatorChatLogQuery.php', 'PhabricatorCheckboxesEditField' => 'applications/transactions/editfield/PhabricatorCheckboxesEditField.php', 'PhabricatorChunkedFileStorageEngine' => 'applications/files/engine/PhabricatorChunkedFileStorageEngine.php', 'PhabricatorClassConfigType' => 'applications/config/type/PhabricatorClassConfigType.php', @@ -2971,6 +2967,7 @@ 'PhabricatorCountdownApplication' => 'applications/countdown/application/PhabricatorCountdownApplication.php', 'PhabricatorCountdownController' => 'applications/countdown/controller/PhabricatorCountdownController.php', 'PhabricatorCountdownCountdownPHIDType' => 'applications/countdown/phid/PhabricatorCountdownCountdownPHIDType.php', + 'PhabricatorCountdownCreateCapability' => 'applications/countdown/capability/PhabricatorCountdownCreateCapability.php', 'PhabricatorCountdownDAO' => 'applications/countdown/storage/PhabricatorCountdownDAO.php', 'PhabricatorCountdownDefaultEditCapability' => 'applications/countdown/capability/PhabricatorCountdownDefaultEditCapability.php', 'PhabricatorCountdownDefaultViewCapability' => 'applications/countdown/capability/PhabricatorCountdownDefaultViewCapability.php', @@ -3075,6 +3072,7 @@ 'PhabricatorDashboardColumn' => 'applications/dashboard/layoutconfig/PhabricatorDashboardColumn.php', 'PhabricatorDashboardConsoleController' => 'applications/dashboard/controller/PhabricatorDashboardConsoleController.php', 'PhabricatorDashboardController' => 'applications/dashboard/controller/PhabricatorDashboardController.php', + 'PhabricatorDashboardCreateCapability' => 'applications/countdown/capability/PhabricatorDashboardCreateCapability.php', 'PhabricatorDashboardDAO' => 'applications/dashboard/storage/PhabricatorDashboardDAO.php', 'PhabricatorDashboardDashboardPHIDType' => 'applications/dashboard/phid/PhabricatorDashboardDashboardPHIDType.php', 'PhabricatorDashboardDatasource' => 'applications/dashboard/typeahead/PhabricatorDashboardDatasource.php', @@ -3676,8 +3674,11 @@ 'PhabricatorLabelProfileMenuItem' => 'applications/search/menuitem/PhabricatorLabelProfileMenuItem.php', 'PhabricatorLanguageSettingsPanel' => 'applications/settings/panel/PhabricatorLanguageSettingsPanel.php', 'PhabricatorLegalpadApplication' => 'applications/legalpad/application/PhabricatorLegalpadApplication.php', + 'PhabricatorLegalpadBodySearchEngineAttachment' => 'applications/legalpad/engineextension/PhabricatorLegalpadBodySearchEngineAttachment.php', 'PhabricatorLegalpadDocumentPHIDType' => 'applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php', + 'PhabricatorLegalpadDocumentSignaturePHIDType' => 'applications/legalpad/phid/PhabricatorLegalpadDocumentSignaturePHIDType.php', 'PhabricatorLegalpadSignaturePolicyRule' => 'applications/legalpad/policyrule/PhabricatorLegalpadSignaturePolicyRule.php', + 'PhabricatorLegalpadSignaturesSearchEngineAttachment' => 'applications/legalpad/engineextension/PhabricatorLegalpadSignaturesSearchEngineAttachment.php', 'PhabricatorLibraryTestCase' => '__tests__/PhabricatorLibraryTestCase.php', 'PhabricatorLinkProfileMenuItem' => 'applications/search/menuitem/PhabricatorLinkProfileMenuItem.php', 'PhabricatorLipsumArtist' => 'applications/lipsum/image/PhabricatorLipsumArtist.php', @@ -4615,6 +4616,7 @@ 'PhabricatorRepositoryIdentityAssignTransaction' => 'applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php', 'PhabricatorRepositoryIdentityChangeWorker' => 'applications/repository/worker/PhabricatorRepositoryIdentityChangeWorker.php', 'PhabricatorRepositoryIdentityEditEngine' => 'applications/repository/engine/PhabricatorRepositoryIdentityEditEngine.php', + 'PhabricatorRepositoryIdentityEditViewCapability' => 'applications/repository/capability/PhabricatorRepositoryIdentityEditViewCapability.php', 'PhabricatorRepositoryIdentityFerretEngine' => 'applications/repository/search/PhabricatorRepositoryIdentityFerretEngine.php', 'PhabricatorRepositoryIdentityPHIDType' => 'applications/repository/phid/PhabricatorRepositoryIdentityPHIDType.php', 'PhabricatorRepositoryIdentityQuery' => 'applications/repository/query/PhabricatorRepositoryIdentityQuery.php', @@ -5385,6 +5387,8 @@ 'PholioTransactionType' => 'applications/pholio/xaction/PholioTransactionType.php', 'PholioTransactionView' => 'applications/pholio/view/PholioTransactionView.php', 'PholioUploadedImageView' => 'applications/pholio/view/PholioUploadedImageView.php', + 'PhorgeCodeWarningSetupCheck' => 'applications/config/check/PhorgeCodeWarningSetupCheck.php', + 'PhorgeSystemDeprecationWarningListener' => 'applications/system/events/PhorgeSystemDeprecationWarningListener.php', 'PhortuneAccount' => 'applications/phortune/storage/PhortuneAccount.php', 'PhortuneAccountAddManagerController' => 'applications/phortune/controller/account/PhortuneAccountAddManagerController.php', 'PhortuneAccountBillingAddressTransaction' => 'applications/phortune/xaction/PhortuneAccountBillingAddressTransaction.php', @@ -5632,6 +5636,7 @@ 'PhrictionEditEngineController' => 'applications/phriction/controller/PhrictionEditEngineController.php', 'PhrictionHistoryConduitAPIMethod' => 'applications/phriction/conduit/PhrictionHistoryConduitAPIMethod.php', 'PhrictionHistoryController' => 'applications/phriction/controller/PhrictionHistoryController.php', + 'PhrictionHovercardEngineExtension' => 'applications/phriction/engineextension/PhrictionHovercardEngineExtension.php', 'PhrictionInfoConduitAPIMethod' => 'applications/phriction/conduit/PhrictionInfoConduitAPIMethod.php', 'PhrictionListController' => 'applications/phriction/controller/PhrictionListController.php', 'PhrictionMarkupPreviewController' => 'applications/phriction/controller/PhrictionMarkupPreviewController.php', @@ -5836,6 +5841,7 @@ 'PonderQuestionSearchEngine' => 'applications/ponder/query/PonderQuestionSearchEngine.php', 'PonderQuestionStatus' => 'applications/ponder/constants/PonderQuestionStatus.php', 'PonderQuestionStatusController' => 'applications/ponder/controller/PonderQuestionStatusController.php', + 'PonderQuestionStatusTestCase' => 'applications/ponder/storage/__tests__/PonderQuestionStatusTestCase.php', 'PonderQuestionStatusTransaction' => 'applications/ponder/xaction/PonderQuestionStatusTransaction.php', 'PonderQuestionTitleTransaction' => 'applications/ponder/xaction/PonderQuestionTitleTransaction.php', 'PonderQuestionTransaction' => 'applications/ponder/storage/PonderQuestionTransaction.php', @@ -6302,6 +6308,7 @@ 'CelerityDefaultPostprocessor' => 'CelerityPostprocessor', 'CelerityHighContrastPostprocessor' => 'CelerityPostprocessor', 'CelerityLargeFontPostprocessor' => 'CelerityPostprocessor', + 'CelerityManagementGenerateSpritesWorkflow' => 'CelerityManagementWorkflow', 'CelerityManagementMapWorkflow' => 'CelerityManagementWorkflow', 'CelerityManagementSyntaxWorkflow' => 'CelerityManagementWorkflow', 'CelerityManagementWorkflow' => 'PhabricatorManagementWorkflow', @@ -6322,9 +6329,6 @@ 'CelerityResourcesOnDisk' => 'CelerityPhysicalResources', 'CeleritySpriteGenerator' => 'Phobject', 'CelerityStaticResourceResponse' => 'Phobject', - 'ChatLogConduitAPIMethod' => 'ConduitAPIMethod', - 'ChatLogQueryConduitAPIMethod' => 'ChatLogConduitAPIMethod', - 'ChatLogRecordConduitAPIMethod' => 'ChatLogConduitAPIMethod', 'ConduitAPIDocumentationPage' => 'Phobject', 'ConduitAPIMethod' => array( 'Phobject', @@ -6459,6 +6463,7 @@ 'DifferentialBlockHeraldAction' => 'HeraldAction', 'DifferentialBlockingReviewerDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'DifferentialBranchField' => 'DifferentialCustomField', + 'DifferentialBranchFieldTestCase' => 'PhabricatorTestCase', 'DifferentialBuildableEngine' => 'HarbormasterBuildableEngine', 'DifferentialChangeDetailMailView' => 'DifferentialMailView', 'DifferentialChangeHeraldFieldGroup' => 'HeraldFieldGroup', @@ -6485,6 +6490,7 @@ 'DifferentialChangesetRenderer' => 'Phobject', 'DifferentialChangesetSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'DifferentialChangesetSearchEngine' => 'PhabricatorApplicationSearchEngine', + 'DifferentialChangesetTestCase' => 'PhabricatorTestCase', 'DifferentialChangesetTestRenderer' => 'DifferentialChangesetRenderer', 'DifferentialChangesetTwoUpRenderer' => 'DifferentialChangesetHTMLRenderer', 'DifferentialChangesetTwoUpTestRenderer' => 'DifferentialChangesetTestRenderer', @@ -6548,7 +6554,7 @@ 'DifferentialDiffRepositoryProjectsHeraldField' => 'DifferentialDiffHeraldField', 'DifferentialDiffSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'DifferentialDiffSearchEngine' => 'PhabricatorApplicationSearchEngine', - 'DifferentialDiffTestCase' => 'PhutilTestCase', + 'DifferentialDiffTestCase' => 'PhabricatorTestCase', 'DifferentialDiffTransaction' => 'PhabricatorApplicationTransaction', 'DifferentialDiffTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'DifferentialDiffViewController' => 'DifferentialController', @@ -7091,6 +7097,7 @@ 'DiffusionSSHWorkflow' => 'PhabricatorSSHWorkflow', 'DiffusionSearchQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod', 'DiffusionServeController' => 'DiffusionController', + 'DiffusionServeControllerTestCase' => 'PhabricatorTestCase', 'DiffusionServiceRef' => 'Phobject', 'DiffusionSetPasswordSettingsPanel' => 'PhabricatorSettingsPanel', 'DiffusionSetupException' => 'Exception', @@ -7648,10 +7655,10 @@ 'HarbormasterBuildableTransactionType' => 'PhabricatorModularTransactionType', 'HarbormasterBuildableViewController' => 'HarbormasterController', 'HarbormasterBuildkiteBuildStepImplementation' => 'HarbormasterBuildStepImplementation', - 'HarbormasterBuildkiteHookController' => 'HarbormasterController', + 'HarbormasterBuildkiteHookHandler' => 'HarbormasterHookHandler', 'HarbormasterBuiltinBuildStepGroup' => 'HarbormasterBuildStepGroup', 'HarbormasterCircleCIBuildStepImplementation' => 'HarbormasterBuildStepImplementation', - 'HarbormasterCircleCIHookController' => 'HarbormasterController', + 'HarbormasterCircleCIHookHandler' => 'HarbormasterHookHandler', 'HarbormasterConduitAPIMethod' => 'ConduitAPIMethod', 'HarbormasterControlBuildStepGroup' => 'HarbormasterBuildStepGroup', 'HarbormasterController' => 'PhabricatorController', @@ -7665,6 +7672,8 @@ 'HarbormasterExternalBuildStepGroup' => 'HarbormasterBuildStepGroup', 'HarbormasterFileArtifact' => 'HarbormasterArtifact', 'HarbormasterHTTPRequestBuildStepImplementation' => 'HarbormasterBuildStepImplementation', + 'HarbormasterHookController' => 'HarbormasterController', + 'HarbormasterHookHandler' => 'Phobject', 'HarbormasterHostArtifact' => 'HarbormasterDrydockLeaseArtifact', 'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 'HarbormasterLintMessagesController' => 'HarbormasterController', @@ -7908,6 +7917,7 @@ 'PhabricatorSubscribableInterface', 'PhabricatorApplicationTransactionInterface', 'PhabricatorDestructibleInterface', + 'PhabricatorConduitResultInterface', ), 'LegalpadDocumentBody' => array( 'LegalpadDAO', @@ -7924,11 +7934,13 @@ 'LegalpadDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'LegalpadDocumentRemarkupRule' => 'PhabricatorObjectRemarkupRule', 'LegalpadDocumentRequireSignatureTransaction' => 'LegalpadDocumentTransactionType', + 'LegalpadDocumentSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'LegalpadDocumentSearchEngine' => 'PhabricatorApplicationSearchEngine', 'LegalpadDocumentSignController' => 'LegalpadController', 'LegalpadDocumentSignature' => array( 'LegalpadDAO', 'PhabricatorPolicyInterface', + 'PhabricatorConduitResultInterface', ), 'LegalpadDocumentSignatureAddController' => 'LegalpadController', 'LegalpadDocumentSignatureListController' => 'LegalpadController', @@ -7946,6 +7958,7 @@ 'LegalpadRequireSignatureHeraldAction' => 'HeraldAction', 'LegalpadSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'LegalpadSignatureNeededByObjectEdgeType' => 'PhabricatorEdgeType', + 'LegalpadSignatureSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'LegalpadTransaction' => 'PhabricatorModularTransaction', 'LegalpadTransactionComment' => 'PhabricatorApplicationTransactionComment', 'LegalpadTransactionQuery' => 'PhabricatorApplicationTransactionQuery', @@ -8388,6 +8401,7 @@ 'PhabricatorApplicationTransactionInterface', 'PhabricatorPolicyInterface', 'PhabricatorFlaggableInterface', + 'PhabricatorMentionableInterface', 'PhabricatorSubscribableInterface', 'PhabricatorDestructibleInterface', 'PhabricatorSpacesInterface', @@ -9164,21 +9178,6 @@ 'PhabricatorChartInterval' => 'Phobject', 'PhabricatorChartRenderingEngine' => 'Phobject', 'PhabricatorChartStackedAreaDataset' => 'PhabricatorChartDataset', - 'PhabricatorChatLogApplication' => 'PhabricatorApplication', - 'PhabricatorChatLogChannel' => array( - 'PhabricatorChatLogDAO', - 'PhabricatorPolicyInterface', - ), - 'PhabricatorChatLogChannelListController' => 'PhabricatorChatLogController', - 'PhabricatorChatLogChannelLogController' => 'PhabricatorChatLogController', - 'PhabricatorChatLogChannelQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', - 'PhabricatorChatLogController' => 'PhabricatorController', - 'PhabricatorChatLogDAO' => 'PhabricatorLiskDAO', - 'PhabricatorChatLogEvent' => array( - 'PhabricatorChatLogDAO', - 'PhabricatorPolicyInterface', - ), - 'PhabricatorChatLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorCheckboxesEditField' => 'PhabricatorEditField', 'PhabricatorChunkedFileStorageEngine' => 'PhabricatorFileStorageEngine', 'PhabricatorClassConfigType' => 'PhabricatorTextConfigType', @@ -9361,6 +9360,7 @@ 'PhabricatorCountdownApplication' => 'PhabricatorApplication', 'PhabricatorCountdownController' => 'PhabricatorController', 'PhabricatorCountdownCountdownPHIDType' => 'PhabricatorPHIDType', + 'PhabricatorCountdownCreateCapability' => 'PhabricatorPolicyCapability', 'PhabricatorCountdownDAO' => 'PhabricatorLiskDAO', 'PhabricatorCountdownDefaultEditCapability' => 'PhabricatorPolicyCapability', 'PhabricatorCountdownDefaultViewCapability' => 'PhabricatorPolicyCapability', @@ -9477,6 +9477,7 @@ 'PhabricatorDashboardColumn' => 'Phobject', 'PhabricatorDashboardConsoleController' => 'PhabricatorDashboardController', 'PhabricatorDashboardController' => 'PhabricatorController', + 'PhabricatorDashboardCreateCapability' => 'PhabricatorPolicyCapability', 'PhabricatorDashboardDAO' => 'PhabricatorLiskDAO', 'PhabricatorDashboardDashboardPHIDType' => 'PhabricatorPHIDType', 'PhabricatorDashboardDatasource' => 'PhabricatorTypeaheadDatasource', @@ -10157,8 +10158,11 @@ 'PhabricatorLabelProfileMenuItem' => 'PhabricatorProfileMenuItem', 'PhabricatorLanguageSettingsPanel' => 'PhabricatorEditEngineSettingsPanel', 'PhabricatorLegalpadApplication' => 'PhabricatorApplication', + 'PhabricatorLegalpadBodySearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 'PhabricatorLegalpadDocumentPHIDType' => 'PhabricatorPHIDType', + 'PhabricatorLegalpadDocumentSignaturePHIDType' => 'PhabricatorPHIDType', 'PhabricatorLegalpadSignaturePolicyRule' => 'PhabricatorPolicyRule', + 'PhabricatorLegalpadSignaturesSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 'PhabricatorLibraryTestCase' => 'PhutilLibraryTestCase', 'PhabricatorLinkProfileMenuItem' => 'PhabricatorProfileMenuItem', 'PhabricatorLipsumArtist' => 'Phobject', @@ -11294,6 +11298,7 @@ 'PhabricatorRepositoryIdentityAssignTransaction' => 'PhabricatorRepositoryIdentityTransactionType', 'PhabricatorRepositoryIdentityChangeWorker' => 'PhabricatorWorker', 'PhabricatorRepositoryIdentityEditEngine' => 'PhabricatorEditEngine', + 'PhabricatorRepositoryIdentityEditViewCapability' => 'PhabricatorPolicyCapability', 'PhabricatorRepositoryIdentityFerretEngine' => 'PhabricatorFerretEngine', 'PhabricatorRepositoryIdentityPHIDType' => 'PhabricatorPHIDType', 'PhabricatorRepositoryIdentityQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', @@ -12205,6 +12210,8 @@ 'PholioTransactionType' => 'PhabricatorModularTransactionType', 'PholioTransactionView' => 'PhabricatorApplicationTransactionView', 'PholioUploadedImageView' => 'AphrontView', + 'PhorgeCodeWarningSetupCheck' => 'PhabricatorSetupCheck', + 'PhorgeSystemDeprecationWarningListener' => 'PhabricatorEventListener', 'PhortuneAccount' => array( 'PhortuneDAO', 'PhabricatorApplicationTransactionInterface', @@ -12517,6 +12524,7 @@ 'PhrictionEditEngineController' => 'PhrictionController', 'PhrictionHistoryConduitAPIMethod' => 'PhrictionConduitAPIMethod', 'PhrictionHistoryController' => 'PhrictionController', + 'PhrictionHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', 'PhrictionInfoConduitAPIMethod' => 'PhrictionConduitAPIMethod', 'PhrictionListController' => 'PhrictionController', 'PhrictionMarkupPreviewController' => 'PhabricatorController', @@ -12740,6 +12748,7 @@ 'PonderQuestionSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PonderQuestionStatus' => 'PonderConstants', 'PonderQuestionStatusController' => 'PonderController', + 'PonderQuestionStatusTestCase' => 'PhutilTestCase', 'PonderQuestionStatusTransaction' => 'PonderQuestionTransactionType', 'PonderQuestionTitleTransaction' => 'PonderQuestionTransactionType', 'PonderQuestionTransaction' => 'PhabricatorModularTransaction', diff --git a/src/aphront/AphrontRequest.php b/src/aphront/AphrontRequest.php index f35f3f1279..2561e397b6 100644 --- a/src/aphront/AphrontRequest.php +++ b/src/aphront/AphrontRequest.php @@ -66,7 +66,7 @@ public function getURILineRange($key, $limit) { } public static function parseURILineRange($range, $limit) { - if (!strlen($range)) { + if (!phutil_nonempty_string($range)) { return null; } @@ -234,7 +234,7 @@ public function getJSONMap($name, $default = array()) { $raw_data = phutil_string_cast($this->requestData[$name]); $raw_data = trim($raw_data); - if (!strlen($raw_data)) { + if (!phutil_nonempty_string($raw_data)) { return $default; } @@ -448,7 +448,7 @@ public function setCookiePrefix($prefix) { } private function getPrefixedCookieName($name) { - if (strlen($this->cookiePrefix)) { + if (phutil_nonempty_string($this->cookiePrefix)) { return $this->cookiePrefix.'_'.$name; } else { return $name; @@ -499,7 +499,7 @@ private function getCookieDomainURI() { // domain is. This makes setup easier, and we'll tell administrators to // configure a base domain during the setup process. $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); - if (!strlen($base_uri)) { + if (!phutil_nonempty_string($base_uri)) { return new PhutilURI('http://'.$host.'/'); } @@ -956,7 +956,7 @@ public function updateEphemeralCookies() { $submit_cookie = PhabricatorCookies::COOKIE_SUBMIT; $submit_key = $this->getCookie($submit_cookie); - if (strlen($submit_key)) { + if (phutil_nonempty_string($submit_key)) { $this->clearCookie($submit_cookie); $this->submitKey = $submit_key; } diff --git a/src/aphront/configuration/AphrontApplicationConfiguration.php b/src/aphront/configuration/AphrontApplicationConfiguration.php index 550a5a0316..3bdd2c00bc 100644 --- a/src/aphront/configuration/AphrontApplicationConfiguration.php +++ b/src/aphront/configuration/AphrontApplicationConfiguration.php @@ -823,7 +823,7 @@ private static function readHTTPPOSTData() { $raw_input = PhabricatorStartup::getRawInput(); $parser = new PhutilQueryStringParser(); - if (strlen($raw_input)) { + if (phutil_nonempty_string($raw_input)) { $content_type = idx($_SERVER, 'CONTENT_TYPE'); $is_multipart = preg_match('@^multipart/form-data@i', $content_type); if ($is_multipart) { diff --git a/src/aphront/response/AphrontAjaxResponse.php b/src/aphront/response/AphrontAjaxResponse.php index 2187defc8f..b9e60809fc 100644 --- a/src/aphront/response/AphrontAjaxResponse.php +++ b/src/aphront/response/AphrontAjaxResponse.php @@ -64,7 +64,7 @@ public function buildResponseString() { if ($viewer) { $postprocessor_key = $viewer->getUserSetting( PhabricatorAccessibilitySetting::SETTINGKEY); - if (strlen($postprocessor_key)) { + if (phutil_nonempty_string($postprocessor_key)) { $response->setPostprocessorKey($postprocessor_key); } } diff --git a/src/aphront/response/AphrontFileResponse.php b/src/aphront/response/AphrontFileResponse.php index 6bae4c808f..6508ad6020 100644 --- a/src/aphront/response/AphrontFileResponse.php +++ b/src/aphront/response/AphrontFileResponse.php @@ -8,7 +8,16 @@ final class AphrontFileResponse extends AphrontResponse { private $compressResponse; private $mimeType; + + /** + * Download filename + * + * This is NULL as default or a string. + * + * @var string|null + */ private $download; + private $rangeMin; private $rangeMax; private $allowOrigins = array(); @@ -18,14 +27,30 @@ public function addAllowOrigin($origin) { return $this; } + /** + * Set a download filename + * + * @param $download string + * @return self + */ public function setDownload($download) { - if (!strlen($download)) { + + // Make sure we have a populated string + if (!phutil_nonempty_string($download)) { $download = 'untitled'; } + $this->download = $download; return $this; } + /** + * Get the download filename + * + * If this was never set, NULL is given. + * + * @return string|null + */ public function getDownload() { return $this->download; } @@ -113,7 +138,7 @@ public function getHeaders() { $headers[] = array('Content-Length', $content_len); } - if (strlen($this->getDownload())) { + if (phutil_nonempty_string($this->getDownload())) { $headers[] = array('X-Download-Options', 'noopen'); $filename = $this->getDownload(); diff --git a/src/aphront/response/AphrontResponse.php b/src/aphront/response/AphrontResponse.php index 5dae168c73..355177e316 100644 --- a/src/aphront/response/AphrontResponse.php +++ b/src/aphront/response/AphrontResponse.php @@ -327,7 +327,7 @@ public static function processValueForJSONEncoding(&$value, $key) { } if ($value instanceof PhutilSafeHTML) { - // TODO: Javelin supports implicity conversion of '__html' objects to + // TODO: Javelin supports implicit conversion of '__html' objects to // JX.HTML, but only for Ajax responses, not behaviors. Just leave things // as they are for now (where behaviors treat responses as HTML or plain // text at their discretion). diff --git a/src/aphront/response/AphrontWebpageResponse.php b/src/aphront/response/AphrontWebpageResponse.php index de642f9b19..00a953ece8 100644 --- a/src/aphront/response/AphrontWebpageResponse.php +++ b/src/aphront/response/AphrontWebpageResponse.php @@ -21,7 +21,7 @@ public function getUnexpectedOutput() { public function buildResponseString() { $unexpected_output = $this->getUnexpectedOutput(); - if (strlen($unexpected_output)) { + if (phutil_nonempty_string($unexpected_output)) { $style = array( 'background: linear-gradient(180deg, #eeddff, #ddbbff);', 'white-space: pre-wrap;', diff --git a/src/aphront/sink/AphrontPHPHTTPSink.php b/src/aphront/sink/AphrontPHPHTTPSink.php index 4953b6e610..1aaa1810ff 100644 --- a/src/aphront/sink/AphrontPHPHTTPSink.php +++ b/src/aphront/sink/AphrontPHPHTTPSink.php @@ -8,7 +8,7 @@ final class AphrontPHPHTTPSink extends AphrontHTTPSink { protected function emitHTTPStatus($code, $message = '') { if ($code != 200) { $header = "HTTP/1.0 {$code}"; - if (strlen($message)) { + if (phutil_nonempty_string($message)) { $header .= " {$message}"; } header($header); diff --git a/src/aphront/site/AphrontSite.php b/src/aphront/site/AphrontSite.php index 85bf42fe6c..08813462c6 100644 --- a/src/aphront/site/AphrontSite.php +++ b/src/aphront/site/AphrontSite.php @@ -15,7 +15,7 @@ public function new404Controller(AphrontRequest $request) { protected function isHostMatch($host, array $uris) { foreach ($uris as $uri) { - if (!strlen($uri)) { + if (!phutil_nonempty_string($uri)) { continue; } diff --git a/src/aphront/site/PhabricatorPlatformSite.php b/src/aphront/site/PhabricatorPlatformSite.php index a3193e65d1..e6c733684b 100644 --- a/src/aphront/site/PhabricatorPlatformSite.php +++ b/src/aphront/site/PhabricatorPlatformSite.php @@ -14,7 +14,7 @@ public function newSiteForRequest(AphrontRequest $request) { // If no base URI has been configured yet, match this site so the user // can follow setup instructions. $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); - if (!strlen($base_uri)) { + if (!phutil_nonempty_string($base_uri)) { return new PhabricatorPlatformSite(); } diff --git a/src/aphront/site/PhabricatorResourceSite.php b/src/aphront/site/PhabricatorResourceSite.php index 88f7777607..3fca474a17 100644 --- a/src/aphront/site/PhabricatorResourceSite.php +++ b/src/aphront/site/PhabricatorResourceSite.php @@ -14,7 +14,7 @@ public function newSiteForRequest(AphrontRequest $request) { $host = $request->getHost(); $uri = PhabricatorEnv::getEnvConfig('security.alternate-file-domain'); - if (!strlen($uri)) { + if (!phutil_nonempty_string($uri)) { return null; } diff --git a/src/aphront/site/PhabricatorShortSite.php b/src/aphront/site/PhabricatorShortSite.php index d4c36aecbe..05cf2c3584 100644 --- a/src/aphront/site/PhabricatorShortSite.php +++ b/src/aphront/site/PhabricatorShortSite.php @@ -14,7 +14,7 @@ public function newSiteForRequest(AphrontRequest $request) { $host = $request->getHost(); $uri = PhabricatorEnv::getEnvConfig('phurl.short-uri'); - if (!strlen($uri)) { + if (!phutil_nonempty_string($uri)) { return null; } diff --git a/src/applications/almanac/editor/AlmanacBindingEditEngine.php b/src/applications/almanac/editor/AlmanacBindingEditEngine.php index 66db7fcbab..8de74b471b 100644 --- a/src/applications/almanac/editor/AlmanacBindingEditEngine.php +++ b/src/applications/almanac/editor/AlmanacBindingEditEngine.php @@ -36,7 +36,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function newEditableObject() { diff --git a/src/applications/almanac/editor/AlmanacDeviceEditEngine.php b/src/applications/almanac/editor/AlmanacDeviceEditEngine.php index d0c2b48f7a..e6d1b55d2e 100644 --- a/src/applications/almanac/editor/AlmanacDeviceEditEngine.php +++ b/src/applications/almanac/editor/AlmanacDeviceEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function newEditableObject() { diff --git a/src/applications/almanac/editor/AlmanacEditor.php b/src/applications/almanac/editor/AlmanacEditor.php index 54c32c79cd..4f92a748d2 100644 --- a/src/applications/almanac/editor/AlmanacEditor.php +++ b/src/applications/almanac/editor/AlmanacEditor.php @@ -4,7 +4,7 @@ abstract class AlmanacEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } } diff --git a/src/applications/almanac/editor/AlmanacInterfaceEditEngine.php b/src/applications/almanac/editor/AlmanacInterfaceEditEngine.php index ca57113bf2..0cf3df229e 100644 --- a/src/applications/almanac/editor/AlmanacInterfaceEditEngine.php +++ b/src/applications/almanac/editor/AlmanacInterfaceEditEngine.php @@ -36,7 +36,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function newEditableObject() { diff --git a/src/applications/almanac/editor/AlmanacNamespaceEditEngine.php b/src/applications/almanac/editor/AlmanacNamespaceEditEngine.php index cf6ad36c00..fe22d4f14c 100644 --- a/src/applications/almanac/editor/AlmanacNamespaceEditEngine.php +++ b/src/applications/almanac/editor/AlmanacNamespaceEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function newEditableObject() { diff --git a/src/applications/almanac/editor/AlmanacNetworkEditEngine.php b/src/applications/almanac/editor/AlmanacNetworkEditEngine.php index 027b2a9aa0..600ead5b05 100644 --- a/src/applications/almanac/editor/AlmanacNetworkEditEngine.php +++ b/src/applications/almanac/editor/AlmanacNetworkEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function newEditableObject() { diff --git a/src/applications/almanac/editor/AlmanacPropertyEditEngine.php b/src/applications/almanac/editor/AlmanacPropertyEditEngine.php index 38139f79fa..999b7af9f4 100644 --- a/src/applications/almanac/editor/AlmanacPropertyEditEngine.php +++ b/src/applications/almanac/editor/AlmanacPropertyEditEngine.php @@ -35,7 +35,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function newEditableObject() { diff --git a/src/applications/almanac/editor/AlmanacServiceEditEngine.php b/src/applications/almanac/editor/AlmanacServiceEditEngine.php index b63075543f..e0c4086d2e 100644 --- a/src/applications/almanac/editor/AlmanacServiceEditEngine.php +++ b/src/applications/almanac/editor/AlmanacServiceEditEngine.php @@ -33,7 +33,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function newEditableObject() { diff --git a/src/applications/almanac/phid/AlmanacBindingPHIDType.php b/src/applications/almanac/phid/AlmanacBindingPHIDType.php index db469690cd..5f0da9b491 100644 --- a/src/applications/almanac/phid/AlmanacBindingPHIDType.php +++ b/src/applications/almanac/phid/AlmanacBindingPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/almanac/phid/AlmanacDevicePHIDType.php b/src/applications/almanac/phid/AlmanacDevicePHIDType.php index 26c88a7a86..31be5eade2 100644 --- a/src/applications/almanac/phid/AlmanacDevicePHIDType.php +++ b/src/applications/almanac/phid/AlmanacDevicePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/almanac/phid/AlmanacInterfacePHIDType.php b/src/applications/almanac/phid/AlmanacInterfacePHIDType.php index c7fe73183a..50b1d3ca4d 100644 --- a/src/applications/almanac/phid/AlmanacInterfacePHIDType.php +++ b/src/applications/almanac/phid/AlmanacInterfacePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/almanac/phid/AlmanacNamespacePHIDType.php b/src/applications/almanac/phid/AlmanacNamespacePHIDType.php index aa0b8fc4ef..71a8a6b475 100644 --- a/src/applications/almanac/phid/AlmanacNamespacePHIDType.php +++ b/src/applications/almanac/phid/AlmanacNamespacePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/almanac/phid/AlmanacNetworkPHIDType.php b/src/applications/almanac/phid/AlmanacNetworkPHIDType.php index 2264ce0e5f..3aa4f1a24b 100644 --- a/src/applications/almanac/phid/AlmanacNetworkPHIDType.php +++ b/src/applications/almanac/phid/AlmanacNetworkPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/almanac/phid/AlmanacServicePHIDType.php b/src/applications/almanac/phid/AlmanacServicePHIDType.php index a64a229e94..fdc48706d4 100644 --- a/src/applications/almanac/phid/AlmanacServicePHIDType.php +++ b/src/applications/almanac/phid/AlmanacServicePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/almanac/query/AlmanacBindingSearchEngine.php b/src/applications/almanac/query/AlmanacBindingSearchEngine.php index fbeb7644b9..053cc2cd34 100644 --- a/src/applications/almanac/query/AlmanacBindingSearchEngine.php +++ b/src/applications/almanac/query/AlmanacBindingSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function newQuery() { diff --git a/src/applications/almanac/query/AlmanacBindingTransactionQuery.php b/src/applications/almanac/query/AlmanacBindingTransactionQuery.php index 2b002e4f4a..55682cf7bc 100644 --- a/src/applications/almanac/query/AlmanacBindingTransactionQuery.php +++ b/src/applications/almanac/query/AlmanacBindingTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new AlmanacBindingTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAlmanacApplication::class; + } + } diff --git a/src/applications/almanac/query/AlmanacDeviceQuery.php b/src/applications/almanac/query/AlmanacDeviceQuery.php index 42796c4839..47006cb756 100644 --- a/src/applications/almanac/query/AlmanacDeviceQuery.php +++ b/src/applications/almanac/query/AlmanacDeviceQuery.php @@ -148,7 +148,7 @@ public function getBuiltinOrders() { } public function getQueryApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } } diff --git a/src/applications/almanac/query/AlmanacDeviceSearchEngine.php b/src/applications/almanac/query/AlmanacDeviceSearchEngine.php index 9f98abb292..86633ac870 100644 --- a/src/applications/almanac/query/AlmanacDeviceSearchEngine.php +++ b/src/applications/almanac/query/AlmanacDeviceSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function newQuery() { diff --git a/src/applications/almanac/query/AlmanacDeviceTransactionQuery.php b/src/applications/almanac/query/AlmanacDeviceTransactionQuery.php index 8b50fa85e5..8d12b526c9 100644 --- a/src/applications/almanac/query/AlmanacDeviceTransactionQuery.php +++ b/src/applications/almanac/query/AlmanacDeviceTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new AlmanacDeviceTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAlmanacApplication::class; + } + } diff --git a/src/applications/almanac/query/AlmanacInterfaceQuery.php b/src/applications/almanac/query/AlmanacInterfaceQuery.php index dbbc0cd53e..88eb9c2d74 100644 --- a/src/applications/almanac/query/AlmanacInterfaceQuery.php +++ b/src/applications/almanac/query/AlmanacInterfaceQuery.php @@ -169,7 +169,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function getBuiltinOrders() { diff --git a/src/applications/almanac/query/AlmanacInterfaceSearchEngine.php b/src/applications/almanac/query/AlmanacInterfaceSearchEngine.php index 9a3c5bc8f0..740affd526 100644 --- a/src/applications/almanac/query/AlmanacInterfaceSearchEngine.php +++ b/src/applications/almanac/query/AlmanacInterfaceSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function newQuery() { diff --git a/src/applications/almanac/query/AlmanacInterfaceTransactionQuery.php b/src/applications/almanac/query/AlmanacInterfaceTransactionQuery.php index ef8d3961d2..0a0925e0a1 100644 --- a/src/applications/almanac/query/AlmanacInterfaceTransactionQuery.php +++ b/src/applications/almanac/query/AlmanacInterfaceTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new AlmanacInterfaceTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAlmanacApplication::class; + } + } diff --git a/src/applications/almanac/query/AlmanacNamespaceQuery.php b/src/applications/almanac/query/AlmanacNamespaceQuery.php index e6c99bf6ea..58ba54f74a 100644 --- a/src/applications/almanac/query/AlmanacNamespaceQuery.php +++ b/src/applications/almanac/query/AlmanacNamespaceQuery.php @@ -92,7 +92,7 @@ public function getBuiltinOrders() { } public function getQueryApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } } diff --git a/src/applications/almanac/query/AlmanacNamespaceSearchEngine.php b/src/applications/almanac/query/AlmanacNamespaceSearchEngine.php index 14a96d22a0..a4658c5f33 100644 --- a/src/applications/almanac/query/AlmanacNamespaceSearchEngine.php +++ b/src/applications/almanac/query/AlmanacNamespaceSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function newQuery() { diff --git a/src/applications/almanac/query/AlmanacNamespaceTransactionQuery.php b/src/applications/almanac/query/AlmanacNamespaceTransactionQuery.php index 64ce639f45..f261a474de 100644 --- a/src/applications/almanac/query/AlmanacNamespaceTransactionQuery.php +++ b/src/applications/almanac/query/AlmanacNamespaceTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new AlmanacNamespaceTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAlmanacApplication::class; + } + } diff --git a/src/applications/almanac/query/AlmanacNetworkQuery.php b/src/applications/almanac/query/AlmanacNetworkQuery.php index 7af9db8585..a688fedda9 100644 --- a/src/applications/almanac/query/AlmanacNetworkQuery.php +++ b/src/applications/almanac/query/AlmanacNetworkQuery.php @@ -64,7 +64,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } } diff --git a/src/applications/almanac/query/AlmanacNetworkSearchEngine.php b/src/applications/almanac/query/AlmanacNetworkSearchEngine.php index 53f9b24e44..5938fd44c7 100644 --- a/src/applications/almanac/query/AlmanacNetworkSearchEngine.php +++ b/src/applications/almanac/query/AlmanacNetworkSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function newQuery() { diff --git a/src/applications/almanac/query/AlmanacNetworkTransactionQuery.php b/src/applications/almanac/query/AlmanacNetworkTransactionQuery.php index 7be51efec0..d49408b63d 100644 --- a/src/applications/almanac/query/AlmanacNetworkTransactionQuery.php +++ b/src/applications/almanac/query/AlmanacNetworkTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new AlmanacNetworkTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAlmanacApplication::class; + } + } diff --git a/src/applications/almanac/query/AlmanacPropertyQuery.php b/src/applications/almanac/query/AlmanacPropertyQuery.php index 4261c70fec..8c93d4d70b 100644 --- a/src/applications/almanac/query/AlmanacPropertyQuery.php +++ b/src/applications/almanac/query/AlmanacPropertyQuery.php @@ -99,7 +99,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } } diff --git a/src/applications/almanac/query/AlmanacQuery.php b/src/applications/almanac/query/AlmanacQuery.php index b5b6146c7f..8b2bc894e8 100644 --- a/src/applications/almanac/query/AlmanacQuery.php +++ b/src/applications/almanac/query/AlmanacQuery.php @@ -55,7 +55,7 @@ protected function didFilterPage(array $objects) { } public function getQueryApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } } diff --git a/src/applications/almanac/query/AlmanacServiceSearchEngine.php b/src/applications/almanac/query/AlmanacServiceSearchEngine.php index a2fd9c23b7..f9786a87d2 100644 --- a/src/applications/almanac/query/AlmanacServiceSearchEngine.php +++ b/src/applications/almanac/query/AlmanacServiceSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function newQuery() { diff --git a/src/applications/almanac/query/AlmanacServiceTransactionQuery.php b/src/applications/almanac/query/AlmanacServiceTransactionQuery.php index 96bf93377b..4ca6b3caa7 100644 --- a/src/applications/almanac/query/AlmanacServiceTransactionQuery.php +++ b/src/applications/almanac/query/AlmanacServiceTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new AlmanacServiceTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAlmanacApplication::class; + } + } diff --git a/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php b/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php index dca5dd986b..8666265d27 100644 --- a/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php +++ b/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function loadResults() { diff --git a/src/applications/almanac/typeahead/AlmanacServiceDatasource.php b/src/applications/almanac/typeahead/AlmanacServiceDatasource.php index 4413465125..d5f990ce47 100644 --- a/src/applications/almanac/typeahead/AlmanacServiceDatasource.php +++ b/src/applications/almanac/typeahead/AlmanacServiceDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function loadResults() { diff --git a/src/applications/almanac/typeahead/AlmanacServiceTypeDatasource.php b/src/applications/almanac/typeahead/AlmanacServiceTypeDatasource.php index 5314084244..b0b81baed9 100644 --- a/src/applications/almanac/typeahead/AlmanacServiceTypeDatasource.php +++ b/src/applications/almanac/typeahead/AlmanacServiceTypeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorAlmanacApplication'; + return PhabricatorAlmanacApplication::class; } public function loadResults() { diff --git a/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php b/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php index 3d1a26c91d..16bdc69bc1 100644 --- a/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php +++ b/src/applications/aphlict/management/PhabricatorAphlictManagementWorkflow.php @@ -30,16 +30,16 @@ protected function parseLaunchArguments(PhutilArgumentParser $args) { $full_path = Filesystem::resolvePath($config_file); $show_path = $full_path; } else { - $root = dirname(dirname(phutil_get_library_root('phabricator'))); + $root = dirname(phutil_get_library_root('phorge')); $try = array( - 'phabricator/conf/aphlict/aphlict.custom.json', - 'phabricator/conf/aphlict/aphlict.default.json', + 'conf/aphlict/aphlict.custom.json', + 'conf/aphlict/aphlict.default.json', ); foreach ($try as $config) { $full_path = $root.'/'.$config; - $show_path = $config; + $show_path = '/'.$config; if (Filesystem::pathExists($full_path)) { break; } diff --git a/src/applications/audit/application/PhabricatorAuditApplication.php b/src/applications/audit/application/PhabricatorAuditApplication.php index 7d8ad94aa7..ee5cbe7781 100644 --- a/src/applications/audit/application/PhabricatorAuditApplication.php +++ b/src/applications/audit/application/PhabricatorAuditApplication.php @@ -19,9 +19,7 @@ public function getShortDescription() { } public function canUninstall() { - // Audit was once a separate application, but has largely merged with - // Diffusion. - return false; + return true; } public function isPinnedByDefault(PhabricatorUser $viewer) { diff --git a/src/applications/audit/conduit/AuditConduitAPIMethod.php b/src/applications/audit/conduit/AuditConduitAPIMethod.php index a5a7957b1f..4dc9eb103e 100644 --- a/src/applications/audit/conduit/AuditConduitAPIMethod.php +++ b/src/applications/audit/conduit/AuditConduitAPIMethod.php @@ -4,7 +4,7 @@ abstract class AuditConduitAPIMethod extends ConduitAPIMethod { final public function getApplication() { return PhabricatorApplication::getByClass( - 'PhabricatorDiffusionApplication'); + 'PhabricatorAuditApplication'); } } diff --git a/src/applications/audit/editor/PhabricatorAuditEditor.php b/src/applications/audit/editor/PhabricatorAuditEditor.php index 7b97ea515a..c6fa129e41 100644 --- a/src/applications/audit/editor/PhabricatorAuditEditor.php +++ b/src/applications/audit/editor/PhabricatorAuditEditor.php @@ -22,7 +22,7 @@ public function getRawPatch() { } public function getEditorApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function getEditorObjectsDescription() { @@ -700,7 +700,7 @@ public function getMailTagsMap() { PhabricatorAuditTransaction::MAILTAG_COMMIT => pht('A commit is created.'), PhabricatorAuditTransaction::MAILTAG_ACTION_CONCERN => - pht('A commit has a concerned raised against it.'), + pht('A commit has a concern raised against it.'), PhabricatorAuditTransaction::MAILTAG_ACTION_ACCEPT => pht('A commit is accepted.'), PhabricatorAuditTransaction::MAILTAG_ACTION_RESIGN => diff --git a/src/applications/audit/query/DiffusionInternalCommitSearchEngine.php b/src/applications/audit/query/DiffusionInternalCommitSearchEngine.php index 45780140fb..ece9d1d48c 100644 --- a/src/applications/audit/query/DiffusionInternalCommitSearchEngine.php +++ b/src/applications/audit/query/DiffusionInternalCommitSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function newQuery() { diff --git a/src/applications/audit/query/PhabricatorAuditTransactionQuery.php b/src/applications/audit/query/PhabricatorAuditTransactionQuery.php index 5966f1cc45..05741466f0 100644 --- a/src/applications/audit/query/PhabricatorAuditTransactionQuery.php +++ b/src/applications/audit/query/PhabricatorAuditTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorAuditTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAuditApplication::class; + } + } diff --git a/src/applications/audit/query/PhabricatorCommitSearchEngine.php b/src/applications/audit/query/PhabricatorCommitSearchEngine.php index 8b7751c795..dfc038c197 100644 --- a/src/applications/audit/query/PhabricatorCommitSearchEngine.php +++ b/src/applications/audit/query/PhabricatorCommitSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function newQuery() { @@ -70,6 +70,9 @@ protected function buildQueryFromParameters(array $map) { } protected function buildCustomSearchFields() { + $show_audit_fields = (id(new PhabricatorAuditApplication())->isInstalled()); + $show_packages = PhabricatorApplication::isClassInstalled( + 'PhabricatorPackagesApplication'); return array( id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Responsible Users')) @@ -93,6 +96,7 @@ protected function buildCustomSearchFields() { ->setConduitKey('auditors') ->setAliases(array('auditor', 'auditors', 'auditorPHID')) ->setDatasource(new DiffusionAuditorFunctionDatasource()) + ->setIsHidden(!$show_audit_fields) ->setDescription( pht( 'Find commits where given users, projects, or packages are '. @@ -104,6 +108,7 @@ protected function buildCustomSearchFields() { ->setOptions(DiffusionCommitAuditStatus::newOptions()) ->setDeprecatedOptions( DiffusionCommitAuditStatus::newDeprecatedOptions()) + ->setIsHidden(!$show_audit_fields) ->setDescription(pht('Find commits with given audit statuses.')), id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Repositories')) @@ -118,6 +123,7 @@ protected function buildCustomSearchFields() { ->setConduitKey('packages') ->setAliases(array('package', 'packages', 'packagePHID')) ->setDatasource(new PhabricatorOwnersPackageDatasource()) + ->setIsHidden(!$show_packages) ->setDescription( pht('Find commits which affect given packages.')), id(new PhabricatorSearchThreeStateField()) @@ -169,9 +175,13 @@ protected function getBuiltinQueryNames() { $names = array(); if ($this->requireViewer()->isLoggedIn()) { - $names['active'] = pht('Active Audits'); + if (id(new PhabricatorAuditApplication())->isInstalled()) { + $names['active'] = pht('Active Audits'); + } $names['authored'] = pht('Authored'); - $names['audited'] = pht('Audited'); + if (id(new PhabricatorAuditApplication())->isInstalled()) { + $names['audited'] = pht('Audited'); + } } $names['all'] = pht('All Commits'); @@ -221,9 +231,11 @@ protected function renderResultList( $bucket = $this->getResultBucket($query); + // hide "Auditors" on /diffusion/commit/query/all/ if Audit not installed + $show_auditors = id(new PhabricatorAuditApplication())->isInstalled(); $template = id(new DiffusionCommitGraphView()) ->setViewer($viewer) - ->setShowAuditors(true); + ->setShowAuditors($show_auditors); $views = array(); if ($bucket) { diff --git a/src/applications/audit/storage/PhabricatorAuditTransaction.php b/src/applications/audit/storage/PhabricatorAuditTransaction.php index e6c1062092..8e49d99520 100644 --- a/src/applications/audit/storage/PhabricatorAuditTransaction.php +++ b/src/applications/audit/storage/PhabricatorAuditTransaction.php @@ -338,7 +338,17 @@ public function getTitleForFeed() { $author = null; } - if ($author) { + // Show both Author and Committer only if they are different. + $show_both = $author && $committer; + if ($show_both) { + if ($new['authorPHID']) { + $show_both = $new['authorPHID'] !== $new['committerPHID']; + } else if (phutil_nonempty_string($new['authorName'])) { + $show_both = $new['authorName'] !== $new['committerName']; + } + } + + if ($show_both) { $title = pht( '%s committed %s (authored by %s).', $committer, diff --git a/src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php b/src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php index 08cc65a235..389763da7d 100644 --- a/src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php +++ b/src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php @@ -104,7 +104,7 @@ protected function newOAuth1Future($uri, $data = array()) { ->setSignatureMethod($this->getSignatureMethod()); $consumer_key = $this->getConsumerKey(); - if (strlen($consumer_key)) { + if (phutil_nonempty_string($consumer_key)) { $future->setConsumerKey($consumer_key); } else { throw new Exception( @@ -118,11 +118,11 @@ protected function newOAuth1Future($uri, $data = array()) { $future->setConsumerSecret($consumer_secret); } - if (strlen($this->getToken())) { + if (phutil_nonempty_string($this->getToken())) { $future->setToken($this->getToken()); } - if (strlen($this->getTokenSecret())) { + if (phutil_nonempty_string($this->getTokenSecret())) { $future->setTokenSecret($this->getTokenSecret()); } @@ -137,7 +137,7 @@ public function getClientRedirectURI() { $request_token_uri = $this->getRequestTokenURI(); $future = $this->newOAuth1Future($request_token_uri); - if (strlen($this->getCallbackURI())) { + if (phutil_nonempty_string($this->getCallbackURI())) { $future->setCallbackURI($this->getCallbackURI()); } diff --git a/src/applications/auth/adapter/PhutilTwitchAuthAdapter.php b/src/applications/auth/adapter/PhutilTwitchAuthAdapter.php index dce2c7e2f0..364ac55eac 100644 --- a/src/applications/auth/adapter/PhutilTwitchAuthAdapter.php +++ b/src/applications/auth/adapter/PhutilTwitchAuthAdapter.php @@ -14,7 +14,7 @@ public function getAdapterDomain() { } public function getAccountID() { - return $this->getOAuthAccountData('_id'); + return $this->getOAuthAccountData('id'); } public function getAccountEmail() { @@ -22,11 +22,11 @@ public function getAccountEmail() { } public function getAccountName() { - return $this->getOAuthAccountData('name'); + return $this->getOAuthAccountData('login'); } public function getAccountImageURI() { - return $this->getOAuthAccountData('logo'); + return $this->getOAuthAccountData('profile_image_url'); } public function getAccountURI() { @@ -42,11 +42,11 @@ public function getAccountRealName() { } protected function getAuthenticateBaseURI() { - return 'https://api.twitch.tv/kraken/oauth2/authorize'; + return 'https://id.twitch.tv/oauth2/authorize'; } protected function getTokenBaseURI() { - return 'https://api.twitch.tv/kraken/oauth2/token'; + return 'https://id.twitch.tv/oauth2/token'; } public function getScope() { @@ -69,7 +69,7 @@ protected function loadOAuthAccountData() { return id(new PhutilTwitchFuture()) ->setClientID($this->getClientID()) ->setAccessToken($this->getAccessToken()) - ->setRawTwitchQuery('user') + ->setRawTwitchQuery('users') ->resolve(); } diff --git a/src/applications/auth/constants/PhabricatorCookies.php b/src/applications/auth/constants/PhabricatorCookies.php index 2573bf3ec6..9dc9d823b2 100644 --- a/src/applications/auth/constants/PhabricatorCookies.php +++ b/src/applications/auth/constants/PhabricatorCookies.php @@ -89,7 +89,7 @@ public static function setClientIDCookie(AphrontRequest $request) { // temporary and clearing it when users log out. $value = $request->getCookie(self::COOKIE_CLIENTID); - if (!strlen($value)) { + if (!phutil_nonempty_string($value)) { $request->setTemporaryCookie( self::COOKIE_CLIENTID, Filesystem::readRandomCharacters(16)); @@ -164,7 +164,7 @@ private static function parseNextURICookie($cookie) { // Old cookies look like: /uri // New cookies look like: timestamp,/uri - if (!strlen($cookie)) { + if (!phutil_nonempty_string($cookie)) { return null; } diff --git a/src/applications/auth/controller/PhabricatorAuthController.php b/src/applications/auth/controller/PhabricatorAuthController.php index aee93b98d6..8bba90a1e6 100644 --- a/src/applications/auth/controller/PhabricatorAuthController.php +++ b/src/applications/auth/controller/PhabricatorAuthController.php @@ -282,7 +282,7 @@ final protected function newCustomStartMessage() { $viewer, PhabricatorAuthLoginMessageType::MESSAGEKEY); - if (!strlen($text)) { + if (!phutil_nonempty_string($text)) { return null; } diff --git a/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php b/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php index 100a3f1d48..ba0644fbed 100644 --- a/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php +++ b/src/applications/auth/controller/PhabricatorAuthNeedsApprovalController.php @@ -55,7 +55,7 @@ private function newCustomWaitForApprovalInstructions() { $viewer, PhabricatorAuthWaitForApprovalMessageType::MESSAGEKEY); - if (!strlen($text)) { + if (!phutil_nonempty_string($text)) { return null; } diff --git a/src/applications/auth/controller/PhabricatorAuthRegisterController.php b/src/applications/auth/controller/PhabricatorAuthRegisterController.php index dd2bcd374f..be310e6268 100644 --- a/src/applications/auth/controller/PhabricatorAuthRegisterController.php +++ b/src/applications/auth/controller/PhabricatorAuthRegisterController.php @@ -25,7 +25,7 @@ public function handleRequest(AphrontRequest $request) { $invite = $this->loadInvite(); $is_setup = false; - if (strlen($account_key)) { + if (phutil_nonempty_string($account_key)) { $result = $this->loadAccountForRegistrationOrLinking($account_key); list($account, $provider, $response) = $result; $is_default = false; @@ -73,6 +73,7 @@ public function handleRequest(AphrontRequest $request) { $default_username = null; $default_realname = null; $default_email = null; + $fbsd_username = ''; } else { $default_realname = $account->getRealName(); $default_email = $account->getEmail(); @@ -251,9 +252,9 @@ public function handleRequest(AphrontRequest $request) { $require_real_name = PhabricatorEnv::getEnvConfig('user.require-real-name'); - $e_username = strlen($value_username) ? null : true; + $e_username = phutil_nonempty_string($value_username) ? null : true; $e_realname = $require_real_name ? true : null; - $e_email = strlen($value_email) ? null : true; + $e_email = phutil_nonempty_string($value_email) ? null : true; $e_password = true; $e_captcha = true; diff --git a/src/applications/auth/controller/PhabricatorAuthSSHKeyEditController.php b/src/applications/auth/controller/PhabricatorAuthSSHKeyEditController.php index 6dfc957f48..88bbcb5f8e 100644 --- a/src/applications/auth/controller/PhabricatorAuthSSHKeyEditController.php +++ b/src/applications/auth/controller/PhabricatorAuthSSHKeyEditController.php @@ -54,7 +54,7 @@ public function handleRequest(AphrontRequest $request) { $cancel_uri); $v_name = $key->getName(); - $e_name = strlen($v_name) ? null : true; + $e_name = phutil_nonempty_string($v_name) ? null : true; $v_key = $key->getEntireKey(); $e_key = strlen($v_key) ? null : true; diff --git a/src/applications/auth/controller/PhabricatorAuthSSHKeyViewController.php b/src/applications/auth/controller/PhabricatorAuthSSHKeyViewController.php index 54c6e3861b..591b835d62 100644 --- a/src/applications/auth/controller/PhabricatorAuthSSHKeyViewController.php +++ b/src/applications/auth/controller/PhabricatorAuthSSHKeyViewController.php @@ -110,7 +110,7 @@ private function buildPropertySection( $properties = id(new PHUIPropertyListView()) ->setUser($viewer); - $properties->addProperty(pht('SSH Key Type'), $ssh_key->getKeyType()); + $properties->addProperty(pht('Public Key'), $ssh_key->getEntireKey()); $properties->addProperty( pht('Created'), phabricator_datetime($ssh_key->getDateCreated(), $viewer)); diff --git a/src/applications/auth/controller/PhabricatorAuthSetExternalController.php b/src/applications/auth/controller/PhabricatorAuthSetExternalController.php index 8b0a44b9dc..2a8bbda7df 100644 --- a/src/applications/auth/controller/PhabricatorAuthSetExternalController.php +++ b/src/applications/auth/controller/PhabricatorAuthSetExternalController.php @@ -40,7 +40,7 @@ public function handleRequest(AphrontRequest $request) { $text = PhabricatorAuthMessage::loadMessageText( $viewer, PhabricatorAuthLinkMessageType::MESSAGEKEY); - if (!strlen($text)) { + if (!phutil_nonempty_string($text)) { $text = pht( 'You can link your %s account to an external account to '. 'allow you to log in more easily in the future. To continue, choose '. diff --git a/src/applications/auth/controller/PhabricatorAuthStartController.php b/src/applications/auth/controller/PhabricatorAuthStartController.php index 5789740e14..76c7eb9df5 100644 --- a/src/applications/auth/controller/PhabricatorAuthStartController.php +++ b/src/applications/auth/controller/PhabricatorAuthStartController.php @@ -98,7 +98,7 @@ public function handleRequest(AphrontRequest $request) { } $next_uri = $request->getStr('next'); - if (!strlen($next_uri)) { + if (!phutil_nonempty_string($next_uri)) { if ($this->getDelegatingController()) { // Only set a next URI from the request path if this controller was // delegated to, which happens when a user tries to view a page which @@ -112,7 +112,7 @@ public function handleRequest(AphrontRequest $request) { } if (!$request->isFormPost()) { - if (strlen($next_uri)) { + if (phutil_nonempty_string($next_uri)) { PhabricatorCookies::setNextURICookie($request, $next_uri); } PhabricatorCookies::setClientIDCookie($request); diff --git a/src/applications/auth/controller/config/PhabricatorAuthEditController.php b/src/applications/auth/controller/config/PhabricatorAuthEditController.php index 693fc5bffd..2dbdbc9a10 100644 --- a/src/applications/auth/controller/config/PhabricatorAuthEditController.php +++ b/src/applications/auth/controller/config/PhabricatorAuthEditController.php @@ -99,10 +99,10 @@ public function handleRequest(AphrontRequest $request) { if (!$errors) { if ($is_new) { - if (!strlen($config->getProviderType())) { + if (!phutil_nonempty_string($config->getProviderType())) { $config->setProviderType($provider->getProviderType()); } - if (!strlen($config->getProviderDomain())) { + if (!phutil_nonempty_string($config->getProviderDomain())) { $config->setProviderDomain($provider->getProviderDomain()); } } diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberDisableController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberDisableController.php index a525e7b930..28b7e69593 100644 --- a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberDisableController.php +++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberDisableController.php @@ -7,16 +7,19 @@ public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $id = $request->getURIData('id'); - $number = id(new PhabricatorAuthContactNumberQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); - if (!$number) { + $sms_auth_factor = new PhabricatorSMSAuthFactor(); + if ($sms_auth_factor->isSMSMailerConfigured()) { + $number = id(new PhabricatorAuthContactNumberQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + } + if (!isset($number) || !$number) { return new Aphront404Response(); } diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberEditController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberEditController.php index 95764496da..c012ca3581 100644 --- a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberEditController.php +++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberEditController.php @@ -4,9 +4,14 @@ final class PhabricatorAuthContactNumberEditController extends PhabricatorAuthContactNumberController { public function handleRequest(AphrontRequest $request) { - return id(new PhabricatorAuthContactNumberEditEngine()) - ->setController($this) - ->buildResponse(); + $sms_auth_factor = new PhabricatorSMSAuthFactor(); + if ($sms_auth_factor->isSMSMailerConfigured()) { + return id(new PhabricatorAuthContactNumberEditEngine()) + ->setController($this) + ->buildResponse(); + } else { + return new Aphront404Response(); + } } } diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberPrimaryController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberPrimaryController.php index cad1bbf3fc..63ba5ad883 100644 --- a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberPrimaryController.php +++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberPrimaryController.php @@ -7,16 +7,19 @@ public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $id = $request->getURIData('id'); - $number = id(new PhabricatorAuthContactNumberQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); - if (!$number) { + $sms_auth_factor = new PhabricatorSMSAuthFactor(); + if ($sms_auth_factor->isSMSMailerConfigured()) { + $number = id(new PhabricatorAuthContactNumberQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + } + if (!isset($number) || !$number) { return new Aphront404Response(); } diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberTestController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberTestController.php index f8c8b013bf..a991c34a6d 100644 --- a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberTestController.php +++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberTestController.php @@ -7,16 +7,19 @@ public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $id = $request->getURIData('id'); - $number = id(new PhabricatorAuthContactNumberQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); - if (!$number) { + $sms_auth_factor = new PhabricatorSMSAuthFactor(); + if ($sms_auth_factor->isSMSMailerConfigured()) { + $number = id(new PhabricatorAuthContactNumberQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + } + if (!isset($number) || !$number) { return new Aphront404Response(); } diff --git a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php index 027d288dbc..75e4b8e3b7 100644 --- a/src/applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php +++ b/src/applications/auth/controller/contact/PhabricatorAuthContactNumberViewController.php @@ -6,11 +6,14 @@ final class PhabricatorAuthContactNumberViewController public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); - $number = id(new PhabricatorAuthContactNumberQuery()) - ->setViewer($viewer) - ->withIDs(array($request->getURIData('id'))) - ->executeOne(); - if (!$number) { + $sms_auth_factor = new PhabricatorSMSAuthFactor(); + if ($sms_auth_factor->isSMSMailerConfigured()) { + $number = id(new PhabricatorAuthContactNumberQuery()) + ->setViewer($viewer) + ->withIDs(array($request->getURIData('id'))) + ->executeOne(); + } + if (!isset($number) || !$number) { return new Aphront404Response(); } diff --git a/src/applications/auth/controller/message/PhabricatorAuthMessageViewController.php b/src/applications/auth/controller/message/PhabricatorAuthMessageViewController.php index 5665744463..a1abb6d37c 100644 --- a/src/applications/auth/controller/message/PhabricatorAuthMessageViewController.php +++ b/src/applications/auth/controller/message/PhabricatorAuthMessageViewController.php @@ -103,7 +103,7 @@ private function buildPropertiesView(PhabricatorAuthMessage $message) { ->setViewer($viewer); $full_description = $message_type->getFullDescription(); - if (strlen($full_description)) { + if (phutil_nonempty_string($full_description)) { $view->addTextContent(new PHUIRemarkupView($viewer, $full_description)); } else { $short_description = $message_type->getShortDescription(); @@ -111,7 +111,7 @@ private function buildPropertiesView(PhabricatorAuthMessage $message) { } $message_text = $message->getMessageText(); - if (strlen($message_text)) { + if (phutil_nonempty_string($message_text)) { $view->addSectionHeader( pht('Message Preview'), PHUIPropertyListView::ICON_SUMMARY); @@ -120,7 +120,7 @@ private function buildPropertiesView(PhabricatorAuthMessage $message) { } $default_text = $message_type->getDefaultMessageText(); - if (strlen($default_text)) { + if (phutil_nonempty_string($default_text)) { $view->addSectionHeader( pht('Default Message'), PHUIPropertyListView::ICON_SUMMARY); diff --git a/src/applications/auth/controller/mfa/PhabricatorAuthFactorProviderViewController.php b/src/applications/auth/controller/mfa/PhabricatorAuthFactorProviderViewController.php index 1dac49bcf9..44bb7e97f6 100644 --- a/src/applications/auth/controller/mfa/PhabricatorAuthFactorProviderViewController.php +++ b/src/applications/auth/controller/mfa/PhabricatorAuthFactorProviderViewController.php @@ -83,7 +83,7 @@ private function buildPropertiesView( $custom_enroll = $provider->getEnrollMessage(); - if (strlen($custom_enroll)) { + if (phutil_nonempty_string($custom_enroll)) { $view->addSectionHeader( pht('Custom Enroll Message'), PHUIPropertyListView::ICON_SUMMARY); diff --git a/src/applications/auth/editor/PhabricatorAuthContactNumberEditEngine.php b/src/applications/auth/editor/PhabricatorAuthContactNumberEditEngine.php index 5b1a059b2f..a4f1ab0cf2 100644 --- a/src/applications/auth/editor/PhabricatorAuthContactNumberEditEngine.php +++ b/src/applications/auth/editor/PhabricatorAuthContactNumberEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function newEditableObject() { diff --git a/src/applications/auth/editor/PhabricatorAuthContactNumberEditor.php b/src/applications/auth/editor/PhabricatorAuthContactNumberEditor.php index 9dfb569e89..d6c872743e 100644 --- a/src/applications/auth/editor/PhabricatorAuthContactNumberEditor.php +++ b/src/applications/auth/editor/PhabricatorAuthContactNumberEditor.php @@ -4,7 +4,7 @@ final class PhabricatorAuthContactNumberEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/auth/editor/PhabricatorAuthFactorProviderEditEngine.php b/src/applications/auth/editor/PhabricatorAuthFactorProviderEditEngine.php index ab74350cc9..ecccfb6026 100644 --- a/src/applications/auth/editor/PhabricatorAuthFactorProviderEditEngine.php +++ b/src/applications/auth/editor/PhabricatorAuthFactorProviderEditEngine.php @@ -24,7 +24,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function setProviderFactor(PhabricatorAuthFactor $factor) { diff --git a/src/applications/auth/editor/PhabricatorAuthFactorProviderEditor.php b/src/applications/auth/editor/PhabricatorAuthFactorProviderEditor.php index 144f275391..7ee30a0ece 100644 --- a/src/applications/auth/editor/PhabricatorAuthFactorProviderEditor.php +++ b/src/applications/auth/editor/PhabricatorAuthFactorProviderEditor.php @@ -4,7 +4,7 @@ final class PhabricatorAuthFactorProviderEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/auth/editor/PhabricatorAuthMessageEditEngine.php b/src/applications/auth/editor/PhabricatorAuthMessageEditEngine.php index 0a9aa32de4..3055201a8c 100644 --- a/src/applications/auth/editor/PhabricatorAuthMessageEditEngine.php +++ b/src/applications/auth/editor/PhabricatorAuthMessageEditEngine.php @@ -24,7 +24,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function setMessageType(PhabricatorAuthMessageType $type) { diff --git a/src/applications/auth/editor/PhabricatorAuthMessageEditor.php b/src/applications/auth/editor/PhabricatorAuthMessageEditor.php index 56e8e716cd..24bc235680 100644 --- a/src/applications/auth/editor/PhabricatorAuthMessageEditor.php +++ b/src/applications/auth/editor/PhabricatorAuthMessageEditor.php @@ -4,7 +4,7 @@ final class PhabricatorAuthMessageEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/auth/editor/PhabricatorAuthPasswordEditor.php b/src/applications/auth/editor/PhabricatorAuthPasswordEditor.php index 0867d22b87..447808f6c9 100644 --- a/src/applications/auth/editor/PhabricatorAuthPasswordEditor.php +++ b/src/applications/auth/editor/PhabricatorAuthPasswordEditor.php @@ -15,7 +15,7 @@ public function getOldHasher() { } public function getEditorApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/auth/editor/PhabricatorAuthProviderConfigEditor.php b/src/applications/auth/editor/PhabricatorAuthProviderConfigEditor.php index 1e75edfbf0..4f66d36d04 100644 --- a/src/applications/auth/editor/PhabricatorAuthProviderConfigEditor.php +++ b/src/applications/auth/editor/PhabricatorAuthProviderConfigEditor.php @@ -4,7 +4,7 @@ final class PhabricatorAuthProviderConfigEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php b/src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php index 3f178c9855..b8114a257f 100644 --- a/src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php +++ b/src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php @@ -15,7 +15,7 @@ public function getIsAdministrativeEdit() { } public function getEditorApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/auth/engine/PhabricatorAuthCSRFEngine.php b/src/applications/auth/engine/PhabricatorAuthCSRFEngine.php index fcb8c13ab7..856b334039 100644 --- a/src/applications/auth/engine/PhabricatorAuthCSRFEngine.php +++ b/src/applications/auth/engine/PhabricatorAuthCSRFEngine.php @@ -47,7 +47,10 @@ public function isValidToken($token) { // We expect a BREACH-mitigating token. See T3684. $breach_prefix = $this->getBREACHPrefix(); $breach_prelen = strlen($breach_prefix); - if (strncmp($token, $breach_prefix, $breach_prelen) !== 0) { + if ( + $token === null || + strncmp($token, $breach_prefix, $breach_prelen) !== 0 + ) { return false; } diff --git a/src/applications/auth/engine/PhabricatorAuthPasswordEngine.php b/src/applications/auth/engine/PhabricatorAuthPasswordEngine.php index a1fec4a6d2..0efb1fe4fc 100644 --- a/src/applications/auth/engine/PhabricatorAuthPasswordEngine.php +++ b/src/applications/auth/engine/PhabricatorAuthPasswordEngine.php @@ -150,6 +150,8 @@ public function checkNewPassword( $base_uri = new PhutilURI($base_uri); $blocklist[] = $base_uri->getDomain(); + $blocklist = array_filter($blocklist); + // Generate additional subterms by splitting the raw blocklist on // characters like "@", " " (space), and "." to break up email addresses, // readable names, and domain names into components. diff --git a/src/applications/auth/factor/PhabricatorAuthFactor.php b/src/applications/auth/factor/PhabricatorAuthFactor.php index fefd9b5fd1..79ea17de7f 100644 --- a/src/applications/auth/factor/PhabricatorAuthFactor.php +++ b/src/applications/auth/factor/PhabricatorAuthFactor.php @@ -413,8 +413,8 @@ final protected function loadMFASyncToken( $sync_type = PhabricatorAuthMFASyncTemporaryTokenType::TOKENTYPE; $sync_token = null; - $sync_key = $request->getStr($this->getMFASyncTokenFormKey()); - if (strlen($sync_key)) { + $sync_key = $request->getStr($this->getMFASyncTokenFormKey(), ''); + if ($sync_key !== '') { $sync_key_digest = PhabricatorHash::digestWithNamedKey( $sync_key, PhabricatorAuthMFASyncTemporaryTokenType::DIGEST_KEY); diff --git a/src/applications/auth/factor/PhabricatorSMSAuthFactor.php b/src/applications/auth/factor/PhabricatorSMSAuthFactor.php index 33f640e692..2350855859 100644 --- a/src/applications/auth/factor/PhabricatorSMSAuthFactor.php +++ b/src/applications/auth/factor/PhabricatorSMSAuthFactor.php @@ -334,7 +334,7 @@ private function newSMSChallengeCode() { return $value; } - private function isSMSMailerConfigured() { + public function isSMSMailerConfigured() { $mailers = PhabricatorMetaMTAMail::newMailers( array( 'outbound' => true, diff --git a/src/applications/auth/guidance/PhabricatorAuthProvidersGuidanceEngineExtension.php b/src/applications/auth/guidance/PhabricatorAuthProvidersGuidanceEngineExtension.php index d4d41f1d83..8d823f82c2 100644 --- a/src/applications/auth/guidance/PhabricatorAuthProvidersGuidanceEngineExtension.php +++ b/src/applications/auth/guidance/PhabricatorAuthProvidersGuidanceEngineExtension.php @@ -53,9 +53,8 @@ public function generateGuidance(PhabricatorGuidanceContext $context) { ->setMessage($message); } else { $message = pht( - 'Anyone who can browse to this this server will be able to '. - 'register an account. To add email domain restrictions, configure '. - '%s.', + 'Anyone who can browse to this server will be able to register '. + 'an account. To add email domain restrictions, configure %s.', $domains_link); $results[] = $this->newGuidance('core.auth.email-domains.off') diff --git a/src/applications/auth/password/PhabricatorAuthPasswordException.php b/src/applications/auth/password/PhabricatorAuthPasswordException.php index d4b13bb10c..fa749722af 100644 --- a/src/applications/auth/password/PhabricatorAuthPasswordException.php +++ b/src/applications/auth/password/PhabricatorAuthPasswordException.php @@ -4,7 +4,7 @@ final class PhabricatorAuthPasswordException extends Exception { private $passwordError; - private $confirmErorr; + private $confirmError; public function __construct( $message, diff --git a/src/applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php b/src/applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php index 2819c84572..c7531e61b0 100644 --- a/src/applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthAuthFactorProviderPHIDType.php b/src/applications/auth/phid/PhabricatorAuthAuthFactorProviderPHIDType.php index f0f9f572e8..23d932cfb9 100644 --- a/src/applications/auth/phid/PhabricatorAuthAuthFactorProviderPHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthAuthFactorProviderPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php b/src/applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php index bf79ea8743..00b9cd9c6e 100644 --- a/src/applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthChallengePHIDType.php b/src/applications/auth/phid/PhabricatorAuthChallengePHIDType.php index 2d2fea26b6..23b2e485c2 100644 --- a/src/applications/auth/phid/PhabricatorAuthChallengePHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthChallengePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthContactNumberPHIDType.php b/src/applications/auth/phid/PhabricatorAuthContactNumberPHIDType.php index 8a4953f4d3..e883aaa5a6 100644 --- a/src/applications/auth/phid/PhabricatorAuthContactNumberPHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthContactNumberPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthInvitePHIDType.php b/src/applications/auth/phid/PhabricatorAuthInvitePHIDType.php index b633e10eab..e22793990d 100644 --- a/src/applications/auth/phid/PhabricatorAuthInvitePHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthInvitePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthMessagePHIDType.php b/src/applications/auth/phid/PhabricatorAuthMessagePHIDType.php index 005e363b08..c1cd101060 100644 --- a/src/applications/auth/phid/PhabricatorAuthMessagePHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthMessagePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthPasswordPHIDType.php b/src/applications/auth/phid/PhabricatorAuthPasswordPHIDType.php index 8433df8824..b0256a1bf6 100644 --- a/src/applications/auth/phid/PhabricatorAuthPasswordPHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthPasswordPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthSSHKeyPHIDType.php b/src/applications/auth/phid/PhabricatorAuthSSHKeyPHIDType.php index b09bdb8b80..e18475ceb9 100644 --- a/src/applications/auth/phid/PhabricatorAuthSSHKeyPHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthSSHKeyPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/phid/PhabricatorAuthSessionPHIDType.php b/src/applications/auth/phid/PhabricatorAuthSessionPHIDType.php index e031c4ae88..ae67f09433 100644 --- a/src/applications/auth/phid/PhabricatorAuthSessionPHIDType.php +++ b/src/applications/auth/phid/PhabricatorAuthSessionPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php b/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php index d1db832aa2..36a83f3678 100644 --- a/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php @@ -264,7 +264,7 @@ public static function assertLDAPExtensionInstalled() { 'talk to LDAP. Usually you can install it with '. '`%s`, `%s`, or a similar package manager command.', 'yum install php-ldap', - 'apt-get install php5-ldap')); + 'apt-get install php-ldap')); } } @@ -427,7 +427,7 @@ public function extendEditForm( } $instruction_text = idx($instructions, $key); - if (strlen($instruction_text)) { + if (phutil_nonempty_string($instruction_text)) { $form->appendRemarkupInstructions($instruction_text); } diff --git a/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php b/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php index b1590b9c82..7133cff042 100644 --- a/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php +++ b/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php @@ -67,7 +67,7 @@ public function processLoginRequest( } $denied = $request->getStr('denied'); - if (strlen($denied)) { + if ($denied) { // Twitter indicates that the user cancelled the login attempt by // returning "denied" as a parameter. throw new PhutilAuthUserAbortedException(); diff --git a/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php b/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php index 4c1c31f146..0d729837b4 100644 --- a/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php @@ -169,7 +169,7 @@ public function extendEditForm( phutil_tag( 'tt', array(), - '`example.oauthserver`')))); + 'example.oauthserver')))); } else { $form ->appendChild( diff --git a/src/applications/auth/query/PhabricatorAuthChallengeQuery.php b/src/applications/auth/query/PhabricatorAuthChallengeQuery.php index c6abead11f..9b0b776d0d 100644 --- a/src/applications/auth/query/PhabricatorAuthChallengeQuery.php +++ b/src/applications/auth/query/PhabricatorAuthChallengeQuery.php @@ -89,7 +89,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php b/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php index ba507c757b..a3b781485f 100644 --- a/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php +++ b/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php @@ -93,7 +93,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthContactNumberTransactionQuery.php b/src/applications/auth/query/PhabricatorAuthContactNumberTransactionQuery.php index a443cbab42..6cadf8f055 100644 --- a/src/applications/auth/query/PhabricatorAuthContactNumberTransactionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthContactNumberTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorAuthContactNumberTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAuthApplication::class; + } + } diff --git a/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php b/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php index dd73d0b081..52b907e62f 100644 --- a/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php +++ b/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php @@ -121,7 +121,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php b/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php index 7083545e3e..362f6be235 100644 --- a/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php +++ b/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php @@ -84,7 +84,7 @@ protected function willFilterPage(array $providers) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthFactorProviderTransactionQuery.php b/src/applications/auth/query/PhabricatorAuthFactorProviderTransactionQuery.php index 5add1345c4..e0402ec557 100644 --- a/src/applications/auth/query/PhabricatorAuthFactorProviderTransactionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthFactorProviderTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorAuthFactorProviderTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAuthApplication::class; + } + } diff --git a/src/applications/auth/query/PhabricatorAuthInviteSearchEngine.php b/src/applications/auth/query/PhabricatorAuthInviteSearchEngine.php index e439dd9fb8..8ed2a31e6c 100644 --- a/src/applications/auth/query/PhabricatorAuthInviteSearchEngine.php +++ b/src/applications/auth/query/PhabricatorAuthInviteSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/auth/query/PhabricatorAuthMessageQuery.php b/src/applications/auth/query/PhabricatorAuthMessageQuery.php index 7158d03a00..c28d561297 100644 --- a/src/applications/auth/query/PhabricatorAuthMessageQuery.php +++ b/src/applications/auth/query/PhabricatorAuthMessageQuery.php @@ -73,7 +73,7 @@ protected function willFilterPage(array $messages) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthMessageTransactionQuery.php b/src/applications/auth/query/PhabricatorAuthMessageTransactionQuery.php index 0b2ce79db3..25a2b81b82 100644 --- a/src/applications/auth/query/PhabricatorAuthMessageTransactionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthMessageTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorAuthMessageTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAuthApplication::class; + } + } diff --git a/src/applications/auth/query/PhabricatorAuthPasswordQuery.php b/src/applications/auth/query/PhabricatorAuthPasswordQuery.php index a77fd54b13..0e9e70e175 100644 --- a/src/applications/auth/query/PhabricatorAuthPasswordQuery.php +++ b/src/applications/auth/query/PhabricatorAuthPasswordQuery.php @@ -104,7 +104,7 @@ protected function willFilterPage(array $passwords) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthPasswordTransactionQuery.php b/src/applications/auth/query/PhabricatorAuthPasswordTransactionQuery.php index 519b6aa77b..2ad7bb70ff 100644 --- a/src/applications/auth/query/PhabricatorAuthPasswordTransactionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthPasswordTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorAuthPasswordTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAuthApplication::class; + } + } diff --git a/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php b/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php index 30e5dad113..d437d08fd5 100644 --- a/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php +++ b/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php @@ -80,7 +80,7 @@ protected function willFilterPage(array $configs) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthProviderConfigTransactionQuery.php b/src/applications/auth/query/PhabricatorAuthProviderConfigTransactionQuery.php index 68c14e71fa..b517c4f57e 100644 --- a/src/applications/auth/query/PhabricatorAuthProviderConfigTransactionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthProviderConfigTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorAuthProviderConfigTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAuthApplication::class; + } + } diff --git a/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php b/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php index 7d4aac4a01..e0970f9b51 100644 --- a/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php +++ b/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php @@ -128,7 +128,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthSSHKeySearchEngine.php b/src/applications/auth/query/PhabricatorAuthSSHKeySearchEngine.php index 0575b40b9c..133389dc8e 100644 --- a/src/applications/auth/query/PhabricatorAuthSSHKeySearchEngine.php +++ b/src/applications/auth/query/PhabricatorAuthSSHKeySearchEngine.php @@ -23,7 +23,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } public function newQuery() { diff --git a/src/applications/auth/query/PhabricatorAuthSSHKeyTransactionQuery.php b/src/applications/auth/query/PhabricatorAuthSSHKeyTransactionQuery.php index 397a03f2b0..ea2602dc9f 100644 --- a/src/applications/auth/query/PhabricatorAuthSSHKeyTransactionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthSSHKeyTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorAuthSSHKeyTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorAuthApplication::class; + } + } diff --git a/src/applications/auth/query/PhabricatorAuthSessionQuery.php b/src/applications/auth/query/PhabricatorAuthSessionQuery.php index 4bc7eba73f..78974c7670 100644 --- a/src/applications/auth/query/PhabricatorAuthSessionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthSessionQuery.php @@ -107,7 +107,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php b/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php index c5cb39096c..90b4741307 100644 --- a/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php +++ b/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php @@ -100,7 +100,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorAuthApplication'; + return PhabricatorAuthApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php b/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php index b5c5b6eaa2..716fad59ba 100644 --- a/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php +++ b/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php @@ -84,7 +84,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } } diff --git a/src/applications/auth/query/PhabricatorExternalAccountQuery.php b/src/applications/auth/query/PhabricatorExternalAccountQuery.php index f44821d7a9..b2c61a1b91 100644 --- a/src/applications/auth/query/PhabricatorExternalAccountQuery.php +++ b/src/applications/auth/query/PhabricatorExternalAccountQuery.php @@ -241,7 +241,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } } diff --git a/src/applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php b/src/applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php index d8660b1956..682cde0df6 100644 --- a/src/applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php +++ b/src/applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php @@ -55,11 +55,15 @@ public static function newFromRawKey($entire_key) { list($type, $body, $comment) = $parts; + // The only goal is to prevent user error by nonsense input. + // This is just a meaningful subset from 'ssh -Q key'. $recognized_keys = array( 'ssh-dsa', 'ssh-dss', 'ssh-rsa', 'ssh-ed25519', + 'sk-ssh-ed25519@openssh.com', + 'sk-ecdsa-sha2-nistp256@openssh.com', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', diff --git a/src/applications/auth/storage/PhabricatorAuthChallenge.php b/src/applications/auth/storage/PhabricatorAuthChallenge.php index 0b740e5fa7..05d00c3e6e 100644 --- a/src/applications/auth/storage/PhabricatorAuthChallenge.php +++ b/src/applications/auth/storage/PhabricatorAuthChallenge.php @@ -56,7 +56,7 @@ public static function newChallengeResponsesFromRequest( AphrontRequest $request) { assert_instances_of($challenges, __CLASS__); - $token_list = $request->getStr(self::HTTPKEY); + $token_list = $request->getStr(self::HTTPKEY, ''); $token_list = explode(' ', $token_list); $token_map = array(); diff --git a/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php b/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php index f60ba8c734..92bb4883bc 100644 --- a/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php +++ b/src/applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php @@ -150,7 +150,7 @@ public function getTitle() { $provider = $this->getProvider(); if ($provider) { $title = $provider->renderConfigPropertyTransactionTitle($this); - if (strlen($title)) { + if (phutil_nonempty_stringlike($title)) { return $title; } } diff --git a/src/applications/auth/view/PhabricatorAuthAccountView.php b/src/applications/auth/view/PhabricatorAuthAccountView.php index 9746be7841..f903f45d26 100644 --- a/src/applications/auth/view/PhabricatorAuthAccountView.php +++ b/src/applications/auth/view/PhabricatorAuthAccountView.php @@ -29,13 +29,14 @@ public function render() { $realname = $account->getRealName(); $use_name = null; - if (strlen($dispname)) { + if (phutil_nonempty_string($dispname)) { $use_name = $dispname; - } else if (strlen($username) && strlen($realname)) { + } else if (phutil_nonempty_string($username) && + phutil_nonempty_string($realname)) { $use_name = $username.' ('.$realname.')'; - } else if (strlen($username)) { + } else if (phutil_nonempty_string($username)) { $use_name = $username; - } else if (strlen($realname)) { + } else if (phutil_nonempty_string($realname)) { $use_name = $realname; } diff --git a/src/applications/badges/editor/PhabricatorBadgesEditEngine.php b/src/applications/badges/editor/PhabricatorBadgesEditEngine.php index 721184852c..5d95254bbd 100644 --- a/src/applications/badges/editor/PhabricatorBadgesEditEngine.php +++ b/src/applications/badges/editor/PhabricatorBadgesEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorBadgesApplication'; + return PhabricatorBadgesApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/badges/editor/PhabricatorBadgesEditor.php b/src/applications/badges/editor/PhabricatorBadgesEditor.php index 785d8c989b..66b07390b5 100644 --- a/src/applications/badges/editor/PhabricatorBadgesEditor.php +++ b/src/applications/badges/editor/PhabricatorBadgesEditor.php @@ -4,7 +4,7 @@ final class PhabricatorBadgesEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorBadgesApplication'; + return PhabricatorBadgesApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/badges/phid/PhabricatorBadgesPHIDType.php b/src/applications/badges/phid/PhabricatorBadgesPHIDType.php index 0adea047f9..072d62f529 100644 --- a/src/applications/badges/phid/PhabricatorBadgesPHIDType.php +++ b/src/applications/badges/phid/PhabricatorBadgesPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorBadgesApplication'; + return PhabricatorBadgesApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/badges/query/PhabricatorBadgesAwardQuery.php b/src/applications/badges/query/PhabricatorBadgesAwardQuery.php index 57e53a5a30..ff975cf631 100644 --- a/src/applications/badges/query/PhabricatorBadgesAwardQuery.php +++ b/src/applications/badges/query/PhabricatorBadgesAwardQuery.php @@ -115,7 +115,7 @@ protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorBadgesApplication'; + return PhabricatorBadgesApplication::class; } } diff --git a/src/applications/badges/query/PhabricatorBadgesQuery.php b/src/applications/badges/query/PhabricatorBadgesQuery.php index cc59465f67..6fb61d01c0 100644 --- a/src/applications/badges/query/PhabricatorBadgesQuery.php +++ b/src/applications/badges/query/PhabricatorBadgesQuery.php @@ -77,7 +77,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorBadgesApplication'; + return PhabricatorBadgesApplication::class; } public function getBuiltinOrders() { diff --git a/src/applications/badges/query/PhabricatorBadgesSearchEngine.php b/src/applications/badges/query/PhabricatorBadgesSearchEngine.php index 6e84c30bc9..b08962ed4b 100644 --- a/src/applications/badges/query/PhabricatorBadgesSearchEngine.php +++ b/src/applications/badges/query/PhabricatorBadgesSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorBadgesApplication'; + return PhabricatorBadgesApplication::class; } public function newQuery() { diff --git a/src/applications/badges/query/PhabricatorBadgesTransactionQuery.php b/src/applications/badges/query/PhabricatorBadgesTransactionQuery.php index bbe86fe3fa..7ad887cc97 100644 --- a/src/applications/badges/query/PhabricatorBadgesTransactionQuery.php +++ b/src/applications/badges/query/PhabricatorBadgesTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorBadgesTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorBadgesApplication::class; + } + } diff --git a/src/applications/badges/typeahead/PhabricatorBadgesDatasource.php b/src/applications/badges/typeahead/PhabricatorBadgesDatasource.php index 458c9230d5..5fba42b933 100644 --- a/src/applications/badges/typeahead/PhabricatorBadgesDatasource.php +++ b/src/applications/badges/typeahead/PhabricatorBadgesDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorBadgesApplication'; + return PhabricatorBadgesApplication::class; } public function loadResults() { diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php index ba1edc26d3..67294db263 100644 --- a/src/applications/base/PhabricatorApplication.php +++ b/src/applications/base/PhabricatorApplication.php @@ -61,6 +61,21 @@ public function getShortDescription() { return pht('%s Application', $this->getName()); } + /** + * Extensions are allowed to register multi-character monograms. + * The name "Monogram" is actually a bit of a misnomer, + * but we're keeping it due to the history. + * + * @return array + */ + public function getMonograms() { + return array(); + } + + public function isDeprecated() { + return false; + } + final public function isInstalled() { if (!$this->canUninstall()) { return true; @@ -235,6 +250,11 @@ public function getHelpDocumentationArticles(PhabricatorUser $viewer) { return array(); } + /** + * Get the Application Overview in raw Remarkup + * + * @return string|null + */ public function getOverview() { return null; } diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php index db9d456094..d1b1fcfc8e 100644 --- a/src/applications/base/controller/PhabricatorController.php +++ b/src/applications/base/controller/PhabricatorController.php @@ -74,7 +74,7 @@ public function willBeginExecution() { $session_engine = new PhabricatorAuthSessionEngine(); $phsid = $request->getCookie(PhabricatorCookies::COOKIE_SESSION); - if (strlen($phsid)) { + if (phutil_nonempty_string($phsid)) { $session_user = $session_engine->loadUserForSession( PhabricatorAuthSession::TYPE_WEB, $phsid); diff --git a/src/applications/calendar/application/PhabricatorCalendarApplication.php b/src/applications/calendar/application/PhabricatorCalendarApplication.php index fa534280a0..c9ff3ac9d3 100644 --- a/src/applications/calendar/application/PhabricatorCalendarApplication.php +++ b/src/applications/calendar/application/PhabricatorCalendarApplication.php @@ -28,6 +28,10 @@ public function getTitleGlyph() { return "\xE2\x8C\xA8"; } + public function getMonograms() { + return array('E'); + } + public function getApplicationGroup() { return self::GROUP_UTILITIES; } diff --git a/src/applications/calendar/controller/PhabricatorCalendarImportEditController.php b/src/applications/calendar/controller/PhabricatorCalendarImportEditController.php index 0af9fa1540..9acbd0f2d2 100644 --- a/src/applications/calendar/controller/PhabricatorCalendarImportEditController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarImportEditController.php @@ -8,7 +8,22 @@ public function handleRequest(AphrontRequest $request) { ->setController($this); $id = $request->getURIData('id'); - if (!$id) { + if ($id) { + + // edit a specific entry + + $calendar_import = self::queryImportByID($request, $id); + if (!$calendar_import) { + return new Aphront404Response(); + } + + // pass the correct import engine to build the response + $engine->setImportEngine($calendar_import->getEngine()); + + } else { + + // create an entry + $list_uri = $this->getApplicationURI('import/'); $import_type = $request->getStr('importType'); @@ -27,6 +42,18 @@ public function handleRequest(AphrontRequest $request) { return $engine->buildResponse(); } + private static function queryImportByID(AphrontRequest $request, int $id) { + return id(new PhabricatorCalendarImportQuery()) + ->setViewer($request->getViewer()) + ->withIDs(array($id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + } + private function buildEngineTypeResponse($cancel_uri) { $import_engines = PhabricatorCalendarImportEngine::getAllImportEngines(); diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php index 1b23b13fbf..3737dc9ac1 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php +++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php @@ -33,7 +33,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function newEditableObject() { diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php index 9e3b23a43d..e5f5689025 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php +++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php @@ -7,7 +7,7 @@ final class PhabricatorCalendarEventEditor private $newIsAllDay; public function getEditorApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/calendar/editor/PhabricatorCalendarExportEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarExportEditEngine.php index bc8fc360c6..b1268f1f4b 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarExportEditEngine.php +++ b/src/applications/calendar/editor/PhabricatorCalendarExportEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function newEditableObject() { diff --git a/src/applications/calendar/editor/PhabricatorCalendarExportEditor.php b/src/applications/calendar/editor/PhabricatorCalendarExportEditor.php index 6ddd172d58..e8ec5601f7 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarExportEditor.php +++ b/src/applications/calendar/editor/PhabricatorCalendarExportEditor.php @@ -4,7 +4,7 @@ final class PhabricatorCalendarExportEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php index 7be3969671..210efe6961 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php +++ b/src/applications/calendar/editor/PhabricatorCalendarImportEditEngine.php @@ -33,7 +33,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function newEditableObject() { diff --git a/src/applications/calendar/editor/PhabricatorCalendarImportEditor.php b/src/applications/calendar/editor/PhabricatorCalendarImportEditor.php index f83869d7b9..f168523988 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarImportEditor.php +++ b/src/applications/calendar/editor/PhabricatorCalendarImportEditor.php @@ -4,7 +4,7 @@ final class PhabricatorCalendarImportEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/calendar/herald/PhabricatorCalendarEventHeraldAdapter.php b/src/applications/calendar/herald/PhabricatorCalendarEventHeraldAdapter.php index 49d5f38959..59982cecbc 100644 --- a/src/applications/calendar/herald/PhabricatorCalendarEventHeraldAdapter.php +++ b/src/applications/calendar/herald/PhabricatorCalendarEventHeraldAdapter.php @@ -5,7 +5,7 @@ final class PhabricatorCalendarEventHeraldAdapter extends HeraldAdapter { private $object; public function getAdapterApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php b/src/applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php index c37ba6c944..4fde813223 100644 --- a/src/applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php +++ b/src/applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php @@ -56,7 +56,7 @@ public function newEditEngineFields( public function getDisplayName(PhabricatorCalendarImport $import) { $filename_key = PhabricatorCalendarImportICSFileTransaction::PARAMKEY_NAME; $filename = $import->getParameter($filename_key); - if (strlen($filename)) { + if (phutil_nonempty_string($filename)) { return pht('ICS File "%s"', $filename); } else { return pht('ICS File'); diff --git a/src/applications/calendar/import/PhabricatorCalendarICSURIImportEngine.php b/src/applications/calendar/import/PhabricatorCalendarICSURIImportEngine.php index bd52ec5bc2..6c992e0836 100644 --- a/src/applications/calendar/import/PhabricatorCalendarICSURIImportEngine.php +++ b/src/applications/calendar/import/PhabricatorCalendarICSURIImportEngine.php @@ -58,16 +58,19 @@ public function newEditEngineFields( PhabricatorCalendarImport $import) { $fields = array(); - if ($engine->getIsCreate()) { - $fields[] = id(new PhabricatorTextEditField()) - ->setKey('uri') - ->setLabel(pht('URI')) - ->setDescription(pht('URI to import.')) - ->setTransactionType( - PhabricatorCalendarImportICSURITransaction::TRANSACTIONTYPE) - ->setConduitDescription(pht('URI to import.')) - ->setConduitTypeDescription(pht('New URI.')); - } + // If you are here, you already have the "can edit" capability. + // So you are supposed to be able to edit again your Calendar import URI. + $uri_key = PhabricatorCalendarImportICSURITransaction::PARAMKEY_URI; + $uri = $import->getParameter($uri_key); + $fields[] = id(new PhabricatorTextEditField()) + ->setKey('uri') + ->setLabel(pht('URI')) + ->setDescription(pht('URI to import.')) + ->setTransactionType( + PhabricatorCalendarImportICSURITransaction::TRANSACTIONTYPE) + ->setConduitDescription(pht('URI to import.')) + ->setConduitTypeDescription(pht('New URI.')) + ->setValue($uri); return $fields; } diff --git a/src/applications/calendar/import/PhabricatorCalendarImportEngine.php b/src/applications/calendar/import/PhabricatorCalendarImportEngine.php index 35e94634fe..626757c61d 100644 --- a/src/applications/calendar/import/PhabricatorCalendarImportEngine.php +++ b/src/applications/calendar/import/PhabricatorCalendarImportEngine.php @@ -447,7 +447,7 @@ private function getFullNodeUID(PhutilCalendarEventNode $node) { private function getParentNodeUID(PhutilCalendarEventNode $node) { $recurrence_id = $node->getRecurrenceID(); - if (!strlen($recurrence_id)) { + if (!phutil_nonempty_string($recurrence_id)) { return null; } @@ -456,7 +456,7 @@ private function getParentNodeUID(PhutilCalendarEventNode $node) { private function getNodeInstanceEpoch(PhutilCalendarEventNode $node) { $instance_iso = $node->getRecurrenceID(); - if (strlen($instance_iso)) { + if (phutil_nonempty_string($instance_iso)) { $instance_datetime = PhutilCalendarAbsoluteDateTime::newFromISO8601( $instance_iso); $instance_epoch = $instance_datetime->getEpoch(); diff --git a/src/applications/calendar/phid/PhabricatorCalendarEventPHIDType.php b/src/applications/calendar/phid/PhabricatorCalendarEventPHIDType.php index fd8e86a1f2..9048a7d208 100644 --- a/src/applications/calendar/phid/PhabricatorCalendarEventPHIDType.php +++ b/src/applications/calendar/phid/PhabricatorCalendarEventPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/calendar/phid/PhabricatorCalendarExportPHIDType.php b/src/applications/calendar/phid/PhabricatorCalendarExportPHIDType.php index c642632ca8..eaf4562150 100644 --- a/src/applications/calendar/phid/PhabricatorCalendarExportPHIDType.php +++ b/src/applications/calendar/phid/PhabricatorCalendarExportPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/calendar/phid/PhabricatorCalendarExternalInviteePHIDType.php b/src/applications/calendar/phid/PhabricatorCalendarExternalInviteePHIDType.php index a5e86893ac..6e50246bb0 100644 --- a/src/applications/calendar/phid/PhabricatorCalendarExternalInviteePHIDType.php +++ b/src/applications/calendar/phid/PhabricatorCalendarExternalInviteePHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/calendar/phid/PhabricatorCalendarImportPHIDType.php b/src/applications/calendar/phid/PhabricatorCalendarImportPHIDType.php index 876ec7acf7..32118f4fd4 100644 --- a/src/applications/calendar/phid/PhabricatorCalendarImportPHIDType.php +++ b/src/applications/calendar/phid/PhabricatorCalendarImportPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php index 683d6cd918..2db8cdfdea 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php @@ -93,7 +93,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } } diff --git a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php index db50bb4d77..90f0bab517 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php @@ -222,12 +222,16 @@ protected function loadPage() { $limit = $this->getRecurrenceLimit($event, $raw_limit); + // note that this can be NULL for some imported events $set = $event->newRecurrenceSet(); - $recurrences = $set->getEventsBetween( - $start_date, - $end_date, - $limit + 1); + $recurrences = array(); + if ($set) { + $recurrences = $set->getEventsBetween( + $start_date, + $end_date, + $limit + 1); + } // We're generating events from the beginning and then filtering them // here (instead of only generating events starting at the start date) @@ -509,7 +513,7 @@ protected function shouldGroupQueryResultRows() { } public function getQueryApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } protected function willFilterPage(array $events) { diff --git a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php index 79d4321545..3a6e931515 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php @@ -12,7 +12,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function newQuery() { diff --git a/src/applications/calendar/query/PhabricatorCalendarEventTransactionQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventTransactionQuery.php index cc465752fb..989e812ffc 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventTransactionQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorCalendarEventTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorCalendarApplication::class; + } + } diff --git a/src/applications/calendar/query/PhabricatorCalendarExportQuery.php b/src/applications/calendar/query/PhabricatorCalendarExportQuery.php index 7ef970216f..ecb2788459 100644 --- a/src/applications/calendar/query/PhabricatorCalendarExportQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarExportQuery.php @@ -84,7 +84,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } } diff --git a/src/applications/calendar/query/PhabricatorCalendarExportSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarExportSearchEngine.php index 032cab0c02..c718374da2 100644 --- a/src/applications/calendar/query/PhabricatorCalendarExportSearchEngine.php +++ b/src/applications/calendar/query/PhabricatorCalendarExportSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/calendar/query/PhabricatorCalendarExportTransactionQuery.php b/src/applications/calendar/query/PhabricatorCalendarExportTransactionQuery.php index 32b9d71b65..5858a20f58 100644 --- a/src/applications/calendar/query/PhabricatorCalendarExportTransactionQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarExportTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorCalendarExportTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorCalendarApplication::class; + } + } diff --git a/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php b/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php index 35891cfd28..f5d8f1eb32 100644 --- a/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php @@ -58,7 +58,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } } diff --git a/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php b/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php index 731b1209cc..cd71c0f463 100644 --- a/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php @@ -101,7 +101,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } } diff --git a/src/applications/calendar/query/PhabricatorCalendarImportLogSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarImportLogSearchEngine.php index 99f292f9a8..fd6896c37d 100644 --- a/src/applications/calendar/query/PhabricatorCalendarImportLogSearchEngine.php +++ b/src/applications/calendar/query/PhabricatorCalendarImportLogSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/calendar/query/PhabricatorCalendarImportQuery.php b/src/applications/calendar/query/PhabricatorCalendarImportQuery.php index 0e3dbbf387..7a3f12895f 100644 --- a/src/applications/calendar/query/PhabricatorCalendarImportQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarImportQuery.php @@ -89,7 +89,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } } diff --git a/src/applications/calendar/query/PhabricatorCalendarImportSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarImportSearchEngine.php index a5e44812ea..509b1224e6 100644 --- a/src/applications/calendar/query/PhabricatorCalendarImportSearchEngine.php +++ b/src/applications/calendar/query/PhabricatorCalendarImportSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/calendar/query/PhabricatorCalendarImportTransactionQuery.php b/src/applications/calendar/query/PhabricatorCalendarImportTransactionQuery.php index 123ec9b3c4..bad8217b66 100644 --- a/src/applications/calendar/query/PhabricatorCalendarImportTransactionQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarImportTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorCalendarImportTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorCalendarApplication::class; + } + } diff --git a/src/applications/calendar/typeahead/PhabricatorCalendarInviteeDatasource.php b/src/applications/calendar/typeahead/PhabricatorCalendarInviteeDatasource.php index 987f9cbf09..7e8271fa92 100644 --- a/src/applications/calendar/typeahead/PhabricatorCalendarInviteeDatasource.php +++ b/src/applications/calendar/typeahead/PhabricatorCalendarInviteeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/calendar/typeahead/PhabricatorCalendarInviteeUserDatasource.php b/src/applications/calendar/typeahead/PhabricatorCalendarInviteeUserDatasource.php index c799602d8a..0f834252ca 100644 --- a/src/applications/calendar/typeahead/PhabricatorCalendarInviteeUserDatasource.php +++ b/src/applications/calendar/typeahead/PhabricatorCalendarInviteeUserDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorCalendarApplication'; + return PhabricatorCalendarApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/calendar/typeahead/PhabricatorCalendarInviteeViewerFunctionDatasource.php b/src/applications/calendar/typeahead/PhabricatorCalendarInviteeViewerFunctionDatasource.php index 6f9a8292f4..d653c9bdea 100644 --- a/src/applications/calendar/typeahead/PhabricatorCalendarInviteeViewerFunctionDatasource.php +++ b/src/applications/calendar/typeahead/PhabricatorCalendarInviteeViewerFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php b/src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php index 26a968d13d..51abdd8cad 100644 --- a/src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php +++ b/src/applications/calendar/xaction/PhabricatorCalendarImportICSFileTransaction.php @@ -53,7 +53,7 @@ public function validateTransactions($object, array $xactions) { $new_value = $object->getParameter(self::PARAMKEY_FILE); foreach ($xactions as $xaction) { $new_value = $xaction->getNewValue(); - if (!strlen($new_value)) { + if (!phutil_nonempty_string($new_value)) { continue; } diff --git a/src/applications/celerity/controller/CelerityResourceController.php b/src/applications/celerity/controller/CelerityResourceController.php index 702fcefb2c..547c881df8 100644 --- a/src/applications/celerity/controller/CelerityResourceController.php +++ b/src/applications/celerity/controller/CelerityResourceController.php @@ -113,7 +113,7 @@ protected function serveResource(array $spec) { $range = AphrontRequest::getHTTPHeader('Range'); - if (strlen($range)) { + if (phutil_nonempty_string($range)) { $response->setContentLength(strlen($data)); list($range_begin, $range_end) = $response->parseHTTPRange($range); diff --git a/src/applications/celerity/management/CelerityManagementGenerateSpritesWorkflow.php b/src/applications/celerity/management/CelerityManagementGenerateSpritesWorkflow.php new file mode 100644 index 0000000000..52735b00f7 --- /dev/null +++ b/src/applications/celerity/management/CelerityManagementGenerateSpritesWorkflow.php @@ -0,0 +1,108 @@ +setName('sprites') + ->setExamples('**sprites** [options]') + ->setSynopsis(pht('Rebuild CSS sprite sheets.')) + ->setArguments( + array( + array( + 'name' => 'force', + 'help' => pht('Force regeneration even no sources have changed.'), + ), + array( + 'name' => 'no-map', + 'help' => + pht( + 'Do not invoke `%s` after updating sprites', + 'celerity map'), + ), + )); + } + + public function execute(PhutilArgumentParser $args) { + $resources_map = CelerityPhysicalResources::getAll(); + + $console = PhutilConsole::getConsole(); + + $root = dirname(phutil_get_library_root('phorge')); + $webroot = $root.'/webroot/rsrc'; + $webroot = Filesystem::readablePath($webroot); + + $generator = new CeleritySpriteGenerator(); + + $sheets = array( + 'tokens' => $generator->buildTokenSheet(), + 'login' => $generator->buildLoginSheet(), + ); + + list($err) = exec_manual('optipng'); + if ($err) { + $have_optipng = false; + $console->writeErr( + " %s %s\n%s\n", + pht('WARNING'), + pht('`%s` not found in PATH.', 'optipng'), + pht('Sprites will not be optimized! Install `%s`!', 'optipng')); + } else { + $have_optipng = true; + } + + foreach ($sheets as $name => $sheet) { + + $sheet->setBasePath($root); + + $manifest_path = $root.'/resources/sprite/manifest/'.$name.'.json'; + if (!$args->getArg('force')) { + if (Filesystem::pathExists($manifest_path)) { + $data = Filesystem::readFile($manifest_path); + $data = phutil_json_decode($data); + if (!$sheet->needsRegeneration($data)) { + continue; + } + } + } + + $sheet + ->generateCSS($webroot."/css/sprite-{$name}.css") + ->generateManifest($root."/resources/sprite/manifest/{$name}.json"); + + foreach ($sheet->getScales() as $scale) { + if ($scale == 1) { + $sheet_name = "sprite-{$name}.png"; + } else { + $sheet_name = "sprite-{$name}-X{$scale}.png"; + } + + $full_path = "{$webroot}/image/{$sheet_name}"; + $sheet->generateImage($full_path, $scale); + + if ($have_optipng) { + $console->writeOut("%s\n", pht('Optimizing...')); + phutil_passthru('optipng -o7 -clobber %s', $full_path); + } + } + } + + $run_map = !$args->getArg('no-map'); + + if ($run_map) { + $console->writeOut( + "%s\n", + pht('Done generating sprites - updating map...')); + $map_flow = id($args->getWorkflows())['map']; + // this is very hacky, but it works because `map` has no arguments. + $map_flow->execute($args); + } else { + $console->writeOut("%s\n", pht('Done.')); + return 0; + } + + } + + +} diff --git a/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php b/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php index 39ee4be57a..b1298e35a7 100644 --- a/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityDarkModePostprocessor.php @@ -218,6 +218,67 @@ public function buildVariables() { 'grey.button.gradient' => 'linear-gradient(to bottom, #223246, #223246)', 'grey.button.hover' => 'linear-gradient(to bottom, #1c293b, #1c293b)', + // Codeblock syntax highlighting + 'syntax.highlighted-line' => '#fa8', + 'syntax.comment' => '#6d6', + 'syntax.comment-multiline' => '#6d6', + 'syntax.comment-single' => '#6d6', + 'syntax.comment-special' => '#6d6', + 'syntax.string-doc' => '#fff', + 'syntax.string-heredoc' => '#fff', + 'syntax.string' => '#f88', + 'syntax.string-backtick' => '#f88', + 'syntax.literal-string-char' => '#f88', + 'syntax.string-double' => '#f88', + 'syntax.string-single' => '#f88', + 'syntax.string-other' => '#f88', + 'syntax.string-regex' => '#f88', + 'syntax.name-variable' => '#8ff', + 'syntax.variable-instance' => '#8ff', + 'syntax.variable-global' => '#8ff', + 'syntax.name-attribute' => '#4cf', + 'syntax.keyword-constant' => '#0cf', + 'syntax.name-operator' => '#0cf', + 'syntax.keyword' => '#e8e', + 'syntax.keyword-declaration' => '#e8e', + 'syntax.keyword-namespace' => '#e8e', + 'syntax.keyword-type' => '#e8e', + 'syntax.comment-preproc' => '#08f', + 'syntax.keyword-preproc' => '#08f', + 'syntax.keyword-reserved' => '#08f', + 'syntax.name-builtin' => '#08f', + 'syntax.builtin-pseudo' => '#08f', + 'syntax.name-class' => '#4ff', + 'syntax.name-tag' => '#dc0', + 'syntax.name-variable-class' => '#4ff', + 'syntax.name-function' => '#8af', + 'syntax.name-exception' => '#ed8', + 'syntax.operator' => '#aaa', + 'syntax.punctuation' => '#aaa', + 'syntax.literal-string-symbol' => '#aaa', + 'syntax.literal-number' => '#fa4', + 'syntax.literal-number-float' => '#fa4', + 'syntax.literal-number-hex' => '#fa4', + 'syntax.literal-number-integer' => '#fa4', + 'syntax.literal-number-octal' => '#fa4', + 'syntax.literal-number-integer-long' => '#fa4', + 'syntax.generic-deleted' => '#f55', + 'syntax.generic-red' => '#f52', + 'syntax.generic-heading' => '#fff', + 'syntax.generic-inserted' => '#4f4', + 'syntax.generic-output' => '#ccc', + 'syntax.generic-prompt' => '#fff', + 'syntax.generic-underline' => '#f4f', + 'syntax.generic-traceback' => '#07f', + 'syntax.name-decorator' => '#c7f', + 'syntax.name-identifier' => '#92969d', + 'syntax.name-entity' => '#f44', + 'syntax.name-label' => '#aa0', + 'syntax.name-namespace' => '#48f', + 'syntax.operator-word' => '#c7f', + 'syntax.text-whitespace' => '#bbb', + 'syntax.literal-string-escape' => '#d84', + 'syntax.literal-string-interpol' => '#b6b', ); } diff --git a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php index be039772c1..a4668775cc 100644 --- a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php +++ b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php @@ -247,6 +247,67 @@ public function buildVariables() { 'delete-color' => '#c0392b', 'create-color' => '#139543', + // Codeblock syntax highlighting + 'syntax.highlighted-line' => '#ffffcc', + 'syntax.comment' => '#74777d', + 'syntax.comment-multiline' => '#74777d', + 'syntax.comment-single' => '#74777d', + 'syntax.comment-special' => '#74777d', + 'syntax.string-doc' => '#000000', + 'syntax.string-heredoc' => '#000000', + 'syntax.string' => '#766510', + 'syntax.string-backtick' => '#766510', + 'syntax.literal-string-char' => '#766510', + 'syntax.string-double' => '#766510', + 'syntax.string-single' => '#766510', + 'syntax.string-other' => '#766510', + 'syntax.string-regex' => '#bb6688', + 'syntax.name-variable' => '#001294', + 'syntax.variable-instance' => '#001294', + 'syntax.variable-global' => '#001294', + 'syntax.name-attribute' => '#354bb3', + 'syntax.keyword-constant' => '#000a65', + 'syntax.name-operator' => '#000a65', + 'syntax.keyword' => '#aa4000', + 'syntax.keyword-declaration' => '#aa4000', + 'syntax.keyword-namespace' => '#aa4000', + 'syntax.keyword-type' => '#aa4000', + 'syntax.comment-preproc' => '#304a96', + 'syntax.keyword-preproc' => '#304a96', + 'syntax.keyword-reserved' => '#304a96', + 'syntax.name-builtin' => '#304a96', + 'syntax.builtin-pseudo' => '#304a96', + 'syntax.name-class' => '#00702a', + 'syntax.name-tag' => '#00702a', + 'syntax.name-variable-class' => '#00702a', + 'syntax.name-function' => '#004012', + 'syntax.name-exception' => '#004012', + 'syntax.operator' => '#aa2211', + 'syntax.punctuation' => '#000000', + 'syntax.literal-string-symbol' => '#aa2211', + 'syntax.literal-number' => '#601200', + 'syntax.literal-number-float' => '#601200', + 'syntax.literal-number-hex' => '#601200', + 'syntax.literal-number-integer' => '#601200', + 'syntax.literal-number-octal' => '#601200', + 'syntax.literal-number-integer-long' => '#601200', + 'syntax.generic-deleted' => '#a00000', + 'syntax.generic-red' => '#ff0000', + 'syntax.generic-heading' => '#000080', + 'syntax.generic-inserted' => '#00a000', + 'syntax.generic-output' => '#808080', + 'syntax.generic-prompt' => '#000080', + 'syntax.generic-underline' => '#800080', + 'syntax.generic-traceback' => '#0040d0', + 'syntax.name-decorator' => '#aa22ff', + 'syntax.name-identifier' => '#92969d', + 'syntax.name-entity' => '#d2413a', + 'syntax.name-label' => '#a0a000', + 'syntax.name-namespace' => '#0000ff', + 'syntax.operator-word' => '#aa22ff', + 'syntax.text-whitespace' => '#bbbbbb', + 'syntax.literal-string-escape' => '#bb6622', + 'syntax.literal-string-interpol' => '#bb66bb', ); } diff --git a/src/applications/chatlog/application/PhabricatorChatLogApplication.php b/src/applications/chatlog/application/PhabricatorChatLogApplication.php deleted file mode 100644 index 912ddd9b6b..0000000000 --- a/src/applications/chatlog/application/PhabricatorChatLogApplication.php +++ /dev/null @@ -1,43 +0,0 @@ - array( - '' => 'PhabricatorChatLogChannelListController', - 'channel/(?P[^/]+)/' - => 'PhabricatorChatLogChannelLogController', - ), - ); - } - -} diff --git a/src/applications/chatlog/conduit/ChatLogConduitAPIMethod.php b/src/applications/chatlog/conduit/ChatLogConduitAPIMethod.php deleted file mode 100644 index e524ef60da..0000000000 --- a/src/applications/chatlog/conduit/ChatLogConduitAPIMethod.php +++ /dev/null @@ -1,9 +0,0 @@ - 'optional list', - 'limit' => 'optional int (default = 100)', - ); - } - - protected function defineReturnType() { - return 'nonempty list'; - } - - protected function execute(ConduitAPIRequest $request) { - $query = new PhabricatorChatLogQuery(); - - $channel_ids = $request->getValue('channelIDs'); - if ($channel_ids) { - $query->withChannelIDs($channel_ids); - } - - $limit = $request->getValue('limit'); - if (!$limit) { - $limit = 100; - } - $query->setLimit($limit); - - $logs = $query->execute(); - - $results = array(); - foreach ($logs as $log) { - $results[] = array( - 'channelID' => $log->getChannelID(), - 'epoch' => $log->getEpoch(), - 'author' => $log->getAuthor(), - 'type' => $log->getType(), - 'message' => $log->getMessage(), - 'loggedByPHID' => $log->getLoggedByPHID(), - ); - } - - return $results; - } - -} diff --git a/src/applications/chatlog/conduit/ChatLogRecordConduitAPIMethod.php b/src/applications/chatlog/conduit/ChatLogRecordConduitAPIMethod.php deleted file mode 100644 index fe972222ae..0000000000 --- a/src/applications/chatlog/conduit/ChatLogRecordConduitAPIMethod.php +++ /dev/null @@ -1,72 +0,0 @@ - 'required list', - ); - } - - protected function defineReturnType() { - return 'list'; - } - - protected function execute(ConduitAPIRequest $request) { - $logs = $request->getValue('logs'); - if (!is_array($logs)) { - $logs = array(); - } - - $template = new PhabricatorChatLogEvent(); - $template->setLoggedByPHID($request->getUser()->getPHID()); - - $objs = array(); - foreach ($logs as $log) { - $channel_name = idx($log, 'channel'); - $service_name = idx($log, 'serviceName'); - $service_type = idx($log, 'serviceType'); - - $channel = id(new PhabricatorChatLogChannel())->loadOneWhere( - 'channelName = %s AND serviceName = %s AND serviceType = %s', - $channel_name, - $service_name, - $service_type); - - if (!$channel) { - $channel = id(new PhabricatorChatLogChannel()) - ->setChannelName($channel_name) - ->setserviceName($service_name) - ->setServiceType($service_type) - ->setViewPolicy(PhabricatorPolicies::POLICY_USER) - ->setEditPolicy(PhabricatorPolicies::POLICY_USER) - ->save(); - } - - $obj = clone $template; - $obj->setChannelID($channel->getID()); - $obj->setType(idx($log, 'type')); - $obj->setAuthor(idx($log, 'author')); - $obj->setEpoch(idx($log, 'epoch')); - $obj->setMessage(idx($log, 'message')); - $obj->save(); - - $objs[] = $obj; - } - - return array_values(mpull($objs, 'getID')); - } - -} diff --git a/src/applications/chatlog/controller/PhabricatorChatLogChannelListController.php b/src/applications/chatlog/controller/PhabricatorChatLogChannelListController.php deleted file mode 100644 index 530c26770a..0000000000 --- a/src/applications/chatlog/controller/PhabricatorChatLogChannelListController.php +++ /dev/null @@ -1,41 +0,0 @@ -getViewer(); - - $channels = id(new PhabricatorChatLogChannelQuery()) - ->setViewer($viewer) - ->execute(); - - $list = new PHUIObjectItemListView(); - foreach ($channels as $channel) { - $item = id(new PHUIObjectItemView()) - ->setHeader($channel->getChannelName()) - ->setHref('/chatlog/channel/'.$channel->getID().'/') - ->addAttribute($channel->getServiceName()) - ->addAttribute($channel->getServiceType()); - $list->addItem($item); - } - - $crumbs = $this - ->buildApplicationCrumbs() - ->addTextCrumb(pht('Channel List'), $this->getApplicationURI()); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText('Channel List') - ->setObjectList($list); - - return $this->newPage() - ->setTitle(pht('Channel List')) - ->setCrumbs($crumbs) - ->appendChild($box); - - } -} diff --git a/src/applications/chatlog/controller/PhabricatorChatLogChannelLogController.php b/src/applications/chatlog/controller/PhabricatorChatLogChannelLogController.php deleted file mode 100644 index b9893f6924..0000000000 --- a/src/applications/chatlog/controller/PhabricatorChatLogChannelLogController.php +++ /dev/null @@ -1,320 +0,0 @@ -getViewer(); - $id = $request->getURIData('channelID'); - - $uri = new PhutilURI($request->getPath()); - - $pager = new AphrontCursorPagerView(); - $pager->setURI($uri); - $pager->setPageSize(250); - - $query = id(new PhabricatorChatLogQuery()) - ->setViewer($viewer) - ->withChannelIDs(array($id)); - - $channel = id(new PhabricatorChatLogChannelQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->executeOne(); - - if (!$channel) { - return new Aphront404Response(); - } - - list($after, $before, $map) = $this->getPagingParameters($request, $query); - - $pager->setAfterID($after); - $pager->setBeforeID($before); - - $logs = $query->executeWithCursorPager($pager); - - // Show chat logs oldest-first. - $logs = array_reverse($logs); - - - // Divide all the logs into blocks, where a block is the same author saying - // several things in a row. A block ends when another user speaks, or when - // two minutes pass without the author speaking. - - $blocks = array(); - $block = null; - - $last_author = null; - $last_epoch = null; - foreach ($logs as $log) { - $this_author = $log->getAuthor(); - $this_epoch = $log->getEpoch(); - - // Decide whether we should start a new block or not. - $new_block = ($this_author !== $last_author) || - ($this_epoch - (60 * 2) > $last_epoch); - - if ($new_block) { - if ($block) { - $blocks[] = $block; - } - $block = array( - 'id' => $log->getID(), - 'epoch' => $this_epoch, - 'author' => $this_author, - 'logs' => array($log), - ); - } else { - $block['logs'][] = $log; - } - - $last_author = $this_author; - $last_epoch = $this_epoch; - } - if ($block) { - $blocks[] = $block; - } - - // Figure out CSS classes for the blocks. We alternate colors between - // lines, and highlight the entire block which contains the target ID or - // date, if applicable. - - foreach ($blocks as $key => $block) { - $classes = array(); - if ($key % 2) { - $classes[] = 'alternate'; - } - $ids = mpull($block['logs'], 'getID', 'getID'); - if (array_intersect_key($ids, $map)) { - $classes[] = 'highlight'; - } - $blocks[$key]['class'] = $classes ? implode(' ', $classes) : null; - } - - - require_celerity_resource('phabricator-chatlog-css'); - - $out = array(); - foreach ($blocks as $block) { - $author = $block['author']; - $author = id(new PhutilUTF8StringTruncator()) - ->setMaximumGlyphs(18) - ->truncateString($author); - $author = phutil_tag('td', array('class' => 'author'), $author); - - $href = $uri->alter('at', $block['id']); - $timestamp = $block['epoch']; - $timestamp = phabricator_datetime($timestamp, $viewer); - $timestamp = phutil_tag( - 'a', - array( - 'href' => $href, - 'class' => 'timestamp', - ), - $timestamp); - - $message = mpull($block['logs'], 'getMessage'); - $message = implode("\n", $message); - $message = phutil_tag( - 'td', - array( - 'class' => 'message', - ), - array( - $timestamp, - $message, - )); - - $out[] = phutil_tag( - 'tr', - array( - 'class' => $block['class'], - ), - array( - $author, - $message, - )); - } - - $links = array(); - - $first_uri = $pager->getFirstPageURI(); - if ($first_uri) { - $links[] = phutil_tag( - 'a', - array( - 'href' => $first_uri, - ), - "\xC2\xAB ".pht('Newest')); - } - - $prev_uri = $pager->getPrevPageURI(); - if ($prev_uri) { - $links[] = phutil_tag( - 'a', - array( - 'href' => $prev_uri, - ), - "\xE2\x80\xB9 ".pht('Newer')); - } - - $next_uri = $pager->getNextPageURI(); - if ($next_uri) { - $links[] = phutil_tag( - 'a', - array( - 'href' => $next_uri, - ), - pht('Older')." \xE2\x80\xBA"); - } - - $pager_bottom = phutil_tag( - 'div', - array('class' => 'phabricator-chat-log-pager-bottom'), - $links); - - $crumbs = $this - ->buildApplicationCrumbs() - ->addTextCrumb($channel->getChannelName(), $uri); - - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->setMethod('GET') - ->setAction($uri) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Date')) - ->setName('date') - ->setValue($request->getStr('date'))) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Jump'))); - - $table = phutil_tag( - 'table', - array( - 'class' => 'phabricator-chat-log', - ), - $out); - - $log = phutil_tag( - 'div', - array( - 'class' => 'phabricator-chat-log-panel', - ), - $table); - - $jump_link = id(new PHUIButtonView()) - ->setTag('a') - ->setHref('#latest') - ->setText(pht('Jump to Bottom')) - ->setIcon('fa-arrow-circle-down'); - - $jump_target = phutil_tag( - 'div', - array( - 'id' => 'latest', - )); - - $content = phutil_tag( - 'div', - array( - 'class' => 'phabricator-chat-log-wrap', - ), - array( - $log, - $jump_target, - $pager_bottom, - )); - - $header = id(new PHUIHeaderView()) - ->setHeader($channel->getChannelName()) - ->setSubHeader($channel->getServiceName()) - ->addActionLink($jump_link); - - $box = id(new PHUIObjectBoxView()) - ->setHeader($header) - ->setCollapsed(true) - ->appendChild($content); - - $box->setShowHide( - pht('Search Dates'), - pht('Hide Dates'), - $form, - '#'); - - return $this->newPage() - ->setTitle(pht('Channel Log')) - ->setCrumbs($crumbs) - ->appendChild($box); - - } - - /** - * From request parameters, figure out where we should jump to in the log. - * We jump to either a date or log ID, but load a few lines of context before - * it so the user can see the nearby conversation. - */ - private function getPagingParameters( - AphrontRequest $request, - PhabricatorChatLogQuery $query) { - - $viewer = $request->getViewer(); - - $at_id = $request->getInt('at'); - $at_date = $request->getStr('date'); - - $context_log = null; - $map = array(); - - $query = clone $query; - $query->setLimit(8); - - if ($at_id) { - // Jump to the log in question, and load a few lines of context before - // it. - $context_logs = $query - ->setAfterID($at_id) - ->execute(); - - $context_log = last($context_logs); - - $map = array( - $at_id => true, - ); - - } else if ($at_date) { - $timestamp = PhabricatorTime::parseLocalTime($at_date, $viewer); - - if ($timestamp) { - $context_logs = $query - ->withMaximumEpoch($timestamp) - ->execute(); - - $context_log = last($context_logs); - - $target_log = head($context_logs); - if ($target_log) { - $map = array( - $target_log->getID() => true, - ); - } - } - } - - if ($context_log) { - $after = null; - $before = $context_log->getID() - 1; - } else { - $after = $request->getInt('after'); - $before = $request->getInt('before'); - } - - return array($after, $before, $map); - } - -} diff --git a/src/applications/chatlog/controller/PhabricatorChatLogController.php b/src/applications/chatlog/controller/PhabricatorChatLogController.php deleted file mode 100644 index 64fcbf20bb..0000000000 --- a/src/applications/chatlog/controller/PhabricatorChatLogController.php +++ /dev/null @@ -1,3 +0,0 @@ -channels = $channels; - return $this; - } - - public function withIDs(array $channel_ids) { - $this->channelIDs = $channel_ids; - return $this; - } - - protected function loadPage() { - $table = new PhabricatorChatLogChannel(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T c %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - $logs = $table->loadAllFromArray($data); - - return $logs; - } - - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); - - $where[] = $this->buildPagingClause($conn); - - if ($this->channelIDs) { - $where[] = qsprintf( - $conn, - 'id IN (%Ld)', - $this->channelIDs); - - } - - if ($this->channels) { - $where[] = qsprintf( - $conn, - 'channelName IN (%Ls)', - $this->channels); - } - - return $this->formatWhereClause($conn, $where); - } - - public function getQueryApplicationClass() { - return 'PhabricatorChatLogApplication'; - } - -} diff --git a/src/applications/chatlog/query/PhabricatorChatLogQuery.php b/src/applications/chatlog/query/PhabricatorChatLogQuery.php deleted file mode 100644 index 88cf6da7e3..0000000000 --- a/src/applications/chatlog/query/PhabricatorChatLogQuery.php +++ /dev/null @@ -1,84 +0,0 @@ -channelIDs = $channel_ids; - return $this; - } - - public function withMaximumEpoch($epoch) { - $this->maximumEpoch = $epoch; - return $this; - } - - protected function loadPage() { - $table = new PhabricatorChatLogEvent(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T e %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - $logs = $table->loadAllFromArray($data); - - return $logs; - } - - protected function willFilterPage(array $events) { - $channel_ids = mpull($events, 'getChannelID', 'getChannelID'); - - $channels = id(new PhabricatorChatLogChannelQuery()) - ->setViewer($this->getViewer()) - ->withIDs($channel_ids) - ->execute(); - $channels = mpull($channels, null, 'getID'); - - foreach ($events as $key => $event) { - $channel = idx($channels, $event->getChannelID()); - if (!$channel) { - unset($events[$key]); - continue; - } - - $event->attachChannel($channel); - } - - return $events; - } - - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); - - $where[] = $this->buildPagingClause($conn); - - if ($this->maximumEpoch !== null) { - $where[] = qsprintf( - $conn, - 'epoch <= %d', - $this->maximumEpoch); - } - - if ($this->channelIDs !== null) { - $where[] = qsprintf( - $conn, - 'channelID IN (%Ld)', - $this->channelIDs); - } - - return $this->formatWhereClause($conn, $where); - } - - public function getQueryApplicationClass() { - return 'PhabricatorChatLogApplication'; - } - -} diff --git a/src/applications/chatlog/storage/PhabricatorChatLogChannel.php b/src/applications/chatlog/storage/PhabricatorChatLogChannel.php deleted file mode 100644 index 2e62bcb6cf..0000000000 --- a/src/applications/chatlog/storage/PhabricatorChatLogChannel.php +++ /dev/null @@ -1,51 +0,0 @@ - array( - 'serviceName' => 'text64', - 'serviceType' => 'text32', - 'channelName' => 'text64', - ), - self::CONFIG_KEY_SCHEMA => array( - 'key_channel' => array( - 'columns' => array('channelName', 'serviceType', 'serviceName'), - 'unique' => true, - ), - ), - ) + parent::getConfiguration(); - } - - public function getCapabilities() { - return array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - ); - } - - public function getPolicy($capability) { - switch ($capability) { - case PhabricatorPolicyCapability::CAN_VIEW: - return $this->viewPolicy; - break; - case PhabricatorPolicyCapability::CAN_EDIT: - return $this->editPolicy; - break; - } - } - - public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { - return false; - } - -} diff --git a/src/applications/chatlog/storage/PhabricatorChatLogDAO.php b/src/applications/chatlog/storage/PhabricatorChatLogDAO.php deleted file mode 100644 index 8014168814..0000000000 --- a/src/applications/chatlog/storage/PhabricatorChatLogDAO.php +++ /dev/null @@ -1,9 +0,0 @@ - false, - self::CONFIG_COLUMN_SCHEMA => array( - 'author' => 'text64', - 'type' => 'text4', - 'message' => 'text', - ), - self::CONFIG_KEY_SCHEMA => array( - 'channel' => array( - 'columns' => array('epoch'), - ), - ), - ) + parent::getConfiguration(); - } - - public function attachChannel(PhabricatorChatLogChannel $channel) { - $this->channel = $channel; - return $this; - } - - public function getChannel() { - return $this->assertAttached($this->channel); - } - - -/* -( PhabricatorPolicyInterface )----------------------------------------- */ - - - public function getCapabilities() { - return array( - PhabricatorPolicyCapability::CAN_VIEW, - ); - } - - public function getPolicy($capability) { - return $this->getChannel()->getPolicy($capability); - } - - public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { - return $this->getChannel()->hasAutomaticCapability($capability, $viewer); - } - -} diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php index a4d5fdd568..994f282f44 100644 --- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php +++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php @@ -37,7 +37,7 @@ public function handleRequest(AphrontRequest $request) { // TODO: The relationship between ConduitAPIRequest and ConduitCall is a // little odd here and could probably be improved. Specifically, the // APIRequest is a sub-object of the Call, which does not parallel the - // role of AphrontRequest (which is an indepenent object). + // role of AphrontRequest (which is an independent object). // In particular, the setUser() and getUser() existing independently on // the Call and APIRequest is very awkward. @@ -289,7 +289,7 @@ private function authenticateUser( ); } - $token_string = idx($metadata, 'token'); + $token_string = idx($metadata, 'token', ''); if (strlen($token_string)) { if (strlen($token_string) != 32) { @@ -683,7 +683,7 @@ private function decodeConduitParams( // Otherwise, look for a single parameter called 'params' which has the // entire param dictionary JSON encoded. $params_json = $request->getStr('params'); - if (strlen($params_json)) { + if (phutil_nonempty_string($params_json)) { $params = null; try { $params = phutil_json_decode($params_json); diff --git a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php index 70a2253084..b3dfa805a2 100644 --- a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php +++ b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php @@ -247,6 +247,10 @@ private function buildMethodProperties(ConduitAPIMethod $method) { )); } + $view->addProperty( + pht('Summary'), + $method->getMethodSummary()); + $view->addProperty( pht('Returns'), $method->getReturnType()); diff --git a/src/applications/conduit/query/PhabricatorConduitLogQuery.php b/src/applications/conduit/query/PhabricatorConduitLogQuery.php index 19cb31a812..d9dcad695a 100644 --- a/src/applications/conduit/query/PhabricatorConduitLogQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitLogQuery.php @@ -107,7 +107,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorConduitApplication'; + return PhabricatorConduitApplication::class; } } diff --git a/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php b/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php index ab86ae6669..9e5e05b7b6 100644 --- a/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php +++ b/src/applications/conduit/query/PhabricatorConduitLogSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorConduitApplication'; + return PhabricatorConduitApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php index eb61d8c480..5e68850d5f 100644 --- a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php @@ -152,7 +152,7 @@ protected function willFilterPage(array $methods) { } public function getQueryApplicationClass() { - return 'PhabricatorConduitApplication'; + return PhabricatorConduitApplication::class; } } diff --git a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php index 06ba536dfe..2fd95c3550 100644 --- a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php +++ b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorConduitApplication'; + return PhabricatorConduitApplication::class; } public function canUseInPanelContext() { @@ -39,7 +39,7 @@ public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { $query->withIsInternal(false); $contains = $saved->getParameter('nameContains'); - if (strlen($contains)) { + if (phutil_nonempty_string($contains)) { $query->withNameContains($contains); } diff --git a/src/applications/conduit/query/PhabricatorConduitTokenQuery.php b/src/applications/conduit/query/PhabricatorConduitTokenQuery.php index 384efccadf..0f3f581513 100644 --- a/src/applications/conduit/query/PhabricatorConduitTokenQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitTokenQuery.php @@ -109,7 +109,7 @@ protected function willFilterPage(array $tokens) { } public function getQueryApplicationClass() { - return 'PhabricatorConduitApplication'; + return PhabricatorConduitApplication::class; } } diff --git a/src/applications/config/check/PhabricatorAuthSetupCheck.php b/src/applications/config/check/PhabricatorAuthSetupCheck.php index e2b076944e..8705047263 100644 --- a/src/applications/config/check/PhabricatorAuthSetupCheck.php +++ b/src/applications/config/check/PhabricatorAuthSetupCheck.php @@ -74,7 +74,8 @@ protected function executeChecks() { ->addRelatedPhabricatorConfig('auth.lock-config') ->addCommand( hsprintf( - 'phabricator/ $ ./bin/auth lock')); + '%s $./bin/auth lock', + PlatformSymbols::getPlatformServerPath())); } } } diff --git a/src/applications/config/check/PhabricatorBaseURISetupCheck.php b/src/applications/config/check/PhabricatorBaseURISetupCheck.php index 92e46641d7..e73c1455bf 100644 --- a/src/applications/config/check/PhabricatorBaseURISetupCheck.php +++ b/src/applications/config/check/PhabricatorBaseURISetupCheck.php @@ -96,9 +96,8 @@ protected function executeChecks() { ->setMessage($message) ->addCommand( hsprintf( - '$ %s', - csprintf( - './bin/config set phabricator.base-uri %s', - $base_uri_guess))); + '%s $./bin/config set phabricator.base-uri %s', + PlatformSymbols::getPlatformServerPath(), + $base_uri_guess)); } } diff --git a/src/applications/config/check/PhabricatorBinariesSetupCheck.php b/src/applications/config/check/PhabricatorBinariesSetupCheck.php index b87282fcc7..9bbf8a367e 100644 --- a/src/applications/config/check/PhabricatorBinariesSetupCheck.php +++ b/src/applications/config/check/PhabricatorBinariesSetupCheck.php @@ -104,7 +104,17 @@ protected function executeChecks() { switch ($vcs['versionControlSystem']) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: - $bad_versions = array(); + $bad_versions = array( + // We need 2.5.0 to use "git cat-file -t -- :" + // https://we.phorge.it/T15179 + '< 2.5.0' => pht( + 'The minimum supported version of Git on the server is %s, '. + 'which was released in %s. In older versions, the Git server '. + 'may not be able to escape arguments with the "--" operator. '. + 'Note: your users do not require a particular version of Git.', + '2.5.0', + '2015'), + ); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $bad_versions = array( diff --git a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php index 608bac675e..ceedd5137e 100644 --- a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php +++ b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php @@ -49,11 +49,14 @@ protected function executeChecks() { ->setName(pht('Daemons Are Not Running')) ->setSummary($summary) ->setMessage($message) - ->addCommand('$ ./bin/phd start'); + ->addCommand( + hsprintf( + '%s $./bin/phd start', + PlatformSymbols::getPlatformServerPath())); } $expect_user = PhabricatorEnv::getEnvConfig('phd.user'); - if (strlen($expect_user)) { + if (phutil_nonempty_string($expect_user)) { try { $all_daemons = id(new PhabricatorDaemonLogQuery()) @@ -90,7 +93,10 @@ protected function executeChecks() { ->setSummary($summary) ->setMessage($message) ->addPhabricatorConfig('phd.user') - ->addCommand('$ ./bin/phd restart'); + ->addCommand( + hsprintf( + '%s $./bin/phd restart', + PlatformSymbols::getPlatformServerPath())); break; } diff --git a/src/applications/config/check/PhabricatorDatabaseSetupCheck.php b/src/applications/config/check/PhabricatorDatabaseSetupCheck.php index a5989f37b3..99815d85e6 100644 --- a/src/applications/config/check/PhabricatorDatabaseSetupCheck.php +++ b/src/applications/config/check/PhabricatorDatabaseSetupCheck.php @@ -35,11 +35,13 @@ protected function executeChecks() { ->addPhabricatorConfig('mysql.port') ->addCommand( hsprintf( - '$ ./bin/config set mysql.host %s', + '%s $./bin/config set mysql.host %s', + PlatformSymbols::getPlatformServerPath(), $host)) ->addCommand( hsprintf( - '$ ./bin/config set mysql.port %s', + '%s $./bin/config set mysql.port %s', + PlatformSymbols::getPlatformServerPath(), $port)); } @@ -106,7 +108,7 @@ private function executeRefChecks(PhabricatorDatabaseRef $ref) { 'The "InnoDB" engine is not available in MySQL (on host "%s"). '. 'Enable InnoDB in your MySQL configuration.'. "\n\n". - '(If you aleady created tables, MySQL incorrectly used some other '. + '(If you already created tables, MySQL incorrectly used some other '. 'engine to create them. You need to convert them or drop and '. 'reinitialize them.)', $ref_key); @@ -134,7 +136,10 @@ private function executeRefChecks(PhabricatorDatabaseRef $ref) { ->setName(pht('Setup MySQL Schema')) ->setMessage($message) ->setIsFatal(true) - ->addCommand(hsprintf('$ ./bin/storage upgrade')); + ->addCommand( + hsprintf( + '%s $./bin/storage upgrade', + PlatformSymbols::getPlatformServerPath())); return true; } @@ -160,7 +165,9 @@ private function executeRefChecks(PhabricatorDatabaseRef $ref) { ->setIsFatal(true) ->setMessage($message) ->addCommand( - hsprintf('$ ./bin/storage upgrade')); + hsprintf( + '%s $./bin/storage upgrade', + PlatformSymbols::getPlatformServerPath())); return true; } diff --git a/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php b/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php index 8466c5a6c6..268f55f80b 100644 --- a/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php +++ b/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php @@ -60,7 +60,10 @@ protected function executeChecks() { $this ->newIssue('elastic.missing-index') ->setName(pht('Elasticsearch Index Not Found')) - ->addCommand('./bin/search init') + ->addCommand( + hsprintf( + '%s $./bin/search init', + PlatformSymbols::getPlatformServerPath())) ->setSummary($summary) ->setMessage($message); @@ -76,7 +79,10 @@ protected function executeChecks() { $this ->newIssue('elastic.broken-index') ->setName(pht('Elasticsearch Index Schema Mismatch')) - ->addCommand('./bin/search init') + ->addCommand( + hsprintf( + '%s $./bin/search init', + PlatformSymbols::getPlatformServerPath())) ->setSummary($summary) ->setMessage($message); } diff --git a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php index ae14a0ab0a..070127ccbd 100644 --- a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php +++ b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php @@ -76,7 +76,10 @@ protected function executeChecks() { $issue->setMessage($message); if ($found_local) { - $command = csprintf('$ ./bin/config delete %s', $key); + $command = hsprintf( + '%s $./bin/config delete %s', + PlatformSymbols::getPlatformServerPath(), + $key); $issue->addCommand($command); } @@ -166,9 +169,12 @@ protected function executeChecks() { 'target' => '_blank', ), $doc_name)); - $command = csprintf( - '$ ./bin/config delete --database %R', - $key); + $command = hsprintf( + '%s $%s', + PlatformSymbols::getPlatformServerPath(), + csprintf( + './bin/config delete --database %R', + $key)); $this->newIssue('config.locked.'.$key) ->setShortName(pht('Deprecated Config Source')) diff --git a/src/applications/config/check/PhabricatorManualActivitySetupCheck.php b/src/applications/config/check/PhabricatorManualActivitySetupCheck.php index 660792ca53..59b04d895f 100644 --- a/src/applications/config/check/PhabricatorManualActivitySetupCheck.php +++ b/src/applications/config/check/PhabricatorManualActivitySetupCheck.php @@ -59,10 +59,10 @@ private function raiseSearchReindexIssue() { phutil_tag( 'a', array( - 'href' => 'https://phurl.io/u/reindex', + 'href' => 'https://secure.phabricator.com/T11932', 'target' => '_blank', ), - 'https://phurl.io/u/reindex')); + 'https://secure.phabricator.com/T11932')); $message[] = pht( 'After rebuilding the index, run this command to clear this setup '. @@ -128,10 +128,10 @@ private function raiseRebuildIdentitiesIssue() { phutil_tag( 'a', array( - 'href' => 'https://phurl.io/u/repoIdentities', + 'href' => 'https://secure.phabricator.com/T12164', 'target' => '_blank', ), - 'https://phurl.io/u/repoIdentities')); + 'https://secure.phabricator.com/T12164')); $message[] = pht( 'After rebuilding repository identities, run this command to clear '. diff --git a/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php b/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php index 09b96d05cf..1862308052 100644 --- a/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php +++ b/src/applications/config/check/PhabricatorPHPConfigSetupCheck.php @@ -83,7 +83,7 @@ protected function executeChecks() { // NOTE: We're intentionally telling you to install "mysqlnd" here; on // Ubuntu, there's no separate "mysqli" package. - phutil_tag('tt', array(), 'sudo apt-get install php5-mysqlnd')); + phutil_tag('tt', array(), 'sudo apt-get install php-mysqlnd')); $this->newIssue('php.mysqli') ->setName(pht('MySQLi Extension Not Available')) @@ -103,7 +103,7 @@ protected function executeChecks() { 'native driver is recommended.'. "\n\n". 'You may be able to install the native driver with a command like: %s', - phutil_tag('tt', array(), 'sudo apt-get install php5-mysqlnd')); + phutil_tag('tt', array(), 'sudo apt-get install php-mysqlnd')); $this->newIssue('php.myqlnd') diff --git a/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php b/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php index 02c1134dc3..322ad26061 100644 --- a/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php +++ b/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php @@ -18,7 +18,7 @@ protected function executeChecks() { 'You are running PHP version %s. PHP versions between 7.0 and 7.1 '. 'are not supported'. "\n\n". - 'PHP removed reqiured signal handling features in '. + 'PHP removed required signal handling features in '. 'PHP 7.0, and did not restore an equivalent mechanism until PHP 7.1.'. "\n\n". 'Upgrade to PHP 7.1 or newer (recommended) or downgrade to an older '. @@ -30,7 +30,7 @@ protected function executeChecks() { ->setName(pht('PHP 7.0-7.1 Not Supported')) ->setMessage($message) ->addLink( - 'https://phurl.io/u/php7', + 'https://secure.phabricator.com/T12101', pht('PHP 7 Compatibility Information')); return; diff --git a/src/applications/config/check/PhabricatorStorageSetupCheck.php b/src/applications/config/check/PhabricatorStorageSetupCheck.php index 86aa1f2ebd..75639f2f43 100644 --- a/src/applications/config/check/PhabricatorStorageSetupCheck.php +++ b/src/applications/config/check/PhabricatorStorageSetupCheck.php @@ -151,19 +151,19 @@ private function checkS3() { $how_many = 0; - if (strlen($access_key)) { + if (phutil_nonempty_string($access_key)) { $how_many++; } - if (strlen($secret_key)) { + if (phutil_nonempty_string($secret_key)) { $how_many++; } - if (strlen($region)) { + if (phutil_nonempty_string($region)) { $how_many++; } - if (strlen($endpoint)) { + if (phutil_nonempty_string($endpoint)) { $how_many++; } diff --git a/src/applications/config/check/PhabricatorWebServerSetupCheck.php b/src/applications/config/check/PhabricatorWebServerSetupCheck.php index cc9660325c..1bbe3a3f2c 100644 --- a/src/applications/config/check/PhabricatorWebServerSetupCheck.php +++ b/src/applications/config/check/PhabricatorWebServerSetupCheck.php @@ -23,7 +23,7 @@ protected function executeChecks() { } $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); - if (!strlen($base_uri)) { + if (!$base_uri) { // If `phabricator.base-uri` is not set then we can't really do // anything. return; @@ -58,6 +58,7 @@ protected function executeChecks() { $gzip_future = id(new HTTPSFuture($base_uri)) ->addHeader('X-Setup-SelfCheck', 1) ->addHeader('Content-Encoding', 'gzip') + ->setMethod('POST') ->setTimeout(5) ->setData($gzip_compressed); diff --git a/src/applications/config/check/PhorgeCodeWarningSetupCheck.php b/src/applications/config/check/PhorgeCodeWarningSetupCheck.php new file mode 100644 index 0000000000..2aaf33ac25 --- /dev/null +++ b/src/applications/config/check/PhorgeCodeWarningSetupCheck.php @@ -0,0 +1,81 @@ +getWarnings(); + if (!$warnings) { + return; + } + + $link = phutil_tag( + 'a', + array('href' => 'https://we.phorge.it/w/docs/report-warnings/'), + pht('%s\'s home page', PlatformSymbols::getPlatformServerName())); + + $message = pht( + 'There is some deprecated code found in the %s code-base.'. + "\n\n". + "This isn't a problem yet, but it means that %s might stop working if ". + 'you upgrade PHP version.'. + "\n\n". + 'This page records a sample of the cases since last server restart. '. + "\n\n". + 'To solve this issue, either:'. + "\n\n". + '- Visit %s, file bug report with the information below, or'. + "\n". + '- Ignore this issue using the `Ignore` button below.'. + "\n\n", + PlatformSymbols::getPlatformServerName(), + PlatformSymbols::getPlatformServerName(), + $link); + $message = array($message); + + $message[] = pht('PHP version: %s', phpversion()); + $message[] = "\n\n"; + + $message[] = pht('Recorded items (sample):'); + $list = array(); + $warnings = array_reverse(isort($warnings, 'counter')); + foreach ($warnings as $key => $data) { + $summary = pht( + '%s, occurrences: %s', + $key, + $data['counter']); + + $trace = phutil_tag('tt', array(), + array($data['message'] , "\n", $data['trace'])); + + $list[] = phutil_tag( + 'li', + array(), + phutil_tag( + 'details', + array(), + array( + phutil_tag('summary', array(), $summary), + $trace, + ))); + } + $message[] = phutil_tag('ul', array(), $list); + + + $this->newIssue('deprecations') + ->setName(pht('Deprecated Code')) + ->setMessage($message) + ->setSummary(pht('There is some deprecated code found in the code-base.')) + ->addLink( + 'https://we.phorge.it/w/docs/report-warnings/', + 'More Details on the website'); + } + +} diff --git a/src/applications/config/controller/PhabricatorConfigConsoleController.php b/src/applications/config/controller/PhabricatorConfigConsoleController.php index bd23d6dde5..e545019666 100644 --- a/src/applications/config/controller/PhabricatorConfigConsoleController.php +++ b/src/applications/config/controller/PhabricatorConfigConsoleController.php @@ -85,14 +85,14 @@ public function newLibraryVersionTable() { $rows = array(); foreach ($versions as $name => $info) { $branchpoint = $info['branchpoint']; - if (strlen($branchpoint)) { + if (phutil_nonempty_string($branchpoint)) { $branchpoint = substr($branchpoint, 0, 12); } else { $branchpoint = null; } $version = $info['hash']; - if (strlen($version)) { + if (phutil_nonempty_string($version)) { $version = substr($version, 0, 12); } else { $version = pht('Unknown'); @@ -138,7 +138,7 @@ public function newLibraryVersionTable() { private function loadVersions(PhabricatorUser $viewer) { $specs = array( - 'phabricator', + 'phorge', 'arcanist', ); @@ -174,16 +174,25 @@ private function loadVersions(PhabricatorUser $viewer) { // A repository may have a bunch of remotes, but we're only going to look // for remotes we host to try to figure out where this repository branched. - $upstream_pattern = '(github\.com/phacility/|secure\.phabricator\.com/)'; + $upstream_pattern = + '('. + implode('|', array( + 'we\.phorge\.it/', + 'github\.com/phorgeit/', + 'github\.com/phacility/', + 'secure\.phabricator\.com/', + )). + ')'; $upstream_futures = array(); $lib_upstreams = array(); foreach ($specs as $lib) { $remote_future = $remote_futures[$lib]; - list($err, $stdout) = $remote_future->resolve(); - if ($err) { - // If this fails for whatever reason, just move on. + try { + list($stdout, $err) = $remote_future->resolvex(); + } catch (CommandException $e) { + $this->logGitErrorWithPotentialTips($e, $lib); continue; } @@ -250,13 +259,14 @@ private function loadVersions(PhabricatorUser $viewer) { $results = array(); foreach ($log_futures as $lib => $future) { - list($err, $stdout) = $future->resolve(); - if (!$err) { + try { + list($stdout, $err) = $future->resolvex(); list($hash, $epoch) = explode(' ', $stdout); - } else { + } catch (CommandException $e) { $hash = null; $epoch = null; - } + $this->logGitErrorWithPotentialTips($e, $lib); + } $result = array( 'hash' => $hash, @@ -267,7 +277,7 @@ private function loadVersions(PhabricatorUser $viewer) { $upstream_future = idx($upstream_futures, $lib); if ($upstream_future) { - list($err, $stdout) = $upstream_future->resolve(); + list($stdout, $err) = $upstream_future->resolvex(); if (!$err) { $branchpoint = trim($stdout); if (strlen($branchpoint)) { @@ -332,5 +342,36 @@ private function newBinaryVersionTable() { ->appendChild($table_view); } + /** + * Help in better troubleshooting git errors. + * @param CommandException $e Exception + * @param string $lib Library name involved + */ + private function logGitErrorWithPotentialTips($e, $lib) { + + // First, detect this specific error message related to [safe] stuff. + $expected_error_msg_part = 'detected dubious ownership in repository'; + $stderr = $e->getStderr(); + if (strpos($stderr, $expected_error_msg_part) !== false) { + + // Found! Let's show a nice resolution tip. + + // Complete path of the problematic repository. + $lib_root = dirname(phutil_get_library_root($lib)); + + phlog(pht( + "Cannot identify the version of the %s repository because ". + "the webserver does not trust it (more info on Task %s).\n". + "Try this system resolution:\n". + "sudo git config --system --add safe.directory %s", + $lib, + 'https://we.phorge.it/T15282', + $lib_root)); + } else { + + // Otherwise show a generic error message + phlog($e); + } + } } diff --git a/src/applications/config/controller/module/PhabricatorConfigModuleController.php b/src/applications/config/controller/module/PhabricatorConfigModuleController.php index 4f3f9c57cb..355166fea6 100644 --- a/src/applications/config/controller/module/PhabricatorConfigModuleController.php +++ b/src/applications/config/controller/module/PhabricatorConfigModuleController.php @@ -9,7 +9,7 @@ public function handleRequest(AphrontRequest $request) { $all_modules = PhabricatorConfigModule::getAllModules(); - if (!strlen($key)) { + if (!phutil_nonempty_string($key)) { $key = head_key($all_modules); } diff --git a/src/applications/config/controller/services/PhabricatorConfigClusterSearchController.php b/src/applications/config/controller/services/PhabricatorConfigClusterSearchController.php index 5e877d6b95..706d6b4fa9 100644 --- a/src/applications/config/controller/services/PhabricatorConfigClusterSearchController.php +++ b/src/applications/config/controller/services/PhabricatorConfigClusterSearchController.php @@ -60,8 +60,20 @@ private function renderStatusView($service) { foreach ($service->getHosts() as $host) { try { + // Default status icon + // + // At the moment the default status is shown also when + // you just use MySQL as search server. So, on MySQL it + // shows "Unknown" even if probably it should says "Active". + // If you have time, please improve the MySQL getConnectionStatus() + // to return something more useful than this default. + $default_status = array( + 'icon' => 'fa-question-circle', + 'color' => 'blue', + 'label' => pht('Unknown'), + ); $status = $host->getConnectionStatus(); - $status = idx($status_map, $status, array()); + $status = idx($status_map, $status, $default_status); } catch (Exception $ex) { $status['icon'] = 'fa-times'; $status['label'] = pht('Connection Error'); diff --git a/src/applications/config/controller/services/PhabricatorConfigDatabaseStatusController.php b/src/applications/config/controller/services/PhabricatorConfigDatabaseStatusController.php index 09f4f344b5..40c4cfdf8c 100644 --- a/src/applications/config/controller/services/PhabricatorConfigDatabaseStatusController.php +++ b/src/applications/config/controller/services/PhabricatorConfigDatabaseStatusController.php @@ -837,7 +837,7 @@ private function getURI(array $properties) { $parts = array(); foreach ($properties as $key => $property) { - if (!strlen($property)) { + if (!phutil_nonempty_string($property)) { continue; } diff --git a/src/applications/config/controller/settings/PhabricatorConfigSettingsListController.php b/src/applications/config/controller/settings/PhabricatorConfigSettingsListController.php index 8513150134..0d34250bb8 100644 --- a/src/applications/config/controller/settings/PhabricatorConfigSettingsListController.php +++ b/src/applications/config/controller/settings/PhabricatorConfigSettingsListController.php @@ -7,7 +7,7 @@ public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $filter = $request->getURIData('filter'); - if (!strlen($filter)) { + if (!phutil_nonempty_string($filter)) { $filter = 'settings'; } diff --git a/src/applications/config/editor/PhabricatorConfigEditor.php b/src/applications/config/editor/PhabricatorConfigEditor.php index 7a8833c84e..4eb8838d3e 100644 --- a/src/applications/config/editor/PhabricatorConfigEditor.php +++ b/src/applications/config/editor/PhabricatorConfigEditor.php @@ -4,7 +4,7 @@ final class PhabricatorConfigEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorConfigApplication'; + return PhabricatorConfigApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/config/option/PhabricatorConfigOption.php b/src/applications/config/option/PhabricatorConfigOption.php index 6d7f88bdfb..ee79005d5d 100644 --- a/src/applications/config/option/PhabricatorConfigOption.php +++ b/src/applications/config/option/PhabricatorConfigOption.php @@ -77,7 +77,7 @@ public function getLockedMessage() { 'This configuration is locked and can not be edited from the web '. 'interface. Use %s in %s to edit it.', phutil_tag('tt', array(), './bin/config'), - phutil_tag('tt', array(), 'phabricator/')); + phutil_tag('tt', array(), PlatformSymbols::getPlatformServerPath())); } public function addExample($value, $description) { @@ -156,11 +156,22 @@ public function getSummary() { return $this->summary; } + /** + * Set the human Description of this Config + * + * @param string|null $description Description as raw Remarkup + * @return self + */ public function setDescription($description) { $this->description = $description; return $this; } + /** + * Get the human Description of this Config + * + * @return string|null Description as raw Remarkup + */ public function getDescription() { return $this->description; } @@ -205,7 +216,7 @@ public function setCustomData($data) { public function newDescriptionRemarkupView(PhabricatorUser $viewer) { $description = $this->getDescription(); - if (!strlen($description)) { + if (!phutil_nonempty_string($description)) { return null; } diff --git a/src/applications/config/option/PhabricatorCoreConfigOptions.php b/src/applications/config/option/PhabricatorCoreConfigOptions.php index b09c12e269..46001f2363 100644 --- a/src/applications/config/option/PhabricatorCoreConfigOptions.php +++ b/src/applications/config/option/PhabricatorCoreConfigOptions.php @@ -200,7 +200,7 @@ public function getOptions() { '$PATH')) ->setDescription( pht( - "Thhi software sometimes executes other binaries on the ". + "This software sometimes executes other binaries on the ". "server. An example of this is the `%s` command, used to ". "syntax-highlight code written in languages other than PHP. By ". "default, it is assumed that these binaries are in the %s of the ". diff --git a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php index cca8794055..7499c978d3 100644 --- a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php +++ b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php @@ -74,7 +74,7 @@ public function getOptions() { - If you use mailing lists, recipients may sometimes get duplicate mail. - Getting threading to work properly is easier, and threading settings - can be customzied by each user. + can be customized by each user. - "Reply All" will never send extra mail to other users involved in the thread. - Required if private reply-to addresses are configured. @@ -227,7 +227,7 @@ public function getOptions() { pht( 'Controls whether email for multiple recipients is sent by '. 'creating one message with everyone in the "To:" line, or '. - 'multiple messages that each have a single recipeint in the '. + 'multiple messages that each have a single recipient in the '. '"To:" line.')) ->setDescription($one_mail_per_recipient_desc), $this->newOption('metamta.can-send-as-user', 'bool', false) diff --git a/src/applications/config/phid/PhabricatorConfigConfigPHIDType.php b/src/applications/config/phid/PhabricatorConfigConfigPHIDType.php index 95c543427f..fb72a18257 100644 --- a/src/applications/config/phid/PhabricatorConfigConfigPHIDType.php +++ b/src/applications/config/phid/PhabricatorConfigConfigPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorConfigApplication'; + return PhabricatorConfigApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/config/query/PhabricatorConfigEntryQuery.php b/src/applications/config/query/PhabricatorConfigEntryQuery.php index f46fdb7d1e..c228c2b7cc 100644 --- a/src/applications/config/query/PhabricatorConfigEntryQuery.php +++ b/src/applications/config/query/PhabricatorConfigEntryQuery.php @@ -54,7 +54,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorConfigApplication'; + return PhabricatorConfigApplication::class; } } diff --git a/src/applications/config/query/PhabricatorConfigTransactionQuery.php b/src/applications/config/query/PhabricatorConfigTransactionQuery.php index 16ab2b47d6..c34f730410 100644 --- a/src/applications/config/query/PhabricatorConfigTransactionQuery.php +++ b/src/applications/config/query/PhabricatorConfigTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorConfigTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorConfigApplication::class; + } + } diff --git a/src/applications/config/view/PhabricatorSetupIssueView.php b/src/applications/config/view/PhabricatorSetupIssueView.php index 003725d5fc..9aff749bb2 100644 --- a/src/applications/config/view/PhabricatorSetupIssueView.php +++ b/src/applications/config/view/PhabricatorSetupIssueView.php @@ -73,20 +73,22 @@ public function render() { 'Install these %d PHP extension(s):', count($extensions)); $install_info = pht( - 'You can usually install a PHP extension using %s or %s. Common '. - 'package names are %s or %s. Try commands like these:', + 'You can usually install a PHP extension using %s, %s, or %s. A '. + 'common package name is %s. Try commands like these:', phutil_tag('tt', array(), 'apt-get'), + phutil_tag('tt', array(), 'dnf'), phutil_tag('tt', array(), 'yum'), - hsprintf('php-%s', pht('extname')), - hsprintf('php5-%s', pht('extname'))); + hsprintf('php-%s', pht('extname'))); // TODO: We should do a better job of detecting how to install extensions // on the current system. $install_commands = hsprintf( - "\$ sudo apt-get install php5-extname ". + "$sudo apt-get install php-extname ". "# Debian / Ubuntu\n". - "\$ sudo yum install php-extname ". - "# Red Hat / Derivatives"); + "$sudo dnf install php-extname ". + "# Red Hat / Derivatives\n". + "$sudo yum install php-extname ". + "# Older Red Hat versions"); $fallback_info = pht( "If those commands don't work, try Google. The process of installing ". @@ -284,7 +286,8 @@ private function renderPhabricatorConfig(array $configs, $related = false) { $update = array(); foreach ($configs as $key) { $update[] = hsprintf( - '$ ./bin/config set %s value', + '%s $./bin/config set %s value', + PlatformSymbols::getPlatformServerPath(), $key); } $update = phutil_tag('pre', array(), phutil_implode_html("\n", $update)); @@ -601,14 +604,14 @@ private function renderRelatedLinks(array $links) { } private function renderRestartLink() { - $doc_href = PhabricatorEnv::getDoclink('Restarting Phabricator'); + $doc_href = PhabricatorEnv::getDoclink('Restarting Phorge'); return phutil_tag( 'a', array( 'href' => $doc_href, 'target' => '_blank', ), - pht('Restarting Phabricator')); + pht('Restarting')); } } diff --git a/src/applications/conpherence/application/PhabricatorConpherenceApplication.php b/src/applications/conpherence/application/PhabricatorConpherenceApplication.php index a0c21bd33a..f3118be9bf 100644 --- a/src/applications/conpherence/application/PhabricatorConpherenceApplication.php +++ b/src/applications/conpherence/application/PhabricatorConpherenceApplication.php @@ -28,6 +28,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('Z'); + } + public function getRoutes() { return array( '/Z(?P[1-9]\d*)' diff --git a/src/applications/conpherence/controller/ConpherenceRoomPictureController.php b/src/applications/conpherence/controller/ConpherenceRoomPictureController.php index fbf3def9cd..5be8331610 100644 --- a/src/applications/conpherence/controller/ConpherenceRoomPictureController.php +++ b/src/applications/conpherence/controller/ConpherenceRoomPictureController.php @@ -24,6 +24,12 @@ public function handleRequest(AphrontRequest $request) { $monogram = $conpherence->getMonogram(); $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); @@ -56,9 +62,7 @@ public function handleRequest(AphrontRequest $request) { if (!$errors && !$is_default) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } else { $xform = PhabricatorFileTransform::getTransformByKey( PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); @@ -195,8 +199,7 @@ public function handleRequest(AphrontRequest $request) { ->setName('picture') ->setLabel(pht('Upload Picture')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton('/'.$monogram) diff --git a/src/applications/conpherence/controller/ConpherenceUpdateController.php b/src/applications/conpherence/controller/ConpherenceUpdateController.php index a792a5a4d4..6d838e72bc 100644 --- a/src/applications/conpherence/controller/ConpherenceUpdateController.php +++ b/src/applications/conpherence/controller/ConpherenceUpdateController.php @@ -328,7 +328,12 @@ private function loadAndRenderUpdates( ->executeOne(); $non_update = false; - $participant = $conpherence->getParticipant($user->getPHID()); + + // The User is always available. The Participant may not. See: + // User: it's you, lurking the Chat (maybe it's a public chat). + // Participant: it's you, if you are a Chat Member. + // https://we.phorge.it/T15497 + $participant = $conpherence->getParticipantIfExists($user->getPHID()); if ($need_transactions && $conpherence->getTransactions()) { $data = ConpherenceTransactionRenderer::renderTransactions( @@ -336,7 +341,7 @@ private function loadAndRenderUpdates( $conpherence); $key = PhabricatorConpherenceColumnMinimizeSetting::SETTINGKEY; $minimized = $user->getUserSetting($key); - if (!$minimized) { + if (!$minimized && $participant) { $participant->markUpToDate($conpherence); } } else if ($need_transactions) { diff --git a/src/applications/conpherence/editor/ConpherenceEditEngine.php b/src/applications/conpherence/editor/ConpherenceEditEngine.php index f5f850e637..7b1b7d1aa4 100644 --- a/src/applications/conpherence/editor/ConpherenceEditEngine.php +++ b/src/applications/conpherence/editor/ConpherenceEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorConpherenceApplication'; + return PhabricatorConpherenceApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/conpherence/editor/ConpherenceEditor.php b/src/applications/conpherence/editor/ConpherenceEditor.php index a1d6431d8c..fe5d0a60ca 100644 --- a/src/applications/conpherence/editor/ConpherenceEditor.php +++ b/src/applications/conpherence/editor/ConpherenceEditor.php @@ -6,7 +6,7 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { const ERROR_EMPTY_MESSAGE = 'error-empty-message'; public function getEditorApplicationClass() { - return 'PhabricatorConpherenceApplication'; + return PhabricatorConpherenceApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/conpherence/phid/PhabricatorConpherenceThreadPHIDType.php b/src/applications/conpherence/phid/PhabricatorConpherenceThreadPHIDType.php index 98f0d74797..d8273b6d6e 100644 --- a/src/applications/conpherence/phid/PhabricatorConpherenceThreadPHIDType.php +++ b/src/applications/conpherence/phid/PhabricatorConpherenceThreadPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorConpherenceApplication'; + return PhabricatorConpherenceApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/conpherence/query/ConpherenceThreadQuery.php b/src/applications/conpherence/query/ConpherenceThreadQuery.php index 99fd18878e..72986d0c22 100644 --- a/src/applications/conpherence/query/ConpherenceThreadQuery.php +++ b/src/applications/conpherence/query/ConpherenceThreadQuery.php @@ -135,7 +135,8 @@ protected function loadPage() { } protected function buildGroupClause(AphrontDatabaseConnection $conn_r) { - if ($this->participantPHIDs !== null || strlen($this->fulltext)) { + if ($this->participantPHIDs !== null || + phutil_nonempty_string($this->fulltext)) { return qsprintf($conn_r, 'GROUP BY thread.id'); } else { return $this->buildApplicationSearchGroupClause($conn_r); @@ -152,7 +153,7 @@ protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { id(new ConpherenceParticipant())->getTableName()); } - if (strlen($this->fulltext)) { + if (phutil_nonempty_string($this->fulltext)) { $joins[] = qsprintf( $conn, 'JOIN %T idx ON idx.threadPHID = thread.phid', @@ -234,7 +235,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $this->participantPHIDs); } - if (strlen($this->fulltext)) { + if (phutil_nonempty_string($this->fulltext)) { $where[] = qsprintf( $conn, 'MATCH(idx.corpus) AGAINST (%s IN BOOLEAN MODE)', @@ -334,7 +335,7 @@ private function loadTransactionsAndHandles(array $conpherences) { } public function getQueryApplicationClass() { - return 'PhabricatorConpherenceApplication'; + return PhabricatorConpherenceApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php b/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php index cbaf43b0a9..505d7c4a0e 100644 --- a/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php +++ b/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorConpherenceApplication'; + return PhabricatorConpherenceApplication::class; } public function newQuery() { @@ -106,7 +106,7 @@ protected function renderResultList( $engines = array(); $fulltext = $query->getParameter('fulltext'); - if (strlen($fulltext) && $conpherences) { + if (phutil_nonempty_string($fulltext) && $conpherences) { $context = $this->loadContextMessages($conpherences, $fulltext); $author_phids = array(); diff --git a/src/applications/conpherence/query/ConpherenceTransactionQuery.php b/src/applications/conpherence/query/ConpherenceTransactionQuery.php index d227217cb4..4e570d72ee 100644 --- a/src/applications/conpherence/query/ConpherenceTransactionQuery.php +++ b/src/applications/conpherence/query/ConpherenceTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new ConpherenceTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorConpherenceApplication::class; + } + } diff --git a/src/applications/conpherence/typeahead/ConpherenceThreadDatasource.php b/src/applications/conpherence/typeahead/ConpherenceThreadDatasource.php index a18b4a73ea..735769a0cc 100644 --- a/src/applications/conpherence/typeahead/ConpherenceThreadDatasource.php +++ b/src/applications/conpherence/typeahead/ConpherenceThreadDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorConpherenceApplication'; + return PhabricatorConpherenceApplication::class; } public function loadResults() { diff --git a/src/applications/console/controller/DarkConsoleController.php b/src/applications/console/controller/DarkConsoleController.php index 3a677920ee..3c4f82feba 100644 --- a/src/applications/console/controller/DarkConsoleController.php +++ b/src/applications/console/controller/DarkConsoleController.php @@ -25,8 +25,9 @@ public function handleRequest(AphrontRequest $request) { return $response; } + // This should be '0' when closed and '1' when opened $visible = $request->getStr('visible'); - if (strlen($visible)) { + if (phutil_nonempty_string($visible)) { $this->writeDarkConsoleSetting( PhabricatorDarkConsoleVisibleSetting::SETTINGKEY, (int)$visible); diff --git a/src/applications/console/core/DarkConsoleCore.php b/src/applications/console/core/DarkConsoleCore.php index 437d6f0be7..73065b900e 100644 --- a/src/applications/console/core/DarkConsoleCore.php +++ b/src/applications/console/core/DarkConsoleCore.php @@ -124,6 +124,9 @@ private function sanitizeForJSON($data) { } else if (is_resource($data)) { return ''; } else { + // This is very probably not a string in strict sense + $data = phutil_string_cast($data); + // Truncate huge strings. Since the data doesn't really matter much, // just truncate bytes to avoid PhutilUTF8StringTruncator overhead. $length = strlen($data); diff --git a/src/applications/console/plugin/errorlog/DarkConsoleErrorLogPluginAPI.php b/src/applications/console/plugin/errorlog/DarkConsoleErrorLogPluginAPI.php index bc276da0da..fb092ec763 100644 --- a/src/applications/console/plugin/errorlog/DarkConsoleErrorLogPluginAPI.php +++ b/src/applications/console/plugin/errorlog/DarkConsoleErrorLogPluginAPI.php @@ -44,6 +44,7 @@ public static function handleErrors($event, $value, $metadata) { 'trace' => $metadata['trace'], ); break; + case PhutilErrorHandler::DEPRECATED: case PhutilErrorHandler::ERROR: // $value is a simple string self::$errors[] = array( diff --git a/src/applications/countdown/application/PhabricatorCountdownApplication.php b/src/applications/countdown/application/PhabricatorCountdownApplication.php index b3b5a79da2..5f06e6ef9f 100644 --- a/src/applications/countdown/application/PhabricatorCountdownApplication.php +++ b/src/applications/countdown/application/PhabricatorCountdownApplication.php @@ -36,6 +36,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('C'); + } + public function getRoutes() { return array( '/C(?P[1-9]\d*)' => 'PhabricatorCountdownViewController', @@ -50,6 +54,10 @@ public function getRoutes() { protected function getCustomCapabilities() { return array( + PhabricatorCountdownCreateCapability::CAPABILITY => array( + 'default' => PhabricatorPolicies::POLICY_USER, + 'caption' => pht('Default create policy for countdowns.'), + ), PhabricatorCountdownDefaultViewCapability::CAPABILITY => array( 'caption' => pht('Default view policy for new countdowns.'), 'template' => PhabricatorCountdownCountdownPHIDType::TYPECONST, diff --git a/src/applications/countdown/capability/PhabricatorCountdownCreateCapability.php b/src/applications/countdown/capability/PhabricatorCountdownCreateCapability.php new file mode 100644 index 0000000000..057b6c22d9 --- /dev/null +++ b/src/applications/countdown/capability/PhabricatorCountdownCreateCapability.php @@ -0,0 +1,16 @@ +getURI(); } + protected function getCreateNewObjectPolicy() { + return $this->getApplication()->getPolicy( + PhabricatorCountdownCreateCapability::CAPABILITY); + } + protected function buildCustomEditFields($object) { $epoch_value = $object->getEpoch(); if ($epoch_value === null) { diff --git a/src/applications/countdown/editor/PhabricatorCountdownEditor.php b/src/applications/countdown/editor/PhabricatorCountdownEditor.php index 2102b3785b..1037320902 100644 --- a/src/applications/countdown/editor/PhabricatorCountdownEditor.php +++ b/src/applications/countdown/editor/PhabricatorCountdownEditor.php @@ -4,7 +4,7 @@ final class PhabricatorCountdownEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorCountdownApplication'; + return PhabricatorCountdownApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/countdown/phid/PhabricatorCountdownCountdownPHIDType.php b/src/applications/countdown/phid/PhabricatorCountdownCountdownPHIDType.php index 7e6278c8ea..f72ecb4808 100644 --- a/src/applications/countdown/phid/PhabricatorCountdownCountdownPHIDType.php +++ b/src/applications/countdown/phid/PhabricatorCountdownCountdownPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorCountdownApplication'; + return PhabricatorCountdownApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/countdown/query/PhabricatorCountdownQuery.php b/src/applications/countdown/query/PhabricatorCountdownQuery.php index 4a6df16e8e..51360c52de 100644 --- a/src/applications/countdown/query/PhabricatorCountdownQuery.php +++ b/src/applications/countdown/query/PhabricatorCountdownQuery.php @@ -67,7 +67,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorCountdownApplication'; + return PhabricatorCountdownApplication::class; } public function getBuiltinOrders() { diff --git a/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php b/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php index 2f9fe9c0e8..a0f93763c8 100644 --- a/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php +++ b/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorCountdownApplication'; + return PhabricatorCountdownApplication::class; } public function newQuery() { diff --git a/src/applications/countdown/query/PhabricatorCountdownTransactionQuery.php b/src/applications/countdown/query/PhabricatorCountdownTransactionQuery.php index 11c10a72eb..7a840ac4ab 100644 --- a/src/applications/countdown/query/PhabricatorCountdownTransactionQuery.php +++ b/src/applications/countdown/query/PhabricatorCountdownTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorCountdownTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorCountdownApplication::class; + } + } diff --git a/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php b/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php index f4c7ba60c9..253a3d5190 100644 --- a/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php @@ -171,7 +171,8 @@ private function buildPropertyListView(PhabricatorDaemonLog $daemon) { phutil_tag( 'tt', array(), - "phabricator/ $ ./bin/phd log --id {$id}")); + PlatformSymbols::getPlatformServerPath(). + " $ ./bin/phd log --id {$id}")); return $view; diff --git a/src/applications/daemon/query/PhabricatorDaemonLogQuery.php b/src/applications/daemon/query/PhabricatorDaemonLogQuery.php index 2c5b6baa3b..a604ef4939 100644 --- a/src/applications/daemon/query/PhabricatorDaemonLogQuery.php +++ b/src/applications/daemon/query/PhabricatorDaemonLogQuery.php @@ -189,7 +189,7 @@ private function getStatusConstants() { } public function getQueryApplicationClass() { - return 'PhabricatorDaemonsApplication'; + return PhabricatorDaemonsApplication::class; } } diff --git a/src/applications/dashboard/application/PhabricatorDashboardApplication.php b/src/applications/dashboard/application/PhabricatorDashboardApplication.php index 9a09283fe3..aeb2f43195 100644 --- a/src/applications/dashboard/application/PhabricatorDashboardApplication.php +++ b/src/applications/dashboard/application/PhabricatorDashboardApplication.php @@ -30,6 +30,10 @@ public function getApplicationOrder() { return 0.160; } + public function getMonograms() { + return array('W'); + } + public function getRoutes() { $menu_rules = $this->getProfileMenuRouting( 'PhabricatorDashboardPortalViewController'); @@ -83,4 +87,12 @@ public function getRemarkupRules() { ); } + protected function getCustomCapabilities() { + return array( + PhabricatorDashboardCreateCapability::CAPABILITY => array( + 'default' => PhabricatorPolicies::POLICY_USER, + 'caption' => pht('Default create policy for Dashboards.'), + ), + ); + } } diff --git a/src/applications/dashboard/controller/PhabricatorDashboardQueryPanelInstallController.php b/src/applications/dashboard/controller/PhabricatorDashboardQueryPanelInstallController.php index a229fcb41c..845ce9ee06 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardQueryPanelInstallController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardQueryPanelInstallController.php @@ -13,12 +13,12 @@ public function handleRequest(AphrontRequest $request) { $e_name = true; $v_engine = $request->getStr('engine'); - if (!strlen($v_engine)) { + if (!phutil_nonempty_string($v_engine)) { $v_engine = $request->getURIData('engineKey'); } $v_query = $request->getStr('query'); - if (!strlen($v_query)) { + if (!phutil_nonempty_string($v_query)) { $v_query = $request->getURIData('queryKey'); } diff --git a/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php b/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php index 86a3ff5805..4f3937e3ae 100644 --- a/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php +++ b/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php @@ -32,7 +32,7 @@ public function handleRequest(AphrontRequest $request) { $panel_ref = null; $panel_key = $request->getStr('panelKey'); - if (strlen($panel_key)) { + if ($panel_key !== null && strlen($panel_key)) { $panel_ref = $ref_list->getPanelRef($panel_key); if (!$panel_ref) { return new Aphront404Response(); @@ -42,7 +42,7 @@ public function handleRequest(AphrontRequest $request) { } $column_key = $request->getStr('columnKey'); - if (strlen($column_key)) { + if (phutil_nonempty_string($column_key)) { $columns = $ref_list->getColumns(); if (!isset($columns[$column_key])) { return new Aphront404Response(); @@ -52,7 +52,7 @@ public function handleRequest(AphrontRequest $request) { $after_ref = null; $after_key = $request->getStr('afterKey'); - if (strlen($after_key)) { + if (phutil_nonempty_string($after_key)) { $after_ref = $ref_list->getPanelRef($after_key); if (!$after_ref) { return new Aphront404Response(); diff --git a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelEditController.php b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelEditController.php index 4ab76d18b5..f40c0a05a1 100644 --- a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelEditController.php +++ b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelEditController.php @@ -15,7 +15,7 @@ public function handleRequest(AphrontRequest $request) { // editing. $context_phid = $request->getStr('contextPHID'); - if (strlen($context_phid)) { + if (phutil_nonempty_string($context_phid)) { $context = id(new PhabricatorObjectQuery()) ->setViewer($viewer) ->withPHIDs(array($context_phid)) diff --git a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php index 2b36d3f8a2..7ea0345ceb 100644 --- a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php +++ b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php @@ -41,12 +41,12 @@ public function handleRequest(AphrontRequest $request) { $op = $request->getURIData('op'); $after = $request->getStr('after'); - if (!strlen($after)) { + if ($after === '') { $after = null; } $target = $request->getStr('target'); - if (!strlen($target)) { + if ($target === '') { $target = null; } @@ -327,7 +327,7 @@ private function handleMoveOperation( 'This is already the last tab. It can not move any farther to '. 'the right.')) ->addCancelButton($cancel_uri); - } else if ((string)head_key($old_config) === $target) { + } else if (!$is_next && (string)head_key($old_config) === $target) { return $this->newDialog() ->setTitle(pht('Impossible!')) ->appendParagraph( diff --git a/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php b/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php index 06b0a3a6fb..cae823f868 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function newEditableObject() { @@ -66,6 +66,11 @@ protected function getObjectViewURI($object) { return $object->getURI(); } + protected function getCreateNewObjectPolicy() { + return $this->getApplication()->getPolicy( + PhabricatorDashboardCreateCapability::CAPABILITY); + } + protected function buildCustomEditFields($object) { $layout_options = PhabricatorDashboardLayoutMode::getLayoutModeMap(); diff --git a/src/applications/dashboard/editor/PhabricatorDashboardPanelEditEngine.php b/src/applications/dashboard/editor/PhabricatorDashboardPanelEditEngine.php index 129f28b347..92c7dbdf1a 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardPanelEditEngine.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardPanelEditEngine.php @@ -57,7 +57,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function newEditableObject() { diff --git a/src/applications/dashboard/editor/PhabricatorDashboardPanelTransactionEditor.php b/src/applications/dashboard/editor/PhabricatorDashboardPanelTransactionEditor.php index ea03c1ac7d..6e6d2b618b 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardPanelTransactionEditor.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardPanelTransactionEditor.php @@ -4,7 +4,7 @@ final class PhabricatorDashboardPanelTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/dashboard/editor/PhabricatorDashboardPortalEditEngine.php b/src/applications/dashboard/editor/PhabricatorDashboardPortalEditEngine.php index 9945ea9d28..17880b5501 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardPortalEditEngine.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardPortalEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function newEditableObject() { diff --git a/src/applications/dashboard/editor/PhabricatorDashboardPortalEditor.php b/src/applications/dashboard/editor/PhabricatorDashboardPortalEditor.php index 2c8a3ccfac..ee2a2307ba 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardPortalEditor.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardPortalEditor.php @@ -4,7 +4,7 @@ final class PhabricatorDashboardPortalEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php b/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php index 4a84577467..8964beab70 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php @@ -4,7 +4,7 @@ final class PhabricatorDashboardTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php index 52e8cc70d5..3b9c3ad192 100644 --- a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php +++ b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php @@ -155,6 +155,7 @@ public function renderPanel() { return $this->renderNormalPanel(); } catch (Exception $ex) { + phlog($ex); return $this->renderErrorPanel( $panel->getName(), pht( @@ -273,6 +274,11 @@ private function renderPanelDiv( ->addClass('dashboard-box') ->addSigil('dashboard-panel'); + // Allow to style Archived Panels differently. + if ($panel && $panel->getIsArchived()) { + $box->addClass('dashboard-panel-disabled'); + } + if ($this->getMovable()) { $box->addSigil('panel-movable'); } @@ -302,6 +308,16 @@ private function renderPanelHeader() { $header = id(new PHUIHeaderView()) ->setHeader($header_text); $header = $this->addPanelHeaderActions($header); + + // If the Panel is Archived, show in edit mode as such. + if ($panel && $panel->getIsArchived()) { + $header->setSubheader( + id(new PHUITagView()) + ->setType(PHUITagView::TYPE_SHADE) + ->setColor(PHUITagView::COLOR_RED) + ->setIcon('fa-ban') + ->setName(pht('Archived'))); + } break; case self::HEADER_MODE_NORMAL: default: diff --git a/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php index 9b63fcac55..935e85f8bb 100644 --- a/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php +++ b/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php @@ -71,6 +71,13 @@ public function renderDashboard() { foreach ($column->getPanelRefs() as $panel_ref) { $panel_phid = $panel_ref->getPanelPHID(); + $panel = idx($panels, $panel_phid); + + // Do not render Archived panels in view mode. + if ($panel && $panel->getIsArchived() && !$is_editable) { + continue; + } + $panel_engine = id(new PhabricatorDashboardPanelRenderingEngine()) ->setViewer($viewer) ->setEnableAsyncRendering(true) @@ -83,7 +90,6 @@ public function renderDashboard() { ->setMovable(true) ->setPanelHandle($handles[$panel_phid]); - $panel = idx($panels, $panel_phid); if ($panel) { $panel_engine->setPanel($panel); } diff --git a/src/applications/dashboard/install/PhabricatorDashboardObjectInstallWorkflow.php b/src/applications/dashboard/install/PhabricatorDashboardObjectInstallWorkflow.php index eb1be21954..27e1335f6e 100644 --- a/src/applications/dashboard/install/PhabricatorDashboardObjectInstallWorkflow.php +++ b/src/applications/dashboard/install/PhabricatorDashboardObjectInstallWorkflow.php @@ -17,16 +17,16 @@ public function handleRequest(AphrontRequest $request) { $target_identifier = head($target_tokens); } - if (!strlen($target_identifier)) { + if (!phutil_nonempty_string($target_identifier)) { $target_identifier = $request->getStr('target'); } - if (!strlen($target_identifier)) { + if (!phutil_nonempty_string($target_identifier)) { $target_identifier = $this->getMode(); } $target = null; - if (strlen($target_identifier)) { + if (phutil_nonempty_string($target_identifier)) { $targets = array(); if (ctype_digit($target_identifier)) { @@ -74,7 +74,7 @@ public function handleRequest(AphrontRequest $request) { } $errors = array(); - if (strlen($target_identifier)) { + if (phutil_nonempty_string($target_identifier)) { if (!$target) { $errors[] = pht('Choose a valid object.'); } else if (!$can_edit) { diff --git a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php index 91c6d17833..6b4f2fe5d6 100644 --- a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php +++ b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php @@ -85,10 +85,12 @@ public function renderPanelContent( $rename_uri = id(new PhutilURI($rename_uri)) ->replaceQueryParam('contextPHID', $context_phid); - $selected = 0; - $key_list = array_keys($config); + // In the future we may persist which panel was selected. + // At the moment we have always selected the first one. + $selected = (string)head($key_list); + $next_keys = array(); $prev_keys = array(); for ($ii = 0; $ii < count($key_list); $ii++) { @@ -100,7 +102,7 @@ public function renderPanelContent( $panel_id = idx($tab_spec, 'panelID'); $subpanel = idx($panels, $panel_id); - $name = idx($tab_spec, 'name'); + $name = coalesce(idx($tab_spec, 'name'), ''); if (!strlen($name)) { if ($subpanel) { $name = $subpanel->getName(); @@ -111,9 +113,12 @@ public function renderPanelContent( $name = pht('Unnamed Tab'); } + // The $idx can be something like "0", "1" or "asdasd98". + $is_selected = (string)$idx === $selected; + $tab_view = id(new PHUIListItemView()) ->setHref('#') - ->setSelected((string)$idx === (string)$selected) + ->setSelected($is_selected) ->addSigil('dashboard-tab-panel-tab') ->setMetadata(array('panelKey' => $idx)) ->setName($name); @@ -179,7 +184,7 @@ public function renderPanelContent( ->setIcon('fa-chevron-left') ->setHref($prev_uri) ->setWorkflow(true) - ->setDisabled(($prev_key === null) || !$can_edit)); + ->setDisabled($prev_key === null)); $dropdown_menu->addAction( id(new PhabricatorActionView()) @@ -187,7 +192,7 @@ public function renderPanelContent( ->setIcon('fa-chevron-right') ->setHref($next_uri) ->setWorkflow(true) - ->setDisabled(($next_key === null) || !$can_edit)); + ->setDisabled($next_key === null)); $dropdown_menu->addAction( id(new PhabricatorActionView()) @@ -280,13 +285,16 @@ public function renderPanelContent( $panel_content = pht('(Invalid Panel)'); } + // Note that $idx can be something like "0", "1" or "asdasd98". + $is_selected = (string)$idx === $selected; + $content_id = celerity_generate_unique_node_id(); $content[] = phutil_tag( 'div', array( 'id' => $content_id, - 'style' => ($idx == $selected) ? null : 'display: none', + 'style' => $is_selected ? null : 'display: none', ), $panel_content); diff --git a/src/applications/dashboard/phid/PhabricatorDashboardDashboardPHIDType.php b/src/applications/dashboard/phid/PhabricatorDashboardDashboardPHIDType.php index 1450e2aa68..0aae485b58 100644 --- a/src/applications/dashboard/phid/PhabricatorDashboardDashboardPHIDType.php +++ b/src/applications/dashboard/phid/PhabricatorDashboardDashboardPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/dashboard/phid/PhabricatorDashboardPanelPHIDType.php b/src/applications/dashboard/phid/PhabricatorDashboardPanelPHIDType.php index d84db72a25..1019740320 100644 --- a/src/applications/dashboard/phid/PhabricatorDashboardPanelPHIDType.php +++ b/src/applications/dashboard/phid/PhabricatorDashboardPanelPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/dashboard/phid/PhabricatorDashboardPortalPHIDType.php b/src/applications/dashboard/phid/PhabricatorDashboardPortalPHIDType.php index 378748d96b..3faad72924 100644 --- a/src/applications/dashboard/phid/PhabricatorDashboardPortalPHIDType.php +++ b/src/applications/dashboard/phid/PhabricatorDashboardPortalPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php index c67b756262..7ec76898b9 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php @@ -19,6 +19,13 @@ public function withPHIDs(array $phids) { return $this; } + /** + * Whether to get only the Archived (`true`), only the not + * Archived (`false`) or all (`null`). Default to `null` (no filter). + * + * @param null|bool $archived + * @return self + */ public function withArchived($archived) { $this->archived = $archived; return $this; @@ -88,7 +95,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php b/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php index dc50fa5d66..be59345aaa 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function newQuery() { diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelTransactionQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPanelTransactionQuery.php index 9a192485c1..ad77844189 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPanelTransactionQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorDashboardPanelTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDashboardApplication::class; + } + } diff --git a/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php index 418262c745..9dd0da68e8 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php @@ -54,7 +54,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/dashboard/query/PhabricatorDashboardPortalSearchEngine.php b/src/applications/dashboard/query/PhabricatorDashboardPortalSearchEngine.php index c1633f2e97..b5ce3068b7 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPortalSearchEngine.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPortalSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function newQuery() { diff --git a/src/applications/dashboard/query/PhabricatorDashboardPortalTransactionQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPortalTransactionQuery.php index f4dff94088..6daf2f8869 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPortalTransactionQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPortalTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorDashboardPortalTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDashboardApplication::class; + } + } diff --git a/src/applications/dashboard/query/PhabricatorDashboardQuery.php b/src/applications/dashboard/query/PhabricatorDashboardQuery.php index 954a565ac6..3078140e24 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardQuery.php @@ -89,7 +89,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php b/src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php index ea3f69faab..c02778c77d 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php +++ b/src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function newQuery() { diff --git a/src/applications/dashboard/query/PhabricatorDashboardTransactionQuery.php b/src/applications/dashboard/query/PhabricatorDashboardTransactionQuery.php index e445f4a13e..0917ae0f9e 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardTransactionQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorDashboardTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDashboardApplication::class; + } + } diff --git a/src/applications/dashboard/typeahead/PhabricatorDashboardDatasource.php b/src/applications/dashboard/typeahead/PhabricatorDashboardDatasource.php index ff3376bdf6..cc43c613c2 100644 --- a/src/applications/dashboard/typeahead/PhabricatorDashboardDatasource.php +++ b/src/applications/dashboard/typeahead/PhabricatorDashboardDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function loadResults() { diff --git a/src/applications/dashboard/typeahead/PhabricatorDashboardPanelDatasource.php b/src/applications/dashboard/typeahead/PhabricatorDashboardPanelDatasource.php index 9ad4901bbf..b6621480bc 100644 --- a/src/applications/dashboard/typeahead/PhabricatorDashboardPanelDatasource.php +++ b/src/applications/dashboard/typeahead/PhabricatorDashboardPanelDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function loadResults() { diff --git a/src/applications/dashboard/typeahead/PhabricatorDashboardPortalDatasource.php b/src/applications/dashboard/typeahead/PhabricatorDashboardPortalDatasource.php index 008bb542ab..2d2e17b6b1 100644 --- a/src/applications/dashboard/typeahead/PhabricatorDashboardPortalDatasource.php +++ b/src/applications/dashboard/typeahead/PhabricatorDashboardPortalDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDashboardApplication'; + return PhabricatorDashboardApplication::class; } public function loadResults() { diff --git a/src/applications/differential/application/PhabricatorDifferentialApplication.php b/src/applications/differential/application/PhabricatorDifferentialApplication.php index 5980f738b0..16b3193dac 100644 --- a/src/applications/differential/application/PhabricatorDifferentialApplication.php +++ b/src/applications/differential/application/PhabricatorDifferentialApplication.php @@ -42,6 +42,10 @@ public function getOverview() { 'engineers to review, discuss and approve changes to software.'); } + public function getMonograms() { + return array('D'); + } + public function getRoutes() { return array( '/D(?P[1-9]\d*)' => array( diff --git a/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php index ae55135e92..cb3eaf537d 100644 --- a/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php @@ -71,12 +71,14 @@ protected function execute(ConduitAPIRequest $request) { $xactions = array( id(new DifferentialDiffTransaction()) ->setTransactionType(DifferentialDiffTransaction::TYPE_DIFF_CREATE) + ->setIsCreateTransaction(true) ->setNewValue($diff_data_dict), ); if ($request->getValue('viewPolicy')) { $xactions[] = id(new DifferentialDiffTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) + ->setIsCreateTransaction(true) ->setNewValue($request->getValue('viewPolicy')); } diff --git a/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php index 51225023ff..7f659ab729 100644 --- a/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php @@ -56,7 +56,7 @@ protected function execute(ConduitAPIRequest $request) { // show "Field:" templates for some fields even if they are empty. $edit_mode = $request->getValue('edit'); - $is_any_edit = (bool)strlen($edit_mode); + $is_any_edit = (bool)$edit_mode; $is_create = ($edit_mode == 'create'); $field_list = DifferentialCommitMessageField::newEnabledFields($viewer); @@ -115,7 +115,7 @@ protected function execute(ConduitAPIRequest $request) { $is_title = ($field_key == $key_title); - if (!strlen($value)) { + if ($value === null || $value === '') { if ($is_template) { $commit_message[] = $label.': '; } diff --git a/src/applications/differential/controller/DifferentialChangesetViewController.php b/src/applications/differential/controller/DifferentialChangesetViewController.php index 0e263d013c..c4722e8d37 100644 --- a/src/applications/differential/controller/DifferentialChangesetViewController.php +++ b/src/applications/differential/controller/DifferentialChangesetViewController.php @@ -451,10 +451,9 @@ private function loadCoverage(DifferentialChangeset $changeset) { continue; } $coverage_data = idx($test_coverage, $changeset->getFileName()); - if (!strlen($coverage_data)) { - continue; + if (phutil_nonempty_string($coverage_data)) { + $coverage[] = $coverage_data; } - $coverage[] = $coverage_data; } if (!$coverage) { diff --git a/src/applications/differential/controller/DifferentialDiffViewController.php b/src/applications/differential/controller/DifferentialDiffViewController.php index e56eb27d99..ff6623efe3 100644 --- a/src/applications/differential/controller/DifferentialDiffViewController.php +++ b/src/applications/differential/controller/DifferentialDiffViewController.php @@ -197,7 +197,6 @@ private function loadSelectableRevisions( if (empty($revisions[$selected_id])) { $selected = id(new DifferentialRevisionQuery()) ->setViewer($viewer) - ->withAuthors(array($viewer->getPHID())) ->withIDs(array($selected_id)) ->requireCapabilities( array( diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index c4ec6acece..f88ea1b32c 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -1090,20 +1090,13 @@ private function buildRawDiffResponse( $request_uri = $this->getRequest()->getRequestURI(); - // this ends up being something like - // D123.diff - // or the verbose - // D123.vs123.id123.highlightjs.diff - // lame but nice to include these options - $file_name = ltrim($request_uri->getPath(), '/').'.'; - foreach ($request_uri->getQueryParamsAsPairList() as $pair) { - list($key, $value) = $pair; - if ($key == 'download') { - continue; - } - $file_name .= $key.$value.'.'; - } - $file_name .= 'diff'; + // Filename ends up being something like D123.1692295858.diff + // This discards some options in the query string that may affect the diff + // response, but is intentional to avoid spammy titles from bot requests. + $timestamp = + PhabricatorTime::getNow() + + phutil_units('24 hours in seconds'); + $file_name = ltrim($request_uri->getPath(), '/').'.'.$timestamp.'.diff'; $iterator = new ArrayIterator(array($raw_diff)); diff --git a/src/applications/differential/customfield/DifferentialBranchField.php b/src/applications/differential/customfield/DifferentialBranchField.php index 2387e3cd3f..9d5d8f81a9 100644 --- a/src/applications/differential/customfield/DifferentialBranchField.php +++ b/src/applications/differential/customfield/DifferentialBranchField.php @@ -36,8 +36,8 @@ public function renderDiffPropertyViewValue(DifferentialDiff $diff) { } private function getBranchDescription(DifferentialDiff $diff) { - $branch = $diff->getBranch(); - $bookmark = $diff->getBookmark(); + $branch = coalesce($diff->getBranch(), ''); + $bookmark = coalesce($diff->getBookmark(), ''); if (strlen($branch) && strlen($bookmark)) { return pht('%s (bookmark) on %s (branch)', $bookmark, $branch); @@ -45,7 +45,7 @@ private function getBranchDescription(DifferentialDiff $diff) { return pht('%s (bookmark)', $bookmark); } else if (strlen($branch)) { $onto = $diff->loadTargetBranch(); - if (strlen($onto) && ($onto !== $branch)) { + if (phutil_nonempty_string($onto) && ($onto !== $branch)) { return pht( '%s (branched from %s)', $branch, diff --git a/src/applications/differential/customfield/__tests__/DifferentialBranchFieldTestCase.php b/src/applications/differential/customfield/__tests__/DifferentialBranchFieldTestCase.php new file mode 100644 index 0000000000..e96b4bd245 --- /dev/null +++ b/src/applications/differential/customfield/__tests__/DifferentialBranchFieldTestCase.php @@ -0,0 +1,39 @@ + true, + ); + } + + private function getTestDiff() { + $parser = new ArcanistDiffParser(); + $raw_diff = <<parseDiff($raw_diff)); + } + + public function testRenderDiffPropertyViewValue() { + $test_object = new DifferentialBranchField(); + $diff = $this->getTestDiff(); + $diff->setBranch('test'); + $this->assertEqual('test', + $test_object->renderDiffPropertyViewValue($diff)); + } +} diff --git a/src/applications/differential/editor/DifferentialDiffEditor.php b/src/applications/differential/editor/DifferentialDiffEditor.php index e78e08d808..eaaeee60fd 100644 --- a/src/applications/differential/editor/DifferentialDiffEditor.php +++ b/src/applications/differential/editor/DifferentialDiffEditor.php @@ -12,7 +12,7 @@ public function setLookupRepository($bool) { } public function getEditorApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/differential/editor/DifferentialRevisionEditEngine.php b/src/applications/differential/editor/DifferentialRevisionEditEngine.php index 74fee82219..a0d0569069 100644 --- a/src/applications/differential/editor/DifferentialRevisionEditEngine.php +++ b/src/applications/differential/editor/DifferentialRevisionEditEngine.php @@ -24,7 +24,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function isEngineConfigurable() { @@ -189,7 +189,7 @@ protected function buildCustomEditFields($object) { // Don't show the "Author" field when creating a revision using the web // workflow, since it adds more noise than signal to this workflow. - if ($this->getIsCreate()) { + if ($is_create) { $author_field->setIsHidden(true); } @@ -239,6 +239,12 @@ protected function buildCustomEditFields($object) { ->setConduitTypeDescription(pht('New reviewers.')) ->setValue($object->getReviewerPHIDsForEdit()); + // Prefill Repository for example when coming from "Attach To". + $repository_phid = $object->getRepositoryPHID(); + if ($is_create && !$repository_phid && $diff) { + $repository_phid = $diff->getRepositoryPHID(); + } + $fields[] = id(new PhabricatorDatasourceEditField()) ->setKey('repositoryPHID') ->setLabel(pht('Repository')) @@ -248,7 +254,7 @@ protected function buildCustomEditFields($object) { ->setDescription(pht('The repository the revision belongs to.')) ->setConduitDescription(pht('Change the repository for this revision.')) ->setConduitTypeDescription(pht('New repository.')) - ->setSingleValue($object->getRepositoryPHID()); + ->setSingleValue($repository_phid); // This is a little flimsy, but allows "Maniphest Tasks: ..." to continue // working properly in commit messages until we fully sort out T5873. @@ -307,7 +313,7 @@ protected function buildCustomEditFields($object) { pht('Hold as Draft')) ->setTransactionType( DifferentialRevisionHoldDraftTransaction::TRANSACTIONTYPE) - ->setDescription(pht('Hold revision as as draft.')) + ->setDescription(pht('Hold revision as draft.')) ->setConduitDescription( pht( 'Change autosubmission from draft state after builds finish.')) diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php index 345fdff72a..9ceb493b5f 100644 --- a/src/applications/differential/editor/DifferentialTransactionEditor.php +++ b/src/applications/differential/editor/DifferentialTransactionEditor.php @@ -15,7 +15,7 @@ final class DifferentialTransactionEditor private $ownersChangesets; public function getEditorApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getEditorObjectsDescription() { @@ -218,7 +218,7 @@ protected function expandTransaction( // No "$", to allow for branches like T123_demo. $match = null; - if (preg_match('/^T(\d+)/i', $branch, $match)) { + if ($branch !== null && preg_match('/^T(\d+)/i', $branch, $match)) { $task_id = $match[1]; $tasks = id(new ManiphestTaskQuery()) ->setViewer($this->getActor()) diff --git a/src/applications/differential/field/DifferentialCommitMessageField.php b/src/applications/differential/field/DifferentialCommitMessageField.php index ebe10bcf88..55c5ce0dad 100644 --- a/src/applications/differential/field/DifferentialCommitMessageField.php +++ b/src/applications/differential/field/DifferentialCommitMessageField.php @@ -60,7 +60,7 @@ public function readFieldValueFromObject(DifferentialRevision $revision) { } public function renderFieldValue($value) { - if (!strlen($value)) { + if (!phutil_nonempty_string($value)) { return null; } diff --git a/src/applications/differential/field/DifferentialRevisionIDCommitMessageField.php b/src/applications/differential/field/DifferentialRevisionIDCommitMessageField.php index ac8ba4ebd4..b39145e9df 100644 --- a/src/applications/differential/field/DifferentialRevisionIDCommitMessageField.php +++ b/src/applications/differential/field/DifferentialRevisionIDCommitMessageField.php @@ -72,7 +72,7 @@ public function readFieldValueFromConduit($value) { } public function renderFieldValue($value) { - if (!strlen($value)) { + if (!phutil_nonempty_string($value)) { return null; } diff --git a/src/applications/differential/field/__tests__/DifferentialCommitMessageFieldTestCase.php b/src/applications/differential/field/__tests__/DifferentialCommitMessageFieldTestCase.php index 41fd2992f4..bd2fb6f29d 100644 --- a/src/applications/differential/field/__tests__/DifferentialCommitMessageFieldTestCase.php +++ b/src/applications/differential/field/__tests__/DifferentialCommitMessageFieldTestCase.php @@ -28,4 +28,21 @@ public function testRevisionCommitMessageFieldParsing() { unset($env); } + public function testRenderFieldValue() { + $test_object = new DifferentialRevertPlanCommitMessageField(); + $this->assertEqual('foo', $test_object->renderFieldValue('foo'), + 'Normal strings should be rendered unaltered'); + + $this->assertEqual(null, $test_object->renderFieldValue(''), + 'Empty strings should be returned as null'); + + $this->assertEqual(null, $test_object->renderFieldValue(null), + 'null values strings should be returned as null'); + + $test_object = new DifferentialRevisionIDCommitMessageField(); + $expected = 'http://phabricator.example.com/D123'; + $this->assertEqual($expected, $test_object->renderFieldValue('123')); + $this->assertEqual(null, $test_object->renderFieldValue(null)); + } + } diff --git a/src/applications/differential/herald/HeraldDifferentialDiffAdapter.php b/src/applications/differential/herald/HeraldDifferentialDiffAdapter.php index 966b306580..767cef9553 100644 --- a/src/applications/differential/herald/HeraldDifferentialDiffAdapter.php +++ b/src/applications/differential/herald/HeraldDifferentialDiffAdapter.php @@ -3,7 +3,7 @@ final class HeraldDifferentialDiffAdapter extends HeraldDifferentialAdapter { public function getAdapterApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function initializeNewAdapter() { diff --git a/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php b/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php index 858a42dbc9..1ac6a33c28 100644 --- a/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php +++ b/src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php @@ -13,7 +13,7 @@ final class HeraldDifferentialRevisionAdapter private $buildRequests = array(); public function getAdapterApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function newObject() { diff --git a/src/applications/differential/phid/DifferentialChangesetPHIDType.php b/src/applications/differential/phid/DifferentialChangesetPHIDType.php index c558c0a6a2..357d787cdf 100644 --- a/src/applications/differential/phid/DifferentialChangesetPHIDType.php +++ b/src/applications/differential/phid/DifferentialChangesetPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/differential/phid/DifferentialDiffPHIDType.php b/src/applications/differential/phid/DifferentialDiffPHIDType.php index 746da368c7..494f87f598 100644 --- a/src/applications/differential/phid/DifferentialDiffPHIDType.php +++ b/src/applications/differential/phid/DifferentialDiffPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/differential/phid/DifferentialRevisionPHIDType.php b/src/applications/differential/phid/DifferentialRevisionPHIDType.php index a7d3c9f4a7..b54d4a624f 100644 --- a/src/applications/differential/phid/DifferentialRevisionPHIDType.php +++ b/src/applications/differential/phid/DifferentialRevisionPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/differential/query/DifferentialChangesetQuery.php b/src/applications/differential/query/DifferentialChangesetQuery.php index ee29aa1cf8..99176c28d4 100644 --- a/src/applications/differential/query/DifferentialChangesetQuery.php +++ b/src/applications/differential/query/DifferentialChangesetQuery.php @@ -172,7 +172,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } } diff --git a/src/applications/differential/query/DifferentialChangesetSearchEngine.php b/src/applications/differential/query/DifferentialChangesetSearchEngine.php index b6279ec339..f3235443e9 100644 --- a/src/applications/differential/query/DifferentialChangesetSearchEngine.php +++ b/src/applications/differential/query/DifferentialChangesetSearchEngine.php @@ -19,7 +19,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/differential/query/DifferentialDiffQuery.php b/src/applications/differential/query/DifferentialDiffQuery.php index 04019df1e0..552a467898 100644 --- a/src/applications/differential/query/DifferentialDiffQuery.php +++ b/src/applications/differential/query/DifferentialDiffQuery.php @@ -185,7 +185,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } } diff --git a/src/applications/differential/query/DifferentialDiffSearchEngine.php b/src/applications/differential/query/DifferentialDiffSearchEngine.php index 89feeb5c3e..31393d4681 100644 --- a/src/applications/differential/query/DifferentialDiffSearchEngine.php +++ b/src/applications/differential/query/DifferentialDiffSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function newQuery() { diff --git a/src/applications/differential/query/DifferentialDiffTransactionQuery.php b/src/applications/differential/query/DifferentialDiffTransactionQuery.php index c9da0bab61..e7a245f4f2 100644 --- a/src/applications/differential/query/DifferentialDiffTransactionQuery.php +++ b/src/applications/differential/query/DifferentialDiffTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new DifferentialDiffTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDifferentialApplication::class; + } + } diff --git a/src/applications/differential/query/DifferentialHunkQuery.php b/src/applications/differential/query/DifferentialHunkQuery.php index 21787f04e5..3b07676aa8 100644 --- a/src/applications/differential/query/DifferentialHunkQuery.php +++ b/src/applications/differential/query/DifferentialHunkQuery.php @@ -78,7 +78,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function getDefaultOrderVector() { diff --git a/src/applications/differential/query/DifferentialRevisionQuery.php b/src/applications/differential/query/DifferentialRevisionQuery.php index 75d3e052e4..d6b249cac5 100644 --- a/src/applications/differential/query/DifferentialRevisionQuery.php +++ b/src/applications/differential/query/DifferentialRevisionQuery.php @@ -1081,7 +1081,7 @@ private function loadReviewerAuthority( } public function getQueryApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/differential/query/DifferentialRevisionSearchEngine.php b/src/applications/differential/query/DifferentialRevisionSearchEngine.php index 1ec5265630..b69b764bb8 100644 --- a/src/applications/differential/query/DifferentialRevisionSearchEngine.php +++ b/src/applications/differential/query/DifferentialRevisionSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } protected function newResultBuckets() { diff --git a/src/applications/differential/query/DifferentialTransactionQuery.php b/src/applications/differential/query/DifferentialTransactionQuery.php index d413782301..b7cf395f95 100644 --- a/src/applications/differential/query/DifferentialTransactionQuery.php +++ b/src/applications/differential/query/DifferentialTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new DifferentialTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDifferentialApplication::class; + } + } diff --git a/src/applications/differential/query/DifferentialViewStateQuery.php b/src/applications/differential/query/DifferentialViewStateQuery.php index 604a6de1db..19104748d3 100644 --- a/src/applications/differential/query/DifferentialViewStateQuery.php +++ b/src/applications/differential/query/DifferentialViewStateQuery.php @@ -54,7 +54,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } } diff --git a/src/applications/differential/storage/DifferentialChangeset.php b/src/applications/differential/storage/DifferentialChangeset.php index 770a49e411..d92b27e574 100644 --- a/src/applications/differential/storage/DifferentialChangeset.php +++ b/src/applications/differential/storage/DifferentialChangeset.php @@ -325,14 +325,16 @@ public function getDiff() { public function getOldStatePathVector() { $path = $this->getOldFile(); - if (!strlen($path)) { + if (!phutil_nonempty_string($path)) { $path = $this->getFilename(); } - $path = trim($path, '/'); - $path = explode('/', $path); + if (!phutil_nonempty_string($path)) { + return null; + } - return $path; + $path = trim($path, '/'); + return explode('/', $path); } public function getNewStatePathVector() { diff --git a/src/applications/differential/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php index dfa6d1f791..8f537cff77 100644 --- a/src/applications/differential/storage/DifferentialDiff.php +++ b/src/applications/differential/storage/DifferentialDiff.php @@ -780,7 +780,7 @@ public function getFieldValuesForConduit() { $refs = array(); $branch = $this->getBranch(); - if (strlen($branch)) { + if (phutil_nonempty_string($branch)) { $refs[] = array( 'type' => 'branch', 'name' => $branch, @@ -788,7 +788,7 @@ public function getFieldValuesForConduit() { } $onto = $this->loadTargetBranch(); - if (strlen($onto)) { + if (phutil_nonempty_string($onto)) { $refs[] = array( 'type' => 'onto', 'name' => $onto, @@ -796,7 +796,7 @@ public function getFieldValuesForConduit() { } $base = $this->getSourceControlBaseRevision(); - if (strlen($base)) { + if ($base !== null && strlen($base)) { $refs[] = array( 'type' => 'base', 'identifier' => $base, @@ -804,7 +804,7 @@ public function getFieldValuesForConduit() { } $bookmark = $this->getBookmark(); - if (strlen($bookmark)) { + if (phutil_nonempty_string($bookmark)) { $refs[] = array( 'type' => 'bookmark', 'name' => $bookmark, diff --git a/src/applications/differential/storage/__tests__/DifferentialChangesetTestCase.php b/src/applications/differential/storage/__tests__/DifferentialChangesetTestCase.php new file mode 100644 index 0000000000..435b77ee87 --- /dev/null +++ b/src/applications/differential/storage/__tests__/DifferentialChangesetTestCase.php @@ -0,0 +1,16 @@ +getOldStatePathVector(); + $this->assertTrue(true, 'getOldStatePathVector did not throw an error'); + } catch (Throwable $ex) { + $this->assertTrue(false, + 'getOldStatePathVector threw an exception:'.$ex->getMessage()); + } + } + +} diff --git a/src/applications/differential/storage/__tests__/DifferentialDiffTestCase.php b/src/applications/differential/storage/__tests__/DifferentialDiffTestCase.php index 45547aed42..6959b33e27 100644 --- a/src/applications/differential/storage/__tests__/DifferentialDiffTestCase.php +++ b/src/applications/differential/storage/__tests__/DifferentialDiffTestCase.php @@ -1,6 +1,12 @@ true, + ); + } public function testDetectCopiedCode() { $copies = $this->detectCopiesIn('lint_engine.diff'); @@ -73,5 +79,35 @@ public function testDetectSlowCopiedCode() { $this->assertTrue(true); } + public function testGetFieldValuesForConduit() { + + $parser = new ArcanistDiffParser(); + $raw_diff = <<parseDiff($raw_diff)); + $this->assertTrue(true); + + $field_values = $diff->getFieldValuesForConduit(); + $this->assertTrue(is_array($field_values)); + foreach (['revisionPHID', 'authorPHID', 'repositoryPHID', 'refs'] + as $key) { + $this->assertTrue(array_key_exists($key, $field_values)); + } + + } } diff --git a/src/applications/differential/typeahead/DifferentialBlockingReviewerDatasource.php b/src/applications/differential/typeahead/DifferentialBlockingReviewerDatasource.php index bea7056ea2..e06693dc22 100644 --- a/src/applications/differential/typeahead/DifferentialBlockingReviewerDatasource.php +++ b/src/applications/differential/typeahead/DifferentialBlockingReviewerDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php b/src/applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php index 8ad8274ea3..bdb109fce9 100644 --- a/src/applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php +++ b/src/applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/differential/typeahead/DifferentialNoReviewersDatasource.php b/src/applications/differential/typeahead/DifferentialNoReviewersDatasource.php index 083b6c7cb5..eeff6de538 100644 --- a/src/applications/differential/typeahead/DifferentialNoReviewersDatasource.php +++ b/src/applications/differential/typeahead/DifferentialNoReviewersDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/differential/typeahead/DifferentialResponsibleDatasource.php b/src/applications/differential/typeahead/DifferentialResponsibleDatasource.php index 5307ab1866..4bca119347 100644 --- a/src/applications/differential/typeahead/DifferentialResponsibleDatasource.php +++ b/src/applications/differential/typeahead/DifferentialResponsibleDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/differential/typeahead/DifferentialResponsibleUserDatasource.php b/src/applications/differential/typeahead/DifferentialResponsibleUserDatasource.php index 8ac7b36520..494bca5f62 100644 --- a/src/applications/differential/typeahead/DifferentialResponsibleUserDatasource.php +++ b/src/applications/differential/typeahead/DifferentialResponsibleUserDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/differential/typeahead/DifferentialResponsibleViewerFunctionDatasource.php b/src/applications/differential/typeahead/DifferentialResponsibleViewerFunctionDatasource.php index d23ecff47d..05ae39ec2e 100644 --- a/src/applications/differential/typeahead/DifferentialResponsibleViewerFunctionDatasource.php +++ b/src/applications/differential/typeahead/DifferentialResponsibleViewerFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/differential/typeahead/DifferentialReviewerDatasource.php b/src/applications/differential/typeahead/DifferentialReviewerDatasource.php index 18aa9e48b7..9030213404 100644 --- a/src/applications/differential/typeahead/DifferentialReviewerDatasource.php +++ b/src/applications/differential/typeahead/DifferentialReviewerDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/differential/typeahead/DifferentialReviewerFunctionDatasource.php b/src/applications/differential/typeahead/DifferentialReviewerFunctionDatasource.php index 3b79016055..ac185878c9 100644 --- a/src/applications/differential/typeahead/DifferentialReviewerFunctionDatasource.php +++ b/src/applications/differential/typeahead/DifferentialReviewerFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/differential/typeahead/DifferentialRevisionClosedStatusDatasource.php b/src/applications/differential/typeahead/DifferentialRevisionClosedStatusDatasource.php index 8487111452..3c9e16a268 100644 --- a/src/applications/differential/typeahead/DifferentialRevisionClosedStatusDatasource.php +++ b/src/applications/differential/typeahead/DifferentialRevisionClosedStatusDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/differential/typeahead/DifferentialRevisionOpenStatusDatasource.php b/src/applications/differential/typeahead/DifferentialRevisionOpenStatusDatasource.php index 0f00e470c3..51da971918 100644 --- a/src/applications/differential/typeahead/DifferentialRevisionOpenStatusDatasource.php +++ b/src/applications/differential/typeahead/DifferentialRevisionOpenStatusDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/differential/typeahead/DifferentialRevisionStatusDatasource.php b/src/applications/differential/typeahead/DifferentialRevisionStatusDatasource.php index 5e240d3c29..978e9a847f 100644 --- a/src/applications/differential/typeahead/DifferentialRevisionStatusDatasource.php +++ b/src/applications/differential/typeahead/DifferentialRevisionStatusDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function loadResults() { diff --git a/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php b/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php index 144152526d..381a61a09d 100644 --- a/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionPlanChangesTransaction.php @@ -86,7 +86,7 @@ protected function validateAction($object, PhabricatorUser $viewer) { if ($object->isClosed()) { throw new Exception( pht( - 'You can not plan changes to this this revision because it has '. + 'You can not plan changes to this revision because it has '. 'already been closed.')); } diff --git a/src/applications/differential/xaction/DifferentialRevisionTitleTransaction.php b/src/applications/differential/xaction/DifferentialRevisionTitleTransaction.php index 50d00e1892..485aec0b5c 100644 --- a/src/applications/differential/xaction/DifferentialRevisionTitleTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionTitleTransaction.php @@ -23,6 +23,17 @@ public function getTitle() { } public function getTitleForFeed() { + $obj = $this->getObject(); + + // To avoid verbose messages we mention the current title just once + if ($obj && $obj->getTitle() === $this->getNewValue()) { + return pht( + '%s retitled %s from %s', + $this->renderAuthor(), + $this->renderObject(), + $this->renderOldValue()); + } + return pht( '%s retitled %s from %s to %s.', $this->renderAuthor(), diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php index e0e74486da..a2b9f81aee 100644 --- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php +++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php @@ -44,6 +44,11 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + // This is a special case, as r and R mean different things. + return array('r', 'R'); + } + public function getRoutes() { $repository_routes = array( '/' => array( @@ -183,6 +188,9 @@ protected function getCustomCapabilities() { DiffusionCreateRepositoriesCapability::CAPABILITY => array( 'default' => PhabricatorPolicies::POLICY_ADMIN, ), + PhabricatorRepositoryIdentityEditViewCapability::CAPABILITY => array( + 'default' => PhabricatorPolicies::POLICY_USER, + ), ); } diff --git a/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php index 345e63fc3b..e37e3ccaf6 100644 --- a/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php @@ -30,7 +30,7 @@ protected function getGitResult(ConduitAPIRequest $request) { $repository = $drequest->getRepository(); $contains = $request->getValue('contains'); - if (strlen($contains)) { + if (phutil_nonempty_string($contains)) { // See PHI958 (and, earlier, PHI720). If "patterns" are provided, pass // them to "git branch ..." to let callers test for reachability from @@ -80,7 +80,7 @@ protected function getMercurialResult(ConduitAPIRequest $request) { ->setRepository($repository); $contains = $request->getValue('contains'); - if (strlen($contains)) { + if (phutil_nonempty_string($contains)) { $query->withContainsCommit($contains); } diff --git a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php index 86ec7b7466..80c3f7b27d 100644 --- a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php @@ -37,7 +37,7 @@ protected function getGitResult(ConduitAPIRequest $request) { $repository = $drequest->getRepository(); $path = $request->getValue('path'); - if (!strlen($path) || $path === '/') { + if (!phutil_nonempty_string($path) || $path === '/') { $path = null; } diff --git a/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php index 694d4bc012..d97f09cc4e 100644 --- a/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php @@ -23,6 +23,7 @@ protected function defineCustomParamTypes() { return array( 'path' => 'required string', 'commit' => 'optional string', + 'encoding' => 'optional string', ); } @@ -152,7 +153,7 @@ protected function getSVNResult(ConduitAPIRequest $request) { $arcanist_changes = DiffusionPathChange::convertToArcanistChanges( $path_changes); - $parser = $this->getDefaultParser(); + $parser = $this->getDefaultParser($request); $parser->setChanges($arcanist_changes); $parser->forcePath($path->getPath()); $changes = $parser->parseDiff($raw_diff); @@ -212,18 +213,20 @@ private function getGitOrMercurialResult(ConduitAPIRequest $request) { return $this->getEmptyResult(); } - $parser = $this->getDefaultParser(); + $parser = $this->getDefaultParser($request); $changes = $parser->parseDiff($raw_diff); return $changes; } - private function getDefaultParser() { + private function getDefaultParser(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $parser = new ArcanistDiffParser(); - $try_encoding = $repository->getDetail('encoding'); + $try_encoding = coalesce( + $request->getValue('encoding'), + $repository->getDetail('encoding')); if ($try_encoding) { $parser->setTryEncoding($try_encoding); } diff --git a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php index c6ae6dbd2f..ba0ee3bd0b 100644 --- a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php @@ -47,15 +47,15 @@ protected function getGitResult(ConduitAPIRequest $request) { $against_hash = $request->getValue('against'); $path = $request->getValue('path'); - if (!strlen($path)) { + if (!phutil_nonempty_string($path)) { $path = null; } $offset = $request->getValue('offset'); $limit = $request->getValue('limit'); - if (strlen($against_hash)) { - $commit_range = "${against_hash}..${commit_hash}"; + if (phutil_nonempty_string($against_hash)) { + $commit_range = "{$against_hash}..{$commit_hash}"; } else { $commit_range = $commit_hash; } diff --git a/src/applications/diffusion/controller/DiffusionBlameController.php b/src/applications/diffusion/controller/DiffusionBlameController.php index 5de464b335..34078a7b7a 100644 --- a/src/applications/diffusion/controller/DiffusionBlameController.php +++ b/src/applications/diffusion/controller/DiffusionBlameController.php @@ -198,6 +198,10 @@ public function handleRequest(AphrontRequest $request) { $map[$identifier] = $data; } + if (empty($epochs)) { + $epochs[] = 0; + } + $epoch_min = min($epochs); $epoch_max = max($epochs); diff --git a/src/applications/diffusion/controller/DiffusionBranchTableController.php b/src/applications/diffusion/controller/DiffusionBranchTableController.php index 13f566a57b..63b1dbc592 100644 --- a/src/applications/diffusion/controller/DiffusionBranchTableController.php +++ b/src/applications/diffusion/controller/DiffusionBranchTableController.php @@ -26,7 +26,7 @@ public function handleRequest(AphrontRequest $request) { ); $contains = $drequest->getSymbolicCommit(); - if (strlen($contains)) { + if (phutil_nonempty_string($contains)) { $params['contains'] = $contains; } diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php index 2b81ab89cc..8cc0ae31c3 100644 --- a/src/applications/diffusion/controller/DiffusionBrowseController.php +++ b/src/applications/diffusion/controller/DiffusionBrowseController.php @@ -22,7 +22,7 @@ public function handleRequest(AphrontRequest $request) { // list. $grep = $request->getStr('grep'); - if (strlen($grep)) { + if (phutil_nonempty_string($grep)) { return $this->browseSearch(); } @@ -326,9 +326,11 @@ public function browseDirectory( )); $crumbs->setBorder(true); + $locate_file = $this->buildLocateFile(); $tabs = $this->buildTabsView('code'); $owners_list = $this->buildOwnersList($drequest); $bar = id(new PHUILeftRightView()) + ->setLeft($locate_file) ->setRight($this->corpusButtons) ->addClass('diffusion-action-bar'); diff --git a/src/applications/diffusion/controller/DiffusionCloneController.php b/src/applications/diffusion/controller/DiffusionCloneController.php index 8bc4faa1a2..bf118bcf6d 100644 --- a/src/applications/diffusion/controller/DiffusionCloneController.php +++ b/src/applications/diffusion/controller/DiffusionCloneController.php @@ -49,8 +49,6 @@ public function handleRequest(AphrontRequest $request) { ->appendChild(pht('Repository has no URIs set.')); } - $info = null; - // Try to load alternatives. This may fail for repositories which have not // cloned yet. If it does, just ignore it and continue. try { diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php index b377666eff..1163341527 100644 --- a/src/applications/diffusion/controller/DiffusionCommitController.php +++ b/src/applications/diffusion/controller/DiffusionCommitController.php @@ -27,8 +27,9 @@ public function handleRequest(AphrontRequest $request) { // If this page is being accessed via "/source/xyz/commit/...", redirect // to the canonical URI. - $has_callsign = strlen($request->getURIData('repositoryCallsign')); - $has_id = strlen($request->getURIData('repositoryID')); + $has_callsign = + phutil_nonempty_string($request->getURIData('repositoryCallsign')); + $has_id = phutil_nonempty_string($request->getURIData('repositoryID')); if (!$has_callsign && !$has_id) { $canonical_uri = $repository->getCommitURI($commit_identifier); return id(new AphrontRedirectResponse()) @@ -922,7 +923,7 @@ private function renderAuditStatusView( private function linkBugtraq($corpus) { $url = PhabricatorEnv::getEnvConfig('bugtraq.url'); - if (!strlen($url)) { + if (!phutil_nonempty_string($url)) { return $corpus; } diff --git a/src/applications/diffusion/controller/DiffusionController.php b/src/applications/diffusion/controller/DiffusionController.php index b3595c1b72..1f77f28d77 100644 --- a/src/applications/diffusion/controller/DiffusionController.php +++ b/src/applications/diffusion/controller/DiffusionController.php @@ -97,19 +97,19 @@ protected function getRepositoryIdentifierFromRequest( AphrontRequest $request) { $short_name = $request->getURIData('repositoryShortName'); - if (strlen($short_name)) { + if (phutil_nonempty_string($short_name)) { // If the short name ends in ".git", ignore it. $short_name = preg_replace('/\\.git\z/', '', $short_name); return $short_name; } $identifier = $request->getURIData('repositoryCallsign'); - if (strlen($identifier)) { + if (phutil_nonempty_string($identifier)) { return $identifier; } $id = $request->getURIData('repositoryID'); - if (strlen($id)) { + if (phutil_nonempty_string($id)) { return (int)$id; } @@ -153,7 +153,7 @@ private function buildCrumbList(array $spec = array()) { if (!$spec['commit'] && !$spec['tags'] && !$spec['branches']) { $branch_name = $drequest->getBranch(); - if (strlen($branch_name)) { + if (phutil_nonempty_string($branch_name)) { $repository_name .= ' ('.$branch_name.')'; } } @@ -211,7 +211,7 @@ private function buildCrumbList(array $spec = array()) { $view_name = pht('History'); break; case 'browse': - $view_name = pht('Browse'); + $view_name = pht('Code'); break; case 'lint': $view_name = pht('Lint'); @@ -502,6 +502,18 @@ protected function buildTabsView($key) { $view = new PHUIListView(); + $view->addMenuItem( + id(new PHUIListItemView()) + ->setKey('home') + ->setName(pht('Home')) + ->setIcon('fa-home') + ->setHref($drequest->generateURI( + array( + 'action' => 'branch', + 'path' => '', + ))) + ->setSelected($key == 'home')); + $view->addMenuItem( id(new PHUIListItemView()) ->setKey('code') @@ -554,4 +566,47 @@ protected function buildTabsView($key) { } + /** + * @return PHUIBoxView|null + */ + protected function buildLocateFile() { + $request = $this->getRequest(); + $viewer = $request->getUser(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + + $form_box = null; + if ($repository->canUsePathTree()) { + Javelin::initBehavior( + 'diffusion-locate-file', + array( + 'controlID' => 'locate-control', + 'inputID' => 'locate-input', + 'symbolicCommit' => $drequest->getSymbolicCommit(), + 'browseBaseURI' => (string)$drequest->generateURI( + array( + 'action' => 'browse', + 'commit' => '', + 'path' => '', + )), + 'uri' => (string)$drequest->generateURI( + array( + 'action' => 'pathtree', + )), + )); + + $form = id(new AphrontFormView()) + ->setUser($viewer) + ->appendChild( + id(new AphrontFormTypeaheadControl()) + ->setHardpointID('locate-control') + ->setID('locate-input') + ->setPlaceholder(pht('Locate File'))); + $form_box = id(new PHUIBoxView()) + ->appendChild($form->buildLayoutView()) + ->addClass('diffusion-profile-locate'); + } + return $form_box; + } + } diff --git a/src/applications/diffusion/controller/DiffusionDiffController.php b/src/applications/diffusion/controller/DiffusionDiffController.php index 69a473dc91..a844dc23d2 100644 --- a/src/applications/diffusion/controller/DiffusionDiffController.php +++ b/src/applications/diffusion/controller/DiffusionDiffController.php @@ -48,6 +48,7 @@ public function handleRequest(AphrontRequest $request) { array( 'commit' => $drequest->getCommit(), 'path' => $drequest->getPath(), + 'encoding' => $request->getStr('encoding'), )); $drequest->updateSymbolicCommit($data['effectiveCommit']); $raw_changes = ArcanistDiffChange::newFromConduit($data['changes']); diff --git a/src/applications/diffusion/controller/DiffusionHistoryController.php b/src/applications/diffusion/controller/DiffusionHistoryController.php index fb35d9b6ad..eeb8f13118 100644 --- a/src/applications/diffusion/controller/DiffusionHistoryController.php +++ b/src/applications/diffusion/controller/DiffusionHistoryController.php @@ -50,7 +50,7 @@ public function handleRequest(AphrontRequest $request) { // ancestors appropriately, but this would currrently be prohibitively // expensive in the general case. - $show_graph = !strlen($drequest->getPath()); + $show_graph = !phutil_nonempty_string($drequest->getPath()); if ($show_graph) { $history_list ->setParents($history_results['parents']) @@ -98,11 +98,10 @@ private function buildHeader(DiffusionRequest $drequest) { $viewer = $this->getViewer(); $repository = $drequest->getRepository(); - $no_path = !strlen($drequest->getPath()); - if ($no_path) { - $header_text = pht('History'); - } else { + if (phutil_nonempty_string($drequest->getPath())) { $header_text = $this->renderPathLinks($drequest, $mode = 'history'); + } else { + $header_text = pht('History'); } $header = id(new PHUIHeaderView()) diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php index ad3518b81e..7a523d799a 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php @@ -109,7 +109,7 @@ public function handleRequest(AphrontRequest $request) { ->setErrors(array($empty_message)); } - $tabs = $this->buildTabsView('code'); + $tabs = $this->buildTabsView('home'); $clone_uri = $drequest->generateURI( array( @@ -243,16 +243,16 @@ private function buildNormalContent(DiffusionRequest $drequest) { $readme = null; } + if ($readme) { + $content[] = $readme; + } + $content[] = $this->buildBrowseTable( $browse_results, $browse_paths, $browse_exception, $browse_pager); - if ($readme) { - $content[] = $readme; - } - try { $branch_button = $this->buildBranchList($drequest); $this->branchButton = $branch_button; @@ -396,7 +396,7 @@ private function buildBranchList(DiffusionRequest $drequest) { foreach ($branches as $branch) { $branch_uri = $drequest->generateURI( array( - 'action' => 'browse', + 'action' => 'branch', 'branch' => $branch->getShortname(), )); $actions->addAction( @@ -433,43 +433,6 @@ private function buildBranchList(DiffusionRequest $drequest) { return $button; } - private function buildLocateFile() { - $request = $this->getRequest(); - $viewer = $request->getUser(); - $drequest = $this->getDiffusionRequest(); - $repository = $drequest->getRepository(); - - $form_box = null; - if ($repository->canUsePathTree()) { - Javelin::initBehavior( - 'diffusion-locate-file', - array( - 'controlID' => 'locate-control', - 'inputID' => 'locate-input', - 'browseBaseURI' => (string)$drequest->generateURI( - array( - 'action' => 'browse', - )), - 'uri' => (string)$drequest->generateURI( - array( - 'action' => 'pathtree', - )), - )); - - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->appendChild( - id(new AphrontFormTypeaheadControl()) - ->setHardpointID('locate-control') - ->setID('locate-input') - ->setPlaceholder(pht('Locate File'))); - $form_box = id(new PHUIBoxView()) - ->appendChild($form->buildLayoutView()) - ->addClass('diffusion-profile-locate'); - } - return $form_box; - } - private function buildBrowseTable( $browse_results, $browse_paths, @@ -508,7 +471,7 @@ private function buildBrowseTable( $repository_name = $repository->getName(); $branch_name = $drequest->getBranch(); - if (strlen($branch_name)) { + if (phutil_nonempty_string($branch_name)) { $repository_name .= ' ('.$branch_name.')'; } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php index 93fe70c0fe..72e18d17e8 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php @@ -28,7 +28,8 @@ public function handleRequest(AphrontRequest $request) { 'the command line:')) ->appendCommand( csprintf( - 'phabricator/ $ ./bin/remove destroy %R', + '%s $ ./bin/remove destroy %R', + PlatformSymbols::getPlatformServerPath(), $repository->getMonogram())) ->appendParagraph( pht( diff --git a/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php b/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php index dfb84cc3f0..e6d07d418f 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php @@ -40,7 +40,7 @@ public function handleRequest(AphrontRequest $request) { } $selected = $request->getURIData('panel'); - if (!strlen($selected)) { + if (!phutil_nonempty_string($selected)) { $selected = head_key($panels); } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryProfilePictureController.php b/src/applications/diffusion/controller/DiffusionRepositoryProfilePictureController.php index cd4e3148c7..59a49def6f 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryProfilePictureController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryProfilePictureController.php @@ -24,6 +24,12 @@ public function handleRequest(AphrontRequest $request) { } $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); $done_uri = $repository->getURI(); @@ -57,9 +63,7 @@ public function handleRequest(AphrontRequest $request) { if (!$errors && !$is_default) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } else { $xform = PhabricatorFileTransform::getTransformByKey( PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); @@ -213,8 +217,7 @@ public function handleRequest(AphrontRequest $request) { ->setName('picture') ->setLabel(pht('Upload Picture')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($done_uri) diff --git a/src/applications/diffusion/controller/DiffusionServeController.php b/src/applications/diffusion/controller/DiffusionServeController.php index 3040328fbf..d092f9740d 100644 --- a/src/applications/diffusion/controller/DiffusionServeController.php +++ b/src/applications/diffusion/controller/DiffusionServeController.php @@ -43,8 +43,8 @@ public function isVCSRequest(AphrontRequest $request) { return null; } - $content_type = $request->getHTTPHeader('Content-Type'); - $user_agent = idx($_SERVER, 'HTTP_USER_AGENT'); + $content_type = $request->getHTTPHeader('Content-Type', ''); + $user_agent = idx($_SERVER, 'HTTP_USER_AGENT', ''); $request_type = $request->getHTTPHeader('X-Phabricator-Request-Type'); // This may have a "charset" suffix, so only match the prefix. @@ -183,8 +183,8 @@ private function serveRequest(AphrontRequest $request) { // won't prompt users who provide a username but no password otherwise. // See T10797 for discussion. - $have_user = strlen(idx($_SERVER, 'PHP_AUTH_USER')); - $have_pass = strlen(idx($_SERVER, 'PHP_AUTH_PW')); + $have_user = strlen(idx($_SERVER, 'PHP_AUTH_USER', '')); + $have_pass = strlen(idx($_SERVER, 'PHP_AUTH_PW', '')); if ($have_user && $have_pass) { $username = $_SERVER['PHP_AUTH_USER']; $password = new PhutilOpaqueEnvelope($_SERVER['PHP_AUTH_PW']); @@ -878,10 +878,29 @@ private function getMercurialArguments() { } $args_raw[] = $_SERVER[$header]; } - $args_raw = implode('', $args_raw); - return id(new PhutilQueryStringParser()) - ->parseQueryString($args_raw); + if ($args_raw) { + $args_raw = implode('', $args_raw); + return id(new PhutilQueryStringParser()) + ->parseQueryString($args_raw); + } + + // Sometimes arguments come in via the query string. Note that this will + // not handle multi-value entries e.g. "a[]=1,a[]=2" however it's unclear + // whether or how the mercurial protocol should handle this. + $query = idx($_SERVER, 'QUERY_STRING', ''); + $query_pairs = id(new PhutilQueryStringParser()) + ->parseQueryString($query); + foreach ($query_pairs as $key => $value) { + // Filter out private/internal keys as well as the command itself. + if (strncmp($key, '__', 2) && $key != 'cmd') { + $args_raw[$key] = $value; + } + } + + // TODO: Arguments can also come in via request body for POST requests. The + // body would be all arguments, url-encoded. + return $args_raw; } private function formatMercurialArguments($command, array $arguments) { diff --git a/src/applications/diffusion/controller/DiffusionTagListController.php b/src/applications/diffusion/controller/DiffusionTagListController.php index 15b8dc93ae..ffc6a1a9ef 100644 --- a/src/applications/diffusion/controller/DiffusionTagListController.php +++ b/src/applications/diffusion/controller/DiffusionTagListController.php @@ -25,7 +25,7 @@ public function handleRequest(AphrontRequest $request) { 'offset' => $pager->getOffset(), ); - if (strlen($drequest->getSymbolicCommit())) { + if (phutil_nonempty_string($drequest->getSymbolicCommit())) { $is_commit = true; $params['commit'] = $drequest->getSymbolicCommit(); } else { diff --git a/src/applications/diffusion/controller/__tests__/DiffusionServeControllerTestCase.php b/src/applications/diffusion/controller/__tests__/DiffusionServeControllerTestCase.php new file mode 100644 index 0000000000..67c5e63a57 --- /dev/null +++ b/src/applications/diffusion/controller/__tests__/DiffusionServeControllerTestCase.php @@ -0,0 +1,21 @@ + true, + ); + } + + public function testHandleRequest() { + $aphront_request = new AphrontRequest('example.com', '/'); + $diffusion_serve_controller = new DiffusionServeController(); + + $diffusion_serve_controller->setRequest($aphront_request); + $result = $diffusion_serve_controller->handleRequest($aphront_request); + $this->assertTrue(true, 'handleRequest did not throw an error'); + $this->assertTrue($result instanceof PhabricatorVCSResponse, + 'handleRequest() returns PhabricatorVCSResponse object'); + } + +} diff --git a/src/applications/diffusion/data/DiffusionCommitRef.php b/src/applications/diffusion/data/DiffusionCommitRef.php index 06dd4d5ffb..76c3371192 100644 --- a/src/applications/diffusion/data/DiffusionCommitRef.php +++ b/src/applications/diffusion/data/DiffusionCommitRef.php @@ -131,6 +131,8 @@ public function getSummary() { } private function formatUser($name, $email) { + $name = coalesce($name, ''); + $email = coalesce($email, ''); if (strlen($name) && strlen($email)) { return "{$name} <{$email}>"; } else if (strlen($email)) { diff --git a/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php b/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php index 800d195726..2c33a82ad5 100644 --- a/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php +++ b/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php @@ -87,7 +87,7 @@ protected function willRenderRef(PhabricatorDocumentRef $ref) { $ref->setSymbolMetadata($this->getSymbolMetadata()); $coverage = $drequest->loadCoverage(); - if (strlen($coverage)) { + if (phutil_nonempty_string($coverage)) { $ref->addCoverage($coverage); } } diff --git a/src/applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php b/src/applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php index ce7a899bda..f59cb8e564 100644 --- a/src/applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php +++ b/src/applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php @@ -25,4 +25,45 @@ public function getConduitDescription() { 'The source commit is associated with the destination revision.'); } + public function getTransactionAddString( + $actor, + $add_count, + $add_edges) { + + return pht( + '%s added %s revision(s): %s.', + $actor, + $add_count, + $add_edges); + } + + public function getTransactionRemoveString( + $actor, + $rem_count, + $rem_edges) { + + return pht( + '%s removed %s revision(s): %s.', + $actor, + $rem_count, + $rem_edges); + } + + public function getTransactionEditString( + $actor, + $total_count, + $add_count, + $add_edges, + $rem_count, + $rem_edges) { + + return pht( + '%s edited revision(s), added %s: %s; removed %s: %s.', + $actor, + $add_count, + $add_edges, + $rem_count, + $rem_edges); + } + } diff --git a/src/applications/diffusion/editor/DiffusionCommitEditEngine.php b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php index 60a2667c95..c807d4e8c4 100644 --- a/src/applications/diffusion/editor/DiffusionCommitEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionCommitEditEngine.php @@ -25,7 +25,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function newEditableObject() { @@ -100,19 +100,22 @@ protected function buildCustomEditFields($object) { $data = $object->getCommitData(); $fields = array(); - - $fields[] = id(new PhabricatorDatasourceEditField()) - ->setKey('auditors') - ->setLabel(pht('Auditors')) - ->setDatasource(new DiffusionAuditorDatasource()) - ->setUseEdgeTransactions(true) - ->setTransactionType( - DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE) - ->setCommentActionLabel(pht('Change Auditors')) - ->setDescription(pht('Auditors for this commit.')) - ->setConduitDescription(pht('Change the auditors for this commit.')) - ->setConduitTypeDescription(pht('New auditors.')) - ->setValue($object->getAuditorPHIDsForEdit()); + // remove "Change Auditors" from "Add Action" dropdown etc + // if Audit is not installed + if (id(new PhabricatorAuditApplication())->isInstalled()) { + $fields[] = id(new PhabricatorDatasourceEditField()) + ->setKey('auditors') + ->setLabel(pht('Auditors')) + ->setDatasource(new DiffusionAuditorDatasource()) + ->setUseEdgeTransactions(true) + ->setTransactionType( + DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE) + ->setCommentActionLabel(pht('Change Auditors')) + ->setDescription(pht('Auditors for this commit.')) + ->setConduitDescription(pht('Change the auditors for this commit.')) + ->setConduitTypeDescription(pht('New auditors.')) + ->setValue($object->getAuditorPHIDsForEdit()); + } $actions = DiffusionCommitActionTransaction::loadAllActions(); $actions = msortv($actions, 'getCommitActionOrderVector'); diff --git a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php index 07213afd2b..c9dbb2b329 100644 --- a/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionRepositoryEditEngine.php @@ -41,7 +41,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function newEditableObject() { diff --git a/src/applications/diffusion/editor/DiffusionRepositoryIdentityEditor.php b/src/applications/diffusion/editor/DiffusionRepositoryIdentityEditor.php index 8e964e3e71..54bf9daea7 100644 --- a/src/applications/diffusion/editor/DiffusionRepositoryIdentityEditor.php +++ b/src/applications/diffusion/editor/DiffusionRepositoryIdentityEditor.php @@ -20,7 +20,7 @@ protected function supportsSearch() { } public function getEditorApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/diffusion/editor/DiffusionURIEditEngine.php b/src/applications/diffusion/editor/DiffusionURIEditEngine.php index dbc1238153..2c7f79359e 100644 --- a/src/applications/diffusion/editor/DiffusionURIEditEngine.php +++ b/src/applications/diffusion/editor/DiffusionURIEditEngine.php @@ -33,7 +33,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function newEditableObject() { diff --git a/src/applications/diffusion/editor/DiffusionURIEditor.php b/src/applications/diffusion/editor/DiffusionURIEditor.php index 90ced865f0..b8aecca8a3 100644 --- a/src/applications/diffusion/editor/DiffusionURIEditor.php +++ b/src/applications/diffusion/editor/DiffusionURIEditor.php @@ -7,7 +7,7 @@ final class DiffusionURIEditor private $repositoryPHID; public function getEditorApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php index c172f27466..e4e8fd2293 100644 --- a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php +++ b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php @@ -1133,7 +1133,7 @@ private function newPushEvent() { ->setHookWait(phutil_microseconds_since($hook_start)); $identifier = $this->getRequestIdentifier(); - if (strlen($identifier)) { + if ($identifier !== null && strlen($identifier)) { $event->setRequestIdentifier($identifier); } diff --git a/src/applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php b/src/applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php index f895847d4a..f3ae92248c 100644 --- a/src/applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php +++ b/src/applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php @@ -9,8 +9,13 @@ public function getHeraldActionName() { return pht('Add auditors'); } + // hide "Add auditors" Herald action if Audit not installed public function supportsRuleType($rule_type) { - return ($rule_type != HeraldRuleTypeConfig::RULE_TYPE_PERSONAL); + if (id(new PhabricatorAuditApplication())->isInstalled()) { + return ($rule_type != HeraldRuleTypeConfig::RULE_TYPE_PERSONAL); + } else { + return false; + } } public function applyEffect($object, HeraldEffect $effect) { diff --git a/src/applications/diffusion/herald/DiffusionAuditorsAddSelfHeraldAction.php b/src/applications/diffusion/herald/DiffusionAuditorsAddSelfHeraldAction.php index d27876d40e..b1ab98db77 100644 --- a/src/applications/diffusion/herald/DiffusionAuditorsAddSelfHeraldAction.php +++ b/src/applications/diffusion/herald/DiffusionAuditorsAddSelfHeraldAction.php @@ -9,8 +9,13 @@ public function getHeraldActionName() { return pht('Add me as an auditor'); } + // hide "Add me as an auditor" Herald action if Audit not installed public function supportsRuleType($rule_type) { - return ($rule_type == HeraldRuleTypeConfig::RULE_TYPE_PERSONAL); + if (id(new PhabricatorAuditApplication())->isInstalled()) { + return ($rule_type == HeraldRuleTypeConfig::RULE_TYPE_PERSONAL); + } else { + return false; + } } public function applyEffect($object, HeraldEffect $effect) { diff --git a/src/applications/diffusion/herald/DiffusionCommitAuditorsHeraldField.php b/src/applications/diffusion/herald/DiffusionCommitAuditorsHeraldField.php index 5f0da133f8..7afd0f6705 100644 --- a/src/applications/diffusion/herald/DiffusionCommitAuditorsHeraldField.php +++ b/src/applications/diffusion/herald/DiffusionCommitAuditorsHeraldField.php @@ -5,6 +5,15 @@ final class DiffusionCommitAuditorsHeraldField const FIELDCONST = 'diffusion.commit.auditors'; + // hide "Auditors" Herald condition if Audit not installed + public function supportsObject($object) { + if (id(new PhabricatorAuditApplication())->isInstalled()) { + return ($object instanceof PhabricatorRepositoryCommit); + } else { + return false; + } + } + public function getHeraldFieldName() { return pht('Auditors'); } diff --git a/src/applications/diffusion/herald/DiffusionCommitPackageAuditHeraldField.php b/src/applications/diffusion/herald/DiffusionCommitPackageAuditHeraldField.php index 587c0b58d3..9fdd785fcb 100644 --- a/src/applications/diffusion/herald/DiffusionCommitPackageAuditHeraldField.php +++ b/src/applications/diffusion/herald/DiffusionCommitPackageAuditHeraldField.php @@ -5,6 +5,16 @@ final class DiffusionCommitPackageAuditHeraldField const FIELDCONST = 'diffusion.commit.package.audit'; + // hide "Affected packages that need audit" Herald condition + // if Audit not installed + public function supportsObject($object) { + if (id(new PhabricatorAuditApplication())->isInstalled()) { + return ($object instanceof PhabricatorRepositoryCommit); + } else { + return false; + } + } + public function getHeraldFieldName() { return pht('Affected packages that need audit'); } diff --git a/src/applications/diffusion/herald/HeraldCommitAdapter.php b/src/applications/diffusion/herald/HeraldCommitAdapter.php index af7c757cd9..0ec9de7e87 100644 --- a/src/applications/diffusion/herald/HeraldCommitAdapter.php +++ b/src/applications/diffusion/herald/HeraldCommitAdapter.php @@ -18,7 +18,7 @@ final class HeraldCommitAdapter private $buildRequests = array(); public function getAdapterApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function newObject() { diff --git a/src/applications/diffusion/herald/HeraldPreCommitAdapter.php b/src/applications/diffusion/herald/HeraldPreCommitAdapter.php index f63f647da6..4707545e9d 100644 --- a/src/applications/diffusion/herald/HeraldPreCommitAdapter.php +++ b/src/applications/diffusion/herald/HeraldPreCommitAdapter.php @@ -22,7 +22,7 @@ public function getHookEngine() { } public function getAdapterApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function isTestAdapterForObject($object) { diff --git a/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php index 02df82c7c0..6ad26a399a 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php @@ -31,8 +31,7 @@ public function buildManagementPanelCurtain() { $repository = $this->getRepository(); $viewer = $this->getViewer(); - $action_list = id(new PhabricatorActionListView()) - ->setViewer($viewer); + $action_list = $this->newActionList(); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, @@ -220,7 +219,7 @@ private function buildBasics() { $view->addProperty(pht('Type'), $type); $callsign = $repository->getCallsign(); - if (!strlen($callsign)) { + if (!phutil_nonempty_string($callsign)) { $callsign = phutil_tag('em', array(), pht('No Callsign')); } $view->addProperty(pht('Callsign'), $callsign); diff --git a/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php index 1a58ef6a49..d938ed834c 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php @@ -111,6 +111,9 @@ public function getPanelNavigationURI() { final protected function newActionList() { $viewer = $this->getViewer(); + + // Generating this ID allows to spawn the "Actions" menu + // on mobile on the header $action_id = celerity_generate_unique_node_id(); return id(new PhabricatorActionListView()) diff --git a/src/applications/diffusion/query/DiffusionCommitHintQuery.php b/src/applications/diffusion/query/DiffusionCommitHintQuery.php index c0794a4387..fdf30e693b 100644 --- a/src/applications/diffusion/query/DiffusionCommitHintQuery.php +++ b/src/applications/diffusion/query/DiffusionCommitHintQuery.php @@ -106,7 +106,7 @@ protected function didFilterPage(array $hints) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/diffusion/query/DiffusionCommitQuery.php b/src/applications/diffusion/query/DiffusionCommitQuery.php index 7b4e80bfec..e1bec46441 100644 --- a/src/applications/diffusion/query/DiffusionCommitQuery.php +++ b/src/applications/diffusion/query/DiffusionCommitQuery.php @@ -931,7 +931,7 @@ protected function shouldGroupQueryResultRows() { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function getOrderableColumns() { diff --git a/src/applications/diffusion/query/DiffusionPullLogSearchEngine.php b/src/applications/diffusion/query/DiffusionPullLogSearchEngine.php index dfdfceb519..dbb9205f61 100644 --- a/src/applications/diffusion/query/DiffusionPullLogSearchEngine.php +++ b/src/applications/diffusion/query/DiffusionPullLogSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function newQuery() { diff --git a/src/applications/diffusion/query/DiffusionRepositoryIdentitySearchEngine.php b/src/applications/diffusion/query/DiffusionRepositoryIdentitySearchEngine.php index f41b6b89a4..d6877433f7 100644 --- a/src/applications/diffusion/query/DiffusionRepositoryIdentitySearchEngine.php +++ b/src/applications/diffusion/query/DiffusionRepositoryIdentitySearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function newQuery() { diff --git a/src/applications/diffusion/query/DiffusionSyncLogSearchEngine.php b/src/applications/diffusion/query/DiffusionSyncLogSearchEngine.php index 6b321189f4..02280efdd2 100644 --- a/src/applications/diffusion/query/DiffusionSyncLogSearchEngine.php +++ b/src/applications/diffusion/query/DiffusionSyncLogSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function newQuery() { diff --git a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php index d11b658131..21c86e7435 100644 --- a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php +++ b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelGitRefQuery.php @@ -27,7 +27,7 @@ protected function executeQuery() { $with_branches = isset($ref_types[$type_branch]); $with_tags = isset($ref_types[$type_tag]); - $with_refs = isset($refs_types[$type_ref]); + $with_refs = isset($ref_types[$type_ref]); $repository = $this->getRepository(); diff --git a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php index 0bbba1dc18..899e69d49b 100644 --- a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php +++ b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php @@ -140,8 +140,9 @@ private function resolveGitRefs() { if (count($lines) !== count($unresolved)) { throw new Exception( pht( - 'Unexpected line count from `%s`!', - 'git cat-file')); + 'Unexpected line count from `%s` in %s!', + 'git cat-file', + $repository->getMonogram())); } $hits = array(); @@ -153,8 +154,9 @@ private function resolveGitRefs() { if (count($parts) < 2) { throw new Exception( pht( - 'Failed to parse `%s` output: %s', + 'Failed to parse `%s` output in %s: %s', 'git cat-file', + $repository->getMonogram(), $line)); } list($identifier, $type) = $parts; @@ -177,8 +179,9 @@ private function resolveGitRefs() { default: throw new Exception( pht( - 'Unexpected object type from `%s`: %s', + 'Unexpected object type from `%s` in %s: %s', 'git cat-file', + $repository->getMonogram(), $line)); } diff --git a/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php b/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php index 7c9e721431..d994410034 100644 --- a/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php +++ b/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php @@ -48,6 +48,9 @@ public function loadPathIDs() { */ public static function normalizePath($path) { + // Ensure we have a string, not a null. + $path = coalesce($path, ''); + // Normalize to single slashes, e.g. "///" => "/". $path = preg_replace('@[/]{2,}@', '/', $path); diff --git a/src/applications/diffusion/request/DiffusionGitRequest.php b/src/applications/diffusion/request/DiffusionGitRequest.php index a283fff206..16953f5996 100644 --- a/src/applications/diffusion/request/DiffusionGitRequest.php +++ b/src/applications/diffusion/request/DiffusionGitRequest.php @@ -3,6 +3,9 @@ final class DiffusionGitRequest extends DiffusionRequest { protected function isStableCommit($symbol) { + if ($symbol === null) { + return false; + } return preg_match('/^[a-f0-9]{40}\z/', $symbol); } diff --git a/src/applications/diffusion/request/DiffusionMercurialRequest.php b/src/applications/diffusion/request/DiffusionMercurialRequest.php index b626d62750..676dc097f7 100644 --- a/src/applications/diffusion/request/DiffusionMercurialRequest.php +++ b/src/applications/diffusion/request/DiffusionMercurialRequest.php @@ -3,6 +3,9 @@ final class DiffusionMercurialRequest extends DiffusionRequest { protected function isStableCommit($symbol) { + if ($symbol === null) { + return false; + } return preg_match('/^[a-f0-9]{40}\z/', $symbol); } @@ -10,11 +13,9 @@ public function getBranch() { if ($this->branch) { return $this->branch; } - if ($this->repository) { return $this->repository->getDefaultBranch(); } - throw new Exception(pht('Unable to determine branch!')); } diff --git a/src/applications/diffusion/request/DiffusionRequest.php b/src/applications/diffusion/request/DiffusionRequest.php index 4bcb4d3bc5..c301b89b28 100644 --- a/src/applications/diffusion/request/DiffusionRequest.php +++ b/src/applications/diffusion/request/DiffusionRequest.php @@ -254,7 +254,7 @@ public function setPath($path) { } public function getPath() { - return $this->path; + return coalesce($this->path, ''); } public function getLine() { diff --git a/src/applications/diffusion/request/DiffusionSvnRequest.php b/src/applications/diffusion/request/DiffusionSvnRequest.php index 5f50366331..33fc3446f9 100644 --- a/src/applications/diffusion/request/DiffusionSvnRequest.php +++ b/src/applications/diffusion/request/DiffusionSvnRequest.php @@ -3,6 +3,9 @@ final class DiffusionSvnRequest extends DiffusionRequest { protected function isStableCommit($symbol) { + if ($symbol === null) { + return false; + } return preg_match('/^[1-9]\d*\z/', $symbol); } diff --git a/src/applications/diffusion/typeahead/DiffusionAuditorDatasource.php b/src/applications/diffusion/typeahead/DiffusionAuditorDatasource.php index 54c3aa72d9..fe0f12dd70 100644 --- a/src/applications/diffusion/typeahead/DiffusionAuditorDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionAuditorDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php b/src/applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php index 2bfc1ee037..eecef5dd03 100644 --- a/src/applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/diffusion/typeahead/DiffusionIdentityUnassignedDatasource.php b/src/applications/diffusion/typeahead/DiffusionIdentityUnassignedDatasource.php index b69498931b..50ca68e59c 100644 --- a/src/applications/diffusion/typeahead/DiffusionIdentityUnassignedDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionIdentityUnassignedDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/diffusion/typeahead/DiffusionRefDatasource.php b/src/applications/diffusion/typeahead/DiffusionRefDatasource.php index f98960e7cc..a0a8e89237 100644 --- a/src/applications/diffusion/typeahead/DiffusionRefDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionRefDatasource.php @@ -15,7 +15,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function loadResults() { diff --git a/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php b/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php index e0662b1e1a..5ba53d252c 100644 --- a/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php @@ -12,17 +12,20 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function loadResults() { - $viewer = $this->getViewer(); - $raw_query = $this->getRawQuery(); - $query = id(new PhabricatorRepositoryQuery()) - ->setOrder('name') - ->withDatasourceQuery($raw_query); - $repos = $this->executeQuery($query); + ->setViewer($this->getViewer()); + + $this->applyFerretConstraints( + $query, + id(new PhabricatorRepository())->newFerretEngine(), + 'title', + $this->getRawQuery()); + + $repos = $query->execute(); $type_icon = id(new PhabricatorRepositoryRepositoryPHIDType()) ->getTypeIcon(); @@ -41,7 +44,7 @@ public function loadResults() { $parts[] = $name; $slug = $repository->getRepositorySlug(); - if (strlen($slug)) { + if (phutil_nonempty_string($slug)) { $parts[] = $slug; } diff --git a/src/applications/diffusion/typeahead/DiffusionRepositoryFunctionDatasource.php b/src/applications/diffusion/typeahead/DiffusionRepositoryFunctionDatasource.php index 5fb7d5f5ac..5865aa0ef4 100644 --- a/src/applications/diffusion/typeahead/DiffusionRepositoryFunctionDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionRepositoryFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDifferentialApplication'; + return PhabricatorDifferentialApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/diffusion/typeahead/DiffusionSymbolDatasource.php b/src/applications/diffusion/typeahead/DiffusionSymbolDatasource.php index 1c29c0dde4..1382bd41e0 100644 --- a/src/applications/diffusion/typeahead/DiffusionSymbolDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionSymbolDatasource.php @@ -18,7 +18,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function loadResults() { diff --git a/src/applications/diffusion/typeahead/DiffusionTaggedRepositoriesFunctionDatasource.php b/src/applications/diffusion/typeahead/DiffusionTaggedRepositoriesFunctionDatasource.php index 019d78d786..dbc7362b2a 100644 --- a/src/applications/diffusion/typeahead/DiffusionTaggedRepositoriesFunctionDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionTaggedRepositoriesFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/diffusion/view/DiffusionCloneURIView.php b/src/applications/diffusion/view/DiffusionCloneURIView.php index c9dae57563..3a79e8d72c 100644 --- a/src/applications/diffusion/view/DiffusionCloneURIView.php +++ b/src/applications/diffusion/view/DiffusionCloneURIView.php @@ -40,6 +40,7 @@ public function render() { require_celerity_resource('diffusion-icons-css'); Javelin::initBehavior('select-content'); + Javelin::initBehavior('phabricator-clipboard-copy'); $uri_id = celerity_generate_unique_node_id(); @@ -53,6 +54,11 @@ public function render() { 'value' => $display, 'class' => 'diffusion-clone-uri', 'readonly' => 'true', + 'sigil' => 'select-content', + 'meta' => array( + 'selectID' => $uri_id, + 'once' => true, + ), )); $uri = $this->getRepositoryURI(); @@ -71,17 +77,30 @@ public function render() { break; } - $io = id(new PHUIButtonView()) + $io = javelin_tag( + 'span', + array( + 'class' => 'diffusion-clone-uri-io', + 'sigil' => 'has-tooltip', + 'meta' => array( + 'tip' => $io_tip, + ), + ), + id(new PHUIIconView())->setIcon($io_icon)); + + $copy = id(new PHUIButtonView()) ->setTag('a') ->setColor(PHUIButtonView::GREY) - ->setIcon($io_icon) + ->setIcon('fa-clipboard') ->setHref('#') - ->addSigil('select-content') + ->addSigil('clipboard-copy') ->addSigil('has-tooltip') ->setMetadata( array( - 'tip' => $io_tip, - 'selectID' => $uri_id, + 'tip' => pht('Copy repository URI'), + 'text' => $display, + 'successMessage' => pht('Repository URI copied.'), + 'errorMessage' => pht('Copy of Repository URI failed.'), )); switch ($uri->getEffectiveIOType()) { @@ -121,19 +140,18 @@ public function render() { ->setHref($auth_uri) ->setDisabled($auth_disabled); - $cells = array(); - $cells[] = phutil_tag('td', array(), $input); - $cells[] = phutil_tag('th', array(), $io); - $cells[] = phutil_tag('th', array(), $credentials); - - $row = phutil_tag('tr', array(), $cells); + $elements = array(); + $elements[] = $io; + $elements[] = $input; + $elements[] = $copy; + $elements[] = $credentials; return phutil_tag( - 'table', + 'div', array( - 'class' => 'diffusion-clone-uri-table', + 'class' => 'diffusion-clone-uri-wrapper', ), - $row); + $elements); } } diff --git a/src/applications/diffusion/view/DiffusionCommitGraphView.php b/src/applications/diffusion/view/DiffusionCommitGraphView.php index 30c252712c..7e70a4e655 100644 --- a/src/applications/diffusion/view/DiffusionCommitGraphView.php +++ b/src/applications/diffusion/view/DiffusionCommitGraphView.php @@ -169,7 +169,10 @@ private function newObjectItemViews() { $this->addBuildAction($item_view, $hash); } - $this->addAuditAction($item_view, $hash); + // hide Audit entry on /diffusion/commit/query/all if Audit not installed + if (id(new PhabricatorAuditApplication())->isInstalled()) { + $this->addAuditAction($item_view, $hash); + } if ($show_auditors) { $auditor_list = $item_view->newMapView(); diff --git a/src/applications/diffusion/view/DiffusionView.php b/src/applications/diffusion/view/DiffusionView.php index 795b47c773..268514ff53 100644 --- a/src/applications/diffusion/view/DiffusionView.php +++ b/src/applications/diffusion/view/DiffusionView.php @@ -71,7 +71,7 @@ final public function linkBrowse( $display_name = idx($details, 'name'); unset($details['name']); - if (strlen($display_name)) { + if (phutil_nonempty_string($display_name)) { $display_name = phutil_tag( 'span', array( diff --git a/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php index 1d351ffa5d..d381602766 100644 --- a/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php +++ b/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php @@ -8,6 +8,9 @@ final public function getCommitActionKey() { } public function isActionAvailable($object, PhabricatorUser $viewer) { + if (!id(new PhabricatorAuditApplication())->isInstalled()) { + return false; + } try { $this->validateAction($object, $viewer); return true; diff --git a/src/applications/diviner/atom/DivinerAtom.php b/src/applications/diviner/atom/DivinerAtom.php index f7f8d2c4ac..93e24d5d03 100644 --- a/src/applications/diviner/atom/DivinerAtom.php +++ b/src/applications/diviner/atom/DivinerAtom.php @@ -346,7 +346,7 @@ public static function newFromDictionary(array $dictionary) { ->setContext(idx($dictionary, 'context')) ->setLanguage(idx($dictionary, 'language')) ->setParentHash(idx($dictionary, 'parentHash')) - ->setDocblockRaw(idx($dictionary, 'docblockRaw')) + ->setDocblockRaw(coalesce(idx($dictionary, 'docblockRaw'), '')) ->setProperties(idx($dictionary, 'properties')); foreach (idx($dictionary, 'warnings', array()) as $warning) { diff --git a/src/applications/diviner/controller/DivinerFindController.php b/src/applications/diviner/controller/DivinerFindController.php index 1bedd65b06..2e3b121706 100644 --- a/src/applications/diviner/controller/DivinerFindController.php +++ b/src/applications/diviner/controller/DivinerFindController.php @@ -32,7 +32,7 @@ public function handleRequest(AphrontRequest $request) { } $context = $request->getStr('context'); - if (strlen($context)) { + if (phutil_nonempty_string($context)) { $query->withContexts(array($context)); } diff --git a/src/applications/diviner/controller/DivinerMainController.php b/src/applications/diviner/controller/DivinerMainController.php index 21450698e6..791e7520b6 100644 --- a/src/applications/diviner/controller/DivinerMainController.php +++ b/src/applications/diviner/controller/DivinerMainController.php @@ -50,7 +50,7 @@ public function handleRequest(AphrontRequest $request) { $text = pht( "(NOTE) **Looking for documentation?** ". "If you're looking for help and information about %s, ". - "you can [[https://secure.phabricator.com/diviner/ | ". + "you can [[https://we.phorge.it/diviner/ | ". "browse the public %s documentation]] on the live site.\n\n". "Diviner is the documentation generator used to build this ". "documentation.\n\n". diff --git a/src/applications/diviner/editor/DivinerLiveBookEditor.php b/src/applications/diviner/editor/DivinerLiveBookEditor.php index 3f392391bb..dea16af377 100644 --- a/src/applications/diviner/editor/DivinerLiveBookEditor.php +++ b/src/applications/diviner/editor/DivinerLiveBookEditor.php @@ -4,7 +4,7 @@ final class DivinerLiveBookEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorDivinerApplication'; + return PhabricatorDivinerApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/diviner/phid/DivinerAtomPHIDType.php b/src/applications/diviner/phid/DivinerAtomPHIDType.php index 7387b0cafe..3e040a9e79 100644 --- a/src/applications/diviner/phid/DivinerAtomPHIDType.php +++ b/src/applications/diviner/phid/DivinerAtomPHIDType.php @@ -17,7 +17,7 @@ public function getTypeIcon() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDivinerApplication'; + return PhabricatorDivinerApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/diviner/phid/DivinerBookPHIDType.php b/src/applications/diviner/phid/DivinerBookPHIDType.php index da08ae3e87..bb840c5342 100644 --- a/src/applications/diviner/phid/DivinerBookPHIDType.php +++ b/src/applications/diviner/phid/DivinerBookPHIDType.php @@ -17,7 +17,7 @@ public function getTypeIcon() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDivinerApplication'; + return PhabricatorDivinerApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/diviner/query/DivinerAtomQuery.php b/src/applications/diviner/query/DivinerAtomQuery.php index 65a5634008..1ca7aad984 100644 --- a/src/applications/diviner/query/DivinerAtomQuery.php +++ b/src/applications/diviner/query/DivinerAtomQuery.php @@ -505,7 +505,7 @@ private function attachAllChildren( } public function getQueryApplicationClass() { - return 'PhabricatorDivinerApplication'; + return PhabricatorDivinerApplication::class; } } diff --git a/src/applications/diviner/query/DivinerAtomSearchEngine.php b/src/applications/diviner/query/DivinerAtomSearchEngine.php index 35caf20a63..47dd418c78 100644 --- a/src/applications/diviner/query/DivinerAtomSearchEngine.php +++ b/src/applications/diviner/query/DivinerAtomSearchEngine.php @@ -7,7 +7,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDivinerApplication'; + return PhabricatorDivinerApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/diviner/query/DivinerBookQuery.php b/src/applications/diviner/query/DivinerBookQuery.php index 2d6527ec96..4bc9ff270d 100644 --- a/src/applications/diviner/query/DivinerBookQuery.php +++ b/src/applications/diviner/query/DivinerBookQuery.php @@ -133,7 +133,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { $this->phids); } - if (strlen($this->nameLike)) { + if (phutil_nonempty_string($this->nameLike)) { $where[] = qsprintf( $conn, 'name LIKE %~', @@ -147,7 +147,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { $this->names); } - if (strlen($this->namePrefix)) { + if (phutil_nonempty_string($this->namePrefix)) { $where[] = qsprintf( $conn, 'name LIKE %>', @@ -167,7 +167,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDivinerApplication'; + return PhabricatorDivinerApplication::class; } public function getOrderableColumns() { diff --git a/src/applications/diviner/query/DivinerLiveBookTransactionQuery.php b/src/applications/diviner/query/DivinerLiveBookTransactionQuery.php index e3ce6d41d7..b552417430 100644 --- a/src/applications/diviner/query/DivinerLiveBookTransactionQuery.php +++ b/src/applications/diviner/query/DivinerLiveBookTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new DivinerLiveBookTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDivinerApplication::class; + } + } diff --git a/src/applications/diviner/storage/DivinerLiveSymbol.php b/src/applications/diviner/storage/DivinerLiveSymbol.php index 38b99208a7..6477e00879 100644 --- a/src/applications/diviner/storage/DivinerLiveSymbol.php +++ b/src/applications/diviner/storage/DivinerLiveSymbol.php @@ -172,7 +172,7 @@ public function save() { public function getTitle() { $title = parent::getTitle(); - if (!strlen($title)) { + if (!phutil_nonempty_string($title)) { $title = $this->getName(); } @@ -182,7 +182,7 @@ public function getTitle() { public function setTitle($value) { $this->writeField('title', $value); - if (strlen($value)) { + if (phutil_nonempty_string($value)) { $slug = DivinerAtomRef::normalizeTitleString($value); $hash = PhabricatorHash::digestForIndex($slug); $this->titleSlugHash = $hash; diff --git a/src/applications/diviner/typeahead/DivinerBookDatasource.php b/src/applications/diviner/typeahead/DivinerBookDatasource.php index 2584626d6e..8fc2a52238 100644 --- a/src/applications/diviner/typeahead/DivinerBookDatasource.php +++ b/src/applications/diviner/typeahead/DivinerBookDatasource.php @@ -11,7 +11,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDivinerApplication'; + return PhabricatorDivinerApplication::class; } public function loadResults() { diff --git a/src/applications/diviner/workflow/DivinerGenerateWorkflow.php b/src/applications/diviner/workflow/DivinerGenerateWorkflow.php index 86b75c8480..0cde772fc3 100644 --- a/src/applications/diviner/workflow/DivinerGenerateWorkflow.php +++ b/src/applications/diviner/workflow/DivinerGenerateWorkflow.php @@ -194,7 +194,7 @@ private function generateBook($book, PhutilArgumentParser $args) { $identifier = $args->getArg('repository'); $repository = null; - if (strlen($identifier)) { + if (phutil_nonempty_string($identifier)) { $repository = id(new PhabricatorRepositoryQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withIdentifiers(array($identifier)) @@ -348,7 +348,7 @@ private function buildAtomizerFutures(array $file_atomizers) { $atomizers[$atomizer][] = $file; } - $root = dirname(phutil_get_library_root('phabricator')); + $root = dirname(phutil_get_library_root('phorge')); $config_root = $this->getConfig('root'); $bar = id(new PhutilConsoleProgressBar()) @@ -363,7 +363,6 @@ private function buildAtomizerFutures(array $file_atomizers) { $this->getBookConfigPath(), $class, $chunk); - $future->setCWD($config_root); $futures[] = $future; diff --git a/src/applications/doorkeeper/phid/DoorkeeperExternalObjectPHIDType.php b/src/applications/doorkeeper/phid/DoorkeeperExternalObjectPHIDType.php index be9e32cf2a..1817e97b37 100644 --- a/src/applications/doorkeeper/phid/DoorkeeperExternalObjectPHIDType.php +++ b/src/applications/doorkeeper/phid/DoorkeeperExternalObjectPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDoorkeeperApplication'; + return PhabricatorDoorkeeperApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php b/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php index 2a33206831..c9a7c140eb 100644 --- a/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php +++ b/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php @@ -41,7 +41,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDoorkeeperApplication'; + return PhabricatorDoorkeeperApplication::class; } } diff --git a/src/applications/drydock/editor/DrydockBlueprintEditEngine.php b/src/applications/drydock/editor/DrydockBlueprintEditEngine.php index a3b6fdaf75..e01513f2b9 100644 --- a/src/applications/drydock/editor/DrydockBlueprintEditEngine.php +++ b/src/applications/drydock/editor/DrydockBlueprintEditEngine.php @@ -24,7 +24,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function setBlueprintImplementation( diff --git a/src/applications/drydock/editor/DrydockBlueprintEditor.php b/src/applications/drydock/editor/DrydockBlueprintEditor.php index aac3e7a020..f07907147c 100644 --- a/src/applications/drydock/editor/DrydockBlueprintEditor.php +++ b/src/applications/drydock/editor/DrydockBlueprintEditor.php @@ -4,7 +4,7 @@ final class DrydockBlueprintEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/drydock/phid/DrydockAuthorizationPHIDType.php b/src/applications/drydock/phid/DrydockAuthorizationPHIDType.php index 058ccff6a9..61495b73e4 100644 --- a/src/applications/drydock/phid/DrydockAuthorizationPHIDType.php +++ b/src/applications/drydock/phid/DrydockAuthorizationPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/drydock/phid/DrydockBlueprintPHIDType.php b/src/applications/drydock/phid/DrydockBlueprintPHIDType.php index ef286cf5f1..e363704454 100644 --- a/src/applications/drydock/phid/DrydockBlueprintPHIDType.php +++ b/src/applications/drydock/phid/DrydockBlueprintPHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/drydock/phid/DrydockLeasePHIDType.php b/src/applications/drydock/phid/DrydockLeasePHIDType.php index faa751b0f1..51d6a6e31a 100644 --- a/src/applications/drydock/phid/DrydockLeasePHIDType.php +++ b/src/applications/drydock/phid/DrydockLeasePHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/drydock/phid/DrydockRepositoryOperationPHIDType.php b/src/applications/drydock/phid/DrydockRepositoryOperationPHIDType.php index 6222552f84..35c78b5e03 100644 --- a/src/applications/drydock/phid/DrydockRepositoryOperationPHIDType.php +++ b/src/applications/drydock/phid/DrydockRepositoryOperationPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/drydock/phid/DrydockResourcePHIDType.php b/src/applications/drydock/phid/DrydockResourcePHIDType.php index a36647964d..8ca8718e6e 100644 --- a/src/applications/drydock/phid/DrydockResourcePHIDType.php +++ b/src/applications/drydock/phid/DrydockResourcePHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/drydock/query/DrydockAuthorizationSearchEngine.php b/src/applications/drydock/query/DrydockAuthorizationSearchEngine.php index 16737a9dfe..81e8c00ba0 100644 --- a/src/applications/drydock/query/DrydockAuthorizationSearchEngine.php +++ b/src/applications/drydock/query/DrydockAuthorizationSearchEngine.php @@ -19,7 +19,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/drydock/query/DrydockBlueprintSearchEngine.php b/src/applications/drydock/query/DrydockBlueprintSearchEngine.php index 3c80ca2700..244afcb986 100644 --- a/src/applications/drydock/query/DrydockBlueprintSearchEngine.php +++ b/src/applications/drydock/query/DrydockBlueprintSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function newQuery() { diff --git a/src/applications/drydock/query/DrydockBlueprintTransactionQuery.php b/src/applications/drydock/query/DrydockBlueprintTransactionQuery.php index f2d70a692e..d6652412b8 100644 --- a/src/applications/drydock/query/DrydockBlueprintTransactionQuery.php +++ b/src/applications/drydock/query/DrydockBlueprintTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new DrydockBlueprintTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDrydockApplication::class; + } + } diff --git a/src/applications/drydock/query/DrydockLeaseSearchEngine.php b/src/applications/drydock/query/DrydockLeaseSearchEngine.php index 7e89336fab..95f713c0d4 100644 --- a/src/applications/drydock/query/DrydockLeaseSearchEngine.php +++ b/src/applications/drydock/query/DrydockLeaseSearchEngine.php @@ -19,7 +19,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function newQuery() { diff --git a/src/applications/drydock/query/DrydockLogSearchEngine.php b/src/applications/drydock/query/DrydockLogSearchEngine.php index 3602714a60..553f4be486 100644 --- a/src/applications/drydock/query/DrydockLogSearchEngine.php +++ b/src/applications/drydock/query/DrydockLogSearchEngine.php @@ -54,7 +54,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function newQuery() { diff --git a/src/applications/drydock/query/DrydockQuery.php b/src/applications/drydock/query/DrydockQuery.php index 4f3f768d37..3db247b581 100644 --- a/src/applications/drydock/query/DrydockQuery.php +++ b/src/applications/drydock/query/DrydockQuery.php @@ -3,7 +3,7 @@ abstract class DrydockQuery extends PhabricatorCursorPagedPolicyAwareQuery { public function getQueryApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } } diff --git a/src/applications/drydock/query/DrydockRepositoryOperationSearchEngine.php b/src/applications/drydock/query/DrydockRepositoryOperationSearchEngine.php index eeb85329bf..3ab573dd8f 100644 --- a/src/applications/drydock/query/DrydockRepositoryOperationSearchEngine.php +++ b/src/applications/drydock/query/DrydockRepositoryOperationSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function newQuery() { diff --git a/src/applications/drydock/query/DrydockResourceSearchEngine.php b/src/applications/drydock/query/DrydockResourceSearchEngine.php index 9015f92408..d3fed0fd78 100644 --- a/src/applications/drydock/query/DrydockResourceSearchEngine.php +++ b/src/applications/drydock/query/DrydockResourceSearchEngine.php @@ -19,7 +19,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function newQuery() { diff --git a/src/applications/drydock/typeahead/DrydockBlueprintDatasource.php b/src/applications/drydock/typeahead/DrydockBlueprintDatasource.php index d36a2bc64b..7cdade856c 100644 --- a/src/applications/drydock/typeahead/DrydockBlueprintDatasource.php +++ b/src/applications/drydock/typeahead/DrydockBlueprintDatasource.php @@ -12,7 +12,7 @@ public function getBrowseTitle() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function loadResults() { diff --git a/src/applications/drydock/typeahead/DrydockLeaseDatasource.php b/src/applications/drydock/typeahead/DrydockLeaseDatasource.php index feb6bd9368..d21f99393e 100644 --- a/src/applications/drydock/typeahead/DrydockLeaseDatasource.php +++ b/src/applications/drydock/typeahead/DrydockLeaseDatasource.php @@ -8,7 +8,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function loadResults() { diff --git a/src/applications/drydock/typeahead/DrydockResourceDatasource.php b/src/applications/drydock/typeahead/DrydockResourceDatasource.php index 6e0b8f7116..b8fc292b5e 100644 --- a/src/applications/drydock/typeahead/DrydockResourceDatasource.php +++ b/src/applications/drydock/typeahead/DrydockResourceDatasource.php @@ -8,7 +8,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorDrydockApplication'; + return PhabricatorDrydockApplication::class; } public function loadResults() { diff --git a/src/applications/feed/query/PhabricatorFeedQuery.php b/src/applications/feed/query/PhabricatorFeedQuery.php index 8302af20c1..51d0d1544a 100644 --- a/src/applications/feed/query/PhabricatorFeedQuery.php +++ b/src/applications/feed/query/PhabricatorFeedQuery.php @@ -169,7 +169,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorFeedApplication'; + return PhabricatorFeedApplication::class; } } diff --git a/src/applications/feed/query/PhabricatorFeedSearchEngine.php b/src/applications/feed/query/PhabricatorFeedSearchEngine.php index d17c756524..58a00deda2 100644 --- a/src/applications/feed/query/PhabricatorFeedSearchEngine.php +++ b/src/applications/feed/query/PhabricatorFeedSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorFeedApplication'; + return PhabricatorFeedApplication::class; } public function newQuery() { diff --git a/src/applications/feed/query/PhabricatorFeedTransactionQuery.php b/src/applications/feed/query/PhabricatorFeedTransactionQuery.php index d0a9f53e35..93e90e2ef7 100644 --- a/src/applications/feed/query/PhabricatorFeedTransactionQuery.php +++ b/src/applications/feed/query/PhabricatorFeedTransactionQuery.php @@ -145,7 +145,7 @@ protected function loadPage() { } public function getQueryApplicationClass() { - return 'PhabricatorFeedApplication'; + return PhabricatorFeedApplication::class; } private function newTransactionQueries() { diff --git a/src/applications/feed/query/PhabricatorFeedTransactionSearchEngine.php b/src/applications/feed/query/PhabricatorFeedTransactionSearchEngine.php index 0cbbcd23b1..cc04532d56 100644 --- a/src/applications/feed/query/PhabricatorFeedTransactionSearchEngine.php +++ b/src/applications/feed/query/PhabricatorFeedTransactionSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorFeedApplication'; + return PhabricatorFeedApplication::class; } public function newQuery() { diff --git a/src/applications/files/application/PhabricatorFilesApplication.php b/src/applications/files/application/PhabricatorFilesApplication.php index e246d4c90c..4014a6ce62 100644 --- a/src/applications/files/application/PhabricatorFilesApplication.php +++ b/src/applications/files/application/PhabricatorFilesApplication.php @@ -67,6 +67,10 @@ protected function getCustomCapabilities() { ); } + public function getMonograms() { + return array('F'); + } + public function getRoutes() { return array( '/F(?P[1-9]\d*)(?:\$(?P\d+(?:-\d+)?))?' diff --git a/src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php b/src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php index 4f827f7603..f1406d3efd 100644 --- a/src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php +++ b/src/applications/files/builtin/PhabricatorFilesComposeAvatarBuiltinFile.php @@ -183,12 +183,21 @@ private function composeImage($color, $image, $border) { 'image/png'); } + /** + * Convert a color from RGBA to a value usable in PHP-GD. + * Each RGB color should be expressed as an integer from 0 to 255. + * The Alpha Channel should be expressed as a float from 0 to 1. + * @param array $rgba array( int Red, int Green, int Blue, float Alpha ) + * @return int + */ private static function rgba2gd($rgba) { + // When working with a truecolor image, we can use bitwise operations + // https://www.php.net/manual/en/function.imagecolorallocate.php#49168 $r = $rgba[0]; $g = $rgba[1]; $b = $rgba[2]; $a = $rgba[3]; - $a = (1 - $a) * 255; + $a = round(((1 - $a) * 255), 0); return ($a << 24) | ($r << 16) | ($g << 8) | $b; } diff --git a/src/applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php b/src/applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php index 0bb56490cc..7fd01ba810 100644 --- a/src/applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php +++ b/src/applications/files/builtin/PhabricatorFilesComposeIconBuiltinFile.php @@ -51,12 +51,14 @@ public static function getAllIcons() { $map = array(); $list = Filesystem::listDirectory($root, $include_hidden = false); foreach ($list as $file) { - $short = preg_replace('/\.png$/', '', $file); - - $map[$short] = array( - 'path' => $root.$file, - 'quip' => idx($quips, $short, $short), - ); + $count = 0; + $short = preg_replace('/\.png$/', '', $file, -1, $count); + if ($count === 1) { + $map[$short] = array( + 'path' => $root.$file, + 'quip' => idx($quips, $short, $short), + ); + } } return $map; @@ -96,6 +98,14 @@ public static function getAllColors() { } private function composeImage($color, $icon) { + // If we don't have the GD extension installed, just return a static + // default project image rather than trying to compose one. + if (!function_exists('imagecreatefromstring')) { + $root = dirname(phutil_get_library_root('phabricator')); + $default_path = $root.'/resources/builtin/profile.png'; + return Filesystem::readFile($default_path); + } + $color_map = self::getAllColors(); $color = idx($color_map, $color); if (!$color) { diff --git a/src/applications/files/conduit/FileUploadConduitAPIMethod.php b/src/applications/files/conduit/FileUploadConduitAPIMethod.php index 8b3b9faaaa..cafe842596 100644 --- a/src/applications/files/conduit/FileUploadConduitAPIMethod.php +++ b/src/applications/files/conduit/FileUploadConduitAPIMethod.php @@ -31,8 +31,10 @@ protected function execute(ConduitAPIRequest $request) { $view_policy = $request->getValue('viewPolicy'); $data = $request->getValue('data_base64'); + if (!phutil_nonempty_string($data)) { + throw new Exception(pht('Field "data_base64" must be non-empty.')); + } $data = $this->decodeBase64($data); - $params = array( 'authorPHID' => $viewer->getPHID(), 'canCDN' => $can_cdn, diff --git a/src/applications/files/config/PhabricatorFilesConfigOptions.php b/src/applications/files/config/PhabricatorFilesConfigOptions.php index 7ed96a412b..67d123e2cb 100644 --- a/src/applications/files/config/PhabricatorFilesConfigOptions.php +++ b/src/applications/files/config/PhabricatorFilesConfigOptions.php @@ -134,9 +134,11 @@ public function getOptions() { ->setDescription( pht( "Configure which uploaded file types may be viewed directly ". - "in the browser. Other file types will be downloaded instead ". - "of displayed. This is mainly a usability consideration, since ". - "browsers tend to freak out when viewing very large binary files.". + "in the browser. Other types will be downloaded instead of ". + "displayed. This is a usability and security consideration, ". + "since browsers tend to freak out when viewing very large ". + "binary files, and some types may be vulnerable to XSS attacks ". + "when viewed in a browser.". "\n\n". "The keys in this map are viewable MIME types; the values are ". "the MIME types they are delivered as when they are viewed in ". diff --git a/src/applications/files/controller/PhabricatorFileDataController.php b/src/applications/files/controller/PhabricatorFileDataController.php index 8189b30a21..b292b6f4b8 100644 --- a/src/applications/files/controller/PhabricatorFileDataController.php +++ b/src/applications/files/controller/PhabricatorFileDataController.php @@ -29,7 +29,7 @@ public function handleRequest(AphrontRequest $request) { $request_kind = $request->getURIData('kind'); $is_download = ($request_kind === 'download'); - if (!strlen($alt) || $main_domain == $alt_domain) { + if (!phutil_nonempty_string($alt) || $main_domain == $alt_domain) { // No alternate domain. $should_redirect = false; $is_alternate_domain = false; @@ -69,7 +69,7 @@ public function handleRequest(AphrontRequest $request) { // an initial request for bytes 0-1 of the audio file, and things go south // if we can't respond with a 206 Partial Content. $range = $request->getHTTPHeader('range'); - if (strlen($range)) { + if (phutil_nonempty_string($range)) { list($begin, $end) = $response->parseHTTPRange($range); } diff --git a/src/applications/files/controller/PhabricatorFileLightboxController.php b/src/applications/files/controller/PhabricatorFileLightboxController.php index 59a826dd42..28123d9002 100644 --- a/src/applications/files/controller/PhabricatorFileLightboxController.php +++ b/src/applications/files/controller/PhabricatorFileLightboxController.php @@ -20,7 +20,7 @@ public function handleRequest(AphrontRequest $request) { return new Aphront404Response(); } - if (strlen($comment)) { + if (phutil_nonempty_string($comment)) { $xactions = array(); $xactions[] = id(new PhabricatorFileTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) diff --git a/src/applications/files/controller/PhabricatorFileViewController.php b/src/applications/files/controller/PhabricatorFileViewController.php index 389fc66247..2dea0a51ce 100644 --- a/src/applications/files/controller/PhabricatorFileViewController.php +++ b/src/applications/files/controller/PhabricatorFileViewController.php @@ -311,7 +311,7 @@ private function buildPropertyViews( $file->getStorageHandle()); $custom_alt = $file->getCustomAltText(); - if (strlen($custom_alt)) { + if (phutil_nonempty_string($custom_alt)) { $finfo->addProperty(pht('Custom Alt Text'), $custom_alt); } diff --git a/src/applications/files/document/PhabricatorPDFDocumentEngine.php b/src/applications/files/document/PhabricatorPDFDocumentEngine.php index 1e85bd4ae5..bc6433290a 100644 --- a/src/applications/files/document/PhabricatorPDFDocumentEngine.php +++ b/src/applications/files/document/PhabricatorPDFDocumentEngine.php @@ -14,14 +14,16 @@ protected function getDocumentIconIcon(PhabricatorDocumentRef $ref) { } protected function canRenderDocumentType(PhabricatorDocumentRef $ref) { - // Since we just render a link to the document anyway, we don't need to - // check anything fancy in config to see if the MIME type is actually - // viewable. + $viewable_types = PhabricatorEnv::getEnvConfig('files.viewable-mime-types'); + $viewable_types = array_keys($viewable_types); - return $ref->hasAnyMimeType( - array( - 'application/pdf', - )); + $pdf_types = array( + 'application/pdf', + ); + + return + $ref->hasAnyMimeType($viewable_types) && + $ref->hasAnyMimeType($pdf_types); } protected function newDocumentContent(PhabricatorDocumentRef $ref) { diff --git a/src/applications/files/document/PhabricatorTextDocumentEngine.php b/src/applications/files/document/PhabricatorTextDocumentEngine.php index 2fce98baf1..8f04a56751 100644 --- a/src/applications/files/document/PhabricatorTextDocumentEngine.php +++ b/src/applications/files/document/PhabricatorTextDocumentEngine.php @@ -70,10 +70,16 @@ protected function loadTextData(PhabricatorDocumentRef $ref) { $encoding = $this->getEncodingConfiguration(); if ($encoding !== null) { if (function_exists('mb_convert_encoding')) { - $content = mb_convert_encoding($content, 'UTF-8', $encoding); - $this->encodingMessage = pht( - 'This document was converted from %s to UTF8 for display.', - $encoding); + try { + $content = mb_convert_encoding($content, 'UTF-8', $encoding); + $this->encodingMessage = pht( + 'This document was converted from %s to UTF8 for display.', + $encoding); + } catch (Throwable $ex) { + $this->encodingMessage = pht( + 'Unable to convert from requested encoding %s to UTF8.', + $encoding); + } } else { $this->encodingMessage = pht( 'Unable to perform text encoding conversion: mbstring extension '. diff --git a/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php b/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php index 5a4cb77340..f43689bbba 100644 --- a/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php +++ b/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php @@ -60,12 +60,12 @@ final public function newDocumentView(PhabricatorDocumentRef $ref) { } $encode_setting = $request->getStr('encode'); - if (strlen($encode_setting)) { + if (phutil_nonempty_string($encode_setting)) { $engine->setEncodingConfiguration($encode_setting); } $highlight_setting = $request->getStr('highlight'); - if (strlen($highlight_setting)) { + if (phutil_nonempty_string($highlight_setting)) { $engine->setHighlightingConfiguration($highlight_setting); } diff --git a/src/applications/files/editor/PhabricatorFileEditEngine.php b/src/applications/files/editor/PhabricatorFileEditEngine.php index a6a39852cb..c5ff926418 100644 --- a/src/applications/files/editor/PhabricatorFileEditEngine.php +++ b/src/applications/files/editor/PhabricatorFileEditEngine.php @@ -28,7 +28,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } protected function newEditableObject() { diff --git a/src/applications/files/editor/PhabricatorFileEditor.php b/src/applications/files/editor/PhabricatorFileEditor.php index 91d168e68d..8892f80174 100644 --- a/src/applications/files/editor/PhabricatorFileEditor.php +++ b/src/applications/files/editor/PhabricatorFileEditor.php @@ -4,7 +4,7 @@ final class PhabricatorFileEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/files/engine/PhabricatorS3FileStorageEngine.php b/src/applications/files/engine/PhabricatorS3FileStorageEngine.php index 443a5a9ddc..95cdfc737b 100644 --- a/src/applications/files/engine/PhabricatorS3FileStorageEngine.php +++ b/src/applications/files/engine/PhabricatorS3FileStorageEngine.php @@ -31,11 +31,11 @@ public function canWriteFiles() { $endpoint = PhabricatorEnv::getEnvConfig('amazon-s3.endpoint'); $region = PhabricatorEnv::getEnvConfig('amazon-s3.region'); - return (strlen($bucket) && - strlen($access_key) && - strlen($secret_key) && - strlen($endpoint) && - strlen($region)); + return phutil_nonempty_string($bucket) && + phutil_nonempty_string($access_key) && + phutil_nonempty_string($secret_key) && + phutil_nonempty_string($endpoint) && + phutil_nonempty_string($region); } @@ -57,7 +57,7 @@ public function writeFile($data, array $params) { $parts[] = 'phabricator'; $instance_name = PhabricatorEnv::getEnvConfig('cluster.instance'); - if (strlen($instance_name)) { + if (phutil_nonempty_string($instance_name)) { $parts[] = $instance_name; } diff --git a/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php b/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php index 9f37bdcaab..a81cb33726 100644 --- a/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php +++ b/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php @@ -197,7 +197,7 @@ private function renderImageFile( $alt = $options['alt']; } - if (!strlen($alt)) { + if (!phutil_nonempty_string($alt)) { $alt = $file->getAltText(); } @@ -346,10 +346,11 @@ private function renderFileLink( } private function parseDimension($string) { - $string = trim($string); - - if (preg_match('/^(?:\d*\\.)?\d+%?$/', $string)) { - return $string; + if ($string !== null) { + $string = trim($string); + if (preg_match('/^(?:\d*\\.)?\d+%?$/', $string)) { + return $string; + } } return null; diff --git a/src/applications/files/phid/PhabricatorFileFilePHIDType.php b/src/applications/files/phid/PhabricatorFileFilePHIDType.php index c583595c4d..70a58834c4 100644 --- a/src/applications/files/phid/PhabricatorFileFilePHIDType.php +++ b/src/applications/files/phid/PhabricatorFileFilePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/files/query/PhabricatorFileAttachmentQuery.php b/src/applications/files/query/PhabricatorFileAttachmentQuery.php index 5fb37dc32e..50aea1a839 100644 --- a/src/applications/files/query/PhabricatorFileAttachmentQuery.php +++ b/src/applications/files/query/PhabricatorFileAttachmentQuery.php @@ -125,7 +125,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } } diff --git a/src/applications/files/query/PhabricatorFileChunkQuery.php b/src/applications/files/query/PhabricatorFileChunkQuery.php index 4398860569..f2c104dc7a 100644 --- a/src/applications/files/query/PhabricatorFileChunkQuery.php +++ b/src/applications/files/query/PhabricatorFileChunkQuery.php @@ -128,7 +128,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } } diff --git a/src/applications/files/query/PhabricatorFileQuery.php b/src/applications/files/query/PhabricatorFileQuery.php index 80e511b1e2..e712d533b0 100644 --- a/src/applications/files/query/PhabricatorFileQuery.php +++ b/src/applications/files/query/PhabricatorFileQuery.php @@ -540,7 +540,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } } diff --git a/src/applications/files/query/PhabricatorFileSearchEngine.php b/src/applications/files/query/PhabricatorFileSearchEngine.php index 59810c830c..c2d36acfd6 100644 --- a/src/applications/files/query/PhabricatorFileSearchEngine.php +++ b/src/applications/files/query/PhabricatorFileSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/files/query/PhabricatorFileTransactionQuery.php b/src/applications/files/query/PhabricatorFileTransactionQuery.php index 72bca16d60..33360b04b5 100644 --- a/src/applications/files/query/PhabricatorFileTransactionQuery.php +++ b/src/applications/files/query/PhabricatorFileTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorFileTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorFilesApplication::class; + } + } diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php index cc1b701dcd..53af38a33b 100644 --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -287,7 +287,7 @@ public static function newChunkedFile( // NOTE: Once we receive the first chunk, we'll detect its MIME type and // update the parent file if a MIME type hasn't been provided. This matters // for large media files like video. - $mime_type = idx($params, 'mime-type'); + $mime_type = idx($params, 'mime-type', ''); if (!strlen($mime_type)) { $file->setMimeType('application/octet-stream'); } @@ -856,7 +856,7 @@ public function getCDNURI($request_kind) { // instance identity in the path allows us to distinguish between requests // originating from different instances but served through the same CDN. $instance = PhabricatorEnv::getEnvConfig('cluster.instance'); - if (strlen($instance)) { + if (phutil_nonempty_string($instance)) { $parts[] = '@'.$instance; } @@ -903,7 +903,7 @@ private function getTransformedURI($transform) { $parts[] = 'xform'; $instance = PhabricatorEnv::getEnvConfig('cluster.instance'); - if (strlen($instance)) { + if (phutil_nonempty_string($instance)) { $parts[] = '@'.$instance; } @@ -971,10 +971,13 @@ public function isTransformableImage() { // warns you if you don't have complete support. $matches = null; - $ok = preg_match( - '@^image/(gif|png|jpe?g)@', - $this->getViewableMimeType(), - $matches); + $ok = false; + if ($this->getViewableMimeType() !== null) { + $ok = preg_match( + '@^image/(gif|png|jpe?g)@', + $this->getViewableMimeType(), + $matches); + } if (!$ok) { return false; } @@ -1278,7 +1281,7 @@ public function getImageWidth() { public function getAltText() { $alt = $this->getCustomAltText(); - if (strlen($alt)) { + if (phutil_nonempty_string($alt)) { return $alt; } diff --git a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php index 21cb2daf11..21ad929c3f 100644 --- a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php +++ b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php @@ -532,4 +532,11 @@ public function testFileTransformDelete() { $this->assertEqual(array(), $alternate_c); } + public function testNewChunkedFile() { + $engine = new PhabricatorTestStorageEngine(); + $file = PhabricatorFile::newChunkedFile($engine, 10, []); + $this->assertTrue($file instanceof PhabricatorFile, + pht('newChunkedFile returns a PhabricatorFile')); + } + } diff --git a/src/applications/files/transform/PhabricatorFileImageTransform.php b/src/applications/files/transform/PhabricatorFileImageTransform.php index 468eae0e03..98ffcdd706 100644 --- a/src/applications/files/transform/PhabricatorFileImageTransform.php +++ b/src/applications/files/transform/PhabricatorFileImageTransform.php @@ -137,8 +137,12 @@ protected function applyImagemagick(array $argv) { protected function newFileFromData($data) { if ($this->file) { $name = $this->file->getName(); + $inherit_properties = array( + 'viewPolicy' => $this->file->getViewPolicy(), + ); } else { $name = 'default.png'; + $inherit_properties = array(); } $defaults = array( @@ -146,7 +150,7 @@ protected function newFileFromData($data) { 'name' => $this->getTransformKey().'-'.$name, ); - $properties = $this->getFileProperties() + $defaults; + $properties = $this->getFileProperties() + $inherit_properties + $defaults; return PhabricatorFile::newFromFileData($data, $properties); } diff --git a/src/applications/files/transform/PhabricatorFileThumbnailTransform.php b/src/applications/files/transform/PhabricatorFileThumbnailTransform.php index 2c61743078..6cf3914556 100644 --- a/src/applications/files/transform/PhabricatorFileThumbnailTransform.php +++ b/src/applications/files/transform/PhabricatorFileThumbnailTransform.php @@ -58,7 +58,7 @@ protected function getFileProperties() { public function generateTransforms() { return array( id(new PhabricatorFileThumbnailTransform()) - ->setName(pht("Profile (400px \xC3\x97 400px)")) + ->setName(pht("Profile (400px \xC3\x97 400px) (Image will be Public)")) ->setKey(self::TRANSFORM_PROFILE) ->setDimensions(400, 400) ->setScaleUp(true), diff --git a/src/applications/files/typeahead/PhabricatorIconDatasource.php b/src/applications/files/typeahead/PhabricatorIconDatasource.php index 63b554a9b7..e904742e26 100644 --- a/src/applications/files/typeahead/PhabricatorIconDatasource.php +++ b/src/applications/files/typeahead/PhabricatorIconDatasource.php @@ -11,7 +11,7 @@ public function getBrowseTitle() { } public function getDatasourceApplicationClass() { - return 'PhabricatorFilesApplication'; + return PhabricatorFilesApplication::class; } public function loadResults() { diff --git a/src/applications/files/view/PhabricatorGlobalUploadTargetView.php b/src/applications/files/view/PhabricatorGlobalUploadTargetView.php index 76aacbe36f..cd65c9a197 100644 --- a/src/applications/files/view/PhabricatorGlobalUploadTargetView.php +++ b/src/applications/files/view/PhabricatorGlobalUploadTargetView.php @@ -67,7 +67,7 @@ public function render() { require_celerity_resource('global-drag-and-drop-css'); $hint_text = $this->getHintText(); - if (!strlen($hint_text)) { + if (!phutil_nonempty_string($hint_text)) { $hint_text = "\xE2\x87\xAA ".pht('Drop Files to Upload'); } diff --git a/src/applications/files/xaction/PhabricatorFileAltTextTransaction.php b/src/applications/files/xaction/PhabricatorFileAltTextTransaction.php index dcf3c6f76f..063a599170 100644 --- a/src/applications/files/xaction/PhabricatorFileAltTextTransaction.php +++ b/src/applications/files/xaction/PhabricatorFileAltTextTransaction.php @@ -27,12 +27,12 @@ public function getTitle() { $old_value = $this->getOldValue(); $new_value = $this->getNewValue(); - if (!strlen($old_value)) { + if (!phutil_nonempty_string($old_value)) { return pht( '%s set the alternate text for this file to %s.', $this->renderAuthor(), $this->renderNewValue()); - } else if (!strlen($new_value)) { + } else if (!phutil_nonempty_string($new_value)) { return pht( '%s removed the alternate text for this file (was %s).', $this->renderAuthor(), @@ -50,13 +50,13 @@ public function getTitleForFeed() { $old_value = $this->getOldValue(); $new_value = $this->getNewValue(); - if (!strlen($old_value)) { + if (!phutil_nonempty_string($old_value)) { return pht( '%s set the alternate text for %s to %s.', $this->renderAuthor(), $this->renderObject(), $this->renderNewValue()); - } else if (!strlen($new_value)) { + } else if (!phutil_nonempty_string($new_value)) { return pht( '%s removed the alternate text for %s (was %s).', $this->renderAuthor(), diff --git a/src/applications/flag/query/PhabricatorFlagQuery.php b/src/applications/flag/query/PhabricatorFlagQuery.php index c6c905465d..07b3e09d10 100644 --- a/src/applications/flag/query/PhabricatorFlagQuery.php +++ b/src/applications/flag/query/PhabricatorFlagQuery.php @@ -173,7 +173,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorFlagsApplication'; + return PhabricatorFlagsApplication::class; } } diff --git a/src/applications/flag/query/PhabricatorFlagSearchEngine.php b/src/applications/flag/query/PhabricatorFlagSearchEngine.php index a433e4241c..bc571b6bf4 100644 --- a/src/applications/flag/query/PhabricatorFlagSearchEngine.php +++ b/src/applications/flag/query/PhabricatorFlagSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorFlagsApplication'; + return PhabricatorFlagsApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/fund/application/PhabricatorFundApplication.php b/src/applications/fund/application/PhabricatorFundApplication.php index 58ce4f8922..8fe832473e 100644 --- a/src/applications/fund/application/PhabricatorFundApplication.php +++ b/src/applications/fund/application/PhabricatorFundApplication.php @@ -30,12 +30,20 @@ public function isPrototype() { return true; } + public function isDeprecated() { + return true; + } + public function getRemarkupRules() { return array( new FundInitiativeRemarkupRule(), ); } + public function getMonograms() { + return array('I'); + } + public function getRoutes() { return array( '/I(?P[1-9]\d*)' => 'FundInitiativeViewController', diff --git a/src/applications/fund/editor/FundBackerEditor.php b/src/applications/fund/editor/FundBackerEditor.php index 403853863b..0736f3339b 100644 --- a/src/applications/fund/editor/FundBackerEditor.php +++ b/src/applications/fund/editor/FundBackerEditor.php @@ -4,7 +4,7 @@ final class FundBackerEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/fund/editor/FundInitiativeEditEngine.php b/src/applications/fund/editor/FundInitiativeEditEngine.php index 824f4e009f..a8b90773ad 100644 --- a/src/applications/fund/editor/FundInitiativeEditEngine.php +++ b/src/applications/fund/editor/FundInitiativeEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/fund/editor/FundInitiativeEditor.php b/src/applications/fund/editor/FundInitiativeEditor.php index 9175156ffd..139d6073a2 100644 --- a/src/applications/fund/editor/FundInitiativeEditor.php +++ b/src/applications/fund/editor/FundInitiativeEditor.php @@ -4,7 +4,7 @@ final class FundInitiativeEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/fund/phid/FundBackerPHIDType.php b/src/applications/fund/phid/FundBackerPHIDType.php index 3feff6364a..0c41b6f697 100644 --- a/src/applications/fund/phid/FundBackerPHIDType.php +++ b/src/applications/fund/phid/FundBackerPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/fund/phid/FundInitiativePHIDType.php b/src/applications/fund/phid/FundInitiativePHIDType.php index 3399dc0a3f..d708139cbe 100644 --- a/src/applications/fund/phid/FundInitiativePHIDType.php +++ b/src/applications/fund/phid/FundInitiativePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/fund/query/FundBackerQuery.php b/src/applications/fund/query/FundBackerQuery.php index 2d6e13dfd9..e580e4339d 100644 --- a/src/applications/fund/query/FundBackerQuery.php +++ b/src/applications/fund/query/FundBackerQuery.php @@ -112,7 +112,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } } diff --git a/src/applications/fund/query/FundBackerSearchEngine.php b/src/applications/fund/query/FundBackerSearchEngine.php index ba4b3bc39e..8d4872f9e5 100644 --- a/src/applications/fund/query/FundBackerSearchEngine.php +++ b/src/applications/fund/query/FundBackerSearchEngine.php @@ -19,7 +19,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/fund/query/FundBackerTransactionQuery.php b/src/applications/fund/query/FundBackerTransactionQuery.php index 9ba1506add..15b493ab39 100644 --- a/src/applications/fund/query/FundBackerTransactionQuery.php +++ b/src/applications/fund/query/FundBackerTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new FundBackerTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorFundApplication::class; + } + } diff --git a/src/applications/fund/query/FundInitiativeQuery.php b/src/applications/fund/query/FundInitiativeQuery.php index 4b18597873..47d2969220 100644 --- a/src/applications/fund/query/FundInitiativeQuery.php +++ b/src/applications/fund/query/FundInitiativeQuery.php @@ -67,7 +67,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/fund/query/FundInitiativeSearchEngine.php b/src/applications/fund/query/FundInitiativeSearchEngine.php index e075fe5063..2773d2a150 100644 --- a/src/applications/fund/query/FundInitiativeSearchEngine.php +++ b/src/applications/fund/query/FundInitiativeSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorFundApplication'; + return PhabricatorFundApplication::class; } public function newQuery() { diff --git a/src/applications/fund/query/FundInitiativeTransactionQuery.php b/src/applications/fund/query/FundInitiativeTransactionQuery.php index 3c178ec645..a35b7d92c1 100644 --- a/src/applications/fund/query/FundInitiativeTransactionQuery.php +++ b/src/applications/fund/query/FundInitiativeTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new FundInitiativeTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorFundApplication::class; + } + } diff --git a/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php b/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php index 57f04e550b..7de2a658c2 100644 --- a/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php +++ b/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php @@ -51,6 +51,10 @@ public function getHelpDocumentationArticles(PhabricatorUser $viewer) { ); } + public function getMonograms() { + return array('B'); + } + public function getRoutes() { return array( '/B(?P[1-9]\d*)' => 'HarbormasterBuildableViewController', @@ -94,10 +98,7 @@ public function getRoutes() { 'lint/' => array( '(?P\d+)/' => 'HarbormasterLintMessagesController', ), - 'hook/' => array( - 'circleci/' => 'HarbormasterCircleCIHookController', - 'buildkite/' => 'HarbormasterBuildkiteHookController', - ), + 'hook/(?P[^/]+)/' => 'HarbormasterHookController', 'log/' => array( 'view/(?P\d+)/(?:\$(?P\d+(?:-\d+)?))?' => 'HarbormasterBuildLogViewController', diff --git a/src/applications/harbormaster/controller/HarbormasterHookController.php b/src/applications/harbormaster/controller/HarbormasterHookController.php new file mode 100644 index 0000000000..8eb442bc41 --- /dev/null +++ b/src/applications/harbormaster/controller/HarbormasterHookController.php @@ -0,0 +1,21 @@ +getURIData('handler'); + $handler = HarbormasterHookHandler::getHandler($name); + + if (!$handler) { + throw new Exception(pht('No handler found for %s', $name)); + } + + return $handler->handleRequest($request); + } + +} diff --git a/src/applications/harbormaster/controller/HarbormasterStepEditController.php b/src/applications/harbormaster/controller/HarbormasterStepEditController.php index 8cd91bb5a4..a420d02627 100644 --- a/src/applications/harbormaster/controller/HarbormasterStepEditController.php +++ b/src/applications/harbormaster/controller/HarbormasterStepEditController.php @@ -139,7 +139,7 @@ public function handleRequest(AphrontRequest $request) { ->setUser($viewer); $instructions = $implementation->getEditInstructions(); - if (strlen($instructions)) { + if (phutil_nonempty_string($instructions)) { $form->appendRemarkupInstructions($instructions); } diff --git a/src/applications/harbormaster/editor/HarbormasterBuildEditEngine.php b/src/applications/harbormaster/editor/HarbormasterBuildEditEngine.php index 9f55965d9c..fcef490256 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildEditEngine.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function newEditableObject() { diff --git a/src/applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php b/src/applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php index c0fa80d71b..886cae3533 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildPlanEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function newEditableObject() { @@ -103,7 +103,7 @@ protected function buildCustomEditFields($object) { $key); $behavior_option = $object->getPlanProperty($storage_key); - if (!strlen($behavior_option)) { + if (!phutil_nonempty_string($behavior_option)) { $behavior_option = $behavior->getPlanOption($object)->getKey(); } diff --git a/src/applications/harbormaster/editor/HarbormasterBuildPlanEditor.php b/src/applications/harbormaster/editor/HarbormasterBuildPlanEditor.php index 1b340b6524..bf9d938bc8 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildPlanEditor.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildPlanEditor.php @@ -4,7 +4,7 @@ final class HarbormasterBuildPlanEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/harbormaster/editor/HarbormasterBuildStepEditEngine.php b/src/applications/harbormaster/editor/HarbormasterBuildStepEditEngine.php index d54832cad1..a93478a086 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildStepEditEngine.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildStepEditEngine.php @@ -37,7 +37,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function newEditableObject() { diff --git a/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php b/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php index 8206edee4d..dec2a70a55 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php @@ -4,7 +4,7 @@ final class HarbormasterBuildStepEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php b/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php index 146caf9f2c..1746bf97b5 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php @@ -4,7 +4,7 @@ final class HarbormasterBuildTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/harbormaster/editor/HarbormasterBuildableEditEngine.php b/src/applications/harbormaster/editor/HarbormasterBuildableEditEngine.php index bd60eeb53b..9e16038bc7 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildableEditEngine.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildableEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } protected function newEditableObject() { diff --git a/src/applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php b/src/applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php index 895c89f858..df8b20864a 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php @@ -4,7 +4,7 @@ final class HarbormasterBuildableTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/harbormaster/integration/HarbormasterHookHandler.php b/src/applications/harbormaster/integration/HarbormasterHookHandler.php new file mode 100644 index 0000000000..5b4aafa612 --- /dev/null +++ b/src/applications/harbormaster/integration/HarbormasterHookHandler.php @@ -0,0 +1,27 @@ +setAncestorClass(__CLASS__) + ->setUniqueMethod('getName') + ->execute(); + } + + public static function getHandler($handler) { + $base = idx(self::getHandlers(), $handler); + + if ($base) { + return (clone $base); + } + + return null; + } + + abstract public function getName(); + + abstract public function handleRequest(AphrontRequest $request); + +} diff --git a/src/applications/harbormaster/step/HarbormasterBuildkiteBuildStepImplementation.php b/src/applications/harbormaster/integration/buildkite/HarbormasterBuildkiteBuildStepImplementation.php similarity index 100% rename from src/applications/harbormaster/step/HarbormasterBuildkiteBuildStepImplementation.php rename to src/applications/harbormaster/integration/buildkite/HarbormasterBuildkiteBuildStepImplementation.php diff --git a/src/applications/harbormaster/controller/HarbormasterBuildkiteHookController.php b/src/applications/harbormaster/integration/buildkite/HarbormasterBuildkiteHookHandler.php similarity index 95% rename from src/applications/harbormaster/controller/HarbormasterBuildkiteHookController.php rename to src/applications/harbormaster/integration/buildkite/HarbormasterBuildkiteHookHandler.php index df50ea9126..2a29ceffa0 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildkiteHookController.php +++ b/src/applications/harbormaster/integration/buildkite/HarbormasterBuildkiteHookHandler.php @@ -1,10 +1,10 @@ setDuration((float)idx($dict, 'duration')); $path = idx($dict, 'path'); - if (strlen($path)) { + if ($path !== null && strlen($path)) { $obj->setProperty('path', $path); } diff --git a/src/applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php b/src/applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php index 08b0be774e..8453c56239 100644 --- a/src/applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php +++ b/src/applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php @@ -17,7 +17,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function loadResults() { diff --git a/src/applications/harbormaster/typeahead/HarbormasterBuildPlanDatasource.php b/src/applications/harbormaster/typeahead/HarbormasterBuildPlanDatasource.php index cf5b815c57..ff831a74c5 100644 --- a/src/applications/harbormaster/typeahead/HarbormasterBuildPlanDatasource.php +++ b/src/applications/harbormaster/typeahead/HarbormasterBuildPlanDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function loadResults() { diff --git a/src/applications/harbormaster/typeahead/HarbormasterBuildStatusDatasource.php b/src/applications/harbormaster/typeahead/HarbormasterBuildStatusDatasource.php index 6cc11bd0c2..86c9da35e0 100644 --- a/src/applications/harbormaster/typeahead/HarbormasterBuildStatusDatasource.php +++ b/src/applications/harbormaster/typeahead/HarbormasterBuildStatusDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorHarbormasterApplication'; + return PhabricatorHarbormasterApplication::class; } public function loadResults() { diff --git a/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageResumeTransaction.php b/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageResumeTransaction.php index 1bd7094799..65f27ae374 100644 --- a/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageResumeTransaction.php +++ b/src/applications/harbormaster/xaction/build/HarbormasterBuildMessageResumeTransaction.php @@ -99,7 +99,7 @@ protected function newCanSendMessageAssertion( throw new HarbormasterMessageException( pht('Unable to Resume Build'), pht( - 'You can not resume this build beacuse it is already resuming.')); + 'You can not resume this build because it is already resuming.')); } if ($build->isRestarting()) { diff --git a/src/applications/herald/adapter/HeraldRuleAdapter.php b/src/applications/herald/adapter/HeraldRuleAdapter.php index 8ed851a1a9..d5d1423ae7 100644 --- a/src/applications/herald/adapter/HeraldRuleAdapter.php +++ b/src/applications/herald/adapter/HeraldRuleAdapter.php @@ -9,7 +9,7 @@ protected function newObject() { } public function getAdapterApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/herald/application/PhabricatorHeraldApplication.php b/src/applications/herald/application/PhabricatorHeraldApplication.php index 0de1c02737..7808820818 100644 --- a/src/applications/herald/application/PhabricatorHeraldApplication.php +++ b/src/applications/herald/application/PhabricatorHeraldApplication.php @@ -49,6 +49,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('H'); + } + public function getRoutes() { return array( '/H(?P[1-9]\d*)' => 'HeraldRuleViewController', diff --git a/src/applications/herald/controller/HeraldNewController.php b/src/applications/herald/controller/HeraldNewController.php index 09b7e62c6e..93d4503754 100644 --- a/src/applications/herald/controller/HeraldNewController.php +++ b/src/applications/herald/controller/HeraldNewController.php @@ -333,8 +333,8 @@ private function newTargetForm( $cancel_params = $params; unset($cancel_params['type']); - $cancel_uri = $this->getApplicationURI('new/'); - $cancel_uri = new PhutilURI($cancel_uri, $params); + $cancel_uri = $this->getApplicationURI('create/'); + $cancel_uri = new PhutilURI($cancel_uri, $cancel_params); $form->appendChild( id(new AphrontFormSubmitControl()) diff --git a/src/applications/herald/controller/HeraldTranscriptController.php b/src/applications/herald/controller/HeraldTranscriptController.php index dfc0dfc0b1..4bc569538f 100644 --- a/src/applications/herald/controller/HeraldTranscriptController.php +++ b/src/applications/herald/controller/HeraldTranscriptController.php @@ -717,8 +717,8 @@ private function newSideNavView( ->setName(pht('Field Values')) ->setIcon('fa-file-text-o'); - $xaction_phids = $this->getTranscriptTransactionPHIDs($xscript); - $has_xactions = (bool)$xaction_phids; + $has_xactions = $xscript->getObjectTranscript() + && $this->getTranscriptTransactionPHIDs($xscript); $nav->newLink('xactions') ->setName(pht('Transactions')) diff --git a/src/applications/herald/editor/HeraldRuleEditor.php b/src/applications/herald/editor/HeraldRuleEditor.php index ea099cc6f3..01f42969f1 100644 --- a/src/applications/herald/editor/HeraldRuleEditor.php +++ b/src/applications/herald/editor/HeraldRuleEditor.php @@ -4,7 +4,7 @@ final class HeraldRuleEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/herald/editor/HeraldWebhookEditEngine.php b/src/applications/herald/editor/HeraldWebhookEditEngine.php index 5bca0af542..527aafe110 100644 --- a/src/applications/herald/editor/HeraldWebhookEditEngine.php +++ b/src/applications/herald/editor/HeraldWebhookEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } protected function newEditableObject() { diff --git a/src/applications/herald/editor/HeraldWebhookEditor.php b/src/applications/herald/editor/HeraldWebhookEditor.php index 1f138e5028..4cbb57484b 100644 --- a/src/applications/herald/editor/HeraldWebhookEditor.php +++ b/src/applications/herald/editor/HeraldWebhookEditor.php @@ -4,7 +4,7 @@ final class HeraldWebhookEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/herald/phid/HeraldRulePHIDType.php b/src/applications/herald/phid/HeraldRulePHIDType.php index a70f89b406..767b242495 100644 --- a/src/applications/herald/phid/HeraldRulePHIDType.php +++ b/src/applications/herald/phid/HeraldRulePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/herald/phid/HeraldTranscriptPHIDType.php b/src/applications/herald/phid/HeraldTranscriptPHIDType.php index 8ba3a0254c..a05c293a44 100644 --- a/src/applications/herald/phid/HeraldTranscriptPHIDType.php +++ b/src/applications/herald/phid/HeraldTranscriptPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } protected function buildQueryForObjects( @@ -35,7 +35,7 @@ public function loadHandles( $id = $xscript->getID(); $handle->setName(pht('Transcript %s', $id)); - $handle->setURI("/herald/transcript/${id}/"); + $handle->setURI("/herald/transcript/$id/"); } } diff --git a/src/applications/herald/phid/HeraldWebhookPHIDType.php b/src/applications/herald/phid/HeraldWebhookPHIDType.php index bf16e22b1a..e88945b13d 100644 --- a/src/applications/herald/phid/HeraldWebhookPHIDType.php +++ b/src/applications/herald/phid/HeraldWebhookPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/herald/phid/HeraldWebhookRequestPHIDType.php b/src/applications/herald/phid/HeraldWebhookRequestPHIDType.php index bcf5afb0d3..034bfa9b66 100644 --- a/src/applications/herald/phid/HeraldWebhookRequestPHIDType.php +++ b/src/applications/herald/phid/HeraldWebhookRequestPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/herald/query/HeraldRuleQuery.php b/src/applications/herald/query/HeraldRuleQuery.php index e104c44122..f5eedd8177 100644 --- a/src/applications/herald/query/HeraldRuleQuery.php +++ b/src/applications/herald/query/HeraldRuleQuery.php @@ -327,7 +327,7 @@ private function validateRuleAuthors(array $rules) { } public function getQueryApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/herald/query/HeraldRuleSearchEngine.php b/src/applications/herald/query/HeraldRuleSearchEngine.php index 95e3079717..b4e10035a3 100644 --- a/src/applications/herald/query/HeraldRuleSearchEngine.php +++ b/src/applications/herald/query/HeraldRuleSearchEngine.php @@ -7,7 +7,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function newQuery() { diff --git a/src/applications/herald/query/HeraldTransactionQuery.php b/src/applications/herald/query/HeraldTransactionQuery.php index ac33da2070..97fff6407d 100644 --- a/src/applications/herald/query/HeraldTransactionQuery.php +++ b/src/applications/herald/query/HeraldTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new HeraldRuleTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorHeraldApplication::class; + } + } diff --git a/src/applications/herald/query/HeraldTranscriptQuery.php b/src/applications/herald/query/HeraldTranscriptQuery.php index 00a9dffeaf..6e9cf223b0 100644 --- a/src/applications/herald/query/HeraldTranscriptQuery.php +++ b/src/applications/herald/query/HeraldTranscriptQuery.php @@ -130,7 +130,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } } diff --git a/src/applications/herald/query/HeraldTranscriptSearchEngine.php b/src/applications/herald/query/HeraldTranscriptSearchEngine.php index e35620f0da..02e5b6b6e4 100644 --- a/src/applications/herald/query/HeraldTranscriptSearchEngine.php +++ b/src/applications/herald/query/HeraldTranscriptSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/herald/query/HeraldWebhookQuery.php b/src/applications/herald/query/HeraldWebhookQuery.php index 77307a71e6..fff20f7e6c 100644 --- a/src/applications/herald/query/HeraldWebhookQuery.php +++ b/src/applications/herald/query/HeraldWebhookQuery.php @@ -54,7 +54,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } } diff --git a/src/applications/herald/query/HeraldWebhookRequestQuery.php b/src/applications/herald/query/HeraldWebhookRequestQuery.php index f0a61a2dc5..5c0e94cedc 100644 --- a/src/applications/herald/query/HeraldWebhookRequestQuery.php +++ b/src/applications/herald/query/HeraldWebhookRequestQuery.php @@ -116,7 +116,7 @@ protected function willFilterPage(array $requests) { public function getQueryApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } } diff --git a/src/applications/herald/query/HeraldWebhookSearchEngine.php b/src/applications/herald/query/HeraldWebhookSearchEngine.php index 84997b60b1..ba898f4434 100644 --- a/src/applications/herald/query/HeraldWebhookSearchEngine.php +++ b/src/applications/herald/query/HeraldWebhookSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function newQuery() { diff --git a/src/applications/herald/query/HeraldWebhookTransactionQuery.php b/src/applications/herald/query/HeraldWebhookTransactionQuery.php index b812305e56..2ca5f0ad5d 100644 --- a/src/applications/herald/query/HeraldWebhookTransactionQuery.php +++ b/src/applications/herald/query/HeraldWebhookTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new HeraldWebhookTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorHeraldApplication::class; + } + } diff --git a/src/applications/herald/typeahead/HeraldAdapterDatasource.php b/src/applications/herald/typeahead/HeraldAdapterDatasource.php index 1fffd1bb6b..b0371b01a4 100644 --- a/src/applications/herald/typeahead/HeraldAdapterDatasource.php +++ b/src/applications/herald/typeahead/HeraldAdapterDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function loadResults() { diff --git a/src/applications/herald/typeahead/HeraldRuleDatasource.php b/src/applications/herald/typeahead/HeraldRuleDatasource.php index 88343bd433..20cb0cf437 100644 --- a/src/applications/herald/typeahead/HeraldRuleDatasource.php +++ b/src/applications/herald/typeahead/HeraldRuleDatasource.php @@ -12,7 +12,7 @@ public function getBrowseTitle() { } public function getDatasourceApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function loadResults() { @@ -22,7 +22,7 @@ public function loadResults() { $query = id(new HeraldRuleQuery()) ->setViewer($viewer); - if (preg_match('/^[hH]\d+\z/', $raw_query)) { + if (($raw_query !== null) && preg_match('/^[hH]\d+\z/', $raw_query)) { $id = trim($raw_query, 'hH'); $id = (int)$id; $query->withIDs(array($id)); diff --git a/src/applications/herald/typeahead/HeraldRuleTypeDatasource.php b/src/applications/herald/typeahead/HeraldRuleTypeDatasource.php index 8dfa7d0f6a..2bd81ff4a4 100644 --- a/src/applications/herald/typeahead/HeraldRuleTypeDatasource.php +++ b/src/applications/herald/typeahead/HeraldRuleTypeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function loadResults() { diff --git a/src/applications/herald/typeahead/HeraldWebhookDatasource.php b/src/applications/herald/typeahead/HeraldWebhookDatasource.php index a66431d515..f35dea6d30 100644 --- a/src/applications/herald/typeahead/HeraldWebhookDatasource.php +++ b/src/applications/herald/typeahead/HeraldWebhookDatasource.php @@ -12,7 +12,7 @@ public function getBrowseTitle() { } public function getDatasourceApplicationClass() { - return 'PhabricatorHeraldApplication'; + return PhabricatorHeraldApplication::class; } public function loadResults() { diff --git a/src/applications/home/menuitem/PhabricatorHomeLauncherProfileMenuItem.php b/src/applications/home/menuitem/PhabricatorHomeLauncherProfileMenuItem.php index dbf1586366..c3ebd825db 100644 --- a/src/applications/home/menuitem/PhabricatorHomeLauncherProfileMenuItem.php +++ b/src/applications/home/menuitem/PhabricatorHomeLauncherProfileMenuItem.php @@ -31,7 +31,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/home/menuitem/PhabricatorHomeProfileMenuItem.php b/src/applications/home/menuitem/PhabricatorHomeProfileMenuItem.php index a002b59da5..9a53e8fad7 100644 --- a/src/applications/home/menuitem/PhabricatorHomeProfileMenuItem.php +++ b/src/applications/home/menuitem/PhabricatorHomeProfileMenuItem.php @@ -26,7 +26,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/legalpad/application/PhabricatorLegalpadApplication.php b/src/applications/legalpad/application/PhabricatorLegalpadApplication.php index a3eaff5792..47a8f4ebef 100644 --- a/src/applications/legalpad/application/PhabricatorLegalpadApplication.php +++ b/src/applications/legalpad/application/PhabricatorLegalpadApplication.php @@ -48,6 +48,10 @@ public function getOverview() { 'open source projects keep track of Contributor License Agreements.'); } + public function getMonograms() { + return array('L'); + } + public function getRoutes() { return array( '/L(?P\d+)' => 'LegalpadDocumentSignController', diff --git a/src/applications/legalpad/conduit/LegalpadDocumentSearchConduitAPIMethod.php b/src/applications/legalpad/conduit/LegalpadDocumentSearchConduitAPIMethod.php new file mode 100644 index 0000000000..bb94c48536 --- /dev/null +++ b/src/applications/legalpad/conduit/LegalpadDocumentSearchConduitAPIMethod.php @@ -0,0 +1,18 @@ +needDocumentBodies(true); + } + + public function getAttachmentForObject($object, $data, $spec) { + return array( + 'body' => $object->getDocumentBody()->getText(), + 'preamble' => $object->getPreamble(), + ); + } + +} diff --git a/src/applications/legalpad/engineextension/PhabricatorLegalpadSignaturesSearchEngineAttachment.php b/src/applications/legalpad/engineextension/PhabricatorLegalpadSignaturesSearchEngineAttachment.php new file mode 100644 index 0000000000..9904951c68 --- /dev/null +++ b/src/applications/legalpad/engineextension/PhabricatorLegalpadSignaturesSearchEngineAttachment.php @@ -0,0 +1,38 @@ +needSignatures(true); + } + + public function getAttachmentForObject($object, $data, $spec) { + $signatures = array(); + foreach ($object->getSignatures() as $signature) { + $signatures[] = array( + 'phid' => $signature->getPHID(), + 'signerPHID' => $signature->getSignerPHID(), + 'exemptionPHID' => $signature->getExemptionPHID(), + 'isExemption' => $signature->getIsExemption(), + 'signerName' => $signature->getSignerName(), + 'signerEmail' => $signature->getSignerEmail(), + 'documentVersion' => $signature->getDocumentVersion(), + 'dateCreated' => (int)$signature->getDateCreated(), + ); + } + + return array( + 'signatures' => $signatures, + ); + } + +} diff --git a/src/applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php b/src/applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php index 39c744f271..9cd7da1105 100644 --- a/src/applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php +++ b/src/applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorLegalpadApplication'; + return PhabricatorLegalpadApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/legalpad/phid/PhabricatorLegalpadDocumentSignaturePHIDType.php b/src/applications/legalpad/phid/PhabricatorLegalpadDocumentSignaturePHIDType.php new file mode 100644 index 0000000000..97d280865c --- /dev/null +++ b/src/applications/legalpad/phid/PhabricatorLegalpadDocumentSignaturePHIDType.php @@ -0,0 +1,47 @@ +withPHIDs($phids); + } + + public function loadHandles( + PhabricatorHandleQuery $query, + array $handles, + array $objects) { + + foreach ($handles as $phid => $handle) { + $sig = $objects[$phid]; + $id = $sig->getID(); + $handle->setName('Signature '.$id); + + $signer_name = $sig->getSignerName(); + $handle->setFullName("Signature {$id} by {$signer_name}"); + $handle->setURI("/legalpad/signature/{$id}"); + } + } +} diff --git a/src/applications/legalpad/query/LegalpadDocumentQuery.php b/src/applications/legalpad/query/LegalpadDocumentQuery.php index 854a187fab..5e26ec31b1 100644 --- a/src/applications/legalpad/query/LegalpadDocumentQuery.php +++ b/src/applications/legalpad/query/LegalpadDocumentQuery.php @@ -259,7 +259,7 @@ private function loadSignatures(array $documents) { } public function getQueryApplicationClass() { - return 'PhabricatorLegalpadApplication'; + return PhabricatorLegalpadApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php b/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php index 591174be57..cf168451a4 100644 --- a/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php +++ b/src/applications/legalpad/query/LegalpadDocumentSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorLegalpadApplication'; + return PhabricatorLegalpadApplication::class; } public function newQuery() { diff --git a/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php b/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php index c310dd3d64..e8b57073e3 100644 --- a/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php +++ b/src/applications/legalpad/query/LegalpadDocumentSignatureQuery.php @@ -4,6 +4,7 @@ final class LegalpadDocumentSignatureQuery extends PhabricatorCursorPagedPolicyAwareQuery { private $ids; + private $phids; private $documentPHIDs; private $signerPHIDs; private $documentVersions; @@ -16,6 +17,11 @@ public function withIDs(array $ids) { return $this; } + public function withPHIDs(array $phids) { + $this->phids = $phids; + return $this; + } + public function withDocumentPHIDs(array $phids) { $this->documentPHIDs = $phids; return $this; @@ -46,20 +52,14 @@ public function withEmailContains($text) { return $this; } - protected function loadPage() { - $table = new LegalpadDocumentSignature(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); + public function newResultObject() { + return new LegalpadDocumentSignature(); + } + protected function loadPage() { + $table = $this->newResultObject(); + $data = $this->loadStandardPageRows($table); $signatures = $table->loadAllFromArray($data); - return $signatures; } @@ -98,6 +98,13 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { $this->ids); } + if ($this->phids !== null) { + $where[] = qsprintf( + $conn, + 'phid IN (%Ls)', + $this->phids); + } + if ($this->documentPHIDs !== null) { $where[] = qsprintf( $conn, @@ -144,7 +151,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorLegalpadApplication'; + return PhabricatorLegalpadApplication::class; } } diff --git a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php index ea14fd4a2f..d2c8149349 100644 --- a/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php +++ b/src/applications/legalpad/query/LegalpadDocumentSignatureSearchEngine.php @@ -5,12 +5,81 @@ final class LegalpadDocumentSignatureSearchEngine private $document; + public function newQuery() { + return new LegalpadDocumentSignatureQuery(); + } + + protected function buildCustomSearchFields() { + return array( + id(new PhabricatorUsersSearchField()) + ->setLabel(pht('Signed By')) + ->setKey('signerPHIDs') + ->setAliases(array('signer', 'signers', 'signerPHID')) + ->setDescription( + pht('Search for signatures by given users.')), + id(new PhabricatorPHIDsSearchField()) + ->setLabel(pht('Documents')) + ->setKey('documentPHIDs') + ->setAliases(array('document', 'documents', 'documentPHID')) + ->setDescription( + pht('Search for signatures on the given documents')), + id(new PhabricatorSearchTextField()) + ->setLabel(pht('Name Contains')) + ->setKey('nameContains') + ->setDescription( + pht('Search for signatures with a name containing the '. + 'given string.')), + id(new PhabricatorSearchTextField()) + ->setLabel(pht('Email Contains')) + ->setKey('emailContains') + ->setDescription( + pht('Search for signatures with an email containing the '. + 'given string.')), + id(new PhabricatorSearchDateField()) + ->setLabel(pht('Created After')) + ->setKey('createdStart'), + id(new PhabricatorSearchDateField()) + ->setLabel(pht('Created Before')) + ->setKey('createdEnd'), + ); + } + + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + + if ($map['signerPHIDs']) { + $query->withSignerPHIDs($map['signerPHIDs']); + } + + if ($map['documentPHIDs']) { + $query->withDocumentPHIDs($map['documentPHIDs']); + } + + if ($map['createdStart']) { + $query->withDateCreatedAfter($map['createdStart']); + } + + if ($map['createdEnd']) { + $query->withDateCreatedAfter($map['createdStart']); + } + + if ($map['nameContains']) { + $query->withNameContains($map['nameContains']); + } + + if ($map['emailContains']) { + $query->withEmailContains($map['emailContains']); + } + + return $query; + } + public function getResultTypeDescription() { return pht('Legalpad Signatures'); } public function getApplicationClassName() { - return 'PhabricatorLegalpadApplication'; + return PhabricatorLegalpadApplication::class; } public function setDocument(LegalpadDocument $document) { @@ -58,12 +127,12 @@ public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { } $name_contains = $saved->getParameter('nameContains'); - if (strlen($name_contains)) { + if (phutil_nonempty_string($name_contains)) { $query->withNameContains($name_contains); } $email_contains = $saved->getParameter('emailContains'); - if (strlen($email_contains)) { + if (phutil_nonempty_string($email_contains)) { $query->withEmailContains($email_contains); } diff --git a/src/applications/legalpad/query/LegalpadTransactionQuery.php b/src/applications/legalpad/query/LegalpadTransactionQuery.php index e4234d16a7..e0555fcd28 100644 --- a/src/applications/legalpad/query/LegalpadTransactionQuery.php +++ b/src/applications/legalpad/query/LegalpadTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new LegalpadTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorLegalpadApplication::class; + } + } diff --git a/src/applications/legalpad/storage/LegalpadDocument.php b/src/applications/legalpad/storage/LegalpadDocument.php index efcd6b5f15..e3245532e8 100644 --- a/src/applications/legalpad/storage/LegalpadDocument.php +++ b/src/applications/legalpad/storage/LegalpadDocument.php @@ -5,7 +5,8 @@ final class LegalpadDocument extends LegalpadDAO PhabricatorPolicyInterface, PhabricatorSubscribableInterface, PhabricatorApplicationTransactionInterface, - PhabricatorDestructibleInterface { + PhabricatorDestructibleInterface, + PhabricatorConduitResultInterface { protected $title; protected $contributorCount; @@ -159,6 +160,10 @@ public function getSignatureTypeIcon() { return idx($map, $type, 'fa-user grey'); } + public function getPreamble() { + return $this->preamble; + } + /* -( PhabricatorSubscribableInterface )----------------------------------- */ @@ -167,6 +172,48 @@ public function isAutomaticallySubscribed($phid) { return ($this->creatorPHID == $phid); } +/* -( PhabricatorConduitResultInterface )---------------------------------- */ + + public function getFieldSpecificationsForConduit() { + return array( + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('title') + ->setType('string') + ->setDescription(pht('The title of this document')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('creatorPHID') + ->setType('phid') + ->setDescription(pht('This user who created this document')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('versions') + ->setType('int') + ->setDescription(pht('The number of versions of this document')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('requireSignature') + ->setType('bool') + ->setDescription(pht( + 'Whether signatures on this doc are required to use this install')), + ); + } + + public function getFieldValuesForConduit() { + return array( + 'title' => $this->title, + 'creatorPHID' => $this->creatorPHID, + 'versions' => $this->versions, + 'requireSignature' => (bool)$this->requireSignature, + ); + } + + public function getConduitSearchAttachments() { + return array( + id(new PhabricatorLegalpadBodySearchEngineAttachment()) + ->setAttachmentKey('body'), + id(new PhabricatorLegalpadSignaturesSearchEngineAttachment()) + ->setAttachmentKey('signatures'), + ); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/legalpad/storage/LegalpadDocumentSignature.php b/src/applications/legalpad/storage/LegalpadDocumentSignature.php index 1cd0875dda..308d51ff2a 100644 --- a/src/applications/legalpad/storage/LegalpadDocumentSignature.php +++ b/src/applications/legalpad/storage/LegalpadDocumentSignature.php @@ -2,7 +2,9 @@ final class LegalpadDocumentSignature extends LegalpadDAO - implements PhabricatorPolicyInterface { + implements + PhabricatorPolicyInterface, + PhabricatorConduitResultInterface { const VERIFIED = 0; const UNVERIFIED = 1; @@ -23,6 +25,7 @@ final class LegalpadDocumentSignature protected function getConfiguration() { return array( + self::CONFIG_AUX_PHID => true, self::CONFIG_SERIALIZATION => array( 'signatureData' => self::SERIALIZATION_JSON, ), @@ -51,6 +54,14 @@ protected function getConfiguration() { ) + parent::getConfiguration(); } + public function getPHIDType() { + return PhabricatorLegalpadDocumentSignaturePHIDType::TYPECONST; + } + + public function generatePHID() { + return PhabricatorPHID::generateNewPHID($this->getPHIDType()); + } + public function save() { if (!$this->getSecretKey()) { $this->setSecretKey(Filesystem::readRandomCharacters(20)); @@ -71,6 +82,76 @@ public function attachDocument(LegalpadDocument $document) { return $this; } + public function getSignerPHID() { + return $this->signerPHID; + } + + public function getIsExemption() { + return (bool)$this->isExemption; + } + + public function getExemptionPHID() { + return $this->exemptionPHID; + } + + public function getSignerName() { + return $this->signerName; + } + + public function getSignerEmail() { + return $this->signerEmail; + } + + public function getDocumentVersion() { + return (int)$this->documentVersion; + } + +/* -( PhabricatorConduitResultInterface )---------------------------------- */ + + public function getFieldSpecificationsForConduit() { + return array( + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('documentPHID') + ->setType('phid') + ->setDescription(pht('The PHID of the document')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('signerPHID') + ->setType('phid?') + ->setDescription(pht('The PHID of the signer')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('exemptionPHID') + ->setType('phid?') + ->setDescription(pht('The PHID of the user who granted the exemption')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('signerName') + ->setType('string') + ->setDescription(pht('The name used by the signer.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('signerEmail') + ->setType('string') + ->setDescription(pht('The email used by the signer.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('isExemption') + ->setType('bool') + ->setDescription(pht('Whether or not this signature is an exemption')), + ); + } + + public function getFieldValuesForConduit() { + return array( + 'documentPHID' => $this->getDocumentPHID(), + 'signerPHID' => $this->getSignerPHID(), + 'exemptionPHID' => $this->getExemptionPHID(), + 'signerName' => $this->getSignerName(), + 'signerEmail' => $this->getSignerEmail(), + 'isExemption' => $this->getIsExemption(), + ); + } + + public function getConduitSearchAttachments() { + return array(); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/legalpad/typeahead/LegalpadDocumentDatasource.php b/src/applications/legalpad/typeahead/LegalpadDocumentDatasource.php index a0117ee7b0..fccf078f36 100644 --- a/src/applications/legalpad/typeahead/LegalpadDocumentDatasource.php +++ b/src/applications/legalpad/typeahead/LegalpadDocumentDatasource.php @@ -16,7 +16,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorLegalpadApplication'; + return PhabricatorLegalpadApplication::class; } public function loadResults() { diff --git a/src/applications/macro/editor/PhabricatorMacroEditEngine.php b/src/applications/macro/editor/PhabricatorMacroEditEngine.php index ff348c3163..d4a63f70ad 100644 --- a/src/applications/macro/editor/PhabricatorMacroEditEngine.php +++ b/src/applications/macro/editor/PhabricatorMacroEditEngine.php @@ -18,7 +18,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorMacroApplication'; + return PhabricatorMacroApplication::class; } public function isEngineConfigurable() { diff --git a/src/applications/macro/editor/PhabricatorMacroEditor.php b/src/applications/macro/editor/PhabricatorMacroEditor.php index 91ed23a259..31db69921e 100644 --- a/src/applications/macro/editor/PhabricatorMacroEditor.php +++ b/src/applications/macro/editor/PhabricatorMacroEditor.php @@ -4,7 +4,7 @@ final class PhabricatorMacroEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorMacroApplication'; + return PhabricatorMacroApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/macro/engine/PhabricatorMemeEngine.php b/src/applications/macro/engine/PhabricatorMemeEngine.php index afee0f9b18..71a45033da 100644 --- a/src/applications/macro/engine/PhabricatorMemeEngine.php +++ b/src/applications/macro/engine/PhabricatorMemeEngine.php @@ -182,7 +182,8 @@ private function newAssetData(PhabricatorFile $template) { // changes to the image. $above_text = $this->getAboveText(); $below_text = $this->getBelowText(); - if (!strlen(trim($above_text)) && !strlen(trim($below_text))) { + if (($above_text === null || !phutil_nonempty_string(trim($above_text))) && + ($below_text === null || !phutil_nonempty_string(trim($below_text)))) { return $template_data; } @@ -272,7 +273,7 @@ private function newGDAsset(PhabricatorFile $template, $data) { $size = $metrics['size']; $above = $this->getAboveText(); - if (strlen($above)) { + if ($above !== null && phutil_nonempty_string(trim($above))) { $x = (int)floor(($dx - $metrics['text']['above']['width']) / 2); $y = $metrics['text']['above']['height'] + 12; @@ -280,7 +281,7 @@ private function newGDAsset(PhabricatorFile $template, $data) { } $below = $this->getBelowText(); - if (strlen($below)) { + if ($below !== null && phutil_nonempty_string(trim($below))) { $x = (int)floor(($dx - $metrics['text']['below']['width']) / 2); $y = $dy - 12 - $metrics['text']['below']['descend']; diff --git a/src/applications/macro/phid/PhabricatorMacroMacroPHIDType.php b/src/applications/macro/phid/PhabricatorMacroMacroPHIDType.php index 19c7f5a1fc..a63b02f8ec 100644 --- a/src/applications/macro/phid/PhabricatorMacroMacroPHIDType.php +++ b/src/applications/macro/phid/PhabricatorMacroMacroPHIDType.php @@ -9,7 +9,7 @@ public function getTypeName() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorMacroApplication'; + return PhabricatorMacroApplication::class; } public function getTypeIcon() { diff --git a/src/applications/macro/query/PhabricatorMacroQuery.php b/src/applications/macro/query/PhabricatorMacroQuery.php index 70e7f7e688..edfc97ef9d 100644 --- a/src/applications/macro/query/PhabricatorMacroQuery.php +++ b/src/applications/macro/query/PhabricatorMacroQuery.php @@ -230,7 +230,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorMacroApplication'; + return PhabricatorMacroApplication::class; } public function getOrderableColumns() { diff --git a/src/applications/macro/query/PhabricatorMacroSearchEngine.php b/src/applications/macro/query/PhabricatorMacroSearchEngine.php index 7ef57a6a10..e61f52b8b8 100644 --- a/src/applications/macro/query/PhabricatorMacroSearchEngine.php +++ b/src/applications/macro/query/PhabricatorMacroSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorMacroApplication'; + return PhabricatorMacroApplication::class; } public function newQuery() { diff --git a/src/applications/macro/query/PhabricatorMacroTransactionQuery.php b/src/applications/macro/query/PhabricatorMacroTransactionQuery.php index 5ff9348c12..4c1baf640d 100644 --- a/src/applications/macro/query/PhabricatorMacroTransactionQuery.php +++ b/src/applications/macro/query/PhabricatorMacroTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorMacroTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorMacroApplication::class; + } + } diff --git a/src/applications/macro/typeahead/PhabricatorEmojiDatasource.php b/src/applications/macro/typeahead/PhabricatorEmojiDatasource.php index a0f0bda981..f967a37acc 100644 --- a/src/applications/macro/typeahead/PhabricatorEmojiDatasource.php +++ b/src/applications/macro/typeahead/PhabricatorEmojiDatasource.php @@ -11,7 +11,7 @@ public function getBrowseTitle() { } public function getDatasourceApplicationClass() { - return 'PhabricatorMacroApplication'; + return PhabricatorMacroApplication::class; } public function loadResults() { diff --git a/src/applications/macro/typeahead/PhabricatorMacroDatasource.php b/src/applications/macro/typeahead/PhabricatorMacroDatasource.php index b7b7efd630..c62adb041a 100644 --- a/src/applications/macro/typeahead/PhabricatorMacroDatasource.php +++ b/src/applications/macro/typeahead/PhabricatorMacroDatasource.php @@ -11,7 +11,7 @@ public function getBrowseTitle() { } public function getDatasourceApplicationClass() { - return 'PhabricatorMacroApplication'; + return PhabricatorMacroApplication::class; } public function loadResults() { diff --git a/src/applications/maniphest/application/PhabricatorManiphestApplication.php b/src/applications/maniphest/application/PhabricatorManiphestApplication.php index 8ed20416bb..9296060d81 100644 --- a/src/applications/maniphest/application/PhabricatorManiphestApplication.php +++ b/src/applications/maniphest/application/PhabricatorManiphestApplication.php @@ -42,6 +42,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('T'); + } + public function getRoutes() { return array( '/T(?P[1-9]\d*)' => 'ManiphestTaskDetailController', diff --git a/src/applications/maniphest/controller/ManiphestReportController.php b/src/applications/maniphest/controller/ManiphestReportController.php index 6fc19ece98..a7efe89194 100644 --- a/src/applications/maniphest/controller/ManiphestReportController.php +++ b/src/applications/maniphest/controller/ManiphestReportController.php @@ -186,7 +186,10 @@ public function renderBurn() { switch ($row['transactionType']) { case ManiphestTaskStatusTransaction::TRANSACTIONTYPE: // NOTE: Hack to avoid json_decode(). - $oldv = trim($row['oldValue'], '"'); + $oldv = $row['oldValue']; + if ($oldv !== null) { + $oldv = trim($oldv, '"'); + } $newv = trim($row['newValue'], '"'); break; case ManiphestTaskMergedIntoTransaction::TRANSACTIONTYPE: diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php index 8916ad26cf..af23126f9b 100644 --- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php +++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php @@ -81,7 +81,7 @@ public function handleRequest(AphrontRequest $request) { $graph_menu = null; $graph_limit = 200; - $overflow_message = null; + $graph_error_message = null; $task_graph = id(new ManiphestTaskGraph()) ->setViewer($viewer) ->setSeedPHID($task->getPHID()) @@ -102,8 +102,9 @@ public function handleRequest(AphrontRequest $request) { // the search button to browse tasks with the search UI instead. $direct_count = count($parent_list) + count($subtask_list); + $graph_table = null; if ($direct_count > $graph_limit) { - $overflow_message = pht( + $graph_error_message = pht( 'This task is directly connected to more than %s other tasks. '. 'Use %s to browse parents or subtasks, or %s to show more of the '. 'graph.', @@ -111,14 +112,13 @@ public function handleRequest(AphrontRequest $request) { phutil_tag('strong', array(), pht('Search...')), phutil_tag('strong', array(), pht('View Standalone Graph'))); - $graph_table = null; } else { // If there aren't too many direct tasks, but there are too many total // tasks, we'll only render directly connected tasks. if ($task_graph->isOverLimit()) { $task_graph->setRenderOnlyAdjacentNodes(true); - $overflow_message = pht( + $graph_error_message = pht( 'This task is connected to more than %s other tasks. '. 'Only direct parents and subtasks are shown here. Use '. '%s to show more of the graph.', @@ -126,13 +126,22 @@ public function handleRequest(AphrontRequest $request) { phutil_tag('strong', array(), pht('View Standalone Graph'))); } - $graph_table = $task_graph->newGraphTable(); + try { + $graph_table = $task_graph->newGraphTable(); + } catch (Throwable $ex) { + phlog($ex); + $graph_error_message = pht( + 'There was an unexpected error displaying the task graph. '. + 'Use %s to browse parents or subtasks, or %s to show the graph.', + phutil_tag('strong', array(), pht('Search...')), + phutil_tag('strong', array(), pht('View Standalone Graph'))); + } } - if ($overflow_message) { + if ($graph_error_message) { $overflow_view = $this->newTaskGraphOverflowView( $task, - $overflow_message, + $graph_error_message, true); $graph_table = array( diff --git a/src/applications/maniphest/editor/ManiphestEditEngine.php b/src/applications/maniphest/editor/ManiphestEditEngine.php index fc3c48b205..46877168e7 100644 --- a/src/applications/maniphest/editor/ManiphestEditEngine.php +++ b/src/applications/maniphest/editor/ManiphestEditEngine.php @@ -18,7 +18,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function isDefaultQuickCreateEngine() { diff --git a/src/applications/maniphest/editor/ManiphestTransactionEditor.php b/src/applications/maniphest/editor/ManiphestTransactionEditor.php index 01fc0af83d..fc2251e806 100644 --- a/src/applications/maniphest/editor/ManiphestTransactionEditor.php +++ b/src/applications/maniphest/editor/ManiphestTransactionEditor.php @@ -7,7 +7,7 @@ final class ManiphestTransactionEditor private $moreValidationErrors = array(); public function getEditorApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function getEditorObjectsDescription() { @@ -327,11 +327,11 @@ protected function expandTransactions( $is_unassigned = ($object->getOwnerPHID() === null); - $any_assign = false; + $any_xassign = null; foreach ($xactions as $xaction) { if ($xaction->getTransactionType() == ManiphestTaskOwnerTransaction::TRANSACTIONTYPE) { - $any_assign = true; + $any_xassign = $xaction; break; } } @@ -355,15 +355,22 @@ protected function expandTransactions( // If the task is not assigned, not being assigned, currently open, and // being closed, try to assign the actor as the owner. - if ($is_unassigned && !$any_assign && $is_open && $is_closing) { - $is_claim = ManiphestTaskStatus::isClaimStatus($new_status); - - // Don't assign the actor if they aren't a real user. - // Don't claim the task if the status is configured to not claim. - if ($actor_phid && $is_claim) { - $results[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTaskOwnerTransaction::TRANSACTIONTYPE) - ->setNewValue($actor_phid); + // Don't assign the actor if they aren't a real user. + if ($is_unassigned && $is_open && $is_closing && $actor_phid) { + $is_autoclaim = ManiphestTaskStatus::isClaimStatus($new_status); + if ($is_autoclaim) { + if ($any_xassign === null) { + $results[] = id(new ManiphestTransaction()) + ->setTransactionType(ManiphestTaskOwnerTransaction::TRANSACTIONTYPE) + ->setNewValue($actor_phid); + } else if ($any_xassign->getNewValue() === null) { + // We have an explicit "Assign / Claim" = nothing in the frontend. + // The user is trying to "undo" the above automatic auto-claim. + // When saving, this would cause the "no effect" warning. + // So we suppress that confusing warning. + // https://we.phorge.it/T15164 + $any_xassign->setIgnoreOnNoEffect(true); + } } } diff --git a/src/applications/maniphest/herald/HeraldManiphestTaskAdapter.php b/src/applications/maniphest/herald/HeraldManiphestTaskAdapter.php index 1aa544de57..b43b914d96 100644 --- a/src/applications/maniphest/herald/HeraldManiphestTaskAdapter.php +++ b/src/applications/maniphest/herald/HeraldManiphestTaskAdapter.php @@ -9,7 +9,7 @@ protected function newObject() { } public function getAdapterApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/maniphest/phid/ManiphestTaskPHIDType.php b/src/applications/maniphest/phid/ManiphestTaskPHIDType.php index 3b3c4a203f..1b0165fec7 100644 --- a/src/applications/maniphest/phid/ManiphestTaskPHIDType.php +++ b/src/applications/maniphest/phid/ManiphestTaskPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/maniphest/query/ManiphestTaskQuery.php b/src/applications/maniphest/query/ManiphestTaskQuery.php index 9e58728cff..c206bd6599 100644 --- a/src/applications/maniphest/query/ManiphestTaskQuery.php +++ b/src/applications/maniphest/query/ManiphestTaskQuery.php @@ -1036,7 +1036,7 @@ private function parseCursor($cursor) { $parts[] = null; } - if (!strlen($parts[1])) { + if (!phutil_nonempty_string($parts[1])) { $parts[1] = null; } @@ -1048,7 +1048,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } } diff --git a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php index 4c69c604e4..361cdca7e4 100644 --- a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php +++ b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php @@ -35,7 +35,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function newQuery() { diff --git a/src/applications/maniphest/query/ManiphestTransactionQuery.php b/src/applications/maniphest/query/ManiphestTransactionQuery.php index 9826107b77..b29c0652a4 100644 --- a/src/applications/maniphest/query/ManiphestTransactionQuery.php +++ b/src/applications/maniphest/query/ManiphestTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new ManiphestTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorManiphestApplication::class; + } + } diff --git a/src/applications/maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php index 68fb521980..7b9e6f91ed 100644 --- a/src/applications/maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php index 85a201fdba..b08cae0a9d 100644 --- a/src/applications/maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php index c4530f9bb3..db73f8d898 100644 --- a/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function loadResults() { diff --git a/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php index 9d73ca994f..26bfacc520 100644 --- a/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function loadResults() { diff --git a/src/applications/maniphest/typeahead/ManiphestTaskSubtypeDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskSubtypeDatasource.php index cfa5592ccc..b24a0cfaad 100644 --- a/src/applications/maniphest/typeahead/ManiphestTaskSubtypeDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestTaskSubtypeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorManiphestApplication'; + return PhabricatorManiphestApplication::class; } public function loadResults() { diff --git a/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php index 5a69199874..3b24dff590 100644 --- a/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php +++ b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php @@ -97,8 +97,17 @@ public function getIcon() { return 'fa-calculator'; } + /** + * Normalize your Story Points from generic stuff to double or null. + * @param mixed $value Your raw Story Points + * @return double|null + */ private function getValueForPoints($value) { - if (!strlen($value)) { + // The Point can be various types also thanks to Conduit API + // like integers, floats, null, and strings of course. + // Everything meaningful must be printable as a string. + $is_empty = phutil_string_cast($value) === ''; + if ($is_empty) { $value = null; } if ($value !== null) { diff --git a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php index 8be6d73a1d..1bf6734db3 100644 --- a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php +++ b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php @@ -38,6 +38,30 @@ public function handleRequest(AphrontRequest $request) { $header->setStatus('fa-ban', 'dark', pht('Uninstalled')); } + if (!$selected->isFirstParty()) { + $header->addTag(id(new PHUITagView()) + ->setName('Extension') + ->setIcon('fa-plug') + ->setColor(PHUITagView::COLOR_INDIGO) + ->setType(PHUITagView::TYPE_SHADE)); + } + + if ($selected->isPrototype()) { + $header->addTag(id(new PHUITagView()) + ->setName('Prototype') + ->setIcon('fa-exclamation-circle') + ->setColor(PHUITagView::COLOR_ORANGE) + ->setType(PHUITagView::TYPE_SHADE)); + } + + if ($selected->isDeprecated()) { + $header->addTag(id(new PHUITagView()) + ->setName('Deprecated') + ->setIcon('fa-exclamation-triangle') + ->setColor(PHUITagView::COLOR_RED) + ->setType(PHUITagView::TYPE_SHADE)); + } + $timeline = $this->buildTransactionTimeline( $selected, new PhabricatorApplicationApplicationTransactionQuery()); @@ -95,6 +119,27 @@ private function buildPropertySectionView( phutil_tag('em', array(), $application->getFlavorText())); } + $phids = PhabricatorPHIDType::getAllTypesForApplication( + get_class($application)); + + $user_friendly_phids = array(); + foreach ($phids as $phid => $type) { + $user_friendly_phids[] = "PHID-{$phid} ({$type->getTypeName()})"; + } + + if ($user_friendly_phids) { + $properties->addProperty( + 'PHIDs', + phutil_implode_html(phutil_tag('br'), $user_friendly_phids)); + } + + $monograms = $application->getMonograms(); + if ($monograms) { + $properties->addProperty( + 'Monograms', + phutil_implode_html(', ', $monograms)); + } + if ($application->isPrototype()) { $proto_href = PhabricatorEnv::getDoclink( 'User Guide: Prototype Applications'); @@ -114,7 +159,7 @@ private function buildPropertySectionView( } $overview = $application->getOverview(); - if (strlen($overview)) { + if (phutil_nonempty_string($overview)) { $overview = new PHUIRemarkupView($viewer, $overview); $properties->addSectionHeader( pht('Overview'), PHUIPropertyListView::ICON_SUMMARY); diff --git a/src/applications/meta/editor/PhabricatorApplicationEditEngine.php b/src/applications/meta/editor/PhabricatorApplicationEditEngine.php index 7cad5d9242..b6442ba3a8 100644 --- a/src/applications/meta/editor/PhabricatorApplicationEditEngine.php +++ b/src/applications/meta/editor/PhabricatorApplicationEditEngine.php @@ -6,7 +6,7 @@ final class PhabricatorApplicationEditEngine const ENGINECONST = 'application.application'; public function getEngineApplicationClass() { - return 'PhabricatorApplicationsApplication'; + return PhabricatorApplicationsApplication::class; } public function getEngineName() { diff --git a/src/applications/meta/editor/PhabricatorApplicationEditor.php b/src/applications/meta/editor/PhabricatorApplicationEditor.php index 83003b4c27..99f291e6d8 100644 --- a/src/applications/meta/editor/PhabricatorApplicationEditor.php +++ b/src/applications/meta/editor/PhabricatorApplicationEditor.php @@ -4,7 +4,7 @@ final class PhabricatorApplicationEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorApplicationsApplication'; + return PhabricatorApplicationsApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/meta/engineextension/PhabricatorDatasourceURIEngineExtension.php b/src/applications/meta/engineextension/PhabricatorDatasourceURIEngineExtension.php index 16d8dd9315..76d60af1e6 100644 --- a/src/applications/meta/engineextension/PhabricatorDatasourceURIEngineExtension.php +++ b/src/applications/meta/engineextension/PhabricatorDatasourceURIEngineExtension.php @@ -10,35 +10,37 @@ public function newQuickSearchDatasources() { public function newJumpURI($query) { // If you search for a URI on the local install, just redirect to that // URI as though you had pasted it into the URI bar. - if (PhabricatorEnv::isSelfURI($query)) { - // Strip off the absolute part of the URI. If we don't, the URI redirect - // validator will get upset that we're performing an unmarked external - // redirect. - - // The correct host and protocol may also differ from the host and - // protocol used in the search: for example, if you search for "http://" - // we want to redirect to "https://" if an install is HTTPS, and - // the "isSelfURI()" check includes alternate domains in addition to the - // canonical domain. - - $uri = id(new PhutilURI($query)) - ->setDomain(null) - ->setProtocol(null) - ->setPort(null); - - $uri = phutil_string_cast($uri); - - // See T13412. If the URI was in the form "http://dev.example.com" with - // no trailing slash, there may be no path. Redirecting to the empty - // string is considered an error by safety checks during redirection, - // so treat this like the user entered the URI with a trailing slash. - if (!strlen($uri)) { - $uri = '/'; - } - - return $uri; + // Skip things that are really not full URLs, like "asdasd". + // Note that the backend of "isSelfURI" is faster with a PhutilURI. + $uri = new PhutilURI($query); + if ($uri->getDomain() === '' || !PhabricatorEnv::isSelfURI($uri)) { + return null; } - return null; + // Strip off the absolute part of the URI. If we don't, the URI redirect + // validator will get upset that we're performing an unmarked external + // redirect. + + // The correct host and protocol may also differ from the host and + // protocol used in the search: for example, if you search for "http://" + // we want to redirect to "https://" if an install is HTTPS, and + // the "isSelfURI()" check includes alternate domains in addition to the + // canonical domain. + $uri = $uri + ->setDomain(null) + ->setProtocol(null) + ->setPort(null); + + $uri = phutil_string_cast($uri); + + // See T13412. If the URI was in the form "http://dev.example.com" with + // no trailing slash, there may be no path. Redirecting to the empty + // string is considered an error by safety checks during redirection, + // so treat this like the user entered the URI with a trailing slash. + if (!strlen($uri)) { + $uri = '/'; + } + + return $uri; } } diff --git a/src/applications/meta/panel/PhabricatorApplicationConfigurationPanel.php b/src/applications/meta/panel/PhabricatorApplicationConfigurationPanel.php index 358a6f753b..d96132c9b0 100644 --- a/src/applications/meta/panel/PhabricatorApplicationConfigurationPanel.php +++ b/src/applications/meta/panel/PhabricatorApplicationConfigurationPanel.php @@ -24,7 +24,13 @@ public function getApplication() { return $this->application; } - public function getPanelURI($path = null) { + /** + * Get the URI for this application configuration panel. + * + * @param string? Optional path to append. + * @return string Relative URI for the panel. + */ + public function getPanelURI($path = '') { $app_key = get_class($this->getApplication()); $panel_key = $this->getPanelKey(); $base = "/applications/panel/{$app_key}/{$panel_key}/"; diff --git a/src/applications/meta/phid/PhabricatorApplicationApplicationPHIDType.php b/src/applications/meta/phid/PhabricatorApplicationApplicationPHIDType.php index 946f56616d..b67ba1a023 100644 --- a/src/applications/meta/phid/PhabricatorApplicationApplicationPHIDType.php +++ b/src/applications/meta/phid/PhabricatorApplicationApplicationPHIDType.php @@ -18,7 +18,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorApplicationsApplication'; + return PhabricatorApplicationsApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/meta/query/PhabricatorAppSearchEngine.php b/src/applications/meta/query/PhabricatorAppSearchEngine.php index ee938abbbb..b4d5d9b9c2 100644 --- a/src/applications/meta/query/PhabricatorAppSearchEngine.php +++ b/src/applications/meta/query/PhabricatorAppSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorApplicationsApplication'; + return PhabricatorApplicationsApplication::class; } public function getPageSize(PhabricatorSavedQuery $saved) { @@ -45,7 +45,7 @@ public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { ->withUnlisted(false); $name = $saved->getParameter('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { $query->withNameContains($name); } @@ -231,13 +231,13 @@ protected function renderResultList( ->setSideColumn($configure); if (!$application->isFirstParty()) { - $tag = id(new PHUITagView()) + $extension_tag = id(new PHUITagView()) ->setName(pht('Extension')) - ->setIcon('fa-puzzle-piece') - ->setColor(PHUITagView::COLOR_BLUE) + ->setIcon('fa-plug') + ->setColor(PHUITagView::COLOR_INDIGO) ->setType(PHUITagView::TYPE_SHADE) ->setSlimShady(true); - $item->addAttribute($tag); + $item->addAttribute($extension_tag); } if ($application->isPrototype()) { @@ -250,6 +250,16 @@ protected function renderResultList( $item->addAttribute($prototype_tag); } + if ($application->isDeprecated()) { + $deprecated_tag = id(new PHUITagView()) + ->setName(pht('Deprecated')) + ->setIcon('fa-exclamation-triangle') + ->setColor(PHUITagView::COLOR_RED) + ->setType(PHUITagView::TYPE_SHADE) + ->setSlimShady(true); + $item->addAttribute($deprecated_tag); + } + $item->addAttribute($description); if ($application->getBaseURI() && $application->isInstalled()) { diff --git a/src/applications/meta/query/PhabricatorApplicationApplicationTransactionQuery.php b/src/applications/meta/query/PhabricatorApplicationApplicationTransactionQuery.php index 77843f713d..85bbf1162e 100644 --- a/src/applications/meta/query/PhabricatorApplicationApplicationTransactionQuery.php +++ b/src/applications/meta/query/PhabricatorApplicationApplicationTransactionQuery.php @@ -7,4 +7,11 @@ public function getTemplateApplicationTransaction() { return new PhabricatorApplicationApplicationTransaction(); } + // NOTE: Although this belongs to the "Applications" application, trying + // to filter its results just leaves us recursing indefinitely. Users + // always have access to applications regardless of other policy settings + // anyway. + public function getQueryApplicationClass() { + return null; + } } diff --git a/src/applications/meta/typeahead/PhabricatorApplicationDatasource.php b/src/applications/meta/typeahead/PhabricatorApplicationDatasource.php index acf9c8b32f..daf8494383 100644 --- a/src/applications/meta/typeahead/PhabricatorApplicationDatasource.php +++ b/src/applications/meta/typeahead/PhabricatorApplicationDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorApplicationsApplication'; + return PhabricatorApplicationsApplication::class; } public function loadResults() { diff --git a/src/applications/metamta/PhabricatorMetaMTAWorker.php b/src/applications/metamta/PhabricatorMetaMTAWorker.php index dcc6a8dc5e..7d7c51f8b2 100644 --- a/src/applications/metamta/PhabricatorMetaMTAWorker.php +++ b/src/applications/metamta/PhabricatorMetaMTAWorker.php @@ -45,9 +45,10 @@ private function loadMessage() { public function renderForDisplay(PhabricatorUser $viewer) { return phutil_tag( 'pre', - array( - ), - 'phabricator/ $ ./bin/mail show-outbound --id '.$this->getTaskData()); + array(), + PlatformSymbols::getPlatformServerPath(). + ' $ ./bin/mail show-outbound --id '. + $this->getTaskData()); } } diff --git a/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php b/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php index e38c7f9801..0d9b89c684 100644 --- a/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php @@ -421,7 +421,7 @@ private function buildMetadataProperties(PhabricatorMetaMTAMail $mail) { $properties->addProperty(pht('Message PHID'), $mail->getPHID()); $details = $mail->getMessage(); - if (!strlen($details)) { + if (!phutil_nonempty_string($details)) { $details = phutil_tag('em', array(), pht('None')); } $properties->addProperty(pht('Status Details'), $details); diff --git a/src/applications/metamta/editor/PhabricatorMetaMTAApplicationEmailEditor.php b/src/applications/metamta/editor/PhabricatorMetaMTAApplicationEmailEditor.php index 843e653039..601b3ffb89 100644 --- a/src/applications/metamta/editor/PhabricatorMetaMTAApplicationEmailEditor.php +++ b/src/applications/metamta/editor/PhabricatorMetaMTAApplicationEmailEditor.php @@ -4,7 +4,7 @@ final class PhabricatorMetaMTAApplicationEmailEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return pht('PhabricatorMetaMTAApplication'); + return PhabricatorMetaMTAApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/metamta/engine/PhabricatorMailEmailEngine.php b/src/applications/metamta/engine/PhabricatorMailEmailEngine.php index 6c9cf1b356..4278aa96a1 100644 --- a/src/applications/metamta/engine/PhabricatorMailEmailEngine.php +++ b/src/applications/metamta/engine/PhabricatorMailEmailEngine.php @@ -507,7 +507,7 @@ private function newEmailAddress($address, $name = null) { public function newDefaultEmailAddress() { $raw_address = PhabricatorEnv::getEnvConfig('metamta.default-address'); - if (!strlen($raw_address)) { + if (!$raw_address) { $domain = $this->newMailDomain(); $raw_address = "noreply@{$domain}"; } @@ -527,7 +527,7 @@ public function newVoidEmailAddress() { private function newMailDomain() { $domain = PhabricatorEnv::getEnvConfig('metamta.reply-handler-domain'); - if (strlen($domain)) { + if ($domain) { return $domain; } diff --git a/src/applications/metamta/exception/PhabricatorMetaMTAReceivedMailProcessingException.php b/src/applications/metamta/exception/PhabricatorMetaMTAReceivedMailProcessingException.php index 5fb1209885..cc77af9595 100644 --- a/src/applications/metamta/exception/PhabricatorMetaMTAReceivedMailProcessingException.php +++ b/src/applications/metamta/exception/PhabricatorMetaMTAReceivedMailProcessingException.php @@ -14,7 +14,8 @@ public function __construct($status_code /* ... */) { $this->statusCode = $args[0]; $args = array_slice($args, 1); - call_user_func_array(array('parent', '__construct'), $args); + $parent = get_parent_class($this); + call_user_func_array(array($parent, '__construct'), $args); } } diff --git a/src/applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php b/src/applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php index 94c13c9e0a..2bfe376084 100644 --- a/src/applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php +++ b/src/applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php @@ -6,7 +6,7 @@ final class PhabricatorMailOutboundMailHeraldAdapter private $mail; public function getAdapterApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php index 73917a2d84..a3c556e6ac 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php @@ -181,8 +181,9 @@ public function execute(PhutilArgumentParser $args) { $received->processReceivedMail(); $console->writeErr( - "%s\n\n phabricator/ $ ./bin/mail show-inbound --id %d\n\n", + "%s\n\n %s $ ./bin/mail show-inbound --id %d\n\n", pht('Mail received! You can view details by running this command:'), + PlatformSymbols::getPlatformServerPath(), $received->getID()); } diff --git a/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php index f390ff27df..54c7ff53f6 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php @@ -63,6 +63,7 @@ protected function didConstruct() { array( 'name' => 'type', 'param' => 'message-type', + 'default' => PhabricatorMailEmailMessage::MESSAGETYPE, 'help' => pht( 'Send the specified type of message (email, sms, ...).'), ), @@ -74,10 +75,6 @@ public function execute(PhutilArgumentParser $args) { $viewer = $this->getViewer(); $type = $args->getArg('type'); - if (!strlen($type)) { - $type = PhabricatorMailEmailMessage::MESSAGETYPE; - } - $type_map = PhabricatorMailExternalMessage::getAllMessageTypes(); if (!isset($type_map[$type])) { throw new PhutilArgumentUsageException( @@ -228,8 +225,9 @@ public function execute(PhutilArgumentParser $args) { $mail->save(); $console->writeErr( - "%s\n\n phabricator/ $ ./bin/mail show-outbound --id %d\n\n", + "%s\n\n %s $ ./bin/mail show-outbound --id %d\n\n", pht('Mail sent! You can view details by running this command:'), + PlatformSymbols::getPlatformServerPath(), $mail->getID()); } diff --git a/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php index f29a63c2eb..1f4f5a3e2a 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php @@ -65,7 +65,7 @@ public function execute(PhutilArgumentParser $args) { foreach ($messages as $message_key => $message) { if ($args->getArg('dump-html')) { $html_body = $message->getHTMLBody(); - if (strlen($html_body)) { + if (phutil_nonempty_string($html_body)) { $template = "{$html_body}"; $console->writeOut("%s\n", $html_body); @@ -188,7 +188,7 @@ public function execute(PhutilArgumentParser $args) { $info[] = null; $info[] = $this->newSectionHeader(pht('TEXT BODY')); - if (strlen($message->getBody())) { + if (phutil_nonempty_string($message->getBody())) { $info[] = tsprintf('%B', $message->getBody()); } else { $info[] = pht('(This message has no text body.)'); @@ -203,7 +203,7 @@ public function execute(PhutilArgumentParser $args) { $info[] = null; $info[] = $this->newSectionHeader(pht('HTML BODY')); - if (strlen($message->getHTMLBody())) { + if (phutil_nonempty_string($message->getHTMLBody())) { $info[] = $message->getHTMLBody(); $info[] = null; } else { diff --git a/src/applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php b/src/applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php index 1436038fae..e8fd037ad2 100644 --- a/src/applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php +++ b/src/applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php index 9c0c5d941a..80c2f91c3a 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php @@ -105,7 +105,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } } diff --git a/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailTransactionQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailTransactionQuery.php index 4f4f6d11de..311e0af833 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailTransactionQuery.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAApplicationEmailTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorMetaMTAApplicationEmailTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorMetaMTAApplication::class; + } + } diff --git a/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php index b7dd3e5ee4..4cb1e2dedc 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php @@ -41,7 +41,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } } diff --git a/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php index d1f0235c90..dd7e961e24 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php @@ -112,7 +112,7 @@ public function newResultObject() { } public function getQueryApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } protected function shouldGroupQueryResultRows() { diff --git a/src/applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php b/src/applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php index df7774aae2..d3ae165e2a 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php index d3289bbc69..04b5d9dc22 100644 --- a/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php @@ -524,7 +524,7 @@ public function newContentSource() { public function newFromAddress() { $raw_from = $this->getHeader('From'); - if (strlen($raw_from)) { + if (phutil_nonempty_string($raw_from)) { return new PhutilEmailAddress($raw_from); } diff --git a/src/applications/metamta/typeahead/PhabricatorMetaMTAApplicationEmailDatasource.php b/src/applications/metamta/typeahead/PhabricatorMetaMTAApplicationEmailDatasource.php index 62c1275c39..117d0771c3 100644 --- a/src/applications/metamta/typeahead/PhabricatorMetaMTAApplicationEmailDatasource.php +++ b/src/applications/metamta/typeahead/PhabricatorMetaMTAApplicationEmailDatasource.php @@ -17,7 +17,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } public function loadResults() { diff --git a/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php b/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php index 2e0e03bb3c..f9fa106e63 100644 --- a/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php +++ b/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableFunctionDatasource.php b/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableFunctionDatasource.php index b5d82c9749..4d17a8b840 100644 --- a/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableFunctionDatasource.php +++ b/src/applications/metamta/typeahead/PhabricatorMetaMTAMailableFunctionDatasource.php @@ -13,7 +13,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/multimeter/controller/MultimeterSampleController.php b/src/applications/multimeter/controller/MultimeterSampleController.php index 190a839f63..0aca60f0e1 100644 --- a/src/applications/multimeter/controller/MultimeterSampleController.php +++ b/src/applications/multimeter/controller/MultimeterSampleController.php @@ -10,7 +10,7 @@ public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $group_map = $this->getColumnMap(); - $group = explode('.', $request->getStr('group')); + $group = explode('.', $request->getStr('group', '')); $group = array_intersect($group, array_keys($group_map)); $group = array_fuse($group); diff --git a/src/applications/notification/client/PhabricatorNotificationServerRef.php b/src/applications/notification/client/PhabricatorNotificationServerRef.php index 714ad5f5d7..2fe903fdba 100644 --- a/src/applications/notification/client/PhabricatorNotificationServerRef.php +++ b/src/applications/notification/client/PhabricatorNotificationServerRef.php @@ -143,8 +143,10 @@ public function isAdminServer() { return ($this->type == 'admin'); } - public function getURI($to_path = null) { - $full_path = rtrim($this->getPath(), '/').'/'.ltrim($to_path, '/'); + public function getURI($to_path = '') { + $path = coalesce($this->path, ''); + $to_path = coalesce($to_path, ''); + $full_path = rtrim($path, '/').'/'.ltrim($to_path, '/'); $uri = id(new PhutilURI('http://'.$this->getHost())) ->setProtocol($this->getProtocol()) @@ -152,16 +154,16 @@ public function getURI($to_path = null) { ->setPath($full_path); $instance = PhabricatorEnv::getEnvConfig('cluster.instance'); - if (strlen($instance)) { + if (phutil_nonempty_string($instance)) { $uri->replaceQueryParam('instance', $instance); } return $uri; } - public function getWebsocketURI($to_path = null) { + public function getWebsocketURI($to_path = '') { $instance = PhabricatorEnv::getEnvConfig('cluster.instance'); - if (strlen($instance)) { + if (phutil_nonempty_string($instance)) { $to_path = $to_path.'~'.$instance.'/'; } diff --git a/src/applications/notification/config/PhabricatorNotificationServersConfigType.php b/src/applications/notification/config/PhabricatorNotificationServersConfigType.php index f13105a249..8fbac5806c 100644 --- a/src/applications/notification/config/PhabricatorNotificationServersConfigType.php +++ b/src/applications/notification/config/PhabricatorNotificationServersConfigType.php @@ -92,7 +92,7 @@ public function validateStoredValue( } $path = idx($spec, 'path'); - if ($type == 'admin' && strlen($path)) { + if ($type == 'admin' && phutil_nonempty_string($path)) { throw $this->newException( pht( 'Notification server configuration describes an invalid host '. diff --git a/src/applications/notification/query/PhabricatorNotificationQuery.php b/src/applications/notification/query/PhabricatorNotificationQuery.php index 2cd97f25c8..021d666b13 100644 --- a/src/applications/notification/query/PhabricatorNotificationQuery.php +++ b/src/applications/notification/query/PhabricatorNotificationQuery.php @@ -190,7 +190,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorNotificationsApplication'; + return PhabricatorNotificationsApplication::class; } } diff --git a/src/applications/notification/query/PhabricatorNotificationSearchEngine.php b/src/applications/notification/query/PhabricatorNotificationSearchEngine.php index 4b56c5f5a1..353ebe9402 100644 --- a/src/applications/notification/query/PhabricatorNotificationSearchEngine.php +++ b/src/applications/notification/query/PhabricatorNotificationSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorNotificationsApplication'; + return PhabricatorNotificationsApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/notification/view/PhabricatorNotificationStatusView.php b/src/applications/notification/view/PhabricatorNotificationStatusView.php index e962395bba..69939525cc 100644 --- a/src/applications/notification/view/PhabricatorNotificationStatusView.php +++ b/src/applications/notification/view/PhabricatorNotificationStatusView.php @@ -40,34 +40,41 @@ protected function getTagAttributes() { protected function getTagContent() { $have = PhabricatorEnv::getEnvConfig('notification.servers'); if ($have) { - $icon = id(new PHUIIconView()) - ->setIcon('fa-circle-o yellow'); - $text = pht('Connecting...'); - return phutil_tag( - 'span', - array( - 'class' => 'connection-status-text '. - 'aphlict-connection-status-connecting', - ), - array( - $icon, - $text, - )); + return $this->buildMessageView( + 'aphlict-connection-status-connecting', + 'fa-circle-o yellow', + pht('Connecting...')); } else { - $text = pht('Notification server not enabled'); - $icon = id(new PHUIIconView()) - ->setIcon('fa-circle-o grey'); - return phutil_tag( - 'span', - array( - 'class' => 'connection-status-text '. - 'aphlict-connection-status-notenabled', - ), - array( - $icon, - $text, - )); + return $this->buildMessageView( + 'aphlict-connection-status-notenabled', + 'fa-circle-o grey', + pht('Notification server not enabled')); } } + /** + * Create an icon and a message. + * + * @param string $class_name Raw CSS class name(s) space separated + * @param string $icon_name Icon name + * @param string $text Text to be shown + * @return array + */ + private function buildMessageView($class_name, $icon_name, $text) { + $icon = id(new PHUIIconView()) + ->setIcon($icon_name); + + $message = phutil_tag( + 'span', + array( + 'class' => 'connection-status-text '.$class_name, + ), + $text); + + return array( + $icon, + $message, + ); + } + } diff --git a/src/applications/nuance/editor/NuanceItemEditor.php b/src/applications/nuance/editor/NuanceItemEditor.php index b41ca77563..4f2056d6ae 100644 --- a/src/applications/nuance/editor/NuanceItemEditor.php +++ b/src/applications/nuance/editor/NuanceItemEditor.php @@ -4,7 +4,7 @@ final class NuanceItemEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/nuance/editor/NuanceQueueEditEngine.php b/src/applications/nuance/editor/NuanceQueueEditEngine.php index 12f5c7b517..2a49079a47 100644 --- a/src/applications/nuance/editor/NuanceQueueEditEngine.php +++ b/src/applications/nuance/editor/NuanceQueueEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } protected function newEditableObject() { diff --git a/src/applications/nuance/editor/NuanceQueueEditor.php b/src/applications/nuance/editor/NuanceQueueEditor.php index 2a18188f98..59abd3dd6d 100644 --- a/src/applications/nuance/editor/NuanceQueueEditor.php +++ b/src/applications/nuance/editor/NuanceQueueEditor.php @@ -4,7 +4,7 @@ final class NuanceQueueEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/nuance/editor/NuanceSourceEditEngine.php b/src/applications/nuance/editor/NuanceSourceEditEngine.php index eac751c3a5..882b9030fe 100644 --- a/src/applications/nuance/editor/NuanceSourceEditEngine.php +++ b/src/applications/nuance/editor/NuanceSourceEditEngine.php @@ -34,7 +34,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } protected function newEditableObject() { diff --git a/src/applications/nuance/editor/NuanceSourceEditor.php b/src/applications/nuance/editor/NuanceSourceEditor.php index b56b183f9e..d141dd0108 100644 --- a/src/applications/nuance/editor/NuanceSourceEditor.php +++ b/src/applications/nuance/editor/NuanceSourceEditor.php @@ -4,7 +4,7 @@ final class NuanceSourceEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/nuance/phid/NuanceImportCursorPHIDType.php b/src/applications/nuance/phid/NuanceImportCursorPHIDType.php index 9d1f816a71..bc28710d37 100644 --- a/src/applications/nuance/phid/NuanceImportCursorPHIDType.php +++ b/src/applications/nuance/phid/NuanceImportCursorPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/nuance/phid/NuanceItemPHIDType.php b/src/applications/nuance/phid/NuanceItemPHIDType.php index 771b398419..c9491776ca 100644 --- a/src/applications/nuance/phid/NuanceItemPHIDType.php +++ b/src/applications/nuance/phid/NuanceItemPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/nuance/phid/NuanceQueuePHIDType.php b/src/applications/nuance/phid/NuanceQueuePHIDType.php index b51812320d..10cc4b707a 100644 --- a/src/applications/nuance/phid/NuanceQueuePHIDType.php +++ b/src/applications/nuance/phid/NuanceQueuePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/nuance/phid/NuanceSourcePHIDType.php b/src/applications/nuance/phid/NuanceSourcePHIDType.php index 774939bf29..17071684e8 100644 --- a/src/applications/nuance/phid/NuanceSourcePHIDType.php +++ b/src/applications/nuance/phid/NuanceSourcePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/nuance/query/NuanceItemSearchEngine.php b/src/applications/nuance/query/NuanceItemSearchEngine.php index 0868d7551a..4b65c73394 100644 --- a/src/applications/nuance/query/NuanceItemSearchEngine.php +++ b/src/applications/nuance/query/NuanceItemSearchEngine.php @@ -4,7 +4,7 @@ final class NuanceItemSearchEngine extends PhabricatorApplicationSearchEngine { public function getApplicationClassName() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } public function getResultTypeDescription() { diff --git a/src/applications/nuance/query/NuanceItemTransactionQuery.php b/src/applications/nuance/query/NuanceItemTransactionQuery.php index a2a29fc218..9b78d21a73 100644 --- a/src/applications/nuance/query/NuanceItemTransactionQuery.php +++ b/src/applications/nuance/query/NuanceItemTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new NuanceItemTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorNuanceApplication::class; + } + } diff --git a/src/applications/nuance/query/NuanceQuery.php b/src/applications/nuance/query/NuanceQuery.php index 1e72f5501e..b8e05f4dfe 100644 --- a/src/applications/nuance/query/NuanceQuery.php +++ b/src/applications/nuance/query/NuanceQuery.php @@ -3,7 +3,7 @@ abstract class NuanceQuery extends PhabricatorCursorPagedPolicyAwareQuery { public function getQueryApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } } diff --git a/src/applications/nuance/query/NuanceQueueSearchEngine.php b/src/applications/nuance/query/NuanceQueueSearchEngine.php index 2f794c2a9c..7f3188b0c1 100644 --- a/src/applications/nuance/query/NuanceQueueSearchEngine.php +++ b/src/applications/nuance/query/NuanceQueueSearchEngine.php @@ -4,7 +4,7 @@ final class NuanceQueueSearchEngine extends PhabricatorApplicationSearchEngine { public function getApplicationClassName() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } public function getResultTypeDescription() { diff --git a/src/applications/nuance/query/NuanceQueueTransactionQuery.php b/src/applications/nuance/query/NuanceQueueTransactionQuery.php index 6179e135e9..d8e209215c 100644 --- a/src/applications/nuance/query/NuanceQueueTransactionQuery.php +++ b/src/applications/nuance/query/NuanceQueueTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new NuanceQueueTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorNuanceApplication::class; + } + } diff --git a/src/applications/nuance/query/NuanceSourceSearchEngine.php b/src/applications/nuance/query/NuanceSourceSearchEngine.php index 44f131aa1b..7a1690eedf 100644 --- a/src/applications/nuance/query/NuanceSourceSearchEngine.php +++ b/src/applications/nuance/query/NuanceSourceSearchEngine.php @@ -4,7 +4,7 @@ final class NuanceSourceSearchEngine extends PhabricatorApplicationSearchEngine { public function getApplicationClassName() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } public function getResultTypeDescription() { diff --git a/src/applications/nuance/query/NuanceSourceTransactionQuery.php b/src/applications/nuance/query/NuanceSourceTransactionQuery.php index b87d06c815..937d1154ad 100644 --- a/src/applications/nuance/query/NuanceSourceTransactionQuery.php +++ b/src/applications/nuance/query/NuanceSourceTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new NuanceSourceTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorNuanceApplication::class; + } + } diff --git a/src/applications/nuance/typeahead/NuanceQueueDatasource.php b/src/applications/nuance/typeahead/NuanceQueueDatasource.php index 15b01fcecd..916dfaa75f 100644 --- a/src/applications/nuance/typeahead/NuanceQueueDatasource.php +++ b/src/applications/nuance/typeahead/NuanceQueueDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorNuanceApplication'; + return PhabricatorNuanceApplication::class; } public function loadResults() { diff --git a/src/applications/oauthserver/editor/PhabricatorOAuthServerEditEngine.php b/src/applications/oauthserver/editor/PhabricatorOAuthServerEditEngine.php index ad47552c19..276863b7f3 100644 --- a/src/applications/oauthserver/editor/PhabricatorOAuthServerEditEngine.php +++ b/src/applications/oauthserver/editor/PhabricatorOAuthServerEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorOAuthServerApplication'; + return PhabricatorOAuthServerApplication::class; } protected function newEditableObject() { diff --git a/src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php b/src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php index 32b9d45054..27454e8777 100644 --- a/src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php +++ b/src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php @@ -4,7 +4,7 @@ final class PhabricatorOAuthServerEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorOAuthServerApplication'; + return PhabricatorOAuthServerApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/oauthserver/phid/PhabricatorOAuthServerClientAuthorizationPHIDType.php b/src/applications/oauthserver/phid/PhabricatorOAuthServerClientAuthorizationPHIDType.php index b2fc1554fd..071fa5a8f6 100644 --- a/src/applications/oauthserver/phid/PhabricatorOAuthServerClientAuthorizationPHIDType.php +++ b/src/applications/oauthserver/phid/PhabricatorOAuthServerClientAuthorizationPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorOAuthServerApplication'; + return PhabricatorOAuthServerApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/oauthserver/phid/PhabricatorOAuthServerClientPHIDType.php b/src/applications/oauthserver/phid/PhabricatorOAuthServerClientPHIDType.php index 4d3d64738b..0fb623903b 100644 --- a/src/applications/oauthserver/phid/PhabricatorOAuthServerClientPHIDType.php +++ b/src/applications/oauthserver/phid/PhabricatorOAuthServerClientPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorOAuthServerApplication'; + return PhabricatorOAuthServerApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php b/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php index 63a62e8cd9..6a431daf3a 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php @@ -79,7 +79,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorOAuthServerApplication'; + return PhabricatorOAuthServerApplication::class; } } diff --git a/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php b/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php index 46fc514824..e500b3f42f 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthServerClientQuery.php @@ -67,7 +67,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorOAuthServerApplication'; + return PhabricatorOAuthServerApplication::class; } } diff --git a/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php b/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php index e07b1ea2c2..064ff34a26 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorOAuthServerApplication'; + return PhabricatorOAuthServerApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/oauthserver/query/PhabricatorOAuthServerTransactionQuery.php b/src/applications/oauthserver/query/PhabricatorOAuthServerTransactionQuery.php index 4dd21e2609..b11139f3ee 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthServerTransactionQuery.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthServerTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorOAuthServerTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorOAuthServerApplication::class; + } + } diff --git a/src/applications/owners/application/PhabricatorOwnersApplication.php b/src/applications/owners/application/PhabricatorOwnersApplication.php index 3ef5f974d9..34bbc4b7b5 100644 --- a/src/applications/owners/application/PhabricatorOwnersApplication.php +++ b/src/applications/owners/application/PhabricatorOwnersApplication.php @@ -45,6 +45,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('O'); + } + public function getRoutes() { return array( '/owners/' => array( diff --git a/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php b/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php index 416f2e38f2..7defcaacc0 100644 --- a/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php +++ b/src/applications/owners/editor/PhabricatorOwnersPackageEditEngine.php @@ -18,7 +18,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } protected function newEditableObject() { diff --git a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php index 8885872706..797fe1ff44 100644 --- a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php +++ b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php @@ -4,7 +4,7 @@ final class PhabricatorOwnersPackageTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/owners/phid/PhabricatorOwnersPackagePHIDType.php b/src/applications/owners/phid/PhabricatorOwnersPackagePHIDType.php index fbff6a2103..a780c31709 100644 --- a/src/applications/owners/phid/PhabricatorOwnersPackagePHIDType.php +++ b/src/applications/owners/phid/PhabricatorOwnersPackagePHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php index a80b972548..71ba6ebd52 100644 --- a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php +++ b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php @@ -284,7 +284,7 @@ protected function newPagingMapFromPartialObject($object) { } public function getQueryApplicationClass() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php b/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php index 26889c0e7a..d0f2790ba7 100644 --- a/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php +++ b/src/applications/owners/query/PhabricatorOwnersPackageSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } public function newQuery() { diff --git a/src/applications/owners/query/PhabricatorOwnersPackageTransactionQuery.php b/src/applications/owners/query/PhabricatorOwnersPackageTransactionQuery.php index e0e7c2792f..06989ab52f 100644 --- a/src/applications/owners/query/PhabricatorOwnersPackageTransactionQuery.php +++ b/src/applications/owners/query/PhabricatorOwnersPackageTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorOwnersPackageTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorOwnersApplication::class; + } + } diff --git a/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php b/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php index ea60acb54c..8ad4db33f6 100644 --- a/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php +++ b/src/applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } public function loadResults() { @@ -26,7 +26,7 @@ public function loadResults() { // If the user is querying by monogram explicitly, like "O123", do an ID // search. Otherwise, do an ngram substring search. - if (preg_match('/^[oO]\d+\z/', $raw_query)) { + if ($raw_query && preg_match('/^[oO]\d+\z/', $raw_query)) { $id = trim($raw_query, 'oO'); $id = (int)$id; $query->withIDs(array($id)); diff --git a/src/applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php b/src/applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php index 2ad0512ee2..a8f4c12b19 100644 --- a/src/applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php +++ b/src/applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php b/src/applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php index 2a0b16e60f..ee60a85615 100644 --- a/src/applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php +++ b/src/applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorOwnersApplication'; + return PhabricatorOwnersApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/packages/editor/PhabricatorPackagesEditEngine.php b/src/applications/packages/editor/PhabricatorPackagesEditEngine.php index 1ba71d7274..3bd4d29395 100644 --- a/src/applications/packages/editor/PhabricatorPackagesEditEngine.php +++ b/src/applications/packages/editor/PhabricatorPackagesEditEngine.php @@ -8,7 +8,7 @@ public function isEngineConfigurable() { } public function getEngineApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } } diff --git a/src/applications/packages/editor/PhabricatorPackagesEditor.php b/src/applications/packages/editor/PhabricatorPackagesEditor.php index 492b14643a..10d4c608a3 100644 --- a/src/applications/packages/editor/PhabricatorPackagesEditor.php +++ b/src/applications/packages/editor/PhabricatorPackagesEditor.php @@ -4,7 +4,7 @@ abstract class PhabricatorPackagesEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } protected function supportsSearch() { diff --git a/src/applications/packages/phid/PhabricatorPackagesPackagePHIDType.php b/src/applications/packages/phid/PhabricatorPackagesPackagePHIDType.php index 6f8e982c81..3f8ba658c9 100644 --- a/src/applications/packages/phid/PhabricatorPackagesPackagePHIDType.php +++ b/src/applications/packages/phid/PhabricatorPackagesPackagePHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/packages/phid/PhabricatorPackagesPublisherPHIDType.php b/src/applications/packages/phid/PhabricatorPackagesPublisherPHIDType.php index f746ddce22..c63f27652c 100644 --- a/src/applications/packages/phid/PhabricatorPackagesPublisherPHIDType.php +++ b/src/applications/packages/phid/PhabricatorPackagesPublisherPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/packages/phid/PhabricatorPackagesVersionPHIDType.php b/src/applications/packages/phid/PhabricatorPackagesVersionPHIDType.php index 203cca20e0..ffad58ffcf 100644 --- a/src/applications/packages/phid/PhabricatorPackagesVersionPHIDType.php +++ b/src/applications/packages/phid/PhabricatorPackagesVersionPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/packages/query/PhabricatorPackagesPackageSearchEngine.php b/src/applications/packages/query/PhabricatorPackagesPackageSearchEngine.php index 817321dec1..5c8c465085 100644 --- a/src/applications/packages/query/PhabricatorPackagesPackageSearchEngine.php +++ b/src/applications/packages/query/PhabricatorPackagesPackageSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } public function newQuery() { diff --git a/src/applications/packages/query/PhabricatorPackagesPackageTransactionQuery.php b/src/applications/packages/query/PhabricatorPackagesPackageTransactionQuery.php index 146f138119..fee7b2d4b8 100644 --- a/src/applications/packages/query/PhabricatorPackagesPackageTransactionQuery.php +++ b/src/applications/packages/query/PhabricatorPackagesPackageTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorPackagesPackageTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPackagesApplication::class; + } + } diff --git a/src/applications/packages/query/PhabricatorPackagesPublisherSearchEngine.php b/src/applications/packages/query/PhabricatorPackagesPublisherSearchEngine.php index be7b83f5fc..03e2e968a4 100644 --- a/src/applications/packages/query/PhabricatorPackagesPublisherSearchEngine.php +++ b/src/applications/packages/query/PhabricatorPackagesPublisherSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } public function newQuery() { diff --git a/src/applications/packages/query/PhabricatorPackagesPublisherTransactionQuery.php b/src/applications/packages/query/PhabricatorPackagesPublisherTransactionQuery.php index e7af82cfa8..111af3c5c6 100644 --- a/src/applications/packages/query/PhabricatorPackagesPublisherTransactionQuery.php +++ b/src/applications/packages/query/PhabricatorPackagesPublisherTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorPackagesPublisherTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPackagesApplication::class; + } + } diff --git a/src/applications/packages/query/PhabricatorPackagesQuery.php b/src/applications/packages/query/PhabricatorPackagesQuery.php index e7e882bdad..1ce60d9486 100644 --- a/src/applications/packages/query/PhabricatorPackagesQuery.php +++ b/src/applications/packages/query/PhabricatorPackagesQuery.php @@ -4,7 +4,7 @@ abstract class PhabricatorPackagesQuery extends PhabricatorCursorPagedPolicyAwareQuery { public function getQueryApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } protected function buildFullKeyClauseParts( diff --git a/src/applications/packages/query/PhabricatorPackagesVersionSearchEngine.php b/src/applications/packages/query/PhabricatorPackagesVersionSearchEngine.php index b3592ffd70..464b423ee4 100644 --- a/src/applications/packages/query/PhabricatorPackagesVersionSearchEngine.php +++ b/src/applications/packages/query/PhabricatorPackagesVersionSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } public function newQuery() { diff --git a/src/applications/packages/query/PhabricatorPackagesVersionTransactionQuery.php b/src/applications/packages/query/PhabricatorPackagesVersionTransactionQuery.php index 7ab10e2490..a414d0ba04 100644 --- a/src/applications/packages/query/PhabricatorPackagesVersionTransactionQuery.php +++ b/src/applications/packages/query/PhabricatorPackagesVersionTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorPackagesVersionTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPackagesApplication::class; + } + } diff --git a/src/applications/packages/typeahead/PhabricatorPackagesPackageDatasource.php b/src/applications/packages/typeahead/PhabricatorPackagesPackageDatasource.php index 9b2296db12..f9e88c7ac0 100644 --- a/src/applications/packages/typeahead/PhabricatorPackagesPackageDatasource.php +++ b/src/applications/packages/typeahead/PhabricatorPackagesPackageDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } public function loadResults() { diff --git a/src/applications/packages/typeahead/PhabricatorPackagesPublisherDatasource.php b/src/applications/packages/typeahead/PhabricatorPackagesPublisherDatasource.php index e8ca7d4c7f..d8fe5ffc52 100644 --- a/src/applications/packages/typeahead/PhabricatorPackagesPublisherDatasource.php +++ b/src/applications/packages/typeahead/PhabricatorPackagesPublisherDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPackagesApplication'; + return PhabricatorPackagesApplication::class; } public function loadResults() { diff --git a/src/applications/passphrase/application/PhabricatorPassphraseApplication.php b/src/applications/passphrase/application/PhabricatorPassphraseApplication.php index 2ab4f1e33d..ae79e5c8b5 100644 --- a/src/applications/passphrase/application/PhabricatorPassphraseApplication.php +++ b/src/applications/passphrase/application/PhabricatorPassphraseApplication.php @@ -34,6 +34,10 @@ public function canUninstall() { return false; } + public function getMonograms() { + return array('K'); + } + public function getRoutes() { return array( '/K(?P\d+)' => 'PassphraseCredentialViewController', diff --git a/src/applications/passphrase/controller/PassphraseCredentialEditController.php b/src/applications/passphrase/controller/PassphraseCredentialEditController.php index a3a346c20f..7247321648 100644 --- a/src/applications/passphrase/controller/PassphraseCredentialEditController.php +++ b/src/applications/passphrase/controller/PassphraseCredentialEditController.php @@ -45,6 +45,12 @@ public function handleRequest(AphrontRequest $request) { // Prefill username if provided. $credential->setUsername((string)$request->getStr('username')); + // Prefill name if provided. + $credential->setName((string)$request->getStr('name')); + + // Prefill description if provided. + $credential->setDescription((string)$request->getStr('description')); + if (!$request->getStr('isInitialized')) { $type->didInitializeNewCredential($viewer, $credential); } diff --git a/src/applications/passphrase/editor/PassphraseCredentialTransactionEditor.php b/src/applications/passphrase/editor/PassphraseCredentialTransactionEditor.php index 1e23c874cf..ead8658543 100644 --- a/src/applications/passphrase/editor/PassphraseCredentialTransactionEditor.php +++ b/src/applications/passphrase/editor/PassphraseCredentialTransactionEditor.php @@ -4,7 +4,7 @@ final class PassphraseCredentialTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPassphraseApplication'; + return PhabricatorPassphraseApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/passphrase/phid/PassphraseCredentialPHIDType.php b/src/applications/passphrase/phid/PassphraseCredentialPHIDType.php index 25681906e3..76fb537554 100644 --- a/src/applications/passphrase/phid/PassphraseCredentialPHIDType.php +++ b/src/applications/passphrase/phid/PassphraseCredentialPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPassphraseApplication'; + return PhabricatorPassphraseApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/passphrase/query/PassphraseCredentialQuery.php b/src/applications/passphrase/query/PassphraseCredentialQuery.php index 98ae9429a8..33a6947324 100644 --- a/src/applications/passphrase/query/PassphraseCredentialQuery.php +++ b/src/applications/passphrase/query/PassphraseCredentialQuery.php @@ -155,7 +155,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPassphraseApplication'; + return PhabricatorPassphraseApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/passphrase/query/PassphraseCredentialSearchEngine.php b/src/applications/passphrase/query/PassphraseCredentialSearchEngine.php index 971094afc4..24cc6609ff 100644 --- a/src/applications/passphrase/query/PassphraseCredentialSearchEngine.php +++ b/src/applications/passphrase/query/PassphraseCredentialSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPassphraseApplication'; + return PhabricatorPassphraseApplication::class; } public function newQuery() { diff --git a/src/applications/passphrase/query/PassphraseCredentialTransactionQuery.php b/src/applications/passphrase/query/PassphraseCredentialTransactionQuery.php index ebc5237091..a76789e7ca 100644 --- a/src/applications/passphrase/query/PassphraseCredentialTransactionQuery.php +++ b/src/applications/passphrase/query/PassphraseCredentialTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PassphraseCredentialTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPassphraseApplication::class; + } + } diff --git a/src/applications/passphrase/storage/PassphraseCredential.php b/src/applications/passphrase/storage/PassphraseCredential.php index c470ea661f..d180fc6b31 100644 --- a/src/applications/passphrase/storage/PassphraseCredential.php +++ b/src/applications/passphrase/storage/PassphraseCredential.php @@ -5,6 +5,7 @@ final class PassphraseCredential extends PassphraseDAO PhabricatorApplicationTransactionInterface, PhabricatorPolicyInterface, PhabricatorFlaggableInterface, + PhabricatorMentionableInterface, PhabricatorSubscribableInterface, PhabricatorDestructibleInterface, PhabricatorSpacesInterface, diff --git a/src/applications/passphrase/view/PassphraseCredentialControl.php b/src/applications/passphrase/view/PassphraseCredentialControl.php index 8ba1ef9bc8..98294832f7 100644 --- a/src/applications/passphrase/view/PassphraseCredentialControl.php +++ b/src/applications/passphrase/view/PassphraseCredentialControl.php @@ -50,7 +50,8 @@ protected function renderInput() { // credential. Populate it into the menu to allow them to save the form // without making any changes. $current_phid = $this->getValue(); - if (strlen($current_phid) && empty($options_map[$current_phid])) { + if (phutil_nonempty_string($current_phid) && + empty($options_map[$current_phid])) { $viewer = $this->getViewer(); $current_name = null; diff --git a/src/applications/paste/application/PhabricatorPasteApplication.php b/src/applications/paste/application/PhabricatorPasteApplication.php index 26e4b1740e..f6b6f7cf65 100644 --- a/src/applications/paste/application/PhabricatorPasteApplication.php +++ b/src/applications/paste/application/PhabricatorPasteApplication.php @@ -32,6 +32,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('P'); + } + public function getRoutes() { return array( '/P(?P[1-9]\d*)(?:\$(?P\d+(?:-\d+)?))?' diff --git a/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php b/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php index c62ecd0494..d55d184874 100644 --- a/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php +++ b/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php @@ -43,7 +43,7 @@ protected function execute(ConduitAPIRequest $request) { $title = $request->getValue('title'); $language = $request->getValue('language'); - if (!strlen($content)) { + if (!phutil_nonempty_string($content)) { throw new ConduitException('ERR-NO-PASTE'); } diff --git a/src/applications/paste/editor/PhabricatorPasteEditEngine.php b/src/applications/paste/editor/PhabricatorPasteEditEngine.php index 146565e87e..31859e7530 100644 --- a/src/applications/paste/editor/PhabricatorPasteEditEngine.php +++ b/src/applications/paste/editor/PhabricatorPasteEditEngine.php @@ -18,7 +18,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorPasteApplication'; + return PhabricatorPasteApplication::class; } protected function newEditableObject() { diff --git a/src/applications/paste/editor/PhabricatorPasteEditor.php b/src/applications/paste/editor/PhabricatorPasteEditor.php index b120f58a57..1af612b08e 100644 --- a/src/applications/paste/editor/PhabricatorPasteEditor.php +++ b/src/applications/paste/editor/PhabricatorPasteEditor.php @@ -10,7 +10,7 @@ public function getNewPasteTitle() { } public function getEditorApplicationClass() { - return 'PhabricatorPasteApplication'; + return PhabricatorPasteApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/paste/phid/PhabricatorPastePastePHIDType.php b/src/applications/paste/phid/PhabricatorPastePastePHIDType.php index d08d52e2e1..b04553aa14 100644 --- a/src/applications/paste/phid/PhabricatorPastePastePHIDType.php +++ b/src/applications/paste/phid/PhabricatorPastePastePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPasteApplication'; + return PhabricatorPasteApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/paste/query/PhabricatorPasteQuery.php b/src/applications/paste/query/PhabricatorPasteQuery.php index e841bff300..bcc1560700 100644 --- a/src/applications/paste/query/PhabricatorPasteQuery.php +++ b/src/applications/paste/query/PhabricatorPasteQuery.php @@ -390,7 +390,7 @@ private function highlightSource($source, $title, $language) { } public function getQueryApplicationClass() { - return 'PhabricatorPasteApplication'; + return PhabricatorPasteApplication::class; } } diff --git a/src/applications/paste/query/PhabricatorPasteSearchEngine.php b/src/applications/paste/query/PhabricatorPasteSearchEngine.php index e269088f60..88482b94cc 100644 --- a/src/applications/paste/query/PhabricatorPasteSearchEngine.php +++ b/src/applications/paste/query/PhabricatorPasteSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPasteApplication'; + return PhabricatorPasteApplication::class; } public function newQuery() { diff --git a/src/applications/paste/query/PhabricatorPasteTransactionQuery.php b/src/applications/paste/query/PhabricatorPasteTransactionQuery.php index b0e9b5bc47..00c70084b0 100644 --- a/src/applications/paste/query/PhabricatorPasteTransactionQuery.php +++ b/src/applications/paste/query/PhabricatorPasteTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorPasteTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPasteApplication::class; + } + } diff --git a/src/applications/paste/typeahead/PasteLanguageSelectDatasource.php b/src/applications/paste/typeahead/PasteLanguageSelectDatasource.php index 45c36db968..4bdf0b545c 100644 --- a/src/applications/paste/typeahead/PasteLanguageSelectDatasource.php +++ b/src/applications/paste/typeahead/PasteLanguageSelectDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPasteApplication'; + return PhabricatorPasteApplication::class; } public function loadResults() { diff --git a/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php b/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php index 8babff859f..195d880fff 100644 --- a/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php +++ b/src/applications/people/cache/PhabricatorUserProfileImageCacheType.php @@ -91,6 +91,9 @@ public function shouldValidateRawCacheData() { } public function isRawCacheDataValid(PhabricatorUser $user, $key, $data) { + if ($data === null) { + return false; + } $parts = explode(',', $data, 2); $version = reset($parts); return ($version === $this->getCacheVersion($user)); diff --git a/src/applications/people/config/PhabricatorUserConfigOptions.php b/src/applications/people/config/PhabricatorUserConfigOptions.php index 3ef736c8ca..66b85a6507 100644 --- a/src/applications/people/config/PhabricatorUserConfigOptions.php +++ b/src/applications/people/config/PhabricatorUserConfigOptions.php @@ -39,12 +39,22 @@ public function getOptions() { $custom_field_type = 'custom:PhabricatorCustomFieldConfigOptionType'; + $fields_description = $this->deformat(pht(<<newOption('user.fields', $custom_field_type, $default) ->setCustomData(id(new PhabricatorUser())->getCustomFieldBaseClass()) ->setDescription(pht('Select and reorder user profile fields.')), $this->newOption('user.custom-field-definitions', 'wild', array()) - ->setDescription(pht('Add new simple fields to user profiles.')), + ->setDescription($fields_description), $this->newOption('user.require-real-name', 'bool', true) ->setDescription(pht('Always require real name for user profiles.')) ->setBoolOptions( diff --git a/src/applications/people/controller/PhabricatorPeopleDeleteController.php b/src/applications/people/controller/PhabricatorPeopleDeleteController.php index 8e6ac91da7..2f45b40d31 100644 --- a/src/applications/people/controller/PhabricatorPeopleDeleteController.php +++ b/src/applications/people/controller/PhabricatorPeopleDeleteController.php @@ -27,9 +27,12 @@ public function handleRequest(AphrontRequest $request) { 'To permanently destroy this user, run this command from the '. 'command line:')) ->appendCommand( - csprintf( - 'phabricator/ $ ./bin/remove destroy %R', - $user->getMonogram())) + hsprintf( + '%s $ %s', + PlatformSymbols::getPlatformServerPath(), + csprintf( + './bin/remove destroy %R', + $user->getMonogram()))) ->appendParagraph( pht( 'Unless you have a very good reason to delete this user, consider '. diff --git a/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php b/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php index d50eccff13..72f90f024b 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php @@ -27,6 +27,12 @@ public function handleRequest(AphrontRequest $request) { $done_uri = '/p/'.$name.'/'; $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); @@ -59,9 +65,7 @@ public function handleRequest(AphrontRequest $request) { if (!$errors && !$is_default) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } else { $xform = PhabricatorFileTransform::getTransformByKey( PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); @@ -250,8 +254,7 @@ public function handleRequest(AphrontRequest $request) { ->setName('picture') ->setLabel(pht('Upload Picture')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($done_uri) diff --git a/src/applications/people/editor/PhabricatorUserEditEngine.php b/src/applications/people/editor/PhabricatorUserEditEngine.php index 4b3a60a2fc..05738145f9 100644 --- a/src/applications/people/editor/PhabricatorUserEditEngine.php +++ b/src/applications/people/editor/PhabricatorUserEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } protected function newEditableObject() { diff --git a/src/applications/people/editor/PhabricatorUserTransactionEditor.php b/src/applications/people/editor/PhabricatorUserTransactionEditor.php index 929b2e224c..88c27d7ee8 100644 --- a/src/applications/people/editor/PhabricatorUserTransactionEditor.php +++ b/src/applications/people/editor/PhabricatorUserTransactionEditor.php @@ -4,7 +4,7 @@ final class PhabricatorUserTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php b/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php index 868cc13da1..4bbbb2466e 100644 --- a/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php +++ b/src/applications/people/mail/PhabricatorPeopleWelcomeMailEngine.php @@ -118,14 +118,14 @@ private function newBody() { $recipient = $this->getRecipient(); $custom_body = $this->getWelcomeMessage(); - if (strlen($custom_body)) { + if (phutil_nonempty_string($custom_body)) { return $this->newRemarkupText($custom_body); } $default_body = PhabricatorAuthMessage::loadMessageText( $recipient, PhabricatorAuthWelcomeMailMessageType::MESSAGEKEY); - if (strlen($default_body)) { + if (phutil_nonempty_string($default_body)) { return $this->newRemarkupText($default_body); } diff --git a/src/applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php b/src/applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php index 71f3aa1392..470eb52f12 100644 --- a/src/applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php +++ b/src/applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php @@ -22,7 +22,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/people/menuitem/PhabricatorPeopleCommitsProfileMenuItem.php b/src/applications/people/menuitem/PhabricatorPeopleCommitsProfileMenuItem.php index b6c1c446cc..20e1b1f35a 100644 --- a/src/applications/people/menuitem/PhabricatorPeopleCommitsProfileMenuItem.php +++ b/src/applications/people/menuitem/PhabricatorPeopleCommitsProfileMenuItem.php @@ -22,7 +22,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/people/menuitem/PhabricatorPeopleManageProfileMenuItem.php b/src/applications/people/menuitem/PhabricatorPeopleManageProfileMenuItem.php index 43d2271a79..127d48ae39 100644 --- a/src/applications/people/menuitem/PhabricatorPeopleManageProfileMenuItem.php +++ b/src/applications/people/menuitem/PhabricatorPeopleManageProfileMenuItem.php @@ -22,7 +22,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php b/src/applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php index cfa760fcd6..1c823ec091 100644 --- a/src/applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php +++ b/src/applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php @@ -22,7 +22,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/people/menuitem/PhabricatorPeopleTasksProfileMenuItem.php b/src/applications/people/menuitem/PhabricatorPeopleTasksProfileMenuItem.php index 5dea58cb29..baffadc6d8 100644 --- a/src/applications/people/menuitem/PhabricatorPeopleTasksProfileMenuItem.php +++ b/src/applications/people/menuitem/PhabricatorPeopleTasksProfileMenuItem.php @@ -22,7 +22,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/people/phid/PhabricatorPeopleExternalIdentifierPHIDType.php b/src/applications/people/phid/PhabricatorPeopleExternalIdentifierPHIDType.php index acd9a24f56..2e2862892f 100644 --- a/src/applications/people/phid/PhabricatorPeopleExternalIdentifierPHIDType.php +++ b/src/applications/people/phid/PhabricatorPeopleExternalIdentifierPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/people/phid/PhabricatorPeopleExternalPHIDType.php b/src/applications/people/phid/PhabricatorPeopleExternalPHIDType.php index 4be4cc5359..ebda57c423 100644 --- a/src/applications/people/phid/PhabricatorPeopleExternalPHIDType.php +++ b/src/applications/people/phid/PhabricatorPeopleExternalPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/people/phid/PhabricatorPeopleUserEmailPHIDType.php b/src/applications/people/phid/PhabricatorPeopleUserEmailPHIDType.php index e0f0478033..206b27d77e 100644 --- a/src/applications/people/phid/PhabricatorPeopleUserEmailPHIDType.php +++ b/src/applications/people/phid/PhabricatorPeopleUserEmailPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/people/phid/PhabricatorPeopleUserPHIDType.php b/src/applications/people/phid/PhabricatorPeopleUserPHIDType.php index 7867f098f1..65139814b7 100644 --- a/src/applications/people/phid/PhabricatorPeopleUserPHIDType.php +++ b/src/applications/people/phid/PhabricatorPeopleUserPHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/people/query/PhabricatorPeopleLogQuery.php b/src/applications/people/query/PhabricatorPeopleLogQuery.php index e87fb33660..d439894e5c 100644 --- a/src/applications/people/query/PhabricatorPeopleLogQuery.php +++ b/src/applications/people/query/PhabricatorPeopleLogQuery.php @@ -129,7 +129,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } } diff --git a/src/applications/people/query/PhabricatorPeopleLogSearchEngine.php b/src/applications/people/query/PhabricatorPeopleLogSearchEngine.php index 7e7fca1cd4..fc1819debc 100644 --- a/src/applications/people/query/PhabricatorPeopleLogSearchEngine.php +++ b/src/applications/people/query/PhabricatorPeopleLogSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getPageSize(PhabricatorSavedQuery $saved) { diff --git a/src/applications/people/query/PhabricatorPeopleQuery.php b/src/applications/people/query/PhabricatorPeopleQuery.php index b74b936ba8..acbeb3b34f 100644 --- a/src/applications/people/query/PhabricatorPeopleQuery.php +++ b/src/applications/people/query/PhabricatorPeopleQuery.php @@ -360,7 +360,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getOrderableColumns() { diff --git a/src/applications/people/query/PhabricatorPeopleSearchEngine.php b/src/applications/people/query/PhabricatorPeopleSearchEngine.php index 57ed133df4..1e0c0052c1 100644 --- a/src/applications/people/query/PhabricatorPeopleSearchEngine.php +++ b/src/applications/people/query/PhabricatorPeopleSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function newQuery() { @@ -200,6 +200,7 @@ protected function getURI($path) { protected function getBuiltinQueryNames() { $names = array( 'active' => pht('Active'), + 'admin' => pht('Administrators'), 'all' => pht('All'), ); @@ -221,6 +222,9 @@ public function buildSavedQueryFromBuiltin($query_key) { case 'active': return $query ->setParameter('isDisabled', false); + case 'admin': + return $query + ->setParameter('isAdmin', true); case 'approval': return $query ->setParameter('needsApproval', true) diff --git a/src/applications/people/query/PhabricatorPeopleTransactionQuery.php b/src/applications/people/query/PhabricatorPeopleTransactionQuery.php index 898bc9ee05..564cd96df8 100644 --- a/src/applications/people/query/PhabricatorPeopleTransactionQuery.php +++ b/src/applications/people/query/PhabricatorPeopleTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorUserTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPeopleApplication::class; + } + } diff --git a/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php b/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php index ead44a56dc..178a3b9460 100644 --- a/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php +++ b/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php @@ -71,7 +71,7 @@ protected function willLoadPage(array $page) { } public function getQueryApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } } diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index 7276253085..82f1affe82 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -321,12 +321,28 @@ private function generateConduitCertificate() { const EMAIL_CYCLE_FREQUENCY = 86400; const EMAIL_TOKEN_LENGTH = 24; + /** + * This function removes the blurb from a profile. + * This is an incredibly broad hammer to handle some spam on the upstream, + * which will be refined later. + * + * @return void + */ + private function cleanUpProfile() { + $this->profile->setBlurb(''); + } + public function getUserProfile() { return $this->assertAttached($this->profile); } public function attachUserProfile(PhabricatorUserProfile $profile) { $this->profile = $profile; + + if ($this->isDisabled) { + $this->cleanUpProfile(); + } + return $this; } @@ -343,6 +359,10 @@ public function loadUserProfile() { $this->profile = PhabricatorUserProfile::initializeNewProfile($this); } + if ($this->isDisabled) { + $this->cleanUpProfile(); + } + return $this->profile; } @@ -1139,7 +1159,7 @@ public function getSSHPublicKeyManagementURI(PhabricatorUser $viewer) { } public function getSSHKeyDefaultName() { - return 'id_rsa_phabricator'; + return 'id_rsa_phorge'; } public function getSSHKeyNotifyPHIDs() { diff --git a/src/applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php b/src/applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php index 2a2a451a49..5a8e70f1b8 100644 --- a/src/applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php +++ b/src/applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/people/typeahead/PhabricatorPeopleDatasource.php b/src/applications/people/typeahead/PhabricatorPeopleDatasource.php index d4a5ad96c7..c06ba5b458 100644 --- a/src/applications/people/typeahead/PhabricatorPeopleDatasource.php +++ b/src/applications/people/typeahead/PhabricatorPeopleDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function loadResults() { diff --git a/src/applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php b/src/applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php index fb3a6226d9..cd3f2939b9 100644 --- a/src/applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php +++ b/src/applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php @@ -14,7 +14,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/people/typeahead/PhabricatorUserLogTypeDatasource.php b/src/applications/people/typeahead/PhabricatorUserLogTypeDatasource.php index 39241a020c..26ccdfe2ea 100644 --- a/src/applications/people/typeahead/PhabricatorUserLogTypeDatasource.php +++ b/src/applications/people/typeahead/PhabricatorUserLogTypeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function loadResults() { diff --git a/src/applications/people/typeahead/PhabricatorViewerDatasource.php b/src/applications/people/typeahead/PhabricatorViewerDatasource.php index e29fd4586b..6f6d1181fe 100644 --- a/src/applications/people/typeahead/PhabricatorViewerDatasource.php +++ b/src/applications/people/typeahead/PhabricatorViewerDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/people/view/PhabricatorUserLogView.php b/src/applications/people/view/PhabricatorUserLogView.php index cf728af973..ef4fa6f10c 100644 --- a/src/applications/people/view/PhabricatorUserLogView.php +++ b/src/applications/people/view/PhabricatorUserLogView.php @@ -36,7 +36,7 @@ public function render() { $rows = array(); foreach ($logs as $log) { - $session = substr($log->getSession(), 0, 6); + $session = substr(coalesce($log->getSession(), ''), 0, 6); $actor_phid = $log->getActorPHID(); $user_phid = $log->getUserPHID(); diff --git a/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php b/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php index d17418636f..3f3440ede0 100644 --- a/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php +++ b/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php @@ -56,9 +56,15 @@ public function getTitle() { '%s empowered this user as an administrator.', $this->renderAuthor()); } else { - return pht( - '%s defrocked this user.', - $this->renderAuthor()); + if (PhabricatorEnv::getEnvConfig('phabricator.serious-business')) { + return pht( + '%s removed the administrator role from this user.', + $this->renderAuthor()); + } else { + return pht( + '%s defrocked this user.', + $this->renderAuthor()); + } } } @@ -70,10 +76,17 @@ public function getTitleForFeed() { $this->renderAuthor(), $this->renderObject()); } else { - return pht( - '%s defrocked %s.', - $this->renderAuthor(), - $this->renderObject()); + if (PhabricatorEnv::getEnvConfig('phabricator.serious-business')) { + return pht( + '%s removed the administrator role from %s.', + $this->renderAuthor(), + $this->renderObject()); + } else { + return pht( + '%s defrocked %s.', + $this->renderAuthor(), + $this->renderObject()); + } } } diff --git a/src/applications/phame/application/PhabricatorPhameApplication.php b/src/applications/phame/application/PhabricatorPhameApplication.php index cd2eb4b487..b030673d1f 100644 --- a/src/applications/phame/application/PhabricatorPhameApplication.php +++ b/src/applications/phame/application/PhabricatorPhameApplication.php @@ -31,6 +31,10 @@ public function getHelpDocumentationArticles(PhabricatorUser $viewer) { ); } + public function getMonograms() { + return array('J'); + } + public function getRoutes() { return array( '/J(?P[1-9]\d*)' => 'PhamePostViewController', diff --git a/src/applications/phame/controller/PhameLiveController.php b/src/applications/phame/controller/PhameLiveController.php index 472f73c8c1..e87cfad138 100644 --- a/src/applications/phame/controller/PhameLiveController.php +++ b/src/applications/phame/controller/PhameLiveController.php @@ -87,7 +87,7 @@ protected function setupLiveEnvironment() { $this->isExternal = $is_external; $this->isLive = $is_live; - if (strlen($post_id)) { + if (phutil_nonempty_string($post_id)) { $post_query = id(new PhamePostQuery()) ->setViewer($viewer) ->needHeaderImage(true) diff --git a/src/applications/phame/controller/blog/PhameBlogFeedController.php b/src/applications/phame/controller/blog/PhameBlogFeedController.php index c0c6e60cc4..1523a8d5e6 100644 --- a/src/applications/phame/controller/blog/PhameBlogFeedController.php +++ b/src/applications/phame/controller/blog/PhameBlogFeedController.php @@ -71,6 +71,11 @@ public function handleRequest(AphrontRequest $request) { '%s', $bloggers[$post->getBloggerPHID()]->getFullName()); + $content[] = phutil_tag( + 'published', + array(), + date('c', $post->getDatePublished())); + $content[] = phutil_tag( 'updated', array(), diff --git a/src/applications/phame/controller/blog/PhameBlogHeaderPictureController.php b/src/applications/phame/controller/blog/PhameBlogHeaderPictureController.php index 0106b9571d..4bec8efecc 100644 --- a/src/applications/phame/controller/blog/PhameBlogHeaderPictureController.php +++ b/src/applications/phame/controller/blog/PhameBlogHeaderPictureController.php @@ -24,6 +24,12 @@ public function handleRequest(AphrontRequest $request) { $blog_uri = '/phame/blog/manage/'.$id; $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); $delete_header = ($request->getInt('delete') == 1); @@ -45,9 +51,7 @@ public function handleRequest(AphrontRequest $request) { if (!$errors && !$delete_header) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } } @@ -86,8 +90,7 @@ public function handleRequest(AphrontRequest $request) { ->setName('header') ->setLabel(pht('Upload Header')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormCheckboxControl()) ->setName('delete') diff --git a/src/applications/phame/controller/blog/PhameBlogProfilePictureController.php b/src/applications/phame/controller/blog/PhameBlogProfilePictureController.php index 4e69034253..91daa784cd 100644 --- a/src/applications/phame/controller/blog/PhameBlogProfilePictureController.php +++ b/src/applications/phame/controller/blog/PhameBlogProfilePictureController.php @@ -24,6 +24,12 @@ public function handleRequest(AphrontRequest $request) { $blog_uri = '/phame/blog/manage/'.$id; $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); @@ -56,9 +62,7 @@ public function handleRequest(AphrontRequest $request) { if (!$errors && !$is_default) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } else { $xform = PhabricatorFileTransform::getTransformByKey( PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); @@ -196,8 +200,7 @@ public function handleRequest(AphrontRequest $request) { ->setName('picture') ->setLabel(pht('Upload Picture')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($blog_uri) diff --git a/src/applications/phame/controller/blog/PhameBlogViewController.php b/src/applications/phame/controller/blog/PhameBlogViewController.php index 758fe955ad..d8b61d8352 100644 --- a/src/applications/phame/controller/blog/PhameBlogViewController.php +++ b/src/applications/phame/controller/blog/PhameBlogViewController.php @@ -117,6 +117,15 @@ public function handleRequest(AphrontRequest $request) { $about, )); + $page->addHeadItem(phutil_tag( + 'link', + array( + 'rel' => 'alternate', + 'type' => 'application/atom+xml', + 'href' => $blog->getFeedURI(), + 'title' => $blog->getName(), + ))); + return $page; } diff --git a/src/applications/phame/controller/post/PhamePostHeaderPictureController.php b/src/applications/phame/controller/post/PhamePostHeaderPictureController.php index 60075cc077..db8f662c39 100644 --- a/src/applications/phame/controller/post/PhamePostHeaderPictureController.php +++ b/src/applications/phame/controller/post/PhamePostHeaderPictureController.php @@ -24,6 +24,12 @@ public function handleRequest(AphrontRequest $request) { $post_uri = '/phame/post/view/'.$id; $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); $delete_header = ($request->getInt('delete') == 1); @@ -45,9 +51,7 @@ public function handleRequest(AphrontRequest $request) { if (!$errors && !$delete_header) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } } @@ -86,8 +90,7 @@ public function handleRequest(AphrontRequest $request) { ->setName('header') ->setLabel(pht('Upload Header')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormCheckboxControl()) ->setName('delete') diff --git a/src/applications/phame/editor/PhameBlogEditEngine.php b/src/applications/phame/editor/PhameBlogEditEngine.php index e11dec6847..9238f7ea01 100644 --- a/src/applications/phame/editor/PhameBlogEditEngine.php +++ b/src/applications/phame/editor/PhameBlogEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/phame/editor/PhameBlogEditor.php b/src/applications/phame/editor/PhameBlogEditor.php index 656ede95fe..eaf1389f17 100644 --- a/src/applications/phame/editor/PhameBlogEditor.php +++ b/src/applications/phame/editor/PhameBlogEditor.php @@ -4,7 +4,7 @@ final class PhameBlogEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phame/editor/PhamePostEditEngine.php b/src/applications/phame/editor/PhamePostEditEngine.php index 04738beb80..651f29b091 100644 --- a/src/applications/phame/editor/PhamePostEditEngine.php +++ b/src/applications/phame/editor/PhamePostEditEngine.php @@ -25,7 +25,7 @@ public function setBlog(PhameBlog $blog) { } public function getEngineApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } protected function newEditableObject() { diff --git a/src/applications/phame/editor/PhamePostEditor.php b/src/applications/phame/editor/PhamePostEditor.php index 61092114d1..60ea50aeec 100644 --- a/src/applications/phame/editor/PhamePostEditor.php +++ b/src/applications/phame/editor/PhamePostEditor.php @@ -4,7 +4,7 @@ final class PhamePostEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phame/herald/HeraldPhameBlogAdapter.php b/src/applications/phame/herald/HeraldPhameBlogAdapter.php index d9368a97d2..160387d24c 100644 --- a/src/applications/phame/herald/HeraldPhameBlogAdapter.php +++ b/src/applications/phame/herald/HeraldPhameBlogAdapter.php @@ -9,7 +9,7 @@ protected function newObject() { } public function getAdapterApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/phame/herald/HeraldPhamePostAdapter.php b/src/applications/phame/herald/HeraldPhamePostAdapter.php index 72e7124271..f9446c6866 100644 --- a/src/applications/phame/herald/HeraldPhamePostAdapter.php +++ b/src/applications/phame/herald/HeraldPhamePostAdapter.php @@ -9,7 +9,7 @@ protected function newObject() { } public function getAdapterApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/phame/phid/PhabricatorPhameBlogPHIDType.php b/src/applications/phame/phid/PhabricatorPhameBlogPHIDType.php index 905f4b893b..ccca276032 100644 --- a/src/applications/phame/phid/PhabricatorPhameBlogPHIDType.php +++ b/src/applications/phame/phid/PhabricatorPhameBlogPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phame/phid/PhabricatorPhamePostPHIDType.php b/src/applications/phame/phid/PhabricatorPhamePostPHIDType.php index 7c86d89d41..174043d206 100644 --- a/src/applications/phame/phid/PhabricatorPhamePostPHIDType.php +++ b/src/applications/phame/phid/PhabricatorPhamePostPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phame/query/PhameBlogSearchEngine.php b/src/applications/phame/query/PhameBlogSearchEngine.php index eaef32af1c..4b8c287173 100644 --- a/src/applications/phame/query/PhameBlogSearchEngine.php +++ b/src/applications/phame/query/PhameBlogSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function newQuery() { diff --git a/src/applications/phame/query/PhameBlogTransactionQuery.php b/src/applications/phame/query/PhameBlogTransactionQuery.php index 77a056b682..4c4c3be379 100644 --- a/src/applications/phame/query/PhameBlogTransactionQuery.php +++ b/src/applications/phame/query/PhameBlogTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhameBlogTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhameApplication::class; + } + } diff --git a/src/applications/phame/query/PhamePostSearchEngine.php b/src/applications/phame/query/PhamePostSearchEngine.php index d1d9a791ec..1cff6d1891 100644 --- a/src/applications/phame/query/PhamePostSearchEngine.php +++ b/src/applications/phame/query/PhamePostSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function newQuery() { diff --git a/src/applications/phame/query/PhamePostTransactionQuery.php b/src/applications/phame/query/PhamePostTransactionQuery.php index 879b6363e3..9b93f4a808 100644 --- a/src/applications/phame/query/PhamePostTransactionQuery.php +++ b/src/applications/phame/query/PhamePostTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhamePostTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhameApplication::class; + } + } diff --git a/src/applications/phame/storage/PhameBlog.php b/src/applications/phame/storage/PhameBlog.php index 95507d61dc..8ab5c2747e 100644 --- a/src/applications/phame/storage/PhameBlog.php +++ b/src/applications/phame/storage/PhameBlog.php @@ -154,7 +154,7 @@ public function validateCustomDomain($domain_full_uri) { $href = PhabricatorEnv::getProductionURI( '/config/edit/policy.allow-public/'); return pht( - 'For custom domains to work, this this server must be '. + 'For custom domains to work, this server must be '. 'configured to allow the public access policy. Configure this '. 'setting %s, or ask an administrator to configure this setting. '. 'The domain can be specified later once this setting has been '. @@ -169,7 +169,7 @@ public function validateCustomDomain($domain_full_uri) { } public function getLiveURI() { - if (strlen($this->getDomain())) { + if (phutil_nonempty_string($this->getDomain())) { return $this->getExternalLiveURI(); } else { return $this->getInternalLiveURI(); @@ -200,6 +200,15 @@ public function getManageURI() { return '/phame/blog/manage/'.$this->getID().'/'; } + /** + * Get relative URI of Phame blog feed. + * + * @return string Relative URI of Phame blog feed + */ + public function getFeedURI() { + return '/phame/blog/feed/'.$this->getID().'/'; + } + public function getProfileImageURI() { return $this->getProfileImageFile()->getBestURI(); } diff --git a/src/applications/phame/storage/PhamePost.php b/src/applications/phame/storage/PhamePost.php index 21149f07c2..5291bcd465 100644 --- a/src/applications/phame/storage/PhamePost.php +++ b/src/applications/phame/storage/PhamePost.php @@ -67,7 +67,8 @@ public function getLiveURI() { $blog = $this->getBlog(); $is_draft = $this->isDraft(); $is_archived = $this->isArchived(); - if (strlen($blog->getDomain()) && !$is_draft && !$is_archived) { + if (phutil_nonempty_string($blog->getDomain()) && + !$is_draft && !$is_archived) { return $this->getExternalLiveURI(); } else { return $this->getInternalLiveURI(); diff --git a/src/applications/phame/typeahead/PhameBlogDatasource.php b/src/applications/phame/typeahead/PhameBlogDatasource.php index 0658dec3b9..87d673b544 100644 --- a/src/applications/phame/typeahead/PhameBlogDatasource.php +++ b/src/applications/phame/typeahead/PhameBlogDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPhameApplication'; + return PhabricatorPhameApplication::class; } public function loadResults() { diff --git a/src/applications/phid/PhabricatorMetaMTAApplicationEmailPHIDType.php b/src/applications/phid/PhabricatorMetaMTAApplicationEmailPHIDType.php index 93d8a47de7..d38d65f1a4 100644 --- a/src/applications/phid/PhabricatorMetaMTAApplicationEmailPHIDType.php +++ b/src/applications/phid/PhabricatorMetaMTAApplicationEmailPHIDType.php @@ -18,7 +18,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorMetaMTAApplication'; + return PhabricatorMetaMTAApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phid/type/PhabricatorPHIDType.php b/src/applications/phid/type/PhabricatorPHIDType.php index 2f82dcf169..6a283a7d08 100644 --- a/src/applications/phid/type/PhabricatorPHIDType.php +++ b/src/applications/phid/type/PhabricatorPHIDType.php @@ -205,4 +205,27 @@ public static function getAllInstalledTypes(PhabricatorUser $viewer) { return $installed_types; } + + /** + * Get all PHID types of an application. + * + * @param string Class name of an application + * @return dict Map of constants of application + */ + public static function getAllTypesForApplication( + string $application) { + $all_types = self::getAllTypes(); + + $application_types = array(); + + foreach ($all_types as $key => $type) { + if ($type->getPHIDTypeApplicationClass() != $application) { + continue; + } + + $application_types[$key] = $type; + } + + return $application_types; + } } diff --git a/src/applications/phlux/editor/PhluxVariableEditor.php b/src/applications/phlux/editor/PhluxVariableEditor.php index 2e36ba8162..5784d19d80 100644 --- a/src/applications/phlux/editor/PhluxVariableEditor.php +++ b/src/applications/phlux/editor/PhluxVariableEditor.php @@ -4,7 +4,7 @@ final class PhluxVariableEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhluxApplication'; + return PhabricatorPhluxApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phlux/phid/PhluxVariablePHIDType.php b/src/applications/phlux/phid/PhluxVariablePHIDType.php index 97525c4052..89e244ee75 100644 --- a/src/applications/phlux/phid/PhluxVariablePHIDType.php +++ b/src/applications/phlux/phid/PhluxVariablePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhluxApplication'; + return PhabricatorPhluxApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phlux/query/PhluxTransactionQuery.php b/src/applications/phlux/query/PhluxTransactionQuery.php index a6969ef557..44d542fcab 100644 --- a/src/applications/phlux/query/PhluxTransactionQuery.php +++ b/src/applications/phlux/query/PhluxTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhluxTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhluxApplication::class; + } + } diff --git a/src/applications/phlux/query/PhluxVariableQuery.php b/src/applications/phlux/query/PhluxVariableQuery.php index 8ec4bc9334..27ffd9875f 100644 --- a/src/applications/phlux/query/PhluxVariableQuery.php +++ b/src/applications/phlux/query/PhluxVariableQuery.php @@ -89,7 +89,7 @@ protected function newPagingMapFromPartialObject($object) { } public function getQueryApplicationClass() { - return 'PhabricatorPhluxApplication'; + return PhabricatorPhluxApplication::class; } } diff --git a/src/applications/pholio/application/PhabricatorPholioApplication.php b/src/applications/pholio/application/PhabricatorPholioApplication.php index 4d80dd12ae..5ac650cc20 100644 --- a/src/applications/pholio/application/PhabricatorPholioApplication.php +++ b/src/applications/pholio/application/PhabricatorPholioApplication.php @@ -32,6 +32,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('M'); + } + public function getRoutes() { return array( '/M(?P[1-9]\d*)(?:/(?P\d+)/)?' => 'PholioMockViewController', diff --git a/src/applications/pholio/controller/PholioImageUploadController.php b/src/applications/pholio/controller/PholioImageUploadController.php index 0ff5e061f5..44d7ea7eed 100644 --- a/src/applications/pholio/controller/PholioImageUploadController.php +++ b/src/applications/pholio/controller/PholioImageUploadController.php @@ -18,7 +18,7 @@ public function handleRequest(AphrontRequest $request) { return new Aphront404Response(); } - if (!strlen($title)) { + if (!phutil_nonempty_string($title)) { $title = $file->getName(); } diff --git a/src/applications/pholio/controller/PholioInlineController.php b/src/applications/pholio/controller/PholioInlineController.php index ddced1f9eb..2201e97f74 100644 --- a/src/applications/pholio/controller/PholioInlineController.php +++ b/src/applications/pholio/controller/PholioInlineController.php @@ -102,6 +102,7 @@ public function handleRequest(AphrontRequest $request) { ->addCancelButton($mock_uri, pht('Close')); } + $error = null; if ($request->isFormPost()) { $v_content = $request->getStr('content'); @@ -112,9 +113,13 @@ public function handleRequest(AphrontRequest $request) { } else if ($inline->getID()) { $inline->delete(); $dictionary = array(); + } else { + $error = pht('Comment cannot be empty.'); } - return id(new AphrontAjaxResponse())->setContent($dictionary); + if ($error === null) { + return id(new AphrontAjaxResponse())->setContent($dictionary); + } } switch ($mode) { @@ -151,6 +156,7 @@ public function handleRequest(AphrontRequest $request) { ->setUser($viewer) ->setName('content') ->setLabel(pht('Comment')) + ->setError($error) ->setValue($v_content)); return $this->newDialog() diff --git a/src/applications/pholio/editor/PholioMockEditor.php b/src/applications/pholio/editor/PholioMockEditor.php index df5d55672c..6eeb83c1bd 100644 --- a/src/applications/pholio/editor/PholioMockEditor.php +++ b/src/applications/pholio/editor/PholioMockEditor.php @@ -5,7 +5,7 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { private $images = array(); public function getEditorApplicationClass() { - return 'PhabricatorPholioApplication'; + return PhabricatorPholioApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/pholio/herald/HeraldPholioMockAdapter.php b/src/applications/pholio/herald/HeraldPholioMockAdapter.php index 5520a60cdc..ed1d2daea1 100644 --- a/src/applications/pholio/herald/HeraldPholioMockAdapter.php +++ b/src/applications/pholio/herald/HeraldPholioMockAdapter.php @@ -5,7 +5,7 @@ final class HeraldPholioMockAdapter extends HeraldAdapter { private $mock; public function getAdapterApplicationClass() { - return 'PhabricatorPholioApplication'; + return PhabricatorPholioApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/pholio/phid/PholioImagePHIDType.php b/src/applications/pholio/phid/PholioImagePHIDType.php index d7a9984fd5..ffbb3f71bf 100644 --- a/src/applications/pholio/phid/PholioImagePHIDType.php +++ b/src/applications/pholio/phid/PholioImagePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPholioApplication'; + return PhabricatorPholioApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/pholio/phid/PholioMockPHIDType.php b/src/applications/pholio/phid/PholioMockPHIDType.php index 13ee4cb81f..bfdecae90d 100644 --- a/src/applications/pholio/phid/PholioMockPHIDType.php +++ b/src/applications/pholio/phid/PholioMockPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPholioApplication'; + return PhabricatorPholioApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/pholio/query/PholioImageQuery.php b/src/applications/pholio/query/PholioImageQuery.php index 69d890ab98..44bee77205 100644 --- a/src/applications/pholio/query/PholioImageQuery.php +++ b/src/applications/pholio/query/PholioImageQuery.php @@ -152,7 +152,7 @@ protected function didFilterPage(array $images) { } public function getQueryApplicationClass() { - return 'PhabricatorPholioApplication'; + return PhabricatorPholioApplication::class; } } diff --git a/src/applications/pholio/query/PholioMockQuery.php b/src/applications/pholio/query/PholioMockQuery.php index 4820ffd8eb..24d56a3b33 100644 --- a/src/applications/pholio/query/PholioMockQuery.php +++ b/src/applications/pholio/query/PholioMockQuery.php @@ -152,7 +152,7 @@ protected function didFilterPage(array $mocks) { } public function getQueryApplicationClass() { - return 'PhabricatorPholioApplication'; + return PhabricatorPholioApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/pholio/query/PholioMockSearchEngine.php b/src/applications/pholio/query/PholioMockSearchEngine.php index cbb175b2de..104533eee2 100644 --- a/src/applications/pholio/query/PholioMockSearchEngine.php +++ b/src/applications/pholio/query/PholioMockSearchEngine.php @@ -7,7 +7,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPholioApplication'; + return PhabricatorPholioApplication::class; } public function newQuery() { diff --git a/src/applications/pholio/query/PholioTransactionQuery.php b/src/applications/pholio/query/PholioTransactionQuery.php index 4c6d8ba0f5..00decb9236 100644 --- a/src/applications/pholio/query/PholioTransactionQuery.php +++ b/src/applications/pholio/query/PholioTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PholioTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPholioApplication::class; + } + } diff --git a/src/applications/pholio/xaction/PholioImageFileTransaction.php b/src/applications/pholio/xaction/PholioImageFileTransaction.php index e18fca28e2..ce1a0e9773 100644 --- a/src/applications/pholio/xaction/PholioImageFileTransaction.php +++ b/src/applications/pholio/xaction/PholioImageFileTransaction.php @@ -110,9 +110,11 @@ public function extractFilePHIDs($object, $value) { $new_phids = $value; $file_phids = array(); - foreach ($new_phids as $phid) { - $file_phids[] = $editor->loadPholioImage($object, $phid) - ->getFilePHID(); + foreach ($new_phids as $phids) { + foreach ($phids as $phid) { + $file_phids[] = $editor->loadPholioImage($object, $phid) + ->getFilePHID(); + } } return $file_phids; diff --git a/src/applications/pholio/xaction/PholioImageNameTransaction.php b/src/applications/pholio/xaction/PholioImageNameTransaction.php index 33b01903ee..f4705a98fc 100644 --- a/src/applications/pholio/xaction/PholioImageNameTransaction.php +++ b/src/applications/pholio/xaction/PholioImageNameTransaction.php @@ -31,8 +31,8 @@ public function getTitle() { '%s renamed an image (%s) from %s to %s.', $this->renderAuthor(), $this->renderHandle(key($new)), - $this->renderValue($old), - $this->renderValue($new)); + $this->renderValue((string)head($old)), + $this->renderValue((string)head($new))); } public function getTitleForFeed() { diff --git a/src/applications/pholio/xaction/PholioImageSequenceTransaction.php b/src/applications/pholio/xaction/PholioImageSequenceTransaction.php index c98c199adf..4d6099ed14 100644 --- a/src/applications/pholio/xaction/PholioImageSequenceTransaction.php +++ b/src/applications/pholio/xaction/PholioImageSequenceTransaction.php @@ -29,7 +29,7 @@ public function getTitle() { return pht( '%s updated an image\'s (%s) sequence.', $this->renderAuthor(), - $this->renderHandleLink(key($new))); + $this->renderHandle(head_key($new))); } public function getTitleForFeed() { diff --git a/src/applications/phortune/application/PhabricatorPhortuneApplication.php b/src/applications/phortune/application/PhabricatorPhortuneApplication.php index 25680ba6c5..0311c94a00 100644 --- a/src/applications/phortune/application/PhabricatorPhortuneApplication.php +++ b/src/applications/phortune/application/PhabricatorPhortuneApplication.php @@ -30,6 +30,10 @@ public function isPrototype() { return true; } + public function isDeprecated() { + return true; + } + public function getRoutes() { return array( '/phortune/' => array( diff --git a/src/applications/phortune/controller/merchant/PhortuneMerchantPictureController.php b/src/applications/phortune/controller/merchant/PhortuneMerchantPictureController.php index 96f4f8e367..a3b9ada7af 100644 --- a/src/applications/phortune/controller/merchant/PhortuneMerchantPictureController.php +++ b/src/applications/phortune/controller/merchant/PhortuneMerchantPictureController.php @@ -14,6 +14,12 @@ protected function handleMerchantRequest(AphrontRequest $request) { $uri = $merchant->getDetailsURI(); $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); @@ -46,9 +52,7 @@ protected function handleMerchantRequest(AphrontRequest $request) { if (!$errors && !$is_default) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } else { $xform = PhabricatorFileTransform::getTransformByKey( PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); @@ -185,8 +189,7 @@ protected function handleMerchantRequest(AphrontRequest $request) { ->setName('picture') ->setLabel(pht('Upload Logo')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($uri) diff --git a/src/applications/phortune/editor/PhortuneAccountEditEngine.php b/src/applications/phortune/editor/PhortuneAccountEditEngine.php index d7272965c2..20df9d16d9 100644 --- a/src/applications/phortune/editor/PhortuneAccountEditEngine.php +++ b/src/applications/phortune/editor/PhortuneAccountEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/phortune/editor/PhortuneAccountEditor.php b/src/applications/phortune/editor/PhortuneAccountEditor.php index 8344bc2b6e..534c701616 100644 --- a/src/applications/phortune/editor/PhortuneAccountEditor.php +++ b/src/applications/phortune/editor/PhortuneAccountEditor.php @@ -4,7 +4,7 @@ final class PhortuneAccountEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phortune/editor/PhortuneAccountEmailEditEngine.php b/src/applications/phortune/editor/PhortuneAccountEmailEditEngine.php index c732e4215f..9bfed006b1 100644 --- a/src/applications/phortune/editor/PhortuneAccountEmailEditEngine.php +++ b/src/applications/phortune/editor/PhortuneAccountEmailEditEngine.php @@ -21,7 +21,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/phortune/editor/PhortuneAccountEmailEditor.php b/src/applications/phortune/editor/PhortuneAccountEmailEditor.php index 40d12a97ba..986d8cdbb6 100644 --- a/src/applications/phortune/editor/PhortuneAccountEmailEditor.php +++ b/src/applications/phortune/editor/PhortuneAccountEmailEditor.php @@ -4,7 +4,7 @@ final class PhortuneAccountEmailEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phortune/editor/PhortuneCartEditor.php b/src/applications/phortune/editor/PhortuneCartEditor.php index aef59f059a..72d7ba6ef9 100644 --- a/src/applications/phortune/editor/PhortuneCartEditor.php +++ b/src/applications/phortune/editor/PhortuneCartEditor.php @@ -19,7 +19,7 @@ public function isInvoice() { } public function getEditorApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phortune/editor/PhortuneMerchantEditEngine.php b/src/applications/phortune/editor/PhortuneMerchantEditEngine.php index e09fba99ab..51e93f9c94 100644 --- a/src/applications/phortune/editor/PhortuneMerchantEditEngine.php +++ b/src/applications/phortune/editor/PhortuneMerchantEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/phortune/editor/PhortuneMerchantEditor.php b/src/applications/phortune/editor/PhortuneMerchantEditor.php index 79fc7d534e..38a844c3ff 100644 --- a/src/applications/phortune/editor/PhortuneMerchantEditor.php +++ b/src/applications/phortune/editor/PhortuneMerchantEditor.php @@ -4,7 +4,7 @@ final class PhortuneMerchantEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phortune/editor/PhortunePaymentMethodEditor.php b/src/applications/phortune/editor/PhortunePaymentMethodEditor.php index 4b6c8cedb3..629958dad1 100644 --- a/src/applications/phortune/editor/PhortunePaymentMethodEditor.php +++ b/src/applications/phortune/editor/PhortunePaymentMethodEditor.php @@ -4,7 +4,7 @@ final class PhortunePaymentMethodEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phortune/editor/PhortunePaymentProviderConfigEditor.php b/src/applications/phortune/editor/PhortunePaymentProviderConfigEditor.php index 96ac6e830a..84d7cff9ee 100644 --- a/src/applications/phortune/editor/PhortunePaymentProviderConfigEditor.php +++ b/src/applications/phortune/editor/PhortunePaymentProviderConfigEditor.php @@ -4,7 +4,7 @@ final class PhortunePaymentProviderConfigEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phortune/editor/PhortuneSubscriptionEditor.php b/src/applications/phortune/editor/PhortuneSubscriptionEditor.php index a2314f5650..4e9c7dc919 100644 --- a/src/applications/phortune/editor/PhortuneSubscriptionEditor.php +++ b/src/applications/phortune/editor/PhortuneSubscriptionEditor.php @@ -4,7 +4,7 @@ final class PhortuneSubscriptionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phortune/phid/PhortuneAccountEmailPHIDType.php b/src/applications/phortune/phid/PhortuneAccountEmailPHIDType.php index fccd50cf16..3ae2f295e5 100644 --- a/src/applications/phortune/phid/PhortuneAccountEmailPHIDType.php +++ b/src/applications/phortune/phid/PhortuneAccountEmailPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortuneAccountPHIDType.php b/src/applications/phortune/phid/PhortuneAccountPHIDType.php index 90632a980d..7f38be410c 100644 --- a/src/applications/phortune/phid/PhortuneAccountPHIDType.php +++ b/src/applications/phortune/phid/PhortuneAccountPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortuneCartPHIDType.php b/src/applications/phortune/phid/PhortuneCartPHIDType.php index c805a4a921..eceddfe2ad 100644 --- a/src/applications/phortune/phid/PhortuneCartPHIDType.php +++ b/src/applications/phortune/phid/PhortuneCartPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortuneChargePHIDType.php b/src/applications/phortune/phid/PhortuneChargePHIDType.php index 013db6ab1c..b41d64b5b6 100644 --- a/src/applications/phortune/phid/PhortuneChargePHIDType.php +++ b/src/applications/phortune/phid/PhortuneChargePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortuneMerchantPHIDType.php b/src/applications/phortune/phid/PhortuneMerchantPHIDType.php index 69b93582b3..0f4fe43174 100644 --- a/src/applications/phortune/phid/PhortuneMerchantPHIDType.php +++ b/src/applications/phortune/phid/PhortuneMerchantPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortunePaymentMethodPHIDType.php b/src/applications/phortune/phid/PhortunePaymentMethodPHIDType.php index 7de421a7b4..846c3c2cbc 100644 --- a/src/applications/phortune/phid/PhortunePaymentMethodPHIDType.php +++ b/src/applications/phortune/phid/PhortunePaymentMethodPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortunePaymentProviderPHIDType.php b/src/applications/phortune/phid/PhortunePaymentProviderPHIDType.php index dc96d08648..74b4a05a69 100644 --- a/src/applications/phortune/phid/PhortunePaymentProviderPHIDType.php +++ b/src/applications/phortune/phid/PhortunePaymentProviderPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortuneProductPHIDType.php b/src/applications/phortune/phid/PhortuneProductPHIDType.php index 409377186f..3d79471b1f 100644 --- a/src/applications/phortune/phid/PhortuneProductPHIDType.php +++ b/src/applications/phortune/phid/PhortuneProductPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortunePurchasePHIDType.php b/src/applications/phortune/phid/PhortunePurchasePHIDType.php index 08f88b1d33..e1faad923b 100644 --- a/src/applications/phortune/phid/PhortunePurchasePHIDType.php +++ b/src/applications/phortune/phid/PhortunePurchasePHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/phid/PhortuneSubscriptionPHIDType.php b/src/applications/phortune/phid/PhortuneSubscriptionPHIDType.php index e07dce12f4..caf505fa55 100644 --- a/src/applications/phortune/phid/PhortuneSubscriptionPHIDType.php +++ b/src/applications/phortune/phid/PhortuneSubscriptionPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phortune/provider/PhortunePaymentProvider.php b/src/applications/phortune/provider/PhortunePaymentProvider.php index 57b2956ecb..eb6d2ff2e3 100644 --- a/src/applications/phortune/provider/PhortunePaymentProvider.php +++ b/src/applications/phortune/provider/PhortunePaymentProvider.php @@ -96,7 +96,7 @@ abstract public function extendEditForm( array $issues); protected function renderConfigurationSecret($value) { - if (strlen($value)) { + if (phutil_nonempty_string($value)) { return str_repeat('*', strlen($value)); } return ''; diff --git a/src/applications/phortune/query/PhortuneAccountEmailQuery.php b/src/applications/phortune/query/PhortuneAccountEmailQuery.php index 0e0a668b8c..3e2aee1f6a 100644 --- a/src/applications/phortune/query/PhortuneAccountEmailQuery.php +++ b/src/applications/phortune/query/PhortuneAccountEmailQuery.php @@ -103,7 +103,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/phortune/query/PhortuneAccountEmailTransactionQuery.php b/src/applications/phortune/query/PhortuneAccountEmailTransactionQuery.php index 2aa9d8418e..578153cdb6 100644 --- a/src/applications/phortune/query/PhortuneAccountEmailTransactionQuery.php +++ b/src/applications/phortune/query/PhortuneAccountEmailTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhortuneAccountEmailTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhortuneApplication::class; + } + } diff --git a/src/applications/phortune/query/PhortuneAccountQuery.php b/src/applications/phortune/query/PhortuneAccountQuery.php index 43c7c5f976..5defc2d9d6 100644 --- a/src/applications/phortune/query/PhortuneAccountQuery.php +++ b/src/applications/phortune/query/PhortuneAccountQuery.php @@ -124,7 +124,7 @@ protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/phortune/query/PhortuneAccountTransactionQuery.php b/src/applications/phortune/query/PhortuneAccountTransactionQuery.php index f49dc822c4..d1540edc87 100644 --- a/src/applications/phortune/query/PhortuneAccountTransactionQuery.php +++ b/src/applications/phortune/query/PhortuneAccountTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhortuneAccountTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhortuneApplication::class; + } + } diff --git a/src/applications/phortune/query/PhortuneCartQuery.php b/src/applications/phortune/query/PhortuneCartQuery.php index 0b3325b932..e54578731f 100644 --- a/src/applications/phortune/query/PhortuneCartQuery.php +++ b/src/applications/phortune/query/PhortuneCartQuery.php @@ -217,7 +217,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } } diff --git a/src/applications/phortune/query/PhortuneCartSearchEngine.php b/src/applications/phortune/query/PhortuneCartSearchEngine.php index a4a0f2848d..b017591fb8 100644 --- a/src/applications/phortune/query/PhortuneCartSearchEngine.php +++ b/src/applications/phortune/query/PhortuneCartSearchEngine.php @@ -44,7 +44,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/phortune/query/PhortuneCartTransactionQuery.php b/src/applications/phortune/query/PhortuneCartTransactionQuery.php index a72b74814f..4a26bc877d 100644 --- a/src/applications/phortune/query/PhortuneCartTransactionQuery.php +++ b/src/applications/phortune/query/PhortuneCartTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhortuneCartTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhortuneApplication::class; + } + } diff --git a/src/applications/phortune/query/PhortuneChargeQuery.php b/src/applications/phortune/query/PhortuneChargeQuery.php index a7eda9d6a6..a0f6026fa6 100644 --- a/src/applications/phortune/query/PhortuneChargeQuery.php +++ b/src/applications/phortune/query/PhortuneChargeQuery.php @@ -138,7 +138,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } } diff --git a/src/applications/phortune/query/PhortuneChargeSearchEngine.php b/src/applications/phortune/query/PhortuneChargeSearchEngine.php index e1fb5a47ba..568d60fc1a 100644 --- a/src/applications/phortune/query/PhortuneChargeSearchEngine.php +++ b/src/applications/phortune/query/PhortuneChargeSearchEngine.php @@ -24,7 +24,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/phortune/query/PhortuneMerchantQuery.php b/src/applications/phortune/query/PhortuneMerchantQuery.php index 2c9aefc74d..19b32d1338 100644 --- a/src/applications/phortune/query/PhortuneMerchantQuery.php +++ b/src/applications/phortune/query/PhortuneMerchantQuery.php @@ -118,7 +118,7 @@ protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/phortune/query/PhortuneMerchantSearchEngine.php b/src/applications/phortune/query/PhortuneMerchantSearchEngine.php index a818f37d28..0688fa5344 100644 --- a/src/applications/phortune/query/PhortuneMerchantSearchEngine.php +++ b/src/applications/phortune/query/PhortuneMerchantSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/phortune/query/PhortuneMerchantTransactionQuery.php b/src/applications/phortune/query/PhortuneMerchantTransactionQuery.php index e71128ce75..b8b7d32f28 100644 --- a/src/applications/phortune/query/PhortuneMerchantTransactionQuery.php +++ b/src/applications/phortune/query/PhortuneMerchantTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhortuneMerchantTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhortuneApplication::class; + } + } diff --git a/src/applications/phortune/query/PhortunePaymentMethodQuery.php b/src/applications/phortune/query/PhortunePaymentMethodQuery.php index b95881d3a7..aeea908506 100644 --- a/src/applications/phortune/query/PhortunePaymentMethodQuery.php +++ b/src/applications/phortune/query/PhortunePaymentMethodQuery.php @@ -140,7 +140,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } } diff --git a/src/applications/phortune/query/PhortunePaymentMethodTransactionQuery.php b/src/applications/phortune/query/PhortunePaymentMethodTransactionQuery.php index 2067e1e360..a0da99b600 100644 --- a/src/applications/phortune/query/PhortunePaymentMethodTransactionQuery.php +++ b/src/applications/phortune/query/PhortunePaymentMethodTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhortunePaymentMethodTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhortuneApplication::class; + } + } diff --git a/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php b/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php index a850acec28..eb478a2997 100644 --- a/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php +++ b/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php @@ -89,7 +89,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } } diff --git a/src/applications/phortune/query/PhortunePaymentProviderConfigTransactionQuery.php b/src/applications/phortune/query/PhortunePaymentProviderConfigTransactionQuery.php index 34797ba657..f346f5cafc 100644 --- a/src/applications/phortune/query/PhortunePaymentProviderConfigTransactionQuery.php +++ b/src/applications/phortune/query/PhortunePaymentProviderConfigTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhortunePaymentProviderConfigTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhortuneApplication::class; + } + } diff --git a/src/applications/phortune/query/PhortuneProductQuery.php b/src/applications/phortune/query/PhortuneProductQuery.php index 30701d4e7b..d479c37843 100644 --- a/src/applications/phortune/query/PhortuneProductQuery.php +++ b/src/applications/phortune/query/PhortuneProductQuery.php @@ -114,7 +114,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } } diff --git a/src/applications/phortune/query/PhortunePurchaseQuery.php b/src/applications/phortune/query/PhortunePurchaseQuery.php index 275537c351..7ed5d1c9a6 100644 --- a/src/applications/phortune/query/PhortunePurchaseQuery.php +++ b/src/applications/phortune/query/PhortunePurchaseQuery.php @@ -104,7 +104,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } } diff --git a/src/applications/phortune/query/PhortuneSubscriptionQuery.php b/src/applications/phortune/query/PhortuneSubscriptionQuery.php index 5622578738..c848f74333 100644 --- a/src/applications/phortune/query/PhortuneSubscriptionQuery.php +++ b/src/applications/phortune/query/PhortuneSubscriptionQuery.php @@ -194,7 +194,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } } diff --git a/src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php b/src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php index 0d2e720aa7..9da6f34c72 100644 --- a/src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php +++ b/src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php @@ -34,7 +34,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhortuneApplication'; + return PhabricatorPhortuneApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/phortune/query/PhortuneSubscriptionTransactionQuery.php b/src/applications/phortune/query/PhortuneSubscriptionTransactionQuery.php index db97925b39..b6d12cc368 100644 --- a/src/applications/phortune/query/PhortuneSubscriptionTransactionQuery.php +++ b/src/applications/phortune/query/PhortuneSubscriptionTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhortuneSubscriptionTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhortuneApplication::class; + } + } diff --git a/src/applications/phortune/storage/PhortuneMerchant.php b/src/applications/phortune/storage/PhortuneMerchant.php index 6e0bf81e22..25426d3b28 100644 --- a/src/applications/phortune/storage/PhortuneMerchant.php +++ b/src/applications/phortune/storage/PhortuneMerchant.php @@ -157,7 +157,7 @@ public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { } public function describeAutomaticCapability($capability) { - return pht("A merchant's members an always view and edit it."); + return pht("A merchant's members can always view and edit it."); } } diff --git a/src/applications/phortune/xaction/PhortuneAccountNameTransaction.php b/src/applications/phortune/xaction/PhortuneAccountNameTransaction.php index 08b8b69422..20272fda27 100644 --- a/src/applications/phortune/xaction/PhortuneAccountNameTransaction.php +++ b/src/applications/phortune/xaction/PhortuneAccountNameTransaction.php @@ -17,7 +17,7 @@ public function getTitle() { $old = $this->getOldValue(); $new = $this->getNewValue(); - if (strlen($old) && strlen($new)) { + if (phutil_nonempty_string($old) && phutil_nonempty_string($new)) { return pht( '%s renamed this account from %s to %s.', $this->renderAuthor(), diff --git a/src/applications/phrequent/query/PhrequentSearchEngine.php b/src/applications/phrequent/query/PhrequentSearchEngine.php index d137c40b64..2cf1bb7d4c 100644 --- a/src/applications/phrequent/query/PhrequentSearchEngine.php +++ b/src/applications/phrequent/query/PhrequentSearchEngine.php @@ -7,7 +7,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhrequentApplication'; + return PhabricatorPhrequentApplication::class; } public function getPageSize(PhabricatorSavedQuery $saved) { diff --git a/src/applications/phrequent/query/PhrequentUserTimeQuery.php b/src/applications/phrequent/query/PhrequentUserTimeQuery.php index 6400771a00..7bbd267c00 100644 --- a/src/applications/phrequent/query/PhrequentUserTimeQuery.php +++ b/src/applications/phrequent/query/PhrequentUserTimeQuery.php @@ -326,7 +326,7 @@ public static function getUserTimeSpentOnObject( } public function getQueryApplicationClass() { - return 'PhabricatorPhrequentApplication'; + return PhabricatorPhrequentApplication::class; } } diff --git a/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php b/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php index 300dbbfa80..61d3611faf 100644 --- a/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php +++ b/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php @@ -25,7 +25,7 @@ protected function defineReturnType() { protected function execute(ConduitAPIRequest $request) { $slug = $request->getValue('slug'); - if (!strlen($slug)) { + if (!phutil_nonempty_string($slug)) { throw new Exception(pht('No such document.')); } $doc = id(new PhrictionDocumentQuery()) diff --git a/src/applications/phriction/editor/PhrictionDocumentEditEngine.php b/src/applications/phriction/editor/PhrictionDocumentEditEngine.php index 22f56f2f57..a9c42216bc 100644 --- a/src/applications/phriction/editor/PhrictionDocumentEditEngine.php +++ b/src/applications/phriction/editor/PhrictionDocumentEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } protected function newEditableObject() { diff --git a/src/applications/phriction/editor/PhrictionTransactionEditor.php b/src/applications/phriction/editor/PhrictionTransactionEditor.php index 1476b24c46..da6bd30b6c 100644 --- a/src/applications/phriction/editor/PhrictionTransactionEditor.php +++ b/src/applications/phriction/editor/PhrictionTransactionEditor.php @@ -90,7 +90,7 @@ public function setShouldPublishContent( } public function getEditorApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } public function getEditorObjectsDescription() { @@ -556,7 +556,7 @@ private function newDocumentContent(PhrictionDocument $document) { ->setContent($this->getOldContent()->getContent()) ->setDescription(''); - if (strlen($this->getDescription())) { + if (phutil_nonempty_string($this->getDescription())) { $content->setDescription($this->getDescription()); } diff --git a/src/applications/phriction/engineextension/PhrictionHovercardEngineExtension.php b/src/applications/phriction/engineextension/PhrictionHovercardEngineExtension.php new file mode 100644 index 0000000000..14e63b9521 --- /dev/null +++ b/src/applications/phriction/engineextension/PhrictionHovercardEngineExtension.php @@ -0,0 +1,169 @@ + $this->getProjectHandlesOfDocuments($objects), + 'ancestors' => $this->getAncestorHandlesOfDocuments($objects), + ); + } + + public function renderHovercard( + PHUIHovercardView $hovercard, + PhabricatorObjectHandle $handle, + $object, + $data) { + + $viewer = $this->getViewer(); + $phid = $object->getPHID(); + + $detail_content = array( + id(new PHUIIconView())->setIcon('fa-book'), + ); + + $ancestor_handles = $data['ancestors'][$phid]; + if ($ancestor_handles) { + foreach ($ancestor_handles as $ancestor_handle) { + $detail_content[] = phutil_tag( + 'a', + array( + 'href' => $ancestor_handle->getUri(), + ), + $ancestor_handle->getName()); + + $detail_content[] = id(new PHUIIconView()) + ->setIcon('fa-angle-right') + ->addClass('phui-crumb-divider'); + } + array_pop($detail_content); + } else { + $detail_content[] = pht('Wiki Document'); + } + + $project_handles = $data['projects'][$phid]; + if ($project_handles) { + $list = id(new PHUIHandleTagListView()) + ->setHandles($project_handles) + ->setSlim(true) + ->setShowHovercards(false); + + $detail_content[] = $list; + } + + $hovercard->setDetail( + phutil_tag( + 'div', + array( + 'class' => 'phui-hovercard-object-type', + ), + $detail_content)); + + $content = $object->getContent(); + + if ($content) { + $hovercard->addField( + pht('Last Author'), + $viewer->renderHandle($content->getAuthorPHID())); + + $hovercard->addField( + pht('Last Edited'), + phabricator_dual_datetime($content->getDateCreated(), $viewer)); + } + } + + private function getProjectHandlesOfDocuments($documents) { + $viewer = $this->getViewer(); + $project_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; + $project_phids = array(); + $project_map = array(); + + $project_edges = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs(mpull($documents, 'getPHID')) + ->withEdgeTypes(array($project_edge_type)) + ->execute(); + + foreach ($project_edges as $document_phid => $edge_types) { + $document_project_phids = array_keys($edge_types[$project_edge_type]); + + $project_map[$document_phid] = array_reverse($document_project_phids); + foreach ($document_project_phids as $project_phid) { + if (!in_array($project_phid, $project_phids, true)) { + $project_phids[] = $project_phid; + } + } + } + + if ($project_phids) { + $project_handles = $viewer->loadHandles($project_phids); + $project_handles = iterator_to_array($project_handles); + $project_handles = mpull($project_handles, null, 'getPHID'); + + foreach ($project_map as $key => $document_project_phids) { + $project_map[$key] = array_select_keys( + $project_handles, + $document_project_phids); + } + } + + return $project_map; + } + + private function getAncestorHandlesOfDocuments($documents) { + $viewer = $this->getViewer(); + $ancestor_slugs = array(); + $ancestor_map = array(); + + foreach ($documents as $document) { + $document_phid = $document->getPHID(); + $document_ancestor_slugs = PhabricatorSlug::getAncestry( + $document->getSlug()); + + $ancestor_map[$document_phid] = $document_ancestor_slugs; + foreach ($document_ancestor_slugs as $slug) { + if (!in_array($slug, $ancestor_slugs, true)) { + $ancestor_slugs[] = $slug; + } + } + } + + if ($ancestor_slugs) { + $ancestors = id(new PhrictionDocumentQuery()) + ->setViewer($viewer) + ->withSlugs($ancestor_slugs) + ->execute(); + $ancestor_phids = mpull($ancestors, 'getPHID', 'getSlug'); + $ancestor_handles = $viewer->loadHandles($ancestor_phids); + $ancestor_handles = iterator_to_array($ancestor_handles); + $ancestor_handles = mpull($ancestor_handles, null, 'getPHID'); + + foreach ($ancestor_map as $key => $document_ancestor_slugs) { + $document_ancestor_phids = array_select_keys( + $ancestor_phids, + $document_ancestor_slugs); + $ancestor_map[$key] = array_select_keys( + $ancestor_handles, + $document_ancestor_phids); + } + } + + return $ancestor_map; + } + +} diff --git a/src/applications/phriction/herald/PhrictionDocumentHeraldAdapter.php b/src/applications/phriction/herald/PhrictionDocumentHeraldAdapter.php index 74de97d5fc..7eae636b3c 100644 --- a/src/applications/phriction/herald/PhrictionDocumentHeraldAdapter.php +++ b/src/applications/phriction/herald/PhrictionDocumentHeraldAdapter.php @@ -5,7 +5,7 @@ final class PhrictionDocumentHeraldAdapter extends HeraldAdapter { private $document; public function getAdapterApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/phriction/markup/PhrictionRemarkupRule.php b/src/applications/phriction/markup/PhrictionRemarkupRule.php index 8394b23218..0ea6e07d09 100644 --- a/src/applications/phriction/markup/PhrictionRemarkupRule.php +++ b/src/applications/phriction/markup/PhrictionRemarkupRule.php @@ -191,7 +191,7 @@ public function didMarkupText() { // If the name is something meaningful to humans, we'll render this // in text as: "Title" . Otherwise, we'll just render: . - $is_interesting_name = (bool)strlen($name); + $is_interesting_name = phutil_nonempty_string($name); $target = idx($document_map, $key, null); diff --git a/src/applications/phriction/phid/PhrictionContentPHIDType.php b/src/applications/phriction/phid/PhrictionContentPHIDType.php index b8f39c0ef4..4db60601d9 100644 --- a/src/applications/phriction/phid/PhrictionContentPHIDType.php +++ b/src/applications/phriction/phid/PhrictionContentPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phriction/phid/PhrictionDocumentPHIDType.php b/src/applications/phriction/phid/PhrictionDocumentPHIDType.php index 57afdb84d6..4dcb039121 100644 --- a/src/applications/phriction/phid/PhrictionDocumentPHIDType.php +++ b/src/applications/phriction/phid/PhrictionDocumentPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phriction/query/PhrictionContentQuery.php b/src/applications/phriction/query/PhrictionContentQuery.php index 8ac92be351..ded328ea25 100644 --- a/src/applications/phriction/query/PhrictionContentQuery.php +++ b/src/applications/phriction/query/PhrictionContentQuery.php @@ -118,7 +118,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } } diff --git a/src/applications/phriction/query/PhrictionContentSearchEngine.php b/src/applications/phriction/query/PhrictionContentSearchEngine.php index f7cd9be2b6..5bdbaa7535 100644 --- a/src/applications/phriction/query/PhrictionContentSearchEngine.php +++ b/src/applications/phriction/query/PhrictionContentSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } public function newQuery() { diff --git a/src/applications/phriction/query/PhrictionDocumentQuery.php b/src/applications/phriction/query/PhrictionDocumentQuery.php index 298db97dbb..491458a128 100644 --- a/src/applications/phriction/query/PhrictionDocumentQuery.php +++ b/src/applications/phriction/query/PhrictionDocumentQuery.php @@ -392,7 +392,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } } diff --git a/src/applications/phriction/query/PhrictionDocumentSearchEngine.php b/src/applications/phriction/query/PhrictionDocumentSearchEngine.php index 49a332268b..cf59159298 100644 --- a/src/applications/phriction/query/PhrictionDocumentSearchEngine.php +++ b/src/applications/phriction/query/PhrictionDocumentSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } public function newQuery() { diff --git a/src/applications/phriction/query/PhrictionTransactionQuery.php b/src/applications/phriction/query/PhrictionTransactionQuery.php index c43006364b..6b924f0985 100644 --- a/src/applications/phriction/query/PhrictionTransactionQuery.php +++ b/src/applications/phriction/query/PhrictionTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhrictionTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhrictionApplication::class; + } + } diff --git a/src/applications/phriction/typeahead/PhrictionDocumentDatasource.php b/src/applications/phriction/typeahead/PhrictionDocumentDatasource.php index 09056fe761..fbc48af97d 100644 --- a/src/applications/phriction/typeahead/PhrictionDocumentDatasource.php +++ b/src/applications/phriction/typeahead/PhrictionDocumentDatasource.php @@ -12,12 +12,15 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPhrictionApplication'; + return PhabricatorPhrictionApplication::class; } public function loadResults() { $viewer = $this->getViewer(); + $app_type = pht('Wiki Document'); + $mid_dot = "\xC2\xB7"; + $query = id(new PhrictionDocumentQuery()) ->setViewer($viewer) ->needContent(true); @@ -34,15 +37,25 @@ public function loadResults() { foreach ($documents as $document) { $content = $document->getContent(); - if (!$document->isActive()) { - $closed = $document->getStatusDisplayName(); - } else { + if ($document->isActive()) { $closed = null; + } else { + $closed = $document->getStatusDisplayName(); } $slug = $document->getSlug(); $title = $content->getTitle(); + // For some time the search result was + // just mentioning the document slug. + // Now, it also mentions the application type. + // Example: "Wiki Document - /foo/bar" + $display_type = sprintf( + '%s %s %s', + $app_type, + $mid_dot, + $slug); + $sprite = 'phabricator-search-icon phui-font-fa phui-icon-view fa-book'; $autocomplete = '[[ '.$slug.' ]]'; @@ -51,7 +64,7 @@ public function loadResults() { ->setDisplayName($title) ->setURI($document->getURI()) ->setPHID($document->getPHID()) - ->setDisplayType($slug) + ->setDisplayType($display_type) ->setPriorityType('wiki') ->setImageSprite($sprite) ->setAutocomplete($autocomplete) diff --git a/src/applications/phurl/application/PhabricatorPhurlApplication.php b/src/applications/phurl/application/PhabricatorPhurlApplication.php index 14ffda77f1..56290ef5e1 100644 --- a/src/applications/phurl/application/PhabricatorPhurlApplication.php +++ b/src/applications/phurl/application/PhabricatorPhurlApplication.php @@ -37,6 +37,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('U'); + } + public function getRoutes() { return array( '/U(?P[1-9]\d*)/?' => 'PhabricatorPhurlURLViewController', diff --git a/src/applications/phurl/editor/PhabricatorPhurlURLEditEngine.php b/src/applications/phurl/editor/PhabricatorPhurlURLEditEngine.php index 12a4d2672f..efadadd745 100644 --- a/src/applications/phurl/editor/PhabricatorPhurlURLEditEngine.php +++ b/src/applications/phurl/editor/PhabricatorPhurlURLEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorPhurlApplication'; + return PhabricatorPhurlApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php b/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php index 49f290c343..a67a1c425d 100644 --- a/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php +++ b/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php @@ -4,7 +4,7 @@ final class PhabricatorPhurlURLEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPhurlApplication'; + return PhabricatorPhurlApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/phurl/phid/PhabricatorPhurlURLPHIDType.php b/src/applications/phurl/phid/PhabricatorPhurlURLPHIDType.php index 979b25c791..14bdb11df5 100644 --- a/src/applications/phurl/phid/PhabricatorPhurlURLPHIDType.php +++ b/src/applications/phurl/phid/PhabricatorPhurlURLPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPhurlApplication'; + return PhabricatorPhurlApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/phurl/query/PhabricatorPhurlURLQuery.php b/src/applications/phurl/query/PhabricatorPhurlURLQuery.php index c30cedf09d..cc82264c45 100644 --- a/src/applications/phurl/query/PhabricatorPhurlURLQuery.php +++ b/src/applications/phurl/query/PhabricatorPhurlURLQuery.php @@ -103,6 +103,6 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorPhurlApplication'; + return PhabricatorPhurlApplication::class; } } diff --git a/src/applications/phurl/query/PhabricatorPhurlURLSearchEngine.php b/src/applications/phurl/query/PhabricatorPhurlURLSearchEngine.php index 8814b25b9c..a3a38c8ee9 100644 --- a/src/applications/phurl/query/PhabricatorPhurlURLSearchEngine.php +++ b/src/applications/phurl/query/PhabricatorPhurlURLSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPhurlApplication'; + return PhabricatorPhurlApplication::class; } public function newQuery() { diff --git a/src/applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php b/src/applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php index 6c4e7d212d..11743281be 100644 --- a/src/applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php +++ b/src/applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorPhurlURLTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPhurlApplication::class; + } + } diff --git a/src/applications/phurl/typeahead/PhabricatorPhurlURLDatasource.php b/src/applications/phurl/typeahead/PhabricatorPhurlURLDatasource.php index f8d3cda0cd..77eabd8a28 100644 --- a/src/applications/phurl/typeahead/PhabricatorPhurlURLDatasource.php +++ b/src/applications/phurl/typeahead/PhabricatorPhurlURLDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPhurlApplication'; + return PhabricatorPhurlApplication::class; } public function loadResults() { diff --git a/src/applications/policy/filter/PhabricatorPolicyFilter.php b/src/applications/policy/filter/PhabricatorPolicyFilter.php index d8f239e51a..3443d17156 100644 --- a/src/applications/policy/filter/PhabricatorPolicyFilter.php +++ b/src/applications/policy/filter/PhabricatorPolicyFilter.php @@ -461,7 +461,7 @@ private function executeExtendedPolicyChecks( // checks make it difficult to create cycles normally, so just do a // simple check here to limit damage. - static $depth; + static $depth = 0; $depth++; diff --git a/src/applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php b/src/applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php index ed75561327..afc84dd853 100644 --- a/src/applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php +++ b/src/applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPolicyApplication'; + return PhabricatorPolicyApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/policy/query/PhabricatorPolicyQuery.php b/src/applications/policy/query/PhabricatorPolicyQuery.php index 9bd6ba5994..58e03d325f 100644 --- a/src/applications/policy/query/PhabricatorPolicyQuery.php +++ b/src/applications/policy/query/PhabricatorPolicyQuery.php @@ -286,7 +286,7 @@ protected function shouldDisablePolicyFiltering() { } public function getQueryApplicationClass() { - return 'PhabricatorPolicyApplication'; + return PhabricatorPolicyApplication::class; } public static function isSpecialPolicy($identifier) { diff --git a/src/applications/ponder/application/PhabricatorPonderApplication.php b/src/applications/ponder/application/PhabricatorPonderApplication.php index 56973447f9..e6f4ef8867 100644 --- a/src/applications/ponder/application/PhabricatorPonderApplication.php +++ b/src/applications/ponder/application/PhabricatorPonderApplication.php @@ -47,6 +47,10 @@ public function getAppEmailBlurb() { pht('Learn More'))); } + public function getMonograms() { + return array('Q'); + } + public function getRoutes() { return array( '/Q(?P[1-9]\d*)' diff --git a/src/applications/ponder/constants/PonderQuestionStatus.php b/src/applications/ponder/constants/PonderQuestionStatus.php index 55e433bf25..52497bef37 100644 --- a/src/applications/ponder/constants/PonderQuestionStatus.php +++ b/src/applications/ponder/constants/PonderQuestionStatus.php @@ -86,4 +86,14 @@ public static function getQuestionStatusClosedMap() { ); } + /** + * Check whenever a Ponder question status is Closed + * + * @param $status string + * @return bool + */ + public static function isQuestionStatusClosed($status) { + return in_array($status, self::getQuestionStatusClosedMap(), true); + } + } diff --git a/src/applications/ponder/editor/PonderEditor.php b/src/applications/ponder/editor/PonderEditor.php index fcfa981f16..76615175e9 100644 --- a/src/applications/ponder/editor/PonderEditor.php +++ b/src/applications/ponder/editor/PonderEditor.php @@ -4,7 +4,7 @@ abstract class PonderEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } protected function getMailSubjectPrefix() { diff --git a/src/applications/ponder/editor/PonderQuestionEditEngine.php b/src/applications/ponder/editor/PonderQuestionEditEngine.php index 640f657ad1..0365226666 100644 --- a/src/applications/ponder/editor/PonderQuestionEditEngine.php +++ b/src/applications/ponder/editor/PonderQuestionEditEngine.php @@ -10,7 +10,7 @@ public function getEngineName() { } public function getEngineApplicationClass() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } public function getSummaryHeader() { diff --git a/src/applications/ponder/herald/HeraldPonderQuestionAdapter.php b/src/applications/ponder/herald/HeraldPonderQuestionAdapter.php index f9434c7f78..6a46f56b3e 100644 --- a/src/applications/ponder/herald/HeraldPonderQuestionAdapter.php +++ b/src/applications/ponder/herald/HeraldPonderQuestionAdapter.php @@ -9,7 +9,7 @@ protected function newObject() { } public function getAdapterApplicationClass() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/ponder/phid/PonderAnswerPHIDType.php b/src/applications/ponder/phid/PonderAnswerPHIDType.php index be148af2f2..19ce39b59d 100644 --- a/src/applications/ponder/phid/PonderAnswerPHIDType.php +++ b/src/applications/ponder/phid/PonderAnswerPHIDType.php @@ -9,7 +9,7 @@ public function getTypeName() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } public function newObject() { diff --git a/src/applications/ponder/phid/PonderQuestionPHIDType.php b/src/applications/ponder/phid/PonderQuestionPHIDType.php index 9bdd80aae6..b135e9fc3c 100644 --- a/src/applications/ponder/phid/PonderQuestionPHIDType.php +++ b/src/applications/ponder/phid/PonderQuestionPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/ponder/query/PonderAnswerQuery.php b/src/applications/ponder/query/PonderAnswerQuery.php index f100f05ae3..9b362cf71e 100644 --- a/src/applications/ponder/query/PonderAnswerQuery.php +++ b/src/applications/ponder/query/PonderAnswerQuery.php @@ -78,7 +78,7 @@ protected function willFilterPage(array $answers) { } public function getQueryApplicationClass() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } } diff --git a/src/applications/ponder/query/PonderAnswerTransactionQuery.php b/src/applications/ponder/query/PonderAnswerTransactionQuery.php index 47d8a042a4..e22bd59d97 100644 --- a/src/applications/ponder/query/PonderAnswerTransactionQuery.php +++ b/src/applications/ponder/query/PonderAnswerTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PonderAnswerTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPonderApplication::class; + } + } diff --git a/src/applications/ponder/query/PonderQuestionQuery.php b/src/applications/ponder/query/PonderQuestionQuery.php index 323f34aac5..535e38ab51 100644 --- a/src/applications/ponder/query/PonderQuestionQuery.php +++ b/src/applications/ponder/query/PonderQuestionQuery.php @@ -144,7 +144,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } } diff --git a/src/applications/ponder/query/PonderQuestionSearchEngine.php b/src/applications/ponder/query/PonderQuestionSearchEngine.php index 4cba28b93b..ab08637124 100644 --- a/src/applications/ponder/query/PonderQuestionSearchEngine.php +++ b/src/applications/ponder/query/PonderQuestionSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorPonderApplication'; + return PhabricatorPonderApplication::class; } public function newQuery() { @@ -157,6 +157,9 @@ protected function renderResultList( 'Asked by %s', $handles[$question->getAuthorPHID()]->renderLink())); + // Render Closed Questions as striked in query results + $item->setDisabled($question->isStatusClosed()); + $item->addAttribute( pht( '%s Answer(s)', diff --git a/src/applications/ponder/query/PonderQuestionTransactionQuery.php b/src/applications/ponder/query/PonderQuestionTransactionQuery.php index 788e6b5cbe..992dbb2681 100644 --- a/src/applications/ponder/query/PonderQuestionTransactionQuery.php +++ b/src/applications/ponder/query/PonderQuestionTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PonderQuestionTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorPonderApplication::class; + } + } diff --git a/src/applications/ponder/storage/PonderQuestion.php b/src/applications/ponder/storage/PonderQuestion.php index 40ec9fbee8..b5034f94d8 100644 --- a/src/applications/ponder/storage/PonderQuestion.php +++ b/src/applications/ponder/storage/PonderQuestion.php @@ -137,6 +137,14 @@ public function getMarkupField() { return self::MARKUP_FIELD_CONTENT; } + /** + * Check whenever this Question has whatever closed status + * + * @return bool + */ + public function isStatusClosed() { + return PonderQuestionStatus::isQuestionStatusClosed($this->status); + } /* -( PhabricatorApplicationTransactionInterface )------------------------- */ diff --git a/src/applications/ponder/storage/__tests__/PonderQuestionStatusTestCase.php b/src/applications/ponder/storage/__tests__/PonderQuestionStatusTestCase.php new file mode 100644 index 0000000000..0057dd03d4 --- /dev/null +++ b/src/applications/ponder/storage/__tests__/PonderQuestionStatusTestCase.php @@ -0,0 +1,25 @@ +setStatus($status); + $this->assertEqual(true, $question->isStatusClosed()); + } + + } + + public function testOpenedStatuses() { + $statuses = PonderQuestionStatus::getQuestionStatusOpenMap(); + foreach ($statuses as $status) { + $question = new PonderQuestion(); + $question->setStatus($status); + $this->assertEqual(false, $question->isStatusClosed()); + } + } + +} diff --git a/src/applications/project/config/PhabricatorProjectConfigOptions.php b/src/applications/project/config/PhabricatorProjectConfigOptions.php index 2d87bf159f..4767a4dccc 100644 --- a/src/applications/project/config/PhabricatorProjectConfigOptions.php +++ b/src/applications/project/config/PhabricatorProjectConfigOptions.php @@ -105,6 +105,16 @@ public function getOptions() { ), ); + $fields_description = $this->deformat(pht(<<deformat(pht(<<newOption('projects.custom-field-definitions', 'wild', array()) ->setSummary(pht('Custom Projects fields.')) - ->setDescription( - pht( - 'Array of custom fields for Projects.')) + ->setDescription($fields_description) ->addExample( '{"mycompany:motto": {"name": "Project Motto", '. '"type": "text"}}', diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php index 13a75c5a73..a24c911b9f 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -693,10 +693,19 @@ private function buildColumnMenu( ->newCreateActionSpecifications(array()); foreach ($specs as $spec) { + + // Prefill tags= when you open the Column menu + // https://we.phorge.it/T15147 + $spec_href = new PhutilURI($spec['uri']); + $spec_slug = $project->getPrimarySlug(); + if ($spec_slug !== null) { + $spec_href->replaceQueryParam('tags', $spec_slug); + } + $column_items[] = id(new PhabricatorActionView()) ->setIcon($spec['icon']) ->setName($spec['name']) - ->setHref($spec['uri']) + ->setHref($spec_href) ->setDisabled($spec['disabled']) ->addSigil('column-add-task') ->setMetadata( diff --git a/src/applications/project/controller/PhabricatorProjectColumnEditController.php b/src/applications/project/controller/PhabricatorProjectColumnEditController.php index 9ddb2b7d8a..bf579b274f 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnEditController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnEditController.php @@ -45,16 +45,32 @@ public function handleRequest(AphrontRequest $request) { $e_name = null; $e_limit = null; + $e_milestone_name = null; $v_limit = $column->getPointLimit(); $v_name = $column->getName(); + $proxy = $column->getProxy(); + + // Is this a normal Column? Example: when true, this is not a Milestone. + $is_column = !$proxy; + + // Is this a Milestone? Example: when true, this is not a normal Column. + $is_milestone = $proxy && $proxy->isMilestone(); + + // Milestone name, eventually coming from the proxed object. + $v_milestone_name = null; + if ($is_milestone) { + $v_milestone_name = $proxy->getName(); + } + $validation_exception = null; $view_uri = $project->getWorkboardURI(); if ($request->isFormPost()) { $v_name = $request->getStr('name'); $v_limit = $request->getStr('limit'); + $v_milestone_name = $request->getStr('milestone.name'); if ($is_new) { $column->setProjectPHID($project->getPHID()); @@ -74,14 +90,22 @@ public function handleRequest(AphrontRequest $request) { } $xactions = array(); + $xactions_milestone = array(); $type_name = PhabricatorProjectColumnNameTransaction::TRANSACTIONTYPE; $type_limit = PhabricatorProjectColumnLimitTransaction::TRANSACTIONTYPE; + $type_project_name = PhabricatorProjectNameTransaction::TRANSACTIONTYPE; - if (!$column->getProxy()) { + if ($is_column) { + // Transaction for Column name. $xactions[] = id(new PhabricatorProjectColumnTransaction()) ->setTransactionType($type_name) ->setNewValue($v_name); + } else if ($is_milestone) { + // Transaction for Milestone name (that internally is a Project Name). + $xactions_milestone[] = id(new PhabricatorProjectTransaction()) + ->setTransactionType($type_project_name) + ->setNewValue($v_milestone_name); } $xactions[] = id(new PhabricatorProjectColumnTransaction()) @@ -94,24 +118,53 @@ public function handleRequest(AphrontRequest $request) { ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->applyTransactions($column, $xactions); - return id(new AphrontRedirectResponse())->setURI($view_uri); } catch (PhabricatorApplicationTransactionValidationException $ex) { + // Error messages related to the Column (like invalid Name, etc.) $e_name = $ex->getShortMessage($type_name); $e_limit = $ex->getShortMessage($type_limit); $validation_exception = $ex; } + + // Save Milestone-related stuff but only if there were no prior problems + // and only if we have changes. + if (!$validation_exception && $xactions_milestone) { + try { + $editor_milestone = id(new PhabricatorProjectTransactionEditor()) + ->setActor($viewer) + ->setContinueOnNoEffect(true) + ->setContentSourceFromRequest($request) + ->applyTransactions($proxy, $xactions_milestone); + } catch (PhabricatorApplicationTransactionValidationException $ex) { + // Error messages related to the Milestone (like invalid Name, etc.) + $e_milestone_name = $ex->getShortMessage($type_project_name); + $validation_exception = $ex; + } + } + + // Refresh the page only if there are no errors to show. + if (!$validation_exception) { + return id(new AphrontRedirectResponse())->setURI($view_uri); + } } $form = id(new AphrontFormView()) ->setUser($request->getUser()); - if (!$column->getProxy()) { + // Show the most appropriate input field for the name. + if ($is_column) { $form->appendChild( id(new AphrontFormTextControl()) ->setValue($v_name) ->setLabel(pht('Name')) ->setName('name') ->setError($e_name)); + } else if ($is_milestone) { + $form->appendChild( + id(new AphrontFormTextControl()) + ->setValue($v_milestone_name) + ->setLabel(pht('Milestone Name')) + ->setName('milestone.name') + ->setError($e_milestone_name)); } $form->appendChild( diff --git a/src/applications/project/controller/PhabricatorProjectColumnHideController.php b/src/applications/project/controller/PhabricatorProjectColumnHideController.php index 254beab78c..09217064e2 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnHideController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnHideController.php @@ -136,7 +136,6 @@ public function handleRequest(AphrontRequest $request) { ->setWidth(AphrontDialogView::WIDTH_FORM) ->setTitle($title) ->appendChild($body) - ->setDisableWorkflowOnCancel(true) ->addCancelButton($view_uri) ->addSubmitButton($button); diff --git a/src/applications/project/controller/PhabricatorProjectEditPictureController.php b/src/applications/project/controller/PhabricatorProjectEditPictureController.php index d141dc07e2..b8be55899b 100644 --- a/src/applications/project/controller/PhabricatorProjectEditPictureController.php +++ b/src/applications/project/controller/PhabricatorProjectEditPictureController.php @@ -26,6 +26,12 @@ public function handleRequest(AphrontRequest $request) { $manage_uri = $this->getApplicationURI('manage/'.$project->getID().'/'); $supported_formats = PhabricatorFile::getTransformableImageFormats(); + if ($supported_formats) { + $supported_formats_message = pht('Supported image formats: %s.', + implode(', ', $supported_formats)); + } else { + $supported_formats_message = pht('Server supports no image formats.'); + } $e_file = true; $errors = array(); @@ -58,9 +64,7 @@ public function handleRequest(AphrontRequest $request) { if (!$errors && !$is_default) { if (!$file->isTransformableImage()) { $e_file = pht('Not Supported'); - $errors[] = pht( - 'This server only supports these image formats: %s.', - implode(', ', $supported_formats)); + $errors[] = $supported_formats_message; } else { $xform = PhabricatorFileTransform::getTransformByKey( PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); @@ -257,8 +261,7 @@ public function handleRequest(AphrontRequest $request) { ->setName('picture') ->setLabel(pht('Upload Picture')) ->setError($e_file) - ->setCaption( - pht('Supported formats: %s', implode(', ', $supported_formats)))) + ->setCaption($supported_formats_message)) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($manage_uri) diff --git a/src/applications/project/controller/PhabricatorProjectMembersAddController.php b/src/applications/project/controller/PhabricatorProjectMembersAddController.php index 79c66dc5b1..572e71f51a 100644 --- a/src/applications/project/controller/PhabricatorProjectMembersAddController.php +++ b/src/applications/project/controller/PhabricatorProjectMembersAddController.php @@ -27,9 +27,14 @@ public function handleRequest(AphrontRequest $request) { $copy = pht('Parent projects and milestones do not support adding '. 'members. You can add members directly to any non-parent subproject.'); + $subprojects_uri = "/project/subprojects/{$id}/"; + return $this->newDialog() ->setTitle(pht('Unsupported Project')) ->appendParagraph($copy) + ->setSubmitURI($subprojects_uri) + ->addSubmitButton(pht('See Subprojects')) + ->setDisableWorkflowOnSubmit(true) ->addCancelButton($done_uri); } diff --git a/src/applications/project/controller/PhabricatorProjectMoveController.php b/src/applications/project/controller/PhabricatorProjectMoveController.php index 1fd8b3c677..7f25905748 100644 --- a/src/applications/project/controller/PhabricatorProjectMoveController.php +++ b/src/applications/project/controller/PhabricatorProjectMoveController.php @@ -24,9 +24,11 @@ public function handleRequest(AphrontRequest $request) { $ordering = id(clone $ordering) ->setViewer($viewer); + // When the Workboard view is "Group By " the header provides + // that context in JSON form $edit_header = null; $raw_header = $request->getStr('header'); - if (strlen($raw_header)) { + if (phutil_nonempty_string($raw_header)) { $edit_header = phutil_json_decode($raw_header); } else { $edit_header = array(); diff --git a/src/applications/project/controller/PhabricatorProjectUpdateController.php b/src/applications/project/controller/PhabricatorProjectUpdateController.php index 7cbd77b4cb..5707693654 100644 --- a/src/applications/project/controller/PhabricatorProjectUpdateController.php +++ b/src/applications/project/controller/PhabricatorProjectUpdateController.php @@ -38,9 +38,14 @@ public function handleRequest(AphrontRequest $request) { $copy = pht('Parent projects and milestones do not support adding '. 'members. You can add members directly to any non-parent subproject.'); + $subprojects_uri = "/project/subprojects/{$id}/"; + return $this->newDialog() ->setTitle(pht('Unsupported Project')) ->appendParagraph($copy) + ->setSubmitURI($subprojects_uri) + ->addSubmitButton(pht('See Subprojects')) + ->setDisableWorkflowOnSubmit(true) ->addCancelButton($done_uri); } diff --git a/src/applications/project/editor/PhabricatorProjectColumnTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectColumnTransactionEditor.php index e0becc3470..3ebae4c247 100644 --- a/src/applications/project/editor/PhabricatorProjectColumnTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectColumnTransactionEditor.php @@ -4,7 +4,7 @@ final class PhabricatorProjectColumnTransactionEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php index eb57c39b2c..ab9fca1109 100644 --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -15,7 +15,7 @@ public function getIsMilestone() { } public function getEditorApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getEditorObjectsDescription() { @@ -295,6 +295,12 @@ public function addSlug(PhabricatorProject $project, $slug, $force) { } public function removeSlugs(PhabricatorProject $project, array $slugs) { + // Do not allow removing the project's primary slug which the edit form + // may allow through a series of renames/moves. See T15636 + if (($key = array_search($project->getPrimarySlug(), $slugs)) !== false) { + unset($slugs[$key]); + } + if (!$slugs) { return; } diff --git a/src/applications/project/editor/PhabricatorProjectTriggerEditor.php b/src/applications/project/editor/PhabricatorProjectTriggerEditor.php index 9014fd6f16..6ac1644fb4 100644 --- a/src/applications/project/editor/PhabricatorProjectTriggerEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTriggerEditor.php @@ -4,7 +4,7 @@ final class PhabricatorProjectTriggerEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/project/engine/PhabricatorProjectEditEngine.php b/src/applications/project/engine/PhabricatorProjectEditEngine.php index 1c84932656..51cd840d3d 100644 --- a/src/applications/project/engine/PhabricatorProjectEditEngine.php +++ b/src/applications/project/engine/PhabricatorProjectEditEngine.php @@ -47,7 +47,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } protected function newEditableObject() { diff --git a/src/applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php b/src/applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php index d2d1f9ab82..ef8964463f 100644 --- a/src/applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php +++ b/src/applications/project/engineextension/PhabricatorProjectHovercardEngineExtension.php @@ -28,6 +28,26 @@ public function willRenderHovercards(array $objects) { ->execute(); $projects = mpull($projects, null, 'getPHID'); + $custom_fields = array(); + foreach ($projects as $project) { + $field = PhabricatorCustomField::getObjectField( + $project, + PhabricatorCustomField::ROLE_VIEW, + 'std:project:internal:description'); + if ($field === null) { + // This means the field is disabled, it would always be null. + break; + } + $field + ->setViewer($viewer) + ->readValueFromObject($project); + $custom_fields[] = $field; + } + + id(new PhabricatorCustomFieldStorageQuery()) + ->addFields($custom_fields) + ->execute(); + return array( 'projects' => $projects, ); diff --git a/src/applications/project/herald/PhabricatorProjectHeraldAdapter.php b/src/applications/project/herald/PhabricatorProjectHeraldAdapter.php index 72f064a5c6..43c46a878c 100644 --- a/src/applications/project/herald/PhabricatorProjectHeraldAdapter.php +++ b/src/applications/project/herald/PhabricatorProjectHeraldAdapter.php @@ -9,7 +9,7 @@ protected function newObject() { } public function getAdapterApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getAdapterContentDescription() { diff --git a/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php b/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php index e29ee13f2d..771b6a8169 100644 --- a/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php +++ b/src/applications/project/lipsum/PhabricatorProjectTestDataGenerator.php @@ -19,6 +19,12 @@ public function generateObject() { PhabricatorProjectNameTransaction::TRANSACTIONTYPE, $this->newProjectTitle()); + $xactions[] = id(new PhabricatorProjectTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD) + ->setOldValue('') + ->setMetadataValue('customfield:key', 'std:project:internal:description') + ->setNewValue($this->newProjectTitle()); + $xactions[] = $this->newTransaction( PhabricatorProjectStatusTransaction::TRANSACTIONTYPE, $this->newProjectStatus()); diff --git a/src/applications/project/menuitem/PhabricatorProjectDetailsProfileMenuItem.php b/src/applications/project/menuitem/PhabricatorProjectDetailsProfileMenuItem.php index a3021e0239..ec7b88d599 100644 --- a/src/applications/project/menuitem/PhabricatorProjectDetailsProfileMenuItem.php +++ b/src/applications/project/menuitem/PhabricatorProjectDetailsProfileMenuItem.php @@ -31,7 +31,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/project/menuitem/PhabricatorProjectManageProfileMenuItem.php b/src/applications/project/menuitem/PhabricatorProjectManageProfileMenuItem.php index 9b8a769318..656bbbe9a3 100644 --- a/src/applications/project/menuitem/PhabricatorProjectManageProfileMenuItem.php +++ b/src/applications/project/menuitem/PhabricatorProjectManageProfileMenuItem.php @@ -31,7 +31,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/project/menuitem/PhabricatorProjectMembersProfileMenuItem.php b/src/applications/project/menuitem/PhabricatorProjectMembersProfileMenuItem.php index 11a57d3a5b..e3c7f9fceb 100644 --- a/src/applications/project/menuitem/PhabricatorProjectMembersProfileMenuItem.php +++ b/src/applications/project/menuitem/PhabricatorProjectMembersProfileMenuItem.php @@ -21,7 +21,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/project/menuitem/PhabricatorProjectReportsProfileMenuItem.php b/src/applications/project/menuitem/PhabricatorProjectReportsProfileMenuItem.php index 8639b6d72c..3b0acdf375 100644 --- a/src/applications/project/menuitem/PhabricatorProjectReportsProfileMenuItem.php +++ b/src/applications/project/menuitem/PhabricatorProjectReportsProfileMenuItem.php @@ -46,7 +46,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/project/menuitem/PhabricatorProjectSubprojectsProfileMenuItem.php b/src/applications/project/menuitem/PhabricatorProjectSubprojectsProfileMenuItem.php index b1782e8f1c..39b25c1f00 100644 --- a/src/applications/project/menuitem/PhabricatorProjectSubprojectsProfileMenuItem.php +++ b/src/applications/project/menuitem/PhabricatorProjectSubprojectsProfileMenuItem.php @@ -29,7 +29,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php b/src/applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php index 34152f85e7..2f0fbb284b 100644 --- a/src/applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php +++ b/src/applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php @@ -38,7 +38,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/project/phid/PhabricatorProjectColumnPHIDType.php b/src/applications/project/phid/PhabricatorProjectColumnPHIDType.php index c58bb44671..cfcf56f8c0 100644 --- a/src/applications/project/phid/PhabricatorProjectColumnPHIDType.php +++ b/src/applications/project/phid/PhabricatorProjectColumnPHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/project/phid/PhabricatorProjectProjectPHIDType.php b/src/applications/project/phid/PhabricatorProjectProjectPHIDType.php index 9247966d75..e032f98588 100644 --- a/src/applications/project/phid/PhabricatorProjectProjectPHIDType.php +++ b/src/applications/project/phid/PhabricatorProjectProjectPHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } protected function buildQueryForObjects( @@ -43,7 +43,7 @@ public function loadHandles( $handle->setName($name); - if (strlen($slug)) { + if (phutil_nonempty_string($slug)) { $handle->setObjectName('#'.$slug); $handle->setMailStampName('#'.$slug); $handle->setURI("/tag/{$slug}/"); diff --git a/src/applications/project/phid/PhabricatorProjectTriggerPHIDType.php b/src/applications/project/phid/PhabricatorProjectTriggerPHIDType.php index 346b0e69fa..cdcd3c1146 100644 --- a/src/applications/project/phid/PhabricatorProjectTriggerPHIDType.php +++ b/src/applications/project/phid/PhabricatorProjectTriggerPHIDType.php @@ -18,7 +18,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php b/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php index 2673902780..c4f9818a4a 100644 --- a/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php +++ b/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php @@ -67,7 +67,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } } diff --git a/src/applications/project/query/PhabricatorProjectColumnQuery.php b/src/applications/project/query/PhabricatorProjectColumnQuery.php index 478d872cb4..6c1763623a 100644 --- a/src/applications/project/query/PhabricatorProjectColumnQuery.php +++ b/src/applications/project/query/PhabricatorProjectColumnQuery.php @@ -225,7 +225,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } } diff --git a/src/applications/project/query/PhabricatorProjectColumnSearchEngine.php b/src/applications/project/query/PhabricatorProjectColumnSearchEngine.php index 68fe1e7eb0..49702b11ea 100644 --- a/src/applications/project/query/PhabricatorProjectColumnSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectColumnSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function canUseInPanelContext() { diff --git a/src/applications/project/query/PhabricatorProjectColumnTransactionQuery.php b/src/applications/project/query/PhabricatorProjectColumnTransactionQuery.php index f536aad05e..2a18104f76 100644 --- a/src/applications/project/query/PhabricatorProjectColumnTransactionQuery.php +++ b/src/applications/project/query/PhabricatorProjectColumnTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorProjectColumnTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorProjectApplication::class; + } + } diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php index b02fa647a2..7b1ee9afec 100644 --- a/src/applications/project/query/PhabricatorProjectQuery.php +++ b/src/applications/project/query/PhabricatorProjectQuery.php @@ -705,7 +705,7 @@ protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/project/query/PhabricatorProjectSearchEngine.php b/src/applications/project/query/PhabricatorProjectSearchEngine.php index cb179c995f..936264a285 100644 --- a/src/applications/project/query/PhabricatorProjectSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function newQuery() { diff --git a/src/applications/project/query/PhabricatorProjectTransactionQuery.php b/src/applications/project/query/PhabricatorProjectTransactionQuery.php index 0211f6c5bc..e0a82666f9 100644 --- a/src/applications/project/query/PhabricatorProjectTransactionQuery.php +++ b/src/applications/project/query/PhabricatorProjectTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorProjectTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorProjectApplication::class; + } + } diff --git a/src/applications/project/query/PhabricatorProjectTriggerQuery.php b/src/applications/project/query/PhabricatorProjectTriggerQuery.php index 306fcb50fe..4f5f6d997c 100644 --- a/src/applications/project/query/PhabricatorProjectTriggerQuery.php +++ b/src/applications/project/query/PhabricatorProjectTriggerQuery.php @@ -121,7 +121,7 @@ protected function didFilterPage(array $triggers) { } public function getQueryApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/project/query/PhabricatorProjectTriggerSearchEngine.php b/src/applications/project/query/PhabricatorProjectTriggerSearchEngine.php index a178ed3e6c..1239dbeade 100644 --- a/src/applications/project/query/PhabricatorProjectTriggerSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectTriggerSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function newQuery() { diff --git a/src/applications/project/query/PhabricatorProjectTriggerTransactionQuery.php b/src/applications/project/query/PhabricatorProjectTriggerTransactionQuery.php index 9ec4d4a53b..7a6c331947 100644 --- a/src/applications/project/query/PhabricatorProjectTriggerTransactionQuery.php +++ b/src/applications/project/query/PhabricatorProjectTriggerTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorProjectTriggerTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorProjectApplication::class; + } + } diff --git a/src/applications/project/state/PhabricatorWorkboardViewState.php b/src/applications/project/state/PhabricatorWorkboardViewState.php index 04f8498d49..0d5d94f1f2 100644 --- a/src/applications/project/state/PhabricatorWorkboardViewState.php +++ b/src/applications/project/state/PhabricatorWorkboardViewState.php @@ -41,7 +41,7 @@ public function readFromRequest(AphrontRequest $request) { $this->requestState['filter'] = $request->getStr('filter'); } - if (strlen($request->getURIData('queryKey'))) { + if (phutil_nonempty_string($request->getURIData('queryKey'))) { $this->requestState['filter'] = $request->getURIData('queryKey'); } @@ -169,7 +169,7 @@ public function getOrder() { public function getQueryKey() { $request_query = idx($this->requestState, 'filter'); - if (strlen($request_query)) { + if (phutil_nonempty_string($request_query)) { return $request_query; } @@ -203,7 +203,7 @@ private function getDefaultQueryKey() { $default_query = $project->getDefaultWorkboardFilter(); - if (strlen($default_query)) { + if (phutil_nonempty_string($default_query)) { return $default_query; } diff --git a/src/applications/project/storage/PhabricatorProjectColumn.php b/src/applications/project/storage/PhabricatorProjectColumn.php index 49d7f28a9f..e4359acd1b 100644 --- a/src/applications/project/storage/PhabricatorProjectColumn.php +++ b/src/applications/project/storage/PhabricatorProjectColumn.php @@ -271,6 +271,19 @@ public function getFieldSpecificationsForConduit() { pht( 'For columns that proxy another object (like a subproject or '. 'milestone), the PHID of the object they proxy.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('isHidden') + ->setType('bool') + ->setDescription(pht('True if this column is hidden.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('isDefaultColumn') + ->setType('bool') + ->setDescription(pht('True if this is the default column.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('sequence') + ->setType('int') + ->setDescription( + pht('The sequence in which this column appears on the workboard.')), ); } @@ -279,6 +292,9 @@ public function getFieldValuesForConduit() { 'name' => $this->getDisplayName(), 'proxyPHID' => $this->getProxyPHID(), 'project' => $this->getProject()->getRefForConduit(), + 'isHidden' => $this->isHidden(), + 'isDefaultColumn' => $this->isDefaultColumn(), + 'sequence' => (int)$this->getSequence(), ); } diff --git a/src/applications/project/trigger/PhabricatorProjectTriggerPlaySoundRule.php b/src/applications/project/trigger/PhabricatorProjectTriggerPlaySoundRule.php index 9e99eb070f..e67e1f1a64 100644 --- a/src/applications/project/trigger/PhabricatorProjectTriggerPlaySoundRule.php +++ b/src/applications/project/trigger/PhabricatorProjectTriggerPlaySoundRule.php @@ -99,6 +99,10 @@ private static function getSoundMap() { 'name' => pht('Bing'), 'uri' => celerity_get_resource_uri('/rsrc/audio/basic/bing.mp3'), ), + 'coin' => array( + 'name' => pht('Coin'), + 'uri' => celerity_get_resource_uri('/rsrc/audio/basic/coin.mp3'), + ), 'glass' => array( 'name' => pht('Glass'), 'uri' => celerity_get_resource_uri('/rsrc/audio/basic/ting.mp3'), diff --git a/src/applications/project/typeahead/PhabricatorProjectDatasource.php b/src/applications/project/typeahead/PhabricatorProjectDatasource.php index 5b999a997f..ea2ba48b09 100644 --- a/src/applications/project/typeahead/PhabricatorProjectDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function loadResults() { @@ -21,7 +21,9 @@ public function loadResults() { $raw_query = $this->getRawQuery(); // Allow users to type "#qa" or "qa" to find "Quality Assurance". - $raw_query = ltrim($raw_query, '#'); + if ($raw_query !== null) { + $raw_query = ltrim($raw_query, '#'); + } $tokens = self::tokenizeString($raw_query); $query = id(new PhabricatorProjectQuery()) @@ -83,7 +85,7 @@ public function loadResults() { } $slug = $proj->getPrimarySlug(); - if (!strlen($slug)) { + if (!phutil_nonempty_string($slug)) { foreach ($proj->getSlugs() as $slug_object) { $slug = $slug_object->getSlug(); if (strlen($slug)) { @@ -94,7 +96,7 @@ public function loadResults() { // If we're building results for the autocompleter and this project // doesn't have any usable slugs, don't return it as a result. - if ($for_autocomplete && !strlen($slug)) { + if ($for_autocomplete && !phutil_nonempty_string($slug)) { continue; } @@ -132,7 +134,7 @@ public function loadResults() { ->setPriorityType('proj') ->setClosed($closed); - if (strlen($slug)) { + if (phutil_nonempty_string($slug)) { $proj_result->setAutocomplete('#'.$slug); } @@ -142,7 +144,7 @@ public function loadResults() { $proj_result->addAttribute($proj->getDisplayIconName()); $description = idx($descriptions, $phid); - if (strlen($description)) { + if (phutil_nonempty_string($description)) { $summary = PhabricatorMarkupEngine::summarizeSentence($description); $proj_result->addAttribute($summary); } diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalAncestorDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalAncestorDatasource.php index 070cb88485..1c7b1ff3fd 100644 --- a/src/applications/project/typeahead/PhabricatorProjectLogicalAncestorDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalAncestorDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalDatasource.php index b7003cb69e..9cd0fa8b06 100644 --- a/src/applications/project/typeahead/PhabricatorProjectLogicalDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalOnlyDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalOnlyDatasource.php index fe521fc6d8..4967e1c59d 100644 --- a/src/applications/project/typeahead/PhabricatorProjectLogicalOnlyDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalOnlyDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php index 026c7ed2ee..b3c7c9b68b 100644 --- a/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php index bc19c12aa3..b0dca85a05 100644 --- a/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php index 807986457a..9d1a91376a 100644 --- a/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php b/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php index 524ced454d..308352b39c 100644 --- a/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php b/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php index d3b5e484e9..a3288b390f 100644 --- a/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/project/typeahead/PhabricatorProjectSubtypeDatasource.php b/src/applications/project/typeahead/PhabricatorProjectSubtypeDatasource.php index 68de11e630..c688239fbb 100644 --- a/src/applications/project/typeahead/PhabricatorProjectSubtypeDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectSubtypeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function loadResults() { diff --git a/src/applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php b/src/applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php index c0e237f0c6..dfd10c051d 100644 --- a/src/applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorProjectApplication'; + return PhabricatorProjectApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/project/view/PhabricatorProjectCardView.php b/src/applications/project/view/PhabricatorProjectCardView.php index a56697ba7e..01a32c1208 100644 --- a/src/applications/project/view/PhabricatorProjectCardView.php +++ b/src/applications/project/view/PhabricatorProjectCardView.php @@ -68,6 +68,27 @@ protected function getTagContent() { $description = null; + // This getProxy() feels hacky - see also PhabricatorProjectDatasource:67 + $description_field = PhabricatorCustomField::getObjectField( + $project, + PhabricatorCustomField::ROLE_VIEW, + 'std:project:internal:description'); + + if ($description_field !== null) { + $description_field = $description_field->getProxy(); + + $description = $description_field->getFieldValue(); + if (phutil_nonempty_string($description)) { + $description = PhabricatorMarkupEngine::summarizeSentence($description); + $description = id(new PHUIRemarkupView($viewer, $description)); + + $description = phutil_tag( + 'div', + array('class' => 'project-card-body phui-header-shell'), + $description); + } + } + $card = phutil_tag( 'div', array( diff --git a/src/applications/repository/capability/PhabricatorRepositoryIdentityEditViewCapability.php b/src/applications/repository/capability/PhabricatorRepositoryIdentityEditViewCapability.php new file mode 100644 index 0000000000..9bb9b62ebc --- /dev/null +++ b/src/applications/repository/capability/PhabricatorRepositoryIdentityEditViewCapability.php @@ -0,0 +1,16 @@ +getLocalPath(); - if (!strlen($local_path)) { + if (!phutil_nonempty_string($local_path)) { $local_key = 'repository.default-local-path'; $local_root = PhabricatorEnv::getEnvConfig($local_key); diff --git a/src/applications/repository/engine/PhabricatorRepositoryIdentityEditEngine.php b/src/applications/repository/engine/PhabricatorRepositoryIdentityEditEngine.php index 742e4a159f..8cadee7d52 100644 --- a/src/applications/repository/engine/PhabricatorRepositoryIdentityEditEngine.php +++ b/src/applications/repository/engine/PhabricatorRepositoryIdentityEditEngine.php @@ -22,7 +22,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function newEditableObject() { @@ -70,7 +70,8 @@ protected function getObjectViewURI($object) { } protected function getCreateNewObjectPolicy() { - return PhabricatorPolicies::POLICY_USER; + return $this->getApplication()->getPolicy( + PhabricatorRepositoryIdentityEditViewCapability::CAPABILITY); } protected function buildCustomEditFields($object) { diff --git a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php index 12aee9bc51..5bcf4554fd 100644 --- a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php +++ b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php @@ -238,7 +238,7 @@ private function getHookContextIdentifier(PhabricatorRepository $repository) { $identifier = $repository->getPHID(); $instance = PhabricatorEnv::getEnvConfig('cluster.instance'); - if (strlen($instance)) { + if (phutil_nonempty_string($instance)) { $identifier = "{$identifier}:{$instance}"; } diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php index 65fe0adaad..513891c5c6 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php @@ -23,7 +23,7 @@ protected function didConstruct() { 'name' => 'stop', 'help' => pht( 'Take repositories out of maintenance mode, returning them '. - 'to normal serice.'), + 'to normal service.'), ), array( 'name' => 'repositories', @@ -42,7 +42,7 @@ public function execute(PhutilArgumentParser $args) { } $message = $args->getArg('start'); - $is_start = (bool)strlen($message); + $is_start = $message !== null; $is_stop = $args->getArg('stop'); if (!$is_start && !$is_stop) { diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php index ced8663b3c..2c684dcb1f 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php @@ -38,14 +38,14 @@ public function execute(PhutilArgumentParser $args) { } $from = $args->getArg('from'); - if (!strlen($from)) { + if (!phutil_nonempty_string($from)) { throw new Exception( pht( 'You must specify a path prefix to move from with --from.')); } $to = $args->getArg('to'); - if (!strlen($to)) { + if (!phutil_nonempty_string($to)) { throw new Exception( pht( 'You must specify a path prefix to move to with --to.')); diff --git a/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php index df84f2dcfd..a803de5023 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryCommitPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositoryIdentityPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryIdentityPHIDType.php index 873cfbbae6..c3844c3ef2 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryIdentityPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryIdentityPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositoryPullEventPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryPullEventPHIDType.php index 45ed2819fb..3b0400436d 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryPullEventPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryPullEventPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositoryPushEventPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryPushEventPHIDType.php index 364ce62e5d..65741fded1 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryPushEventPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryPushEventPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositoryPushLogPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryPushLogPHIDType.php index 6af117db19..a5dc223ac8 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryPushLogPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryPushLogPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositoryRefCursorPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryRefCursorPHIDType.php index 30fbc4b6ae..aac22de8ca 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryRefCursorPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryRefCursorPHIDType.php @@ -18,7 +18,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositoryRepositoryPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryRepositoryPHIDType.php index 6ca67257cf..dcc0e174e5 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryRepositoryPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryRepositoryPHIDType.php @@ -18,7 +18,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositorySyncEventPHIDType.php b/src/applications/repository/phid/PhabricatorRepositorySyncEventPHIDType.php index ade9560a93..86a3a6d0f9 100644 --- a/src/applications/repository/phid/PhabricatorRepositorySyncEventPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositorySyncEventPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/phid/PhabricatorRepositoryURIPHIDType.php b/src/applications/repository/phid/PhabricatorRepositoryURIPHIDType.php index 658b942418..b330631c55 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryURIPHIDType.php +++ b/src/applications/repository/phid/PhabricatorRepositoryURIPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php b/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php index 08f0ada159..976aef9ab4 100644 --- a/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php @@ -54,7 +54,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php b/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php index 2b05b542d5..78fd4f729d 100644 --- a/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php @@ -150,7 +150,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryIdentityTransactionQuery.php b/src/applications/repository/query/PhabricatorRepositoryIdentityTransactionQuery.php index f62a8610ff..c85ff3f5ad 100644 --- a/src/applications/repository/query/PhabricatorRepositoryIdentityTransactionQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryIdentityTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorRepositoryIdentityTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDiffusionApplication::class; + } + } diff --git a/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php b/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php index 8d4f14e0ce..b398012173 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php @@ -125,7 +125,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php b/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php index d1ce937b86..eec40da96d 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php @@ -112,7 +112,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php b/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php index 16897a1e4b..2f264510b2 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php @@ -184,7 +184,7 @@ private function shouldJoinPushEventTable() { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php b/src/applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php index 271d8a9c14..642b22bc24 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php +++ b/src/applications/repository/query/PhabricatorRepositoryPushLogSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function newQuery() { diff --git a/src/applications/repository/query/PhabricatorRepositoryQuery.php b/src/applications/repository/query/PhabricatorRepositoryQuery.php index 05b011e85a..04a99bcf11 100644 --- a/src/applications/repository/query/PhabricatorRepositoryQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryQuery.php @@ -9,7 +9,6 @@ final class PhabricatorRepositoryQuery private $types; private $uuids; private $uris; - private $datasourceQuery; private $slugs; private $almanacServicePHIDs; @@ -62,6 +61,10 @@ public function withIdentifiers(array $identifiers) { $slugs = array(); foreach ($identifiers as $identifier) { + if ($identifier === null) { + continue; + } + if (ctype_digit((string)$identifier)) { $ids[$identifier] = $identifier; continue; @@ -120,11 +123,6 @@ public function withURIs(array $uris) { return $this; } - public function withDatasourceQuery($query) { - $this->datasourceQuery = $query; - return $this; - } - public function withSlugs(array $slugs) { $this->slugs = $slugs; return $this; @@ -633,22 +631,6 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $this->uuids); } - if (phutil_nonempty_string($this->datasourceQuery)) { - // This handles having "rP" match callsigns starting with "P...". - $query = trim($this->datasourceQuery); - if (preg_match('/^r/', $query)) { - $callsign = substr($query, 1); - } else { - $callsign = $query; - } - $where[] = qsprintf( - $conn, - 'r.name LIKE %> OR r.callsign LIKE %> OR r.repositorySlug LIKE %>', - $query, - $callsign, - $query); - } - if ($this->slugs !== null) { $where[] = qsprintf( $conn, @@ -677,7 +659,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } private function getNormalizedURIs() { diff --git a/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php b/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php index 5e894333f6..41cc4aff7f 100644 --- a/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php @@ -132,7 +132,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $name_hashes); } - if (strlen($this->datasourceQuery)) { + if (phutil_nonempty_string($this->datasourceQuery)) { $where[] = qsprintf( $conn, 'refNameRaw LIKE %>', @@ -143,7 +143,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/repository/query/PhabricatorRepositorySearchEngine.php b/src/applications/repository/query/PhabricatorRepositorySearchEngine.php index 90a10e9872..33ae85481d 100644 --- a/src/applications/repository/query/PhabricatorRepositorySearchEngine.php +++ b/src/applications/repository/query/PhabricatorRepositorySearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } public function newQuery() { diff --git a/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php b/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php index cc568ef8e1..983187ffab 100644 --- a/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php +++ b/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php @@ -105,7 +105,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryTransactionQuery.php b/src/applications/repository/query/PhabricatorRepositoryTransactionQuery.php index 27410709c3..0656e80ef7 100644 --- a/src/applications/repository/query/PhabricatorRepositoryTransactionQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorRepositoryTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDiffusionApplication::class; + } + } diff --git a/src/applications/repository/query/PhabricatorRepositoryURIQuery.php b/src/applications/repository/query/PhabricatorRepositoryURIQuery.php index 5b75e1ef63..566aa0977f 100644 --- a/src/applications/repository/query/PhabricatorRepositoryURIQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryURIQuery.php @@ -91,7 +91,7 @@ protected function willFilterPage(array $uris) { } public function getQueryApplicationClass() { - return 'PhabricatorDiffusionApplication'; + return PhabricatorDiffusionApplication::class; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryURITransactionQuery.php b/src/applications/repository/query/PhabricatorRepositoryURITransactionQuery.php index 28ae9d9d62..271cba67bd 100644 --- a/src/applications/repository/query/PhabricatorRepositoryURITransactionQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryURITransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorRepositoryURITransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDiffusionApplication::class; + } + } diff --git a/src/applications/repository/search/PhabricatorRepositoryFulltextEngine.php b/src/applications/repository/search/PhabricatorRepositoryFulltextEngine.php index f666af552f..838692c351 100644 --- a/src/applications/repository/search/PhabricatorRepositoryFulltextEngine.php +++ b/src/applications/repository/search/PhabricatorRepositoryFulltextEngine.php @@ -7,10 +7,21 @@ protected function buildAbstractDocument( PhabricatorSearchAbstractDocument $document, $object) { $repo = $object; - $document->setDocumentTitle($repo->getName()); + + $title_fields = array( + $repo->getName(), + $repo->getRepositorySlug(), + ); + $callsign = $repo->getCallsign(); + if ($callsign) { + $title_fields[] = $callsign; + $title_fields[] = 'r'.$callsign; + } + + $document->setDocumentTitle(implode("\n", $title_fields)); $document->addField( PhabricatorSearchDocumentFieldType::FIELD_BODY, - $repo->getRepositorySlug()."\n".$repo->getDetail('description')); + $repo->getDetail('description')); $document->setDocumentCreated($repo->getDateCreated()); $document->setDocumentModified($repo->getDateModified()); diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index c68eb22668..b2855c0c06 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -217,7 +217,7 @@ public function getAllMonograms() { $monograms[] = 'R'.$this->getID(); $callsign = $this->getCallsign(); - if (strlen($callsign)) { + if (phutil_nonempty_string($callsign)) { $monograms[] = 'r'.$callsign; } @@ -345,12 +345,12 @@ public function getCloneName() { // Make some reasonable effort to produce reasonable default directory // names from repository names. - if (!strlen($name)) { + if (!phutil_nonempty_string($name)) { $name = $this->getName(); $name = phutil_utf8_strtolower($name); $name = preg_replace('@[ -/:->]+@', '-', $name); $name = trim($name, '-'); - if (!strlen($name)) { + if (!phutil_nonempty_string($name)) { $name = $this->getCallsign(); } } @@ -779,15 +779,15 @@ public function generateURI(array $params) { break; case 'compare': $uri = $this->getPathURI("/{$action}/"); - if (strlen($head)) { + if (phutil_nonempty_scalar($head)) { $query['head'] = $head; - } else if (strlen($raw_commit)) { + } else if (phutil_nonempty_scalar($raw_commit)) { $query['commit'] = $raw_commit; - } else if (strlen($raw_branch)) { + } else if (phutil_nonempty_scalar($raw_branch)) { $query['head'] = $raw_branch; } - if (strlen($against)) { + if (phutil_nonempty_scalar($against)) { $query['against'] = $against; } break; @@ -1160,7 +1160,7 @@ public function getRemoteProtocol() { */ public function getRemoteURIObject() { $raw_uri = $this->getDetail('remote-uri'); - if (!strlen($raw_uri)) { + if (!phutil_nonempty_string($raw_uri)) { return new PhutilURI(''); } @@ -2480,7 +2480,8 @@ public function newBuiltinURIs() { $has_https = false; } - $has_ssh = (bool)strlen(PhabricatorEnv::getEnvConfig('phd.user')); + $phd_user = PhabricatorEnv::getEnvConfig('phd.user'); + $has_ssh = phutil_nonempty_string($phd_user); $protocol_map = array( PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH => $has_ssh, diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommit.php b/src/applications/repository/storage/PhabricatorRepositoryCommit.php index 9e20a36676..ef3657d49f 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryCommit.php +++ b/src/applications/repository/storage/PhabricatorRepositoryCommit.php @@ -478,7 +478,7 @@ public function newCommitAuthorView(PhabricatorUser $viewer) { } $author = $this->getRawAuthorStringForDisplay(); - if (strlen($author)) { + if (phutil_nonempty_string($author)) { return DiffusionView::renderName($author); } @@ -493,7 +493,7 @@ public function newCommitCommitterView(PhabricatorUser $viewer) { } $committer = $this->getRawCommitterStringForDisplay(); - if (strlen($committer)) { + if (phutil_nonempty_string($committer)) { return DiffusionView::renderName($committer); } diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommitData.php b/src/applications/repository/storage/PhabricatorRepositoryCommitData.php index c77da64ec2..96508a2452 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryCommitData.php +++ b/src/applications/repository/storage/PhabricatorRepositoryCommitData.php @@ -97,7 +97,7 @@ public function getAuthorString() { $ref = $this->getCommitRef(); $author = $ref->getAuthor(); - if (strlen($author)) { + if (phutil_nonempty_string($author)) { return $author; } @@ -131,7 +131,7 @@ public function getCommitterString() { $ref = $this->getCommitRef(); $committer = $ref->getCommitter(); - if (strlen($committer)) { + if (phutil_nonempty_string($committer)) { return $committer; } diff --git a/src/applications/repository/storage/PhabricatorRepositoryIdentity.php b/src/applications/repository/storage/PhabricatorRepositoryIdentity.php index 74fbd06544..df19f302f6 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryIdentity.php +++ b/src/applications/repository/storage/PhabricatorRepositoryIdentity.php @@ -142,7 +142,10 @@ public function getCapabilities() { } public function getPolicy($capability) { - return PhabricatorPolicies::getMostOpenPolicy(); + $app = PhabricatorApplication::getByClass( + 'PhabricatorDiffusionApplication'); + return $app->getPolicy( + PhabricatorRepositoryIdentityEditViewCapability::CAPABILITY); } public function hasAutomaticCapability( diff --git a/src/applications/repository/storage/PhabricatorRepositoryURI.php b/src/applications/repository/storage/PhabricatorRepositoryURI.php index 26db694a66..774df1fd95 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryURI.php +++ b/src/applications/repository/storage/PhabricatorRepositoryURI.php @@ -752,7 +752,7 @@ public static function getURINormalizerDomainMap() { // without requiring an index rebuild. $ssh_host = PhabricatorEnv::getEnvConfig('diffusion.ssh-host'); - if (strlen($ssh_host)) { + if (phutil_nonempty_string($ssh_host)) { $domain_map[''] = $ssh_host; } diff --git a/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php b/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php index 241a95dad9..49d1a44943 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php +++ b/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php @@ -57,7 +57,7 @@ public function getTitle() { $new_label = idx(idx($map, $new, array()), 'label', $new); return pht( - '%s changed the display type for this URI from "%s" to "%s".', + '%s changed the I/O type for this URI from "%s" to "%s".', $this->renderHandleLink($author_phid), $old_label, $new_label); diff --git a/src/applications/repository/worker/__tests__/PhabricatorChangeParserTestCase.php b/src/applications/repository/worker/__tests__/PhabricatorChangeParserTestCase.php index c3dd6f3d60..0ffb963b71 100644 --- a/src/applications/repository/worker/__tests__/PhabricatorChangeParserTestCase.php +++ b/src/applications/repository/worker/__tests__/PhabricatorChangeParserTestCase.php @@ -517,7 +517,7 @@ public function testSubversionParser() { '/dir', null, null, - // TODO: This might reasonbly be considered a bug in the parser; it + // TODO: This might reasonably be considered a bug in the parser; it // should probably be COPY_AWAY. DifferentialChangeType::TYPE_CHILD, DifferentialChangeType::FILE_DIRECTORY, diff --git a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php index a0ea2d84b2..fc0bd42477 100644 --- a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php +++ b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php @@ -36,7 +36,7 @@ final protected function updateCommitData( $author = $ref->getAuthor(); $committer = $ref->getCommitter(); - $has_committer = (bool)strlen($committer); + $has_committer = phutil_nonempty_string($committer); $identity_engine = id(new DiffusionRepositoryIdentityEngine()) ->setViewer($viewer) diff --git a/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php index ab13c6a571..418acf476e 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryCallsignTransaction.php @@ -25,12 +25,12 @@ public function getTitle() { $old = $this->getOldValue(); $new = $this->getNewValue(); - if (!strlen($old)) { + if ($old === null) { return pht( '%s set the callsign for this repository to %s.', $this->renderAuthor(), $this->renderNewValue()); - } else if (!strlen($new)) { + } else if ($new === null) { return pht( '%s removed the callsign (%s) for this repository.', $this->renderAuthor(), diff --git a/src/applications/repository/xaction/PhabricatorRepositoryDefaultBranchTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryDefaultBranchTransaction.php index 23eebf60c8..949e394b91 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryDefaultBranchTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryDefaultBranchTransaction.php @@ -17,12 +17,12 @@ public function getTitle() { $old = $this->getOldValue(); $new = $this->getNewValue(); - if (!strlen($new)) { + if (!phutil_nonempty_string($new)) { return pht( '%s removed %s as the default branch.', $this->renderAuthor(), $this->renderOldValue()); - } else if (!strlen($old)) { + } else if (!phutil_nonempty_string($old)) { return pht( '%s set the default branch to %s.', $this->renderAuthor(), diff --git a/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php index ef129da832..203b98abec 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryEncodingTransaction.php @@ -17,12 +17,12 @@ public function getTitle() { $old = $this->getOldValue(); $new = $this->getNewValue(); - if (strlen($old) && !strlen($new)) { + if ($old !== null && $new === null) { return pht( '%s removed the %s encoding configured for this repository.', $this->renderAuthor(), $this->renderOldValue()); - } else if (strlen($new) && !strlen($old)) { + } else if ($new !== null && $old === null) { return pht( '%s set the encoding for this repository to %s.', $this->renderAuthor(), diff --git a/src/applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php index e81ecbe80b..f7a127005a 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryIdentityAssignTransaction.php @@ -52,7 +52,7 @@ public function validateTransactions($object, array $xactions) { foreach ($xactions as $xaction) { $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); - if (!strlen($new)) { + if ($new === null || !strlen($new)) { continue; } diff --git a/src/applications/repository/xaction/PhabricatorRepositoryMaintenanceTransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryMaintenanceTransaction.php index caf9e84527..c1259aa983 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryMaintenanceTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryMaintenanceTransaction.php @@ -25,11 +25,14 @@ public function getTitle() { $old = $this->getOldValue(); $new = $this->getNewValue(); - if (strlen($old) && !strlen($new)) { + $old_nonempty = phutil_nonempty_string($old); + $new_nonempty = phutil_nonempty_string($new); + + if ($old_nonempty && !$new_nonempty) { return pht( '%s took this repository out of maintenance mode.', $this->renderAuthor()); - } else if (!strlen($old) && strlen($new)) { + } else if (!$old_nonempty && $new_nonempty) { return pht( '%s put this repository into maintenance mode.', $this->renderAuthor()); diff --git a/src/applications/repository/xaction/PhabricatorRepositorySVNSubpathTransaction.php b/src/applications/repository/xaction/PhabricatorRepositorySVNSubpathTransaction.php index 10de2d9980..43ebefa015 100644 --- a/src/applications/repository/xaction/PhabricatorRepositorySVNSubpathTransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositorySVNSubpathTransaction.php @@ -17,12 +17,12 @@ public function getTitle() { $old = $this->getOldValue(); $new = $this->getNewValue(); - if (!strlen($new)) { + if ($new === null || !strlen($new)) { return pht( '%s removed %s as the "Import Only" path.', $this->renderAuthor(), $this->renderOldValue()); - } else if (!strlen($old)) { + } else if ($old === null || !$old) { return pht( '%s set the repository "Import Only" path to %s.', $this->renderAuthor(), diff --git a/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php b/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php index 4297d5e244..ed50f65e4e 100644 --- a/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php +++ b/src/applications/repository/xaction/PhabricatorRepositoryStagingURITransaction.php @@ -17,12 +17,12 @@ public function getTitle() { $old = $this->getOldValue(); $new = $this->getNewValue(); - if (!strlen($old)) { + if ($old === null || !strlen($old)) { return pht( '%s set %s as the staging area for this repository.', $this->renderAuthor(), $this->renderNewValue()); - } else if (!strlen($new)) { + } else if ($new === null || !strlen($new)) { return pht( '%s removed %s as the staging area for this repository.', $this->renderAuthor(), diff --git a/src/applications/search/applicationpanel/PhabricatorSearchApplicationStorageEnginePanel.php b/src/applications/search/applicationpanel/PhabricatorSearchApplicationStorageEnginePanel.php index 6dd0786c0c..e8bc9854b7 100644 --- a/src/applications/search/applicationpanel/PhabricatorSearchApplicationStorageEnginePanel.php +++ b/src/applications/search/applicationpanel/PhabricatorSearchApplicationStorageEnginePanel.php @@ -48,7 +48,7 @@ public function buildConfigurationPagePanel() { } $instructions = pht( - 'To configure the search engines, edit [[ %s | `%s` ]] configuration. '. + 'To configure the search engines, edit [[ %s | %s ]] configuration. '. 'See **[[ %s | %s ]]** for documentation.', '/config/edit/cluster.search/', 'cluster.search', diff --git a/src/applications/search/compiler/PhutilSearchQueryCompiler.php b/src/applications/search/compiler/PhutilSearchQueryCompiler.php index c3f93e16c9..951939b52c 100644 --- a/src/applications/search/compiler/PhutilSearchQueryCompiler.php +++ b/src/applications/search/compiler/PhutilSearchQueryCompiler.php @@ -103,7 +103,9 @@ public function newTokens($query) { private function tokenizeQuery($query) { $maximum_bytes = 1024; - + if ($query === null) { + $query = ''; + } $query_bytes = strlen($query); if ($query_bytes > $maximum_bytes) { throw new PhutilSearchQueryCompilerSyntaxException( @@ -295,7 +297,7 @@ private function tokenizeQuery($query) { $use_substring = true; } else if (phutil_preg_match('/^_/', $value)) { // See T13632. Assume users searching for any term that begins - // with an undescore intend to perform substring search if they + // with an underscore intend to perform substring search if they // don't provide an explicit search function. $use_substring = true; } diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php index 290623e149..d8f53538e4 100644 --- a/src/applications/search/controller/PhabricatorApplicationSearchController.php +++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php @@ -115,7 +115,7 @@ private function processSearchRequest() { if ($this->queryKey == 'advanced') { $run_query = false; $query_key = $request->getStr('query'); - } else if (!strlen($this->queryKey)) { + } else if (!phutil_nonempty_string($this->queryKey)) { $found_query_data = false; if ($request->isHTTPGet() || $request->isQuicksand()) { @@ -775,7 +775,7 @@ private function renderNewUserView( $force_nux) { // Don't render NUX if the user has clicked away from the default page. - if (strlen($this->getQueryKey())) { + if (phutil_nonempty_string($this->getQueryKey())) { return null; } @@ -854,7 +854,7 @@ public static function newOverheatedError($has_results) { $overheated_link = phutil_tag( 'a', array( - 'href' => 'https://phurl.io/u/overheated', + 'href' => 'https://secure.phabricator.com/T13274', 'target' => '_blank', ), pht('Learn More')); diff --git a/src/applications/search/controller/PhabricatorSearchController.php b/src/applications/search/controller/PhabricatorSearchController.php index b3d3ab18fa..336604d228 100644 --- a/src/applications/search/controller/PhabricatorSearchController.php +++ b/src/applications/search/controller/PhabricatorSearchController.php @@ -13,7 +13,7 @@ public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $query = $request->getStr('query'); - if ($request->getStr('jump') != 'no' && strlen($query)) { + if ($request->getStr('jump') != 'no' && phutil_nonempty_string($query)) { $jump_uri = id(new PhabricatorDatasourceEngine()) ->setViewer($viewer) ->newJumpURI($query); diff --git a/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php b/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php index b55f43255c..445be7777f 100644 --- a/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php +++ b/src/applications/search/editor/PhabricatorProfileMenuEditEngine.php @@ -74,7 +74,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } protected function newEditableObject() { diff --git a/src/applications/search/editor/PhabricatorProfileMenuEditor.php b/src/applications/search/editor/PhabricatorProfileMenuEditor.php index 71f8c32e94..d385c8e90e 100644 --- a/src/applications/search/editor/PhabricatorProfileMenuEditor.php +++ b/src/applications/search/editor/PhabricatorProfileMenuEditor.php @@ -4,7 +4,7 @@ final class PhabricatorProfileMenuEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php index 95f1ffafa3..cad397b0ac 100644 --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -179,7 +179,7 @@ public function buildQueryFromSavedQuery(PhabricatorSavedQuery $original) { $order = $saved->getParameter('order'); $builtin = $query->getBuiltinOrderAliasMap(); - if (strlen($order) && isset($builtin[$order])) { + if (phutil_nonempty_string($order) && isset($builtin[$order])) { $query->setOrder($order); } else { // If the order is invalid or not available, we choose the first @@ -275,16 +275,18 @@ protected function buildSearchFields() { ->setOptions($orders); } - $buckets = $this->newResultBuckets(); - if ($query && $buckets) { - $bucket_options = array( - self::BUCKET_NONE => pht('No Bucketing'), - ) + mpull($buckets, 'getResultBucketName'); + if (id(new PhabricatorAuditApplication())->isInstalled()) { + $buckets = $this->newResultBuckets(); + if ($query && $buckets) { + $bucket_options = array( + self::BUCKET_NONE => pht('No Bucketing'), + ) + mpull($buckets, 'getResultBucketName'); - $fields[] = id(new PhabricatorSearchSelectField()) - ->setLabel(pht('Bucket')) - ->setKey('bucket') - ->setOptions($bucket_options); + $fields[] = id(new PhabricatorSearchSelectField()) + ->setLabel(pht('Bucket')) + ->setKey('bucket') + ->setOptions($bucket_options); + } } $field_map = array(); @@ -872,7 +874,7 @@ protected function readListFromRequest( protected function readBoolFromRequest( AphrontRequest $request, $key) { - if (!strlen($request->getStr($key))) { + if (!phutil_nonempty_string($request->getStr($key))) { return null; } return $request->getBool($key); @@ -1137,7 +1139,8 @@ public function buildConduitResponse( $viewer = $this->requireViewer(); $query_key = $request->getValue('queryKey'); - if (!strlen($query_key)) { + $is_empty_query_key = phutil_string_cast($query_key) === ''; + if ($is_empty_query_key) { $saved_query = new PhabricatorSavedQuery(); } else if ($this->isBuiltinQuery($query_key)) { $saved_query = $this->buildSavedQueryFromBuiltin($query_key); diff --git a/src/applications/search/engine/PhabricatorProfileMenuEngine.php b/src/applications/search/engine/PhabricatorProfileMenuEngine.php index aedbcc787f..8799ae8f0b 100644 --- a/src/applications/search/engine/PhabricatorProfileMenuEngine.php +++ b/src/applications/search/engine/PhabricatorProfileMenuEngine.php @@ -135,7 +135,7 @@ public function buildResponse() { if ($is_view) { $selected_item = $this->selectViewItem($view_list, $item_id); } else { - if (!strlen($item_id)) { + if (!phutil_nonempty_scalar($item_id)) { $item_id = self::ITEM_MANAGE; } $selected_item = $this->selectEditItem($view_list, $item_id); @@ -1308,7 +1308,7 @@ private function selectViewItem( // render the default view instead. $selected_view = null; - if (strlen($item_id)) { + if (phutil_nonempty_string($item_id)) { $item_views = $view_list->getViewsWithItemIdentifier($item_id); if ($item_views) { $selected_view = head($item_views); diff --git a/src/applications/search/engine/PhabricatorProfileMenuItemView.php b/src/applications/search/engine/PhabricatorProfileMenuItemView.php index d947afcba6..7f0dd1d397 100644 --- a/src/applications/search/engine/PhabricatorProfileMenuItemView.php +++ b/src/applications/search/engine/PhabricatorProfileMenuItemView.php @@ -140,7 +140,7 @@ public function newListItemView() { ->setName($this->getName()); $uri = $this->getURI(); - if (strlen($uri)) { + if (phutil_nonempty_string($uri)) { if ($this->getIsExternalLink()) { if (!PhabricatorEnv::isValidURIForLink($uri)) { $uri = '#'; @@ -176,7 +176,7 @@ public function newListItemView() { } $tooltip = $this->getTooltip(); - if (strlen($tooltip)) { + if (phutil_nonempty_string($tooltip)) { $view->setTooltip($tooltip); } diff --git a/src/applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php b/src/applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php index 55b7c2225a..2544f8b7da 100644 --- a/src/applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php +++ b/src/applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php @@ -27,7 +27,7 @@ public function applyConstraintsToQuery( PhabricatorSavedQuery $saved, array $map) { - if (!strlen($map['query'])) { + if (!(isset($map['query']) && strlen($map['query']))) { return; } diff --git a/src/applications/search/field/PhabricatorSearchDateField.php b/src/applications/search/field/PhabricatorSearchDateField.php index 41decd9503..8f43494222 100644 --- a/src/applications/search/field/PhabricatorSearchDateField.php +++ b/src/applications/search/field/PhabricatorSearchDateField.php @@ -17,7 +17,7 @@ public function getValueForQuery($value) { } protected function validateControlValue($value) { - if (!strlen($value)) { + if (!phutil_nonempty_scalar($value)) { return; } @@ -32,7 +32,7 @@ protected function validateControlValue($value) { } protected function parseDateTime($value) { - if (!strlen($value)) { + if (!phutil_nonempty_scalar($value)) { return null; } diff --git a/src/applications/search/field/PhabricatorSearchTextField.php b/src/applications/search/field/PhabricatorSearchTextField.php index 915f22a6e9..21d256e882 100644 --- a/src/applications/search/field/PhabricatorSearchTextField.php +++ b/src/applications/search/field/PhabricatorSearchTextField.php @@ -11,6 +11,15 @@ protected function getValueFromRequest(AphrontRequest $request, $key) { return $request->getStr($key); } + protected function validateControlValue($value) { + if (!is_array($value)) { + return; + } + $this->addError( + pht('Invalid'), + pht('Text value for "%s" can not be parsed.', $this->getLabel())); + } + protected function newControl() { return new AphrontFormTextControl(); } diff --git a/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php b/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php index 9d89c52ff8..717bba7c39 100644 --- a/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php +++ b/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php @@ -117,7 +117,7 @@ public function getDisplayName( return pht('Archived Dashboard'); } - if (strlen($this->getName($config))) { + if (phutil_nonempty_string($this->getName($config))) { return $this->getName($config); } else { return $dashboard->getName(); diff --git a/src/applications/search/menuitem/PhabricatorEditEngineProfileMenuItem.php b/src/applications/search/menuitem/PhabricatorEditEngineProfileMenuItem.php index 71e3d7e8a5..23d5ad3f30 100644 --- a/src/applications/search/menuitem/PhabricatorEditEngineProfileMenuItem.php +++ b/src/applications/search/menuitem/PhabricatorEditEngineProfileMenuItem.php @@ -71,7 +71,7 @@ public function getDisplayName( if (!$form) { return pht('(Restricted/Invalid Form)'); } - if (strlen($this->getName($config))) { + if (phutil_nonempty_string($this->getName($config))) { return $this->getName($config); } else { return $form->getName(); diff --git a/src/applications/search/menuitem/PhabricatorManageProfileMenuItem.php b/src/applications/search/menuitem/PhabricatorManageProfileMenuItem.php index 89ac4a5633..83c3133705 100644 --- a/src/applications/search/menuitem/PhabricatorManageProfileMenuItem.php +++ b/src/applications/search/menuitem/PhabricatorManageProfileMenuItem.php @@ -31,7 +31,7 @@ public function getDisplayName( PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/search/menuitem/PhabricatorProfileMenuItem.php b/src/applications/search/menuitem/PhabricatorProfileMenuItem.php index 773a0f09ac..2dc0270d24 100644 --- a/src/applications/search/menuitem/PhabricatorProfileMenuItem.php +++ b/src/applications/search/menuitem/PhabricatorProfileMenuItem.php @@ -126,7 +126,7 @@ final protected function isEmptyTransaction($value, array $xactions) { $result = $xaction['new']; } - return !strlen($result); + return !phutil_nonempty_string($result); } final protected function newError($title, $message, $xaction = null) { diff --git a/src/applications/search/phidtype/PhabricatorProfileMenuItemPHIDType.php b/src/applications/search/phidtype/PhabricatorProfileMenuItemPHIDType.php index 0fbdcaca48..53f248114e 100644 --- a/src/applications/search/phidtype/PhabricatorProfileMenuItemPHIDType.php +++ b/src/applications/search/phidtype/PhabricatorProfileMenuItemPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php b/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php index 862f694fc8..82778fa1e2 100644 --- a/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php +++ b/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php @@ -54,7 +54,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } } diff --git a/src/applications/search/query/PhabricatorNamedQueryQuery.php b/src/applications/search/query/PhabricatorNamedQueryQuery.php index 0ed92646e6..3d57005745 100644 --- a/src/applications/search/query/PhabricatorNamedQueryQuery.php +++ b/src/applications/search/query/PhabricatorNamedQueryQuery.php @@ -67,7 +67,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } } diff --git a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php index 9f26a81424..0d26dace7a 100644 --- a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php +++ b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php @@ -149,7 +149,7 @@ protected function willFilterPage(array $page) { } public function getQueryApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } protected function getPrimaryTableAlias() { diff --git a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationTransactionQuery.php b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationTransactionQuery.php index 0023660512..721bac781e 100644 --- a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationTransactionQuery.php +++ b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorProfileMenuItemConfigurationTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorSearchApplication::class; + } + } diff --git a/src/applications/search/query/PhabricatorSavedQueryQuery.php b/src/applications/search/query/PhabricatorSavedQueryQuery.php index 765c751940..d8e939d424 100644 --- a/src/applications/search/query/PhabricatorSavedQueryQuery.php +++ b/src/applications/search/query/PhabricatorSavedQueryQuery.php @@ -67,7 +67,7 @@ protected function buildWhereClause(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } } diff --git a/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php b/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php index 3fcf0a8f7a..dca98e8411 100644 --- a/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php +++ b/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php @@ -10,7 +10,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } public function buildSavedQueryFromRequest(AphrontRequest $request) { diff --git a/src/applications/search/query/PhabricatorSearchDocumentQuery.php b/src/applications/search/query/PhabricatorSearchDocumentQuery.php index 4aed4722bd..7282b33efb 100644 --- a/src/applications/search/query/PhabricatorSearchDocumentQuery.php +++ b/src/applications/search/query/PhabricatorSearchDocumentQuery.php @@ -99,7 +99,7 @@ protected function willFilterPage(array $handles) { } public function getQueryApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } protected function nextPage(array $page) { diff --git a/src/applications/search/typeahead/PhabricatorSearchDatasource.php b/src/applications/search/typeahead/PhabricatorSearchDatasource.php index 2d8182a2a2..c609d022bb 100644 --- a/src/applications/search/typeahead/PhabricatorSearchDatasource.php +++ b/src/applications/search/typeahead/PhabricatorSearchDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } public function getComponentDatasources() { diff --git a/src/applications/search/typeahead/PhabricatorSearchDocumentTypeDatasource.php b/src/applications/search/typeahead/PhabricatorSearchDocumentTypeDatasource.php index c99cc4ede3..9b94d0b24d 100644 --- a/src/applications/search/typeahead/PhabricatorSearchDocumentTypeDatasource.php +++ b/src/applications/search/typeahead/PhabricatorSearchDocumentTypeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorSearchApplication'; + return PhabricatorSearchApplication::class; } public function loadResults() { diff --git a/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php index 8ae2704161..0ed286da02 100644 --- a/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php +++ b/src/applications/settings/controller/PhabricatorSettingsTimezoneController.php @@ -31,11 +31,12 @@ public function handleRequest(AphrontRequest $request) { $did_calibrate = false; if ($request->isFormPost()) { $timezone = $request->getStr('timezone'); + $ignore_conflict_checkbox = $request->getInt('ignoreConflict'); $pref_ignore = PhabricatorTimezoneIgnoreOffsetSetting::SETTINGKEY; $pref_timezone = PhabricatorTimezoneSetting::SETTINGKEY; - if ($timezone == 'ignore') { + if ($timezone === 'ignore' || $ignore_conflict_checkbox) { $this->writeSettings( array( $pref_ignore => $client_offset, @@ -83,20 +84,29 @@ public function handleRequest(AphrontRequest $request) { $guess = 'ignore'; } - $current_zone = $viewer->getTimezoneIdentifier(); - $current_zone = phutil_tag('strong', array(), $current_zone); + $current_zone_identifier = $viewer->getTimezoneIdentifier(); + $current_zone_formatted = phutil_tag( + 'strong', + array(), + $current_zone_identifier); $form = id(new AphrontFormView()) ->appendChild( id(new AphrontFormMarkupControl()) ->setLabel(pht('Current Setting')) - ->setValue($current_zone)) + ->setValue($current_zone_formatted)) ->appendChild( id(new AphrontFormSelectControl()) ->setName('timezone') ->setLabel(pht('New Setting')) ->setOptions($options) - ->setValue($guess)); + ->setValue($guess)) + ->appendChild(id(new AphrontFormCheckboxControl()) + ->addCheckbox( + 'ignoreConflict', + 1, + pht('Ignore New Setting and Keep %s', $current_zone_identifier))); + return $this->newDialog() ->setTitle(pht('Adjust Timezone')) @@ -104,13 +114,13 @@ public function handleRequest(AphrontRequest $request) { ->appendParagraph( pht( 'Your browser timezone (%s) differs from your profile timezone '. - '(%s). You can ignore this conflict or adjust your profile setting '. - 'to match your client.', + '(%s). You can adjust your profile setting to match your browser, '. + 'or ignore this conflict to keep your current profile setting.', $this->formatOffset($client_offset), $this->formatOffset($server_offset))) ->appendForm($form) ->addCancelButton(pht('Cancel')) - ->addSubmitButton(pht('Change Timezone')); + ->addSubmitButton(pht('Confirm')); } private function formatOffset($offset) { diff --git a/src/applications/settings/editor/PhabricatorSettingsEditEngine.php b/src/applications/settings/editor/PhabricatorSettingsEditEngine.php index b45de2dce1..a5ae9b3348 100644 --- a/src/applications/settings/editor/PhabricatorSettingsEditEngine.php +++ b/src/applications/settings/editor/PhabricatorSettingsEditEngine.php @@ -53,7 +53,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorSettingsApplication'; + return PhabricatorSettingsApplication::class; } protected function newEditableObject() { diff --git a/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php b/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php index a927cc7475..d44766f136 100644 --- a/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php +++ b/src/applications/settings/editor/PhabricatorUserPreferencesEditor.php @@ -4,7 +4,7 @@ final class PhabricatorUserPreferencesEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorSettingsApplication'; + return PhabricatorSettingsApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php b/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php index 7056fd02de..60a2064546 100644 --- a/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php @@ -19,6 +19,18 @@ public function getPanelGroupKey() { return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY; } + /** + * Whether to display "Contact Numbers" panel in users' Personal + * Settings by checking if global SMS support is configured + */ + public function isUserPanel() { + $sms_auth_factor = new PhabricatorSMSAuthFactor(); + if ($sms_auth_factor->isSMSMailerConfigured()) { + return true; + } + return false; + } + public function isMultiFactorEnrollmentPanel() { return true; } diff --git a/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php index a888bb3175..665bb42b2f 100644 --- a/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php @@ -172,7 +172,7 @@ private function returnNewAddressResponse( $email = null; $errors = array(); if ($request->isDialogFormPost()) { - $email = trim($request->getStr('email')); + $email = trim($request->getStr('email', '')); if ($new == 'verify') { // The user clicked "Done" from the "an email has been sent" dialog. @@ -184,7 +184,7 @@ private function returnNewAddressResponse( new PhabricatorSettingsAddEmailAction(), 1); - if (!strlen($email)) { + if (!phutil_nonempty_string($email)) { $e_email = pht('Required'); $errors[] = pht('Email is required.'); } else if (!PhabricatorUserEmail::isValidAddress($email)) { diff --git a/src/applications/settings/panel/PhabricatorExternalEditorSettingsPanel.php b/src/applications/settings/panel/PhabricatorExternalEditorSettingsPanel.php index 3a49b6c3c3..2782851d93 100644 --- a/src/applications/settings/panel/PhabricatorExternalEditorSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorExternalEditorSettingsPanel.php @@ -39,7 +39,7 @@ public function newSettingsPanelEditFormHeadContent( $viewer = $this->getViewer(); $pattern = $viewer->getUserSetting(PhabricatorEditorSetting::SETTINGKEY); - if (!strlen($pattern)) { + if (!phutil_nonempty_string($pattern)) { return null; } diff --git a/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php b/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php index 0054610c28..492544bef6 100644 --- a/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php @@ -257,7 +257,7 @@ private function processNew(AphrontRequest $request) { // example, with SMS). if (!$request->isFormPost() || !$request->getBool('mfa.start')) { $enroll = $selected_provider->getEnrollMessage(); - if (!strlen($enroll)) { + if (!phutil_nonempty_string($enroll)) { $enroll = $selected_provider->getEnrollDescription($viewer); } diff --git a/src/applications/settings/phid/PhabricatorUserPreferencesPHIDType.php b/src/applications/settings/phid/PhabricatorUserPreferencesPHIDType.php index 7022c56c33..ad24ae5af6 100644 --- a/src/applications/settings/phid/PhabricatorUserPreferencesPHIDType.php +++ b/src/applications/settings/phid/PhabricatorUserPreferencesPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorSettingsApplication'; + return PhabricatorSettingsApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/settings/query/PhabricatorUserPreferencesQuery.php b/src/applications/settings/query/PhabricatorUserPreferencesQuery.php index 1a6133724d..5fa9b89bdd 100644 --- a/src/applications/settings/query/PhabricatorUserPreferencesQuery.php +++ b/src/applications/settings/query/PhabricatorUserPreferencesQuery.php @@ -190,7 +190,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorSettingsApplication'; + return PhabricatorSettingsApplication::class; } } diff --git a/src/applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php b/src/applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php index 7955830340..9b398e9d82 100644 --- a/src/applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php +++ b/src/applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorUserPreferencesTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorSettingsApplication::class; + } + } diff --git a/src/applications/settings/setting/PhabricatorMonospacedFontSetting.php b/src/applications/settings/setting/PhabricatorMonospacedFontSetting.php index 2a96f5d95f..5cfbec1b26 100644 --- a/src/applications/settings/setting/PhabricatorMonospacedFontSetting.php +++ b/src/applications/settings/setting/PhabricatorMonospacedFontSetting.php @@ -25,7 +25,7 @@ protected function getControlInstructions() { } public function validateTransactionValue($value) { - if (!strlen($value)) { + if (!phutil_nonempty_string($value)) { return; } diff --git a/src/applications/slowvote/application/PhabricatorSlowvoteApplication.php b/src/applications/slowvote/application/PhabricatorSlowvoteApplication.php index 1e4bd78419..8fa4edc6f6 100644 --- a/src/applications/slowvote/application/PhabricatorSlowvoteApplication.php +++ b/src/applications/slowvote/application/PhabricatorSlowvoteApplication.php @@ -45,6 +45,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('V'); + } + public function getRoutes() { return array( '/V(?P[1-9]\d*)' => 'PhabricatorSlowvotePollController', diff --git a/src/applications/slowvote/editor/PhabricatorSlowvoteEditor.php b/src/applications/slowvote/editor/PhabricatorSlowvoteEditor.php index cf088f37d4..14135bf88d 100644 --- a/src/applications/slowvote/editor/PhabricatorSlowvoteEditor.php +++ b/src/applications/slowvote/editor/PhabricatorSlowvoteEditor.php @@ -4,7 +4,7 @@ final class PhabricatorSlowvoteEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorSlowvoteApplication'; + return PhabricatorSlowvoteApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/slowvote/phid/PhabricatorSlowvotePollPHIDType.php b/src/applications/slowvote/phid/PhabricatorSlowvotePollPHIDType.php index 42ced85b22..f31ae71588 100644 --- a/src/applications/slowvote/phid/PhabricatorSlowvotePollPHIDType.php +++ b/src/applications/slowvote/phid/PhabricatorSlowvotePollPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorSlowvoteApplication'; + return PhabricatorSlowvoteApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php b/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php index d2ae9f1a35..bdc736022a 100644 --- a/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php +++ b/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php @@ -165,7 +165,7 @@ protected function getPrimaryTableAlias() { } public function getQueryApplicationClass() { - return 'PhabricatorSlowvoteApplication'; + return PhabricatorSlowvoteApplication::class; } } diff --git a/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php b/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php index 8b93f75faf..557f6b815c 100644 --- a/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php +++ b/src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorSlowvoteApplication'; + return PhabricatorSlowvoteApplication::class; } public function newQuery() { diff --git a/src/applications/slowvote/query/PhabricatorSlowvoteTransactionQuery.php b/src/applications/slowvote/query/PhabricatorSlowvoteTransactionQuery.php index d09096d50b..b1a8b8a2d5 100644 --- a/src/applications/slowvote/query/PhabricatorSlowvoteTransactionQuery.php +++ b/src/applications/slowvote/query/PhabricatorSlowvoteTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorSlowvoteTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorSlowvoteApplication::class; + } + } diff --git a/src/applications/spaces/application/PhabricatorSpacesApplication.php b/src/applications/spaces/application/PhabricatorSpacesApplication.php index d542551ae4..f40538b69b 100644 --- a/src/applications/spaces/application/PhabricatorSpacesApplication.php +++ b/src/applications/spaces/application/PhabricatorSpacesApplication.php @@ -49,6 +49,10 @@ public function getRemarkupRules() { ); } + public function getMonograms() { + return array('S'); + } + public function getRoutes() { return array( '/S(?P[1-9]\d*)' => 'PhabricatorSpacesViewController', diff --git a/src/applications/spaces/editor/PhabricatorSpacesNamespaceEditor.php b/src/applications/spaces/editor/PhabricatorSpacesNamespaceEditor.php index e4dc9c5b69..abfafa491e 100644 --- a/src/applications/spaces/editor/PhabricatorSpacesNamespaceEditor.php +++ b/src/applications/spaces/editor/PhabricatorSpacesNamespaceEditor.php @@ -4,7 +4,7 @@ final class PhabricatorSpacesNamespaceEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return pht('PhabricatorSpacesApplication'); + return PhabricatorSpacesApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/spaces/phid/PhabricatorSpacesNamespacePHIDType.php b/src/applications/spaces/phid/PhabricatorSpacesNamespacePHIDType.php index 1399e71c8e..a46040f98d 100644 --- a/src/applications/spaces/phid/PhabricatorSpacesNamespacePHIDType.php +++ b/src/applications/spaces/phid/PhabricatorSpacesNamespacePHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorSpacesApplication'; + return PhabricatorSpacesApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php b/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php index 388b6ab4d8..6703ed664e 100644 --- a/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php +++ b/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php @@ -33,7 +33,7 @@ public function withIsArchived($archived) { } public function getQueryApplicationClass() { - return 'PhabricatorSpacesApplication'; + return PhabricatorSpacesApplication::class; } public function newResultObject() { diff --git a/src/applications/spaces/query/PhabricatorSpacesNamespaceSearchEngine.php b/src/applications/spaces/query/PhabricatorSpacesNamespaceSearchEngine.php index 3880143a24..835a5d596f 100644 --- a/src/applications/spaces/query/PhabricatorSpacesNamespaceSearchEngine.php +++ b/src/applications/spaces/query/PhabricatorSpacesNamespaceSearchEngine.php @@ -4,7 +4,7 @@ final class PhabricatorSpacesNamespaceSearchEngine extends PhabricatorApplicationSearchEngine { public function getApplicationClassName() { - return 'PhabricatorSpacesApplication'; + return PhabricatorSpacesApplication::class; } public function getResultTypeDescription() { diff --git a/src/applications/spaces/query/PhabricatorSpacesNamespaceTransactionQuery.php b/src/applications/spaces/query/PhabricatorSpacesNamespaceTransactionQuery.php index 34d7e43570..c55fedd8d0 100644 --- a/src/applications/spaces/query/PhabricatorSpacesNamespaceTransactionQuery.php +++ b/src/applications/spaces/query/PhabricatorSpacesNamespaceTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorSpacesNamespaceTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorSpacesApplication::class; + } + } diff --git a/src/applications/spaces/typeahead/PhabricatorSpacesNamespaceDatasource.php b/src/applications/spaces/typeahead/PhabricatorSpacesNamespaceDatasource.php index 25951b53af..05008cd1fd 100644 --- a/src/applications/spaces/typeahead/PhabricatorSpacesNamespaceDatasource.php +++ b/src/applications/spaces/typeahead/PhabricatorSpacesNamespaceDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorSpacesApplication'; + return PhabricatorSpacesApplication::class; } public function loadResults() { diff --git a/src/applications/spaces/xaction/PhabricatorSpacesNamespaceNameTransaction.php b/src/applications/spaces/xaction/PhabricatorSpacesNamespaceNameTransaction.php index d7fcbc2c7a..d88425598f 100644 --- a/src/applications/spaces/xaction/PhabricatorSpacesNamespaceNameTransaction.php +++ b/src/applications/spaces/xaction/PhabricatorSpacesNamespaceNameTransaction.php @@ -15,7 +15,7 @@ public function applyInternalEffects($object, $value) { public function getTitle() { $old = $this->getOldValue(); - if (!strlen($old)) { + if (!phutil_nonempty_string($old)) { return pht( '%s created this space.', $this->renderAuthor()); diff --git a/src/applications/system/application/PhabricatorSystemApplication.php b/src/applications/system/application/PhabricatorSystemApplication.php index 6184c0de24..f3abc54d14 100644 --- a/src/applications/system/application/PhabricatorSystemApplication.php +++ b/src/applications/system/application/PhabricatorSystemApplication.php @@ -17,6 +17,7 @@ public function isUnlisted() { public function getEventListeners() { return array( new PhabricatorSystemDebugUIEventListener(), + new PhorgeSystemDeprecationWarningListener(), ); } diff --git a/src/applications/system/controller/PhabricatorSystemReadOnlyController.php b/src/applications/system/controller/PhabricatorSystemReadOnlyController.php index e83fe67b0e..2d00d1f405 100644 --- a/src/applications/system/controller/PhabricatorSystemReadOnlyController.php +++ b/src/applications/system/controller/PhabricatorSystemReadOnlyController.php @@ -16,7 +16,7 @@ public function handleRequest(AphrontRequest $request) { case PhabricatorEnv::READONLY_CONFIG: $title = pht('Administrative Read-Only Mode'); $body[] = pht( - 'An administrator has placed this server into read-only mode.'); + 'An Administrator has placed this server into read-only mode.'); $body[] = pht( 'This mode may be used to perform temporary maintenance, test '. 'configuration, or archive an installation permanently.'); @@ -26,7 +26,7 @@ public function handleRequest(AphrontRequest $request) { 'has been turned on by rolling your chair away from your desk and '. 'yelling "Hey! Why is %s in read-only mode??!" using '. 'your very loudest outside voice.', - PlatformSymbols::getPlatformServerSymbol()); + PlatformSymbols::getPlatformServerName()); $body[] = pht( 'This mode is active because it is enabled in the configuration '. 'option "%s".', @@ -110,7 +110,7 @@ public function handleRequest(AphrontRequest $request) { if ($viewer->getIsAdmin()) { $body[] = pht( - 'As an administrator, you can review status information from the '. + 'As an Administrator, you can review status information from the '. '%s control panel. This may provide more information about the '. 'current state of affairs.', phutil_tag( diff --git a/src/applications/system/controller/robots/PhabricatorRobotsPlatformController.php b/src/applications/system/controller/robots/PhabricatorRobotsPlatformController.php index b4a3c4fa37..7151f2e4aa 100644 --- a/src/applications/system/controller/robots/PhabricatorRobotsPlatformController.php +++ b/src/applications/system/controller/robots/PhabricatorRobotsPlatformController.php @@ -18,6 +18,15 @@ protected function newRobotsRules() { $out[] = 'User-Agent: *'; $out[] = 'Disallow: /diffusion/'; $out[] = 'Disallow: /source/'; + // See T15670. Also prevent directly accessing commits in Diffusion. + $out[] = 'Disallow: /r*'; + + // See T15662. Prevent indexing line anchor links in Pastes. Per RFC 9309 + // section 2.2.3, percentage-encode "$" to avoid interpretation as end of + // match pattern. However, crawlers may not abide by it but follow the + // original standard at https://www.robotstxt.org/orig.html with no mention + // how to interpret characters like "$" and thus entirely ignore this rule. + $out[] = 'Disallow: /P*%24*'; // Add a small crawl delay (number of seconds between requests) for spiders // which respect it. The intent here is to prevent spiders from affecting diff --git a/src/applications/system/events/PhorgeSystemDeprecationWarningListener.php b/src/applications/system/events/PhorgeSystemDeprecationWarningListener.php new file mode 100644 index 0000000000..147e84a584 --- /dev/null +++ b/src/applications/system/events/PhorgeSystemDeprecationWarningListener.php @@ -0,0 +1,58 @@ +getKey(self::CACHE_KEY); + + if (!$cache_entry) { + $cache_entry = array(); + } + + $trace_entry = idx($cache_entry, $trace_key); + + if ($trace_entry) { + $trace_entry['counter']++; + } else { + $trace_entry = array( + 'counter' => 1, + 'message' => $value, + 'trace' => PhutilErrorHandler::formatStacktrace($metadata['trace']), + ); + } + $cache_entry[$trace_key] = $trace_entry; + + $cache->setKey(self::CACHE_KEY , $cache_entry); + } + + public function getWarnings() { + $cache = PhabricatorCaches::getRuntimeCache(); + return $cache->getKey(self::CACHE_KEY); + } + +} diff --git a/src/applications/tokens/phid/PhabricatorTokenTokenPHIDType.php b/src/applications/tokens/phid/PhabricatorTokenTokenPHIDType.php index 93ff9500ac..0ff41cb4e5 100644 --- a/src/applications/tokens/phid/PhabricatorTokenTokenPHIDType.php +++ b/src/applications/tokens/phid/PhabricatorTokenTokenPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorTokensApplication'; + return PhabricatorTokensApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php index 0e6ad9eb54..63c23e12fb 100644 --- a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php +++ b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php @@ -3,10 +3,16 @@ final class PhabricatorTokenGivenQuery extends PhabricatorCursorPagedPolicyAwareQuery { + private $ids; private $authorPHIDs; private $objectPHIDs; private $tokenPHIDs; + public function withIDs(array $ids) { + $this->ids = $ids; + return $this; + } + public function withTokenPHIDs(array $token_phids) { $this->tokenPHIDs = $token_phids; return $this; @@ -29,6 +35,13 @@ public function newResultObject() { protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); + if ($this->ids !== null) { + $where[] = qsprintf( + $conn, + 'id IN (%Ld)', + $this->ids); + } + if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, @@ -107,7 +120,7 @@ protected function willFilterPage(array $results) { } public function getQueryApplicationClass() { - return 'PhabricatorTokensApplication'; + return PhabricatorTokensApplication::class; } } diff --git a/src/applications/tokens/query/PhabricatorTokenQuery.php b/src/applications/tokens/query/PhabricatorTokenQuery.php index 07e15d4b03..f20bbebe1e 100644 --- a/src/applications/tokens/query/PhabricatorTokenQuery.php +++ b/src/applications/tokens/query/PhabricatorTokenQuery.php @@ -71,7 +71,7 @@ private function getBuiltinTokens() { public function getQueryApplicationClass() { - return 'PhabricatorTokensApplication'; + return PhabricatorTokensApplication::class; } } diff --git a/src/applications/tokens/query/PhabricatorTokenReceiverQuery.php b/src/applications/tokens/query/PhabricatorTokenReceiverQuery.php index 2eb8158418..c1ba9f5998 100644 --- a/src/applications/tokens/query/PhabricatorTokenReceiverQuery.php +++ b/src/applications/tokens/query/PhabricatorTokenReceiverQuery.php @@ -35,7 +35,7 @@ public function getTokenCounts() { } public function getQueryApplicationClass() { - return 'PhabricatorTokensApplication'; + return PhabricatorTokensApplication::class; } } diff --git a/src/applications/transactions/bulk/PhabricatorBulkEngine.php b/src/applications/transactions/bulk/PhabricatorBulkEngine.php index 0091321245..e2d2c02a29 100644 --- a/src/applications/transactions/bulk/PhabricatorBulkEngine.php +++ b/src/applications/transactions/bulk/PhabricatorBulkEngine.php @@ -153,7 +153,7 @@ private function loadObjectList() { ->setViewer($viewer); $query_key = $request->getURIData('queryKey'); - if (strlen($query_key)) { + if (phutil_nonempty_string($query_key)) { if ($search_engine->isBuiltinQuery($query_key)) { $saved = $search_engine->buildSavedQueryFromBuiltin($query_key); } else { diff --git a/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php b/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php index 758e7f3439..811b4e1565 100644 --- a/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php +++ b/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php @@ -31,9 +31,9 @@ public function getDescriptionForConfirm(PhabricatorWorkerBulkJob $job) { $parts[] = pht('To silence this edit, run this command:'); $command = csprintf( - 'phabricator/ $ ./bin/bulk make-silent --id %R', + '%s $ ./bin/bulk make-silent --id %R', + PlatformSymbols::getPlatformServerPath(), $job->getID()); - $command = (string)$command; $parts[] = phutil_tag('tt', array(), $command); diff --git a/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php b/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php index 82eaf08e83..10709bb79f 100644 --- a/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php +++ b/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php @@ -21,7 +21,7 @@ protected function newDocumentationPages(PhabricatorUser $viewer) { record allows you to see who edited an object, when, and how their edit changed things. -One common reason to call this method is that you're implmenting a webhook and +One common reason to call this method is that you're implementing a webhook and just received a notification that an object has changed. See the Webhooks documentation for more detailed discussion of this use case. diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index 60ccdaa401..f70e172a99 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -941,7 +941,7 @@ final public function buildResponse() { } } else { $form_key = $request->getURIData('formKey'); - if (strlen($form_key)) { + if (phutil_nonempty_string($form_key)) { $config = $this->loadEditEngineConfigurationWithIdentifier($form_key); if (!$config) { @@ -971,14 +971,14 @@ final public function buildResponse() { } $page_key = $request->getURIData('pageKey'); - if (!strlen($page_key)) { + if (!phutil_nonempty_string($page_key)) { $pages = $this->getPages($object); if ($pages) { $page_key = head_key($pages); } } - if (strlen($page_key)) { + if (phutil_nonempty_string($page_key)) { $page = $this->selectPage($object, $page_key); if (!$page) { return new Aphront404Response(); @@ -1169,7 +1169,7 @@ private function buildEditResponse($object) { if ($this->getIsCreate()) { $template = $request->getStr('template'); - if (strlen($template)) { + if (phutil_nonempty_string($template)) { $template_object = $this->newObjectFromIdentifier( $template, array( diff --git a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php index 2028f10d90..26811d7534 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php @@ -95,7 +95,7 @@ private function buildEditTypesDocumentationPages( $section[] = $type->getConduitDescription(); $type_documentation = $type->getConduitDocumentation(); - if (strlen($type_documentation)) { + if (phutil_nonempty_string($type_documentation)) { $section[] = $type_documentation; } diff --git a/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php b/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php index d177595a2b..0d1b6cf425 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php @@ -43,11 +43,27 @@ public function getIcon() { return $this->icon; } + /** + * Set the text of the tag + * + * This is usually the 'name' key of your subtype map. + * Sometime this is an uppercase text like 'BUG' for a 'bug' subtype name. + * + * @param string|null $text + * @return self + */ public function setTagText($text) { $this->tagText = $text; return $this; } + /** + * Get the text of the tag + * + * @see PhabricatorEditEngineSubtype::setTagText() + * + * @return string|null + */ public function getTagText() { return $this->tagText; } @@ -89,7 +105,7 @@ public function getMutations() { } public function hasTagView() { - return (bool)strlen($this->getTagText()); + return phutil_nonempty_string($this->getTagText()); } public function newTagView() { diff --git a/src/applications/transactions/editfield/PhabricatorEditField.php b/src/applications/transactions/editfield/PhabricatorEditField.php index 7eafbc60cf..4c72d63214 100644 --- a/src/applications/transactions/editfield/PhabricatorEditField.php +++ b/src/applications/transactions/editfield/PhabricatorEditField.php @@ -169,11 +169,20 @@ public function getConduitDescription() { return $this->conduitDescription; } + /** + * Set the Conduit documentation in raw Remarkup. + * @param string|null $conduit_documentation + * @return self + */ public function setConduitDocumentation($conduit_documentation) { $this->conduitDocumentation = $conduit_documentation; return $this; } + /** + * Get the Conduit documentation in raw Remarkup. + * @return string|null + */ public function getConduitDocumentation() { return $this->conduitDocumentation; } @@ -418,7 +427,7 @@ public function appendToForm(AphrontFormView $form) { } $instructions = $this->getControlInstructions(); - if (strlen($instructions)) { + if (phutil_nonempty_string($instructions)) { $form->appendRemarkupInstructions($instructions); } diff --git a/src/applications/transactions/editfield/PhabricatorTextEditField.php b/src/applications/transactions/editfield/PhabricatorTextEditField.php index 68854cf2e2..d915d4db75 100644 --- a/src/applications/transactions/editfield/PhabricatorTextEditField.php +++ b/src/applications/transactions/editfield/PhabricatorTextEditField.php @@ -18,7 +18,7 @@ protected function newControl() { $control = new AphrontFormTextControl(); $placeholder = $this->getPlaceholder(); - if (strlen($placeholder)) { + if (phutil_nonempty_string($placeholder)) { $control->setPlaceholder($placeholder); } diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index 77b5fbfbb6..b3db97af49 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -391,7 +391,7 @@ private function adjustTransactionValues( $new = $this->getTransactionNewValue($object, $xaction); $xaction->setNewValue($new); - // Apply an optional transformation to convert "external" tranaction + // Apply an optional transformation to convert "external" transaction // values (provided by APIs) into "internal" values. $old = $xaction->getOldValue(); @@ -617,7 +617,7 @@ private function getTransactionNewValue( return true; case PhabricatorTransactions::TYPE_SPACE: $space_phid = $xaction->getNewValue(); - if (!strlen($space_phid)) { + if (!phutil_nonempty_string($space_phid)) { // If an install has no Spaces or the Spaces controls are not visible // to the viewer, we might end up with the empty string here instead // of a strict `null`, because some controller just used `getStr()` @@ -2320,6 +2320,7 @@ private function newFileTransaction( } $xaction = $object->getApplicationTransactionTemplate() + ->setIgnoreOnNoEffect(true) ->setTransactionType(PhabricatorTransactions::TYPE_FILE) ->setMetadataValue('attach.implicit', true) ->setNewValue($new_map); diff --git a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php index 5a30fbfdde..b95c31cc56 100644 --- a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php +++ b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php @@ -41,7 +41,7 @@ public function getSummaryText() { } public function getEngineApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } protected function newEditableObject() { diff --git a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php index 34b7653001..5229ee6bec 100644 --- a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php +++ b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php @@ -4,7 +4,7 @@ final class PhabricatorEditEngineConfigurationEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/applications/transactions/phid/PhabricatorApplicationTransactionTransactionPHIDType.php b/src/applications/transactions/phid/PhabricatorApplicationTransactionTransactionPHIDType.php index e6184b1ae4..796f68e2cc 100644 --- a/src/applications/transactions/phid/PhabricatorApplicationTransactionTransactionPHIDType.php +++ b/src/applications/transactions/phid/PhabricatorApplicationTransactionTransactionPHIDType.php @@ -17,7 +17,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php b/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php index b4ac0ddbfe..d10446e6eb 100644 --- a/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php +++ b/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php @@ -14,7 +14,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } protected function buildQueryForObjects( diff --git a/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php b/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php index 195de601f0..64e94947ac 100644 --- a/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php +++ b/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php @@ -292,8 +292,14 @@ protected function shouldGroupQueryResultRows() { return parent::shouldGroupQueryResultRows(); } + /** + * Get name of class of application queried by the query. + * + * To be overwritten by child classes when applicable. + * + * @return string|null Application class name + */ public function getQueryApplicationClass() { - // TODO: Sort this out? return null; } diff --git a/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php b/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php index d2cbc3e697..d29fd10873 100644 --- a/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php +++ b/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php @@ -278,7 +278,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } } diff --git a/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php b/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php index 694b4c48b6..c74ba38713 100644 --- a/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php +++ b/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php @@ -23,7 +23,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } public function newQuery() { diff --git a/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php b/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php index 2a4677944e..429ed8db5e 100644 --- a/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php +++ b/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorEditEngineConfigurationTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorTransactionsApplication::class; + } + } diff --git a/src/applications/transactions/query/PhabricatorEditEngineQuery.php b/src/applications/transactions/query/PhabricatorEditEngineQuery.php index 400b62a487..48295ab31c 100644 --- a/src/applications/transactions/query/PhabricatorEditEngineQuery.php +++ b/src/applications/transactions/query/PhabricatorEditEngineQuery.php @@ -43,7 +43,7 @@ protected function willFilterPage(array $engines) { } public function getQueryApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } } diff --git a/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php b/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php index 4ba1a0e879..822548f860 100644 --- a/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php +++ b/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } public function newQuery() { diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index e32fe92983..c03cefeed0 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -539,7 +539,7 @@ public function getColor() { case PhabricatorTransactions::TYPE_COMMENT; $comment = $this->getComment(); if ($comment && $comment->getIsRemoved()) { - return 'black'; + return 'grey'; } break; case PhabricatorTransactions::TYPE_EDGE: @@ -1033,12 +1033,22 @@ public function getTitle() { count($rem), $this->renderSubscriberList($rem, 'rem')); } else if ($add) { + if ($this->isSelfSubscription()) { + return pht( + '%s subscribed.', + $this->renderHandleLink($author_phid)); + } return pht( '%s added %d subscriber(s): %s.', $this->renderHandleLink($author_phid), count($add), $this->renderSubscriberList($add, 'add')); } else if ($rem) { + if ($this->isSelfSubscription()) { + return pht( + '%s unsubscribed.', + $this->renderHandleLink($author_phid)); + } return pht( '%s removed %d subscriber(s): %s.', $this->renderHandleLink($author_phid), diff --git a/src/applications/transactions/storage/PhabricatorModularTransactionType.php b/src/applications/transactions/storage/PhabricatorModularTransactionType.php index 7d5e3c533e..e2f0a02239 100644 --- a/src/applications/transactions/storage/PhabricatorModularTransactionType.php +++ b/src/applications/transactions/storage/PhabricatorModularTransactionType.php @@ -334,12 +334,28 @@ final protected function isNewObject() { return $this->getEditor()->getIsNewObject(); } + /** + * Check whenever a new transaction's value is considered an "empty text" + * @param mixed $value A string, null, an integer... + * @param array $xactions Transactions + */ final protected function isEmptyTextTransaction($value, array $xactions) { foreach ($xactions as $xaction) { $value = $xaction->getNewValue(); } - return !strlen($value); + // The $value can be a lot of stuff, null, string, integer and maybe more. + // We cast to string to answer the question "Is this string empty?". + // Note: Don't use phutil_nonempty_stringlike() since it was not designed + // for integers. + // Note: Don't use phutil_nonempty_scalar() since very probably we could + // receive a boolean, causing exceptions. + // https://we.phorge.it/T15239 + $value_clean = phutil_string_cast($value); + + // We made our lives easier and we don't need strlen(something) + // and we should not. + return $value_clean === ''; } /** diff --git a/src/applications/transactions/typeahead/PhabricatorEditEngineDatasource.php b/src/applications/transactions/typeahead/PhabricatorEditEngineDatasource.php index 6520ca6732..74771ee834 100644 --- a/src/applications/transactions/typeahead/PhabricatorEditEngineDatasource.php +++ b/src/applications/transactions/typeahead/PhabricatorEditEngineDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } protected function renderSpecialTokens(array $values) { diff --git a/src/applications/transactions/typeahead/PhabricatorTransactionsObjectTypeDatasource.php b/src/applications/transactions/typeahead/PhabricatorTransactionsObjectTypeDatasource.php index e7292fc522..d515b8303a 100644 --- a/src/applications/transactions/typeahead/PhabricatorTransactionsObjectTypeDatasource.php +++ b/src/applications/transactions/typeahead/PhabricatorTransactionsObjectTypeDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorTransactionsApplication'; + return PhabricatorTransactionsApplication::class; } protected function renderSpecialTokens(array $values) { diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php index ee2020f890..180ba071a9 100644 --- a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php +++ b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php @@ -493,6 +493,11 @@ private function renderEvent( $xaction->getComment() && $xaction->getComment()->getIsRemoved(); + // Make designers happy to make CSS customizations + if ($has_removed_comment) { + $event->addClass('phui-timeline-shell-removed'); + } + if ($xaction->getCommentVersion() > 1 && !$has_removed_comment) { $event->setIsEdited(true); } diff --git a/src/applications/transactions/xaction/PhabricatorEditEngineDefaultTransaction.php b/src/applications/transactions/xaction/PhabricatorEditEngineDefaultTransaction.php index 7b980cdb1a..38011058b6 100644 --- a/src/applications/transactions/xaction/PhabricatorEditEngineDefaultTransaction.php +++ b/src/applications/transactions/xaction/PhabricatorEditEngineDefaultTransaction.php @@ -65,7 +65,7 @@ private function renderDefaultValueAsFallbackText($default_value) { return id(new PhutilJSON())->encodeAsList($default_value); } - return id(new PhutilJSON())->encodeAsObject($default_value); + return id(new PhutilJSON())->encodeFormatted($default_value); } } diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php index 2d55c5f663..5753c3cded 100644 --- a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php +++ b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php @@ -39,7 +39,7 @@ public function handleRequest(AphrontRequest $request) { $parameters = array(); $raw_parameters = $request->getStr('parameters'); - if (strlen($raw_parameters)) { + if (phutil_nonempty_string($raw_parameters)) { try { $parameters = phutil_json_decode($raw_parameters); } catch (PhutilJSONParserException $ex) { diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php index 5594044c42..8a396c1ed3 100644 --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php @@ -28,7 +28,7 @@ public function loadResults() { // We only need to do a prefix phase query if there's an actual query // string. If the user didn't type anything, nothing can possibly match it. - if (strlen($this->getRawQuery())) { + if (phutil_nonempty_string($this->getRawQuery())) { $phases[] = self::PHASE_PREFIX; } @@ -207,7 +207,11 @@ private function getResultPhase(PhabricatorTypeaheadResult $result) { } protected function sliceResults(array $results) { - $offset = $this->getOffset(); + if ($this->getOffset()) { + $offset = $this->getOffset(); + } else { + $offset = 0; + } $limit = $this->getLimit(); if ($offset || $limit) { diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php index 29a8d4b7ba..a35a8e8f0f 100644 --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php @@ -464,7 +464,11 @@ public static function isFunctionToken($token) { // We're looking for a "(" so that a string like "members(q" is identified // and parsed as a function call. This allows us to start generating // results immediately, before the user fully types out "members(quack)". - return (strpos($token, '(') !== false); + if ($token) { + return (strpos($token, '(') !== false); + } else { + return false; + } } diff --git a/src/applications/typeahead/datasource/__tests__/PhabricatorTypeaheadTestNumbersDatasource.php b/src/applications/typeahead/datasource/__tests__/PhabricatorTypeaheadTestNumbersDatasource.php index f16a64bb9b..ae5c87b45c 100644 --- a/src/applications/typeahead/datasource/__tests__/PhabricatorTypeaheadTestNumbersDatasource.php +++ b/src/applications/typeahead/datasource/__tests__/PhabricatorTypeaheadTestNumbersDatasource.php @@ -12,7 +12,7 @@ public function getPlaceholderText() { } public function getDatasourceApplicationClass() { - return 'PhabricatorPeopleApplication'; + return PhabricatorPeopleApplication::class; } public function getDatasourceFunctions() { diff --git a/src/applications/uiexample/examples/PHUIButtonExample.php b/src/applications/uiexample/examples/PHUIButtonExample.php index 89f8e15fe8..7d00290505 100644 --- a/src/applications/uiexample/examples/PHUIButtonExample.php +++ b/src/applications/uiexample/examples/PHUIButtonExample.php @@ -115,6 +115,8 @@ public function renderExample() { $button->setMetadata( array( 'text' => $copy, + 'successMessage' => pht('Text copied into clipboard.'), + 'errorMessage' => pht('Failed to copy text into clipboard.'), )); } diff --git a/src/applications/uiexample/examples/PhabricatorSetupIssueUIExample.php b/src/applications/uiexample/examples/PhabricatorSetupIssueUIExample.php index d6386e59d4..4343bcb8cb 100644 --- a/src/applications/uiexample/examples/PhabricatorSetupIssueUIExample.php +++ b/src/applications/uiexample/examples/PhabricatorSetupIssueUIExample.php @@ -24,8 +24,12 @@ public function renderExample() { ->setSummary(pht('Summary')) ->setMessage(pht('Message')) ->setIssueKey('example.key') - ->addCommand('$ # Add Command') - ->addCommand(hsprintf('$ %s', '$ ls -1 > /dev/null')) + ->addCommand(hsprintf( + '%s $# Add Command', + PlatformSymbols::getPlatformServerPath())) + ->addCommand(hsprintf( + '%s $ls -1 > /dev/null', + PlatformSymbols::getPlatformServerPath())) ->addPHPConfig('php.config.example') ->addPhabricatorConfig('test.value') ->addPHPExtension('libexample'); diff --git a/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php b/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php index 2001fec0c7..2160507a53 100644 --- a/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php +++ b/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php @@ -41,7 +41,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorXHProfApplication'; + return PhabricatorXHProfApplication::class; } } diff --git a/src/applications/xhprof/query/PhabricatorXHProfSampleSearchEngine.php b/src/applications/xhprof/query/PhabricatorXHProfSampleSearchEngine.php index 171f28f027..f0a5273f3e 100644 --- a/src/applications/xhprof/query/PhabricatorXHProfSampleSearchEngine.php +++ b/src/applications/xhprof/query/PhabricatorXHProfSampleSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorXHProfApplication'; + return PhabricatorXHProfApplication::class; } public function newQuery() { diff --git a/src/docs/book/contributor.book b/src/docs/book/contributor.book index 10db63c011..1d0386251f 100644 --- a/src/docs/book/contributor.book +++ b/src/docs/book/contributor.book @@ -1,11 +1,11 @@ { - "name": "phabcontrib", - "title": "Phabricator Contributor Documentation", - "short": "Phabricator Contributor Docs", - "preface": "Information for Phabricator contributors and developers.", + "name": "contrib", + "title": "Phorge Contributor Documentation", + "short": "Contributor Docs", + "preface": "Information for Phorge contributors and developers.", "root": "../../../", "uri.source": - "https://secure.phabricator.com/diffusion/P/browse/master/%f$%l", + "https://we.phorge.it/diffusion/P/browse/master/%f$%l", "rules": { "(\\.diviner$)": "DivinerArticleAtomizer" }, diff --git a/src/docs/book/flavor.book b/src/docs/book/flavor.book index 978244f19d..c9a80fae92 100644 --- a/src/docs/book/flavor.book +++ b/src/docs/book/flavor.book @@ -1,11 +1,11 @@ { - "name": "phabflavor", - "title": "Phabricator Flavor Text", + "name": "flavor", + "title": "Phorge Flavor Text", "short": "Flavor Text", - "preface": "Recommendations, lore, and dark rituals.", + "preface": "A collection of short articles which pertain to software development in general, not necessarily to Phorge specifically.", "root": "../../../", "uri.source": - "https://secure.phabricator.com/diffusion/P/browse/master/%f$%l", + "https://we.phorge.it/diffusion/P/browse/master/%f$%l", "rules": { "(\\.diviner$)": "DivinerArticleAtomizer" }, @@ -20,14 +20,11 @@ "(^webroot/rsrc/externals/)" ], "groups": { - "overview": { - "name": "Overview" - }, "javascript": { - "name": "Javascript" + "name": "JavaScript" }, "lore": { - "name": "Phabricator Lore" + "name": "Phorge Lore" }, "php": { "name": "PHP" diff --git a/src/docs/book/phabricator.book b/src/docs/book/phorge.book similarity index 95% rename from src/docs/book/phabricator.book rename to src/docs/book/phorge.book index d109a9b70e..b421144f2e 100644 --- a/src/docs/book/phabricator.book +++ b/src/docs/book/phorge.book @@ -1,11 +1,11 @@ { - "name": "phabdev", - "title": "Phabricator Technical Documentation", - "short": "Phabricator Tech Docs", - "preface": "Technical reference material for Phabricator developers.", + "name": "dev", + "title": "Phorge Technical Documentation", + "short": "Tech Docs", + "preface": "Technical reference material for Phorge developers.", "root": "../../../", "uri.source": - "https://secure.phabricator.com/diffusion/P/browse/master/%f$%l", + "https://we.phorge.it/diffusion/P/browse/master/%f$%l", "rules": { "(\\.diviner$)": "DivinerArticleAtomizer", "(\\.php$)": "DivinerPHPAtomizer" @@ -57,10 +57,6 @@ "name": "Celerity", "include": "(^src/applications/celerity/)" }, - "chatlog": { - "name": "Chatlog", - "include": "(^src/applications/chatlog/)" - }, "conduit": { "name": "Conduit", "include": "(^src/applications/conduit/)" diff --git a/src/docs/book/user.book b/src/docs/book/user.book index fb2dccc578..b5b6dac7b1 100644 --- a/src/docs/book/user.book +++ b/src/docs/book/user.book @@ -1,11 +1,11 @@ { - "name": "phabricator", - "title": "Phabricator User Documentation", - "short": "Phabricator User Docs", - "preface": "Instructions for installing, configuring, and using Phabricator.", + "name": "phorge", + "title": "Phorge Administrator and User Documentation", + "short": "User Docs", + "preface": "Instructions for installing, configuring, and using Phorge.", "root": "../../../", "uri.source": - "https://secure.phabricator.com/diffusion/P/browse/master/%f$%l", + "https://we.phorge.it/diffusion/P/browse/master/%f$%l", "rules": { "(\\.diviner$)": "DivinerArticleAtomizer" }, @@ -37,9 +37,6 @@ }, "fieldmanual": { "name": "Field Manuals" - }, - "cellar": { - "name": "Musty Cellar" } } } diff --git a/src/docs/contributor/adding_new_classes.diviner b/src/docs/contributor/adding_new_classes.diviner index ea932eba5c..83a19c10fb 100644 --- a/src/docs/contributor/adding_new_classes.diviner +++ b/src/docs/contributor/adding_new_classes.diviner @@ -1,22 +1,22 @@ @title Adding New Classes @group developer -Guide to adding new classes to extend Phabricator. +Guide to adding new classes to extend Phorge. Overview ======== -Phabricator is highly modular, and many parts of it can be extended by adding +Phorge is highly modular, and many parts of it can be extended by adding new classes. This document explains how to write new classes to change or -expand the behavior of Phabricator. +expand the behavior of Phorge. -IMPORTANT: The upstream does not offer support with extension development. +NOTE: The upstream offers limited support with extension development. Fundamentals ============ -Phabricator primarily discovers functionality by looking at concrete subclasses -of some base class. For example, Phabricator determines which applications are +Phorge primarily discovers functionality by looking at concrete subclasses +of some base class. For example, Phorge determines which applications are available by looking at all of the subclasses of @{class@phabricator:PhabricatorApplication}. It discovers available workflows in `arc` by looking at all of the subclasses of @@ -24,7 +24,7 @@ discovers available workflows in `arc` by looking at all of the subclasses of by looking at all of the subclasses of @{class@arcanist:PhutilLocale}. This pattern holds in many cases, so you can often add functionality by adding -new classes with no other work. Phabricator will automatically discover and +new classes with no other work. Phorge will automatically discover and integrate the new capabilities or features at runtime. There are two main ways to add classes: @@ -42,20 +42,20 @@ The next sections walk through these approaches in greater detail. Extensions Directory ==================== -The easiest way to extend Phabricator by adding new classes is to drop them -into the extensions directory, at `phabricator/src/extensions/`. +The easiest way to extend Phorge by adding new classes is to drop them +into the extensions directory, at `phorge/src/extensions/`. This is intended as a quick way to add small pieces of functionality, test new -features, or get started on a larger project. Extending Phabricator like this +features, or get started on a larger project. Extending Phorge like this imposes a small performance penalty compared to using a library. -This directory exists in all libphutil libraries, so you can find a similar +This directory also exists for Arcanist, so you can find a similar directory in `arcanist/src/extensions/`. For example, to add a new application, create a file like this one and add it -to `phabricator/src/extensions/`. +to `phorge/src/extensions/`. -```name=phabricator/src/extensions/ExampleApplication.php, lang=php +```name=phorge/src/extensions/ExampleApplication.php, lang=php 4. You represent that you are legally entitled to grant the above license. If -> your employer(s) has rights to intellectual property that you create that -> includes your Contributions, you represent that you have received permission -> to make Contributions on behalf of that employer, that your employer has -> waived such rights for your Contributions to Phacility, or that your employer -> has executed a separate Corporate CLA with Phacility. - -Ownership of your work varies based on where you live, how you are employed, -and your agreements with your employer. However, at least in the US, it is -likely that your employer owns your work unless you have anticipated conflicts -and specifically avoided them. This generally makes sense: if you are paid by -your employer for your work, they own the product of your work and you receive -salary and benefits in fair exchange for that work. - -Your employer may have an ownership claim on your work even if you perform it -on your own time, if you use their equipment (like a company laptop or phone), -resources, facilities, or trade secrets, or signed something like an "Invention -Assignment Agreement" when you were hired. Such agreements are common. The -details of the strength of their claim will vary based on your situation and -local law. - -If you are unsure, you should speak with your employer or a lawyer. If you -contribute code you do not own under the individual CLA, you are exposing -yourself to liability. You may also be exposing us to liability, but we'll have -the CLA on our side to show that we were unwilling pawns in your malicious -scheme to defraud your employer. - -The good news is that most employers are happy to contribute to open source -projects. Incentives are generally well aligned: they get features they want, -and it reflects well on them. In the past, potential contributors who have -approached their employers about a corporate CLA have generally had little -difficulty getting approval. diff --git a/src/docs/contributor/contrib_intro.diviner b/src/docs/contributor/contrib_intro.diviner index 59ad9b44df..00a2e42c0e 100644 --- a/src/docs/contributor/contrib_intro.diviner +++ b/src/docs/contributor/contrib_intro.diviner @@ -1,12 +1,12 @@ @title Contributor Introduction @group contrib -Introduction to contributing to Phabricator and Arcanist. +Introduction to contributing to Phorge and Arcanist. Overview ======== -If you'd like to contribute to Phabricator, this document can guide you though +If you'd like to contribute to Phorge, this document can guide you though ways you can help improve the project. Writing code is valuable, but often isn't the best or easiest way to contribute. @@ -19,24 +19,11 @@ heavy lifting. Without writing any code, learning the whole codebase, making a big time commitment, or having to touch PHP, here are some ways you can materially -contribute to Phabricator: +contribute to Phorge: - - Drop by the [[ https://phurl.io/u/discourse | community forum ]] just to - say "thanks". A big part of the reason we build this software is to help - people solve problems, and knowing that our efforts are appreciated is - really rewarding. - - Recommend Phabricator to people who you think might find it useful. Our + - Recommend Phorge to people who you think might find it useful. Our most powerful growth channel is word of mouth, and mentioning or tweeting - about Phabricator helps the project grow. If writing a tweet sounds like - too much work, you can use one of these form tweets written by our PR - department to quickly and easily shill on our behalf. Hail corporate! - -> Phabricator seems like it's pretty okay - -> I am not being paid to mention Phabricator in this extemporaneous, completely organic tweet - -> Phabricator is objectively the best thing. Source: I am a certified, internationally recognized expert. - + about Phorge helps the project grow. - Submit high-quality bug reports by carefully following the guide in @{article:Contributing Bug Reports}. diff --git a/src/docs/contributor/contributing_code.diviner b/src/docs/contributor/contributing_code.diviner index b6816e03b5..adc56aca1a 100644 --- a/src/docs/contributor/contributing_code.diviner +++ b/src/docs/contributor/contributing_code.diviner @@ -1,4 +1,199 @@ @title Contributing Code @group detail -Effective June 1, 2021: Phabricator is no longer actively maintained, and no longer accepting contributions. +Phorge is an open-source project, and welcomes contributions from the community +at large. However, there are some guidelines we ask you to follow. + + +Overview +======== + +The most important parts of contributing code to Phorge are: + + - File a task with a bug report or feature request //before// you write code. + - We do not accept GitHub pull requests. + - Some alternative approaches are available if your change isn't something + we want to bring upstream. + +The rest of this article describes these points in more detail, and then +provides guidance on writing and submitting patches. + +If you just want to contribute some code but don't have a specific bug or +feature in mind, see the bottom of this document for tips on finding ways to get +started. + +For general information on contributing to Phorge, see +@{article:Contributor Introduction}. + + +Coordinate First +================ + +Before sending code, you should file a task describing what you'd like to write. + +When you file a task, mention that you'd like to write the code to fix it. We +can help contextualize your request or bug and guide you through writing an +upstreamable patch, provided it's something that's upstreamable. If it isn't +upstreamable, we can let you know what the issues are and help find another +plan of attack. + +You don't have to file first (for example, if you spot a misspelling it's +normally fine to just send a diff), but for anything even moderately complex +you're strongly encouraged to file first and coordinate with the upstream. + + +Rejecting Patches +================= + +If you send us a patch without coordinating it with us first, it will probably +be immediately rejected, or sit in limbo for a long time and eventually be +rejected. The reasons we do this vary from patch to patch, but some of the most +common reasons are: + +**Unjustifiable Costs**: We support code in the upstream forever. Support is +enormously expensive and takes up a huge amount of our time. The cost to support +a change over its lifetime is often 10x or 100x or 1000x greater than the cost +to write the first version of it. Many uncoordinated patches we receive are +"white elephants", which would cost much more to maintain than the value they +provide. + +As an author, it may look like you're giving us free work and we're rejecting it +as too expensive, but this viewpoint doesn't align with the reality of a large +project which is actively supported by a small, experienced team. Writing code +is cheap; maintaining it is expensive. + +By coordinating with us first, you can make sure the patch is something we +consider valuable enough to put long-term support resources behind, and that +you're building it in a way that we're comfortable taking over. + +**Not a Good Fit**: Many patches aren't good fits for the upstream: they +implement features we simply don't want. Coordinating with us first helps +make sure we're on the same page and interested in a feature. + +The most common type of patch along these lines is a patch which adds new +configuration options. We consider additional configuration options to have +an exceptionally high lifetime support cost and are very unlikely to accept +them. Coordinate with us first. + +**Not a Priority**: If you send us a patch against something which isn't a +priority, we probably won't have time to look at it. We don't give special +treatment to low-priority issues just because there's code written: we'd still +be spending time on something lower-priority when we could be spending it on +something higher-priority instead. + +If you coordinate with us first, you can make sure your patch is in an area +of the codebase that we can prioritize. + +**Overly Ambitious Patches**: Sometimes we'll get huge patches from new +contributors. These can have a lot of fundamental problems and require a huge +amount of our time to review and correct. If you're interested in contributing, +you'll have more success if you start small and learn as you go. + +We can help you break a large change into smaller pieces and learn how the +codebase works as you proceed through the implementation, but only if you +coordinate with us first. + +**Generality**: We often receive several feature requests which ask for similar +features, and can come up with a general approach which covers all of the use +cases. If you send us a patch for //your use case only//, the approach may be +too specific. When a cleaner and more general approach is available, we usually +prefer to pursue it. + +By coordinating with us first, we can make you aware of similar use cases and +opportunities to generalize an approach. These changes are often small, but can +have a big impact on how useful a piece of code is. + +**Infrastructure and Sequencing**: Sometimes patches are written against a piece +of infrastructure with major planned changes. We don't want to accept these +because they'll make the infrastructure changes more difficult to implement. + +Coordinate with us first to make sure a change doesn't need to wait on other +pieces of infrastructure. We can help you identify technical blockers and +possibly guide you through resolving them if you're interested. + + +No Prototype Changes +==================== + +With rare exceptions, we do not accept patches for prototype applications for +the same reasons that we don't accept feature requests or bug reports. To learn +more about prototype applications, see +@{article:User Guide: Prototype Applications}. + + +No Pull Requests +================ + +We do not accept pull requests on GitHub: + + - Pull requests do not get lint and unit tests run, so issues which are + normally caught statically can slip by. + - Phorge is code review software, and developed using its own workflows. + Pull requests bypass some of these workflows (for example, they will not + trigger Herald rules to notify interested parties). + - GitHub is not the authoritative master repository and we maintain a linear + history, so merging pull requests is cumbersome on our end. + - If you're comfortable enough with Phorge to contribute to it, you + should also be comfortable using it to submit changes. + +Instead of sending a pull request, use `arc diff` to create a revision on the +upstream install. Your change will go through the normal Phorge review +process. + +(GitHub does not allow repositories to disable pull requests, which is why +it's technically possible to submit them.) + + +Alternatives +============ + +If you've written code but we're not accepting it into the upstream, some +alternative approaches include: + +**Maintain a local fork.** This will require some ongoing effort to port your +changes forward when you update, but is often very reasonable for simple +changes. + +**Develop as an application.** Many parts of Phorge's infrastructure are +modular, and modularity is increasing over time. A lot of changes can be built +as external modules or applications without forking Phorge itself. There +isn't much documentation for this right now, but you can look at +how other applications are implemented, and at other third-party code that +extends Phorge. + +**Rise to prominence.** We're more willing to accept borderline changes from +community members who are active, make multiple contributions, or have a history +with the project. This is not carte blanche, but distinguishing yourself can +make us feel more comfortable about supporting a change which is slightly +outside of our comfort zone. + + +Writing and Submitting Patches +================== + +To actually submit a patch, run `arc diff` in `phorge/` or `arcanist/`. +When executed in these directories, `arc` should automatically talk to the +upstream install. You can add #blessed_reviewers as a reviewer. + +You should read the relevant coding convention documents before you submit a +change. If you're a new contributor, you don't need to worry about this too +much. Just try to make your code look similar to the code around it, and we +can help you through the details during review. + + - @{article:General Coding Standards} (for all languages) + - @{article:PHP Coding Standards} (for PHP) + - @{article:Javascript Coding Standards} (for Javascript) + +In general, if you're coordinating with us first, we can usually provide +guidance on how to implement things. The other articles in this section also +provide information on how to work in the Phorge codebase. + + +Next Steps +========== + +Continue by: + + - preparing your development environment as described in the + @{article:Developer Setup} + - returning to the @{article:Contributor Introduction} diff --git a/src/docs/contributor/css_coding_standards.diviner b/src/docs/contributor/css_coding_standards.diviner index c321124eae..e83778f24c 100644 --- a/src/docs/contributor/css_coding_standards.diviner +++ b/src/docs/contributor/css_coding_standards.diviner @@ -1,14 +1,14 @@ @title CSS Coding Standards @group standards -This document describes CSS features and coding standards for Phabricator. +This document describes CSS features and coding standards for Phorge. = Overview = This document describes technical and style guidelines for writing CSS in -Phabricator. +Phorge. -Phabricator has a limited CSS preprocessor. This document describes the features +Phorge has a limited CSS preprocessor. This document describes the features it makes available. = Z-Indexes = @@ -19,7 +19,7 @@ Great Z-Index War where all indexes grow without bound in an endless arms race. = Color Variables = -Phabricator's preprocessor provides some standard color variables. You can +Phorge's preprocessor provides some standard color variables. You can reference these with `{$color}`. For example: lang=css @@ -60,7 +60,7 @@ by the media query. = Device Rules = -Phabricator's environment defines several device classes which can be used to +Phorge's environment defines several device classes which can be used to adjust behavior responsively. In particular: lang=css @@ -86,6 +86,6 @@ either tablets or phones: = Image Inlining = -Phabricator's CSS preprocessor automatically inlines images which are less than +Phorge's CSS preprocessor automatically inlines images which are less than 32KB using `data:` URIs. This is primarily useful for gradients or textures which are small and difficult to sprite. diff --git a/src/docs/contributor/database.diviner b/src/docs/contributor/database.diviner index aaea485dc6..6572e42fc1 100644 --- a/src/docs/contributor/database.diviner +++ b/src/docs/contributor/database.diviner @@ -7,10 +7,10 @@ questions like how to store new types of data. Database System =============== -Phabricator uses MySQL or another MySQL-compatible database (like MariaDB +Phorge uses MySQL or another MySQL-compatible database (like MariaDB or Amazon RDS). -Phabricator uses the InnoDB table engine. The only exception is the +Phorge uses the InnoDB table engine. The only exception is the `search_documentfield` table which uses MyISAM because MySQL doesn't support fulltext search in InnoDB (recent versions do, but we haven't added support yet). @@ -21,22 +21,22 @@ SQLite. PHP Drivers =========== -Phabricator supports [[ http://www.php.net/book.mysql | MySQL ]] and +Phorge supports [[ http://www.php.net/book.mysql | MySQL ]] and [[ http://www.php.net/book.mysqli | MySQLi ]] PHP extensions. Databases ========= -Each Phabricator application has its own database. The names are prefixed by -`phabricator_` (this is configurable). +Each Phorge application has its own database. The names are prefixed by +`phabricator_` (this is configurable with `storage.default-namespace`). -Phabricator uses a separate database for each application. To understand why, -see @{article:Why does Phabricator need so many databases?}. +Phorge uses a separate database for each application. To understand why, +see @{article:Why does Phorge need so many databases?}. Connections =========== -Phabricator specifies if it will use any opened connection just for reading or +Phorge specifies if it will use any opened connection just for reading or also for writing. This allows opening write connections to a primary and read connections to a replica in primary/replica setups (which are not actually supported yet). @@ -45,7 +45,7 @@ Tables ====== Most table names are prefixed by their application names. For example, -Differential revisions are stored in database `phabricator_differential` and +Differential revisions are stored in database `phorge_differential` and table `differential_revision`. This generally makes queries easier to recognize and understand. @@ -57,7 +57,7 @@ We use lower-case table names with words separated by underscores. Column Names ============ -Phabricator uses `camelCase` names for columns. The main advantage is that they +Phorge uses `camelCase` names for columns. The main advantage is that they directly map to properties in PHP classes. Don't use MySQL reserved words (such as `order`) for column names. @@ -65,17 +65,17 @@ Don't use MySQL reserved words (such as `order`) for column names. Data Types ========== -Phabricator defines a set of abstract data types (like `uint32`, `epoch`, and +Phorge defines a set of abstract data types (like `uint32`, `epoch`, and `phid`) which map to MySQL column types. The mapping depends on the MySQL version. -Phabricator uses `utf8mb4` character sets where available (MySQL 5.5 or newer), +Phorge uses `utf8mb4` character sets where available (MySQL 5.5 or newer), and `binary` character sets in most other cases. The primary motivation is to allow 4-byte unicode characters to be stored (the `utf8` character set, which is more widely available, does not support them). On newer MySQL, we use `utf8mb4` to take advantage of improved collation rules. -Phabricator stores dates with an `epoch` abstract data type, which maps to +Phorge stores dates with an `epoch` abstract data type, which maps to `int unsigned`. Although this makes dates less readable when browsing the database, it makes date and time manipulation more consistent and straightforward in the application. @@ -134,8 +134,8 @@ eventually, but there isn't a strong case for them at the present time. PHIDs ===== -Each globally referencable object in Phabricator has an associated PHID -("Phabricator ID") which serves as a global identifier, similar to a GUID. +Each globally referenceable object in Phorge has an associated PHID +("Phorge ID") which serves as a global identifier, similar to a GUID. We use PHIDs for referencing data in different databases. We use both auto-incrementing IDs and global PHIDs because each is useful in @@ -150,6 +150,8 @@ are permitted to subscribe to different types of objects (revisions, tasks, etc). Without PHIDs, we would need to add a "type" column to avoid ID collision; using PHIDs makes implementing features like this simpler. +For more information, see @{article:Handles Technical Documentation} + Transactions ============ @@ -169,7 +171,7 @@ update, and understand than application code. Schema Denormalization ====================== -Phabricator uses schema denormalization sparingly. Avoid denormalization unless +Phorge uses schema denormalization sparingly. Avoid denormalization unless there is a compelling reason (usually, performance) to denormalize. Schema Changes and Migrations diff --git a/src/docs/contributor/describing_problems.diviner b/src/docs/contributor/describing_problems.diviner index d06d7b7d64..30f3b4cad0 100644 --- a/src/docs/contributor/describing_problems.diviner +++ b/src/docs/contributor/describing_problems.diviner @@ -37,21 +37,21 @@ a problem and why it's important for you to resolve it. Here are some examples of good ways to start a problem description: -> My company does contracting work for government agencies. Because of the -> nature of our customers, deadlines are critical and it's very important -> for us to keep track of where we are on a timeline. We're using Maniphest -> to track tasks... +(NOTE) My company does contracting work for government agencies. Because of the + nature of our customers, deadlines are critical and it's very important +for us to keep track of where we are on a timeline. We're using Maniphest +to track tasks... -> I have poor eyesight, and use a screenreader to help me use software like -> Phabricator in my job as a developer. I'm having difficulty... +(NOTE) I have poor eyesight, and use a screenreader to help me use software like + Phorge in my job as a developer. I'm having difficulty... -> We work on a large server program which has very long compile times. -> Switching branches is a huge pain (you have to rebuild the binary after -> every switch, which takes about 8 minutes), but we've recently begun using -> `git worktree` to help, which has made life a lot better. However, ... +(NOTE) We work on a large server program which has very long compile times. + Switching branches is a huge pain (you have to rebuild the binary after + every switch, which takes about 8 minutes), but we've recently begun using + `git worktree` to help, which has made life a lot better. However, ... -> I triage manual test failures from our offshore QA team. Here's how our -> workflow works... +(NOTE) I triage manual test failures from our offshore QA team. Here's how our + workflow works... All of these descriptions are helpful: the provide context about what goals you're trying to accomplish and why. @@ -59,19 +59,19 @@ you're trying to accomplish and why. Here are some examples of ways to start a problem description that probably are not very good: -> {icon times color=red} Add custom keyboard shortcuts. +(IMPORTANT) Add custom keyboard shortcuts. -> {icon times color=red} I have a problem: there is no way to download -> .tar archives of repositories. +(IMPORTANT) I have a problem: there is no way to download + .tar archives of repositories. -> {icon times color=red} I want an RSS feed of my tokens. My root problem is -> that I do not have an RSS feed of my tokens. +(IMPORTANT) I want an RSS feed of my tokens. My root problem is + that I do not have an RSS feed of my tokens. -> {icon times color=red} There is no way to see other users' email addresses. -> That is a problem. +(IMPORTANT) There is no way to see other users' email addresses. + That is a problem. -> {icon times color=red} I've used some other software that has a cool -> feature. Phabricator should have that feature too. +(IMPORTANT) I've used some other software that has a cool + feature. Phorge should have that feature too. These problem descriptions are not helpful. They do not describe goals or provide context. diff --git a/src/docs/contributor/developer_setup.diviner b/src/docs/contributor/developer_setup.diviner index 95508ccd19..8c39c5fc3d 100644 --- a/src/docs/contributor/developer_setup.diviner +++ b/src/docs/contributor/developer_setup.diviner @@ -1,19 +1,19 @@ @title Developer Setup @group developer -How to configure a Phabricator development environment. +How to configure a Phorge development environment. Overview ======== There are some options and workflows that may be useful if you are developing -or debugging Phabricator. +or debugging Phorge. Configuration ============= -To adjust Phabricator for development: +To adjust Phorge for development: - Enable `phabricator.developer-mode` to enable some options and show more debugging information. @@ -48,17 +48,17 @@ After adding, renaming, or moving classes, run `arc liberate` to rebuild the class map: ``` -phabricator/ $ arc liberate +phorge/ $ arc liberate ``` -Until you do this, Phabricator won't recognize your new, moved, or renamed +Until you do this, Phorge won't recognize your new, moved, or renamed classes. You do not need to run this after modifying an existing class. After any modifications to static resources (CSS / JS) but before sending changes for review or pushing them to the remote, run `bin/celerity map`: ``` -phabricator/ $ ./bin/celerity map +phorge/ $ ./bin/celerity map ``` This rebuilds the static resource map. @@ -91,7 +91,7 @@ Phame) you can normally test them by adding more entries to your webserver configuration that look exactly like the primary entry (or expanding the primary entry to match more domains). -Phabricator routes all requests based on host headers, so alternate domains +Phorge routes all requests based on host headers, so alternate domains do not normally need any kind of special configuration. You may also need to add `/etc/hosts` entries for the domains themselves. @@ -103,8 +103,8 @@ Creating Test Data You can create test objects with the "Lipsum" utility: ``` -phabricator/ $ ./bin/lipsum help generate -phabricator/ $ ./bin/lipsum generate ... +phorge/ $ ./bin/lipsum help generate +phorge/ $ ./bin/lipsum generate ... ``` Test data can make your local install feel a little more realistic. With diff --git a/src/docs/contributor/feature_requests.diviner b/src/docs/contributor/feature_requests.diviner index 20fe4b2d30..b2ca702cec 100644 --- a/src/docs/contributor/feature_requests.diviner +++ b/src/docs/contributor/feature_requests.diviner @@ -1,4 +1,211 @@ @title Contributing Feature Requests @group detail -Effective June 1, 2021: Phabricator is no longer actively maintained, and there is no way to file a feature request. +Describes how to file an effective Phorge feature request. + +Overview +======== + +Phorge is an open-source project, and welcomes feature requests from the community +at large. However, there are some guidelines we ask you to follow. + +Overview +======== + +This article describes how to file an effective feature request. + +The most important things to do are: + + - understand the upstream; + - make sure your feature makes sense in the project; + - align your expectations around timelines and priorities; + - describe your problem, not your solution. + +The rest of this article walks through these points in detail. + +If you have a bug report (not a feature request), see +@{article:Contributing Bug Reports} for a more tailored guide. + +For general information on contributing to Phorge, see +@{article:Contributor Introduction}. + + +Understanding the Upstream +========================== + +Before filing a feature request, it may be useful to understand how the +upstream operates. + +Phorge has a designated core team who controls the project and roadmap. +We have a cohesive vision for the project in the long term, and a general +roadmap that extends for years into the future. While the specifics of how +we get there are flexible, many major milestones are well-established. + +Although we set project direction, the community is also a critical part of +Phorge. We aren't all-knowing, and we rely on feedback to help us identify +issues, guide product direction, prioritize changes, and suggest features. + +Feature requests are an important part of this, but we ultimately build only +features which make sense as part of the long term plan. + +Since it's hard to absorb a detailed understanding of that vision, //describing +a problem// is often more effective than //requesting a feature//. We have the +context to develop solutions which fit into our plans, address similar use +cases, make sense with the available infrastructure, and work within the +boundaries of our product vision. For more details on this, see below. + + +Target Audiences +================ + +Some feature requests support very unusual use cases. Although we are broadly +inclusive of many different kinds of users and use cases, we are not trying +to make the software all things to all users. Use cases which are far afield +from the things the majority of users do with Phorge often face substantial +barriers. + +Phorge is primarily targeted at software projects and organizations with +a heavy software focus. We are most likely to design, build, and prioritize +features which serve these organizations and projects. + +Phorge is primarily targeted at software professionals and other +professionals with adjacent responsibilities (like project management and +operations). Particularly, we assume users are proficient computer users and +familiar with software development concepts. We are most likely to design, build +and prioritize features which serve these users. + +Phorge is primarily targeted at professionals working in teams on full-time +projects. Particularly, we assume most users will use the software regularly and +are often willing to spend a little more time up front to get a more efficient +workflow in the long run. We are most likely to design, build and prioritize +features which serve these use cases. + +Phorge is not limited to these kinds of organizations, users and use cases, +but features which are aimed at a different group of users (like students, +casual projects, or inexperienced computer users) may be harder to get +upstreamed. Features aimed at very different groups of users (like wedding +planners, book clubs, or dogs) will be much harder to get upstreamed. + +In many cases, a feature makes something better for all users. For example, +suppose we fixed an issue where colorblind users had difficulty doing something. +Dogs would benefit the most, but colorblind human users would also benefit, and +no one would be worse off. If the benefit for core users is very small these +kinds of features may be hard to prioritize, but there is no exceptional barrier +to getting them upstreamed. + +In other cases, a feature makes something better for some users and worse for +other users. These kinds of features face a high barrier if they make the +software better at planning weddings and worse at reviewing code. + + +Setting Expectations +==================== + +We have a lot of users and a small team. Even if your feature is something we're +interested in and a good fit for where we want the product to go, it may take +us a long time to get around to building it. + +Our long-term roadmap (which we call our +[[ https://we.phorge.it/w/starmap/ | Starmap ]]) has many years worth +of work. Your feature request is competing against thousands of other requests +for priority. + +In general, we try to prioritize work that will have the greatest impact on the +most users. Many feature requests are perfectly reasonable requests, but have +very little impact, impact only a few users, and/or are complex to develop and +support relative to their impact. It can take us a long time to get to these. + +Even if your feature request is simple and has substantial impact for a large +number of users, the size of the request queue means that it is mathematically +unlikely to be near the top. + +As a whole, this means that the overwhelming majority of feature requests will +sit in queue for a long time without any updates, and that we won't be able to +give you any updates or predictions about timelines. One day, out of nowhere, +your feature will materialize. That day may be a decade from now. You should +have realistic expectations about this when filing a feature request. + + +Describe Problems +================= + +When you file a feature request, we need you to describe the problem you're +facing first, not just your desired solution. Describing the problem you are +facing is the **most important part** of a feature request. + +Often, your problem may have a lot in common with other similar problems. If we +understand your use case we can compare it to other use cases and sometimes find +a more powerful or more general solution which solves several problems at once. + +At other times, we'll have a planned solution to the problem that might be +different from your desired solution but accomplish the same goal. Understanding +the root issue can let us merge and contextualize things. + +Sometimes there's already a way to solve your problem that might just not be +obvious. + +Finally, your proposed solution may not be compatible with the direction we +want to take the product, but we may be able to come up with another solution +which has approximately the same effect and does fit into the product direction. + +If you only describe the solution and not the problem, we can't generalize, +contextualize, merge, reframe, or offer alternative solutions or workarounds. + +You must describe the problem you are facing when filing a feature request. We +will not accept feature requests which do not contextualize the request by +describing the root problem. + +If you aren't sure exactly what we're after when we ask you to describe a root +problem, you can find examples and more discussion in +@{article:Describing Root Problems}. + + +Hypotheticals +============= + +We sometimes receive hypothetical feature requests about anticipated problems +or concerns which haven't actually occurred yet. We usually can't do much about +these until the problems actually occur, since the context required to +understand and properly fix the root issue won't exist. + +One situation where this happens is when installs are thinking about adopting +Phorge and trying to guess what problems users might encounter during the +transition. More generally, this includes any request like "if users do **X**, +they might find **Y** confusing", where no actual users have encountered +confusion yet. + +These requests are necessarily missing important context, maybe including the +answers to questions like these: + + - Why did users do **X**? + - What were they trying to do? + - What did they expect to happen? + - How often do users do this? + +The answers to these questions are important in establishing that the issue is +really a problem, figuring out the best solution for it, and prioritizing the +issue relative to other issues. + +Without knowing this information, we can't be confident that we've found a good +solution to the problem, can't know if we've actually fixed the problem, and +can't even know if the issue was really a problem in the first place (some +hypothetical requests describe problems which no users ever encounter). + +We usually can't move forward without this information. In particular, we don't +want to spend time solving hypothetical problems which no real users will ever +encounter: the value of those changes is zero (or negative, by making the +product more complex without providing a benefit), but they consume development +time which could be better spent building much more valuable features. + +Generally, you should wait until a problem actually occurs before filing a +request about it. + + +Next Steps +========== + +Continue by: + + - learning about @{article: Contributing Bug Reports}; or + - reading general support information in @{article:Support Resources}; or + - returning to the @{article:Contributor Introduction}. diff --git a/src/docs/contributor/general_coding_standards.diviner b/src/docs/contributor/general_coding_standards.diviner index 9b151312fd..532b922f9f 100644 --- a/src/docs/contributor/general_coding_standards.diviner +++ b/src/docs/contributor/general_coding_standards.diviner @@ -1,7 +1,7 @@ @title General Coding Standards @group standards -This document is a general coding standard for contributing to Phabricator, +This document is a general coding standard for contributing to Phorge, Arcanist, and Diviner. = Overview = @@ -60,7 +60,7 @@ And, obviously, don't do this sort of thing: determine if code is fast or slow by measuring it. - Reject performance discussions that aren't rooted in concrete data. -In Phabricator, you can usually use the builtin XHProf profiling to quickly +In Phorge, you can usually use the builtin XHProf profiling to quickly gather concrete performance data. @@ -142,7 +142,7 @@ example. = Documentation, Comments and Formatting = - Prefer to remove code by deleting it over removing it by commenting it out. - It shall live forever in source control, and can be retrieved therefrom if + It shall live forever in source control, and can be retrieved there from if it is ever again called upon. - In source code, use only ASCII printable characters plus space and linefeed. Do not use UTF-8 or other multibyte encodings. diff --git a/src/docs/tech/handles.diviner b/src/docs/contributor/handles.diviner similarity index 97% rename from src/docs/tech/handles.diviner rename to src/docs/contributor/handles.diviner index 18e33dc133..ffdd0f0705 100644 --- a/src/docs/tech/handles.diviner +++ b/src/docs/contributor/handles.diviner @@ -1,12 +1,12 @@ @title Handles Technical Documentation -@group handles +@group developer Technical overview of Handles. Overview ======== -Most objects in Phabricator have PHIDs, which are globally unique identifiers +Most objects in Phorge have PHIDs, which are globally unique identifiers that look like `PHID-USER-2zw4hwdt4i5b5ypikv6x`. If you know the PHID for an object, you can load a **handle** for that object to get more information about it. diff --git a/src/docs/contributor/internationalization.diviner b/src/docs/contributor/internationalization.diviner index 99c35e675e..84fe2d2591 100644 --- a/src/docs/contributor/internationalization.diviner +++ b/src/docs/contributor/internationalization.diviner @@ -1,12 +1,12 @@ @title Internationalization @group developer -Describes Phabricator translation and localization. +Describes Phorge translation and localization. Overview ======== -Phabricator partially supports internationalization, but many of the tools +Phorge partially supports internationalization, but many of the tools are missing or in a prototype state. This document describes what tools exist today, how to add new translations, @@ -23,7 +23,7 @@ Once you've created a locale, applications can add translations for that locale. For instructions on adding new classes, see -@{article@phabcontrib:Adding New Classes}. +@{article@contrib:Adding New Classes}. Adding Translations to Locale @@ -38,7 +38,7 @@ provide translations into different locales without needing to define those locales themselves. For instructions on adding new classes, see -@{article@phabcontrib:Adding New Classes}. +@{article@contrib:Adding New Classes}. Writing Translatable Code @@ -58,7 +58,7 @@ $dialog->appendParagraph(pht('This is an example.')); ``` This allows the code to return the correct Spanish or German or Russian -version of the text, if the viewer is using Phabricator in one of those +version of the text, if the viewer is using Phorge in one of those languages and a translation is available. Using `pht()` properly so that strings are translatable can be tricky. Briefly, @@ -275,7 +275,7 @@ return pht('This will take %s hour(s).', new PhutilNumber($count)); If you now load the web UI, you'll see "hour(s)" literally in the UI. To fix this so the translation sounds better in English, provide translations for this -string in the @{class@phabricator:PhabricatorUSEnglishTranslation} file: +string in the @{class:PhabricatorUSEnglishTranslation} file: ```lang=php 'This will take %s hour(s).' => array( @@ -378,4 +378,4 @@ Next Steps Continue by: - adding a new locale or translation file with - @{article@phabcontrib:Adding New Classes}. + @{article@contrib:Adding New Classes}. diff --git a/src/docs/contributor/javascript_coding_standards.diviner b/src/docs/contributor/javascript_coding_standards.diviner index 3b47a566a6..39103e94ec 100644 --- a/src/docs/contributor/javascript_coding_standards.diviner +++ b/src/docs/contributor/javascript_coding_standards.diviner @@ -1,12 +1,12 @@ @title Javascript Coding Standards @group standards -This document describes Javascript coding standards for Phabricator and Javelin. +This document describes Javascript coding standards for Phorge and Javelin. = Overview = This document outlines technical and style guidelines which are followed in -Phabricator and Javelin. Contributors should also follow these guidelines. Many +Phorge and Javelin. Contributors should also follow these guidelines. Many of these guidelines are automatically enforced by lint. These guidelines are essentially identical to the Facebook guidelines, since I diff --git a/src/docs/contributor/n_plus_one.diviner b/src/docs/contributor/n_plus_one.diviner index 6d259671a1..21bac266e7 100644 --- a/src/docs/contributor/n_plus_one.diviner +++ b/src/docs/contributor/n_plus_one.diviner @@ -39,7 +39,7 @@ queries which each return 1 result.** This is particularly true if your database is on a different machine which is, say, 1-2ms away on the network. In this case, issuing 100 queries serially has a minimum cost of 100-200ms, even if they can be satisfied instantly by MySQL. This is far higher than the entire -server-side generation cost for most Phabricator pages should be. +server-side generation cost for most Phorge pages should be. = Batching Queries = diff --git a/src/docs/contributor/phabricator_code_layout.diviner b/src/docs/contributor/phorge_code_layout.diviner similarity index 78% rename from src/docs/contributor/phabricator_code_layout.diviner rename to src/docs/contributor/phorge_code_layout.diviner index 422f228a27..fee99ed897 100644 --- a/src/docs/contributor/phabricator_code_layout.diviner +++ b/src/docs/contributor/phorge_code_layout.diviner @@ -1,19 +1,19 @@ -@title Phabricator Code Layout +@title Phorge Code Layout @group developer -Guide to Phabricator code layout, including how URI mapping works through +Guide to Phorge code layout, including how URI mapping works through application class and subdirectory organization best practices. = URI Mapping = -When a user visits a Phabricator URI, the Phabricator infrastructure parses -that URI with a regular expression to determine what controller class to load. +When a user visits a Phorge URI, the Phorge infrastructure parses that URI with + a regular expression to determine what controller class to load. -The Phabricator infrastructure knows where a given controller class lives on +The Phorge infrastructure knows where a given controller class lives on disk from a cache file the Arcanist phutil mapper generates. This mapping should be updated whenever new classes or files are added: - arc liberate /path/to/phabricator/src + arc liberate /path/to/phorge/src Finally, a given controller class will map to an application which will have most of its code in standardized subdirectories and classes. @@ -22,11 +22,11 @@ most of its code in standardized subdirectories and classes. Suppose you were working on the application `Derp`. - phabricator/src/applications/derp/ + phorge/src/applications/derp/ If `Derp` were as simple as possible, it would have one subdirectory: - phabricator/src/applications/derp/controller/ + phorge/src/applications/derp/controller/ containing the file `DerpController.php` with the class @@ -37,25 +37,25 @@ containing the file `DerpController.php` with the class If `Derp` were (relatively) complex, one could reasonably expect to see the following directory layout: - phabricator/src/applications/derp/conduit/ - phabricator/src/applications/derp/constants/ - phabricator/src/applications/derp/controller/ - phabricator/src/applications/derp/editor/ - phabricator/src/applications/derp/exception/ - phabricator/src/applications/derp/query/ - phabricator/src/applications/derp/replyhandler/ - phabricator/src/applications/derp/storage/ - phabricator/src/applications/derp/view/ + phorge/src/applications/derp/conduit/ + phorge/src/applications/derp/constants/ + phorge/src/applications/derp/controller/ + phorge/src/applications/derp/editor/ + phorge/src/applications/derp/exception/ + phorge/src/applications/derp/query/ + phorge/src/applications/derp/replyhandler/ + phorge/src/applications/derp/storage/ + phorge/src/applications/derp/view/ (The following two folders are also likely to be included for JavaScript and CSS respectively. However, static resources are largely outside the scope of this document. See @{article:Adding New CSS and JS}.) - phabricator/webroot/rsrc/js/application/derp/ - phabricator/webroot/rsrc/css/application/derp/ + phorge/webroot/rsrc/js/application/derp/ + phorge/webroot/rsrc/css/application/derp/ -These directories under `phabricator/src/applications/derp/` represent -the basic set of class types from which most Phabricator applications are +These directories under `phorge/src/applications/derp/` represent +the basic set of class types from which most Phorge applications are assembled. Each would contain a class file. For `Derp`, these classes could be something like: diff --git a/src/docs/contributor/php_coding_standards.diviner b/src/docs/contributor/php_coding_standards.diviner index a14acf17f2..6c68d194ca 100644 --- a/src/docs/contributor/php_coding_standards.diviner +++ b/src/docs/contributor/php_coding_standards.diviner @@ -1,13 +1,13 @@ @title PHP Coding Standards @group standards -This document describes PHP coding standards for Phabricator and related +This document describes PHP coding standards for Phorge and related projects (like Arcanist). = Overview = This document outlines technical and style guidelines which are followed in -Phabricator and Arcanist. Contributors should also follow these guidelines. +Phorge and Arcanist. Contributors should also follow these guidelines. Many of these guidelines are automatically enforced by lint. These guidelines are essentially identical to the Facebook guidelines, since I @@ -176,3 +176,7 @@ diffs which add elements to the array affect only one line. return $this->favoriteFood; } } + +# Extra Readings + +* @{article:PHP Pitfalls} diff --git a/src/docs/contributor/rendering_html.diviner b/src/docs/contributor/rendering_html.diviner index a8fe5a899d..40892b5ad7 100644 --- a/src/docs/contributor/rendering_html.diviner +++ b/src/docs/contributor/rendering_html.diviner @@ -1,11 +1,11 @@ @title Rendering HTML @group developer -Rendering HTML in the Phabricator environment. +Rendering HTML in the Phorge environment. = Overview = -Phabricator attempts to prevent XSS by treating strings as default-unsafe when +Phorge attempts to prevent XSS by treating strings as default-unsafe when rendering. This means that if you try to build HTML through string concatenation, it won't work: the string will be escaped by the rendering pipeline, and the browser will treat it as plain text, not HTML. @@ -51,7 +51,7 @@ content correctly (without double-escaping): array(), $content)); -In Phabricator, the @{function:javelin_tag} function is similar to +In Phorge, the @{function:javelin_tag} function is similar to @{function@arcanist:phutil_tag}, but provides special handling for the `sigil` and `meta` attributes. @@ -117,7 +117,7 @@ If you need to build a list of items with some element in between each of them = AphrontView Classes = -Subclasses of @{class:AphrontView} in Phabricator should return a +Subclasses of @{class:AphrontView} in Phorge should return a @{class@arcanist:PhutilSafeHTML} object. The easiest way to do this is to return `phutil_tag()` or `javelin_tag()`: diff --git a/src/docs/contributor/reproduction_steps.diviner b/src/docs/contributor/reproduction_steps.diviner index 1050e43c48..4a8d916efc 100644 --- a/src/docs/contributor/reproduction_steps.diviner +++ b/src/docs/contributor/reproduction_steps.diviner @@ -6,7 +6,7 @@ Describes how to provide reproduction steps. Overview ======== -When you submit a bug report about Phabricator, you **MUST** include +When you submit a bug report about Phorge, you **MUST** include reproduction steps. We can not help you with bugs we can not reproduce, and will not accept reports which omit reproduction steps or have incomplete or insufficient instructions. @@ -15,7 +15,7 @@ This document explains what we're looking for in good reproduction steps. Briefly: - Reproduction steps must allow us to reproduce the problem locally on a - clean, up-to-date install of Phabricator. + clean, up-to-date install of Phorge. - Reproduction should be as simple as possible. Good reproduction steps can take time to write out clearly, simplify, and @@ -70,7 +70,7 @@ Reliable Reproduction ===================== When you file a bug report, the first thing we do to fix it is to try to -reproduce the problem locally on an up-to-date install of Phabricator. We will +reproduce the problem locally on an up-to-date install of Phorge. We will do this by following the steps you provide. If we can recreate the issue locally, we can almost always resolve the problem (often very promptly). @@ -80,7 +80,7 @@ settings, feed stories, etc) that we don't have access to. We either can't follow these steps, or can't reproduce issues by following them. Reproduction steps must be complete and self-contained, and must allow -**anyone** to reproduce the issue on a new, empty install of Phabricator. If +**anyone** to reproduce the issue on a new, empty install of Phorge. If the bug you're seeing depends on data or configuration which would not be present on a new install, you need to include that information in your steps. @@ -101,11 +101,7 @@ to private or proprietary data. Now, to verify that your steps provide a complete, self-contained way to reproduce the issue, follow them yourself on a new, empty, up-to-date instance -of Phabricator. - -If you can't easily start an empty instance locally, you can launch an empty -instance on Phacility in about 60 seconds (see the "Resources" section for -details). +of Phorge. If you can follow your steps and reproduce the issue on a clean instance, we'll probably be able to follow them and reproduce the issue ourselves. @@ -128,32 +124,6 @@ them by removing some steps or describing steps more narrowly. For help, see "Simplifying Steps" below. -Resources -========= - -We provide some resources which can make it easier to start building steps, or -to simplify steps. - -**Phacility Test Instances**: You can launch a new, up-to-date instance of -Phabricator on Phacility in about a minute at . -These instances run `stable`. - -You can use these instances to make sure that issues haven't already been -fixed, that they reproduce on a clean install, or that your steps are really -complete, and that the root cause isn't custom code or local extensions. Using -a test instance will avoid disrupting other users on your install. - -**Test Repositories**: There are several test repositories on -`secure.phabricator.com` which you can push commits to if you need to build -an example to demonstrate a problem. - -For example, if you're seeing an issue with a certain filename but the commit -where the problem occurs is in a proprietary internal repository, push a commit -that affects a file with a similar name to a test repository, then reproduce -against the test data. This will allow you to generate steps which anyone can -follow. - - Simplifying Steps ================= @@ -239,10 +209,6 @@ to a minimal reproduction case, we can't accept it as a bug report. These issues are tremendously time consuming for us to pursue and rarely benefit more than one install. -If the issue is important but falls outside the scope of permissible bug -reports, we're happy to provide more tailored support at consulting rates. See -[[ https://secure.phabricator.com/w/consulting/ | Consulting ]] for details. - Next Steps ========== diff --git a/src/docs/contributor/running_builtin_php_webserver.diviner b/src/docs/contributor/running_builtin_php_webserver.diviner index 1e4bdacb5c..b511731dc1 100644 --- a/src/docs/contributor/running_builtin_php_webserver.diviner +++ b/src/docs/contributor/running_builtin_php_webserver.diviner @@ -3,7 +3,7 @@ As of version 5.4.0, the PHP command line interface provides a built-in web server. This web server is designed for developmental purposes only, and should -not be used in production. Phabricator can be executed under it with the +not be used in production. Phorge can be executed under it with the command: - $ php -S localhost:8000 -t path/to/phabricator/webroot/ path/to/phabricator/webroot/index.php + $ php -S localhost:8000 -t path/to/phorge/webroot/ path/to/phorge/webroot/index.php diff --git a/src/docs/contributor/unit_tests.diviner b/src/docs/contributor/unit_tests.diviner index 7977a4a876..35cee09566 100644 --- a/src/docs/contributor/unit_tests.diviner +++ b/src/docs/contributor/unit_tests.diviner @@ -1,11 +1,11 @@ @title Writing Unit Tests @group developer -Simple guide to Arcanist and Phabricator unit tests. +Simple guide to Arcanist and Phorge unit tests. = Overview = -Arcanist and Phabricator provide and use a simple unit test framework. This +Arcanist and Phorge provide and use a simple unit test framework. This document is aimed at project contributors and describes how to use it to add and run tests in these projects or other libphutil libraries. @@ -16,11 +16,11 @@ for information on customizing engines. = Adding Tests = -To add new tests to a Arcanist or Phabricator module: +To add new tests to a Arcanist or Phorge module: - Create a `__tests__/` directory in the module if it doesn't exist yet. - Add classes to the `__tests__/` directory which extend from - @{class:PhabricatorTestCase} (in Phabricator) or + @{class:PhabricatorTestCase} (in Phorge) or @{class@arcanist:PhutilTestCase} (elsewhere). - Run `arc liberate` on the library root so your classes are loadable. @@ -38,7 +38,7 @@ Once you've added test classes, you can run them with: Here's a simple example test: lang=php - class PhabricatorTrivialTestCase extends PhabricatorTestCase { + class PhorgeTrivialTestCase extends PhabricatorTestCase { private $two; @@ -56,7 +56,7 @@ Here's a simple example test: You can see this class at @{class:PhabricatorTrivialTestCase} and run it with: - phabricator/ $ arc unit src/infrastructure/testing/testcase/ + phorge/ $ arc unit src/infrastructure/testing/testcase/ PASS <1ms* testAllIsRightWithTheWorld For more information on writing tests, see @@ -64,7 +64,7 @@ For more information on writing tests, see = Database Isolation = -By default, Phabricator isolates unit tests from the database. It makes a crude +By default, Phorge isolates unit tests from the database. It makes a crude effort to simulate some side effects (principally, ID assignment on insert), but any queries which read data will fail to select any rows and throw an exception about isolation. In general, isolation is good, but this can make certain types diff --git a/src/docs/contributor/using_edges.diviner b/src/docs/contributor/using_edges.diviner index 89d07f4c2f..132d728019 100644 --- a/src/docs/contributor/using_edges.diviner +++ b/src/docs/contributor/using_edges.diviner @@ -6,8 +6,7 @@ Guide to the Edges infrastructure. = Overview = Edges are a generic way of storing a relationship between two objects (like a -Task and its attached files). If you are familiar with the Facebook associations -framework, Phabricator Edges are substantially similar. +Task and its attached files). An edge is defined by a source PHID (the edge origin), a destination PHID (the edge destination) and an edge type (which describes the relationship, diff --git a/src/docs/contributor/using_oauthserver.diviner b/src/docs/contributor/using_oauthserver.diviner index b40496d7cf..3e30065fd1 100644 --- a/src/docs/contributor/using_oauthserver.diviner +++ b/src/docs/contributor/using_oauthserver.diviner @@ -1,19 +1,19 @@ -@title Using the Phabricator OAuth Server +@title Using the Phorge OAuth Server @group developer -How to use the Phabricator OAuth Server. +How to use the Phorge OAuth Server. = Overview = -Phabricator includes an OAuth Server which supports the +Phorge includes an OAuth Server which supports the `Authorization Code Grant` flow as described in the OAuth 2.0 specification: http://tools.ietf.org/html/draft-ietf-oauth-v2-23 This functionality can allow clients to integrate with a given -Phabricator instance in a secure way with granular data access. -For example, Phabricator can be used as a central identity store for any +Phorge instance in a secure way with granular data access. +For example, Phorge can be used as a central identity store for any clients that implement OAuth 2.0. = Vocabulary = @@ -41,7 +41,7 @@ clients that implement OAuth 2.0. = Obtaining an Authorization Code = -POST or GET `https://phabricator.example.com/oauthserver/auth/` with the +POST or GET `https://phorge.example.com/oauthserver/auth/` with the following parameters: - Required - **client_id** - the id of the newly registered client. @@ -69,14 +69,14 @@ redirect_uri with a valid authorization code. If there is an error, the OAuth Server will return a descriptive error message. This error will be presented to the resource owner on the -Phabricator domain if there is reason to believe there is something fishy +Phorge domain if there is reason to believe there is something fishy with the client. For example, if there is an issue with the redirect_uri. Otherwise, the OAuth Server will redirect to the pertinent redirect_uri and include the pertinent error information. = Obtaining an Access Token = -POST or GET `https://phabricator.example.com/oauthserver/token/` +POST or GET `https://phorge.example.com/oauthserver/token/` with the following parameters: - Required - **client_id** - the id of the client @@ -101,7 +101,7 @@ message. Simply include a query param with the key of "access_token" and the value as the earlier obtained access token. For example: -```https://phabricator.example.com/api/user.whoami?access_token=ykc7ly7vtibj334oga4fnfbuvnwz4ocp``` +```https://phorge.example.com/api/user.whoami?access_token=ykc7ly7vtibj334oga4fnfbuvnwz4ocp``` If the token has expired or is otherwise invalid, the client will receive an error indicating as such. In these cases, the client should re-initiate diff --git a/src/docs/contributor/version.diviner b/src/docs/contributor/version.diviner index 8ffafede1b..6e960c7993 100644 --- a/src/docs/contributor/version.diviner +++ b/src/docs/contributor/version.diviner @@ -19,10 +19,10 @@ resolved because valid reproduction steps must also reproduce against a clean, up-to-date install. See @{article:Providing Reproduction Steps} for details. -Phabricator Version -=================== +Phorge Version +============== -To get Phabricator version information: +To get Phorge version information: - Go to the {nav Config} application. You can type "Config" into the global search box, or navigate to `https://your.install.com/config/`. You must @@ -52,7 +52,7 @@ prevents you from reaching the version reporting screen. Running a Fork? =============== -If you've forked Phabricator and have local commits, please make sure you are +If you've forked Phorge and have local commits, please make sure you are reporting upstream commit hashes, not local commit hashes. The UI will attempt to figure out where you branched from, but it may not be able to in all cases. @@ -68,7 +68,7 @@ $ git merge-base HEAD origin/master ``` Note that if you report a bug and have local commits, we will almost always ask -you to reproduce the issue against a clean copy of Phabricator before we +you to reproduce the issue against a clean copy of Phorge before we continue. You can get help faster by doing this //before// reporting an issue. diff --git a/src/docs/flavor/about_flavor_text.diviner b/src/docs/flavor/about_flavor_text.diviner deleted file mode 100644 index 3d46ff41d0..0000000000 --- a/src/docs/flavor/about_flavor_text.diviner +++ /dev/null @@ -1,9 +0,0 @@ -@title About Flavor Text -@group overview - -Explains what's going on here. - -= Overview = - -Flavor Text is a collection of short articles which pertain to software -development in general, not necessarily to Phabricator specifically. diff --git a/src/docs/flavor/javascript_object_array.diviner b/src/docs/flavor/javascript_object_array.diviner index cdba029e9a..113dbb1000 100644 --- a/src/docs/flavor/javascript_object_array.diviner +++ b/src/docs/flavor/javascript_object_array.diviner @@ -1,13 +1,13 @@ -@title Javascript Object and Array +@title JavaScript Object and Array @group javascript -This document describes the behaviors of Object and Array in Javascript, and +This document describes the behaviors of Object and Array in JavaScript, and a specific approach to their use which produces basically reasonable language behavior. = Primitives = -Javascript has two native datatype primitives, Object and Array. Both are +JavaScript has two native datatype primitives, Object and Array. Both are classes, so you can use `new` to instantiate new objects and arrays: COUNTEREXAMPLE @@ -43,11 +43,11 @@ and Array are both classes, but "object" is also a primitive type. Object is = Objects are Maps, Arrays are Lists = PHP has a single `array` datatype which behaves like as both map and a list, -and a common mistake is to treat Javascript arrays (or objects) in the same way. +and a common mistake is to treat JavaScript arrays (or objects) in the same way. **Don't do this.** It sort of works until it doesn't. Instead, learn how -Javascript's native datatypes work and use them properly. +JavaScript's native datatypes work and use them properly. -In Javascript, you should think of Objects as maps ("dictionaries") and Arrays +In JavaScript, you should think of Objects as maps ("dictionaries") and Arrays as lists ("vectors"). You store keys-value pairs in a map, and store ordered values in a list. So, @@ -58,7 +58,13 @@ store key-value pairs in Objects. species: 'zebra' }; - console.log(o.name); + o.paws = 4; + + o['numberOfEars'] = 2; + + console.log(o.name); + console.log(o.paws); + console.log(o.numberOfEars); ...and store ordered values in Arrays. @@ -71,8 +77,14 @@ Don't store key-value pairs in Arrays and don't expect Objects to be ordered. var a = []; a['name'] = 'Hubert'; // No! Don't do this! -This technically works because Arrays are Objects and you think everything is -fine and dandy, but it won't do what you want and will burn you. +Technically, both work because Arrays //are// Objects and you think everything +is fine and dandy, but it won't do what you want and will burn you. For example, +using `.length` will play tricks on you. + +In short, trust me: + +* use `[]` only to create a stack of consecutive elements numerically indexed +* use `{}` to create associative maps ("associative arrays") = Iterating over Maps and Lists = @@ -140,7 +152,7 @@ The correct way to deal with this is: continue; } f(list[ii]); - } + } Avoid sparse arrays if possible. diff --git a/src/docs/flavor/javascript_pitfalls.diviner b/src/docs/flavor/javascript_pitfalls.diviner index 9276ae4712..fbc2eae05c 100644 --- a/src/docs/flavor/javascript_pitfalls.diviner +++ b/src/docs/flavor/javascript_pitfalls.diviner @@ -1,12 +1,12 @@ -@title Javascript Pitfalls +@title JavaScript Pitfalls @group javascript -This document discusses pitfalls and flaws in the Javascript language, and how +This document discusses pitfalls and flaws in the JavaScript language, and how to avoid, work around, or at least understand them. = Implicit Semicolons = -Javascript tries to insert semicolons if you forgot them. This is a pretty +JavaScript tries to insert semicolons if you forgot them. This is a pretty horrible idea. Notably, it can mask syntax errors by transforming subexpressions on their own lines into statements with no effect: @@ -46,11 +46,11 @@ you can pass `arguments` to Function.prototype.apply() without converting it. There is essentially only one reasonable, consistent way to use these primitives but it is not obvious. Navigate these troubled waters with -@{article:Javascript Object and Array}. +@{article:JavaScript Object and Array}. = typeof null == "object" = -This statement is true in Javascript: +This statement is true in JavaScript: typeof null == 'object' @@ -58,9 +58,9 @@ This is pretty much a bug in the language that can never be fixed now. = Number, String, and Boolean objects = -Like Java, Javascript has primitive versions of number, string, and boolean, +Like Java, JavaScript has primitive versions of number, string, and boolean, and object versions. In Java, there's some argument for this distinction. In -Javascript, it's pretty much completely worthless and the behavior of these +JavaScript, it's pretty much completely worthless and the behavior of these objects is wrong. String and Boolean in particular are essentially unusable: lang=js @@ -83,5 +83,5 @@ Number.prototype, etc.) and their logical behavior is at best absurd and at worst strictly wrong. **Never use** `new Number()`, `new String()` or `new Boolean()` unless -your Javascript is God Tier and you are absolutely sure you know what you are +your JavaScript is God Tier and you are absolutely sure you know what you are doing. diff --git a/src/docs/flavor/php_pitfalls.diviner b/src/docs/flavor/php_pitfalls.diviner index 3f4be45dd7..b280cb2394 100644 --- a/src/docs/flavor/php_pitfalls.diviner +++ b/src/docs/flavor/php_pitfalls.diviner @@ -49,18 +49,7 @@ If a value is not truthy, it is "falsey". These values are falsey in PHP: false // boolean array() // empty array -Disregarding some bizarre edge cases, all other values are truthy. Note that -because "0" is falsey, this sort of thing (intended to prevent users from making -empty comments) is wrong in PHP: - - COUNTEREXAMPLE - if ($comment_text) { - make_comment($comment_text); - } - -This is wrong because it prevents users from making the comment "0". //THIS -COMMENT IS TOTALLY AWESOME AND I MAKE IT ALL THE TIME SO YOU HAD BETTER NOT -BREAK IT!!!// A better test is probably `strlen()`. +Disregarding some bizarre edge cases, all other values are truthy. In addition to truth tests with `if`, PHP has two special truthiness operators which look like functions but aren't: `empty()` and `isset()`. These operators @@ -92,16 +81,16 @@ variables. `empty()` evaluates truthiness exactly opposite of `if()`. `isset()` returns `true` for everything except `null`. This is the truth table: -| Value | `if()` | `empty()` | `isset()` | -|-------|--------|-----------|-----------| -| `null` | `false` | `true` | `false` | -| `0` | `false` | `true` | `true` | -| `0.0` | `false` | `true` | `true` | -| `"0"` | `false` | `true` | `true` | -| `""` | `false` | `true` | `true` | -| `false` | `false` | `true` | `true` | -| `array()` | `false` | `true` | `true` | -| Everything else | `true` | `false` | `true` | +| Value | `if()` | `empty()` | `isset()` | +|---------------|--------|-----------|-----------| +| `null` | `false`| `true` | `false` | +| `0` | `false`| `true` | `true` | +| `0.0` | `false`| `true` | `true` | +| `"0"` | `false`| `true` | `true` | +| `""` | `false`| `true` | `true` | +|`false` | `false`| `true` | `true` | +|`array()` | `false`| `true` | `true` | +|Everything else| `true` | `false` | `true` | The value of these operators is that they accept undeclared variables and do not issue a warning. Specifically, if you try to do this you get a warning: @@ -138,6 +127,64 @@ Put another way, use `isset()` if you want to type `if ($value !== null)` but are testing something that may not be declared. Use `empty()` if you want to type `if (!$value)` but you are testing something that may not be declared. += Check for non-empty strings = + +As already mentioned, note that you cannot just use an `if` or `empty()` to +check for a non-empty string, mostly because "0" is falsey, so you cannot rely +on this sort of thing to prevent users from making empty comments: + + COUNTEREXAMPLE + if ($comment_text) { + make_comment($comment_text); + } + +This is wrong because it prevents users from making the comment "0". + +//THE COMMENT "0" IS TOTALLY AWESOME AND I MAKE IT ALL THE TIME SO YOU HAD +BETTER NOT BREAK IT!!!// + +Another way //was// also `strlen()`: + + COUNTEREXAMPLE + if (strlen($comment_text)) { + make_comment($comment_text); + } + +But using `strlen(null)` causes a deprecation warning since PHP 8.1. Also, +using `strlen()` uses too many CPU cycles to just check of a non-empty. + +In short, outside Phorge, this is a general way to check for non-empty strings +for most wild input types: + +```lang=php + $value_str = (string) $value; + if ($value_str !== '') { + // do something + } +``` + +To do the same thing in Phorge, use this better and safer approach: + +```lang=php + $value_str = phutil_string_cast($value); + if ($value_str !== '') { + // do something + } +``` + +And, if you are 100% sure that you are __only__ working with string and +null, evaluate this instead: + +```lang=php + if (phutil_nonempty_string($value)) { + // do something + } +``` + +WARNING: The function `phutil_nonempty_string()` is designed to throw a nice +exception if it receives `true`, `false`, an array, an object or anything +alien that is not a string and not null. Do your evaluations. + = usort(), uksort(), and uasort() are Slow = This family of functions is often extremely slow for large datasets. You should @@ -256,7 +303,7 @@ echo $obj->flavor; // Outputs 'coconut'. echo get_class($obj); // Outputs 'stdClass'. ``` -This is occasionally useful, mostly to force an object to become a Javascript +This is occasionally useful, mostly to force an object to become a JavaScript dictionary (vs a list) when passed to `json_encode()`. = Invoking `new` With an Argument Vector is Really Hard = diff --git a/src/docs/flavor/project_history.diviner b/src/docs/flavor/project_history.diviner index c3b5363d50..6b51e203ed 100644 --- a/src/docs/flavor/project_history.diviner +++ b/src/docs/flavor/project_history.diviner @@ -1,30 +1,27 @@ -@title Phabricator Project History +@title Phorge Project History @group lore -A riveting tale of adventure. In this document, I refer to worldly and -sophisticated engineer Evan Priestley as "I", which is only natural as I am he. - -This document is mostly just paragraph after paragraph of self-aggrandizement. +A riveting tale of adventure. = In The Beginning = -I wrote the original version of Differential in one night at a Facebook -Hackathon in April or May 2007, along with Luke Shepard. I joined the company in -April and code review was already an established and mostly-mandatory part of -the culture, but it happened over email and was inefficient and hard to keep -track of. I remember feeling like I was spending a lot of time waiting for code -review to happen, which was a major motivator for building the tool. +Evan Priestley wrote the original version of Differential in one night at a +Facebook Hackathon in April or May 2007, along with Luke Shepard. He joined the +company in April and code review was already an established and mostly-mandatory +part of the culture, but it happened over email and was inefficient and hard to +keep track of. Evan remembers feeling like he was spending a lot of time waiting + for code review to happen, which was a major motivator for building the tool. The original name of the tool was "Diffcamp". Some time earlier there had been an attempt to create a project management tool that was a sort of hybrid between -Trac and Basecamp called "Traccamp". Since we were writing the code review tool -at the height of the brief popularity Traccamp enjoyed, we integrated and called -the new tool Diffcamp even though it had no relation to Basecamp. Traccamp fell -by the wayside shortly thereafter and was eventually removed. +Trac and Basecamp called "Traccamp". Since they were writing the code review tool +at the height of the brief popularity Traccamp enjoyed, Evan and Luke integrated + and called the new tool Diffcamp even though it had no relation to Basecamp. + Traccamp fell by the wayside shortly thereafter and was eventually removed. -However, Diffcamp didn't share its fate. We spent some more time working on it -and got good enough to win hearts and minds over emailing diffs around and was -soon the de facto method of code review at Facebook. +However, Diffcamp didn't share its fate. Evan and Luke spent some more time +working on it and got good enough to win hearts and minds over emailing diffs +around and was soon the de facto method of code review at Facebook. = The Long Bloat = @@ -34,10 +31,10 @@ was 100% SVN in early 2007 but 90%+ of Engineers worked primarily in git with SVN bridging by 2010). As these patches were contributed pretty much randomly, it also gained a lot of performance problems, usability issues, and bugs. -Through 2007 and 2008 I worked mostly on frontend and support infrastructure; -among other things, I wrote a static resource management system called Haste. In -2009 I worked on the Facebook Lite site, where I built the Javelin Javascript -library and an MVC-flavored framework called Alite. +Through 2007 and 2008 Evan worked mostly on frontend and support infrastructure; +among other things, he wrote a static resource management system called Haste. +In 2009 Evan worked on the Facebook Lite site, where he built the Javelin +JavaScript library and an MVC-flavored framework called Alite. But by early 2010, Diffcamp was in pretty bad shape. Two years of having random features grafted onto it without real direction had left it slow and difficult @@ -49,12 +46,18 @@ problems. = Differential = -I joined the new Dev Tools team around February 2010 and took over Diffcamp. I -renamed it to Differential, moved it to a new Alite-based infrastructure with -Javelin, and started making it somewhat less terrible. I eventually wrote +Evan joined the new Dev Tools team around February 2010 and took over Diffcamp. +He renamed it to Differential, moved it to a new Alite-based infrastructure with +Javelin, and started making it somewhat less terrible. He eventually wrote Diffusion and built Herald to replace a very difficult-to-use predecessor. These -tools were less negatively received than the older versions. By December 2010 I -started open sourcing them; Haste became //Celerity// and Alite became -//Aphront//. I wrote Maniphest to track open issues with the project in January -or February, left Facebook in April, and shortly after, we open sourced +tools were less negatively received than the older versions. By December 2010, +Evan started open sourcing them; Haste became //Celerity// and Alite became +//Aphront//. He wrote Maniphest to track open issues with the project in January +or February, left Facebook in April, and shortly after, open sourced Phabricator. + += Phork = +In 2021, Evan announced that Phabricator was no longer maintained. A group of +open-source contributors came together and forked it. This new +group called renamed the project "Phorge" and continues to maintain this +beloved and well-used project. diff --git a/src/docs/flavor/recommendations_on_branching.diviner b/src/docs/flavor/recommendations_on_branching.diviner index 38d196ad9b..521b08d873 100644 --- a/src/docs/flavor/recommendations_on_branching.diviner +++ b/src/docs/flavor/recommendations_on_branching.diviner @@ -7,13 +7,13 @@ This document discusses organizing branches in your remote/origin for feature development and release management, not the use of local branches in Git or queues or bookmarks in Mercurial. -This document is purely advisory. Phabricator works with a variety of branching +This document is purely advisory. Phorge works with a variety of branching strategies, and diverging from the recommendations in this document will not impact your ability to use it for code review and source management. = Overview = -This document describes a branching strategy used by Facebook and Phabricator to +This document describes a branching strategy used by Facebook and Phorge to develop software. It scales well and removes the pain associated with most branching strategies. This strategy is most applicable to web applications, and may be less applicable to other types of software. The basics are: @@ -118,7 +118,7 @@ describing similar systems: - Martin Fowler discusses these systems in a 2010 blog post here: [[http://martinfowler.com/bliki/FeatureToggle.html | Martin Fowler's FeatureToggle]]. - - Phabricator just adds config options but defaults them to off. When + - Phorge just adds config options but defaults them to off. When developing, we turn them on locally. Once a feature is ready, we default it on. We have a vastly less context to deal with than most projects, however, and sometimes get away with simply not linking new features in the UI until diff --git a/src/docs/flavor/recommendations_on_revision_control.diviner b/src/docs/flavor/recommendations_on_revision_control.diviner index 8e30e2f4f1..3e5a5a791f 100644 --- a/src/docs/flavor/recommendations_on_revision_control.diviner +++ b/src/docs/flavor/recommendations_on_revision_control.diviner @@ -3,12 +3,12 @@ Project recommendations on how to organize revision control. -This document is purely advisory. Phabricator works with a variety of revision +This document is purely advisory. Phorge works with a variety of revision control strategies, and diverging from the recommendations in this document will not impact your ability to use it for code review and source management. -This is my (epriestley's) personal take on the issue and not necessarily -representative of the views of the Phabricator team as a whole. +This is Evan's personal take on the issue and not necessarily +representative of the views of the Phorge team as a whole. = Overview = diff --git a/src/docs/flavor/so_many_databases.diviner b/src/docs/flavor/so_many_databases.diviner index 74fb21332f..cde2c93825 100644 --- a/src/docs/flavor/so_many_databases.diviner +++ b/src/docs/flavor/so_many_databases.diviner @@ -1,19 +1,19 @@ -@title Why does Phabricator need so many databases? +@title Why does Phorge need so many databases? @group lore -Phabricator uses about 60 databases (and we may have added more by the time you +Phorge uses about 60 databases (and we may have added more by the time you read this document). This sometimes comes as a surprise, since you might assume it would only use one database. The approach we use is designed to work at scale for huge installs with many thousands of users. We care a lot about working well for large installs, and about scaling up gracefully to meet the needs of growing organizations. We want -small startups to be able to install Phabricator and have it grow with them as +small startups to be able to install Phorge and have it grow with them as they expand to many thousands of employees. -A cost of this approach is that it makes Phabricator more difficult to install +A cost of this approach is that it makes Phorge more difficult to install on shared hosts which require a lot of work to create or authorize access to -each database. However, Phabricator does a lot of advanced or complex things +each database. However, Phorge does a lot of advanced or complex things which are difficult to configure or manage on shared hosts, and we don't recommend installing it on a shared host. The install documentation explicitly discourages installing on shared hosts. @@ -26,11 +26,11 @@ operating at scale. Listing Databases ================= -You can get a full list of the databases Phabricator needs with `bin/storage +You can get a full list of the databases Phorge needs with `bin/storage databases`. It will look something like this: ``` -$ /core/lib/phabricator/bin/storage databases +$ /core/lib/phorge/bin/storage databases secure_audit secure_calendar secure_chatlog @@ -89,9 +89,9 @@ tables which are organized into separate application databases, just like it's easier to work with a large project if you organize source files into directories. -If you aren't developing Phabricator and never look at the data in the +If you aren't developing Phorge and never look at the data in the database, you probably won't benefit from this organization. However, if you -are a developer or want to extend Phabricator or look under the hood, it's +are a developer or want to extend Phorge or look under the hood, it's easier to find what you're looking for and work with the tables when they're organized by application. @@ -118,7 +118,7 @@ each database, databases do cost something. However, this cost is an artificial cost imposed by the selected environment, and this is only the first of many issues you'll run into trying to install and -run Phabricator on a shared host. These issues are why we strongly discourage +run Phorge on a shared host. These issues are why we strongly discourage using shared hosts, and recommend against them in the install guide. diff --git a/src/docs/flavor/soon_static_resources.diviner b/src/docs/flavor/soon_static_resources.diviner index 96f28cfe2f..79fbc29819 100644 --- a/src/docs/flavor/soon_static_resources.diviner +++ b/src/docs/flavor/soon_static_resources.diviner @@ -121,6 +121,6 @@ version of CSS and JS into the database. = Reference Implementation: Celerity = -Some of the ideas discussed here are implemented in Phabricator's //Celerity// +Some of the ideas discussed here are implemented in Phorge's's //Celerity// system, which is essentially a simplified version of the //Haste// system used by Facebook. diff --git a/src/docs/flavor/things_you_should_do_now.diviner b/src/docs/flavor/things_you_should_do_now.diviner index 0d3b4135ba..58a5b8eaea 100644 --- a/src/docs/flavor/things_you_should_do_now.diviner +++ b/src/docs/flavor/things_you_should_do_now.diviner @@ -134,5 +134,5 @@ query escaping system the rest of the application does. Hopefully, whatever language you're writing in has good query libraries that can handle escaping for you. If so, use them. If you're using PHP and don't have -a solution in place yet, the Phabricator implementation of `qsprintf()` is +a solution in place yet, the Phorge implementation of `qsprintf()` is similar to Facebook's system and was successful there. diff --git a/src/docs/flavor/writing_reviewable_code.diviner b/src/docs/flavor/writing_reviewable_code.diviner index edff09f7d0..8cebc5d3e4 100644 --- a/src/docs/flavor/writing_reviewable_code.diviner +++ b/src/docs/flavor/writing_reviewable_code.diviner @@ -3,14 +3,14 @@ Project recommendations on how to structure changes. -This document is purely advisory. Phabricator works with a variety of revision +This document is purely advisory. Phorge works with a variety of revision control strategies, and diverging from the recommendations in this document will not impact your ability to use it for code review and source management. = Overview = This document describes a strategy for structuring changes used successfully at -Facebook and in Phabricator. In essence: +Facebook and in Phorge. In essence: - Each commit should be as small as possible, but no smaller. - The smallest a commit can be is a single cohesive idea: don't make commits @@ -71,8 +71,8 @@ make any sense and you would increase the collective complexity. The real goal is for each change to have minimal complexity, line size is just a proxy that is often well-correlated with complexity. -We generally follow these practices in Phabricator. The median change size for -Phabricator is 35 lines. +We generally follow these practices in Phorge. The median change size for +Phorge is 35 lines. = Write Sensible Commit Messages = @@ -146,7 +146,7 @@ really all that important in commit messages include: reasons //why// a change is happening into the commit message. - Although maybe the spelling and grammar shouldn't be egregiously bad? -Phabricator does not have guidelines for this stuff. You can obviously set +Phorge does not have guidelines for this stuff. You can obviously set guidelines at your organization if you prefer, but getting the //why// into the message is the most important part. diff --git a/src/docs/user/cluster/cluster.diviner b/src/docs/user/cluster/cluster.diviner index 2362b80485..10f8e98e4c 100644 --- a/src/docs/user/cluster/cluster.diviner +++ b/src/docs/user/cluster/cluster.diviner @@ -1,7 +1,7 @@ @title Clustering Introduction @group cluster -Guide to configuring Phabricator across multiple hosts for availability and +Guide to configuring Phorge across multiple hosts for availability and performance. @@ -12,7 +12,7 @@ WARNING: This feature is a prototype. Installs should expect a challenging adventure when deploying clusters. In the best of times, configuring a cluster is complex and requires significant operations experience. -Phabricator can be configured to run on multiple hosts with redundant services +Phorge can be configured to run on multiple hosts with redundant services to improve its availability and scalability, and make disaster recovery much easier. @@ -20,7 +20,7 @@ Clustering is more complex to setup and maintain than running everything on a single host, but greatly reduces the cost of recovering from hardware and network failures. -Each Phabricator service has an array of clustering options that can be +Each Phorge service has an array of clustering options that can be configured somewhat independently. Configuring a cluster is inherently complex, and this is an advanced feature aimed at installs with large userbases and experienced operations personnel who need this high degree of flexibility. @@ -55,7 +55,7 @@ See below for a walkthrough of these services in greater detail. Preparing for Clustering ======================== -To begin deploying Phabricator in cluster mode, set up `cluster.addresses` +To begin deploying Phorge in cluster mode, set up `cluster.addresses` in your configuration. This option should contain a list of network address blocks which are considered @@ -65,8 +65,8 @@ hosts in the cluster, so this list should be as small as possible. See "Cluster Whitelist Security" below for discussion. If you are deploying hardware in EC2, a reasonable approach is to launch a -dedicated Phabricator VPC, whitelist the whole VPC as a Phabricator cluster, -and then deploy only Phabricator services into that VPC. +dedicated Phorge VPC, whitelist the whole VPC as a Phorge cluster, +and then deploy only Phorge services into that VPC. If you have additional auxiliary hosts which run builds and tests via Drydock, you should //not// include them in the cluster address definition. For more @@ -84,7 +84,7 @@ When you configure `cluster.addresses`, you should keep the list of trusted cluster hosts as small as possible. Hosts on this list gain additional capabilities, including these: -**Trusted HTTP Headers**: Normally, Phabricator distrusts the load balancer +**Trusted HTTP Headers**: Normally, Phorge distrusts the load balancer HTTP headers `X-Forwarded-For` and `X-Forwarded-Proto` because they may be client-controlled and can be set to arbitrary values by an attacker if no load balancer is deployed. In particular, clients can set `X-Forwarded-For` to any @@ -134,7 +134,7 @@ highest impact on availability and resistance to data loss. This is usually the most important service to make redundant if your focus is on availability and disaster recovery. -Configuring replicas allows Phabricator to run in read-only mode if you lose +Configuring replicas allows Phorge to run in read-only mode if you lose the master and to quickly promote the replica as a replacement. For details, see @{article:Cluster: Databases}. @@ -147,7 +147,7 @@ Configuring multiple repository hosts is complex, but is required before you can add multiple daemon or web hosts. Repository replicas are important for availability if you host repositories -on Phabricator, but less important if you host repositories elsewhere +on Phorge, but less important if you host repositories elsewhere (instead, you should focus on making that service more available). The distributed nature of Git and Mercurial tend to mean that they are @@ -243,7 +243,7 @@ Cluster: Fulltext Search Configuring search services is relatively simple and has no pre-requisites. -By default, Phabricator uses MySQL as a fulltext search engine, so deploying +By default, Phorge uses MySQL as a fulltext search engine, so deploying multiple database hosts will effectively also deploy multiple fulltext search hosts. @@ -257,7 +257,7 @@ Overlaying Services =================== Although hosts can run a single dedicated service type, certain groups of -services work well together. Phabricator clusters usually do not need to be +services work well together. Phorge clusters usually do not need to be very large, so deploying a small number of hosts with multiple services is a good place to start. diff --git a/src/docs/user/cluster/cluster_daemons.diviner b/src/docs/user/cluster/cluster_daemons.diviner index 8cde3e7b7d..53495280ff 100644 --- a/src/docs/user/cluster/cluster_daemons.diviner +++ b/src/docs/user/cluster/cluster_daemons.diviner @@ -1,7 +1,7 @@ @title Cluster: Daemons @group cluster -Configuring Phabricator to use multiple daemon hosts. +Configuring Phorge to use multiple daemon hosts. Overview ======== @@ -38,7 +38,7 @@ Dedicated Daemon Hosts You can launch additional daemon hosts without any special configuration. Daemon hosts must be able to reach other hosts on the network, but do not need -to run any services (like HTTP or SSH). Simply deploy the Phabricator software +to run any services (like HTTP or SSH). Simply deploy the Phorge software and configuration and start the daemons. Normally, there is little reason to deploy dedicated daemon hosts. They can diff --git a/src/docs/user/cluster/cluster_databases.diviner b/src/docs/user/cluster/cluster_databases.diviner index 51bf14d925..c13cc2eba4 100644 --- a/src/docs/user/cluster/cluster_databases.diviner +++ b/src/docs/user/cluster/cluster_databases.diviner @@ -1,12 +1,12 @@ @title Cluster: Databases @group cluster -Configuring Phabricator to use multiple database hosts. +Configuring Phorge to use multiple database hosts. Overview ======== -You can deploy Phabricator with multiple database hosts, configured as a master +You can deploy Phorge with multiple database hosts, configured as a master and a set of replicas. The advantages of doing this are: - faster recovery from disasters by promoting a replica; @@ -15,24 +15,24 @@ and a set of replicas. The advantages of doing this are: This configuration is complex, and many installs do not need to pursue it. -If you lose the master, Phabricator can degrade automatically into read-only +If you lose the master, Phorge can degrade automatically into read-only mode and remain available, but can not fully recover without operational intervention unless the master recovers on its own. -Phabricator will not currently send read traffic to replicas unless the master +Phorge will not currently send read traffic to replicas unless the master has failed, so configuring a replica will not currently spread any load away -from the master. Future versions of Phabricator are expected to be able to +from the master. Future versions of Phorge are expected to be able to distribute some read traffic to replicas. -Phabricator can not currently be configured into a multi-master mode, nor can +Phorge can not currently be configured into a multi-master mode, nor can it be configured to automatically promote a replica to become the new master. There are no current plans to support multi-master mode or autonomous failover, although this may change in the future. -Phabricator applications //can// be partitioned across multiple database +Phorge applications //can// be partitioned across multiple database masters. This does not provide redundancy and generally does not increase resilience or resistance to data loss, but can help you scale and operate -Phabricator. For details, see +Phorge. For details, see @{article:Cluster: Partitioning and Advanced Configuration}. @@ -44,32 +44,32 @@ To begin, set up a replica database server and configure MySQL replication. If you aren't sure how to do this, refer to the MySQL manual for instructions. The MySQL documentation is comprehensive and walks through the steps and options in good detail. You should understand MySQL replication before -deploying it in production: Phabricator layers on top of it, and does not +deploying it in production: Phorge layers on top of it, and does not attempt to abstract it away. -Some useful notes for configuring replication for Phabricator: +Some useful notes for configuring replication for Phorge: -**Binlog Format**: Phabricator issues some queries which MySQL will detect as +**Binlog Format**: Phorge issues some queries which MySQL will detect as unsafe if you use the `STATEMENT` binlog format (the default). Instead, use `MIXED` (recommended) or `ROW` as the `binlog_format`. -**Grant `REPLICATION CLIENT` Privilege**: If you give the user that Phabricator +**Grant `REPLICATION CLIENT` Privilege**: If you give the user that Phorge will use to connect to the replica database server the `REPLICATION CLIENT` -privilege, Phabricator's status console can give you more information about +privilege, Phorge's status console can give you more information about replica health and state. -**Copying Data to Replicas**: Phabricator currently uses a mixture of MyISAM +**Copying Data to Replicas**: Phorge currently uses a mixture of MyISAM and InnoDB tables, so it can be difficult to guarantee that a dump is wholly consistent and suitable for loading into a replica because MySQL uses different consistency mechanisms for the different storage engines. An approach you may want to consider to limit downtime but still produce a -consistent dump is to leave Phabricator running but configured in read-only +consistent dump is to leave Phorge running but configured in read-only mode while dumping: - Stop all the daemons. - Set `cluster.read-only` to `true` and deploy the new configuration. The - web UI should now show that Phabricator is in "Read Only" mode. + web UI should now show that Phorge is in "Read Only" mode. - Dump the database. You can do this with `bin/storage dump --for-replica` to add the `--master-data` flag to the underlying command and include a `CHANGE MASTER ...` statement in the dump. @@ -81,18 +81,18 @@ binary logs on startup with the `expire_logs_days` option. If you do not configure this and do not explicitly purge old logs with `PURGE BINARY LOGS`, the binary logs on disk will grow unboundedly and relatively quickly. -Once you have a working replica, continue below to tell Phabricator about it. +Once you have a working replica, continue below to tell Phorge about it. Configuring Replicas ==================== -Once your replicas are in working order, tell Phabricator about them by +Once your replicas are in working order, tell Phorge about them by configuring the `cluster.databases` option. This option must be configured from -the command line or in configuration files because Phabricator needs to read +the command line or in configuration files because Phorge needs to read it //before// it can connect to databases. -This option value will list all of the database hosts that you want Phabricator +This option value will list all of the database hosts that you want Phorge to interact with: your master and all your replicas. Each entry in the list should have these keys: @@ -105,7 +105,7 @@ should have these keys: host. If omitted, the default from `mysql.user` will be used. - `pass`: //Optional string.// The password to use to connect to this host. If omitted, the default from `mysql.pass` will be used. - - `disabled`: //Optional bool.// If set to `true`, Phabricator will not + - `disabled`: //Optional bool.// If set to `true`, Phorge will not connect to this host. You can use this to temporarily take a host out of service. @@ -113,7 +113,7 @@ When `cluster.databases` is configured the `mysql.host` option is not used. The other MySQL connection configuration options (`mysql.port`, `mysql.user`, `mysql.pass`) are used only to provide defaults. -Once you've configured this option, restart Phabricator for the changes to take +Once you've configured this option, restart Phorge for the changes to take effect, then continue to "Monitoring Replicas" to verify the configuration. @@ -131,7 +131,7 @@ request//. If you are recovering from a disaster, the view this page shows may be partial or misleading, and two requests served by different servers may see different views of the cluster. -**Connection**: Phabricator tries to connect to each configured database, then +**Connection**: Phorge tries to connect to each configured database, then shows the result in this column. If it fails, a brief diagnostic message with details about the error is shown. If it succeeds, the column shows a rough measurement of latency from the current webserver to the database. @@ -141,7 +141,7 @@ things are properly configured and stable, the replicas should be actively replicating and no more than a few seconds behind master, and the master should //not// be replicating from another database. -To report this status, the user Phabricator is connecting as must have the +To report this status, the user Phorge is connecting as must have the `REPLICATION CLIENT` privilege (or the `SUPER` privilege) so it can run the `SHOW SLAVE STATUS` command. The `REPLICATION CLIENT` privilege only enables the user to run diagnostic commands so it should be reasonable to grant it in @@ -163,12 +163,12 @@ see an older view of the world which could be confusing for users: it may appear that their data has been lost, even if it is safe and just hasn't replicated yet. -Phabricator will attempt to prevent clients from seeing out-of-date views, but +Phorge will attempt to prevent clients from seeing out-of-date views, but sometimes sending traffic to a delayed replica is the best available option (for example, if the master can not be reached). **Health**: This column shows the result of recent health checks against the -server. After several checks in a row fail, Phabricator will mark the server +server. After several checks in a row fail, Phorge will mark the server as unhealthy and stop sending traffic to it until several checks in a row later succeed. @@ -189,12 +189,12 @@ To test that your configuration can survive a disaster, turn off the master database. Do this with great ceremony, making a cool explosion sound as you run the `mysqld stop` command. -If things have been set up properly, Phabricator should degrade to a temporary +If things have been set up properly, Phorge should degrade to a temporary read-only mode immediately. After a brief period of unresponsiveness, it will degrade further into a longer-term read-only mode. For details on how this works internally, see "Unreachable Masters" below. -Once satisfied, turn the master back on. After a brief delay, Phabricator +Once satisfied, turn the master back on. After a brief delay, Phorge should recognize that the master is healthy again and recover fully. Throughout this process, the {nav Database Servers} console will show a @@ -202,7 +202,7 @@ current view of the world from the perspective of the web server handling the request. You can use it to monitor state. You can perform a more narrow test by enabling `cluster.read-only` in -configuration. This will put Phabricator into read-only mode immediately +configuration. This will put Phorge into read-only mode immediately without turning off any databases. You can use this mode to understand which capabilities will and will not be @@ -211,7 +211,7 @@ accessible in a disaster (like wiki pages or contact information) is really accessible. See the next section, "Degradation to Read Only Mode", for more details about -when, why, and how Phabricator degrades. +when, why, and how Phorge degrades. If you run custom code or extensions, they may not accommodate read-only mode properly. You should specifically test that they function correctly in @@ -221,14 +221,14 @@ read-only mode and do not prevent you from accessing important information. Degradation to Read-Only Mode ============================= -Phabricator will degrade to read-only mode when any of these conditions occur: +Phorge will degrade to read-only mode when any of these conditions occur: - you turn it on explicitly; - you configure cluster mode, but don't set up any masters; - the master can not be reached while handling a request; or - recent attempts to connect to the master have consistently failed. -When Phabricator is running in read-only mode, users can still read data and +When Phorge is running in read-only mode, users can still read data and browse and clone repositories, but they can not edit, update, or push new changes. For example, users can still read disaster recovery information on the wiki or emergency contact information on user profiles. @@ -239,14 +239,14 @@ reasons you might want to do this include: - to test that the mode works like you expect it to; - to make sure that information you need will be available; - to prevent new writes while performing database maintenance; or - - to permanently archive a Phabricator install. + - to permanently archive a Phorge install. You can also enable this mode implicitly by configuring `cluster.databases` but disabling the master, or by not specifying any host as a master. This may be more convenient than turning it on explicitly during the course of operations work. -If Phabricator is unable to reach the master database, it will degrade into +If Phorge is unable to reach the master database, it will degrade into read-only mode automatically. See "Unreachable Masters" below for details on how this process works. @@ -258,12 +258,12 @@ the new master. See the next section, "Promoting a Replica", for details. Promoting a Replica =================== -If you lose access to the master database, Phabricator will degrade into +If you lose access to the master database, Phorge will degrade into read-only mode. This is described in greater detail below. The easiest way to get out of read-only mode is to restore the master database. If the database recovers on its own or operations staff can revive it, -Phabricator will return to full working order after a few moments. +Phorge will return to full working order after a few moments. If you can't restore the master or are unsure you will be able to restore the master quickly, you can promote a replica to become the new master instead. @@ -295,16 +295,16 @@ redundancy. Unreachable Masters =================== -This section describes how Phabricator determines that a master has been lost, +This section describes how Phorge determines that a master has been lost, marks it unreachable, and degrades into read-only mode. -Phabricator degrades into read-only mode automatically in two ways: very +Phorge degrades into read-only mode automatically in two ways: very briefly in response to a single connection failure, or more permanently in response to a series of connection failures. In the first case, if a request needs to connect to the master but is not able -to, Phabricator will temporarily degrade into read-only mode for the remainder -of that request. The alternative is to fail abruptly, but Phabricator can +to, Phorge will temporarily degrade into read-only mode for the remainder +of that request. The alternative is to fail abruptly, but Phorge can sometimes degrade successfully and still respond to the user's request, so it makes an effort to finish serving the request from replicas. @@ -314,19 +314,19 @@ if it was a read that did not actually need to use the master it may succeed. This temporary mode is intended to recover as gracefully as possible from brief interruptions in service (a few seconds), like a server being restarted, a network link becoming temporarily unavailable, or brief periods of load-related -disruption. If the anomaly is temporary, Phabricator should recover immediately +disruption. If the anomaly is temporary, Phorge should recover immediately (on the next request once service is restored). This mode can be slow for users (they need to wait on connection attempts to the master which fail) and does not reduce load on the master (requests still attempt to connect to it). -The second way Phabricator degrades is by running periodic health checks +The second way Phorge degrades is by running periodic health checks against databases, and marking them unhealthy if they fail over a longer period of time. This mechanism is very similar to the health checks that most HTTP load balancers perform against web servers. -If a database fails several health checks in a row, Phabricator will mark it as +If a database fails several health checks in a row, Phorge will mark it as unhealthy and stop sending all traffic (except for more health checks) to it. This improves performance during a service interruption and reduces load on the master, which may help it recover from load problems. @@ -336,13 +336,13 @@ console. The "Health" column shows how many checks have run recently and how many have succeeded. Health checks run every 3 seconds, and 5 checks in a row must fail or succeed -before Phabricator marks the database as healthy or unhealthy, so it will +before Phorge marks the database as healthy or unhealthy, so it will generally take about 15 seconds for a database to change state after it goes down or comes up. -If all of the recent checks fail, Phabricator will mark the database as +If all of the recent checks fail, Phorge will mark the database as unhealthy and stop sending traffic to it. If the master was the database that -was marked as unhealthy, Phabricator will actively degrade into read-only mode +was marked as unhealthy, Phorge will actively degrade into read-only mode until it recovers. This mode only attempts to connect to the unhealthy database once every few @@ -350,7 +350,7 @@ seconds to see if it is recovering, so performance will be better on average (users rarely need to wait for bad connections to fail or time out) and the database will receive less load. -Once all of the recent checks succeed, Phabricator will mark the database as +Once all of the recent checks succeed, Phorge will mark the database as healthy again and continue sending traffic to it. Health checks are tracked individually for each web server, so some web servers @@ -397,7 +397,7 @@ might be quick) without needing to restore backups (which might be very slow). Delayed replication is outside the scope of this document, but may be worth considering as an additional data security step on top of backup snapshots depending on your resources and needs. If you configure a delayed replica, do -not add it to the `cluster.databases` configuration: Phabricator should never +not add it to the `cluster.databases` configuration: Phorge should never send traffic to it, and does not need to know about it. diff --git a/src/docs/user/cluster/cluster_devices.diviner b/src/docs/user/cluster/cluster_devices.diviner index c90aa220c4..af29978345 100644 --- a/src/docs/user/cluster/cluster_devices.diviner +++ b/src/docs/user/cluster/cluster_devices.diviner @@ -6,7 +6,7 @@ Guide to configuring hosts to act as cluster devices. Cluster Context =============== -This document describes a step in configuring Phabricator to run on +This document describes a step in configuring Phorge to run on multiple hosts in a cluster configuration. This is an advanced feature. For more information on clustering, see @{article:Clustering Introduction}. @@ -38,7 +38,7 @@ Today, this is primarily necessary when configuring repository clusters. Using Almanac ============= -The tool Phabricator uses to manage cluster devices is the **Almanac** +The tool Phorge uses to manage cluster devices is the **Almanac** application, and most configuration will occur through the application's web UI. If you are not familiar with it, see @{article:Almanac User Guide} first. This document assumes you are familiar with Almanac concepts. @@ -52,7 +52,7 @@ remainder of this document walks through these points in more detail. - Create an Almanac device record for each device. - Generate, add, and trust SSH keys if necessary. - - Install Phabricator on the host. + - Install Phorge on the host. - Use `bin/almanac register` from the host to register it as a device. See below for guidance on each of these steps. @@ -76,7 +76,7 @@ setting up a small cluster (2-3 devices) for the first time. Using **shared keys** makes key management easier but safety checks won't be able to catch a few kinds of mistakes. This may be a better choice if you are setting up a larger cluster, plan to expand the cluster later, or have -experience with Phabricator clustering. +experience with Phorge clustering. Because all cluster keys are all-powerful, there is no material difference between these methods from a security or trust viewpoint. Unique keys are just @@ -87,11 +87,11 @@ easier at larger scales. Create Almanac Device Records ============================= -For each host you plan to make part of a Phabricator cluster, go to the +For each host you plan to make part of a Phorge cluster, go to the {nav Almanac} application and create a **device** record. For guidance on this application, see @{article:Almanac User Guide}. -Add **interfaces** to each device record so Phabricator can tell how to +Add **interfaces** to each device record so Phorge can tell how to connect to these hosts. Normally, you'll add one HTTP interface (usually on port 80) and one SSH interface (by default, on port 2222) to each device: @@ -107,9 +107,9 @@ up with records that look like these: Note that these hosts will normally run two `sshd` ports: the standard `sshd` which you connect to to operate and administrate the host, and the special -Phabricator `sshd` that you connect to to clone and push repositories. +Phorge `sshd` that you connect to to clone and push repositories. -You should specify the Phabricator `sshd` port, **not** the standard `sshd` +You should specify the Phorge `sshd` port, **not** the standard `sshd` port. If you're using **unique** SSH keys for each device, continue to the next step. @@ -141,8 +141,8 @@ in the web UI and run this command from the command line for each key, to mark each key as trusted: ``` -phabricator/ $ ./bin/almanac trust-key --id -phabricator/ $ ./bin/almanac trust-key --id +phorge/ $ ./bin/almanac trust-key --id +phorge/ $ ./bin/almanac trust-key --id ... ``` @@ -153,27 +153,27 @@ policy checks without requiring additional credentials. Guard them carefully! If you need to revoke trust for a key later, use `untrust-key`: ``` -phabricator/ $ ./bin/almanac untrust-key --id +phorge/ $ ./bin/almanac untrust-key --id ``` Once the keys are trusted, continue to the next step. -Install Phabricator +Install Phorge =================== -If you haven't already, install Phabricator on each device you plan to enroll +If you haven't already, install Phorge on each device you plan to enroll in the cluster. Cluster repository devices must provide services over both HTTP and SSH, so you need to install and configure both a webserver and a -Phabricator `sshd` on these hosts. +Phorge `sshd` on these hosts. Generally, you will follow whatever process you otherwise use when installing -Phabricator. +Phorge. NOTE: Do not start the daemons on the new devices yet. They won't work properly until you've finished configuring things. -Once Phabricator is installed, you can enroll the devices in the cluster by +Once Phorge is installed, you can enroll the devices in the cluster by registering them. @@ -223,7 +223,7 @@ with the trusted key. The `--identify-as` flag allows several different hosts to share the same key but still identify as different devices. The overall effect of the `bin/almanac` command is to copy identity and key -files into `phabricator/conf/keys/`. You can inspect the results by examining +files into `phorge/conf/keys/`. You can inspect the results by examining that directory. The helper script just catches potential mistakes and makes sure the process is completed correctly. diff --git a/src/docs/user/cluster/cluster_notifications.diviner b/src/docs/user/cluster/cluster_notifications.diviner index 79c89769fc..2893aefa91 100644 --- a/src/docs/user/cluster/cluster_notifications.diviner +++ b/src/docs/user/cluster/cluster_notifications.diviner @@ -1,7 +1,7 @@ @title Cluster: Notifications @group cluster -Configuring Phabricator to use multiple notification servers. +Configuring Phorge to use multiple notification servers. Overview ======== @@ -29,7 +29,7 @@ never authoritative and never the only way for users to learn about events. For example, if a notification about a task update is not delivered, the next page you load will still show the notification in your notification menu. -Generally, Phabricator works fine without notifications configured at all, so +Generally, Phorge works fine without notifications configured at all, so clustering assumes that losing some messages during a disruption is acceptable. @@ -88,7 +88,7 @@ A simple example with two servers might look like this: ``` -Configuring Phabricator +Configuring Phorge ======================= To configure clustering on the client side, add every service you run to diff --git a/src/docs/user/cluster/cluster_partitioning.diviner b/src/docs/user/cluster/cluster_partitioning.diviner index 20ae11d6a6..cfac0d02ac 100644 --- a/src/docs/user/cluster/cluster_partitioning.diviner +++ b/src/docs/user/cluster/cluster_partitioning.diviner @@ -1,12 +1,12 @@ @title Cluster: Partitioning and Advanced Configuration @group cluster -Guide to partitioning Phabricator applications across multiple database hosts. +Guide to partitioning Phorge applications across multiple database hosts. Overview ======== -You can partition Phabricator's applications across multiple databases. For +You can partition Phorge's applications across multiple databases. For example, you can move an application like Files or Maniphest to a dedicated database host. @@ -18,7 +18,7 @@ The advantages of doing this are: operating the cluster easier. This configuration is complex, and very few installs will benefit from pursuing -it. Phabricator will normally run comfortably with a single database master +it. Phorge will normally run comfortably with a single database master even for large organizations. Partitioning generally does not do much to increase resilience or make it @@ -41,10 +41,10 @@ See "Advanced Configuration", below, for additional discussion. What Partitioning Does ====================== -When you partition Phabricator, you move all of the data for one or more +When you partition Phorge, you move all of the data for one or more applications (like Maniphest) to a new master database host. This is possible -because Phabricator stores data for each application in its own logical -database (like `phabricator_maniphest`) and performs no joins between databases. +because Phorge stores data for each application in its own logical +database (like `phorge_maniphest`) and performs no joins between databases. If you're running into scale limits on a single master database, you can move one or more of your most commonly-used applications to a second database host @@ -78,7 +78,7 @@ each `replica` database follows. Here's a simple example config: { "host": "db001.corporation.com", "role": "master", - "user": "phabricator", + "user": "phorge", "pass": "hunter2!trustno1", "port": 3306, "partition": [ @@ -88,7 +88,7 @@ each `replica` database follows. Here's a simple example config: { "host": "db002.corporation.com", "role": "replica", - "user": "phabricator", + "user": "phorge", "pass": "hunter2!trustno1", "port": 3306, "master": "db001.corporation.com:3306" @@ -96,7 +96,7 @@ each `replica` database follows. Here's a simple example config: { "host": "db003.corporation.com", "role": "master", - "user": "phabricator", + "user": "phorge", "pass": "hunter2!trustno1", "port": 3306, "partition": [ @@ -108,7 +108,7 @@ each `replica` database follows. Here's a simple example config: { "host": "db004.corporation.com", "role": "replica", - "user": "phabricator", + "user": "phorge", "pass": "hunter2!trustno1", "port": 3306, "master": "db003.corporation.com:3306" @@ -137,7 +137,7 @@ configuration. To commit the configuration, run this command: ``` -phabricator/ $ ./bin/storage partition +phorge/ $ ./bin/storage partition ``` Run this command after making any partition or clustering changes. Webservers @@ -154,16 +154,16 @@ To add a new partition, follow these steps: - Add the new database to `cluster.databases`, but keep its "partition" configuration empty (just an empty list). If this is the first time you are partitioning, you will need to configure your existing master as the - new "default". This will let Phabricator interact with it, but won't send + new "default". This will let Phorge interact with it, but won't send any traffic to it yet. - Run `bin/storage partition`. - Run `bin/storage upgrade` to initialize the schemata on the new hosts. - - Stop writes to the applications you want to move by putting Phabricator + - Stop writes to the applications you want to move by putting Phorge in read-only mode, or shutting down the webserver and daemons, or telling everyone not to touch anything. - Dump the data from the application databases on the old master. - Load the data into the application databases on the new master. - - Reconfigure the "partition" setup so that Phabricator knows the databases + - Reconfigure the "partition" setup so that Phorge knows the databases have moved. - Run `bin/storage partition`. - While still in read-only mode, check that all the data appears to be @@ -178,7 +178,7 @@ end-to-end before performing a larger, higher-stakes migration. How Partitioning Works ====================== -If you have multiple masters, Phabricator keeps the entire set of schemata up +If you have multiple masters, Phorge keeps the entire set of schemata up to date on all of them. When you run `bin/storage upgrade` or other storage management commands, they generally affect all masters (if they do not, they will prompt you to be more specific). @@ -197,7 +197,7 @@ There are some exceptions to this rule. For example, all masters keep track of which patches have been applied to that particular master so that `bin/storage upgrade` can upgrade hosts correctly. -Phabricator does not perform joins across logical databases, so there are no +Phorge does not perform joins across logical databases, so there are no meaningful differences in runtime behavior if two applications are on the same physical host or different physical hosts. @@ -212,7 +212,7 @@ only one master. `persistent` //(bool)// Enables persistent connections. Defaults to off. -With persistent connections enabled, Phabricator will keep a pool of database +With persistent connections enabled, Phorge will keep a pool of database connections open between web requests and reuse them when serving subsequent requests. @@ -224,7 +224,7 @@ because requests are unable to bind to an outbound port, enabling this option is likely to fix the issue. This option may also slightly increase performance. The cost of using persistent connections is that you may need to raise the -MySQL `max_connections` setting: although Phabricator will make far fewer +MySQL `max_connections` setting: although Phorge will make far fewer connections, the connections it does make will be longer-lived. Raising this setting will increase MySQL memory requirements and may run into other limits, like `open_files_limit`, which may also need to be raised. diff --git a/src/docs/user/cluster/cluster_repositories.diviner b/src/docs/user/cluster/cluster_repositories.diviner index a2e5fd5b68..2160a73fef 100644 --- a/src/docs/user/cluster/cluster_repositories.diviner +++ b/src/docs/user/cluster/cluster_repositories.diviner @@ -1,12 +1,12 @@ @title Cluster: Repositories @group cluster -Configuring Phabricator to use multiple repository hosts. +Configuring Phorge to use multiple repository hosts. Overview ======== -If you use Git, you can deploy Phabricator with multiple repository hosts, +If you use Git, you can deploy Phorge with multiple repository hosts, configured so that each host is readable and writable. The advantages of doing this are: @@ -22,11 +22,11 @@ This configuration is not currently supported with Subversion or Mercurial. How Reads and Writes Work ========================= -Phabricator repository replicas are multi-master: every node is readable and +Phorge repository replicas are multi-master: every node is readable and writable, and a cluster of nodes can (almost always) survive the loss of any arbitrary subset of nodes so long as at least one node is still alive. -Phabricator maintains an internal version for each repository, and increments +Phorge maintains an internal version for each repository, and increments it when the repository is mutated. Before responding to a read, replicas make sure their version of the repository @@ -77,7 +77,7 @@ similar agents of other rogue nations is beyond the scope of this document. Repository Hosts ================ -Repository hosts must run a complete, fully configured copy of Phabricator, +Repository hosts must run a complete, fully configured copy of Phorge, including a webserver. They must also run a properly configured `sshd`. If you are converting existing hosts into cluster hosts, you may need to @@ -123,7 +123,7 @@ Almanac: - First, register at least one device according to the device clustering instructions. - - Create a new service of type **Phabricator Cluster: Repository** in + - Create a new service of type **Phorge Cluster: Repository** in Almanac. - Bind this service to all the interfaces on the device or devices. - For each binding, add a `protocol` key with one of these values: @@ -170,11 +170,11 @@ To migrate a repository back off a service, use this command: $ ./bin/repository clusterize --remove-service ``` -This command only changes how Phabricator connects to the repository; it does +This command only changes how Phorge connects to the repository; it does not move any data or make any complex structural changes. -When Phabricator needs information about a non-clustered repository, it just -runs a command like `git log` directly on disk. When Phabricator needs +When Phorge needs information about a non-clustered repository, it just +runs a command like `git log` directly on disk. When Phorge needs information about a clustered repository, it instead makes a service call to another server, asking that server to run `git log` instead. @@ -213,9 +213,9 @@ To expand an existing cluster, follow these general steps: For instructions on configuring and registering devices, see @{article:Cluster: Devices}. -As soon as you add active bindings to a service, Phabricator will begin +As soon as you add active bindings to a service, Phorge will begin synchronizing repositories and sending traffic to the new device. You do not -need to copy any repository data to the device: Phabricator will automatically +need to copy any repository data to the device: Phorge will automatically synchronize it. If you have a large amount of repository data, you may want to help this @@ -297,7 +297,7 @@ Configuration}. This screen shows all the configured devices which are hosting the repository and the available version on that device. -**Version**: When a repository is mutated by a push, Phabricator increases +**Version**: When a repository is mutated by a push, Phorge increases an internal version number for the repository. This column shows which version is on disk on the corresponding device. @@ -335,9 +335,9 @@ There are three major cluster failure modes: are reachable. - **Ambiguous Leaders**: The internal state of the repository is unclear. -Phabricator can detect these issues, and responds by freezing the repository +Phorge can detect these issues, and responds by freezing the repository (usually preventing all reads and writes) until the issue is resolved. These -conditions are normally rare and very little data is at risk, but Phabricator +conditions are normally rare and very little data is at risk, but Phorge errs on the side of caution and requires decisions which may result in data loss to be confirmed by a human. @@ -357,13 +357,13 @@ in a brief window during and immediately after a write. This looks like this: - During or immediately after the write, lightning strikes the server and destroys it. -Phabricator can not commit changes to a working copy (stored on disk) and to +Phorge can not commit changes to a working copy (stored on disk) and to the global state (stored in a database) atomically, so there is necessarily a narrow window between committing these two different states when some tragedy can befall a server, leaving the global and local views of the repository state possibly divergent. -In these cases, Phabricator fails into a frozen state where further writes +In these cases, Phorge fails into a frozen state where further writes are not permitted until the failure is investigated and resolved. When a repository is frozen in this way it remains readable. @@ -391,7 +391,7 @@ was complete on disk. To demote the device and release the write lock, run this command: ``` -phabricator/ $ ./bin/repository thaw --demote +phorge/ $ ./bin/repository thaw --demote ``` {icon exclamation-triangle, color="yellow"} Any committed but unacknowledged @@ -411,7 +411,7 @@ this: and destroys it. Here, all of the "leader" devices with the most up-to-date copy of the -repository have been lost. Phabricator will freeze the repository refuse to +repository have been lost. Phorge will freeze the repository refuse to serve requests because it can not serve reads consistently and can not accept new writes without data loss. @@ -424,7 +424,7 @@ quickly, you can use the monitoring console to review which changes are present on the leaders but not present on the followers by examining the push logs. -If you are comfortable discarding these changes, you can instruct Phabricator +If you are comfortable discarding these changes, you can instruct Phorge that it can forget about the leaders by doing this: - Disable the service bindings to all of the leader devices so they are no @@ -434,7 +434,7 @@ that it can forget about the leaders by doing this: To demote a device, run this command: ``` -phabricator/ $ ./bin/repository thaw rXYZ --demote repo002.corp.net +phorge/ $ ./bin/repository thaw rXYZ --demote repo002.corp.net ``` {icon exclamation-triangle, color="red"} Any data which is only present on @@ -450,7 +450,7 @@ devices by using `--demote ` and `--all-repositories`. **This is dangerous and discards all unreplicated data in any repository on any device.** ``` -phabricator/ $ ./bin/repository thaw --demote repo.corp.net --all-repositories +phorge/ $ ./bin/repository thaw --demote repo.corp.net --all-repositories ``` After you do this, continue below to promote a leader and restore the cluster @@ -474,15 +474,15 @@ error, like these: If you are moving repositories into cluster services, you can also reach this state if you use `clusterize` to associate a repository with a service that is -bound to multiple active devices. In this case, Phabricator will not know which +bound to multiple active devices. In this case, Phorge will not know which device or devices have up-to-date information. -When Phabricator can not tell which device in a cluster is a leader, it freezes +When Phorge can not tell which device in a cluster is a leader, it freezes the cluster because it is possible that some devices have less data and others have more, and if it chooses a leader arbitrarily it may destroy some data which you would prefer to retain. -To resolve this, you need to tell Phabricator which device has the most +To resolve this, you need to tell Phorge which device has the most up-to-date data and promote that device to become a leader. If you know all devices have the same data, you are free to promote any device. @@ -496,7 +496,7 @@ Once you have identified a device which has data you're happy with, use device will become authoritative: ``` -phabricator/ $ ./bin/repository thaw rXYZ --promote repo002.corp.net +phorge/ $ ./bin/repository thaw rXYZ --promote repo002.corp.net ``` {icon exclamation-triangle, color="red"} Any data which is only present on @@ -514,7 +514,7 @@ If something issues a `--force` push that destroys branch heads, the mutation will propagate to the replicas. You may be able to manually restore the branches by using tools like the -Phabricator push log or the Git reflog so it is less important to retain +Phorge push log or the Git reflog so it is less important to retain repository snapshots than database snapshots, but it is still possible for data to be lost permanently, especially if you don't notice the problem for some time. @@ -543,8 +543,8 @@ changes may either encounter conflicts or encounter problems with change propagation. You can encounter conflicts because directly modifying the working copy on disk -won't prevent users or Phabricator itself from performing writes to the same -working copy at the same time. Phabricator does not compromise the lower-level +won't prevent users or Phorge itself from performing writes to the same +working copy at the same time. Phorge does not compromise the lower-level locks provided by the VCS so this is theoretically safe -- and this rarely causes any significant problems in practice -- but doesn't make things any simpler or easier. diff --git a/src/docs/user/cluster/cluster_search.diviner b/src/docs/user/cluster/cluster_search.diviner index 25c35aa34a..4bbd1c2fbd 100644 --- a/src/docs/user/cluster/cluster_search.diviner +++ b/src/docs/user/cluster/cluster_search.diviner @@ -4,10 +4,10 @@ Overview ======== -You can configure Phabricator to connect to one or more fulltext search +You can configure Phorge to connect to one or more fulltext search services. -By default, Phabricator will use MySQL for fulltext search. This is suitable +By default, Phorge will use MySQL for fulltext search. This is suitable for most installs. However, alternate engines are supported. @@ -33,10 +33,10 @@ like this: ] ``` -When a user makes a change to a document, Phabricator writes the updated +When a user makes a change to a document, Phorge writes the updated document into every configured, writable fulltext service. -When a user issues a query, Phabricator tries configured, readable services +When a user issues a query, Phorge tries configured, readable services in order until it is able to execute the query successfully. These options are supported by all service types: @@ -141,7 +141,7 @@ After adding new search services, you will need to rebuild document indexes on them. To do this, first initialize the services: ``` -phabricator/ $ ./bin/search init +phorge/ $ ./bin/search init ``` This will perform index setup steps and other one-time configuration. @@ -149,14 +149,14 @@ This will perform index setup steps and other one-time configuration. To populate documents in all indexes, run this command: ``` -phabricator/ $ ./bin/search index --force --background --type all +phorge/ $ ./bin/search index --force --background --type all ``` This initiates an exhaustive rebuild of the document indexes. To get a more detailed list of indexing options available, run: ``` -phabricator/ $ ./bin/search help index +phorge/ $ ./bin/search help index ``` @@ -166,7 +166,7 @@ Advanced Example This is a more advanced example which shows a configuration with multiple different services in different roles. In this example: - - Phabricator is using an Elasticsearch 2 service as its primary fulltext + - Phorge is using an Elasticsearch 2 service as its primary fulltext service. - An Elasticsearch 5 service is online, but only receiving writes. - The MySQL service is serving as a backup if Elasticsearch fails. diff --git a/src/docs/user/cluster/cluster_ssh.diviner b/src/docs/user/cluster/cluster_ssh.diviner index bdd41776f5..57551309a6 100644 --- a/src/docs/user/cluster/cluster_ssh.diviner +++ b/src/docs/user/cluster/cluster_ssh.diviner @@ -1,12 +1,12 @@ @title Cluster: SSH Servers @group cluster -Configuring Phabricator to use multiple SSH servers. +Configuring Phorge to use multiple SSH servers. Overview ======== -You can run Phabricator on multiple SSH servers. The advantages of doing this +You can run Phorge on multiple SSH servers. The advantages of doing this are: - you can completely survive the loss of multiple SSH hosts. @@ -24,14 +24,14 @@ Adding SSH Hosts After configuring repositories in cluster mode, you can add more web hosts at any time. -First, deploy the Phabricator software and configuration to a host, then +First, deploy the Phorge software and configuration to a host, then register the host as a cluster device if it is not already registered (for help, see @{article:Cluster: Devices}.) Once the host is registered, start the SSH server, and then add the host to the SSH load balancer pool. -Phabricator SSH servers are stateless, so you can pull them in and out of +Phorge SSH servers are stateless, so you can pull them in and out of production freely. You may also want to run web services on these hosts, since the service is very diff --git a/src/docs/user/cluster/cluster_webservers.diviner b/src/docs/user/cluster/cluster_webservers.diviner index 7c7c3e8b1f..7483e8f6cd 100644 --- a/src/docs/user/cluster/cluster_webservers.diviner +++ b/src/docs/user/cluster/cluster_webservers.diviner @@ -1,12 +1,12 @@ @title Cluster: Web Servers @group cluster -Configuring Phabricator to use multiple web servers. +Configuring Phorge to use multiple web servers. Overview ======== -You can run Phabricator on multiple web servers. The advantages of doing this +You can run Phorge on multiple web servers. The advantages of doing this are: - you can completely survive the loss of multiple web hosts; and @@ -22,14 +22,14 @@ Adding Web Hosts After configuring repositories in cluster mode, you can add more web hosts at any time. -First, deploy the Phabricator software and configuration to a host, then +First, deploy the Phorge software and configuration to a host, then register the host as a cluster device if it is not already registered (for help, see @{article:Cluster: Devices}.) Once the host is registered, start the web server, and then add the host to the load balancer pool. -Phabricator web servers are stateless, so you can pull them in and out of +Phorge web servers are stateless, so you can pull them in and out of production freely. You may also want to run SSH services on these hosts, since the service is very diff --git a/src/docs/user/configuration/advanced_configuration.diviner b/src/docs/user/configuration/advanced_configuration.diviner index 690b51ea04..5a2d565386 100644 --- a/src/docs/user/configuration/advanced_configuration.diviner +++ b/src/docs/user/configuration/advanced_configuration.diviner @@ -1,21 +1,21 @@ @title Configuration User Guide: Advanced Configuration @group config -Configuring Phabricator for multiple environments. +Configuring Phorge for multiple environments. = Overview = -Phabricator reads configuration from multiple sources. This document explains +Phorge reads configuration from multiple sources. This document explains the configuration stack and how to set up advanced configuration sources, which may be useful for deployments with multiple environments (e.g., development and production). This is a complicated topic for advanced users. You do not need to understand -this topic to install Phabricator. +this topic to install Phorge. = Configuration Sources = -Phabricator supports the following configuration sources, from highest priority +Phorge supports the following configuration sources, from highest priority to lowest priority: - **Database**: Values are stored in the database and edited from the web UI @@ -26,14 +26,14 @@ to lowest priority: - **Config Files**: Values are stored in a config file in `conf/`. The file to use is selected by writing to `conf/local/ENVIRONMENT`, or setting the `PHABRICATOR_ENV` configuration variable. See below for more information. - - **Defaults**: Defaults hard-coded in the Phabricator source, which can not + - **Defaults**: Defaults hard-coded in the Phorge source, which can not be edited. They have the lowest priority, and all other settings override them. -Normally, you install and configure Phabricator by writing enough configuration +Normally, you install and configure Phorge by writing enough configuration into the local config to get access to the database configuration (e.g., the MySQL username, password, and hostname), then use the web interface to further -configure Phabricator. +configure Phorge. = Configuration Files = @@ -52,7 +52,7 @@ examples below. First, write an `exampleconfig.conf.php` file here (rename it according to the name you chose): - phabricator/conf/custom/exampleconfig.conf.php + phorge/conf/custom/exampleconfig.conf.php Its contents should look like this: @@ -77,14 +77,14 @@ a config like this: == Selecting a Configuration File == To select a configuration file, write the name of the file (relative to -`phabricator/conf/`) to `phabricator/conf/local/ENVIRONMENT`. For example, to -select `phabricator/conf/custom/exampleconfig.conf.php`, you would write -"custom/exampleconfig" to `phabricator/conf/local/ENVIRONMENT`: +`phorge/conf/`) to `phorge/conf/local/ENVIRONMENT`. For example, to +select `phorge/conf/custom/exampleconfig.conf.php`, you would write +"custom/exampleconfig" to `phorge/conf/local/ENVIRONMENT`: - phabricator/ $ echo custom/exampleconfig > conf/local/ENVIRONMENT - phabricator/ $ cat conf/local/ENVIRONMENT + phorge/ $ echo custom/exampleconfig > conf/local/ENVIRONMENT + phorge/ $ cat conf/local/ENVIRONMENT custom/exampleconfig - phabricator/ $ + phorge/ $ You can also set the environmental variable `PHABRICATOR_ENV`. This is more involved but may be easier in some deployment environments. Note that this needs @@ -107,8 +107,8 @@ setenv.add-environment = ( ) ``` -After creating and selecting a configuration file, restart Phabricator (for -help, see @{article:Restarting Phabricator}). Any configuration you set should +After creating and selecting a configuration file, restart Phorge (for +help, see @{article:Restarting Phorge}). Any configuration you set should take effect immediately, and your file should be visible in the Config application when examining configuration. diff --git a/src/docs/user/configuration/configuration_guide.diviner b/src/docs/user/configuration/configuration_guide.diviner index 8b221bda41..c5aa82af86 100644 --- a/src/docs/user/configuration/configuration_guide.diviner +++ b/src/docs/user/configuration/configuration_guide.diviner @@ -1,7 +1,7 @@ @title Configuration Guide @group config -This document contains basic configuration instructions for Phabricator. +This document contains basic configuration instructions for Phorge. = Prerequisites = @@ -11,7 +11,8 @@ If you haven't, see @{article:Installation Guide}. The next steps are: - Configure your webserver (Apache, nginx, or lighttpd). - - Access Phabricator with your browser. + - Configure the databases. + - Access Phorge with your browser. - Follow the instructions to complete setup. = Webserver: Configuring Apache = @@ -24,8 +25,8 @@ documentation for help. Make sure `mod_php` and `mod_rewrite` are enabled, and `mod_ssl` if you intend to set up SSL. If you haven't already, set up a domain name to point to the host you're -installing on. You can either install Phabricator on a subdomain (like -phabricator.example.com) or an entire domain, but you can not install it in +installing on. You can either install Phorge on a subdomain (like +phorge.example.com) or an entire domain, but you can not install it in some subdirectory of an existing website. Navigate to whatever domain you're going to use and make sure Apache serves you something to verify that DNS is correctly configured. @@ -33,32 +34,32 @@ is correctly configured. NOTE: The domain must contain a dot ('.'), i.e. not be just a bare name like 'http://example/'. Some web browsers will not set cookies otherwise. -Now create a VirtualHost entry for Phabricator. It should look something like +Now create a VirtualHost entry for Phorge. It should look something like this: name=httpd.conf # Change this to the domain which points to your host. - ServerName phabricator.example.com + ServerName phorge.example.com - # Change this to the path where you put 'phabricator' when you checked it - # out from GitHub when following the Installation Guide. + # Change this to the path where you put 'phorge' when you checked it + # out from the upstream when following the Installation Guide. # # Make sure you include "/webroot" at the end! - DocumentRoot /path/to/phabricator/webroot + DocumentRoot /path/to/phorge/webroot RewriteEngine on RewriteRule ^(.*)$ /index.php?__path__=$1 [B,L,QSA] If Apache isn't currently configured to serve documents out of the directory -where you put Phabricator, you may also need to add `` section. The +where you put Phorge, you may also need to add `` section. The syntax for this section depends on which version of Apache you're running. (If you don't know, you can usually figure this out by running `httpd -v`.) For Apache versions older than 2.4, use this: name="Apache Older Than 2.4" - + Order allow,deny Allow from all @@ -66,7 +67,7 @@ For Apache versions older than 2.4, use this: For Apache versions 2.4 and newer, use this: name="Apache 2.4 and Newer" - + Require all granted @@ -81,8 +82,8 @@ For nginx, use a configuration like this: name=nginx.conf server { - server_name phabricator.example.com; - root /path/to/phabricator/webroot; + server_name phorge.example.com; + root /path/to/phorge/webroot; location / { index index.php; @@ -121,8 +122,8 @@ up to their sections. For lighttpd, add a section like this to your lighttpd.conf: - $HTTP["host"] =~ "phabricator(\.example\.com)?" { - server.document-root = "/path/to/phabricator/webroot" + $HTTP["host"] =~ "phorge(\.example\.com)?" { + server.document-root = "/path/to/phorge/webroot" url.rewrite-once = ( # This simulates QSA ("query string append") mode in apache "^(/[^?]*)\?(.*)" => "/index.php?__path__=$1&$2", @@ -138,7 +139,7 @@ server.modules list: Finally, you should run the following commands to enable php support: - $ sudo apt-get install php5-cgi # for Ubuntu; other distros should be similar + $ sudo apt-get install php-cgi # for Ubuntu; other distros should be similar $ sudo lighty-enable-mod fastcgi-php Restart lighttpd after making your edits, then continue below. @@ -167,22 +168,22 @@ no one else will be able to sign up or log in. For more information, see During setup, you'll need to configure MySQL. To do this, get MySQL running and verify you can connect to it. Consult the MySQL documentation for help. When -MySQL works, you need to load the Phabricator schemata into it. To do this, run: +MySQL works, you need to load the Phorge schemata into it. To do this, run: - phabricator/ $ ./bin/storage upgrade + phorge/ $ ./bin/storage upgrade If your configuration uses an unprivileged user to connect to the database, you may have to override the default user so the schema changes can be applied with root or some other admin user: - phabricator/ $ ./bin/storage upgrade --user --password + phorge/ $ ./bin/storage upgrade --user --password You can avoid the prompt the script issues by passing the `--force` flag (for example, if you are scripting the upgrade process). - phabricator/ $ ./bin/storage upgrade --force + phorge/ $ ./bin/storage upgrade --force -NOTE: When you update Phabricator, run `storage upgrade` again to apply any +NOTE: When you update Phorge, run `storage upgrade` again to apply any new updates. = Next Steps = @@ -200,7 +201,7 @@ Continue by: @{article:Configuring a Preamble Script}; or - configuring where uploaded files and attachments will be stored with @{article:Configuring File Storage}; or - - configuring Phabricator so it can send mail with + - configuring Phorge so it can send mail with @{article:Configuring Outbound Email}; or - configuring inbound mail with @{article:Configuring Inbound Email}; or - importing repositories with @{article:Diffusion User Guide}; or @@ -209,4 +210,4 @@ Continue by: @{article:Notifications User Guide: Setup and Configuration}; or - configuring backups with @{article:Configuring Backups and Performing Migrations}; or - - contributing to Phabricator with @{article:Contributor Introduction}. + - contributing to Phorge with @{article:Contributor Introduction}. diff --git a/src/docs/user/configuration/configuration_locked.diviner b/src/docs/user/configuration/configuration_locked.diviner index 57ed76c5c7..19b06ea424 100644 --- a/src/docs/user/configuration/configuration_locked.diviner +++ b/src/docs/user/configuration/configuration_locked.diviner @@ -24,7 +24,7 @@ Locked Configuration can edit it from the CLI instead, with `bin/config`: ``` -phabricator/ $ ./bin/config set +phorge/ $ ./bin/config set ``` Some configuration options take complicated values which can be difficult @@ -42,7 +42,7 @@ file: Then, set it with `--stdin` like this: ``` -phabricator/ $ ./bin/config set --stdin < config.json +phorge/ $ ./bin/config set --stdin < config.json ``` A few settings have alternate CLI tools. Refer to the setting page for @@ -57,13 +57,13 @@ locked include: **Required for bootstrapping**: Some options, like `mysql.host`, must be -available before Phabricator can read configuration from the database. +available before Phorge can read configuration from the database. -If you stored `mysql.host` only in the database, Phabricator would not know how +If you stored `mysql.host` only in the database, Phorge would not know how to connect to the database in order to read the value in the first place. These options must be provided in a configuration source which is read earlier -in the bootstrapping process, before Phabricator connects to the database. +in the bootstrapping process, before Phorge connects to the database. **Errors could not be fixed from the web UI**: Some options, like @@ -83,7 +83,7 @@ attacker who has gained access to an administrator account in order to gain greater access. For example, an attacker who could modify `cluster.mailers` (and other -similar options), could potentially reconfigure Phabricator to send mail +similar options), could potentially reconfigure Phorge to send mail through an evil server they controlled, then trigger password resets on other user accounts to compromise them. @@ -105,8 +105,8 @@ administrator accounts) from reading them. You can review (and edit) hidden configuration from the CLI: ``` -phabricator/ $ ./bin/config get -phabricator/ $ ./bin/config set +phorge/ $ ./bin/config get +phorge/ $ ./bin/config set ``` @@ -117,12 +117,12 @@ Locked Configuration With Database Values You may receive a setup issue warning you that a locked configuration key has a value set in the database. Most commonly, this is because: - - In some earlier version of Phabricator, this configuration was not locked. + - In some earlier version of Phorge, this configuration was not locked. - In the past, you or some other administrator used the web UI to set a value. This value was written to the database. - In a later version of the software, the value became locked. -When Phabricator was originally released, locked configuration did not yet +When Phorge was originally released, locked configuration did not yet exist. Locked configuration was introduced later, and then configuration options were gradually locked for a long time after that. @@ -134,10 +134,10 @@ to lock the value. Locking values was more common in the past, and it is now relatively rare for an unlocked value to become locked: when new values are introduced, they are generally locked or hidden appropriately. In most cases, this setup issue only -affects installs that have used Phabricator for a long time. +affects installs that have used Phorge for a long time. -At time of writing (February 2019), Phabricator currently respects these old -database values. However, some future version of Phabricator will refuse to +At time of writing (February 2019), Phorge currently respects these old +database values. However, some future version of Phorge will refuse to read locked configuration from the database, because this improves security if an attacker manages to find a way to bypass restrictions on editing locked configuration from the web UI. @@ -147,19 +147,19 @@ you should move these configuration values from the database to a local config file. Usually, you'll do this by first copying the value from the database: ``` -phabricator/ $ ./bin/config get +phorge/ $ ./bin/config get ``` ...into local configuration: ``` -phabricator/ $ ./bin/config set +phorge/ $ ./bin/config set ``` ...and then removing the database value: ``` -phabricator/ $ ./bin/config delete --database +phorge/ $ ./bin/config delete --database ``` See @{Configuration User Guide: Advanced Configuration} for some more detailed diff --git a/src/docs/user/configuration/configuring_accounts_and_registration.diviner b/src/docs/user/configuration/configuring_accounts_and_registration.diviner index e703c46402..5d28b3342c 100644 --- a/src/docs/user/configuration/configuring_accounts_and_registration.diviner +++ b/src/docs/user/configuration/configuring_accounts_and_registration.diviner @@ -1,12 +1,12 @@ @title Configuring Accounts and Registration @group config -Describes how to configure user access to Phabricator. +Describes how to configure user access to Phorge. Overview ======== -Phabricator supports a number of login systems. You can enable or disable these +Phorge supports a number of login systems. You can enable or disable these systems to configure who can register for and access your install, and how users with existing accounts can login. @@ -18,7 +18,7 @@ support logging in with other credentials. For example: - **LDAP:** Users use LDAP credentials to log in or register. - **OAuth:** Users use accounts on a supported OAuth2 provider (like GitHub, Facebook, or Google) to log in or register. - - **Other Providers:** More providers are available, and Phabricator + - **Other Providers:** More providers are available, and Phorge can be extended with custom providers. See the "Auth" application for a list of available providers. @@ -26,14 +26,14 @@ By default, no providers are enabled. You must use the "Auth" application to add one or more providers after you complete the installation process. After you add a provider, you can link it to existing accounts (for example, -associate an existing Phabricator account with a GitHub OAuth account) or users +associate an existing Phorge account with a GitHub OAuth account) or users can use it to register new accounts (assuming you enable these options). Recovering Inaccessible Accounts ================================ -If you accidentally lock yourself out of Phabricator (for example, by disabling +If you accidentally lock yourself out of Phorge (for example, by disabling all authentication providers), you can normally use the "send a login link" action from the login screen to email yourself a login link and regain access to your account. @@ -43,7 +43,7 @@ can use the `bin/auth` script to recover access to an account. To recover access, run: ``` -phabricator/ $ ./bin/auth recover +phorge/ $ ./bin/auth recover ``` ...where `` is the account username you want to recover access diff --git a/src/docs/user/configuration/configuring_backups.diviner b/src/docs/user/configuration/configuring_backups.diviner index 0e851d01ac..e4e088aa8e 100644 --- a/src/docs/user/configuration/configuring_backups.diviner +++ b/src/docs/user/configuration/configuring_backups.diviner @@ -1,21 +1,21 @@ @title Configuring Backups and Performing Migrations @group config -Advice for backing up Phabricator, or migrating from one machine to another. +Advice for backing up Phorge, or migrating from one machine to another. Overview ======== -Phabricator does not currently have a comprehensive backup system, but creating -backups is not particularly difficult and Phabricator does have a few basic +Phorge does not currently have a comprehensive backup system, but creating +backups is not particularly difficult and Phorge does have a few basic tools which can help you set up a reasonable process. In particular, the things which needs to be backed up are: - the MySQL databases; - hosted repositories; - uploaded files; and - - your Phabricator configuration files. + - your Phorge configuration files. This document discusses approaches for backing up this data. @@ -24,25 +24,25 @@ same steps you would if you were creating a backup and then restoring it, you will just backup the old machine and then restore the data onto the new machine. -WARNING: You need to restart Phabricator after restoring data. +WARNING: You need to restart Phorge after restoring data. -Restarting Phabricator after performing a restore makes sure that caches are +Restarting Phorge after performing a restore makes sure that caches are flushed properly. For complete instructions, see -@{article:Restarting Phabricator}. +@{article:Restarting Phorge}. Backup: MySQL Databases ======================= -Most of Phabricator's data is stored in MySQL, and it's the most important thing +Most of Phorge's data is stored in MySQL, and it's the most important thing to back up. You can run `bin/storage dump` to get a dump of all the MySQL databases. This is a convenience script which just runs a normal `mysqldump`, -but will only dump databases Phabricator owns. +but will only dump databases Phorge owns. Since most of this data is compressible, it may be helpful to run it through gzip prior to storage. For example: - phabricator/ $ ./bin/storage dump --compress --output backup.sql.gz + phorge/ $ ./bin/storage dump --compress --output backup.sql.gz Then store the backup somewhere safe, like in a box buried under an old tree stump. No one will ever think to look for it there. @@ -59,7 +59,7 @@ to uncompress it first, if you compressed it prior to storage.) Backup: Hosted Repositories =========================== -If you host repositories in Phabricator, you should back them up. You can use +If you host repositories in Phorge, you should back them up. You can use `bin/repository list-paths` to show the local paths on disk for each repository. To back them up, copy them elsewhere. @@ -87,11 +87,11 @@ need to do any additional work. **Amazon S3**: If you use Amazon S3, redundancy and backups are built in to the service. This is probably sufficient for most installs. If you trust Amazon with your data //except not really//, you can backup your S3 bucket outside of -Phabricator. +Phorge. **Local Disk**: If you use the local disk storage engine, you'll need to back up files manually. You can do this by creating a copy of the root directory where -you told Phabricator to put files (the `storage.local-disk.path` configuration +you told Phorge to put files (the `storage.local-disk.path` configuration setting). For more information about configuring how files are stored, see @@ -108,15 +108,15 @@ Backup: Configuration Files =========================== You should also backup your configuration files, and any scripts you use to -deploy or administrate Phabricator (like a customized upgrade script). The best +deploy or administrate Phorge (like a customized upgrade script). The best way to do this is to check them into a private repository somewhere and just use whatever backup process you already have in place for repositories. Just copying them somewhere will work fine too, of course. -In particular, you should backup this configuration file which Phabricator +In particular, you should backup this configuration file which Phorge creates: - phabricator/conf/local/local.json + phorge/conf/local/local.json This file contains all of the configuration settings that have been adjusted by using `bin/config set `. @@ -126,14 +126,14 @@ Restore: Configuration Files ============================ To restore configuration files, just copy them into the right locations. Copy -your backup of `local.json` to `phabricator/conf/local/local.json`. +your backup of `local.json` to `phorge/conf/local/local.json`. Security ======== -MySQL dumps have no builtin encryption and most data in Phabricator is stored in +MySQL dumps have no builtin encryption and most data in Phorge is stored in a raw, accessible form, so giving a user access to backups is a lot like giving -them shell access to the machine Phabricator runs on. In particular, a user who +them shell access to the machine Phorge runs on. In particular, a user who has the backups can: - read data that policies do not permit them to see; @@ -141,7 +141,7 @@ has the backups can: - read other users' session and conduit tokens and impersonate them. Some of this information is durable, so disclosure of even a very old backup may -present a risk. If you restrict access to the Phabricator host or database, you +present a risk. If you restrict access to the Phorge host or database, you should also restrict access to the backups. diff --git a/src/docs/user/configuration/configuring_encryption.diviner b/src/docs/user/configuration/configuring_encryption.diviner index 36315506e6..e237573432 100644 --- a/src/docs/user/configuration/configuring_encryption.diviner +++ b/src/docs/user/configuration/configuring_encryption.diviner @@ -6,14 +6,14 @@ Setup guide for configuring encryption. Overview ======== -Phabricator supports at-rest encryption of uploaded file data stored in the +Phorge supports at-rest encryption of uploaded file data stored in the "Files" application. Configuring at-rest file data encryption does not encrypt any other data or resources. In particular, it does not encrypt the database and does not encrypt Passphrase credentials. -Attackers who compromise a Phabricator host can read the master key and decrypt +Attackers who compromise a Phorge host can read the master key and decrypt the data. In most configurations, this does not represent a significant barrier above and beyond accessing the file data. Thus, configuring at-rest encryption is primarily useful for two types of installs: @@ -81,7 +81,7 @@ Format: Raw Data The `raw` storage format is automatically selected for all newly uploaded file data if no key is marked as the `default` key in the keyring. This is -the behavior of Phabricator if you haven't configured anything. +the behavior of Phorge if you haven't configured anything. This format stores raw data without modification. @@ -104,7 +104,7 @@ length, then base64 encoded when represented in `keyring`. You can generate a valid, properly encoded AES256 master key with this command: ``` -phabricator/ $ ./bin/files generate-key --type aes-256-cbc +phorge/ $ ./bin/files generate-key --type aes-256-cbc ``` This mode is generally similar to the default server-side encryption mode @@ -134,7 +134,7 @@ default. To change the format of an individual file, run this command: ``` -phabricator/ $ ./bin/files encode --as F123 [--key ] +phorge/ $ ./bin/files encode --as F123 [--key ] ``` This will change the storage format of the specified file. @@ -167,7 +167,7 @@ to leave the old key in place for now so existing data can be decrypted. To cycle an individual file, run this command: ``` -phabricator/ $ ./bin/files cycle F123 +phorge/ $ ./bin/files cycle F123 ``` Verify that cycling worked properly by examining the command output and @@ -177,7 +177,7 @@ can cycle additional files to gain additional confidence. You can cycle all files with this command: ``` -phabricator/ $ ./bin/files cycle --all +phorge/ $ ./bin/files cycle --all ``` Once all files have been cycled, remove the old master key from the keyring. diff --git a/src/docs/user/configuration/configuring_file_domain.diviner b/src/docs/user/configuration/configuring_file_domain.diviner index 6f7c410435..a54e850eb1 100644 --- a/src/docs/user/configuration/configuring_file_domain.diviner +++ b/src/docs/user/configuration/configuring_file_domain.diviner @@ -6,7 +6,7 @@ Setup guide for an alternate file domain or CDN. Overview ======== -Serving files that users upload from the same domain that Phabricator runs on +Serving files that users upload from the same domain that Phorge runs on is a security risk. In general, doing this creates a risk that users who have permission to upload @@ -17,7 +17,7 @@ history of security issues). The attacker can then trick another user into executing the file and gain access to their session. The best way to mitigate this threat is to serve files from a separate domain. -For example, if Phabricator is hosted at `https://phabricator.example.com/`, +For example, if Phorge is hosted at `https://phorge.example.com/`, you can serve files from `https://files.exampleusercontent.com/`. The alternate file domain should be a completely different domain from your @@ -48,7 +48,7 @@ CloudFront is a CDN service that's part of Amazon Web Services. It makes particular sense to use if you're hosting your install in AWS. To configure it, set up a new CloudFront distribution which is pointed at -your Phabricator install as an origin (make sure you point it at the primary +your Phorge install as an origin (make sure you point it at the primary domain name of your install, not just a load balancer or instance). You do not need to set up a new domain name, which makes setup a bit more straightforward. @@ -57,10 +57,10 @@ the **Allowed HTTP Methods** setting from `GET, HEAD` to `GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE`. Once configured, accessing the distribution's domain name should return a -Phabricator error page indicating that Phabricator does not recognize the +Phorge error page indicating that Phorge does not recognize the domain. If you see this page, it means you've configured things correctly. -Continue to "Configuring Phabricator", below. +Continue to "Configuring Phorge", below. Approach: CloudFlare ======== @@ -68,7 +68,7 @@ Approach: CloudFlare WARNING: You should review all your CloudFlare settings, and be very sure to turn off all JavaScript, HTML, CSS minification and optimization features, including systems like "Rocket Loader". These -features will break Phabricator in strange and mysterious ways that +features will break Phorge in strange and mysterious ways that are unpredictable. Only allow CloudFlare to cache files, and never optimize them. @@ -76,23 +76,23 @@ optimize them. To set up CloudFlare, you'll need to register a second domain and go through their enrollment process to host the alternate domain on their servers. Use a -CNAME record to forward a subdomain to your Phabricator install. +CNAME record to forward a subdomain to your Phorge install. CloudFlare will automatically generate SSL certificates for hosted domains, which can significantly reduce the cost and complexity of setup. Once configured, accessing the CNAME-forwarded subdomain should return a -Phabricator error page indicating that Phabricator does not recognize the +Phorge error page indicating that Phorge does not recognize the domain. If you see this page, it means you've configured things correctly. -Continue to "Configuring Phabricator", below. +Continue to "Configuring Phorge", below. Approach: Self Hosted ======== To do this, just set up a second domain exactly like your primary domain is -set up. When setup is complete, visiting the domain should return a Phabricator -error page indicating that Phabricator does not recognize the domain. This +set up. When setup is complete, visiting the domain should return a Phorge +error page indicating that Phorge does not recognize the domain. This means that you've configured things correctly. Note that if you use SSL (which you should), you'll also need to get a @@ -102,18 +102,18 @@ You can also configure a self-hosted domain to route through a caching server to provide some of the performance benefits of a CDN, but this is advanced and outside the scope of this documentation. -Continue to "Configuring Phabricator", below. +Continue to "Configuring Phorge", below. -Configuring Phabricator +Configuring Phorge ======== -After you've set up a CDN or an alternate domain, configure Phabricator to +After you've set up a CDN or an alternate domain, configure Phorge to recognize the domain. Run this command, providing the domain you have configured in place of the `` token. You should include the protocol, -so an example domain might be `https://cdn.phabcdn.net/`. +so an example domain might be `https://cdn.examplecdn.com/`. - phabricator/ $ ./bin/config set security.alternate-file-domain + phorge/ $ ./bin/config set security.alternate-file-domain -Phabricator should now serve CSS, JS, images, profile pictures, and user +Phorge should now serve CSS, JS, images, profile pictures, and user content through the file domain. You can verify this with "View Source" or by downloading a file and checking the URL. diff --git a/src/docs/user/configuration/configuring_file_storage.diviner b/src/docs/user/configuration/configuring_file_storage.diviner index d6abb22c13..6d7fb247b9 100644 --- a/src/docs/user/configuration/configuring_file_storage.diviner +++ b/src/docs/user/configuration/configuring_file_storage.diviner @@ -6,8 +6,8 @@ Setup file storage and support for large files. Overview ======== -This document describes how to configure Phabricator to support large file -uploads, and how to choose where Phabricator stores files. +This document describes how to configure Phorge to support large file +uploads, and how to choose where Phorge stores files. There are two major things to configure: @@ -17,16 +17,16 @@ There are two major things to configure: The following sections will guide you through this configuration. -How Phabricator Stores Files +How Phorge Stores Files ============================ -Phabricator stores files in "storage engines", which are modular backends +Phorge stores files in "storage engines", which are modular backends that implement access to some storage system (like MySQL, the filesystem, or a cloud storage service like Amazon S3). -Phabricator stores large files by breaking them up into many chunks (a few +Phorge stores large files by breaking them up into many chunks (a few megabytes in size) and storing the chunks in an underlying storage engine. -This makes it easier to implement new storage engines and gives Phabricator +This makes it easier to implement new storage engines and gives Phorge more flexibility in managing file data. The first section of this document discusses configuring your install so that @@ -45,7 +45,7 @@ of the stack. Generally, the minimum value of all the limits is the effective one. To upload large files, you need to increase all the limits to at least -**32MB**. This will allow you to upload file chunks, which will let Phabricator +**32MB**. This will allow you to upload file chunks, which will let Phorge store arbitrarily large files. The settings which limit file uploads are: @@ -62,20 +62,20 @@ somewhat larger than the desired maximum filesize. - **lighttpd**: lighttpd limits requests with the lighttpd `server.max-request-size` directive. -Set the applicable limit to at least **32MB**. Phabricator can not read these +Set the applicable limit to at least **32MB**. Phorge can not read these settings, so it can not raise setup warnings if they are misconfigured. **PHP**: PHP has several directives which limit uploads. These directives are found in `php.ini`. - **post_max_size**: Maximum POST request size PHP will accept. If you - exceed this, Phabricator will give you a useful error. This often defaults - to `8M`. Set this to at least `32MB`. Phabricator will give you a setup + exceed this, Phorge will give you a useful error. This often defaults + to `8M`. Set this to at least `32MB`. Phorge will give you a setup warning about this if it is set too low. - **memory_limit**: For some uploads, file data will be read into memory - before Phabricator can adjust the memory limit. If you exceed this, PHP + before Phorge can adjust the memory limit. If you exceed this, PHP may give you a useful error, depending on your configuration. It is - recommended that you set this to `-1` to disable it. Phabricator will + recommended that you set this to `-1` to disable it. Phorge will give you a setup warning about this if it is set too low. You may also want to configure these PHP options: @@ -87,7 +87,7 @@ You may also want to configure these PHP options: - **upload_max_filesize**: Maximum file size PHP will accept in a raw file upload. This is not normally used when uploading files via drag-and-drop, but affects some other kinds of file uploads. If you exceed this, - Phabricator will give you a useful error. This often defaults to `2M`. Set + Phorge will give you a useful error. This often defaults to `2M`. Set this to at least `32MB`. Once you've adjusted all this configuration, your server will be able to @@ -98,7 +98,7 @@ enable you to store arbitrarily large files. Storage Engines =============== -Phabricator supports several different file storage engines: +Phorge supports several different file storage engines: | Engine | Setup | Cost | Notes | |--------|-------|------|-------| @@ -110,7 +110,7 @@ Phabricator supports several different file storage engines: You can review available storage engines and their configuration by navigating to {nav Applications > Files > Help/Options > Storage Engines} in the web UI. -By default, Phabricator is configured to store files up to 1MB in MySQL, and +By default, Phorge is configured to store files up to 1MB in MySQL, and reject files larger than 1MB. To store larger files, you can either: - increase the MySQL limit to at least 8MB; or @@ -172,7 +172,7 @@ Testing Storage Engines ======================= You can test that things are correctly configured by dragging and dropping -a file onto the Phabricator home page. If engines have been configured +a file onto the Phorge home page. If engines have been configured properly, the file should upload. Migrating Files Between Engines @@ -183,11 +183,11 @@ script to perform migrations. For example, suppose you previously used MySQL but recently set up S3 and want to migrate all your files there. First, migrate one file to make sure things work: - phabricator/ $ ./bin/files migrate --engine amazon-s3 F12345 + phorge/ $ ./bin/files migrate --engine amazon-s3 F12345 If that works properly, you can then migrate everything: - phabricator/ $ ./bin/files migrate --engine amazon-s3 --all + phorge/ $ ./bin/files migrate --engine amazon-s3 --all You can use `--dry-run` to show which migrations would be performed without taking any action. Run `bin/files help` for more options and information. diff --git a/src/docs/user/configuration/configuring_inbound_email.diviner b/src/docs/user/configuration/configuring_inbound_email.diviner index 39982b10cd..75338f149e 100644 --- a/src/docs/user/configuration/configuring_inbound_email.diviner +++ b/src/docs/user/configuration/configuring_inbound_email.diviner @@ -2,15 +2,15 @@ @group config This document contains instructions for configuring inbound email, so users -may interact with some Phabricator applications via email. +may interact with some Phorge applications via email. Preamble ======== -Phabricator can process inbound mail in two general ways: +Phorge can process inbound mail in two general ways: **Handling Replies**: When users reply to email notifications about changes, -Phabricator can turn email into comments on the relevant discussion thread. +Phorge can turn email into comments on the relevant discussion thread. **Creating Objects**: You can configure an address like `bugs@yourcompany.com` to create new objects (like tasks) when users send email. @@ -21,7 +21,7 @@ broader set of changes to objects beyond commenting. (For example, you can use To configure inbound mail, you will generally: - - Configure some mail domain to submit mail to Phabricator for processing. + - Configure some mail domain to submit mail to Phorge for processing. - For handling replies, set `metamta.reply-handler-domain` in your configuration. - For handling email that creates objects, configure inbound addresses in the @@ -34,11 +34,11 @@ Configuration Overview ====================== Usually, the most challenging part of configuring inbound mail is getting mail -delivered to Phabricator for processing. This step can be made much easier if -you use a third-party mail service which can submit mail to Phabricator via +delivered to Phorge for processing. This step can be made much easier if +you use a third-party mail service which can submit mail to Phorge via webhooks. -Some available approaches for delivering mail to Phabricator are: +Some available approaches for delivering mail to Phorge are: | Receive Mail With | Setup | Cost | Notes | |--------|-------|------|-------| @@ -47,37 +47,37 @@ Some available approaches for delivering mail to Phabricator are: | Mailgun | Easy | Cheap | Discouraged | | Local MTA | Difficult | Free | Discouraged | -The remainder of this document walks through configuring Phabricator to +The remainder of this document walks through configuring Phorge to receive mail, and then configuring your chosen transport to deliver mail -to Phabricator. +to Phorge. Configuring "Reply" Email ========================= -By default, Phabricator uses a `noreply@phabricator.example.com` email address +By default, Phorge uses a `noreply@phorge.example.com` email address as the "From" address when it sends mail. The exact address it uses can be configured with `metamta.default-address`. -When a user takes an action that generates mail, Phabricator sets the -"Reply-To" addresss for the mail to that user's name and address. This means +When a user takes an action that generates mail, Phorge sets the +"Reply-To" address for the mail to that user's name and address. This means that users can reply to email to discuss changes, but: the conversation won't -be recorded in Phabricator; and users will not be able to use email commands +be recorded in Phorge; and users will not be able to use email commands to take actions or make edits. -To change this behavior so that users can interact with objects in Phabricator +To change this behavior so that users can interact with objects in Phorge over email, change the configuration key `metamta.reply-handler-domain` to some domain you configure according to the instructions below, e.g. -`phabricator.example.com`. Once you set this key, email will use a -"Reply-To" like `T123+273+af310f9220ad@phabricator.example.com`, which -- when +`phorge.example.com`. Once you set this key, email will use a +"Reply-To" like `T123+273+af310f9220ad@phorge.example.com`, which -- when configured correctly, according to the instructions below -- will parse incoming email and allow users to interact with Differential revisions, Maniphest tasks, etc. over email. -If you don't want Phabricator to take up an entire domain (or subdomain) you +If you don't want Phorge to take up an entire domain (or subdomain) you can configure a general prefix so you can use a single mailbox to receive mail on. To make use of this set `metamta.single-reply-handler-prefix` to the -prefix of your choice, and Phabricator will prepend this to the "Reply-To" +prefix of your choice, and Phorge will prepend this to the "Reply-To" mail address. This works because everything up to the first (optional) '+' character in an email address is considered the receiver, and everything after is essentially ignored. @@ -87,7 +87,7 @@ Configuring "Create" Email ========================== You can set up application email addresses to allow users to create objects via -email. For example, you could configure `bugs@phabricator.example.com` to +email. For example, you could configure `bugs@phorge.example.com` to create a Maniphest task out of any email which is sent to it. You can find application email settings for each application at: @@ -104,7 +104,7 @@ with the `[ Content source ]` and/or `[ Receiving email address ]` fields to route or handle objects based on which address mail was sent to. You'll also need to configure the actual mail domain to submit mail to -Phabricator by following the instructions below. Phabricator will let you add +Phorge by following the instructions below. Phorge will let you add any address as an application address, but can only process mail which is actually delivered to it. @@ -130,7 +130,7 @@ authenticating senders in the general case (e.g., where you are an open source project and need to interact with users whose email accounts you have no control over). -You can also set `metamta.public-replies`, which will change how Phabricator +You can also set `metamta.public-replies`, which will change how Phorge delivers email. Instead of sending each recipient a unique mail with a personal reply-to address, it will send a single email to everyone with a public reply-to address. This decreases security because anyone who can spoof a "From" address @@ -142,7 +142,7 @@ not received an email about an object can not blindly interact with it. If you enable application email addresses, those addresses also use the weaker "From" authentication mechanism. -NOTE: Phabricator does not currently attempt to verify "From" addresses because +NOTE: Phorge does not currently attempt to verify "From" addresses because this is technically complex, seems unreasonably difficult in the general case, and no installs have had a need for it yet. If you have a specific case where a reasonable mechanism exists to provide sender verification (e.g., DKIM @@ -155,17 +155,17 @@ Testing and Debugging Inbound Email =================================== You can use the `bin/mail` utility to test and review inbound mail. This can -help you determine if mail is being delivered to Phabricator or not: +help you determine if mail is being delivered to Phorge or not: - phabricator/ $ ./bin/mail list-inbound # List inbound messages. - phabricator/ $ ./bin/mail show-inbound # Show details about a message. + phorge/ $ ./bin/mail list-inbound # List inbound messages. + phorge/ $ ./bin/mail show-inbound # Show details about a message. You can also test receiving mail, but note that this just simulates receiving the mail and doesn't send any information over the network. It is primarily aimed at developing email handlers: it will still work properly if your inbound email configuration is incorrect or even disabled. - phabricator/ $ ./bin/mail receive-test # Receive test message. + phorge/ $ ./bin/mail receive-test # Receive test message. Run `bin/mail help ` for detailed help on using these commands. @@ -179,13 +179,13 @@ like this: - Configure a mail domain according to Mailgun's instructions. - Add a Mailgun route with a `catch_all()` rule which takes the action - `forward("https://phabricator.example.com/mail/mailgun/")`. Replace the + `forward("https://phorge.example.com/mail/mailgun/")`. Replace the example domain with your actual domain. - Configure a mailer in `cluster.mailers` with your Mailgun API key. Use of Mailgun is discouraged because of concerns that they may not be a -trustworthy custodian of sensitive data. See for -discussion and context. +trustworthy custodian of sensitive data. +See for discussion and context. Postmark Setup ============== @@ -194,7 +194,7 @@ To process inbound mail from Postmark, configure this URI as your inbound webhook URI in the Postmark control panel: ``` -https:///mail/postmark/ +https:///mail/postmark/ ``` See also the Postmark section in @{article:Configuring Outbound Email} for @@ -209,21 +209,21 @@ To use SendGrid, you need a SendGrid account with access to the "Parse API" for inbound email. Provided you have such an account, configure it like this: - Configure an MX record according to SendGrid's instructions, i.e. add - `phabricator.example.com MX 10 mx.sendgrid.net.` or similar. + `phorge.example.com MX 10 mx.sendgrid.net.` or similar. - Go to the "Parse Incoming Emails" page on SendGrid () and add the domain as the "Hostname". - - Add the URL `https://phabricator.example.com/mail/sendgrid/` as the "Url", + - Add the URL `https://phorge.example.com/mail/sendgrid/` as the "Url", using your domain (and HTTP instead of HTTPS if you are not configured with SSL). - If you get an error that the hostname "can't be located or verified", it means your MX record is either incorrectly configured or hasn't propagated yet. - - Set `metamta.reply-handler-domain` to `phabricator.example.com` + - Set `metamta.reply-handler-domain` to `phorge.example.com` (whatever you configured the MX record for). That's it! If everything is working properly you should be able to send email -to `anything@phabricator.example.com` and it should appear in +to `anything@phorge.example.com` and it should appear in `bin/mail list-inbound` within a few seconds. @@ -267,10 +267,10 @@ probably means something like this: - add your host to /etc/mail/local-host-names; and - restart sendmail. -Now, you can actually configure sendmail to deliver to Phabricator. In +Now, you can actually configure sendmail to deliver to Phorge. In `/etc/aliases`, add an entry like this: - phabricator: "| /path/to/phabricator/scripts/mail/mail_handler.php" + phorge: "| /path/to/phorge/scripts/mail/mail_handler.php" If you use the `PHABRICATOR_ENV` environmental variable to select a configuration, you can pass the value to the script as an argument: @@ -283,12 +283,12 @@ without an argument. After making this change, run `sudo newaliases`. Now you likely need to symlink this script into `/etc/smrsh/`: - sudo ln -s /path/to/phabricator/scripts/mail/mail_handler.php /etc/smrsh/ + sudo ln -s /path/to/phorge/scripts/mail/mail_handler.php /etc/smrsh/ Finally, edit `/etc/mail/virtusertable` and add an entry like this: - @yourdomain.com phabricator@localhost + @yourdomain.com phorge@localhost -That will forward all mail to @yourdomain.com to the Phabricator processing +That will forward all mail to @yourdomain.com to the Phorge processing script. Run `sudo /etc/mail/make` or similar and then restart sendmail with `sudo /etc/init.d/sendmail restart`. diff --git a/src/docs/user/configuration/configuring_outbound_email.diviner b/src/docs/user/configuration/configuring_outbound_email.diviner index 9acf3abe10..91c8962b59 100644 --- a/src/docs/user/configuration/configuring_outbound_email.diviner +++ b/src/docs/user/configuration/configuring_outbound_email.diviner @@ -1,17 +1,17 @@ @title Configuring Outbound Email @group config -Instructions for configuring Phabricator to send email and other types of +Instructions for configuring Phorge to send email and other types of messages, like text messages. Overview ======== -Phabricator sends outbound messages through "mailers". Most mailers send +Phorge sends outbound messages through "mailers". Most mailers send email and most messages are email messages, but mailers may also send other types of messages (like text messages). -Phabricator can send outbound messages through multiple different mailers, +Phorge can send outbound messages through multiple different mailers, including a local mailer or various third-party services. Options include: | Send Mail With | Setup | Cost | Inbound | Media | Notes | @@ -41,7 +41,7 @@ options. If you have some internal mail or messaging service you'd like to use you can also write a custom mailer, but this requires digging into the code. -Phabricator sends mail in the background, so the daemons need to be running for +Phorge sends mail in the background, so the daemons need to be running for it to be able to deliver mail. You should receive setup warnings if they are not. For more information on using daemons, see @{article:Managing Daemons with phd}. @@ -50,10 +50,10 @@ not. For more information on using daemons, see Outbound "From" and "To" Addresses ================================== -When Phabricator sends outbound mail, it must select some "From" address to +When Phorge sends outbound mail, it must select some "From" address to send mail from, since mailers require this. -When mail only has "CC" recipients, Phabricator generates a dummy "To" address, +When mail only has "CC" recipients, Phorge generates a dummy "To" address, since some mailers require this and some users write mail rules that depend on whether they appear in the "To" or "CC" line. @@ -65,12 +65,12 @@ contrast, if the address is a real user address, that user will receive a lot of mail they probably don't want. If you plan to configure //inbound// mail later, you usually don't need to do -anything. Phabricator will automatically create a `noreply@` mailbox which +anything. Phorge will automatically create a `noreply@` mailbox which works the right way (accepts and discards all mail it receives) and automatically use it when generating addresses. If you don't plan to configure inbound mail, you may need to configure an -address for Phabricator to use. You can do this by setting +address for Phorge to use. You can do this by setting `metamta.default-address`. @@ -131,7 +131,7 @@ It also supports these local mailers: - `smtp`: Connect directly to an SMTP server. - `test`: Internal mailer for testing. Does not send mail. -You can also write your own mailer by extending `PhabricatorMailAdapter`. +You can also write your own mailer by extending `PhorgeMailAdapter`. The `media` field supports these values: @@ -169,7 +169,7 @@ tricky because of shell escaping. The easiest way to do it is to use the Then set the value like this: ``` -phabricator/ $ ./bin/config set --stdin cluster.mailers < mailers.json +phorge/ $ ./bin/config set --stdin cluster.mailers < mailers.json ``` For alternatives and more information on configuration, see @@ -222,8 +222,8 @@ Mailer: Mailgun |---------| Use of Mailgun is discouraged because of concerns that they may not be a -trustworthy custodian of sensitive data. See for -discussion and context. +trustworthy custodian of sensitive data. +See for discussion and context. Mailgun is a third-party email delivery service. You can learn more at . Mailgun is easy to configure and works well. @@ -315,7 +315,7 @@ SendGrid is a third-party email delivery service. You can learn more at . You can configure SendGrid in two ways: you can send via SMTP or via the REST -API. To use SMTP, configure Phabricator to use an `smtp` mailer. +API. To use SMTP, configure Phorge to use an `smtp` mailer. To use the REST API mailer, set `type` to `sendgrid`, then configure these `options`: @@ -348,7 +348,7 @@ do any of this, strongly consider using Postmark instead. To use this mailer, set `type` to `sendmail`, then configure these `options`: - - `message-id`: Optional bool. Set to `false` if Phabricator will not be + - `message-id`: Optional bool. Set to `false` if Phorge will not be able to select a custom "Message-ID" header when sending mail via this mailer. See "Message-ID Headers" below. @@ -370,7 +370,7 @@ To use this mailer, set `type` to `smtp`, then configure these `options`: - `password`: Optional string. Password for authentication. - `protocol`: Optional string. Set to `tls` or `ssl` if necessary. Use `ssl` for Gmail. - - `message-id`: Optional bool. Set to `false` if Phabricator will not be + - `message-id`: Optional bool. Set to `false` if Phorge will not be able to select a custom "Message-ID" header when sending mail via this mailer. See "Message-ID Headers" below. @@ -395,9 +395,9 @@ Testing and Debugging Outbound Email You can use the `bin/mail` utility to test, debug, and examine outbound mail. In particular: - phabricator/ $ ./bin/mail list-outbound # List outbound mail. - phabricator/ $ ./bin/mail show-outbound # Show details about messages. - phabricator/ $ ./bin/mail send-test # Send test messages. + phorge/ $ ./bin/mail list-outbound # List outbound mail. + phorge/ $ ./bin/mail show-outbound # Show details about messages. + phorge/ $ ./bin/mail send-test # Send test messages. Run `bin/mail help ` for more help on using these commands. @@ -411,12 +411,12 @@ You can monitor daemons using the Daemon Console (`/daemon/`, or click Priorities ========== -By default, Phabricator will try each mailer in order: it will try the first +By default, Phorge will try each mailer in order: it will try the first mailer first. If that fails (for example, because the service is not available at the moment) it will try the second mailer, and so on. If you want to load balance between multiple mailers instead of using one as -a primary, you can set `priority`. Phabricator will start with mailers in the +a primary, you can set `priority`. Phorge will start with mailers in the highest priority group and go through them randomly, then fall back to the next group. @@ -446,14 +446,14 @@ like this: } ``` -Phabricator will start with servers in the highest priority group (the group +Phorge will start with servers in the highest priority group (the group with the **largest** `priority` number). In this example, the highest group is `300`, which has the two SMTP servers. They'll be tried in random order first. -If both fail, Phabricator will move on to the next priority group. In this +If both fail, Phorge will move on to the next priority group. In this example, there are no other priority groups. -If it still hasn't sent the mail, Phabricator will try servers which are not +If it still hasn't sent the mail, Phorge will try servers which are not in any priority group, in the configured order. In this example there is only one such server, so it will try to send via Postmark. @@ -462,14 +462,14 @@ Message-ID Headers ================== Email has a "Message-ID" header which is important for threading messages -correctly in mail clients. Normally, Phabricator is free to select its own +correctly in mail clients. Normally, Phorge is free to select its own "Message-ID" header values for mail it sends. However, some mailers (including Amazon SES) do not allow selection of custom "Message-ID" values and will ignore or replace the "Message-ID" in mail that is submitted through them. -When Phabricator adds other mail headers which affect threading, like +When Phorge adds other mail headers which affect threading, like "In-Reply-To", it needs to know if its "Message-ID" headers will be respected or not to select header values which will produce good threading behavior. If we guess wrong and think we can set a "Message-ID" header when we can't, you @@ -498,11 +498,11 @@ we can set a "Message-ID" header. If the outbound pathway does not actually allow selection of a "Message-ID" header, you can set the `message-id` option on the mailer to `false` to tell -Phabricator that it should not assume it can select a value for this header. +Phorge that it should not assume it can select a value for this header. For example, if you are sending mail via a local Postfix server which then forwards the mail to Amazon SES (a service which does not allow selection of -a "Message-ID" header), your `smtp` configuration in Phabricator should +a "Message-ID" header), your `smtp` configuration in Phorge should specify `"message-id": false`. diff --git a/src/docs/user/configuration/configuring_preamble.diviner b/src/docs/user/configuration/configuring_preamble.diviner index 6b6b9da149..15ae20e75b 100644 --- a/src/docs/user/configuration/configuring_preamble.diviner +++ b/src/docs/user/configuration/configuring_preamble.diviner @@ -6,14 +6,14 @@ Adjust environmental settings (SSL, remote IPs) using a preamble script. Overview ======== -If Phabricator is deployed in an environment where HTTP headers behave oddly +If Phorge is deployed in an environment where HTTP headers behave oddly (usually, because it is behind a load balancer), it may not be able to detect some environmental features (like the client's IP, or the presence of SSL) correctly. You can use a special preamble script to make arbitrary adjustments to the -environment and some parts of Phabricator's configuration in order to fix these -problems and set up the environment which Phabricator expects. +environment and some parts of Phorge's configuration in order to fix these +problems and set up the environment which Phorge expects. Creating a Preamble Script @@ -21,15 +21,15 @@ Creating a Preamble Script To create a preamble script, write a file to: - phabricator/support/preamble.php + phorge/support/preamble.php -(This file is in Phabricator's `.gitignore`, so you do not need to worry about +(This file is in Phorge's `.gitignore`, so you do not need to worry about colliding with `git` or interacting with updates.) This file should be a valid PHP script. If you aren't very familiar with PHP, you can check for syntax errors with `php -l`: - phabricator/ $ php -l support/preamble.php + phorge/ $ php -l support/preamble.php No syntax errors detected in support/preamble.php If present, this script will be executed at the very beginning of each web @@ -40,14 +40,14 @@ examples, see the next sections. Adjusting Client IPs ==================== -If your install is behind a load balancer, Phabricator may incorrectly detect +If your install is behind a load balancer, Phorge may incorrectly detect all requests as originating from the load balancer, rather than from the correct client IPs. In common cases where networks are configured like this, the `X-Forwarded-For` header will have trustworthy information about the real client IP. You can use the function `preamble_trust_x_forwarded_for_header()` in your -preamble to tell Phabricator that you expect to receive requests from a +preamble to tell Phorge that you expect to receive requests from a load balancer or proxy which modifies this header: ```name="Trust X-Forwarded-For Header", lang=php @@ -89,9 +89,9 @@ if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { Adjusting SSL ============= -If your install is behind an SSL terminating load balancer, Phabricator may +If your install is behind an SSL terminating load balancer, Phorge may detect requests as HTTP when the client sees them as HTTPS. This can cause -Phabricator to generate links with the wrong protocol, issue cookies without +Phorge to generate links with the wrong protocol, issue cookies without the SSL-only flag, or reject requests outright. To fix this, you can set `$_SERVER['HTTPS']` explicitly: @@ -103,7 +103,7 @@ name=Explicitly Configure SSL Availability $_SERVER['HTTPS'] = true; ``` -You can also set this value to `false` to explicitly tell Phabricator that a +You can also set this value to `false` to explicitly tell Phorge that a request is not an SSL request. diff --git a/src/docs/user/configuration/custom_fields.diviner b/src/docs/user/configuration/custom_fields.diviner index 75d83fc8ba..5879931317 100644 --- a/src/docs/user/configuration/custom_fields.diviner +++ b/src/docs/user/configuration/custom_fields.diviner @@ -5,7 +5,7 @@ How to add custom fields to applications which support them. = Overview = -Several Phabricator applications allow the configuration of custom fields. These +Several Phorge applications allow the configuration of custom fields. These fields allow you to add more information to objects, and in some cases reorder or remove builtin fields. @@ -134,7 +134,7 @@ The `strings` value supports different strings per control type. They are: - **search.default** Text for the search interface, defaults to "(Any)". - **search.require** Text for the search interface, defaults to "Require". -Internally, Phabricator implements some additional custom field types and +Internally, Phorge implements some additional custom field types and options. These are not intended for general use and are subject to abrupt change, but are documented here for completeness: @@ -149,7 +149,7 @@ change, but are documented here for completeness: If you want custom fields to have advanced behaviors (sophisticated rendering, advanced validation, complicated controls, interaction with other systems, etc), -you can write a custom field as an extension and add it to Phabricator. +you can write a custom field as an extension and add it to Phorge. NOTE: This API is somewhat new and fairly large. You should expect that there will be occasional changes to the API requiring minor updates in your code. @@ -167,9 +167,9 @@ want to add a field to: | Projects | @{class:PhabricatorProjectCustomField} | The easiest way to get started is to drop your subclass into -`phabricator/src/extensions/`. If Phabricator is configured in development +`phorge/src/extensions/`. If Phorge is configured in development mode, the class should immediately be available in the UI. If not, you can -restart Phabricator (for help, see @{article:Restarting Phabricator}). +restart Phorge (for help, see @{article:Restarting Phorge}). For example, this is a simple template which adds a custom field to Maniphest: @@ -211,6 +211,6 @@ integrations, see the base class for your application and Continue by: - - learning more about extending Phabricator with custom code in - @{article@phabcontrib:Adding New Classes}; + - learning more about extending Phorge with custom code in + @{article@contrib:Adding New Classes}; - or returning to the @{article: Configuration Guide}. diff --git a/src/docs/user/configuration/managing_caches.diviner b/src/docs/user/configuration/managing_caches.diviner index e873b99d8c..c4e209fc25 100644 --- a/src/docs/user/configuration/managing_caches.diviner +++ b/src/docs/user/configuration/managing_caches.diviner @@ -1,17 +1,17 @@ @title Managing Caches @group config -Discusses Phabricator caches and cache management. +Discusses Phorge caches and cache management. Overview ======== -Phabricator uses various caches to improve performance, similar to the caches +Phorge uses various caches to improve performance, similar to the caches a web browser uses to improve web performance. In particular, blocks of text which are expensive to render (like formatted text and syntax highlighted code) are often cached after they're rendered for -the first time. When they're rendered again, Phabricator can read the cache +the first time. When they're rendered again, Phorge can read the cache instead of recomputing the result. Because text is cached, you may continue to see the old result even after you @@ -28,20 +28,20 @@ you can just ignore the out of date caches and they'll fix themselves eventually (usually within 30 days). If you don't want to wait, you can purge the caches. This will remove any -cached data and force Phabricator to recompute the results. +cached data and force Phorge to recompute the results. Purging Caches ============== -If you need to purge Phabricator's caches, you can use the CLI tool. Run it +If you need to purge Phorge's caches, you can use the CLI tool. Run it with the `--help` flag to see options: - phabricator/ $ ./bin/cache purge --help + phorge/ $ ./bin/cache purge --help This tool can purge caches in a granular way, but it's normally easiest to just purge all of the caches: - phabricator/ $ ./bin/cache purge --all + phorge/ $ ./bin/cache purge --all You can purge caches safely. The data they contain can always be rebuilt from -other data if Phabricator needs it. +other data if Phorge needs it. diff --git a/src/docs/user/configuration/managing_daemons.diviner b/src/docs/user/configuration/managing_daemons.diviner index cf2ba85ea2..d4af3162a4 100644 --- a/src/docs/user/configuration/managing_daemons.diviner +++ b/src/docs/user/configuration/managing_daemons.diviner @@ -1,11 +1,11 @@ @title Managing Daemons with phd @group config -Explains Phabricator daemons and the daemon control program `phd`. +Explains Phorge daemons and the daemon control program `phd`. = Overview = -Phabricator uses daemons (background processing scripts) to handle a number of +Phorge uses daemons (background processing scripts) to handle a number of tasks: - tracking repositories, discovering new commits, and importing and parsing @@ -16,19 +16,19 @@ tasks: Daemons are started and stopped with **phd** (the **Ph**abricator **D**aemon launcher). Daemons can be monitored via a web console. -You do not need to run daemons for most parts of Phabricator to work, but some +You do not need to run daemons for most parts of Phorge to work, but some features (principally, repository tracking with Diffusion) require them and several features will benefit in performance or stability if you configure daemons. = phd = -**phd** is a command-line script (located at `phabricator/bin/phd`). To get +**phd** is a command-line script (located at `phorge/bin/phd`). To get a list of commands, run `phd help`: - phabricator/ $ ./bin/phd help + phorge/ $ ./bin/phd help NAME - phd - phabricator daemon launcher + phd - phorge daemon launcher ... Generally, you will use: @@ -43,9 +43,65 @@ If you want finer-grained control, you can use: - **phd launch** to launch individual daemons; and - **phd debug** to debug problems with daemons. -NOTE: When you upgrade Phabricator or change configuration, you should restart +NOTE: When you upgrade Phorge or change configuration, you should restart the daemons by running `phd restart`. +Automatically start phd +======================= + +NOTE: If you are opinionated against systemd, cover the eyes of your children +right now!1! + +Computers are good in automatically starting stuff, thanks to the invention +of the "init system". + +Phorge virtually supports any init system. Which one is yours? Don't worry. +If you don't know, it's systemd. + +We propose a minimal systemd configuration file, following some assumptions: + +- your lovely Phorge is installed `/somewhere` +- you have a database service called `mariadb` +- you have a dedicated Unix user called `daemon-user` - coming from + @{article:Diffusion User Guide} + +With the above assumptions, create this configuration file as super-user: + +```lang=ini,name=/etc/systemd/system/phorge-phd.service +[Unit] +Description=Phorge Daemons +Documentation=https://we.phorge.it/book/phorge/article/managing_daemons/ +After=syslog.target network.target mariadb.service + +[Service] +Type=forking +User=daemon-user +Group=daemon-user +ExecStart=/somewhere/phorge/bin/phd start +ExecStop=/somewhere/phorge/bin/phd stop + +[Install] +WantedBy=multi-user.target +``` + +To install this new systemd configuration, execute these commands as +super-user: + +``` +systemctl daemon-reload +systemctl enable --now phorge-phd +``` + +Now the process has started and will survive after any reboot. + +To check if everything is OK: + +``` +systemctl status phorge-phd +``` + +Anything else can be explored in depth by reading the systemd documentation. + = Daemon Console = You can view status and debugging information for daemons in the Daemon Console @@ -81,12 +137,12 @@ launch custom daemons, or launch special daemons like the IRC bot. To debug a daemon, use `phd debug`: - phabricator/bin/ $ ./phd debug + phorge/bin/ $ ./phd debug You can pass arguments like this (normal arguments are passed to the daemon control mechanism, not to the daemon itself): - phabricator/bin/ $ ./phd debug -- --flavor apple + phorge/bin/ $ ./phd debug -- --flavor apple In debug mode, daemons do not daemonize, and they print additional debugging output to the console. This should make it easier to debug problems. You can @@ -94,7 +150,7 @@ terminate the daemon with `^C`. To launch a nonstandard daemon, use `phd launch`: - phabricator/bin/ $ ./phd launch + phorge/bin/ $ ./phd launch This daemon will daemonize and run normally. @@ -109,7 +165,7 @@ This daemon will daemonize and run normally. just those started with `phd start`. If you're writing a restart script, have it launch any custom daemons explicitly after `phd restart`. - You can write your own daemons and manage them with `phd` by extending - @{class:PhabricatorDaemon}. See @{article@phabcontrib:Adding New Classes}. + @{class:PhabricatorDaemon}. See @{article@contrib:Adding New Classes}. - See @{article:Diffusion User Guide} for details about tuning the repository daemon. @@ -128,4 +184,4 @@ Continue by: - learning about the repository daemon with @{article:Diffusion User Guide}; or - - writing your own daemons with @{article@phabcontrib:Adding New Classes}. + - writing your own daemons with @{article@contrib:Adding New Classes}. diff --git a/src/docs/user/configuration/managing_garbage.diviner b/src/docs/user/configuration/managing_garbage.diviner index 0b18bd2a0a..5c5ef87762 100644 --- a/src/docs/user/configuration/managing_garbage.diviner +++ b/src/docs/user/configuration/managing_garbage.diviner @@ -6,7 +6,7 @@ Understanding and configuring garbage collection. Overview ======== -Phabricator generates various logs and caches during normal operation. Some of +Phorge generates various logs and caches during normal operation. Some of these logs and caches are usually of very little use after some time has passed, so they are deleted automatically (often after a month or two) in a process called "garbage collection". @@ -35,7 +35,7 @@ You can review the current retention policies in `bin/garbage set-policy` to select a new policy: ``` -phabricator/ $ ./bin/garbage set-policy --collector cache.markup --days 7 +phorge/ $ ./bin/garbage set-policy --collector cache.markup --days 7 ``` You can use `--days` to select how long data is retained for. You can also use @@ -53,7 +53,7 @@ Troubleshooting You can manually run a collector with `bin/garbage collect`. ``` -phabricator/ $ ./bin/garbage collect --collector cache.general +phorge/ $ ./bin/garbage collect --collector cache.general ``` By using the `--trace` flag, you can inspect the operation of the collector diff --git a/src/docs/user/configuration/notifications.diviner b/src/docs/user/configuration/notifications.diviner index 13d93317f9..de1a32d5e1 100644 --- a/src/docs/user/configuration/notifications.diviner +++ b/src/docs/user/configuration/notifications.diviner @@ -6,11 +6,11 @@ Guide to setting up notifications. Overview ======== -By default, Phabricator delivers information about events (like users creating +By default, Phorge delivers information about events (like users creating tasks or commenting on code reviews) through email and in-application notifications. -Phabricator can also be configured to deliver notifications in real time, by +Phorge can also be configured to deliver notifications in real time, by popping up a message in any open browser windows if something has happened or an object has been updated. @@ -41,13 +41,13 @@ The notification server uses Node.js, so you'll need to install it first. To install Node.js, follow the instructions on [[ http://nodejs.org | nodejs.org ]]. -You will also need to install the `ws` module for Node. This needs to be +You will also need to install the dependencies for Node. This needs to be installed into the notification server directory: - phabricator/ $ cd support/aphlict/server/ - phabricator/support/aphlict/server/ $ npm install ws + phorge/ $ cd support/aphlict/server/ + phorge/support/aphlict/server/ $ npm install -Once Node.js and the `ws` module are installed, you're ready to start the +Once Node.js and its dependencies are installed, you're ready to start the server. @@ -57,21 +57,21 @@ Running the Aphlict Server After installing Node.js, you can control the notification server with the `bin/aphlict` command. To start the server: - phabricator/ $ bin/aphlict start + phorge/ $ bin/aphlict start By default, the server must be able to listen on port `22280`. If you're using a host firewall (like a security group in EC2), make sure traffic can reach the server. The server configuration is controlled by a configuration file, which is -separate from Phabricator's configuration settings. The default file can -be found at `phabricator/conf/aphlict/aphlict.default.json`. +separate from Phorge's configuration settings. The default file can +be found at `phorge/conf/aphlict/aphlict.default.json`. To make adjustments to the default configuration, either copy this file to create `aphlict.custom.json` in the same directory (this file will be used if it exists) or specify a configuration file explicitly with the `--config` flag: - phabricator/ $ bin/aphlict start --config path/to/config.json + phorge/ $ bin/aphlict start --config path/to/config.json The configuration file has these settings: @@ -118,12 +118,12 @@ installs. For more information on how to configure a cluster, see The defaults are appropriate for simple cases, but you may need to adjust them if you are running a more complex configuration. -Configuring Phabricator +Configuring Phorge ======================= -After starting the server, configure Phabricator to connect to it by adjusting +After starting the server, configure Phorge to connect to it by adjusting `notification.servers`. This configuration option should have a list of servers -that Phabricator should interact with. +that Phorge should interact with. Normally, you'll list one client server and one admin server, like this: @@ -131,7 +131,7 @@ Normally, you'll list one client server and one admin server, like this: [ { "type": "client", - "host": "phabricator.mycompany.com", + "host": "phorge.mycompany.com", "port": 22280, "protocol": "https" }, @@ -165,7 +165,7 @@ Troubleshooting You can run `aphlict` in the foreground to get output to your console: - phabricator/ $ ./bin/aphlict debug + phorge/ $ ./bin/aphlict debug Because the notification server uses WebSockets, your browser error console may also have information that is useful in figuring out what's wrong. @@ -178,10 +178,10 @@ information that is useful in resolving issues. SSL and HTTPS ============= -If you serve Phabricator over HTTPS, you must also serve websockets over HTTPS. +If you serve Phorge over HTTPS, you must also serve websockets over HTTPS. Browsers will refuse to connect to `ws://` websockets from HTTPS pages. -If a client connects to Phabricator over HTTPS, Phabricator will automatically +If a client connects to Phorge over HTTPS, Phorge will automatically select an appropriate HTTPS service from `notification.servers` and instruct the browser to open a websocket connection with `wss://`. @@ -241,10 +241,10 @@ upstream websocket_pool { } ``` -```lang=nginx, name=/etc/nginx/sites-enabled/phabricator.example.com.conf +```lang=nginx, name=/etc/nginx/sites-enabled/phorge.example.com.conf server { - server_name phabricator.example.com; - root /path/to/phabricator/webroot; + server_name phorge.example.com; + root /path/to/phorge/webroot; // ... @@ -260,10 +260,10 @@ server { With this approach, you should make these additional adjustments: -**Phabricator Configuration**: The entry in `notification.servers` with type +**Phorge Configuration**: The entry in `notification.servers` with type `"client"` should have these adjustments made: - - Set `host` to the Phabricator host. + - Set `host` to the Phorge host. - Set `port` to the standard HTTPS port (usually `443`). - Set `protocol` to `"https"`. - Set `path` to `/ws/`, so it matches the special `location` in your diff --git a/src/docs/user/configuration/storage_adjust.diviner b/src/docs/user/configuration/storage_adjust.diviner index 2403cd3fed..e460ddd041 100644 --- a/src/docs/user/configuration/storage_adjust.diviner +++ b/src/docs/user/configuration/storage_adjust.diviner @@ -6,13 +6,13 @@ Explains how to apply storage adjustments to the MySQL schemata. Overview ======== -Phabricator uses a workflow called //storage adjustment// to make some minor +Phorge uses a workflow called //storage adjustment// to make some minor kinds of changes to the MySQL schema. This workflow compliments the //storage upgrade// workflow, which makes major changes. You can perform storage adjustment by running: - phabricator/ $ ./bin/storage adjust + phorge/ $ ./bin/storage adjust This document describes what adjustments are, how they relate to storage upgrades, how to perform them, and how to troubleshoot issues with storage @@ -22,7 +22,7 @@ adjustment. Understanding Adjustments =================== -Storage adjustments make minor changes to the Phabricator MySQL schemata to +Storage adjustments make minor changes to the Phorge MySQL schemata to improve consistency, unicode handling, and performance. Changes covered by adjustment include: @@ -62,7 +62,7 @@ adjustment workflow can resolve. You can also review adjustments from the CLI, by running: - phabricator/ $ ./bin/storage adjust + phorge/ $ ./bin/storage adjust Before you're prompted to actually apply adjustments, you'll be given a list of available adjustments. You can then make a choice to apply them. @@ -73,11 +73,11 @@ Performing Adjustments To perform adjustments, run the `adjust` workflow: - phabricator/ $ ./bin/storage adjust + phorge/ $ ./bin/storage adjust For details about flags, use: - phabricator/ $ ./bin/storage help adjust + phorge/ $ ./bin/storage help adjust You do not normally need to run this workflow manually: it will be run automatically after you run the `upgrade` workflow. @@ -97,7 +97,7 @@ set can safely store 4-byte unicode characters. The adjustment workflow allows us to alter the schema to primarily use `binary` character sets on older MySQL, and primarily use `utf8mb4` character -sets on newer MySQL. The net effect is that Phabricator works consistently and +sets on newer MySQL. The net effect is that Phorge works consistently and can store 4-byte unicode characters regardless of the MySQL version. Under newer MySQL, we can also take advantage of the better collation rules the `utf8mb4` character set offers. @@ -143,7 +143,7 @@ upstream. In general, adjustments are not critical. If you run into issues applying adjustments, it is safe to file a task in the upstream describing the problem -you've encountered and continue using Phabricator normally until the issue can +you've encountered and continue using Phorge normally until the issue can be resolved. Surplus Schemata @@ -154,13 +154,13 @@ After performing adjustment, you may receive an error that a table or column is | Target | Error | | --- | --- | -| phabricator_example.example_table | Surplus | +| phorge_example.example_table | Surplus | -Generally, "Surplus" means that Phabricator does not expect the table or column +Generally, "Surplus" means that Phorge does not expect the table or column to exist. These surpluses usually exist because you (or someone else with database access) added the table or column manually. Rarely, they can also exist for other reasons. They are usually safe to delete, but because -deleting them destroys data and Phabricator can not be sure that the table or +deleting them destroys data and Phorge can not be sure that the table or column doesn't have anything important in it, it does not delete them automatically. @@ -174,18 +174,18 @@ message): ```lang=sql CREATE DATABASE my_backups; -RENAME TABLE phabricator_example.example_table +RENAME TABLE phorge_example.example_table TO my_backups.example_table; ``` -Phabricator will ignore tables that aren't in databases it owns, so you can -safely move anything you aren't sure about outside of the Phabricator databases. +Phorge will ignore tables that aren't in databases it owns, so you can +safely move anything you aren't sure about outside of the Phorge databases. If you're sure you don't need a table, use `DROP TABLE` to destroy it, specifying the correct table name (the one given in the error message): ```lang=sql -DROP TABLE phabricator_example.example_table; +DROP TABLE phorge_example.example_table; ``` This will destroy the table permanently. diff --git a/src/docs/user/configuration/troubleshooting_https.diviner b/src/docs/user/configuration/troubleshooting_https.diviner index bdc3439d7d..b3aae81035 100644 --- a/src/docs/user/configuration/troubleshooting_https.diviner +++ b/src/docs/user/configuration/troubleshooting_https.diviner @@ -5,7 +5,7 @@ Detailed instructions for troubleshooting HTTPS connection problems. = Overview = -If you're having trouble connecting to an HTTPS install of Phabricator, and +If you're having trouble connecting to an HTTPS install of Phorge, and particularly if you're receiving a "There was an error negotiating the SSL connection." error, this document may be able to help you diagnose and resolve the problem. @@ -17,7 +17,7 @@ Connection negotiation can fail for several reasons. The major ones are: certificates). - The SSL certificate is signed for the wrong domain. For example, a certificate signed for `www.example.com` will not work for - `phabricator.example.com`. + `phorge.example.com`. - The server rejects TLSv1 SNI connections for the domain (this is complicated, see below). @@ -50,7 +50,7 @@ with: $ openssl x509 -text -in If the certificate was accidentally generated for, e.g. `www.example.com` but -you installed Phabricator on `phabricator.example.com`, you need to generate a +you installed Phorge on `phorge.example.com`, you need to generate a new certificate for the right domain. = SNI Problems = @@ -73,7 +73,7 @@ an error in `SSL23_GET_SERVER_HELLO` with `reason(1112)`, like this: /SourceCache/OpenSSL098/OpenSSL098-44/src/ssl/s23_clnt.c:602: ...it indicates server is misconfigured. The most common cause of this problem -is an Apache server that does not explicitly name the Phabricator domain as a +is an Apache server that does not explicitly name the Phorge domain as a valid VirtualHost. This error occurs only for some versions of the OpenSSL client library diff --git a/src/docs/user/feedback.diviner b/src/docs/user/feedback.diviner deleted file mode 100644 index 0998002b10..0000000000 --- a/src/docs/user/feedback.diviner +++ /dev/null @@ -1,7 +0,0 @@ -@title Give Feedback! Get Support! -@short Feedback/Support -@group cellar - -Deprecated. - -This article has moved to @{article:Support Resources}. diff --git a/src/docs/user/field/conduit_changes.diviner b/src/docs/user/field/conduit_changes.diviner index 96a890600a..b2246310e3 100644 --- a/src/docs/user/field/conduit_changes.diviner +++ b/src/docs/user/field/conduit_changes.diviner @@ -25,7 +25,7 @@ Methods have one of three statuses: them before they stabilize. - **Stable**: This is an established method which generally will not change. - **Deprecated**: This method will be removed in a future version of - Phabricator and callers should cease using it. + Phorge and callers should cease using it. Normally, a method is deprecated only when it is obsolete or a new, more powerful method is available to replace it. diff --git a/src/docs/user/field/darkconsole.diviner b/src/docs/user/field/darkconsole.diviner index 065be2d8f1..494b175416 100644 --- a/src/docs/user/field/darkconsole.diviner +++ b/src/docs/user/field/darkconsole.diviner @@ -6,9 +6,9 @@ Enabling and using the built-in debugging and performance console. Overview ======== -DarkConsole is a debugging console built into Phabricator which exposes +DarkConsole is a debugging console built into Phorge which exposes configuration, performance and error information. It can help you detect, -understand and resolve bugs and performance problems in Phabricator +understand and resolve bugs and performance problems in Phorge applications. diff --git a/src/docs/user/field/exit_codes.diviner b/src/docs/user/field/exit_codes.diviner index 7c69b3509b..40f1d281e3 100644 --- a/src/docs/user/field/exit_codes.diviner +++ b/src/docs/user/field/exit_codes.diviner @@ -1,7 +1,7 @@ @title Command Line Exit Codes @group fieldmanual -Explains the use of exit codes in Phabricator command line scripts. +Explains the use of exit codes in Phorge command line scripts. Overview ======== @@ -21,7 +21,7 @@ like `cmdx && cmdy` operate on exit codes. The code `0` means success. Other codes signal some sort of error or status condition, depending on the system and command. -With rare exception, Phabricator uses //all other codes// to signal +With rare exception, Phorge uses //all other codes// to signal **catastrophic failure**. This is an explicit architectural decision and one we are unlikely to deviate @@ -35,10 +35,10 @@ they are not appropriate for communicating application state in a modern operational environment. This document explains the reasoning behind our use of exit codes in more detail. -In particular, this approach is informed by a focus on operating Phabricator +In particular, this approach is informed by a focus on operating Phorge clusters at scale. This is not a common deployment scenario, but we consider it the most important one. Our use of exit codes makes it easier to deploy and -operate a Phabricator cluster at larger scales. It makes it slightly harder to +operate a Phorge cluster at larger scales. It makes it slightly harder to deploy and operate a small cluster or single host by gluing together `bash` scripts. We are willingly trading the small scale away for advantages at larger scales. @@ -56,11 +56,10 @@ operations environment faces different forces than the interactive shell did in the 1970s, particularly at scale. We consider correctness to be very important to modern operations environments. -In particular, we manage a Phabricator cluster (Phacility) and believe that -having reliable, repeatable processes for provisioning, configuration and -deployment is critical to maintaining and scaling our operations. Our use of -exit codes makes it easier to implement processes that are correct and reliable -on top of Phabricator management scripts. +In particular, we believe that having reliable, repeatable processes for +provisioning, configuration and deployment is critical to maintaining and +scaling our operations. Our use of exit codes makes it easier to implement +processes that are correct and reliable on top of Phorge management scripts. Exit codes as signals for application state are problematic because they are ambiguous: you can't use them to distinguish between dissimilar failure states @@ -228,13 +227,13 @@ particular, ease of use in a `bash` environment is not a compelling motivation. We are broadly willing to make output machine parseable or provide an explicit machine output mode (often a `--json` flag) if there is a reasonable use case -for it. However, we operate a large production cluster of Phabricator instances +for it. However, we operate a large production cluster of Phorge instances with the tools available in the upstream, so the lack of machine parseable output is not sufficient to motivate adding such output on its own: we also need to understand the problem you're facing, and why it isn't a problem we face. A simpler or cleaner approach to the problem may already exist. -If you just want to write `bash` scripts on top of Phabricator scripts and you +If you just want to write `bash` scripts on top of Phorge scripts and you are unswayed by these concerns, you can often just build a composite command to get roughly the same effect that you'd get out of an exit code. diff --git a/src/docs/user/field/performance.diviner b/src/docs/user/field/performance.diviner index c5980acd0e..09e7023fb9 100644 --- a/src/docs/user/field/performance.diviner +++ b/src/docs/user/field/performance.diviner @@ -23,18 +23,18 @@ Performance and the Upstream ============================ Performance issues and hangs will often require upstream involvement to fully -resolve. The intent is for Phabricator to perform well in all reasonable cases, +resolve. The intent is for Phorge to perform well in all reasonable cases, not require tuning for different workloads (as long as those workloads are generally reasonable). Poor performance with a reasonable workload is likely a bug, not a configuration problem. -However, some pages are slow because Phabricator legitimately needs to do a lot +However, some pages are slow because Phorge legitimately needs to do a lot of work to generate them. For example, if you write a 100MB wiki document, -Phabricator will need substantial time to process it, it will take a long time +Phorge will need substantial time to process it, it will take a long time to download over the network, and your browser will probably not be able to render it especially quickly. -We may be able to improve performance in some cases, but Phabricator is not +We may be able to improve performance in some cases, but Phorge is not magic and can not wish away real complexity. The best solution to these problems is usually to find another way to solve your problem: for example, maybe the 100MB document can be split into several smaller documents. diff --git a/src/docs/user/field/permanently_destroying_data.diviner b/src/docs/user/field/permanently_destroying_data.diviner index 04907fc0be..8f5d667252 100644 --- a/src/docs/user/field/permanently_destroying_data.diviner +++ b/src/docs/user/field/permanently_destroying_data.diviner @@ -6,7 +6,7 @@ How to permanently destroy data and manage leaked secrets. Overview ======== -Phabricator intentionally makes it difficult to permanently destroy data, but +Phorge intentionally makes it difficult to permanently destroy data, but provides a command-line tool for destroying objects if you're certain that you want to destroy something. @@ -24,7 +24,7 @@ Destroying Data To permanently destroy an object, run this command from the command line: ``` -phabricator/ $ ./bin/remove destroy +phorge/ $ ./bin/remove destroy ``` The `` may be an object monogram or PHID. For instance, you can use @@ -36,7 +36,7 @@ The `` may be an object monogram or PHID. For instance, you can use CLI Access Required =================== -In almost all cases, Phabricator requires operational access from the CLI to +In almost all cases, Phorge requires operational access from the CLI to permanently destroy data. One major reason for this requirement is that it limits the reach of an attacker who compromises a privileged account. @@ -56,9 +56,9 @@ Sometimes you may want to destroy an object because it has leaked a secret, like an API key or another credential. For example, an engineer might accidentally send a change for review which includes a sensitive private key. -No Phabricator command can rewind time, and once data is written to Phabricator +No Phorge command can rewind time, and once data is written to Phorge the cat is often out of the bag: it has often been transmitted to external -systems which Phabricator can not interact with via email, webhooks, API calls, +systems which Phorge can not interact with via email, webhooks, API calls, repository mirroring, CDN caching, and so on. You can try to clean up the mess, but you're generally already too late. @@ -66,7 +66,7 @@ The `bin/remove destroy` command will make a reasonable attempt to completely destroy objects, but this is just an attempt. It can not unsend email or uncall the API, and no command can rewind time and undo a leak. -**Revoking Credentials**: If Phabricator credentials were accidentally +**Revoking Credentials**: If Phorge credentials were accidentally disclosed, you can revoke them so they no longer function. See @{article:Revoking Credentials} for more information. @@ -75,7 +75,7 @@ Preventing Leaks ================ Because time can not be rewound, it is best to prevent sensitive data from -leaking in the first place. Phabricator supports some technical measures that +leaking in the first place. Phorge supports some technical measures that can make it more difficult to accidentally disclose secrets: **Differential Diff Herald Rules**: You can write "Differential Diff" rules diff --git a/src/docs/user/field/repository_hints.diviner b/src/docs/user/field/repository_hints.diviner index d618fac4aa..9c50243a9a 100644 --- a/src/docs/user/field/repository_hints.diviner +++ b/src/docs/user/field/repository_hints.diviner @@ -6,7 +6,7 @@ Dealing with rewrites of published repositories and other unusual problems. Overview ======== -Some repositories have unusual commits. You can provide "hints" to Phabricator +Some repositories have unusual commits. You can provide "hints" to Phorge about these commits to improve behavior. Supported hints are: @@ -17,7 +17,7 @@ Supported hints are: new pages. - **Unreadable Commits**: If some commits are not readable (which is rare, but can happen in some cases if they are generated with an external tool) - you can provide hints so that Phabricator doesn't try to read them. + you can provide hints so that Phorge doesn't try to read them. The remainder of this document explains how to create and remove hints, and how to specify each type of hint. @@ -28,7 +28,7 @@ Creating Hints To create hints, pipe a JSON list of hints to `bin/repository hint`: ``` -phabricator/ $ cat hints.json | ./bin/repository hint +phorge/ $ cat hints.json | ./bin/repository hint ``` The hints should be a list of objects like this: @@ -76,7 +76,7 @@ For example, use a hint specification like this: ] ``` -Phabricator won't treat commits without any hint specially. +Phorge won't treat commits without any hint specially. Hint: Rewritten Commits @@ -104,13 +104,13 @@ For example, a hint might look like this: ] ``` -Phabricator will show users that the commit was rewritten in the web UI. +Phorge will show users that the commit was rewritten in the web UI. Hint: Unreadable Commits ======================== -The `"unreadable"` hint allows you to tell Phabricator that it should not +The `"unreadable"` hint allows you to tell Phorge that it should not bother trying to read the changes associated with a particular commit. In some rare cases, repositories can contain commits which aren't readable (for example, if they were created by external tools during an import or @@ -130,5 +130,5 @@ For example, a hint might look like this: ] ``` -Phabricator won't try to read, parse, import, or display the changes associated +Phorge won't try to read, parse, import, or display the changes associated with this commit. diff --git a/src/docs/user/field/repository_imports.diviner b/src/docs/user/field/repository_imports.diviner index 262874186c..c5fba58dd7 100644 --- a/src/docs/user/field/repository_imports.diviner +++ b/src/docs/user/field/repository_imports.diviner @@ -7,7 +7,7 @@ Overview ======== When you first import an external source code repository (or push new commits to -a hosted repository), Phabricator imports those commits in the background. +a hosted repository), Phorge imports those commits in the background. While a repository is initially importing, some features won't work. While individual commits are importing, some of their metadata won't be available in @@ -20,7 +20,7 @@ help you understand the import process and troubleshoot problems with it. Understanding the Import Pipeline ================================= -Phabricator first performs commit discovery on repositories. This examines +Phorge first performs commit discovery on repositories. This examines a repository and identifies all the commits in it at a very shallow level, then creates stub objects for them. These stub objects primarily serve to assign various internal IDs to each commit. @@ -54,7 +54,7 @@ the import process is stuck. First, to identify which commits have missing import steps, run this command: ``` -phabricator/ $ ./bin/repository importing rXYZ +phorge/ $ ./bin/repository importing rXYZ ``` That will show what work remains to be done. Each line shows a commit which @@ -69,7 +69,7 @@ sections are "Queued Tasks" (work waiting in queue) and "Leased Tasks" Third, run this command to look at the daemon logs: ``` -phabricator/ $ ./bin/phd log +phorge/ $ ./bin/phd log ``` This can show you any errors the daemons have encountered recently. @@ -104,14 +104,14 @@ importing` and use this command to re-run any missing steps manually in the foreground: ``` -phabricator/ $ ./bin/repository reparse --importing --trace rXYZabcdef012... +phorge/ $ ./bin/repository reparse --importing --trace rXYZabcdef012... ``` This command is always safe to run, no matter what the actual root cause of the problem is. If this fails with an error, you've likely identified a problem with -Phabricator. Collect as much information as you can about what makes the commit +Phorge. Collect as much information as you can about what makes the commit special and file a bug in the upstream by following the instructions in @{article:Contributing Bug Reports}. @@ -132,7 +132,7 @@ wait that long. In the Daemon console, temporarily failures usually look like tasks in the "Leased Tasks" column with a large "Expires" value but a low "Failures" count -(usually 0 or 1). The "Expires" column is showing how long Phabricator is +(usually 0 or 1). The "Expires" column is showing how long Phorge is waiting to retry these tasks. In the daemon log, these temporary failures might have created log entries, but @@ -150,7 +150,7 @@ daemons, all task leases are immediately expired, so any tasks waiting for a long time will run right away: ``` -phabricator/ $ ./bin/phd restart +phorge/ $ ./bin/phd restart ``` This command is always safe to run, no matter what the actual root cause of @@ -172,7 +172,7 @@ been too ambitious with running manual SQL commands and deleted a bunch of extra things they shouldn't have. There is no normal set of conditions under which this should occur, but you can -force Phabricator to re-queue the tasks to recover from it if it does occur. +force Phorge to re-queue the tasks to recover from it if it does occur. This will look like missing steps in `repository importing`, but nothing in the "Queued Tasks" or "Leased Tasks" sections of the daemon console. The daemon @@ -182,12 +182,12 @@ To re-queue parse tasks for a repository, run this command, which will queue up all of the missing work in `repository importing`: ``` -phabricator/ $ ./bin/repository reparse --importing --all rXYZ +phorge/ $ ./bin/repository reparse --importing --all rXYZ ``` This command may cause duplicate work to occur if you have misdiagnosed the problem and the tasks aren't actually lost. For example, it could queue a -second task to perform publishing, which could cause Phabricator to send a +second task to perform publishing, which could cause Phorge to send a second copy of email about the commit. Other than that, it is safe to run even if this isn't the problem. @@ -215,7 +215,7 @@ To mark a repository as imported even though it really isn't, run this command: ``` -phabricator/ $ ./bin/repository mark-imported rXYZ +phorge/ $ ./bin/repository mark-imported rXYZ ``` If you do this by mistake, you can reverse it later by using the @@ -231,7 +231,7 @@ to any command to get more details about what it is doing. For any command, you can use `help` to learn more about what it does and which flag it takes: ``` -phabricator/ $ bin/repository help +phorge/ $ bin/repository help ``` In particular, you can use flags with the `repository reparse` command to diff --git a/src/docs/user/field/restarting.diviner b/src/docs/user/field/restarting.diviner index 5bdd92da78..8e21c2ac77 100644 --- a/src/docs/user/field/restarting.diviner +++ b/src/docs/user/field/restarting.diviner @@ -1,14 +1,14 @@ -@title Restarting Phabricator +@title Restarting Phorge @group fieldmanual Instructions on how to restart HTTP and PHP servers to reload configuration -changes in Phabricator. +changes in Phorge. Overview ======== -Phabricator's setup and configuration instructions sometimes require you to +Phorge's setup and configuration instructions sometimes require you to restart your server processes, particularly after making configuration changes. This document explains how to restart them properly. @@ -112,5 +112,5 @@ like one of these on your system, or a different command: ``` $ sudo /etc/init.d/php-fpm restart -$ sudo service php5-fpm reload +$ sudo service php-fpm reload ``` diff --git a/src/docs/user/field/revoking_credentials.diviner b/src/docs/user/field/revoking_credentials.diviner index b1e18bcd97..9d3c046fbe 100644 --- a/src/docs/user/field/revoking_credentials.diviner +++ b/src/docs/user/field/revoking_credentials.diviner @@ -21,7 +21,7 @@ specified targets. For example, if you believe `@alice` may have had her SSH key compromised, you can revoke her keys like this: ``` -phabricator/ $ ./bin/auth revoke --type ssh --from @alice +phorge/ $ ./bin/auth revoke --type ssh --from @alice ``` The flag `--everything` revokes all credential types. @@ -41,13 +41,13 @@ third-party credential types if you have extensions installed. To list all revokable credential types: ``` -phabricator/ $ ./bin/auth revoke --list +phorge/ $ ./bin/auth revoke --list ``` To get details about exactly how a specific revoker works: ``` -phabricator/ $ ./bin/auth revoke --list --type ssh +phorge/ $ ./bin/auth revoke --list --type ssh ``` @@ -79,10 +79,10 @@ are normally sent over the network. You can revoke these credentials by running these commands: ``` -phabricator/ $ ./bin/auth revoke --type password --everywhere -phabricator/ $ ./bin/auth revoke --type conduit --everywhere -phabricator/ $ ./bin/auth revoke --type session --everywhere -phabricator/ $ ./bin/auth revoke --type temporary --everywhere +phorge/ $ ./bin/auth revoke --type password --everywhere +phorge/ $ ./bin/auth revoke --type conduit --everywhere +phorge/ $ ./bin/auth revoke --type session --everywhere +phorge/ $ ./bin/auth revoke --type temporary --everywhere ``` Depending on the nature of the compromise you may also consider revoking `ssh` @@ -97,5 +97,5 @@ credentials without affecting other users. You can revoke all credentials for a user by running this command: ``` -phabricator/ $ ./bin/auth revoke --everything --from @alice +phorge/ $ ./bin/auth revoke --everything --from @alice ``` diff --git a/src/docs/user/field/worker_queue.diviner b/src/docs/user/field/worker_queue.diviner index b88a2ea40b..d53b0771de 100644 --- a/src/docs/user/field/worker_queue.diviner +++ b/src/docs/user/field/worker_queue.diviner @@ -6,7 +6,7 @@ Advanced guide to managing the background worker task queue. Overview ======== -Phabricator uses daemonized worker processes to execute some tasks (like +Phorge uses daemonized worker processes to execute some tasks (like importing repositories and sending mail) in the background. In most cases, this queue will automatically execute tasks in an appropriate @@ -16,7 +16,7 @@ which tasks execute, when, and at what priority. Reference: Priority Levels ========================== -Tasks queued by Phabricator use these default priority levels: +Tasks queued by Phorge use these default priority levels: | Priority | Name | Tasks | |---|---|---| @@ -33,7 +33,7 @@ with priority 2000). Any positive integer is a valid priority level, and if you adjust the priority of tasks with `bin/worker priority` you may select any level even if -Phabricator would never naturally queue tasks at that level. For example, you +Phorge would never naturally queue tasks at that level. For example, you may adjust tasks to priority `5678`, which will make them execute after all other types of natural tasks. @@ -48,7 +48,7 @@ The most common case where you may want to make an adjustment to the default behavior of the worker queue is when importing a very large repository like the Linux kernel. -Although Phabricator will automatically process imports of new repositories at +Although Phorge will automatically process imports of new repositories at a lower priority level than all other non-import tasks, you may still run into issues like these: @@ -63,7 +63,7 @@ import at a lower priority than all other tasks (including other imports of new repositories), you can run a command like this: ``` -phabricator/ $ ./bin/worker priority --priority 5000 --container R123 +phorge/ $ ./bin/worker priority --priority 5000 --container R123 ``` This means: set all tasks associated with container `R123` (in this example, @@ -75,7 +75,7 @@ schedule tasks to execute at night or over the weekend. For example, to pause an import for 6 hours, run a command like this: ``` -phabricator/ $ ./bin/worker delay --until "6 hours" --container R123 +phorge/ $ ./bin/worker delay --until "6 hours" --container R123 ``` The selected tasks will not execute until 6 hours from the time this command diff --git a/src/docs/user/field/xhprof.diviner b/src/docs/user/field/xhprof.diviner index ad6fe82cb8..a1cfd36558 100644 --- a/src/docs/user/field/xhprof.diviner +++ b/src/docs/user/field/xhprof.diviner @@ -7,7 +7,7 @@ Overview ======== XHProf is a profiling tool which will let you understand application -performance in Phabricator. +performance in Phorge. After you install XHProf, you can use it from the web UI and the CLI to generate detailed performance profiles. It is the most powerful tool available @@ -60,7 +60,7 @@ Using XHProf: CLI From the command line, use the `--xprofile ` flag to generate a profile of any script. -You can then upload this file to Phabricator (using `arc upload` may be easiest) +You can then upload this file to Phorge (using `arc upload` may be easiest) and view it in the web UI. diff --git a/src/docs/user/installation_guide.diviner b/src/docs/user/installation_guide.diviner index d2931fcb49..4b10b49405 100644 --- a/src/docs/user/installation_guide.diviner +++ b/src/docs/user/installation_guide.diviner @@ -1,58 +1,65 @@ @title Installation Guide @group intro -This document contains basic install instructions to get Phabricator up and +This document contains basic install instructions to get Phorge up and running. Overview ======== -Phabricator is a LAMP (Linux, Apache, MySQL, PHP) application. To install -Phabricator, you will need: +Phorge is a LAMP (Linux, Apache, MySQL, PHP) application. To install +Phorge, you will need: - a normal computer to install it on (shared hosts and unusual environments are not supported) running some flavor of Linux or a similar OS; - - a domain name (like `phabricator.mycompany.com`); + - a domain name (like `phorge.example.com`); - basic sysadmin skills; - Apache, nginx, or another webserver; - - PHP, MySQL, and Git. + - PHP; + - MySQL (you will need a server with multiple databases); + - git The remainder of this document details these requirements. +You may be interested also in preparing these optional stuff: + + - have valid SMTP parameters for outgoing email notifications; + - having nothing listening on port 22, to then setup a SSH+git server + Installation Requirements ========================= You will need **a computer**. Options include: - **A Normal Computer**: This is strongly recommended. Many installs use a VM - in EC2. Phabricator installs properly and works well on a normal computer. + in EC2. Phorge installs properly and works well on a normal computer. - **A Shared Host**: This may work, but is not recommended. Many shared - hosting environments have restrictions which prevent some of Phabricator's + hosting environments have restrictions which prevent some of Phorge's features from working. Consider using a normal computer instead. We do not support shared hosts. - **A SAN Appliance, Network Router, Gaming Console, Raspberry Pi, etc.**: - Although you may be able to install Phabricator on specialized hardware, it + Although you may be able to install Phorge on specialized hardware, it is unlikely to work well and will be difficult for us to support. Strongly consider using a normal computer instead. We do not support specialized hardware. - **A Toaster, Car, Firearm, Thermostat, etc.**: Yes, many modern devices now have embedded computing capability. We live in interesting times. However, - you should not install Phabricator on these devices. Instead, install it on + you should not install Phorge on these devices. Instead, install it on a normal computer. We do not support installing on noncomputing devices. -To install the Phabricator server software, you will need an **operating +To install the Phorge server software, you will need an **operating system** on your normal computer which is **not Windows**. Note that the command line interface //does// work on Windows, and you can //use// -Phabricator from any operating system with a web browser. However, the server +Phorge from any operating system with a web browser. However, the server software does not run on Windows. It does run on most other operating systems, so choose one of these instead: - - **Linux**: Most installs use Linux. - - **Mac OS X**: Mac OS X is an acceptable flavor of Linux. - - **FreeBSD**: While FreeBSD is certainly not a flavor of Linux, it is a fine - operating system possessed of many desirable qualities, and Phabricator will - install and run properly on FreeBSD. - - **Solaris, etc.**: Other systems which look like Linux and quack like Linux + - **GNU/Linux**: Most installs use Linux. + - **Mac OS X**: Mac OS X is an acceptable non-flavor of Linux. + - **BSD**: While BSD is certainly not a flavor of Linux, it is a fine + operating system possessed of many desirable qualities, and Phorge will + install and run properly on BSD. + - **Solaris, etc.**: Other systems which look like *nix and quack like *nix will generally work fine, although we may suffer a reduced ability to support and resolve issues on unusual operating systems. @@ -64,26 +71,31 @@ Beyond an operating system, you will need **a webserver**. works fine. - **Other**: Other webservers which can run PHP are also likely to work fine, although these installation instructions will not cover how to set them up. - - **PHP Builtin Server**: Phabricator will not work with the builtin - webserver because Phabricator depends on making requests to itself on some + - **PHP Builtin Server**: Phorge will not work with the builtin + webserver because Phorge depends on making requests to itself on some workflows, and the builtin webserver is single-threaded. You will also need: - **MySQL**: You need MySQL. We strongly recommend MySQL 5.5 or newer. - - **PHP**: You need PHP 5.5 or newer. + You will need a server with multiple databases. + - **PHP**: You need a PHP engine: + - PHP 7 - 7.2 or newer. + - PHP 8 - 8.0 or newer. + - **git**: You need git 2.5.0 or newer on the server. + No particular version is needed on your clients. You'll probably also need a **domain name**. In particular, you should read this note: -NOTE: Phabricator must be installed on an entire domain. You can not install it -to a path on an existing domain, like `example.com/phabricator/`. Instead, -install it to an entire domain or subdomain, like `phabricator.example.com`. +NOTE: Phorge must be installed on an entire domain. You can not install it +to a path on an existing domain, like `example.com/phorge/`. Instead, +install it to an entire domain or subdomain, like `phorge.example.com`. Level Requirements ================== -To install and administrate Phabricator, you'll need to be comfortable with +To install and administrate Phorge, you'll need to be comfortable with common system administration skills. For example, you should be familiar with using the command line, installing software on your operating system of choice, working with the filesystem, managing processes, dealing with permissions, @@ -95,7 +107,7 @@ need to know. However, if you aren't very familiar or comfortable with using this set of skills to troubleshoot and resolve problems, you may encounter issues which you have substantial difficulty working through. -We assume users installing and administrating Phabricator are comfortable with +We assume users installing and administrating Phorge are comfortable with common system administration skills and concepts. If you aren't, proceed at your own risk and expect that your skills may be tested. @@ -106,28 +118,34 @@ Here's a general description of what you need to install: - git (usually called "git" in package management systems) - Apache (usually "httpd" or "apache2") (or nginx) - - MySQL Server (usually "mysqld" or "mysql-server") + - MySQL Server (usually "mysqld" or "mysql-server" or "mariadb-server") - PHP (usually "php") - Required PHP extensions: mbstring, iconv, mysql (or mysqli), curl, pcntl - (these might be something like "php-mysql" or "php5-mysqlnd") - - Optional PHP extensions: gd + (these might be something like "php-mysql" or "php-mysqlnd") + - Optional PHP extensions: gd, zip If you already have LAMP setup, you've probably already got everything you need. It may also be helpful to refer to the install scripts above, even if they don't work for your system. -Now that you have all that stuff installed, grab Phabricator and its +Now that you have all that stuff installed, grab Phorge and its dependencies: $ cd somewhere/ # pick some install directory - somewhere/ $ git clone https://github.com/phacility/arcanist.git - somewhere/ $ git clone https://github.com/phacility/phabricator.git + somewhere/ $ git clone https://we.phorge.it/source/arcanist.git + somewhere/ $ git clone https://we.phorge.it/source/phorge.git + +After cloning, flag them as safe (to avoid the error //"fatal: +detected dubious ownership in repository at ..."//): + + $ sudo git config --system --add safe.directory somewhere/arcanist + $ sudo git config --system --add safe.directory somewhere/phorge Next Steps ========== Continue by: - - configuring Phabricator with the @{article:Configuration Guide}; or - - learning how to keep Phabricator up to date with - @{article:Upgrading Phabricator}. + - configuring Phorge with the @{article:Configuration Guide}; or + - learning how to keep Phorge up to date with + @{article:Upgrading Phorge}. diff --git a/src/docs/user/introduction.diviner b/src/docs/user/introduction.diviner index cbd739fc8d..a591aae160 100644 --- a/src/docs/user/introduction.diviner +++ b/src/docs/user/introduction.diviner @@ -1,33 +1,34 @@ @title Introduction @group intro -This document provides a high-level overview of the Phabricator project. +This document provides a high-level overview of the Phorge project. -= What is Phabricator? = += What is Phorge? = -**Phabricator** (pronounced like the word //fabricator//) is a suite of web +**Phorge** (pronounced like the word //forge//) is a suite of web applications which make it easier to build software, particularly when working -with teams. Phabricator is largely based on Facebook's internal tools. +with teams. Phorge is a fork of Phabricator, which in turn is largely based on +Facebook's internal tools. -The major components of Phabricator are: +The major components of Phorge are: - **Differential**, a code review tool; and - **Diffusion**, a repository browser; and - **Maniphest**, a bug tracker; and - **Phriction**, a wiki. -Phabricator also includes a number of smaller tools. +Phorge also includes a number of smaller tools. -= Why use Phabricator? = += Why use Phorge? = -Phabricator gives you a box of solid tools for a comparatively small setup cost. +Phorge gives you a box of solid tools for a comparatively small setup cost. The tools all work together and are richly integrated. The whole thing is free -and open source. You own all your data. Phabricator is extremely fast and proven +and open source. You own all your data. Phorge is extremely fast and proven to scale both to large datasets (Facebook has 500,000+ commits across many repositories) and large organizations (Facebook has 500+ fulltime engineers). -Phabricator's tools are easy to learn, understand, and use. +Phorge's tools are easy to learn, understand, and use. -However, Phabricator may also not be a good solution for you: +However, Phorge may also not be a good solution for you: - If you develop primarily on Windows, you are likely to find integration with the toolsets you use lacking. @@ -40,4 +41,4 @@ However, Phabricator may also not be a good solution for you: Continue by: - - installing Phabricator with the @{article:Installation Guide}. + - installing Phorge with the @{article:Installation Guide}. diff --git a/src/docs/user/reporting_security.diviner b/src/docs/user/reporting_security.diviner index c74fc40e1a..98293964b1 100644 --- a/src/docs/user/reporting_security.diviner +++ b/src/docs/user/reporting_security.diviner @@ -1,36 +1,17 @@ @title Reporting Security Vulnerabilities @group intro -Describes how to report security vulnerabilities in Phabricator. +Describes how to report security vulnerabilities in Phorge. Overview ======== -Phabricator runs a disclosure and award program through -[[ https://www.hackerone.com/ | HackerOne ]]. This program is the best way to -submit security issues to us, and awards responsible disclosure of -vulnerabilities with cash bounties. You can find our project page -here: +Phorge accepts bug reports on the upstream install. Please use the +[[https://we.phorge.it/maniphest/task/edit/form/1/ | security reporting form]] +to report security vulnerabilities. -(NOTE) https://hackerone.com/phabricator +If you aren't sure if something qualifies, you can submit the issue as a normal + bug report. For instructions, see @{article:Contributing Bug Reports}. -The project page has detailed information about the scope of the program and -how to participate. - -We have a 24 hour response timeline, and are usually able to respond to (and, -very often, fix) issues more quickly than that. - - -Other Channels -============== - -If you aren't sure if something qualifies or don't want to report via -HackerOne, you can submit the issue as a normal bug report. For instructions, -see @{article:Contributing Bug Reports}. - - -Get Updated -=========== - -General information about security changes is reported weekly in the -[[ https://secure.phabricator.com/w/changelog/ | Changelog ]]. +General information about security changes is reported in the +[[ https://we.phorge.it/w/changelog/ | Changelog ]]. diff --git a/src/docs/user/support.diviner b/src/docs/user/support.diviner index 20c4b241f0..a4d7cbafca 100644 --- a/src/docs/user/support.diviner +++ b/src/docs/user/support.diviner @@ -2,4 +2,35 @@ @short Support @group intro -Effective June 1, 2021: Phabricator is no longer actively supported. +Resources for reporting bugs, requesting features, and getting support. + +Overview +======== + +The upstream provides free support for a range of problems. + + +Reporting Security Vulnerabilities +================================== + +The upstream accepts, fixes, and awards bounties for reports of material +security issues with the software. + +To report security issues, see @{article:Reporting Security Vulnerabilities}. + + +Reporting Bugs +============== + +The upstream will accept **reproducible** bug reports in modern, first-party +production code running in reasonable environments. Before submitting a bug +report you **must update** to the latest version of Phorge. + +To report bugs, see @{article:Contributing Bug Reports}. + + +Contributing +============ + +If you'd like to contribute to Phorge, start with +@{article:Contributor Introduction}. diff --git a/src/docs/user/upgrading.diviner b/src/docs/user/upgrading.diviner index 705b38e4ee..4b11317dee 100644 --- a/src/docs/user/upgrading.diviner +++ b/src/docs/user/upgrading.diviner @@ -1,12 +1,12 @@ -@title Upgrading Phabricator +@title Upgrading Phorge @group intro -This document contains instructions for keeping Phabricator up to date. +This document contains instructions for keeping Phorge up to date. Overview ======== -Phabricator is under active development, and new features are released +Phorge is under active development, and new features are released continuously. Staying up to date will keep your install secure. We recommend installs upgrade regularly (every 1-2 weeks). Upgrades usually go @@ -18,8 +18,8 @@ to a useful new feature or an important security change. Staying On Top of Changes ========================= -We release a weekly [[https://secure.phabricator.com/w/changelog | Changelog]], -which describes changes in the previous week. You can look at the changelogs +We release a [[https://we.phorge.it/w/changelog | Changelog]], +which describes changes over time. You can look at the changelogs for an idea of what new features are available, upcoming changes, security information, and warnings about compatibility issues or migrations. @@ -27,21 +27,18 @@ information, and warnings about compatibility issues or migrations. Stable Branch ============= -You can either run the `master` or `stable` branch of Phabricator. The `stable` -branch is run in the [[ https://phacility.com | Phacility Cluster ]], and lags -about a week behind `master`. +You can either run the `master` or `stable` branch of Phorge. The `stable` +branch is a little more stable than `master`, and may be helpful if you +administrate a larger install. -The `stable` branch is a little more stable than `master`, and may be helpful -if you administrate a larger install. - -We promote `master` to `stable` about once a week, then publish the changelog -and deploy the cluster. During the week, major bugfixes are cherry-picked to -the `stable` branch. The changelog lists the `stable` hashes for that week, -as well as any fixes which were cherry-picked. +We promote `master` to `stable` frequently, then publish the changelog. During +the week, major bugfixes are cherry-picked to the `stable` branch. The changelog + lists the `stable` hashes for that week, as well as any fixes which were + cherry-picked. To switch to `stable`, check the branch out in each working copy: - phabricator/ $ git checkout stable + phorge/ $ git checkout stable arcanist/ $ git checkout stable You can now follow the upgrade process normally. @@ -50,26 +47,26 @@ You can now follow the upgrade process normally. Upgrade Process =============== -IMPORTANT: You **MUST** restart Phabricator after upgrading. For help, see -@{article:Restarting Phabricator}. +IMPORTANT: You **MUST** restart Phorge after upgrading. For help, see +@{article:Restarting Phorge}. -IMPORTANT: You **MUST** upgrade `arcanist` and `phabricator` at the same time. +IMPORTANT: You **MUST** upgrade `arcanist` and `phorge` at the same time. -Phabricator runs on many different systems, with many different webservers. +Phorge runs on many different systems, with many different webservers. Given this diversity, we don't currently maintain a comprehensive upgrade script which can work on any system. However, the general steps are the same on every system: - Stop the webserver (including `php-fpm`, if you use it). - - Stop the daemons, with `phabricator/bin/phd stop`. - - Run `git pull` in `arcanist/` and `phabricator/`. - - Run `phabricator/bin/storage upgrade`. - - Start the daemons, with `phabricator/bin/phd start`. + - Stop the daemons, with `phorge/bin/phd stop`. + - Run `git pull` in `arcanist/` and `phorge/`. + - Run `phorge/bin/storage upgrade`. + - Start the daemons, with `phorge/bin/phd start`. - Restart the webserver (and `php-fpm`, if you stopped it earlier). For some more discussion details, see @{article:Configuration Guide}. -This template script roughly outlines the steps required to upgrade Phabricator. +This template script roughly outlines the steps required to upgrade Phorge. You'll need to adjust paths and commands a bit for your particular system: ```lang=sh @@ -78,23 +75,23 @@ You'll need to adjust paths and commands a bit for your particular system: set -e set -x -# This is an example script for updating Phabricator, similar to the one used to -# update . It might not work perfectly on your +# This is an example script for updating Phorge, similar to the one used to +# update . It might not work perfectly on your # system, but hopefully it should be easy to adapt. This script is not intended # to work without modifications. # NOTE: This script assumes you are running it from a directory which contains -# arcanist/, and phabricator/. +# arcanist/, and phorge/. ROOT=`pwd` # You can hard-code the path here instead. ### STOP WEB SERVER AND DAEMONS ############################################### # Stop daemons. -$ROOT/phabricator/bin/phd stop +$ROOT/phorge/bin/phd stop # If running the notification server, stop it. -# $ROOT/phabricator/bin/aphlict stop +# $ROOT/phorge/bin/aphlict stop # Stop the webserver (apache, nginx, lighttpd, etc). This command will differ # depending on which system and webserver you are running: replace it with an @@ -108,20 +105,20 @@ sudo /etc/init.d/httpd stop cd $ROOT/arcanist git pull -cd $ROOT/phabricator +cd $ROOT/phorge git pull # Upgrade the database schema. You may want to add the "--force" flag to allow # this script to run noninteractively. -$ROOT/phabricator/bin/storage upgrade +$ROOT/phorge/bin/storage upgrade # Restart the webserver. As above, this depends on your system and webserver. # NOTE: If you're running php-fpm, restart it here too. sudo /etc/init.d/httpd start # Restart daemons. -$ROOT/phabricator/bin/phd start +$ROOT/phorge/bin/phd start # If running the notification server, start it. -# $ROOT/phabricator/bin/aphlict start +# $ROOT/phorge/bin/aphlict start ``` diff --git a/src/docs/user/userguide/almanac.diviner b/src/docs/user/userguide/almanac.diviner index 9250724c03..5b13a9f80a 100644 --- a/src/docs/user/userguide/almanac.diviner +++ b/src/docs/user/userguide/almanac.diviner @@ -11,7 +11,7 @@ lists of //devices// and //services// that humans and other applications can use to keep track of what is running where. Almanac is an infrastructure application that will normally be used by -administrators to configure advanced Phabricator features. In most cases, +administrators to configure advanced Phorge features. In most cases, normal users will very rarely interact with Almanac directly. At a very high level, Almanac can be thought of as a bit like a DNS server. @@ -19,11 +19,11 @@ Callers ask it for information about services, and it responds with details about which devices host those services. However, it can respond to a broader range of queries and provide more detailed responses than DNS alone can. -Today, the primary use cases for Almanac are internal to Phabricator: +Today, the primary use cases for Almanac are internal to Phorge: - Providing a list of build servers to Drydock so it can run build and integration tasks. - - Configuring Phabricator to operate in a cluster setup. + - Configuring Phorge to operate in a cluster setup. Beyond internal uses, Almanac is a general-purpose service and device inventory application and can be used to configure and manage other types of service and diff --git a/src/docs/user/userguide/amazon_rds.diviner b/src/docs/user/userguide/amazon_rds.diviner index 442d499013..aab2058140 100644 --- a/src/docs/user/userguide/amazon_rds.diviner +++ b/src/docs/user/userguide/amazon_rds.diviner @@ -6,7 +6,7 @@ Discusses using Amazon RDS as a database. Overview ======== -Phabricator works with Amazon RDS. However, most of our documentation and setup +Phorge works with Amazon RDS. However, most of our documentation and setup checks assume you are using local MySQL, and upstream support is less available for RDS. diff --git a/src/docs/user/userguide/arcanist.diviner b/src/docs/user/userguide/arcanist.diviner index 0de18a9358..74d3246bbd 100644 --- a/src/docs/user/userguide/arcanist.diviner +++ b/src/docs/user/userguide/arcanist.diviner @@ -1,9 +1,9 @@ @title Arcanist User Guide @group userguide -Guide to Arcanist, a command-line interface to Phabricator. +Guide to Arcanist, a command-line interface to Phorge. -Arcanist provides command-line access to many Phabricator tools (like +Arcanist provides command-line access to many Phorge tools (like Differential, Files, and Paste), integrates with static analysis ("lint") and unit tests, and manages common workflows like getting changes into Differential for review. @@ -12,7 +12,7 @@ A detailed command reference is available by running `arc help`. This document provides an overview of common workflows and installation. Arcanist has technical, contributor-focused documentation here: - + = Quick Start = @@ -90,7 +90,7 @@ have PHP installed, you can download it from . To install Arcanist, pick an install directory and clone the code from GitHub: - some_install_path/ $ git clone https://github.com/phacility/arcanist.git + some_install_path/ $ git clone https://github.com/phorgeit/arcanist.git Now add `some_install_path/arcanist/bin/` to your PATH environment variable. When you type "arc", you should see something like this: @@ -143,7 +143,7 @@ three sources, in order: # User configuration is read from `~/.arcconfig`. This file is JSON, and can be updated using `arc set-config`. # Host configuration is read from `/etc/arcconfig` (on Windows, the path - is `C:\ProgramData\Phabricator\Arcanist\config`). + is `C:\ProgramData\Phorge\Arcanist\config`). Arcanist uses the first definition it encounters as the runtime setting. diff --git a/src/docs/user/userguide/arcanist_coverage.diviner b/src/docs/user/userguide/arcanist_coverage.diviner index cb25c0cc74..b1b449a7db 100644 --- a/src/docs/user/userguide/arcanist_coverage.diviner +++ b/src/docs/user/userguide/arcanist_coverage.diviner @@ -1,7 +1,7 @@ @title Arcanist User Guide: Code Coverage @group userguide -Explains code coverage features in Arcanist and Phabricator. +Explains code coverage features in Arcanist and Phorge. This is a configuration guide that helps you set up advanced features. If you're just getting started, you don't need to look at this yet. Instead, start with @@ -27,9 +27,9 @@ to `src/some/file.php` and give you a detailed coverage report. If the test engine enables coverage by default, it will be uploaded to Differential and displayed in the right gutter when viewing diffs. -= Enabling Coverage for Arcanist and Phabricator = += Enabling Coverage for Arcanist and Phorge = -If you're contributing, Arcanist and Phabricator support coverage if +If you're contributing, Arcanist and Phorge support coverage if you install Xdebug: http://xdebug.org/ diff --git a/src/docs/user/userguide/arcanist_diff.diviner b/src/docs/user/userguide/arcanist_diff.diviner index 4140deb537..7d5a0161d9 100644 --- a/src/docs/user/userguide/arcanist_diff.diviner +++ b/src/docs/user/userguide/arcanist_diff.diviner @@ -14,7 +14,7 @@ information. = Overview = While `arc` has a large number of commands that interface with various -Phabricator applications, the primary use of `arc` is to send changes for +Phorge applications, the primary use of `arc` is to send changes for review in Differential (for more information on Differential, see @{article:Differential User Guide}). If you aren't familiar with Differential, it may be instructive to read that article first to understand the big picture @@ -167,7 +167,7 @@ You can use `arc help ` for detailed help with any of these. Differential will make a guess about a next step on accepted revisions, but it may not be the best next step for your workflow. -Phabricator will also automatically close revisions if the changes are pushed +Phorge will also automatically close revisions if the changes are pushed to a repository that is tracked in Diffusion. Specifically, it will close revisions based on commit and tree hashes, and `Differential Revision` identifiers in commit messages. diff --git a/src/docs/user/userguide/arcanist_lint_unit.diviner b/src/docs/user/userguide/arcanist_lint_unit.diviner index 6ecc9aaa3e..dc7f336fef 100644 --- a/src/docs/user/userguide/arcanist_lint_unit.diviner +++ b/src/docs/user/userguide/arcanist_lint_unit.diviner @@ -38,7 +38,7 @@ make this work, you need to do three things: If you haven't created a library for the class to live in yet, you need to do that first. Follow the instructions in -@{article@phabcontrib:Adding New Classes}, then make the library loadable by +@{article@contrib:Adding New Classes}, then make the library loadable by adding it to your `.arcconfig` like this: { diff --git a/src/docs/user/userguide/arcanist_new_project.diviner b/src/docs/user/userguide/arcanist_new_project.diviner index a8e8e49202..86e57c0c86 100644 --- a/src/docs/user/userguide/arcanist_new_project.diviner +++ b/src/docs/user/userguide/arcanist_new_project.diviner @@ -15,11 +15,11 @@ An `.arcconfig` file is a JSON file which you check into your project's root. Arcanist uses `.arcconfig` files to customize a number of things about its behavior. The first thing you're likely to want to configure is the URI -for your Phabricator install. A simple, valid file looks something like this: +for your Phorge install. A simple, valid file looks something like this: name=.arcconfig { - "phabricator.uri" : "https://phabricator.example.com/" + "phabricator.uri" : "https://phorge.example.com/" } For details on available options, see below. @@ -31,7 +31,7 @@ configuration, not user configuration. Common options are: - - **phabricator.uri**: the URI for the Phabricator install that `arc` should + - **phabricator.uri**: the URI for the Phorge install that `arc` should connect to when run in this project. This option was previously called `conduit_uri`. - **repository.callsign**: The callsign of this repository in Diffusion. @@ -47,7 +47,7 @@ Other options include: - **load**: list of additional Phutil libraries to load at startup. See below for details about path resolution, or see - @{article@phabcontrib:Adding New Classes} for a general introduction to + @{article@contrib:Adding New Classes} for a general introduction to libphutil libraries. - **https.cabundle**: specifies the path to an alternate certificate bundle for use when making HTTPS connections. @@ -62,9 +62,9 @@ Other options include: These options are supported, but their use is discouraged: - **http.basicauth.user**: specify an HTTP basic auth username for use when - connecting to Phabricator. + connecting to Phorge. - **http.basicauth.pass**: specify an HTTP basic auth password for use when - connecting to Phabricator. + connecting to Phorge. - **https.blindly-trust-domains**: a list of domains to trust blindly over HTTPS, even if their certificates are invalid. This is a brute force solution to certificate validity problems, and is discouraged. Instead, @@ -182,7 +182,7 @@ some features won't work unless you set up an `.arcconfig` file. Without `.arcconfig`: - - You will need to set a default Phabricator URI with + - You will need to set a default Phorge URI with `arc set-config default `, or specify an explicit URI with `--conduit-uri` each time you run a command. - You will not be able to run linters through arc unless you pass `--engine` diff --git a/src/docs/user/userguide/arcanist_quick_start.diviner b/src/docs/user/userguide/arcanist_quick_start.diviner index 25847ab8a6..a48b93de12 100644 --- a/src/docs/user/userguide/arcanist_quick_start.diviner +++ b/src/docs/user/userguide/arcanist_quick_start.diviner @@ -20,7 +20,7 @@ First, install dependencies: Then install Arcanist itself: - somewhere/ $ git clone https://github.com/phacility/arcanist.git + somewhere/ $ git clone https://github.com/phorgeit/arcanist.git Add `arc` to your path: @@ -40,17 +40,17 @@ Create a `.arcconfig` file in your project's working copy: yourproject/ $ $EDITOR .arcconfig yourproject/ $ cat .arcconfig { - "phabricator.uri" : "https://phabricator.example.com/" + "phabricator.uri" : "https://phorge.example.com/" } -Set `phabricator.uri` to the URI for your Phabricator install (where `arc` +Set `phabricator.uri` to the URI for your Phorge install (where `arc` should send changes to). NOTE: You should **commit this file** to the repository. = Install Arcanist Credentials = -Credentials allow you to authenticate. You must have an account on Phabricator +Credentials allow you to authenticate. You must have an account on Phorge before you can perform this step. $ cd yourproject/ @@ -58,7 +58,7 @@ before you can perform this step. ... Follow the instructions. This will link your user account on your local machine -to your Phabricator account. +to your Phorge account. = Send Changes For Review = diff --git a/src/docs/user/userguide/audit.diviner b/src/docs/user/userguide/audit.diviner index 5223f6c969..c179a9e61f 100644 --- a/src/docs/user/userguide/audit.diviner +++ b/src/docs/user/userguide/audit.diviner @@ -1,13 +1,13 @@ @title Audit User Guide @group userguide -Guide to using Phabricator to audit published commits. +Guide to using Phorge to audit published commits. Overview ======== -Phabricator supports two code review workflows, "review" (pre-publish) and +Phorge supports two code review workflows, "review" (pre-publish) and "audit" (post-publish). To understand the differences between the two, see @{article:User Guide: Review vs Audit}. @@ -59,14 +59,14 @@ a typical audit workflow: - Alice publishes a commit containing some Javascript. - This triggers an audit request to Bailey, the Javascript technical lead on the project (see below for a description of trigger mechanisms). - - Later, Bailey logs into Phabricator and sees the audit request. She ignores + - Later, Bailey logs into Phorge and sees the audit request. She ignores it for the moment, since it isn't blocking anything. At the end of the week she looks through her open requests to see what the team has been up to. - Bailey notices a few minor problems with Alice's commit. She leaves comments describing improvements and uses "Raise Concern" to send the commit back into Alice's queue. - - Later, Alice logs into Phabricator and sees that Bailey has raised a + - Later, Alice logs into Phorge and sees that Bailey has raised a concern (usually, Alice will also get an email). She resolves the issue somehow, maybe by making a followup commit with fixes. - After the issues have been dealt with, she uses "Request Verification" to @@ -163,7 +163,7 @@ The `bin/audit` command allows you to perform several maintenance operations. Get more information about a command by running: ``` -phabricator/ $ ./bin/audit help +phorge/ $ ./bin/audit help ``` Supported operations are: diff --git a/src/docs/user/userguide/calendar.diviner b/src/docs/user/userguide/calendar.diviner index 5c09a753b7..14fb825b59 100644 --- a/src/docs/user/userguide/calendar.diviner +++ b/src/docs/user/userguide/calendar.diviner @@ -29,7 +29,7 @@ Reminders are sent 15 minutes before events begin. Availability ============ -Across all applications, Phabricator shows a red dot next to usernames if the +Across all applications, Phorge shows a red dot next to usernames if the user is currently attending an event. This provides a hint that they may be in a meeting (or on vacation) and could take a while to get back to you about a revision or task. diff --git a/src/docs/user/userguide/calendar_exports.diviner b/src/docs/user/userguide/calendar_exports.diviner index 487de559a4..1d6d6bc1da 100644 --- a/src/docs/user/userguide/calendar_exports.diviner +++ b/src/docs/user/userguide/calendar_exports.diviner @@ -9,15 +9,15 @@ Overview IMPORTANT: Calendar is a prototype application. See @{article:User Guide: Prototype Applications}. -You can export events from Phabricator to other calendar applications like +You can export events from Phorge to other calendar applications like **Google Calendar** or **Calendar.app**. This document will guide you through -how to export event data from Phabricator. +how to export event data from Phorge. When you export events into another application, they generally will not be editable from that application. Exporting events allows you to create one calendar that shows all the events you care about in whatever application you prefer (so you can keep track of everything you need to do), but does not let -you edit Phabricator events from another application. +you edit Phorge events from another application. When exporting events, you can either export individual events one at a time or export an entire group of events (for example, all events you are attending). @@ -75,7 +75,7 @@ The **policy modes** for exports are: information, as though they were logged in with your account. WARNING: Anyone who learns the URI for an export can see the data you choose -to export, even if they don't have a Phabricator account! Be careful about how +to export, even if they don't have a Phorge account! Be careful about how much data you export and treat the URI as a secret. If you accidentally share a URI, you can disable the export. diff --git a/src/docs/user/userguide/calendar_imports.diviner b/src/docs/user/userguide/calendar_imports.diviner index a8fbc6ff09..bebfb84ed2 100644 --- a/src/docs/user/userguide/calendar_imports.diviner +++ b/src/docs/user/userguide/calendar_imports.diviner @@ -9,14 +9,14 @@ Overview IMPORTANT: Calendar is a prototype application. See @{article:User Guide: Prototype Applications}. -You can import events into Phabricator to other calendar applications or from +You can import events into Phorge to other calendar applications or from `.ics` files. This document will guide you through how to import event data -into Phabricator. +into Phorge. When you import events from another application, they can not be edited in -Phabricator. Importing events allows you to share events or keep track of +Phorge. Importing events allows you to share events or keep track of events from different sources, but does not let you edit events from other -applications in Phabricator. +applications in Phorge. Import Policies @@ -36,18 +36,18 @@ Importing `.ics` Files event or an entire event calendar. If you have an event or calendar in `.ics` format, you can import it into -Phabricator in two ways: +Phorge in two ways: - Navigate to {nav Calendar > Imports > Import Events > Import .ics File}. - Drag and drop the file onto a Calendar. -This will create a copy of the event in Phabricator. +This will create a copy of the event in Phorge. If you want to update an imported event later, just repeat this process. The event will be updated with the latest information. Many applications send `.ics` files as email attachments. You can import these -into Phabricator. +into Phorge. .ics Files: Google Calendar @@ -68,9 +68,9 @@ saving the calendar as a `.ics` file. You can also convert an individual event into an `.ics` file by dragging it from the calendar to your desktop (or any other folder). -When you import an event using an `.ics` file, Phabricator can not +When you import an event using an `.ics` file, Phorge can not automatically keep the event up to date. You'll need to repeat the process if -there are changes to the event or calendar later, so Phabricator can learn +there are changes to the event or calendar later, so Phorge can learn about the updates. @@ -78,7 +78,7 @@ Importing .ics URIs ===================== If you have a calendar in another application that supports publishing a -`.ics` URI, you can subscribe to it in Phabricator. This will import the entire +`.ics` URI, you can subscribe to it in Phorge. This will import the entire calendar, and can be configured to automatically keep it up to date and in sync with the external calendar. @@ -87,7 +87,7 @@ below for some guidance on popular calendar applications). Then, browse to {nav Calendar > Imports > Import Events > Import .ics URI}. When you import a URI, you can choose to enable automatic updates. If you do, -Phabricator will periodically update the events it imports from this source. +Phorge will periodically update the events it imports from this source. You can stop this later by turning off the automatic updates or disabling the import. @@ -121,7 +121,7 @@ shared. **Calendar.app** does not support subscriptions via `.ics` URIs. You can export a calendar as an `.ics` file by following the steps above, but -Phabricator can not automatically keep events imported in this way up to date. +Phorge can not automatically keep events imported in this way up to date. Next Steps diff --git a/src/docs/user/userguide/conduit.diviner b/src/docs/user/userguide/conduit.diviner index 35daee505f..dd74919a72 100644 --- a/src/docs/user/userguide/conduit.diviner +++ b/src/docs/user/userguide/conduit.diviner @@ -6,7 +6,7 @@ Overview of the Conduit API. Overview ======== -Conduit is the HTTP API for Phabricator. It is roughly JSON-RPC: you usually +Conduit is the HTTP API for Phorge. It is roughly JSON-RPC: you usually pass a JSON blob, and usually get a JSON blob back, although both call and result formats are flexible in some cases. @@ -30,7 +30,7 @@ in another language without needing a real client. includes examples which show how to format calls. **Other Clients**: There are also clients available in other languages. You -can check the [[ https://secure.phabricator.com/w/community_resources/ | +can check the [[ https://we.phorge.it/w/community_resources/ | Community Resources ]] page for links. API Console diff --git a/src/docs/user/userguide/differential.diviner b/src/docs/user/userguide/differential.diviner index 496fba6388..fad92dfa4e 100644 --- a/src/docs/user/userguide/differential.diviner +++ b/src/docs/user/userguide/differential.diviner @@ -5,7 +5,7 @@ Guide to the Differential (pre-push code review) tool and workflow. = Overview = -Phabricator supports two code review workflows, "review" (pre-push) and +Phorge supports two code review workflows, "review" (pre-push) and "audit" (post-push). To understand the differences between the two, see @{article:User Guide: Review vs Audit}. @@ -14,7 +14,7 @@ This document summarizes the pre-push "review" workflow implemented by the tool = How Review Works = -Code review in Phabricator is a lightweight, asynchronous web-based process. If +Code review in Phorge is a lightweight, asynchronous web-based process. If you are familiar with GitHub, it is similar to how pull requests work: - An author prepares a change to a codebase, then sends it for review. They @@ -66,6 +66,7 @@ Continue by: - diving into the details of inline comments in @{article:Differential User Guide: Inline Comments}; or - reading the FAQ at @{article:Differential User Guide: FAQ}; or + - learning how to use markup in comments at @{article:Remarkup Reference}; or - learning about test plans in @{article:Differential User Guide: Test Plans}; or - learning more about Herald in @{article:Herald User Guide}. diff --git a/src/docs/user/userguide/differential_faq.diviner b/src/docs/user/userguide/differential_faq.diviner index 0df1f7b1c9..979daf762a 100644 --- a/src/docs/user/userguide/differential_faq.diviner +++ b/src/docs/user/userguide/differential_faq.diviner @@ -41,8 +41,9 @@ always "request review" of an accepted revision, with a comment like: If authors are being jerks about this (making sweeping changes as soon as they get an accept), solve the problem socially by telling them to stop being jerks. -Unless you've configured additional layers of enforcement, there's nothing -stopping them from silently changing the code before pushing it, anyway. +Unless you've configured additional layers of enforcement (by +using @{article:Herald}), there's nothing stopping them from silently changing +the code before pushing it, anyway. = How can I enable syntax highlighting? = @@ -50,6 +51,10 @@ stopping them from silently changing the code before pushing it, anyway. You need to install and configure **Pygments** to highlight anything else than PHP. See the `pygments.enabled` configuration setting. += What formatting can be used in comments? = + +Phorge implements a markup language similar to other markup languages like +Markdown and Wiki markup. See @{article:Remarkup Reference}. = What do the very light green and red backgrounds mean? = diff --git a/src/docs/user/userguide/differential_land.diviner b/src/docs/user/userguide/differential_land.diviner index 8d5f30d784..540e4b61f0 100644 --- a/src/docs/user/userguide/differential_land.diviner +++ b/src/docs/user/userguide/differential_land.diviner @@ -1,7 +1,7 @@ @title Differential User Guide: Automated Landing @group userguide -Configuring Phabricator so you can "Land Revision" from the web UI. +Configuring Phorge so you can "Land Revision" from the web UI. Overview @@ -9,7 +9,7 @@ Overview IMPORTANT: This feature is a prototype and has substantial limitations. -Phabricator can be configured so that approved revisions may be published +Phorge can be configured so that approved revisions may be published directly from the web interface. This can make publishing changes more convenient, particularly for open source projects where authors may not have commit access to the repository. This document explains the workflow and how to diff --git a/src/docs/user/userguide/diffusion.diviner b/src/docs/user/userguide/diffusion.diviner index 8ee66d3f8f..60c28006a2 100644 --- a/src/docs/user/userguide/diffusion.diviner +++ b/src/docs/user/userguide/diffusion.diviner @@ -1,7 +1,7 @@ @title Diffusion User Guide @group userguide -Guide to Diffusion, the Phabricator application for hosting and browsing +Guide to Diffusion, the Phorge application for hosting and browsing repositories. Overview @@ -16,7 +16,7 @@ other existing hosting). Both types of repositories can be browsed and interacted with, but hosted repositories support some additional triggers and access controls which are not available for observed repositories. -Diffusion is integrated with the other tools in the Phabricator suite. For +Diffusion is integrated with the other tools in the Phorge suite. For instance: - when you commit Differential revisions to a tracked repository, they are @@ -61,7 +61,7 @@ API to create and edit repositories, see Repository Clustering ===================== -Phabricator repository hosts can be set up in a cluster configuration so you +Phorge repository hosts can be set up in a cluster configuration so you can lose hosts with minimal downtime and data loss. This is an advanced feature which most installs do not need to pursue. @@ -88,7 +88,7 @@ helpful: - get details about automatically taking actions in response to commits in @{article:Diffusion User Guide: Permanent Refs}; or - - understand how Phabricator updates repositories with + - understand how Phorge updates repositories with @{article:Diffusion User Guide: Repository Updates}; or - fix issues with repository imports with @{article:Troubleshooting Repository Imports}. diff --git a/src/docs/user/userguide/diffusion_api.diviner b/src/docs/user/userguide/diffusion_api.diviner index c2508dda72..3eaafa1bb8 100644 --- a/src/docs/user/userguide/diffusion_api.diviner +++ b/src/docs/user/userguide/diffusion_api.diviner @@ -90,7 +90,7 @@ Now that the repository exists, you can add URIs to it. This is optional, and if you're creating a //hosted// repository you may be able to skip this step. -However, if you want Phabricator to observe an existing remote, you'll +However, if you want Phorge to observe an existing remote, you'll configure it here by adding a URI in "Observe" mode. Use the PHID from the previous step to identify the repository you want to add a URI to, and call `diffusion.uri.edit` to create a new URI in Observe mode for the repository. diff --git a/src/docs/user/userguide/diffusion_existing.diviner b/src/docs/user/userguide/diffusion_existing.diviner index 341146f0fd..f0c9bf0a33 100644 --- a/src/docs/user/userguide/diffusion_existing.diviner +++ b/src/docs/user/userguide/diffusion_existing.diviner @@ -10,7 +10,7 @@ Overview If you have an existing repository, you can observe or import it into Diffusion. -Observing a repository creates a read-only copy in Phabricator that is kept +Observing a repository creates a read-only copy in Phorge that is kept up to date by continuously importing new changes. Importing a repository creates a read-write copy. @@ -31,7 +31,7 @@ To observe an existing repository: **URIs** section, in **Observe** mode. - Activate the repository in Diffusion. -This creates a read-only copy of the repository in Phabricator. Phabricator +This creates a read-only copy of the repository in Phorge. Phorge will keep its copy in sync with the remote by periodically polling the remote for changes. diff --git a/src/docs/user/userguide/diffusion_hooks.diviner b/src/docs/user/userguide/diffusion_hooks.diviner index 54c93a933b..0f74a53f58 100644 --- a/src/docs/user/userguide/diffusion_hooks.diviner +++ b/src/docs/user/userguide/diffusion_hooks.diviner @@ -5,7 +5,7 @@ Guide to commit hooks in hosted repositories. = Overview = -Phabricator installs pre-receive/pre-commit hooks in hosted repositories +Phorge installs pre-receive/pre-commit hooks in hosted repositories automatically. They enforce a few rules automatically (like preventing dangerous changes unless a repository is configured to allow them). They can also enforce more complex rules via Herald, using the "Commit Hook: @@ -26,7 +26,7 @@ relevant directory of the repository on disk: - **SVN** Put hooks in `hooks/pre-commit-phabricator.d/`. - **Git** Put hooks in `hooks/pre-receive-phabricator.d/`. - - **Mercurial** Phabricator does not currently support custom hooks in + - **Mercurial** Phorge does not currently support custom hooks in Mercurial. These hooks act like normal `pre-commit` or `pre-receive` hooks: @@ -37,14 +37,14 @@ These hooks act like normal `pre-commit` or `pre-receive` hooks: passed. - They should emit output and return codes like normal hooks do. - These hooks will run only after all the Herald rules have passed and - Phabricator is otherwise ready to accept the commit or push. + Phorge is otherwise ready to accept the commit or push. These additional variables will be available in the environment, in addition to the variables the VCS normally provides: - `PHABRICATOR_REPOSITORY` The PHID of the repository the hook is executing for. - - `PHABRICATOR_USER` The Phabricator username that the session is + - `PHABRICATOR_USER` The Phorge username that the session is authenticated under. - `PHABRICATOR_REMOTE_ADDRESS` The connection's remote address (that is, the IP address of whoever is pushing or committing). diff --git a/src/docs/user/userguide/diffusion_hosting.diviner b/src/docs/user/userguide/diffusion_hosting.diviner index 47197f52fd..0618ff97a5 100644 --- a/src/docs/user/userguide/diffusion_hosting.diviner +++ b/src/docs/user/userguide/diffusion_hosting.diviner @@ -1,19 +1,19 @@ @title Diffusion User Guide: Repository Hosting @group userguide -Guide to configuring Phabricator repository hosting. +Guide to configuring Phorge repository hosting. Overview ======== -Phabricator can host repositories and provide authenticated read and write +Phorge can host repositories and provide authenticated read and write access to them over HTTP and SSH. This document describes how to configure repository hosting. Understanding Supported Protocols ================================= -Phabricator supports hosting over these protocols: +Phorge supports hosting over these protocols: | VCS | SSH | HTTP | |-----|-----|------| @@ -47,12 +47,12 @@ configure it for technical reasons. Creating System User Accounts ============================= -Phabricator uses two system user accounts, plus a third account if you +Phorge uses two system user accounts, plus a third account if you configure SSH access. This section will guide you through creating and -configuring them. These are system user accounts on the machine Phabricator -runs on, not Phabricator user accounts. +configuring them. These are system user accounts on the machine Phorge +runs on, not Phorge user accounts. -The system accounts Phabricator uses are: +The system accounts Phorge uses are: - The user the webserver runs as. We'll call this `www-user`. - The user the daemons run as. We'll call this `daemon-user`. This @@ -73,22 +73,22 @@ To create these users: the daemons to start as this user. - Create a `vcs-user` if one does not already exist and you plan to set up SSH. When users clone repositories, they will use a URI like - `vcs-user@phabricator.yourcompany.com`, so common names for this user are + `vcs-user@phorge.yourcompany.com`, so common names for this user are `git` or `hg`. Continue below to configure these accounts. -Configuring Phabricator +Configuring Phorge ======================= -Now that you have created or identified these accounts, update the Phabricator +Now that you have created or identified these accounts, update the Phorge configuration to specify them. First, set `phd.user` to the `daemon-user`: ``` -phabricator/ $ ./bin/config set phd.user daemon-user +phorge/ $ ./bin/config set phd.user daemon-user ``` Restart the daemons to make sure this configuration works properly. They should @@ -97,7 +97,7 @@ start as the correct user automatically. If you're using a `vcs-user` for SSH, you should also configure that: ``` -phabricator/ $ ./bin/config set diffusion.ssh-user vcs-user +phorge/ $ ./bin/config set diffusion.ssh-user vcs-user ``` Next, you'll set up `sudo` permissions so these users can interact with one @@ -214,7 +214,7 @@ disabled. If you plan to use authenticated HTTP, you (and all other users) also need to configure a VCS password for your account in {nav Settings > VCS Password}. -Your VCS password must be a different password than your main Phabricator +Your VCS password must be a different password than your main Phorge password because VCS passwords are very easy to accidentally disclose. They are often stored in plaintext in world-readable files, observable in `ps` output, and present in command output and logs. We strongly encourage you to use SSH @@ -234,10 +234,10 @@ Configuring SSH SSH access requires some additional setup. You will configure and run a second, restricted copy of `sshd` on the machine, on a different port from the standard `sshd`. This special copy of `sshd` will serve repository requests and provide -other Phabricator SSH services. +other Phorge SSH services. -NOTE: The Phabricator `sshd` service **MUST** be 6.2 or newer, because -Phabricator relies on the `AuthorizedKeysCommand` option. +NOTE: The Phorge `sshd` service **MUST** be 6.2 or newer, because +Phorge relies on the `AuthorizedKeysCommand` option. Before continuing, you must choose a strategy for which port each copy of `sshd` will run on. The next section lays out various approaches. @@ -273,7 +273,7 @@ This may be very easy to set up, particularly if you are hosted in AWS, and is often the simplest and cleanest approach. **Swap Ports**: You can move the administrative `sshd` to a new port, then run -Phabricator `sshd` on port 22. This is somewhat complicated and can be a bit +Phorge `sshd` on port 22. This is somewhat complicated and can be a bit risky if you make a mistake. See "Moving the sshd Port" below for help. **Change Client Config**: You can run on a nonstandard port, but configure SSH @@ -282,7 +282,7 @@ when connecting to the host. To do this, add a section like this to your `~/.ssh/config`: ``` -Host phabricator.corporation.com +Host phorge.corporation.com Port 2222 ``` @@ -295,7 +295,7 @@ A downside to this approach is that your users will each need to set up their `~/.ssh/config` files individually. This file also allows you to define short names for hosts using the `Host` and -`HostName` options. If you choose to do this, be aware that Phabricator uses +`HostName` options. If you choose to do this, be aware that Phorge uses remote/clone URIs to figure out which repository it is operating in, but can not resolve host aliases defined in your `ssh` config. If you create host aliases they may break some features related to repository identification. @@ -327,7 +327,7 @@ Now that you've decided how you'll handle port assignment, you're ready to continue `sshd` setup. If you plan to connect to a port other than `22`, you should set this port -as `diffusion.ssh-port` in your Phabricator config: +as `diffusion.ssh-port` in your Phorge config: ``` $ ./bin/config set diffusion.ssh-port 2222 @@ -336,22 +336,22 @@ $ ./bin/config set diffusion.ssh-port 2222 This port is not special, and you are free to choose a different port, provided you make the appropriate configuration adjustment below. -**Configure and Start Phabricator SSHD**: Now, you'll configure and start a -copy of `sshd` which will serve Phabricator services, including repositories, +**Configure and Start Phorge SSHD**: Now, you'll configure and start a +copy of `sshd` which will serve Phorge services, including repositories, over SSH. This instance will use a special locked-down configuration that uses -Phabricator to handle authentication and command execution. +Phorge to handle authentication and command execution. There are three major steps: - - Create a `phabricator-ssh-hook.sh` file. - - Create a `sshd_phabricator` config file. + - Create a `phorge-ssh-hook.sh` file. + - Create a `sshd_phorge config file. - Start a copy of `sshd` using the new configuration. -**Create `phabricator-ssh-hook.sh`**: Copy the template in -`phabricator/resources/sshd/phabricator-ssh-hook.sh` to somewhere like -`/usr/libexec/phabricator-ssh-hook.sh` and edit it to have the correct +**Create `phorge-ssh-hook.sh`**: Copy the template in +`phorge/resources/sshd/phorge-ssh-hook.sh` to somewhere like +`/usr/libexec/phorge-ssh-hook.sh` and edit it to have the correct settings. Both the script itself **and** the parent directory the script resides in must @@ -359,15 +359,15 @@ be owned by `root`, and the script must have `755` permissions: ``` $ sudo chown root /path/to/somewhere/ -$ sudo chown root /path/to/somewhere/phabricator-ssh-hook.sh -$ sudo chmod 755 /path/to/somewhere/phabricator-ssh-hook.sh +$ sudo chown root /path/to/somewhere/phorge-ssh-hook.sh +$ sudo chmod 755 /path/to/somewhere/phorge-ssh-hook.sh ``` If you don't do this, `sshd` will refuse to execute the hook. -**Create `sshd_config` for Phabricator**: Copy the template in -`phabricator/resources/sshd/sshd_config.phabricator.example` to somewhere like -`/etc/ssh/sshd_config.phabricator`. +**Create `sshd_config` for Phorge**: Copy the template in +`phorge/resources/sshd/sshd_config.phorge.example` to somewhere like +`/etc/ssh/sshd_config.phorge`. Open the file and edit the `AuthorizedKeysCommand`, `AuthorizedKeysCommandUser`, and `AllowUsers` settings to be correct for your @@ -376,20 +376,20 @@ system. This configuration file also specifies the `Port` the service should run on. If you intend to run on a non-default port, adjust it now. -**Start SSHD**: Now, start the Phabricator `sshd`: +**Start SSHD**: Now, start the Phorge `sshd`: - sudo /path/to/sshd -f /path/to/sshd_config.phabricator + sudo /path/to/sshd -f /path/to/sshd_config.phorge If you did everything correctly, you should be able to run this command: ``` -$ echo {} | ssh vcs-user@phabricator.yourcompany.com conduit conduit.ping +$ echo {} | ssh vcs-user@phorge.yourcompany.com conduit conduit.ping ``` ...and get a response like this: ```lang=json -{"result":"phabricator.yourcompany.com","error_code":null,"error_info":null} +{"result":"phorge.yourcompany.com","error_code":null,"error_info":null} ``` If you get an authentication error, make sure you added your public key in @@ -421,7 +421,7 @@ Troubleshooting HTTP Some general tips for troubleshooting problems with HTTP: - - Make sure `diffusion.allow-http-auth` is enabled in your Phabricator config. + - Make sure `diffusion.allow-http-auth` is enabled in your Phorge config. - Make sure HTTP serving is enabled for the repository you're trying to clone. You can find this in {nav Edit Repository > Hosting}. - Make sure you've configured a VCS password. This is separate from your main @@ -467,20 +467,20 @@ Some general tips for troubleshooting problems with SSH: You should see an `svn checkout svn+ssh://...`, `git clone ssh://...` or `hg clone ssh://...` command. Run that command verbatim to clone the repository. - - Check your `phabricator-ssh-hook.sh` file for proper settings. - - Check your `sshd_config.phabricator` file for proper settings. + - Check your `phorge-ssh-hook.sh` file for proper settings. + - Check your `sshd_config.phorge` file for proper settings. To troubleshoot SSH setup: connect to the server with `ssh`, without running a command. You may need to use the `-T` flag, and will need to use `-p` if you are running on a nonstandard port. You should see a message like this one: - $ ssh -T -p 2222 vcs-user@phabricator.yourcompany.com - phabricator-ssh-exec: Welcome to Phabricator. + $ ssh -T -p 2222 vcs-user@phorge.yourcompany.com + phorge-ssh-exec: Welcome to Phorge. You are logged in as alincoln. You haven't specified a command to run. This means you're requesting an - interactive shell, but Phabricator does not provide an interactive shell over + interactive shell, but Phorge does not provide an interactive shell over SSH. Usually, you should run a command like `git clone` or `hg push` rather than @@ -498,7 +498,7 @@ settings: - The `vcs-user` has `NP` in `/etc/shadow`. - The `vcs-user` has `/bin/sh` or some other valid shell in `/etc/passwd`. - Your SSH private key is correct, and you've added the corresponding - public key to Phabricator in the Settings panel. + public key to Phorge in the Settings panel. If you can get this far, but can't execute VCS commands like `git clone`, there is probably an issue with your `sudoers` configuration. Check: @@ -513,7 +513,7 @@ is probably an issue with your `sudoers` configuration. Check: It may also be helpful to run `sshd` in debug mode: - $ /path/to/sshd -d -d -d -f /path/to/sshd_config.phabricator + $ /path/to/sshd -d -d -d -f /path/to/sshd_config.phorge This will run it in the foreground and emit a large amount of debugging information when you connect to it. @@ -525,7 +525,7 @@ doing something like this: $ sudo -E -n -u daemon-user -- /path/to/some/vcs-binary --help That will try to run the binary via `sudo` in a manner similar to the way that -Phabricator will run it. This can give you better error messages about issues +Phorge will run it. This can give you better error messages about issues with `sudoers` configuration. @@ -533,7 +533,7 @@ Miscellaneous Troubleshooting ============================= - If you're getting an error about `svnlook` not being found, add the path - where `svnlook` is located to the Phabricator configuration + where `svnlook` is located to the Phorge configuration `environment.append-paths` (even if it already appears in PATH). This issue is caused by SVN wiping the environment (including PATH) when invoking commit hooks. @@ -543,10 +543,10 @@ Moving the sshd Port ==================== If you want to move the standard (administrative) `sshd` to a different port to -make Phabricator repository URIs cleaner, this section has some tips. +make Phorge repository URIs cleaner, this section has some tips. This is optional, and it is normally easier to do this by putting a load -balancer in front of Phabricator and having it accept TCP traffic on port 22 +balancer in front of Phorge and having it accept TCP traffic on port 22 and forward it to some other port. When moving `sshd`, be careful when editing the configuration. If you get it @@ -582,17 +582,43 @@ Very carefully, restart `sshd`. Verify that you can connect on the new port: ssh -p 222 ... -Now you can move the Phabricator `sshd` to port 22, then adjust the value -for `diffusion.ssh-port` in your Phabricator configuration. +Now you can move the Phorge `sshd` to port 22, then adjust the value +for `diffusion.ssh-port` in your Phorge configuration. +You can set up and enable this systemd unit to start the second sshd +daemon on every reboot: + +``` +name=/etc/systemd/system/phorge-ssh.service,lang=ini +[Unit] +Description=Phorge sshd +Documentation=https://we.phorge.it/book/phorge/article/diffusion_hosting/#sshd-setup +After=network.target auditd.service + +[Service] +ExecStartPre=/usr/sbin/sshd -t -f /path/to/config_file.edited +ExecStart=/usr/sbin/sshd -f /path/to/config_file.edited +ExecReload=/usr/sbin/sshd -t -f /path/to/config_file.edited +ExecReload=/bin/kill -HUP $MAINPID +KillMode=process +Restart=on-failure +RestartPreventExitStatus=255 +Type=notify +RuntimeDirectory=sshd +RuntimeDirectoryMode=0755 + +[Install] +WantedBy=multi-user.target +Alias=phorge-sshd.service +``` No Direct Pushes ================ You may get an error about "No Direct Pushes" when trying to push. This means you are pushing directly to the repository instead of pushing through -Phabricator. This is not supported: writes to hosted repositories must go -through Phabricator so it can perform authentication, enforce permissions, +Phorge. This is not supported: writes to hosted repositories must go +through Phorge so it can perform authentication, enforce permissions, write logs, proxy requests, apply rewriting, etc. One way to do a direct push by mistake is to use a `file:///` URI to interact @@ -616,9 +642,9 @@ MUST configure things as described above for hosted repositories to work. The technical reason this error occurs is that the `PHABRICATOR_USER` variable is not defined in the environment when commit hooks run. This variable is set -by Phabricator when a request passes through the authentication layer that this +by Phorge when a request passes through the authentication layer that this document provides instructions for configuring. Its absence indicates that the -request did not pass through Phabricator. +request did not pass through Phorge. Next Steps diff --git a/src/docs/user/userguide/diffusion_managing.diviner b/src/docs/user/userguide/diffusion_managing.diviner index e3743526e9..c0b97cb53c 100644 --- a/src/docs/user/userguide/diffusion_managing.diviner +++ b/src/docs/user/userguide/diffusion_managing.diviner @@ -37,7 +37,7 @@ Basics: Callsigns ================= Each repository can optionally be identified by a "callsign", which is a short -uppercase string like "P" (for Phabricator) or "ARC" (for Arcanist). +uppercase string like "P" (for Phorge) or "ARC" (for Arcanist). The primary goal of callsigns is to namespace commits to SVN repositories: if you use multiple SVN repositories, each repository has a revision 1, revision 2, @@ -55,11 +55,11 @@ If you choose to assign a callsign to a repository, it must be unique within an install but do not need to be globally unique, so you are free to use the single-letter callsigns for brevity. For example, Facebook uses "E" for the Engineering repository, "O" for the Ops repository, "Y" for a Yum package -repository, and so on, while Phabricator uses "P" and Arcanist uses "ARC". +repository, and so on, while Phorge uses "P" and Arcanist uses "ARC". Keeping callsigns brief will make them easier to use, and the use of one-character callsigns is encouraged if they are reasonably evocative. -If you configure a callsign like `XYZ`, Phabricator will activate callsign URIs +If you configure a callsign like `XYZ`, Phorge will activate callsign URIs and activate the callsign identifier (like `rXYZ`) for the repository. These more human-readable identifiers can make things a little easier to interact with. @@ -104,7 +104,7 @@ already, so everything will work fine. The majority of repositories do not need to adjust this setting. If your repository is primarily written in some other encoding, specify it here -so Phabricator can convert from it properly when reading content to embed in +so Phorge can convert from it properly when reading content to embed in a webpage or email. @@ -131,7 +131,7 @@ users. This option is only available in Git and Mercurial, because it is impossible to make dangerous changes in Subversion. -This option has no effect if a repository is not hosted because Phabricator +This option has no effect if a repository is not hosted because Phorge can not prevent dangerous changes in a remote repository it is merely observing. @@ -195,7 +195,7 @@ Policies: View ============== The view policy for a repository controls who can view the repository from -the web UI and clone, fetch, or check it out from Phabricator. +the web UI and clone, fetch, or check it out from Phorge. Users who can view a repository can also access the "Manage" interface to review information about the repository and examine the edit history, but can @@ -220,7 +220,7 @@ Policies: Push The push policy for a repository controls who can push changes to the repository. -This policy has no effect if Phabricator is not hosting the repository, because +This policy has no effect if Phorge is not hosting the repository, because it can not control who is allowed to make changes to a remote repository it is merely observing. @@ -230,7 +230,7 @@ You do not need to be able to edit a repository to push to it. Further restrictions on who can push (and what they can push) can be configured for hosted repositories with Herald, which allows you to write more -sophisticated rules that evaluate when Phabricator receives a push. To get +sophisticated rules that evaluate when Phorge receives a push. To get started with Herald, see @{article:Herald User Guide}. Additionally, Git and Mercurial repositories have a setting which allows @@ -241,7 +241,7 @@ will prevent any users from pushing changes which rewrite or destroy history. URIs ==== -The **URIs** panel allows you to add and manage URIs which Phabricator will +The **URIs** panel allows you to add and manage URIs which Phorge will fetch from, serve from, and push to. These options are covered in detail in @{article:Diffusion User Guide: URIs}. @@ -292,14 +292,14 @@ whatever other kind of creative nonsense they manage to dream up. Branches ======== -The **Branches** panel allows you to configure how Phabricator interacts with +The **Branches** panel allows you to configure how Phorge interacts with branches. This panel is not available for Subversion repositories, because Subversion does not have formal branches. You can configure a **Default Branch**. This controls which branch is shown by -default in the UI. If no branch is provided, Phabricator will use `master` in +default in the UI. If no branch is provided, Phorge will use `master` in Git and `default` in Mercurial. **Fetch Refs**: In Git, if you are observing a remote repository, you can @@ -324,13 +324,13 @@ refs/tags/* This may be useful if the remote is on a service like GitHub, GitLab, or Gerrit and uses custom refs (like `refs/pull/` or `refs/changes/`) to store -metadata that you don't want to bring into Phabricator. +metadata that you don't want to bring into Phorge. **Permanent Refs**: To learn more about permanent refs, see: - @{article:Diffusion User Guide: Permanent Refs} -By default, Phabricator considers all branches to be permanent refs. If you +By default, Phorge considers all branches to be permanent refs. If you only want some branches to be treated as permanent refs, specify them here. When specifying branches, you should enter one branch name per line. You can @@ -356,7 +356,7 @@ For more details, see @{article:Harbormaster User Guide}. Automation ========== -The **Automation** panel configures support for allowing Phabricator to make +The **Automation** panel configures support for allowing Phorge to make writes directly to the repository, so that it can perform operations like automatically landing revisions from the web UI. @@ -400,7 +400,7 @@ All three identifiers can be used to refer to the repository in cases where the intent is unambiguous, but only the first two forms work in ambiguous contexts. -For example, if you type `R123` or `rXY` into a comment, Phabricator will +For example, if you type `R123` or `rXY` into a comment, Phorge will recognize them as references to the repository. If you type `xylophone`, it assumes you mean the word "xylophone". @@ -421,7 +421,7 @@ Each commit may have several identifiers: works if a repository has a callsign. - Any unique prefix of the commit hash. -Git and Mercurial use commit hashes to identify commits, and Phabricator will +Git and Mercurial use commit hashes to identify commits, and Phorge will recognize a commit if the hash prefix is unique and sufficiently long. Commit hashes qualified with a repository identifier must be at least 5 characters long; unqualified commit hashes must be at least 7 characters long. @@ -429,7 +429,7 @@ long; unqualified commit hashes must be at least 7 characters long. In Subversion, commit identifiers are sequential integers and prefixes can not be used to identify them. -When rendering the name of a Git or Mercurial commit hash, Phabricator tends to +When rendering the name of a Git or Mercurial commit hash, Phorge tends to shorten it to 12 characters. This "short length" is relatively long compared to Git itself (which often uses 7 characters). See this post on the LKML for a historical explanation of Git's occasional internal use of 7-character hashes: diff --git a/src/docs/user/userguide/diffusion_permanent.diviner b/src/docs/user/userguide/diffusion_permanent.diviner index fba4341c0c..f28e3259f7 100644 --- a/src/docs/user/userguide/diffusion_permanent.diviner +++ b/src/docs/user/userguide/diffusion_permanent.diviner @@ -7,8 +7,8 @@ Overview ======== Diffusion can close tasks and revisions and take other actions when commits -appear in a repository (either because they were pushed to Phabricator, or -because they were pushed to some remote which Phabricator is observing). +appear in a repository (either because they were pushed to Phorge, or +because they were pushed to some remote which Phorge is observing). This document explains when Diffusion acts on commits and how to configure this behavior. @@ -24,11 +24,11 @@ for example, many tools push temporary commits to secret places like `refs/pull/123`, `refs/notes/*`, or `refs/changes/12/345678/1`. Sometimes, human users intentionally push changes to branches like -"tmp-hack-ignore-123". This is formally discouraged by Phabricator, but the +"tmp-hack-ignore-123". This is formally discouraged by Phorge, but the practice is so widespread that we've given up trying to stop anyone from doing it. -Phabricator will import these commits and create pages for them so you can view +Phorge will import these commits and create pages for them so you can view them in the web UI and link to them, but does not take any other actions until they are "published". @@ -40,7 +40,7 @@ using a tool like GitHub) will not. Usually, commits are published by pushing them directly to a permanent branch like "master", or by merging a temporary branch into a permanent branch. -When a commit is published, Phabricator acts on it and: +When a commit is published, Phorge acts on it and: - sends email; - delivers notifications; diff --git a/src/docs/user/userguide/diffusion_symbols.diviner b/src/docs/user/userguide/diffusion_symbols.diviner index 7d14ad92b2..6d6cc8847b 100644 --- a/src/docs/user/userguide/diffusion_symbols.diviner +++ b/src/docs/user/userguide/diffusion_symbols.diviner @@ -5,7 +5,7 @@ Guide to configuring and using the symbol index. = Overview = -Phabricator can maintain a symbol index, which keeps track of where classes +Phorge can maintain a symbol index, which keeps track of where classes and functions are defined in the codebase. Once you set up indexing, you can use the index to do things like: @@ -25,11 +25,11 @@ your codebase and set up a cronjob which pipes its output to: ./scripts/symbols/import_repository_symbols.php -Phabricator includes a script which can identify symbols in PHP projects: +Phorge includes a script which can identify symbols in PHP projects: ./scripts/symbols/generate_php_symbols.php -Phabricator also includes a script which can identify symbols in any +Phorge also includes a script which can identify symbols in any programming language that has classes and/or functions, and is supported by Exuberant Ctags (http://ctags.sourceforge.net): @@ -59,7 +59,7 @@ from the project root (where ".arcconfig" is) beginning with a "/". You can look at `generate_php_symbols.php` for an example of how you might write such a script, and run this command to see its output: - $ cd phabricator/ + $ cd phorge/ $ find . -type f -name '*.php' | ./scripts/symbols/generate_php_symbols.php To actually build the symbol index, pipe this data to the @@ -83,12 +83,12 @@ You can configure some more options by going to {nav Diffusion > (Select You can leave this blank for "All languages". - **Uses Symbols From**: If this project depends on other repositories, add the other repositories which symbols should be looked for here. For example, - Phabricator lists "Arcanist" because it uses classes and functions defined + Phorge lists "Arcanist" because it uses classes and functions defined in `arcanist/`. == External Symbols == -By @{article@phabcontrib:Adding New Classes}, you can teach Phabricator +By @{article@contrib:Adding New Classes}, you can teach Phorge about symbols from the outside world. Extend @{class:DiffusionExternalSymbolsSource}; Once loaded, your new implementation will be used any time a symbol is queried. diff --git a/src/docs/user/userguide/diffusion_updates.diviner b/src/docs/user/userguide/diffusion_updates.diviner index 7012e6228e..dc3c7dfef1 100644 --- a/src/docs/user/userguide/diffusion_updates.diviner +++ b/src/docs/user/userguide/diffusion_updates.diviner @@ -6,7 +6,7 @@ Explains how Diffusion updates repositories to discover new changes. Overview ======== -When Phabricator is configured to import repositories which are hosted +When Phorge is configured to import repositories which are hosted elsewhere, it needs to poll those repositories for changes. If it polls too frequently, it can create too much load locally and on remote services. If it polls too rarely, it may take a long time for commits to show up in the web @@ -15,11 +15,11 @@ interface. This document describes the rules around polling and how to understand and adjust the behavior. In general: - - Phabricator chooses a default poll interval based on repository + - Phorge chooses a default poll interval based on repository activity. These intervals range from every 15 seconds (for active repositories) to every 6 hours (for repositories with no commits in two months). - - If you use `arc` to push commits, or you host repositories on Phabricator, + - If you use `arc` to push commits, or you host repositories on Phorge, repositories automatically update after changes are pushed. - If you don't use `arc` and your repository is hosted elsewhere, this document describes ways you can make polling more responsive. @@ -28,7 +28,7 @@ adjust the behavior. In general: Default Behavior ================ -By default, Phabricator determines how frequently to poll repositories by +By default, Phorge determines how frequently to poll repositories by examining how long it has been since the last commit. In most cases this is fairly accurate and produces good behavior. In particular, it automatically reduces the polling frequency for rarely-used repositories. This dramatically @@ -77,21 +77,21 @@ update frequency so the import finishes as quickly as possible. Triggering Repository Updates ============================= -If you want Phabricator to update a repository more quickly than the default +If you want Phorge to update a repository more quickly than the default update frequency (for example, because you just pushed a commit to it), you can -tell Phabricator that it should schedule an update as soon as possible. +tell Phorge that it should schedule an update as soon as possible. There are several ways to do this: - If you push changes with `arc land` or `arc commit`, this will be done for you automatically. These commits should normally be recognized within a few seconds. - - If your repository is hosted on Phabricator, this will also be done for you + - If your repository is hosted on Phorge, this will also be done for you automatically. - You can schedule an update from the web interface, in Diffusion > (Choose a Repository) > Manage Repository > Status > Update Now. - You can make a call to the Conduit API method `diffusion.looksoon`. This - hints to Phabricator that it should poll a repository as soon as it can. + hints to Phorge that it should poll a repository as soon as it can. All of the other mechanisms do this under the hood. In particular, you may be able to add a commit hook to your external repository @@ -109,7 +109,7 @@ Troubleshooting Updates You can manually run a repository update from the command line to troubleshoot issues, using the `--trace` flag to get full details: - phabricator/ $ ./bin/repository update --trace + phorge/ $ ./bin/repository update --trace To catch potential issues with permissions, run this command as the same user that the daemons run as. diff --git a/src/docs/user/userguide/diffusion_uris.diviner b/src/docs/user/userguide/diffusion_uris.diviner index a19d38d3a5..1e186ce6ee 100644 --- a/src/docs/user/userguide/diffusion_uris.diviner +++ b/src/docs/user/userguide/diffusion_uris.diviner @@ -6,38 +6,38 @@ Guide to configuring repository URIs for fetching, cloning and mirroring. Overview ======== -Phabricator can create, host, observe, mirror, proxy, and import repositories. +Phorge can create, host, observe, mirror, proxy, and import repositories. For example, you can: -**Host Repositories**: Phabricator can host repositories locally. Phabricator +**Host Repositories**: Phorge can host repositories locally. Phorge maintains the writable master version of the repository, and you can push and pull the repository. This is the most straightforward kind of repository configuration, and similar to repositories on other services like GitHub or Bitbucket. -**Observe Repositories**: Phabricator can create a copy of an repository which +**Observe Repositories**: Phorge can create a copy of an repository which is hosted elsewhere (like GitHub or Bitbucket) and track updates to the remote -repository. This will create a read-only copy of the repository in Phabricator. +repository. This will create a read-only copy of the repository in Phorge. -**Mirror Repositories**: Phabricator can publish any repository to mirrors, +**Mirror Repositories**: Phorge can publish any repository to mirrors, overwriting them with an exact copy of the repository that stays up to date as -the source changes. This works with both local repositories that Phabricator is -hosting and remote repositories that Phabricator is observing. +the source changes. This works with both local repositories that Phorge is +hosting and remote repositories that Phorge is observing. **Proxy Repositories**: If you are observing a repository, you can allow users -to read Phabricator's copy of the repository. Phabricator supports granular +to read Phorge's copy of the repository. Phorge supports granular read permissions, so this can let you open a private repository up a little bit in a flexible way. **Import Repositories**: If you have a repository elsewhere that you want to -host on Phabricator, you can observe the remote repository first, then turn +host on Phorge, you can observe the remote repository first, then turn the tracking off once the repository fully synchronizes. This allows you to -copy an existing repository and begin hosting it in Phabricator. +copy an existing repository and begin hosting it in Phorge. You can also import repositories by creating an empty hosted repository and then pushing everything to the repository directly. -You configure the behavior of a Phabricator repository by adding and +You configure the behavior of a Phorge repository by adding and configuring URIs and marking them to be fetched from, mirrored to, clonable, and so on. By configuring all the URIs that a repository should interact with and expose to users, you configure the read, write, and mirroring behavior @@ -50,12 +50,12 @@ detail. Host a Repository ================= -You can create new repositories that Phabricator will host, like you would -create repositories on services like GitHub or Bitbucket. Phabricator will -serve a read-write copy of the repository and you can clone it from Phabricator -and push changes to Phabricator. +You can create new repositories that Phorge will host, like you would +create repositories on services like GitHub or Bitbucket. Phorge will +serve a read-write copy of the repository and you can clone it from Phorge +and push changes to Phorge. -If you haven't already, you may need to configure Phabricator for hosting +If you haven't already, you may need to configure Phorge for hosting before you can create your first hosted repository. For a detailed guide, see @{article:Diffusion User Guide: Repository Hosting}. @@ -64,7 +64,7 @@ This is the default mode for new repositories. To host a repository: - Create a new repository. - Activate it. -Phabricator will create an empty repository and allow you to fetch from it and +Phorge will create an empty repository and allow you to fetch from it and push to it. @@ -72,10 +72,10 @@ Observe a Repository ==================== If you have an existing repository hosted on another service (like GitHub, -Bitbucket, or a private server) that you want to work with in Phabricator, -you can configure Phabricator to observe it. +Bitbucket, or a private server) that you want to work with in Phorge, +you can configure Phorge to observe it. -When observing a repository, Phabricator will keep track of changes in the +When observing a repository, Phorge will keep track of changes in the remote repository and allow you to browse and interact with the repository from the web UI in Diffusion and other applications, but you can continue hosting it elsewhere. @@ -88,7 +88,7 @@ To observe a repository: - If necessary, configure a credential. - Activate the repository. -Phabricator will perform an initial import of the repository, creating a local +Phorge will perform an initial import of the repository, creating a local read-only copy. Once this process completes, it will continue keeping track of changes in the remote, fetching them, and reflecting them in the UI. @@ -98,21 +98,21 @@ Mirror a Repository NOTE: Mirroring is not supported in Subversion. -You can create a read-only mirror of an existing repository. Phabricator will +You can create a read-only mirror of an existing repository. Phorge will continuously publish the state of the source repository to the mirror, creating an exact copy. -For example, if you have a repository hosted in Phabricator that you want to -mirror to GitHub, you can configure Phabricator to automatically maintain the +For example, if you have a repository hosted in Phorge that you want to +mirror to GitHub, you can configure Phorge to automatically maintain the mirror. This is how the upstream repositories are set up. The mirror copy must be read-only for users because any writes made to the -mirror will be undone when Phabricator updates it. The mirroring process copies +mirror will be undone when Phorge updates it. The mirroring process copies the entire repository state exactly, so the remote state will be completely replaced with an exact copy of the source repository. This may remove or destroy information. Normally, you should only mirror to an empty repository. -You can mirror any repository, even if Phabricator is only observing it and not +You can mirror any repository, even if Phorge is only observing it and not hosting it directly. To begin mirroring a repository: @@ -133,14 +133,14 @@ Import a Repository =================== If you have an existing repository that you want to move so it is hosted on -Phabricator, there are three ways to do it: +Phorge, there are three ways to do it: **Observe First**: //(Git, Mercurial)// Observe the existing repository first, -according to the instructions above. Once Phabricator's copy of the repository +according to the instructions above. Once Phorge's copy of the repository is fully synchronized, change the **I/O Type** for the **Observe** URI to **None** to stop fetching changes from the remote. -By default, this will automatically make Phabricator's copy of the repository +By default, this will automatically make Phorge's copy of the repository writable, and you can begin pushing to it. If you've adjusted URI configuration away from the defaults, you may need to set at least one URI to **Read/Write** mode so you can push to it. @@ -169,7 +169,7 @@ repository. Builtin Clone URIs ================== -By default, Phabricator automatically exposes and activates HTTP, HTTPS and +By default, Phorge automatically exposes and activates HTTP, HTTPS and SSH clone URIs by examining configuration. **HTTP**: The `http://` clone URI will be available if these conditions are @@ -198,7 +198,7 @@ Customizing Displayed Clone URIs ================================ If you have an unusual configuration and want the UI to offers users specific -clone URIs other than the URIs that Phabricator serves or interacts with, you +clone URIs other than the URIs that Phorge serves or interacts with, you can add those URIs with the **I/O Type** set to **None** and then set their **Display Type** to **Always**. @@ -214,11 +214,11 @@ Reference: I/O Types This section details the available **I/O Type** options for URIs. -Each repository has some **builtin** URIs. These are URIs hosted by Phabricator +Each repository has some **builtin** URIs. These are URIs hosted by Phorge itself. The modes available for each URI depend primarily on whether it is a builtin URI or not. -**Default**: This setting has Phabricator guess the correct option for the +**Default**: This setting has Phorge guess the correct option for the URI. For **builtin** URIs, the default behavior is //Read/Write// if the repository @@ -228,7 +228,7 @@ For custom URIs, the default type is //None// because we can not automatically guess if you want to ignore, observe, or mirror a URI and //None// is the safest default. -**Observe**: Phabricator will observe this repository and regularly fetch any +**Observe**: Phorge will observe this repository and regularly fetch any changes made to it to a local read-only copy. You can not observe builtin URIs because reading a repository from itself @@ -240,11 +240,11 @@ authorities: the observed remote copy and the hosted local copy. Take the other URI out of //Read/Write// mode first. WARNING: If you observe a remote repository, the entire state of the working -copy that Phabricator maintains will be deleted and replaced with the state of -the remote. If some changes are present only in Phabricator's working copy, +copy that Phorge maintains will be deleted and replaced with the state of +the remote. If some changes are present only in Phorge's working copy, they will be unrecoverably destroyed. -**Mirror**: Phabricator will push any changes made to this repository to the +**Mirror**: Phorge will push any changes made to this repository to the remote URI, keeping a read-only mirror hosted at that URI up to date. This works for both observed and hosted repositories. @@ -253,30 +253,30 @@ This option is not available for builtin URIs because it does not make sense to mirror a repository to itself. It is possible to mirror a repository to another repository that is also -hosted by Phabricator by adding that other repository's URI, although this is +hosted by Phorge by adding that other repository's URI, although this is silly and probably very rarely of any use. WARNING: If you mirror to a remote repository, the entire state of that remote -will be replaced with the state of the working copy Phabricator maintains. If +will be replaced with the state of the working copy Phorge maintains. If some changes are present only in the remote, they will be unrecoverably destroyed. -**None**: Phabricator will not fetch changes from or push changes to this URI. +**None**: Phorge will not fetch changes from or push changes to this URI. For builtin URIs, it will not let users fetch changes from or push changes to this URI. You can use this mode to turn off an Observe URI after an import, stop a Mirror URI from updating, or to add URIs that you're only using to customize which -clone URIs are displayed to the user but don't want Phabricator to interact +clone URIs are displayed to the user but don't want Phorge to interact with directly. -**Read Only**: Phabricator will serve the repository from this URI in read-only +**Read Only**: Phorge will serve the repository from this URI in read-only mode. Users will be able to fetch from it but will not be able to push to it. -Because Phabricator must be able to serve the repository from URIs configured +Because Phorge must be able to serve the repository from URIs configured in this mode, this option is only available for builtin URIs. -**Read/Write**: Phabricator will serve the repository from this URI in +**Read/Write**: Phorge will serve the repository from this URI in read/write mode. Users will be able to fetch from it and push to it. URIs can not be set into this mode if another URI is set to //Observe// mode, @@ -284,7 +284,7 @@ because that would mean the repository had two different authorities: the observed remote copy and the hosted local copy. Take the other URI out of //Observe// mode first. -Because Phabricator must be able to serve the repository from URIs configured +Because Phorge must be able to serve the repository from URIs configured in this mode, this option is only available for builtin URIs. @@ -293,7 +293,7 @@ Reference: Display Types This section details the available **Display Type** options for URIs. -**Default**: Phabricator will guess the correct option for the URI. It +**Default**: Phorge will guess the correct option for the URI. It guesses based on the configured **I/O Type** and whether the URI is **builtin** or not. @@ -315,5 +315,5 @@ Next Steps Continue by: - - configuring Phabricator to host repositories with + - configuring Phorge to host repositories with @{article:Diffusion User Guide: Repository Hosting}. diff --git a/src/docs/user/userguide/diviner.diviner b/src/docs/user/userguide/diviner.diviner index 01484be14c..1c0b6f4f78 100644 --- a/src/docs/user/userguide/diviner.diviner +++ b/src/docs/user/userguide/diviner.diviner @@ -8,7 +8,7 @@ Overview Diviner is an application for creating technical documentation. -This article is maintained in a text file in the Phabricator repository and +This article is maintained in a text file in the Phorge repository and generated into the display document you are currently reading using Diviner. Beyond generating articles, Diviner can also analyze source code and generate @@ -20,7 +20,7 @@ Generating Documentation To generate documentation, run: - phabricator/ $ ./bin/diviner generate --book + phorge/ $ ./bin/diviner generate --book Diviner ".book" Files diff --git a/src/docs/user/userguide/drydock.diviner b/src/docs/user/userguide/drydock.diviner index 0d43f7f3f0..52ef2d74af 100644 --- a/src/docs/user/userguide/drydock.diviner +++ b/src/docs/user/userguide/drydock.diviner @@ -256,5 +256,5 @@ Continue by: - understanding Drydock security concerns with @{article:Drydock User Guide: Security}; or - learning about blueprints in @{article:Drydock Blueprints}; or - - allowing Phabricator to write to repositories with + - allowing Phorge to write to repositories with @{article:Drydock User Guide: Repository Automation}. diff --git a/src/docs/user/userguide/drydock_hosts.diviner b/src/docs/user/userguide/drydock_hosts.diviner index 1b8f22cce1..eb506a9c0a 100644 --- a/src/docs/user/userguide/drydock_hosts.diviner +++ b/src/docs/user/userguide/drydock_hosts.diviner @@ -41,20 +41,20 @@ installing software on hosts. You'll need to make sure any hosts are configured properly with any software you need, and have tools like `git`, `hg` or `svn` that may be required to interact with working copies. -You do **not** need to install PHP, arcanist, or Phabricator on the +You do **not** need to install PHP, arcanist, or Phorge on the hosts unless you are specifically running `arc` commands. **You must configure authentication.** Drydock also does not handle credentials for VCS operations. If you're interacting with repositories hosted on -Phabricator, the simplest way to set this up is something like this: +Phorge, the simplest way to set this up is something like this: - - Create a new bot user in Phabricator. + - Create a new bot user in Phorge. - In {nav Settings > SSH Public Keys}, add a public key or generate a keypair. - Put the private key on your build hosts as `~/.ssh/id_rsa` for whatever user you're connecting with. -This will let processes on the host access Phabricator as the bot user, and +This will let processes on the host access Phorge as the bot user, and use the bot user's permissions to pull and push changes. If you're using hosted repositories from an external service, you can follow diff --git a/src/docs/user/userguide/drydock_quick_start.diviner b/src/docs/user/userguide/drydock_quick_start.diviner index 4b0bd7110d..977b07a482 100644 --- a/src/docs/user/userguide/drydock_quick_start.diviner +++ b/src/docs/user/userguide/drydock_quick_start.diviner @@ -30,7 +30,7 @@ Install `git`, `hg` or `svn` if you haven't already and set up private keys for `builder` so it can pull and push any repositories you want to operate on. -If your repository and/or staging area are hosted in Phabricator, you may want +If your repository and/or staging area are hosted in Phorge, you may want to create a corresponding bot account so you can add keys and give it permissions. diff --git a/src/docs/user/userguide/drydock_repository_automation.diviner b/src/docs/user/userguide/drydock_repository_automation.diviner index 35ef932cc2..25b6343e24 100644 --- a/src/docs/user/userguide/drydock_repository_automation.diviner +++ b/src/docs/user/userguide/drydock_repository_automation.diviner @@ -1,7 +1,7 @@ @title Drydock User Guide: Repository Automation @group userguide -Configuring repository automation so Phabricator can push commits. +Configuring repository automation so Phorge can push commits. Overview @@ -11,7 +11,7 @@ IMPORTANT: This feature is very new and some of the capabilities described in this document are not yet available. This feature as a whole is a prototype. By configuring Drydock and Diffusion appropriately, you can enable **Repository -Automation** for a repository. This will allow Phabricator to make changes +Automation** for a repository. This will allow Phorge to make changes to the repository. @@ -25,7 +25,7 @@ Limitations Security ======== -Configuring repository automation amounts to telling Phabricator where it +Configuring repository automation amounts to telling Phorge where it should perform working copy operations (like merges, cherry-picks and pushes) when doing writes. diff --git a/src/docs/user/userguide/drydock_security.diviner b/src/docs/user/userguide/drydock_security.diviner index 9a212437c7..f7df777ef9 100644 --- a/src/docs/user/userguide/drydock_security.diviner +++ b/src/docs/user/userguide/drydock_security.diviner @@ -14,7 +14,7 @@ anything you aren't comfortable with. For example, running unit tests on Drydock normally involves running relatively untrusted code (it often has a single author and has not yet been reviewed) that needs very few capabilities (generally, it only needs to be able to report -results back to Phabricator). In contrast, automating merge requests on Drydock +results back to Phorge). In contrast, automating merge requests on Drydock involves running trusted code that needs more access (it must be able to write to repositories). @@ -61,11 +61,11 @@ configuring Drydock. | Custom | Special Requirements | Use multiple pools. | Absolute | Special Requirements | Completely isolate all resources. -**Zero Isolation**: Run Drydock operations on the same host that Phabricator -runs on. This is only suitable for developing or testing Phabricator. Any -Drydock operation can potentially compromise Phabricator. It is intentionally +**Zero Isolation**: Run Drydock operations on the same host that Phorge +runs on. This is only suitable for developing or testing Phorge. Any +Drydock operation can potentially compromise Phorge. It is intentionally difficult to configure Drydock to operate in this mode. Running Drydock -operations on the Phabricator host is strongly discouraged. +operations on the Phorge host is strongly discouraged. **Low Isolation**: Designate a separate Drydock host and run Drydock operations on it. This is suitable for small installs and provides a reasonable @@ -105,14 +105,14 @@ approach which defuses only the threats you care about. Attackers have three primary targets: - capturing hosts; - - compromising Phabricator; and + - compromising Phorge; and - compromising the integrity of other Drydock processes. **Attacks against hosts** are the least sophisticated. In this scenario, an attacker wants to run a program like a Bitcoin miner or botnet client on hardware that they aren't paying for or which can't be traced to them. They write a "unit test" or which launches this software, then send a revision -containing this "unit test" for review. If Phabricator is configured to +containing this "unit test" for review. If Phorge is configured to automatically run tests on new revisions, it may execute automatically and give the attacker access to computing resources they did not previously control and which can not easily be traced back to them. @@ -125,22 +125,22 @@ prevent untrusted, anonymous contributors from running tests. For example, create a "Trusted Contributors" project and only run tests if a revision author is a member of the project. -**Attacks against Phabricator** are more sophisticated. In this scenario, an -attacker tries to compromise Phabricator itself (for example, to make themselves +**Attacks against Phorge** are more sophisticated. In this scenario, an +attacker tries to compromise Phorge itself (for example, to make themselves an administrator or gain access to an administrator account). -This is made possible if Drydock is running on the same host as Phabricator or -runs on a privileged subnet with access to resources like Phabricator database +This is made possible if Drydock is running on the same host as Phorge or +runs on a privileged subnet with access to resources like Phorge database hosts. Most installs should be concerned about this attack. The best way to defuse this attack is to run Drydock processes on a separate host which is not on a privileged subnet. For example, use a `build.mycompany.com` host or pool for Drydock processes, separate from your -`phabricator.mycompany.com` host or pool. +`phorge.mycompany.com` host or pool. Even if the host is not privileged, many Drydock processes have some level of privilege (enabling them to clone repositories, or report test results back to -Phabricator). Be aware that tests can hijack credentials they are run with, +Phorge). Be aware that tests can hijack credentials they are run with, and potentially hijack credentials given to other processes on the same hosts. You should use credentials with a minimum set of privileges and assume all processes on a host have the highest level of access that any process on the diff --git a/src/docs/user/userguide/events.diviner b/src/docs/user/userguide/events.diviner index e18578288b..d004dad84f 100644 --- a/src/docs/user/userguide/events.diviner +++ b/src/docs/user/userguide/events.diviner @@ -1,7 +1,7 @@ @title Events User Guide: Installing Event Listeners @group userguide -Using Phabricator event listeners to customize behavior. +Using Phorge event listeners to customize behavior. = Overview = @@ -9,27 +9,27 @@ Using Phabricator event listeners to customize behavior. system is strongly discouraged. We have been removing events since 2013 and will continue to remove events in the future. -Phabricator and Arcanist allow you to install custom runtime event listeners +Phorge and Arcanist allow you to install custom runtime event listeners which can react to certain things happening (like a Maniphest Task being edited or a user creating a new Differential Revision) and run custom code to perform logging, synchronize with other systems, or modify workflows. -These listeners are PHP classes which you install beside Phabricator or -Arcanist, and which Phabricator loads at runtime and runs in-process. They +These listeners are PHP classes which you install beside Phorge or +Arcanist, and which Phorge loads at runtime and runs in-process. They require somewhat more effort upfront than simple configuration switches, but are the most direct and powerful way to respond to events. -= Installing Event Listeners (Phabricator) = += Installing Event Listeners (Phorge) = -To install event listeners in Phabricator, follow these steps: +To install event listeners in Phorge, follow these steps: - Write a listener class which extends @{class@arcanist:PhutilEventListener}. - Add it to a libphutil library, or create a new library (for instructions, - see @{article@phabcontrib:Adding New Classes}. - - Configure Phabricator to load the library by adding it to `load-libraries` - in the Phabricator config. - - Configure Phabricator to install the event listener by adding the class - name to `events.listeners` in the Phabricator config. + see @{article@contrib:Adding New Classes}. + - Configure Phorge to load the library by adding it to `load-libraries` + in the Phorge config. + - Configure Phorge to install the event listener by adding the class + name to `events.listeners` in the Phorge config. You can verify your listener is registered in the "Events" tab of DarkConsole. It should appear at the top under "Registered Event Listeners". You can also @@ -42,8 +42,8 @@ To install event listeners in Arcanist, follow these steps: - Write a listener class which extends @{class@arcanist:PhutilEventListener}. - Add it to a libphutil library, or create a new library (for instructions, - see @{article@phabcontrib:Adding New Classes}. - - Configure Phabricator to load the library by adding it to `load` + see @{article@contrib:Adding New Classes}. + - Configure Phorge to load the library by adding it to `load` in the Arcanist config (e.g., `.arcconfig`, or user/global config). - Configure Arcanist to install the event listener by adding the class name to `events.listeners` in the Arcanist config. @@ -54,7 +54,7 @@ event listener. = Example Listener = -Phabricator includes an example event listener, +Phorge includes an example event listener, @{class:PhabricatorExampleEventListener}, which may be useful as a starting point in developing your own listeners. This listener listens for a test event that is emitted by the script `scripts/util/emit_test_event.php`. @@ -80,7 +80,7 @@ test event was emitted. = Available Events = -You can find a list of all Phabricator events in @{class:PhabricatorEventType}. +You can find a list of all Phorge events in @{class:PhabricatorEventType}. == All Events == @@ -184,9 +184,9 @@ NOTE: This event is unstable and subject to change. If you're having problems with your listener, try these steps: - - If you're getting an error about Phabricator being unable to find the + - If you're getting an error about Phorge being unable to find the listener class, make sure you've added it to a libphutil library and - configured Phabricator to load the library with `load-libraries`. + configured Phorge to load the library with `load-libraries`. - Make sure the listener is registered. It should appear in the "Events" tab of DarkConsole. If it's not there, you may have forgotten to add it to `events.listeners`. diff --git a/src/docs/user/userguide/external_editor.diviner b/src/docs/user/userguide/external_editor.diviner index ce39ad6610..1e44aeec34 100644 --- a/src/docs/user/userguide/external_editor.diviner +++ b/src/docs/user/userguide/external_editor.diviner @@ -30,7 +30,7 @@ editor://open/?file=%f Mapping Repositories ==================== -When you open a file in an external editor, Phabricator needs to be able to +When you open a file in an external editor, Phorge needs to be able to build a URI which includes the correct absolute path on disk to the local version of the file, including the repository directory. diff --git a/src/docs/user/userguide/forms.diviner b/src/docs/user/userguide/forms.diviner index 034293ff29..bd65007e9d 100644 --- a/src/docs/user/userguide/forms.diviner +++ b/src/docs/user/userguide/forms.diviner @@ -1,7 +1,7 @@ @title User Guide: Customizing Forms @group userguide -Guide to prefilling and customizing forms in Phabricator applications. +Guide to prefilling and customizing forms in Phorge applications. Overview ======== @@ -194,17 +194,17 @@ they go to create new objects in an application. Using {nav Mark as "Create" Form} from the detail page for a form configuration, you can mark a form to appear in the create menu. -When a user visits the application, Phabricator finds all the form +When a user visits the application, Phorge finds all the form configurations that are: - marked as "create" forms; and - visible to the user based on policy configuration; and - enabled. -If there is only one such form, Phabricator renders a single "Create" button. +If there is only one such form, Phorge renders a single "Create" button. (If there are zero forms, it renders the button but disables it.) -If there are several such forms, Phabricator renders a dropdown which allows +If there are several such forms, Phorge renders a dropdown which allows the user to choose between them. You can reorder these forms by returning to the configuration list and using @@ -446,7 +446,7 @@ the report like this: //after// creating it and open the policies; or - regardless of their edit form access, they can use the Conduit API to change the task policy; or - - regardless of any policy controls in Phabricator, they can screenshot, + - regardless of any policy controls in Phorge, they can screenshot, print, or forward email about the task to anyone; or - regardless of any technical controls in any software, they can decline to report the issue to you in the first place and sell it on the black market @@ -473,7 +473,7 @@ current configuration may not be exactly the same as the one described below. We run an open source project with a small core team, a moderate number of regular contributors, and a large public userbase. Access to the upstream -Phabricator instance is open to the public. +Phorge instance is open to the public. Although our product is fairly technical, we receive many bug reports and feature requests which are of very poor quality. Some users also ignore all the diff --git a/src/docs/user/userguide/harbormaster.diviner b/src/docs/user/userguide/harbormaster.diviner index a6f2047fdd..cf4b234e82 100644 --- a/src/docs/user/userguide/harbormaster.diviner +++ b/src/docs/user/userguide/harbormaster.diviner @@ -10,7 +10,7 @@ WARNING: Harbormaster is still very rough. Read this document carefully to understand what it can and can not do and what to expect in the future. The Harbormaster application provides build and continuous integration support -for Phabricator. +for Phorge. Harbormaster is not a mature application. You should expect it to have major missing capabilities and to change substantially over time. The current version @@ -95,7 +95,7 @@ plans should therefore look something like this: - Have the build step "Wait for Message" after the external system is notified. - Write custom code on the build server to respond to the request, run a - build, then report the results back to Phabricator by calling the + build, then report the results back to Phorge by calling the `harbormaster.sendmessage` Conduit API. You'll need to write a nontrivial amount of code to get this working today. @@ -149,7 +149,7 @@ side effect of running `arc diff`. In Git, it pushes changes to a tag like The build system can then interact with this copy using normal VCS commands. This is simpler to configure, use, troubleshoot and work with than `arc patch`. -With `arc patch`, the build system downloads patches from Phabricator and +With `arc patch`, the build system downloads patches from Phorge and applies them to a local working copy. This is more complex and more error-prone than staging areas. @@ -201,7 +201,7 @@ area repository and do a checkout of the relevant tag in order to perform a build. **`arc patch`**: You can also have the build system pull changes out of -Phabricator as patches and apply them with `arc patch`. This mechanism is the +Phorge as patches and apply them with `arc patch`. This mechanism is the most complex to configure and debug, and is much less reliable than using staging areas. It is not recommended. @@ -225,6 +225,6 @@ line. Run it as `bin/harbormaster help` for details. In particular, you can run manual builds in the foreground from the CLI to see more details about what they're doing: - phabricator/ $ ./bin/harbormaster build D123 --plan 456 --trace + phorge/ $ ./bin/harbormaster build D123 --plan 456 --trace This may help you understand or debug issues with a build plan. diff --git a/src/docs/user/userguide/herald.diviner b/src/docs/user/userguide/herald.diviner index b7cfdeca95..a3cfa26f54 100644 --- a/src/docs/user/userguide/herald.diviner +++ b/src/docs/user/userguide/herald.diviner @@ -13,7 +13,7 @@ interested in, even if they didn't add you as a reviewer. One way to think about Herald is that it is a lot like the mail rules you can set up in most email clients to organize mail based on "To", "Subject", etc. -Herald works very similarly, but operates on Phabricator objects (like revisions +Herald works very similarly, but operates on Phorge objects (like revisions and commits) instead of emails. For example, you can write a personal rule like this which triggers on tasks: diff --git a/src/docs/user/userguide/jump.diviner b/src/docs/user/userguide/jump.diviner index 0421f194a8..8e6277303b 100644 --- a/src/docs/user/userguide/jump.diviner +++ b/src/docs/user/userguide/jump.diviner @@ -6,7 +6,7 @@ Command reference for global search shortcuts. Overview ======== -Phabricator's global search bar automatically interprets certain commands as +Phorge's global search bar automatically interprets certain commands as shortcuts to make it easy to navigate to specific places. To use these shortcuts, just type them into the global search bar in the main diff --git a/src/docs/user/userguide/legalpad.diviner b/src/docs/user/userguide/legalpad.diviner index 053f275d9f..cd5f6578e9 100644 --- a/src/docs/user/userguide/legalpad.diviner +++ b/src/docs/user/userguide/legalpad.diviner @@ -36,7 +36,7 @@ identifying the corporation they are signing on behalf of) or **No One** (for policy documents or other documents which do not require a signature). **Require Signature** This field allows you to create a document which all of -your users must sign before they can use Phabricator, like a terms of service +your users must sign before they can use Phorge, like a terms of service document. See "Use Case: Terms of Service" below for details. These documents must be signable by individuals. @@ -84,7 +84,7 @@ do other things with their account (you'll need to agree to it, too, as soon as you save your changes, so that will give you a sense of the workflow). Note that although users who have not signed all of the required documents can -not use most Phabricator functions, they can browse other Legalpad documents +not use most Phorge functions, they can browse other Legalpad documents that they have permission to see. This allows a terms document to be supplemented with additional policy or guideline documents that users are free to review before agreeing to the terms. @@ -123,13 +123,3 @@ To add an exemption, go to **Manage Document**, then **View Signatures**, then You can optionally add notes about why a user is exempt from signing a document. To review the notes later (and see who added the exemption), click the colored asterisk in the list view. - - -Roadmap -======== - -You can find discussion about the Legalpad roadmap here: - -https://secure.phabricator.com/T5505 - -If there are features you'd like to see, let us know. diff --git a/src/docs/user/userguide/mail_rules.diviner b/src/docs/user/userguide/mail_rules.diviner index cdbbf1919d..5eb20043a5 100644 --- a/src/docs/user/userguide/mail_rules.diviner +++ b/src/docs/user/userguide/mail_rules.diviner @@ -1,12 +1,12 @@ -@title User Guide: Managing Phabricator Email +@title User Guide: Managing Phorge Email @group userguide -How to effectively manage Phabricator email notifications. +How to effectively manage Phorge email notifications. Overview ======== -Phabricator uses email as a major notification channel, but the amount of email +Phorge uses email as a major notification channel, but the amount of email it sends can seem overwhelming if you're working on an active team. This document discusses some strategies for managing email. @@ -29,13 +29,13 @@ The best approach to managing mail is to write mail rules. Simply writing rules to move mail from Differential, Maniphest and Herald to separate folders will vastly simplify mail management. -Phabricator also adds mail headers (see below) which can allow you to write +Phorge also adds mail headers (see below) which can allow you to write more sophisticated mail rules. Mail Headers ============ -Phabricator sends various information in mail headers that can be useful in +Phorge sends various information in mail headers that can be useful in crafting rules to route and manage mail. To see a full list of headers, use the "View Raw Message" feature in your mail client. diff --git a/src/docs/user/userguide/multi_factor_auth.diviner b/src/docs/user/userguide/multi_factor_auth.diviner index eca85d0f92..9ef92157c9 100644 --- a/src/docs/user/userguide/multi_factor_auth.diviner +++ b/src/docs/user/userguide/multi_factor_auth.diviner @@ -1,7 +1,7 @@ @title User Guide: Multi-Factor Authentication @group userguide -Explains how multi-factor authentication works in Phabricator. +Explains how multi-factor authentication works in Phorge. Overview ======== @@ -30,11 +30,11 @@ you'll be stopped and asked to enter additional credentials. Usually, this means you'll receive an SMS with a authorization code on your phone, or you'll open an app on your phone which will show you a authorization code or ask you to confirm the action. If you're given a authorization code, -you'll enter it into Phabricator. +you'll enter it into Phorge. -If you're logging in, Phabricator will log you in after you enter the code. +If you're logging in, Phorge will log you in after you enter the code. -If you're taking a sensitive action, Phabricator will sometimes put your +If you're taking a sensitive action, Phorge will sometimes put your account in "high security" mode for a few minutes. In this mode, you can take sensitive actions like changing passwords or SSH keys freely, without entering any more credentials. @@ -64,7 +64,7 @@ Factor: Mobile Phone App (TOTP) =============================== TOTP stands for "Time-based One-Time Password". This factor operates by having -you enter authorization codes from your mobile phone into Phabricator. The codes +you enter authorization codes from your mobile phone into Phorge. The codes change every 30 seconds, so you will need to have your phone with you in order to enter them. @@ -77,16 +77,16 @@ Your company may have a preferred application, or may use some other application, so check any in-house documentation for details. In general, any TOTP application should work properly. -After you've downloaded the application onto your phone, use the Phabricator +After you've downloaded the application onto your phone, use the Phorge settings panel to add a factor to your account. You'll be prompted to scan a QR code, and then read an authorization code from your phone and type it into -Phabricator. +Phorge. Later, when you need to authenticate, you'll follow this same process: launch -the application, read the authorization code, and type it into Phabricator. +the application, read the authorization code, and type it into Phorge. This will prove you have your phone. -Don't lose your phone! You'll need it to log into Phabricator in the future. +Don't lose your phone! You'll need it to log into Phorge in the future. Factor: SMS @@ -106,7 +106,7 @@ When you're asked to confirm your identity in the future, you'll be texted an authorization code to enter into the prompt. (WARNING) SMS is a very weak factor and can be compromised or intercepted. For -details, see: . +details, see: . Factor: Duo @@ -123,7 +123,7 @@ to take a sensitive action, you'll be asked to confirm it in the application. Administration: Configuration ============================= -New Phabricator installs start without any multi-factor providers enabled. +New Phorge installs start without any multi-factor providers enabled. Users won't be able to add new factors until you set up multi-factor authentication by configuring at least one provider. @@ -134,7 +134,7 @@ Providers may be in these states: - **Active**: Users may add new factors. Users will be prompted to respond to challenges from these providers when they take a sensitive action. - **Deprecated**: Users may not add new factors, but they will still be - asked to respond to challenges from exising factors. + asked to respond to challenges from existing factors. - **Disabled**: Users may not add new factors, and existing factors will not be used. If MFA is required and a user only has disabled factors, they will be forced to add a new factor. @@ -187,7 +187,7 @@ their phone, run this command: ```lang=console # Strip all factors from a given user account. -phabricator/ $ ./bin/auth strip --user --all-types +phorge/ $ ./bin/auth strip --user --all-types ``` You can run `bin/auth help strip` for more detail and all available flags and @@ -198,7 +198,7 @@ This command can selectively strip factors by factor type. You can use ```lang=console # Show supported factor types. -phabricator/ $ ./bin/auth list-factors +phorge/ $ ./bin/auth list-factors ``` Once you've identified the factor types you want to strip, you can strip @@ -207,7 +207,7 @@ types: ```lang=console # Strip all SMS and TOTP factors for a user. -phabricator/ $ ./bin/auth strip --user --type sms --type totp +phorge/ $ ./bin/auth strip --user --type sms --type totp ``` The `bin/auth strip` command can also selectively strip factors for certain @@ -218,5 +218,5 @@ Once you have a provider PHID, use `--provider` to select factors to strip: ```lang=console # Strip all factors for a particular provider. -phabricator/ $ ./bin/auth strip --user --provider +phorge/ $ ./bin/auth strip --user --provider ``` diff --git a/src/docs/user/userguide/multimeter.diviner b/src/docs/user/userguide/multimeter.diviner index 6aab740486..e2f242bcdc 100644 --- a/src/docs/user/userguide/multimeter.diviner +++ b/src/docs/user/userguide/multimeter.diviner @@ -9,7 +9,7 @@ Overview IMPORTANT: This document describes a prototype application. Multimeter is a sampling profiler that can give you coarse information about -Phabricator resource usage. In particular, it can help quickly identify sources +Phorge resource usage. In particular, it can help quickly identify sources of load, like bots or scripts which are making a very large number of requests. Configuring and Using Multimeter @@ -26,7 +26,7 @@ data, at a small performance cost. Using Multimeter ================ -Multimeter shows you what Phabricator has spent time doing recently. By +Multimeter shows you what Phorge has spent time doing recently. By looking at the samples it collects, you can identify major sources of load or resource use, whether they are specific users, pages, subprocesses, or other types of activity. @@ -36,7 +36,7 @@ or configuration to make better use of resources (for example, rewrite bots that are making too many calls), or report specific, actionable issues to the upstream for resolution. -The main screen of Multimeter shows you everything Phabricator has spent +The main screen of Multimeter shows you everything Phorge has spent resources on recently, broken down by action type. Categories are folded up by default, with "(All)" labels. diff --git a/src/docs/user/userguide/phame.diviner b/src/docs/user/userguide/phame.diviner index b0cde513f9..82d76bd395 100644 --- a/src/docs/user/userguide/phame.diviner +++ b/src/docs/user/userguide/phame.diviner @@ -7,14 +7,14 @@ Overview ======== Phame is a simple platform for writing blogs and blog posts. Content published -through Phame is integrated with other Phabricator applications (like Feed, +through Phame is integrated with other Phorge applications (like Feed, Herald and Dashboards). You can use Phame to write and publish posts on any topic. You might use it to make announcements, hold discussions, or provide progress updates about a project. -In the upstream, we use several Phame blogs to discuss changes to Phabricator, +In the upstream, we use several Phame blogs to discuss changes to Phorge, make company announcements, photograph food, and provide visionary thought leadership. @@ -61,7 +61,7 @@ for all users who can see it. Using Phame With Other Applications =================================== -Phame integrates with other Phabricator applications, so you can do a few +Phame integrates with other Phorge applications, so you can do a few interesting things: **Dashboards**: You can create a dashboard panel which shows posts on a @@ -87,7 +87,7 @@ WARNING: This feature is still a prototype and has some known issues. You can host a Phame blog on an external domain, like `blog.mycompany.com`. External blogs are public (they do not require login) and are only supported if -your Phabricator install is also public. You can make an install public by +your Phorge install is also public. You can make an install public by adjusting `policy.allow-public` in Config, but make sure you understand the effects of adjusting this setting before touching it. @@ -97,7 +97,7 @@ like this: - **View Policy**: Set the "View Policy" for the blog to "Public". Blogs must have a public view policy to be served from an external domain. - **Full Domain URI**: Set this to the full URI of your external domain, - like `https://blog.mycompany.com/`. When users visit this URI, Phabricator + like `https://blog.mycompany.com/`. When users visit this URI, Phorge will serve the blog to them. To configure the blog's navigation breadcrumbs so that it links back to the @@ -112,7 +112,7 @@ users return to the blog's parent site. It will look something like this: - {nav My Company > Blog Name} -Finally, configure DNS for `blog.mycompany.com` to point at Phabricator. +Finally, configure DNS for `blog.mycompany.com` to point at Phorge. If everything is set up properly, visiting `blog.mycompany.com` should now serve your blog. diff --git a/src/docs/user/userguide/profile_menu.diviner b/src/docs/user/userguide/profile_menu.diviner index 88eb32d3ff..a7e48a6924 100644 --- a/src/docs/user/userguide/profile_menu.diviner +++ b/src/docs/user/userguide/profile_menu.diviner @@ -130,7 +130,7 @@ the menu for, but most objects support these items: - {icon map-marker} **Label**: Lets you label sections of menu items. This is also purely cosmetic. - {icon link} **Link**: Allows you to create an item which links to - somewhere else in Phabricator, or to an external site. + somewhere else in Phorge, or to an external site. - {icon plus} **Form**: Provides quick access to custom and built-in forms from any application that supports EditEngine. - {icon briefcase} **Projects**: Provides quick access to a project. diff --git a/src/docs/user/userguide/projects.diviner b/src/docs/user/userguide/projects.diviner index 4e3ab3616f..0d9dd2aaa2 100644 --- a/src/docs/user/userguide/projects.diviner +++ b/src/docs/user/userguide/projects.diviner @@ -8,7 +8,7 @@ Overview NOTE: This document is only partially complete. -Phabricator projects are flexible, general-purpose groups of objects that you +Phorge projects are flexible, general-purpose groups of objects that you can use to organize information. Projects have some basic information like a name and an icon, and may optionally have members. @@ -37,7 +37,7 @@ only members of the "Backend" project, nor does it allow them to see it if they otherwise could not. Likewise, removing projects does not affect visibility. If you're familiar with other software that works differently, this may be -unexpected, but the rule in Phabricator is simple: **adding and removing +unexpected, but the rule in Phorge is simple: **adding and removing projects never affects policies.** Note that you //can// write policy rules which restrict capabilities to members @@ -124,7 +124,7 @@ documentation to the menu to make it easy to find and access. You could also link to a Conpherence if you have a chatroom for a project. **Link to External Resources**: You can link to external resources outside -of Phabricator if you have other pages which are relevant to a project. +of Phorge if you have other pages which are relevant to a project. **Set Workboard as Default**: For projects that are mostly used to organize tasks, change the default item to the workboard instead of the profile to get @@ -278,13 +278,13 @@ As discussed above, adding and removing projects never affects who can see an object. This is an explicit product design choice aimed at reducing the complexity of policy management. -Phabricator projects are a flexible, general-purpose, freeform tool. This is a +Phorge projects are a flexible, general-purpose, freeform tool. This is a good match for many organizational use cases, but a very poor match for policies. It is important that policies be predictable and rigid, because the cost of making a mistake with policies is high (inadvertent disclosure of private information). -In Phabricator, each object (like a task) can be tagged with multiple projects. +In Phorge, each object (like a task) can be tagged with multiple projects. This is important in a flexible organizational tool, but is a liability in a policy tool. diff --git a/src/docs/user/userguide/prototypes.diviner b/src/docs/user/userguide/prototypes.diviner index c84bad1178..52ae25bf75 100644 --- a/src/docs/user/userguide/prototypes.diviner +++ b/src/docs/user/userguide/prototypes.diviner @@ -6,7 +6,7 @@ Information about prototypes. Overview ======== -Phabricator includes //prototype applications//, which are applications in an +Phorge includes //prototype applications//, which are applications in an early stage of development. When we begin working on a new application, we usually implement it as a diff --git a/src/docs/user/userguide/remarkup.diviner b/src/docs/user/userguide/remarkup.diviner index f96c8c7b25..46e732c386 100644 --- a/src/docs/user/userguide/remarkup.diviner +++ b/src/docs/user/userguide/remarkup.diviner @@ -6,7 +6,7 @@ arguments. = Overview = -Phabricator uses a lightweight markup language called "Remarkup", similar to +Phorge uses a lightweight markup language called "Remarkup", similar to other lightweight markup languages like Markdown and Wiki markup. This document describes how to format text using Remarkup. @@ -196,6 +196,9 @@ When rendered, this produces: - [X] Preheat oven to 450 degrees. - [ ] Zest 35 lemons. +Code Blocks +=========== + Make **code blocks** by indenting two spaces: f(x, y); @@ -211,6 +214,9 @@ You can specify a language for syntax highlighting with `lang=xxx`: lang=html ... +When using fenced code blocks (triple backticks) you can simply append the +language right after the backticks, like this: ##```html## + This will highlight the block using a highlighter for that language, if one is available (in most cases, this means you need to configure Pygments): @@ -331,7 +337,7 @@ On an entirely different topic, ... = Linking URIs = -URIs are automatically linked: http://phabricator.org/ +URIs are automatically linked: http://phorge.it/ If you have a URI with problematic characters in it, like "`http://comma.org/,`", you can surround it with angle brackets: @@ -341,7 +347,7 @@ If you have a URI with problematic characters in it, like This will force the parser to consume the whole URI: You can also use create named links, where you choose the displayed text. These -work within Phabricator or on the internet at large: +work within Phorge or on the internet at large: [[/herald/transcript/ | Herald Transcripts]] [[http://www.boring-legal-documents.com/ | exciting legal documents]] @@ -352,7 +358,7 @@ Markdown-style links are also supported: = Linking to Objects = -You can link to Phabricator objects, such as Differential revisions, Diffusion +You can link to Phorge objects, such as Differential revisions, Diffusion commits and Maniphest tasks, by mentioning the name of an object: D123 # Link to Differential revision D123 @@ -366,7 +372,7 @@ can be found on the date stamp of any transaction/comment): T123#412 # Link to comment id #412 of task T123 -See the Phabricator configuration setting `remarkup.ignored-object-names` to +See the Phorge configuration setting `remarkup.ignored-object-names` to modify this behavior. = Embedding Objects @@ -501,7 +507,7 @@ following: By default, the font used to create the text for the meme is `tuffy.ttf`. For the more authentic feel of `impact.ttf`, you simply have to place the Impact -TrueType font in the Phabricator subfolder `/resources/font/`. If Remarkup +TrueType font in the Phorge subfolder `/resources/font/`. If Remarkup detects the presence of `impact.ttf`, it will automatically use it. = Mentioning Users = @@ -732,3 +738,7 @@ to the first anchor with "xyz" as a prefix of the anchor name. Remarkup editors provide a fullscreen composition mode. This can make it easier to edit large blocks of text, or improve focus by removing distractions. You can exit **Fullscreen** mode by clicking the button again or by pressing escape. + +See Also +======== +* @{article:Remarkup Reference: Cowsay} diff --git a/src/docs/user/userguide/remarkup_cowsay.diviner b/src/docs/user/userguide/remarkup_cowsay.diviner new file mode 100644 index 0000000000..696189afb6 --- /dev/null +++ b/src/docs/user/userguide/remarkup_cowsay.diviner @@ -0,0 +1,61 @@ +@title Remarkup Reference: Cowsay +@group userguide + +Overview +-------- + +Cowsay is an application by Tony Monroe which has been ported over to +Phabricator/Phorge to allow your comments to be voiced by +a cow. + +Basic Usage +----------- + +A basic example of using cowsay would be to add a comment + cowsay{{{Great work!}}} +which generates: +``` + _____________ +< Great work! > + ------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || +``` + +Other Cowsay Templates +---------------------- +Other templates are available in externals/cowsay/cows/, and you can specify +one by calling cowsay with the 'cow' parameter. eg: + cowsay(cow='tux'){{{Great work!}}} +generates +``` + _____________ +< Great work! > + ------------- + \ + \ + .--. + |o_o | + |:_/ | + // \ \ + (| | ) + /'\_ _/`\ + \___)=(___/ +``` + +Other Parameters +---------------- +* think (set to 1 for thinking bubbles) +* eyes (default 'oo') +* tongue (default ' ', try 'P') + +See Also +======== +* @{article:Remarkup Reference} +* Have you tried figlet: +``` +figlet{{{figlet generates big text!}}} +``` diff --git a/src/docs/user/userguide/reviews_vs_audit.diviner b/src/docs/user/userguide/reviews_vs_audit.diviner index 642683840c..bf6afb64b4 100644 --- a/src/docs/user/userguide/reviews_vs_audit.diviner +++ b/src/docs/user/userguide/reviews_vs_audit.diviner @@ -6,7 +6,7 @@ Discusses the differences between "review" and "audit" workflows. Overview ======== -Phabricator supports two similar but separate code review workflows: "review" +Phorge supports two similar but separate code review workflows: "review" and "audit". Review occurs in **Differential**, before changes are published. You can learn @@ -111,7 +111,7 @@ supplement review and is better than nothing on its own. If you are unpersuaded by the arguments above (or work on a team that is unswayed), audits provide some of the benefits of review with less friction: - - Audits are driven entirely by Phabricator: users do not need to install + - Audits are driven entirely by Phorge: users do not need to install `arc`. - Audits require little adjustment to existing workflows and little training. - Audits are completely nonblocking, and send fewer notifications than review. diff --git a/src/docs/user/userguide/search.diviner b/src/docs/user/userguide/search.diviner index 9276298f31..b10e4c147c 100644 --- a/src/docs/user/userguide/search.diviner +++ b/src/docs/user/userguide/search.diviner @@ -1,12 +1,12 @@ @title Search User Guide @group userguide -Introduction to searching for documents in Phabricator. +Introduction to searching for documents in Phorge. Overview ======== -Phabricator has two major ways to search for documents and objects (like tasks, +Phorge has two major ways to search for documents and objects (like tasks, code reviews, users, wiki documents, and so on): **global search** and **application search**. @@ -34,14 +34,14 @@ By default, global search queries search all document types: for example, they will find matching tasks, commits, wiki documents, users, etc. You can use the dropdown to the left of the search box to select a different search scope. -If you choose the **Current Application** scope, Phabricator will search for +If you choose the **Current Application** scope, Phorge will search for open documents in the current application. For example, if you're in Maniphest and run a search, you'll get matching tasks. If you're in Phriction and run a search, you'll get matching wiki documents. Some pages (like the 404 page) don't belong to an application, or belong to an application which doesn't have any searchable documents. In these cases, -Phabricator will search all documents. +Phorge will search all documents. To quickly **jump to an object** like a task, enter the object's ID in the global search box and search for it. For example, you can enter `T123` or @@ -53,7 +53,7 @@ After running a search, you can scroll up to add filters and refine the result set. You can also select **Advanced Search** from the dropdown menu to jump here immediately, or press return in the search box without entering a query. -This interface supports standard Phabricator search and filtering features, +This interface supports standard Phorge search and filtering features, like **saved queries** and **typeaheads**. See below for more details on using these features. diff --git a/src/docs/user/userguide/spaces.diviner b/src/docs/user/userguide/spaces.diviner index 4a748fb25b..a2809cbe3e 100644 --- a/src/docs/user/userguide/spaces.diviner +++ b/src/docs/user/userguide/spaces.diviner @@ -21,7 +21,7 @@ share the same access policy. For example: enemies at the company, that she might use the element of surprise to later expand her domain. -Phabricator's access control policies are generally powerful enough to handle +Phorge's access control policies are generally powerful enough to handle these use cases on their own, but applying the same policy to a large group of objects requires a lot of effort and is error-prone. @@ -138,7 +138,7 @@ Limitations and Caveats Some information is shared between spaces, so they do not completely isolate users from other activity on the install. This section discusses limitations of the isolation model. Most of these limitations are intrinsic to the policy -model Phabricator uses. +model Phorge uses. **Shared IDs**: Spaces do not have unique object IDs: there is only one `T1`, not a separate one in each space. It can be moved between spaces, but `T1` diff --git a/src/docs/user/userguide/tone.diviner b/src/docs/user/userguide/tone.diviner index 553fe2851b..6398917d2c 100644 --- a/src/docs/user/userguide/tone.diviner +++ b/src/docs/user/userguide/tone.diviner @@ -1,12 +1,12 @@ @title User Guide: Project Tone @group userguide -Explains why Phabricator uses a lighthearted tone. +Explains why Phorge uses a lighthearted tone. Overview ======== -Phabricator uses a lighthearted tone in documentation and some interfaces, and +Phorge uses a lighthearted tone in documentation and some interfaces, and includes some features which primarily exist to add flavor or make things sillier. @@ -28,7 +28,7 @@ setting in the {nav Config} application. documentation.) If you don't like a piece of flavor because it creates legitimate difficulty or -confusion for you or your users and makes it harder to use Phabricator, let us +confusion for you or your users and makes it harder to use Phorge, let us know. We don't intend flavor or tone to get in the way of usability, and can sometimes take a joke too far (particularly for users who don't speak English natively). diff --git a/src/docs/user/userguide/unlocking.diviner b/src/docs/user/userguide/unlocking.diviner index 456655a393..cd1157a21d 100644 --- a/src/docs/user/userguide/unlocking.diviner +++ b/src/docs/user/userguide/unlocking.diviner @@ -6,7 +6,7 @@ Explains how to access locked or invisible objects and accounts. Overview ======== -Phabricator tries to make it difficult for users to lock themselves out of +Phorge tries to make it difficult for users to lock themselves out of things, but you can occasionally end up in situations where no one has access to an object that you need access to. diff --git a/src/docs/user/userguide/users.diviner b/src/docs/user/userguide/users.diviner index d66f8080d3..5e15f2f2af 100644 --- a/src/docs/user/userguide/users.diviner +++ b/src/docs/user/userguide/users.diviner @@ -18,7 +18,7 @@ Administrators **Administrators** are normal users with a few extra capabilities. Their primary role is to keep things running smoothly, and they are not all-powerful. -In Phabricator, administrators are more like //janitors//. +In Phorge, administrators are more like //janitors//. Administrators can create, delete, enable, disable, and approve user accounts. Various applications have a few other capabilities which are reserved for diff --git a/src/docs/user/userguide/utf8.diviner b/src/docs/user/userguide/utf8.diviner index b6742f0c36..0e6727197d 100644 --- a/src/docs/user/userguide/utf8.diviner +++ b/src/docs/user/userguide/utf8.diviner @@ -1,11 +1,11 @@ @title User Guide: UTF-8 and Character Encoding @group userguide -How Phabricator handles character encodings. +How Phorge handles character encodings. = Overview = -Phabricator stores all internal text data as UTF-8, processes all text data +Phorge stores all internal text data as UTF-8, processes all text data as UTF-8, outputs in UTF-8, and expects all inputs to be UTF-8. Principally, this means that you should write your source code in UTF-8. In most cases this does not require you to change anything, because ASCII text is a subset of @@ -17,14 +17,14 @@ options: - Convert all files in the repository to ASCII or UTF-8 (see "Detecting and Repairing Files" below). This is recommended, especially if the encoding problems are accidental. - - Configure Phabricator to convert files into UTF-8 from whatever encoding + - Configure Phorge to convert files into UTF-8 from whatever encoding your repository is in when it needs to (see "Support for Alternate Encodings" below). This is not completely supported, and repositories with files that have multiple encodings are not supported. = Support for Alternate Encodings = -Phabricator has some support for encodings other than UTF-8. +Phorge has some support for encodings other than UTF-8. NOTE: Alternate encodings are not completely supported, and a few features will not work correctly. Codebases with files that have multiple different encodings diff --git a/src/docs/user/userguide/webhooks.diviner b/src/docs/user/userguide/webhooks.diviner index 10d1f36da0..47b02cc261 100644 --- a/src/docs/user/userguide/webhooks.diviner +++ b/src/docs/user/userguide/webhooks.diviner @@ -7,7 +7,7 @@ Guide to configuring webhooks. Overview ======== -If you'd like to react to events in Phabricator or publish them into external +If you'd like to react to events in Phorge or publish them into external systems, you can configure webhooks. Configure webhooks in {nav Herald > Webhooks}. Users must have the @@ -35,7 +35,7 @@ You can also use the command-line tool, which supports a few additional options: ``` -phabricator/ $ ./bin/webhook call --id 42 --object D123 +phorge/ $ ./bin/webhook call --id 42 --object D123 ``` @@ -43,12 +43,12 @@ Verifying Requests ================== When your webhook callback URI receives a request, it didn't necessarily come -from Phabricator. An attacker or mischievous user can normally call your hook +from Phorge. An attacker or mischievous user can normally call your hook directly and pretend to be notifying you of an event. To verify that the request is authentic, first retrieve the webhook key from the web UI with {nav View HMAC Key}. This is a shared secret which will let you -verify that Phabricator originated a request. +verify that Phorge originated a request. When you receive a request, compute the SHA256 HMAC value of the request body using the HMAC key as the key. The value should match the value in the @@ -124,7 +124,7 @@ The **action** map has metadata about the action: - `test` This was a test call from the web UI or console. - `silent` This is a silent edit which won't send mail or notifications in - Phabricator. If your hook is doing something like copying events into + Phorge. If your hook is doing something like copying events into a chatroom, it may want to respect this flag. - `secure` Details about this object should only be transmitted over secure channels. Your hook may want to respect this flag. @@ -178,7 +178,7 @@ Retries and Rate Limiting Test requests are never retried: they execute exactly once. Live requests are automatically retried. If your endpoint does not return a -HTTP 2XX response, the request will be retried regularly until it suceeds. +HTTP 2XX response, the request will be retried regularly until it succeeds. Retries will continue until the request succeeds or is garbage collected. By default, this is after 7 days. @@ -198,7 +198,7 @@ to some kind of worker queue if you expect to routinely require more than 10 seconds to respond to requests. Hook callbacks are single-threaded: you will never receive more than one -simultaneous call to the same webhook from Phabricator. If you have a firehose +simultaneous call to the same webhook from Phorge. If you have a firehose hook on an active install, it may be important to respond to requests quickly to avoid accumulating a backlog. diff --git a/src/infrastructure/cluster/PhabricatorDatabaseRef.php b/src/infrastructure/cluster/PhabricatorDatabaseRef.php index 1eb232ad86..0fe2aabbcb 100644 --- a/src/infrastructure/cluster/PhabricatorDatabaseRef.php +++ b/src/infrastructure/cluster/PhabricatorDatabaseRef.php @@ -229,7 +229,7 @@ public function getRefKey() { $host = $this->getHost(); $port = $this->getPort(); - if (strlen($port)) { + if ($port) { return "{$host}:{$port}"; } @@ -393,7 +393,7 @@ private static function queryRefs(array $refs) { if ($is_replica) { $latency = idx($replica_status, 'Seconds_Behind_Master'); - if (!strlen($latency)) { + if (!phutil_nonempty_string($latency)) { $ref->setReplicaStatus(self::REPLICATION_NOT_REPLICATING); } else { $latency = (int)$latency; diff --git a/src/infrastructure/contentsource/PhabricatorContentSource.php b/src/infrastructure/contentsource/PhabricatorContentSource.php index ee77052113..de36f50b1f 100644 --- a/src/infrastructure/contentsource/PhabricatorContentSource.php +++ b/src/infrastructure/contentsource/PhabricatorContentSource.php @@ -81,6 +81,13 @@ final public function serialize() { )); } + /** + * Get the internal source name + * + * This is usually coming from a SOURCECONST constant. + * + * @return string|null + */ final public function getSource() { return $this->source; } diff --git a/src/infrastructure/contentsource/PhabricatorUnknownContentSource.php b/src/infrastructure/contentsource/PhabricatorUnknownContentSource.php index a0dfd042d7..7b8c063b8a 100644 --- a/src/infrastructure/contentsource/PhabricatorUnknownContentSource.php +++ b/src/infrastructure/contentsource/PhabricatorUnknownContentSource.php @@ -7,7 +7,7 @@ final class PhabricatorUnknownContentSource public function getSourceName() { $source = $this->getSource(); - if (strlen($source)) { + if ($source) { return pht('Unknown ("%s")', $source); } else { return pht('Unknown'); diff --git a/src/infrastructure/customfield/field/PhabricatorCustomField.php b/src/infrastructure/customfield/field/PhabricatorCustomField.php index 9e2bf6895f..4e55348c36 100644 --- a/src/infrastructure/customfield/field/PhabricatorCustomField.php +++ b/src/infrastructure/customfield/field/PhabricatorCustomField.php @@ -1679,7 +1679,7 @@ private static function adjustCustomFieldsForObjectSubtype( foreach ($map as $field_key => $field) { // For now, only support overriding standard custom fields. In the // future there's no technical or product reason we couldn't let you - // override (some properites of) other fields like "Title", but they + // override (some properties of) other fields like "Title", but they // don't usually support appropriate "setX()" methods today. if (!($field instanceof PhabricatorStandardCustomField)) { // For fields that are proxies on top of StandardCustomField, which diff --git a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php index 8ae5529f95..60a5171cdd 100644 --- a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php +++ b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php @@ -89,7 +89,7 @@ public function appendFieldsToForm(AphrontFormView $form) { $field_handles = array_select_keys($handles, $phids[$field_key]); $instructions = $field->getInstructionsForEdit(); - if (strlen($instructions)) { + if (phutil_nonempty_string($instructions)) { $form->appendRemarkupInstructions($instructions); } diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php index 4c0bce861b..87cde00801 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php @@ -268,7 +268,7 @@ public function shouldAppearInEditView() { public function readValueFromRequest(AphrontRequest $request) { $value = $request->getStr($this->getFieldKey()); - if (!strlen($value)) { + if (!phutil_nonempty_string($value)) { $value = null; } $this->setFieldValue($value); @@ -301,10 +301,14 @@ public function shouldAppearInPropertyView() { } public function renderPropertyViewValue(array $handles) { - if (!strlen($this->getFieldValue())) { - return null; + // If your field needs to render anything more complicated then a string, + // then you should override this method. + $value_str = phutil_string_cast($this->getFieldValue()); + + if (phutil_nonempty_string($value_str)) { + return $value_str; } - return $this->getFieldValue(); + return null; } public function shouldAppearInApplicationSearch() { @@ -389,7 +393,7 @@ protected function isValueEmpty($value) { if (is_array($value)) { return empty($value); } - return !strlen($value); + return $value === null || !strlen($value); } public function getApplicationTransactionTitle( @@ -477,7 +481,7 @@ public function updateAbstractDocument( } $field_value = $this->getFieldValue(); - if (strlen($field_value)) { + if (($field_value !== null) && (strlen($field_value))) { $document->addField($field_key, $field_value); } } diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php index 4aba7543e7..ca56816b42 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php @@ -11,7 +11,7 @@ public function buildFieldIndexes() { $indexes = array(); $value = $this->getFieldValue(); - if (strlen($value)) { + if (phutil_nonempty_scalar($value)) { $indexes[] = $this->newNumericIndex((int)$value); } @@ -24,7 +24,7 @@ public function buildOrderIndex() { public function getValueForStorage() { $value = $this->getFieldValue(); - if (strlen($value)) { + if (phutil_nonempty_scalar($value)) { return (int)$value; } else { return null; @@ -32,7 +32,7 @@ public function getValueForStorage() { } public function setValueFromStorage($value) { - if (strlen($value)) { + if (phutil_nonempty_scalar($value)) { $value = (int)$value; } else { $value = null; @@ -74,7 +74,8 @@ private function newDateControl() { // specify as a string. Parse the string into an epoch. $value = $this->getFieldValue(); - if (!ctype_digit($value)) { + if ($value !== null && gettype($value) !== 'integer' && + !ctype_digit($value)) { $value = PhabricatorTime::parseLocalTime($value, $this->getViewer()); } diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php index f06c30d482..f56f958a62 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php @@ -11,7 +11,7 @@ public function buildFieldIndexes() { $indexes = array(); $value = $this->getFieldValue(); - if (strlen($value)) { + if (phutil_nonempty_scalar($value)) { $indexes[] = $this->newNumericIndex((int)$value); } @@ -24,7 +24,8 @@ public function buildOrderIndex() { public function getValueForStorage() { $value = $this->getFieldValue(); - if (strlen($value)) { + $is_nonempty = phutil_string_cast($value) !== ''; + if ($is_nonempty) { return $value; } else { return null; @@ -32,7 +33,7 @@ public function getValueForStorage() { } public function setValueFromStorage($value) { - if (strlen($value)) { + if (phutil_nonempty_scalar($value)) { $value = (int)$value; } else { $value = null; @@ -47,15 +48,28 @@ public function readApplicationSearchValueFromRequest( return $request->getStr($this->getFieldKey()); } + /** + * Apply an application search constraint to a query. + * If you have a field of type integer, and a value (or an array of values), + * the result set will be limited to the rows with these values. + * @param PhabricatorApplicationSearchEngine $engine + * @param PhabricatorCursorPagedPolicyAwareQuery $query + * @param mixed $value Single value or array of values (IN query). + */ public function applyApplicationSearchConstraintToQuery( PhabricatorApplicationSearchEngine $engine, PhabricatorCursorPagedPolicyAwareQuery $query, $value) { - if (strlen($value)) { - $query->withApplicationSearchContainsConstraint( - $this->newNumericIndex(null), - $value); + // The basic use case is with a single value. + // The backend really works with an array. So also array allowed. + if (is_array($value) || phutil_nonempty_scalar($value)) { + $value = (array)$value; + if ($value) { + $query->withApplicationSearchContainsConstraint( + $this->newNumericIndex(null), + $value); + } } } @@ -83,7 +97,7 @@ public function validateApplicationTransactions( foreach ($xactions as $xaction) { $value = $xaction->getNewValue(); - if (strlen($value)) { + if (phutil_nonempty_scalar($value)) { if (!preg_match('/^-?\d+/', $value)) { $errors[] = new PhabricatorApplicationTransactionValidationError( $type, @@ -103,9 +117,9 @@ public function getApplicationTransactionHasEffect( $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); - if (!strlen($old) && strlen($new)) { + if (!phutil_nonempty_scalar($old) && phutil_nonempty_scalar($new)) { return true; - } else if (strlen($old) && !strlen($new)) { + } else if (phutil_nonempty_scalar($old) && !phutil_nonempty_scalar($new)) { return true; } else { return ((int)$old !== (int)$new); diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php index a96ebefda1..e34c3913f0 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php @@ -39,14 +39,9 @@ public function setValueFromStorage($value) { // should hold until this can get cleaned up more thoroughly. // TODO: Clean this up. - $result = array(); - if (!is_array($value)) { + if (is_string($value) && phutil_nonempty_string($value)) { $value = json_decode($value, true); - if (is_array($value)) { - $result = array_values($value); - } } - $this->setFieldValue($value); return $this; diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php index b0b9a3ef8e..54f54c7503 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php @@ -30,7 +30,7 @@ public function getApplicationTransactionRemarkupBlocks( public function renderPropertyViewValue(array $handles) { $value = $this->getFieldValue(); - if (!strlen($value)) { + if (!phutil_nonempty_string($value)) { return null; } diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php index 5957afe56a..958d65d7e7 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php @@ -11,7 +11,7 @@ public function buildFieldIndexes() { $indexes = array(); $value = $this->getFieldValue(); - if (strlen($value)) { + if (($value !== null) && (strlen($value))) { $indexes[] = $this->newStringIndex($value); } @@ -73,7 +73,7 @@ public function renderEditControl(array $handles) { } public function renderPropertyViewValue(array $handles) { - if (!strlen($this->getFieldValue())) { + if (!phutil_nonempty_string($this->getFieldValue())) { return null; } return idx($this->getOptions(), $this->getFieldValue()); diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldText.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldText.php index 56164bb7b5..758c50e817 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldText.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldText.php @@ -11,7 +11,7 @@ public function buildFieldIndexes() { $indexes = array(); $value = $this->getFieldValue(); - if (strlen($value)) { + if (phutil_nonempty_string($value)) { $indexes[] = $this->newStringIndex($value); } @@ -30,7 +30,7 @@ public function applyApplicationSearchConstraintToQuery( PhabricatorCursorPagedPolicyAwareQuery $query, $value) { - if (strlen($value)) { + if (phutil_nonempty_string($value)) { $query->withApplicationSearchContainsConstraint( $this->newStringIndex(null), $value); diff --git a/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php b/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php index 0f60d77602..cf0140a38a 100644 --- a/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php +++ b/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php @@ -80,6 +80,10 @@ final public function loadStorageSourceData(array $fields) { $object_phid = $row['objectPHID']; $value = $row['fieldValue']; + if (!isset($map[$index]) || !isset($map[$index][$object_phid])) { + continue; + } + $key = $map[$index][$object_phid]; $result[$key] = $value; } diff --git a/src/infrastructure/daemon/workers/editor/PhabricatorWorkerBulkJobEditor.php b/src/infrastructure/daemon/workers/editor/PhabricatorWorkerBulkJobEditor.php index e94ca6dc49..bd4a271fc7 100644 --- a/src/infrastructure/daemon/workers/editor/PhabricatorWorkerBulkJobEditor.php +++ b/src/infrastructure/daemon/workers/editor/PhabricatorWorkerBulkJobEditor.php @@ -4,7 +4,7 @@ final class PhabricatorWorkerBulkJobEditor extends PhabricatorApplicationTransactionEditor { public function getEditorApplicationClass() { - return 'PhabricatorDaemonsApplication'; + return PhabricatorDaemonsApplication::class; } public function getEditorObjectsDescription() { diff --git a/src/infrastructure/daemon/workers/phid/PhabricatorWorkerBulkJobPHIDType.php b/src/infrastructure/daemon/workers/phid/PhabricatorWorkerBulkJobPHIDType.php index b94f7eab27..d3d672897a 100644 --- a/src/infrastructure/daemon/workers/phid/PhabricatorWorkerBulkJobPHIDType.php +++ b/src/infrastructure/daemon/workers/phid/PhabricatorWorkerBulkJobPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDaemonsApplication'; + return PhabricatorDaemonsApplication::class; } protected function buildQueryForObjects( diff --git a/src/infrastructure/daemon/workers/phid/PhabricatorWorkerTriggerPHIDType.php b/src/infrastructure/daemon/workers/phid/PhabricatorWorkerTriggerPHIDType.php index b1dadb568e..c301e3d2be 100644 --- a/src/infrastructure/daemon/workers/phid/PhabricatorWorkerTriggerPHIDType.php +++ b/src/infrastructure/daemon/workers/phid/PhabricatorWorkerTriggerPHIDType.php @@ -13,7 +13,7 @@ public function newObject() { } public function getPHIDTypeApplicationClass() { - return 'PhabricatorDaemonsApplication'; + return PhabricatorDaemonsApplication::class; } protected function buildQueryForObjects( diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php index b359157e06..bdedc3584c 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php @@ -96,7 +96,7 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { } public function getQueryApplicationClass() { - return 'PhabricatorDaemonsApplication'; + return PhabricatorDaemonsApplication::class; } } diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobSearchEngine.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobSearchEngine.php index 3b9d6c9d48..cda4176f29 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobSearchEngine.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobSearchEngine.php @@ -8,7 +8,7 @@ public function getResultTypeDescription() { } public function getApplicationClassName() { - return 'PhabricatorDaemonsApplication'; + return PhabricatorDaemonsApplication::class; } public function newQuery() { diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobTransactionQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobTransactionQuery.php index 350277a888..f3eddb8020 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobTransactionQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobTransactionQuery.php @@ -7,4 +7,8 @@ public function getTemplateApplicationTransaction() { return new PhabricatorWorkerBulkJobTransaction(); } + public function getQueryApplicationClass() { + return PhabricatorDaemonsApplication::class; + } + } diff --git a/src/infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php b/src/infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php index f705fa4377..752d895952 100644 --- a/src/infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php +++ b/src/infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php @@ -42,6 +42,11 @@ public function setCoverage($coverage) { return $this; } + /** + * Get the Coverage, expressed as a string, each letter with this meaning: + * N: Not Executable, C: Covered, U: Uncovered. + * @return string|null + */ public function getCoverage() { return $this->coverage; } @@ -139,7 +144,7 @@ public function renderCoverage() { $not_applicable = '-'; $coverage = $this->getCoverage(); - if (!strlen($coverage)) { + if (!phutil_nonempty_string($coverage)) { return $not_applicable; } @@ -157,7 +162,7 @@ public function renderModifiedCoverage() { $not_applicable = '-'; $coverage = $this->getCoverage(); - if (!strlen($coverage)) { + if (!phutil_nonempty_string($coverage)) { return $not_applicable; } diff --git a/src/infrastructure/editor/PhabricatorEditorURIEngine.php b/src/infrastructure/editor/PhabricatorEditorURIEngine.php index d4821c4ffd..76c95dead2 100644 --- a/src/infrastructure/editor/PhabricatorEditorURIEngine.php +++ b/src/infrastructure/editor/PhabricatorEditorURIEngine.php @@ -16,7 +16,7 @@ public static function newForViewer(PhabricatorUser $viewer) { $pattern = $viewer->getUserSetting(PhabricatorEditorSetting::SETTINGKEY); - if (!strlen(trim($pattern))) { + if ($pattern === null || trim($pattern) === '') { return null; } diff --git a/src/infrastructure/editor/__tests__/PhabricatorEditorURIEngineTestCase.php b/src/infrastructure/editor/__tests__/PhabricatorEditorURIEngineTestCase.php index 43cebff2e6..b6a2b4d8db 100644 --- a/src/infrastructure/editor/__tests__/PhabricatorEditorURIEngineTestCase.php +++ b/src/infrastructure/editor/__tests__/PhabricatorEditorURIEngineTestCase.php @@ -3,6 +3,12 @@ final class PhabricatorEditorURIEngineTestCase extends PhabricatorTestCase { + protected function getPhabricatorTestCaseConfiguration() { + return array( + self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true, + ); + } + public function testPatternParsing() { $map = array( '' => array(), @@ -129,4 +135,15 @@ public function testPatternProtocols() { } } + public function testNewForViewer() { + $phabricator_user = $this->generateNewTestUser(); + try { + $engine = PhabricatorEditorURIEngine::newForViewer($phabricator_user); + $this->assertTrue(true, 'newForViewer did not throw an error'); + } catch (Throwable $ex) { + $this->assertTrue(false, + 'newForViewer threw an exception:'.$ex->getMessage()); + } + } + } diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php index 352e9aeb5e..a471874e43 100644 --- a/src/infrastructure/env/PhabricatorEnv.php +++ b/src/infrastructure/env/PhabricatorEnv.php @@ -125,7 +125,7 @@ private static function initializeCommonEnvironment($config_optional) { // If an instance identifier is defined, write it into the environment so // it's available to subprocesses. $instance = self::getEnvConfig('cluster.instance'); - if (strlen($instance)) { + if (phutil_nonempty_string($instance)) { putenv('PHABRICATOR_INSTANCE='.$instance); $_ENV['PHABRICATOR_INSTANCE'] = $instance; } @@ -432,8 +432,8 @@ public static function isSelfURI($raw_uri) { $uri = new PhutilURI($raw_uri); $host = $uri->getDomain(); - if (!strlen($host)) { - return false; + if (!phutil_nonempty_string($host)) { + return true; } $host = phutil_utf8_strtolower($host); @@ -455,7 +455,7 @@ private static function getSelfURIMap() { $self_map = array(); foreach ($self_uris as $self_uri) { $host = id(new PhutilURI($self_uri))->getDomain(); - if (!strlen($host)) { + if (!phutil_nonempty_string($host)) { continue; } @@ -495,7 +495,7 @@ public static function getDoclink($resource, $type = 'article') { ); $uri = new PhutilURI( - 'https://secure.phabricator.com/diviner/find/', + 'https://we.phorge.it/diviner/find/', $params); return phutil_string_cast($uri); @@ -661,7 +661,7 @@ public static function isValidURIForLink($uri) { public static function isValidLocalURIForLink($uri) { $uri = (string)$uri; - if (!strlen($uri)) { + if (!phutil_nonempty_string($uri)) { return false; } @@ -726,7 +726,7 @@ public static function requireValidRemoteURIForLink($raw_uri) { $uri = new PhutilURI($raw_uri); $proto = $uri->getProtocol(); - if (!strlen($proto)) { + if (!$proto) { throw new Exception( pht( 'URI "%s" is not a valid linkable resource. A valid linkable '. @@ -745,7 +745,7 @@ public static function requireValidRemoteURIForLink($raw_uri) { } $domain = $uri->getDomain(); - if (!strlen($domain)) { + if (!$domain) { throw new Exception( pht( 'URI "%s" is not a valid linkable resource. A valid linkable '. @@ -793,7 +793,7 @@ public static function requireValidRemoteURIForFetch( $uri = new PhutilURI($raw_uri); $proto = $uri->getProtocol(); - if (!strlen($proto)) { + if (!$proto) { throw new Exception( pht( 'URI "%s" is not a valid fetchable resource. A valid fetchable '. @@ -812,7 +812,7 @@ public static function requireValidRemoteURIForFetch( } $domain = $uri->getDomain(); - if (!strlen($domain)) { + if (!$domain) { throw new Exception( pht( 'URI "%s" is not a valid fetchable resource. A valid fetchable '. diff --git a/src/infrastructure/env/__tests__/PhabricatorEnvTestCase.php b/src/infrastructure/env/__tests__/PhabricatorEnvTestCase.php index f73299aa12..59e925f15c 100644 --- a/src/infrastructure/env/__tests__/PhabricatorEnvTestCase.php +++ b/src/infrastructure/env/__tests__/PhabricatorEnvTestCase.php @@ -243,6 +243,12 @@ public function testSelfURI() { 'https://old.example.com/path/to/resource.png' => true, 'https://other.example.com/' => false, + + '/' => true, + '/self' => true, + '#self' => true, + '/#self' => true, + '/self/#self' => true, ); foreach ($map as $input => $expect) { diff --git a/src/infrastructure/events/PhabricatorAutoEventListener.php b/src/infrastructure/events/PhabricatorAutoEventListener.php index 0ed76b3390..a8419f6865 100644 --- a/src/infrastructure/events/PhabricatorAutoEventListener.php +++ b/src/infrastructure/events/PhabricatorAutoEventListener.php @@ -10,6 +10,6 @@ * * All concrete subclasses of this class are automatically registered at * startup. This allows it to be used with custom one-offs that can be dropped - * into `phabricator/src/extensions/`. + * into `phorge/src/extensions/`. */ abstract class PhabricatorAutoEventListener extends PhabricatorEventListener {} diff --git a/src/infrastructure/export/format/PhabricatorCSVExportFormat.php b/src/infrastructure/export/format/PhabricatorCSVExportFormat.php index 8f3879d5fb..f9d8bf1ad3 100644 --- a/src/infrastructure/export/format/PhabricatorCSVExportFormat.php +++ b/src/infrastructure/export/format/PhabricatorCSVExportFormat.php @@ -48,11 +48,11 @@ private function addRow(array $values) { // like it might be too tempting for Excel to ignore, mangle the value // to dissuade remote code execution. See T12800. - if (preg_match('/^\s*[+=@-]/', $value)) { + if ($value && preg_match('/^\s*[+=@-]/', $value)) { $value = '(!) '.$value; } - if (preg_match('/\s|,|\"/', $value)) { + if ($value && preg_match('/\s|,|\"/', $value)) { $value = str_replace('"', '""', $value); $value = '"'.$value.'"'; } diff --git a/src/infrastructure/javelin/markup.php b/src/infrastructure/javelin/markup.php index 533baad65e..285f027d3d 100644 --- a/src/infrastructure/javelin/markup.php +++ b/src/infrastructure/javelin/markup.php @@ -74,10 +74,31 @@ function phabricator_form(PhabricatorUser $user, $attributes, $content) { $body = array(); $http_method = idx($attributes, 'method'); - $is_post = (strcasecmp($http_method, 'POST') === 0); + $is_post = $http_method && (strcasecmp($http_method, 'POST') === 0); $http_action = idx($attributes, 'action'); - $is_absolute_uri = preg_match('#^(https?:|//)#', $http_action); + + if ($http_action === null) { + // Not sure what this is. + $is_absolute_uri = false; + + } else if ($http_action instanceof PhutilURI) { + // This is the happy path, I think + + // For now, this is close enough - I suspect we'll stay with "https" schema + // for the rest of eternity. + $protocol = $http_action->getProtocol(); + $is_absolute_uri = ($protocol == 'http' || $protocol == 'https'); + + } else if (is_string($http_action)) { + // Also good path? + $is_absolute_uri = preg_match('#^(https?:|//)#', $http_action); + } else { + throw new Exception( + pht( + 'Unexpected object type provided as `action` - %s', + gettype($http_action))); + } if ($is_post) { diff --git a/src/infrastructure/log/PhabricatorSSHLog.php b/src/infrastructure/log/PhabricatorSSHLog.php index de4c270f72..e3c15de6bf 100644 --- a/src/infrastructure/log/PhabricatorSSHLog.php +++ b/src/infrastructure/log/PhabricatorSSHLog.php @@ -24,7 +24,7 @@ public static function getLog() { ); $sudo_user = PhabricatorEnv::getEnvConfig('phd.user'); - if (strlen($sudo_user)) { + if (phutil_nonempty_string($sudo_user)) { $data['S'] = $sudo_user; } diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php index b8fef85b98..8763111dd1 100644 --- a/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php @@ -44,7 +44,18 @@ public function getMatchingLineCount(array $lines, $cursor) { } public function markupText($text, $children) { - if (preg_match('/^\s*```/', $text)) { + // Header/footer eventually useful to be nice with "flavored markdown". + // When it starts with ```stuff the header is 'stuff' (->language) + // When it ends with stuff``` the footer is 'stuff' (->garbage) + $header_line = null; + $footer_line = null; + + $matches = null; + if (preg_match('/^\s*```(.*)/', $text, $matches)) { + if (isset($matches[1])) { + $header_line = $matches[1]; + } + // If this is a ```-style block, trim off the backticks and any leading // blank line. $text = preg_replace('/^\s*```(\s*\n)?/', '', $text); @@ -52,6 +63,13 @@ public function markupText($text, $children) { } $lines = explode("\n", $text); + + // If we have a flavored header, it has sense to look for the footer. + if ($header_line !== null && $lines) { + $footer_line = $lines[last_key($lines)]; + } + + // Strip final empty lines while ($lines && !strlen(last($lines))) { unset($lines[last_key($lines)]); } @@ -65,20 +83,39 @@ public function markupText($text, $children) { $parser = new PhutilSimpleOptions(); $custom = $parser->parse(head($lines)); + $valid_options = null; if ($custom) { - $valid = true; + $valid_options = true; foreach ($custom as $key => $value) { if (!array_key_exists($key, $options)) { - $valid = false; + $valid_options = false; break; } } - if ($valid) { + if ($valid_options) { array_shift($lines); $options = $custom + $options; } } + // Parse flavored markdown strictly to don't eat legitimate Remarkup. + // Proceed only if we tried to parse options and we failed + // (no options also mean no language). + // For example this is not a valid option: ```php + // Proceed only if the footer exists and it is not: blabla``` + // Accept only 2 lines or more. First line: header; then content. + if ( + $valid_options === false && + $header_line !== null && + $footer_line === '' && + count($lines) > 1 + ) { + if (self::isKnownLanguageCode($header_line)) { + array_shift($lines); + $options['lang'] = $header_line; + } + } + // Normalize the text back to a 0-level indent. $min_indent = 80; foreach ($lines as $line) { @@ -116,6 +153,11 @@ public function markupText($text, $children) { return implode("\n", $out); } + // The name is usually a sufficient source of information for file ext. + if (empty($options['lang']) && isset($options['name'])) { + $options['lang'] = $this->guessFilenameExtension($options['name']); + } + if (empty($options['lang'])) { // If the user hasn't specified "lang=..." explicitly, try to guess the // language. If we fail, fall back to configured defaults. @@ -249,4 +291,75 @@ private function highlightSource($text, array $options) { $engine->highlightSource($options['lang'], $text))); } + /** + * Check if a language code can be used in a generic flavored markdown. + * @param string $lang Language code + * @return bool + */ + private static function isKnownLanguageCode($lang) { + $languages = self::knownLanguageCodes(); + return isset($languages[$lang]); + } + + /** + * Get the available languages for a generic flavored markdown. + * @return array Languages as array keys. Ignore the value. + */ + private static function knownLanguageCodes() { + // This is a friendly subset from https://pygments.org/languages/ + static $map = array( + 'arduino' => 1, + 'assembly' => 1, + 'awk' => 1, + 'bash' => 1, + 'bat' => 1, + 'c' => 1, + 'cmake' => 1, + 'cobol' => 1, + 'cpp' => 1, + 'css' => 1, + 'csharp' => 1, + 'dart' => 1, + 'delphi' => 1, + 'fortran' => 1, + 'go' => 1, + 'groovy' => 1, + 'haskell' => 1, + 'java' => 1, + 'javascript' => 1, + 'kotlin' => 1, + 'lisp' => 1, + 'lua' => 1, + 'matlab' => 1, + 'make' => 1, + 'perl' => 1, + 'php' => 1, + 'powershell' => 1, + 'python' => 1, + 'r' => 1, + 'ruby' => 1, + 'rust' => 1, + 'scala' => 1, + 'sh' => 1, + 'sql' => 1, + 'typescript' => 1, + 'vba' => 1, + ); + return $map; + } + + /** + * Get the extension from a filename. + * @param string "/path/to/something.name" + * @return null|string ".name" + */ + private function guessFilenameExtension($name) { + $name = basename($name); + $pos = strrpos($name, '.'); + if ($pos !== false) { + return substr($name, $pos + 1); + } + return null; + } + } diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupInterpreterBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupInterpreterBlockRule.php index a54e6b8b13..5585ddf541 100644 --- a/src/infrastructure/markup/blockrule/PhutilRemarkupInterpreterBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupInterpreterBlockRule.php @@ -2,13 +2,35 @@ final class PhutilRemarkupInterpreterBlockRule extends PhutilRemarkupBlockRule { - const START_BLOCK_PATTERN = '/^([\w]+)\s*(?:\(([^)]+)\)\s*)?{{{/'; + /** + * Second part of the regex to find stuff like: + * interpreterName {{{ stuff }}} + * interpreterName (options) {{{ stuff }}} + * You have found the kernel of cowsay and figlet. + */ const END_BLOCK_PATTERN = '/}}}\s*$/'; + /** + * Constructs the first part of the regex to find stuff like: + * interpreterName {{{ stuff }}} + * interpreterName (options) {{{ stuff }}} + * The exact regex is constructed from the available interpreters. + * @return string First part of interpreters regex + */ + private function getStartBlockPattern() { + $interpreters = id(new PhutilClassMapQuery()) + ->setAncestorClass('PhutilRemarkupBlockInterpreter') + ->execute(); + $interpreters_regex = mpull($interpreters, 'getInterpreterName'); + $interpreters_regex = array_map('preg_quote', $interpreters_regex); + $interpreters_regex = implode('|', $interpreters_regex); + return "/^($interpreters_regex)\s*(?:\(([^)]+)\)\s*)?{{{/"; + } + public function getMatchingLineCount(array $lines, $cursor) { $num_lines = 0; - if (preg_match(self::START_BLOCK_PATTERN, $lines[$cursor])) { + if (preg_match(self::getStartBlockPattern(), $lines[$cursor])) { $num_lines++; while (isset($lines[$cursor])) { @@ -33,7 +55,7 @@ public function markupText($text, $children) { } $matches = null; - preg_match(self::START_BLOCK_PATTERN, head($lines), $matches); + preg_match(self::getStartBlockPattern(), head($lines), $matches); $argv = array(); if (isset($matches[2])) { @@ -49,7 +71,7 @@ public function markupText($text, $children) { } $lines[$first_key] = preg_replace( - self::START_BLOCK_PATTERN, + self::getStartBlockPattern(), '', $lines[$first_key]); $lines[$last_key] = preg_replace( diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupLiteralBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupLiteralBlockRule.php index 4f30ad089e..505c7ecfd1 100644 --- a/src/infrastructure/markup/blockrule/PhutilRemarkupLiteralBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupLiteralBlockRule.php @@ -7,7 +7,7 @@ public function getPriority() { } public function getMatchingLineCount(array $lines, $cursor) { - // NOTE: We're consuming all continguous blocks of %%% literals, so this: + // NOTE: We're consuming all contiguous blocks of %%% literals, so this: // // %%%a%%% // %%%b%%% diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupTableBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupTableBlockRule.php index 72e61881ce..f02b9b7422 100644 --- a/src/infrastructure/markup/blockrule/PhutilRemarkupTableBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupTableBlockRule.php @@ -114,7 +114,7 @@ private function newTable(PhutilDOMNode $table) { if ($cell->isContentNode()) { $content = $node->getContent(); - if (!strlen(trim($content))) { + if ($content === null || trim($content) === '') { continue; } diff --git a/src/infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php b/src/infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php index cb67041c62..a525924fff 100644 --- a/src/infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php +++ b/src/infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php @@ -68,11 +68,11 @@ private function evaluateExpression($expression) { 'platform' => array( 'server' => array( 'name' => PlatformSymbols::getPlatformServerName(), - 'path' => pht('phabricator/'), + 'path' => PlatformSymbols::getPlatformServerPath(), ), 'client' => array( 'name' => PlatformSymbols::getPlatformClientName(), - 'path' => pht('arcanist/'), + 'path' => PlatformSymbols::getPlatformClientPath(), ), ), ), diff --git a/src/infrastructure/markup/remarkup/__tests__/PhutilRemarkupEngineTestCase.php b/src/infrastructure/markup/remarkup/__tests__/PhutilRemarkupEngineTestCase.php index c3b4960d0c..38831d034a 100644 --- a/src/infrastructure/markup/remarkup/__tests__/PhutilRemarkupEngineTestCase.php +++ b/src/infrastructure/markup/remarkup/__tests__/PhutilRemarkupEngineTestCase.php @@ -2,6 +2,8 @@ /** * Test cases for @{class:PhutilRemarkupEngine}. + * @TODO: This unit is not always triggered when you need it. + * https://we.phorge.it/T15500 */ final class PhutilRemarkupEngineTestCase extends PhutilTestCase { diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/code-block-guess-from-name.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/code-block-guess-from-name.txt new file mode 100644 index 0000000000..5a2c6a7c50 --- /dev/null +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/code-block-guess-from-name.txt @@ -0,0 +1,7 @@ + name=/etc/phpmyadmin/config.txt + $lol = 1; +~~~~~~~~~~ +
/etc/phpmyadmin/config.txt
$lol = 1;
+~~~~~~~~~~ +name=/etc/phpmyadmin/config.txt + $lol = 1; diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/interpreter-test.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/interpreter-test.txt index 477f3eeea0..edbd49208c 100644 --- a/src/infrastructure/markup/remarkup/__tests__/remarkup/interpreter-test.txt +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/interpreter-test.txt @@ -30,9 +30,7 @@ Argv: () Content: (content) Argv: (x=y) - - -
No interpreter found: phutil_fake_test_block_interpreter
+

phutil_fake_test_block_interpreter {{{ content }}}

~~~~~~~~~~ Content: (content) Argv: (foo=bar) @@ -53,6 +51,4 @@ Argv: () Content: (content) Argv: (x=y) - - -(No interpreter found: phutil_fake_test_block_interpreter) +phutil_fake_test_block_interpreter {{{ content }}} diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-flavored.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-flavored.txt new file mode 100644 index 0000000000..f224942b1d --- /dev/null +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-flavored.txt @@ -0,0 +1,7 @@ +```cpp +code +``` +~~~~~~~~~~ +
code
+~~~~~~~~~~ + code diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-comment.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-comment.txt new file mode 100644 index 0000000000..bcdaca8063 --- /dev/null +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-comment.txt @@ -0,0 +1,18 @@ +```#comment +code + +#more comment +more code``` + +~~~~~~~~~~ +
#comment
+code
+
+#more comment
+more code
+~~~~~~~~~~ + #comment + code + + #more comment + more code diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-empty.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-empty.txt new file mode 100644 index 0000000000..c05d44ec0f --- /dev/null +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored-empty.txt @@ -0,0 +1,9 @@ +``` +cpp +second line``` +~~~~~~~~~~ +
cpp
+second line
+~~~~~~~~~~ + cpp + second line diff --git a/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored.txt b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored.txt new file mode 100644 index 0000000000..5dc2cef421 --- /dev/null +++ b/src/infrastructure/markup/remarkup/__tests__/remarkup/tick-block-multi-flavored.txt @@ -0,0 +1,20 @@ +```cpp +code + +more code + +more code +``` + +~~~~~~~~~~ +
code
+
+more code
+
+more code
+~~~~~~~~~~ + code + + more code + + more code diff --git a/src/infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php b/src/infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php index 56bab68e44..c5344a692c 100644 --- a/src/infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php +++ b/src/infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php @@ -212,6 +212,7 @@ public function markupKeystrokes(array $matches) { 'line-height: 0.6rem;', 'border-radius: 3px;', 'box-shadow: inset 0 -1px 0 rgba(71, 87, 120, 0.08);', + '-webkit-user-select: none;', 'user-select: none;', 'background: #f7f7f7;', 'border: 1px solid #C7CCD9;', diff --git a/src/infrastructure/markup/syntax/highlighter/xhpast/PhutilXHPASTSyntaxHighlighterFuture.php b/src/infrastructure/markup/syntax/highlighter/xhpast/PhutilXHPASTSyntaxHighlighterFuture.php index c34153ba42..f617122ec8 100644 --- a/src/infrastructure/markup/syntax/highlighter/xhpast/PhutilXHPASTSyntaxHighlighterFuture.php +++ b/src/infrastructure/markup/syntax/highlighter/xhpast/PhutilXHPASTSyntaxHighlighterFuture.php @@ -32,7 +32,7 @@ private function applyXHPHighlight($result) { // We perform two passes here: one using the AST to find symbols we care // about -- particularly, class names and function names. These are used - // in the crossreference stuff to link into Diffusion. After we've done our + // in the cross-reference stuff to link into Diffusion. After we've done our // AST pass, we do a followup pass on the token stream to catch all the // simple stuff like strings and comments. diff --git a/src/infrastructure/parser/PhutilPygmentizeParser.php b/src/infrastructure/parser/PhutilPygmentizeParser.php index 35c1d5739c..39b0e441c5 100644 --- a/src/infrastructure/parser/PhutilPygmentizeParser.php +++ b/src/infrastructure/parser/PhutilPygmentizeParser.php @@ -32,7 +32,7 @@ public function parse($block) { $c = $block[$ii]; switch ($mode) { case 'text': - // We're in general text between tags, and just passing characers + // We're in general text between tags, and just passing characters // through unmodified. if ($c == '<') { $mode = 'tag'; diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php index 42ccad3316..cdd941a5f4 100644 --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -735,6 +735,9 @@ final protected function buildPagingClauseFromMultipleColumns( array( 'table' => 'optional string|null', 'column' => 'string', + 'customfield' => 'optional bool', + 'customfield.index.key' => 'optional string', + 'customfield.index.table' => 'optional string', 'value' => 'wild', 'type' => 'string', 'reverse' => 'optional bool', @@ -1747,7 +1750,7 @@ protected function getPagingValueMapForCustomFields( $map = array(); foreach ($fields->getFields() as $field) { - $map['custom:'.$field->getFieldKey()] = $field->getValueForStorage(); + $map[$field->getModernFieldKey()] = $field->getValueForStorage(); } return $map; @@ -1758,7 +1761,7 @@ protected function getPagingValueMapForCustomFields( * @task customfield */ protected function isCustomFieldOrderKey($key) { - $prefix = 'custom:'; + $prefix = 'custom.'; return !strncmp($key, $prefix, strlen($prefix)); } @@ -2449,7 +2452,7 @@ protected function withNgramsConstraint( PhabricatorSearchNgrams $index, $value) { - if (strlen($value)) { + if (phutil_nonempty_string($value)) { $this->ngrams[] = array( 'index' => $index, 'value' => $value, diff --git a/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php b/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php index 93f623338f..88ec92daba 100644 --- a/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php +++ b/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php @@ -290,12 +290,12 @@ protected function detectEncodingForStorage($string) { } protected function getUTF8StringFromStorage($string, $encoding) { - if ($encoding == 'utf8') { + if ($encoding == 'utf8' || !phutil_nonempty_string($string)) { return $string; } if (function_exists('mb_detect_encoding')) { - if (strlen($encoding)) { + if (phutil_nonempty_string($encoding)) { $try_encodings = array( $encoding, ); diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php index f68635860a..d29e1bcc07 100644 --- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php +++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php @@ -50,7 +50,6 @@ public function getOldPatches() { 'after' => array( /* First Patch */ ), ), 'db.calendar' => array(), - 'db.chatlog' => array(), 'db.conduit' => array(), 'db.countdown' => array(), 'db.daemon' => array(), diff --git a/src/infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php b/src/infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php index 72b6dfb4b0..35c9df7857 100644 --- a/src/infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php +++ b/src/infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php @@ -11,65 +11,67 @@ public function getStyleName() { public function getStyleMap() { return array( - 'hll' => 'color: #ffffcc', - 'c' => 'color: #74777d', - 'cm' => 'color: #74777d', - 'c1' => 'color: #74777d', - 'cs' => 'color: #74777d', - 'sd' => 'color: #000000', - 'sh' => 'color: #000000', - 's' => 'color: #766510', - 'sb' => 'color: #766510', - 'sc' => 'color: #766510', - 's2' => 'color: #766510', - 's1' => 'color: #766510', - 'sx' => 'color: #766510', - 'sr' => 'color: #bb6688', - 'nv' => 'color: #001294', - 'vi' => 'color: #001294', - 'vg' => 'color: #001294', - 'na' => 'color: #354bb3', - 'kc' => 'color: #000a65', - 'no' => 'color: #000a65', - 'k' => 'color: #aa4000', - 'kd' => 'color: #aa4000', - 'kn' => 'color: #aa4000', - 'kt' => 'color: #aa4000', - 'cp' => 'color: #304a96', - 'kp' => 'color: #304a96', - 'kr' => 'color: #304a96', - 'nb' => 'color: #304a96', - 'bp' => 'color: #304a96', - 'nc' => 'color: #00702a', - 'nt' => 'color: #00702a', - 'vc' => 'color: #00702a', - 'nf' => 'color: #004012', - 'nx' => 'color: #004012', - 'o' => 'color: #aa2211', - 'ss' => 'color: #aa2211', - 'm' => 'color: #601200', - 'mf' => 'color: #601200', - 'mh' => 'color: #601200', - 'mi' => 'color: #601200', - 'mo' => 'color: #601200', - 'il' => 'color: #601200', - 'gd' => 'color: #a00000', - 'gr' => 'color: #ff0000', - 'gh' => 'color: #000080', - 'gi' => 'color: #00a000', - 'go' => 'color: #808080', - 'gp' => 'color: #000080', - 'gu' => 'color: #800080', - 'gt' => 'color: #0040d0', - 'nd' => 'color: #aa22ff', - 'ni' => 'color: #92969d', - 'ne' => 'color: #d2413a', - 'nl' => 'color: #a0a000', - 'nn' => 'color: #0000ff', - 'ow' => 'color: #aa22ff', - 'w' => 'color: #bbbbbb', - 'se' => 'color: #bb6622', - 'si' => 'color: #bb66bb', + 'hll' => 'color: {$syntax.highlighted-line}', + 'c' => 'color: {$syntax.comment}', + 'cm' => 'color: {$syntax.comment-multiline}', + 'dc' => 'color: {$syntax.comment-multiline}', + 'c1' => 'color: {$syntax.comment-single}', + 'cs' => 'color: {$syntax.comment-special}', + 'sd' => 'color: {$syntax.string-doc}', + 'sh' => 'color: {$syntax.string-heredoc}', + 's' => 'color: {$syntax.string}', + 'sb' => 'color: {$syntax.string-backtick}', + 'sc' => 'color: {$syntax.literal-string-char}', + 's2' => 'color: {$syntax.string-double}', + 's1' => 'color: {$syntax.string-single}', + 'sx' => 'color: {$syntax.string-other}', + 'sr' => 'color: {$syntax.string-regex}', + 'nv' => 'color: {$syntax.name-variable}', + 'vi' => 'color: {$syntax.variable-instance}', + 'vg' => 'color: {$syntax.variable-global}', + 'na' => 'color: {$syntax.name-attribute}', + 'kc' => 'color: {$syntax.keyword-constant}', + 'no' => 'color: {$syntax.name-operator}', + 'k' => 'color: {$syntax.keyword}', + 'kd' => 'color: {$syntax.keyword-declaration}', + 'kn' => 'color: {$syntax.keyword-namespace}', + 'kt' => 'color: {$syntax.keyword-type}', + 'cp' => 'color: {$syntax.comment-preproc}', + 'kp' => 'color: {$syntax.keyword-preproc}', + 'kr' => 'color: {$syntax.keyword-reserved}', + 'nb' => 'color: {$syntax.name-builtin}', + 'bp' => 'color: {$syntax.builtin-pseudo}', + 'nc' => 'color: {$syntax.name-class}', + 'nt' => 'color: {$syntax.name-tag}', + 'vc' => 'color: {$syntax.name-variable-class}', + 'nf' => 'color: {$syntax.name-function}', + 'nx' => 'color: {$syntax.name-exception}', + 'o' => 'color: {$syntax.operator}', + 'p' => 'color: {$syntax.punctuation}', + 'ss' => 'color: {$syntax.literal-string-symbol}', + 'm' => 'color: {$syntax.literal-number}', + 'mf' => 'color: {$syntax.literal-number-float}', + 'mh' => 'color: {$syntax.literal-number-hex}', + 'mi' => 'color: {$syntax.literal-number-integer}', + 'mo' => 'color: {$syntax.literal-number-octal}', + 'il' => 'color: {$syntax.literal-number-integer-long}', + 'gd' => 'color: {$syntax.generic-deleted}', + 'gr' => 'color: {$syntax.generic-red}', + 'gh' => 'color: {$syntax.generic-heading}', + 'gi' => 'color: {$syntax.generic-inserted}', + 'go' => 'color: {$syntax.generic-output}', + 'gp' => 'color: {$syntax.generic-prompt}', + 'gu' => 'color: {$syntax.generic-underline}', + 'gt' => 'color: {$syntax.generic-traceback}', + 'nd' => 'color: {$syntax.name-decorator}', + 'ni' => 'color: {$syntax.name-identifier}', + 'ne' => 'color: {$syntax.name-entity}', + 'nl' => 'color: {$syntax.name-label}', + 'nn' => 'color: {$syntax.name-namespace}', + 'ow' => 'color: {$syntax.operator-word}', + 'w' => 'color: {$syntax.text-whitespace}', + 'se' => 'color: {$syntax.literal-string-escape}', + 'si' => 'color: {$syntax.literal-string-interpol}', ); } diff --git a/src/view/AphrontDialogView.php b/src/view/AphrontDialogView.php index b8b00a6b3e..14aa4a19b6 100644 --- a/src/view/AphrontDialogView.php +++ b/src/view/AphrontDialogView.php @@ -51,6 +51,15 @@ public function getIsStandalone() { return $this->isStandalone; } + /** + * Set the URI associated to the Submit Button + * + * If you want a normal link and not any form submission, + * see also: setDisableWorkflowOnSubmit(false). + * + * @param string $uri + * @return self + */ public function setSubmitURI($uri) { $this->submitURI = $uri; return $this; @@ -92,6 +101,15 @@ public function getResizeX() { return $this->resizeX; } + /** + * Add a Submit Button and specify its text + * + * If you want to associate an URI for this Button, + * see also: setSubmitURI(). + * + * @param string $text Text shown for that button + * @return self + */ public function addSubmitButton($text = null) { if (!$text) { $text = pht('Okay'); @@ -228,6 +246,14 @@ public function appendForm(AphrontFormView $form) { return $this->appendChild($form->buildLayoutView()); } + /** + * Enable or Disable a Workflow on Submit + * + * For example, if your Submit Button should be a normal link, + * without activating any Workflow, you can set false. + * @param bool $disable_workflow_on_submit + * @return self + */ public function setDisableWorkflowOnSubmit($disable_workflow_on_submit) { $this->disableWorkflowOnSubmit = $disable_workflow_on_submit; return $this; diff --git a/src/view/control/AphrontTableView.php b/src/view/control/AphrontTableView.php index a3c0a49be4..ab1f6be0ed 100644 --- a/src/view/control/AphrontTableView.php +++ b/src/view/control/AphrontTableView.php @@ -135,7 +135,7 @@ public function render() { $col_classes = array(); foreach ($this->columnClasses as $key => $class) { - if (strlen($class)) { + if (phutil_nonempty_string($class)) { $col_classes[] = $class; } else { $col_classes[] = null; @@ -392,7 +392,7 @@ public static function renderSingleDisplayLine($line) { // white-space: pre to prevent wrapping. We need to append a character // (  -- nonbreaking space) afterward to give the bounds div height // (alternatively, we could hard-code the line height). This is gross but - // it's not clear that there's a better appraoch. + // it's not clear that there's a better approach. return phutil_tag( 'div', diff --git a/src/view/form/control/AphrontFormControl.php b/src/view/form/control/AphrontFormControl.php index 0125fa87b3..83e0207241 100644 --- a/src/view/form/control/AphrontFormControl.php +++ b/src/view/form/control/AphrontFormControl.php @@ -56,11 +56,22 @@ public function getLabel() { return $this->label; } + /** + * Set the Caption + * The Caption shows a tip usually nearby the related input field. + * @param string|PhutilSafeHTML|null + * @return self + */ public function setCaption($caption) { $this->caption = $caption; return $this; } + /** + * Get the Caption + * The Caption shows a tip usually nearby the related input field. + * @return string|PhutilSafeHTML|null + */ public function getCaption() { return $this->caption; } @@ -172,7 +183,7 @@ final public function render() { $this->renderInput()); $error = null; - if (strlen($this->getError())) { + if ($this->getError()) { $error = $this->getError(); if ($error === true) { $error = phutil_tag( @@ -187,7 +198,7 @@ final public function render() { } } - if (strlen($this->getLabel())) { + if (phutil_nonempty_string($this->getLabel())) { $label = phutil_tag( 'label', array( @@ -203,7 +214,11 @@ final public function render() { $custom_class .= ' aphront-form-control-nolabel'; } - if (strlen($this->getCaption())) { + // The Caption can be stuff like PhutilSafeHTML and other objects that + // can be casted to a string. After this cast we have never null. + $has_caption = phutil_string_cast($this->getCaption()) !== ''; + + if ($has_caption) { $caption = phutil_tag( 'div', array('class' => 'aphront-form-caption'), diff --git a/src/view/form/control/AphrontFormDateControl.php b/src/view/form/control/AphrontFormDateControl.php index 833c0a7c89..35613b3d66 100644 --- a/src/view/form/control/AphrontFormDateControl.php +++ b/src/view/form/control/AphrontFormDateControl.php @@ -267,9 +267,25 @@ protected function renderInput() { $week_key = PhabricatorWeekStartDaySetting::SETTINGKEY; $week_start = $viewer->getUserSetting($week_key); + $date_pht = array( + 'S|M|T|W|T|F|S' => pht('S|M|T|W|T|F|S'), + 'January' => pht('January'), + 'February' => pht('February'), + 'March' => pht('March'), + 'April' => pht('April'), + 'May' => pht('May'), + 'June' => pht('June'), + 'July' => pht('July'), + 'August' => pht('August'), + 'September' => pht('September'), + 'October' => pht('October'), + 'November' => pht('November'), + 'December' => pht('December'), + ); Javelin::initBehavior('fancy-datepicker', array( 'format' => $this->getDateFormat(), 'weekStart' => $week_start, + 'pht' => $date_pht, )); $classes = array(); diff --git a/src/view/form/control/AphrontFormTokenizerControl.php b/src/view/form/control/AphrontFormTokenizerControl.php index fe80c86f81..7af8fcae32 100644 --- a/src/view/form/control/AphrontFormTokenizerControl.php +++ b/src/view/form/control/AphrontFormTokenizerControl.php @@ -68,7 +68,7 @@ protected function renderInput() { $datasource->setViewer($this->getUser()); $placeholder = null; - if (!strlen($this->placeholder)) { + if (!phutil_nonempty_string($this->placeholder)) { $placeholder = $datasource->getPlaceholderText(); } diff --git a/src/view/form/control/PhabricatorRemarkupControl.php b/src/view/form/control/PhabricatorRemarkupControl.php index 67afd10630..ba7950bcc9 100644 --- a/src/view/form/control/PhabricatorRemarkupControl.php +++ b/src/view/form/control/PhabricatorRemarkupControl.php @@ -128,6 +128,8 @@ protected function renderInput() { 'disabled' => $this->getDisabled(), 'sendOnEnter' => $this->getSendOnEnter(), 'rootID' => $root_id, + 'remarkupMetadataID' => $metadata_id, + 'remarkupMetadataValue' => $metadata_value, 'autocompleteMap' => (object)array( 64 => array( // "@" 'datasourceURI' => $user_datasource->getDatasourceURI(), diff --git a/src/view/layout/AphrontSideNavFilterView.php b/src/view/layout/AphrontSideNavFilterView.php index b2d5a716d4..c7c8b5b534 100644 --- a/src/view/layout/AphrontSideNavFilterView.php +++ b/src/view/layout/AphrontSideNavFilterView.php @@ -92,12 +92,21 @@ public function newLink($key) { return $this->getMenuView()->getItem($key); } + /** + * Add a thing in the menu + * + * @param string $key Internal name + * @param string $name Human name + * @param mixed $uri Destination URI. For example as string or as PhutilURI. + * @param string $type Item type. For example see PHUIListItemView constants. + * @param string $icon Icon name + */ private function addThing($key, $name, $uri, $type, $icon = null) { $item = id(new PHUIListItemView()) ->setName($name) ->setType($type); - if (strlen($icon)) { + if (phutil_nonempty_string($icon)) { $item->setIcon($icon); } @@ -145,7 +154,7 @@ public function getBaseURI() { public function selectFilter($key, $default = null) { $this->selectedFilter = $default; - if ($this->menu->getItem($key) && strlen($key)) { + if ($this->menu->getItem($key) && phutil_nonempty_string($key)) { $this->selectedFilter = $key; } return $this->selectedFilter; diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php index 6f6ce56cad..005ede23a7 100644 --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -23,6 +23,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView private $crumbs; private $navigation; private $footer; + private $headItems = array(); public function setShowFooter($show_footer) { $this->showFooter = $show_footer; @@ -188,7 +189,7 @@ public function getTitle() { } } - if (strlen($prefix)) { + if (phutil_nonempty_string($prefix)) { $title = $prefix.' '.$title; } @@ -375,6 +376,18 @@ protected function willRenderPage() { } + /** + * Insert a HTML element into of the page to render. + * Used by PhameBlogViewController. + * + * @param PhutilSafeHTML HTML header to add + */ + public function addHeadItem($html) { + if ($html instanceof PhutilSafeHTML) { + $this->headItems[] = $html; + } + } + protected function getHead() { $monospaced = null; @@ -400,16 +413,18 @@ protected function getHead() { $font_css = hsprintf( '', $monospaced); } return hsprintf( - '%s%s%s', + '%s%s%s%s', parent::getHead(), $font_css, + phutil_implode_html('', $this->headItems), $response->renderSingleResource('javelin-magical-init', 'phabricator')); } diff --git a/src/view/page/menu/PhabricatorMainMenuView.php b/src/view/page/menu/PhabricatorMainMenuView.php index 29a259b44c..f0b2bbbe83 100644 --- a/src/view/page/menu/PhabricatorMainMenuView.php +++ b/src/view/page/menu/PhabricatorMainMenuView.php @@ -327,13 +327,13 @@ private function renderPhabricatorLogo() { $logo_node = phutil_tag( 'span', array( - 'class' => 'phabricator-main-menu-eye', + 'class' => 'phabricator-main-menu-project-logo', 'style' => implode(' ', $logo_style), )); $wordmark_text = PhabricatorCustomLogoConfigType::getLogoWordmark(); - if (!strlen($wordmark_text)) { + if (!phutil_nonempty_string($wordmark_text)) { $wordmark_text = PlatformSymbols::getPlatformServerName(); } diff --git a/src/view/phui/PHUICurtainObjectRefView.php b/src/view/phui/PHUICurtainObjectRefView.php index e7ead114c7..855aa855c6 100644 --- a/src/view/phui/PHUICurtainObjectRefView.php +++ b/src/view/phui/PHUICurtainObjectRefView.php @@ -7,6 +7,7 @@ final class PHUICurtainObjectRefView private $epoch; private $highlighted; private $exiled; + private $hovercarded = true; private $exileNote = false; public function setHandle(PhabricatorObjectHandle $handle) { @@ -30,6 +31,11 @@ public function setExiled($is_exiled, $note = false) { return $this; } + public function setHovercarded($hovercarded) { + $this->hovercarded = $hovercarded; + return $this; + } + protected function getTagAttributes() { $classes = array(); $classes[] = 'phui-curtain-object-ref-view'; @@ -155,7 +161,11 @@ private function newTitle() { $handle = $this->handle; if ($handle) { - $title_view = $handle->renderLink(); + if ($this->hovercarded) { + $title_view = $handle->renderHovercardLink(); + } else { + $title_view = $handle->renderLink(); + } } return $title_view; diff --git a/src/view/phui/PHUIIconView.php b/src/view/phui/PHUIIconView.php index a5897e02d6..5a6bba71b1 100644 --- a/src/view/phui/PHUIIconView.php +++ b/src/view/phui/PHUIIconView.php @@ -780,7 +780,7 @@ public static function getIcons() { 'fa-reddit-alien', 'fa-edge', 'fa-credit-card-alt', - 'fa-codiepie:before', + 'fa-codiepie', 'fa-modx', 'fa-fort-awesome', 'fa-usb', @@ -843,7 +843,7 @@ public static function getIcons() { 'fa-address-card-o', 'fa-user-circle', 'fa-user-circle-o', - 'fa-user-o:before', + 'fa-user-o', 'fa-id-badge', 'fa-drivers-license', 'fa-id-card', @@ -861,7 +861,7 @@ public static function getIcons() { 'fa-thermometer-half', 'fa-thermometer-1', 'fa-thermometer-quarter', - 'fa-thermometer-0:', + 'fa-thermometer-0', 'fa-thermometer-empty', 'fa-shower', 'fa-bathtub', diff --git a/src/view/phui/PHUIInfoView.php b/src/view/phui/PHUIInfoView.php index cefeedbe5f..58898d40d2 100644 --- a/src/view/phui/PHUIInfoView.php +++ b/src/view/phui/PHUIInfoView.php @@ -19,6 +19,12 @@ final class PHUIInfoView extends AphrontTagView { private $flush; private $icon; + /** + * Set a title + * + * @param string|null $title + * @return self + */ public function setTitle($title) { $this->title = $title; return $this; @@ -147,7 +153,7 @@ protected function getTagContent() { } $title = $this->title; - if ($title || strlen($title)) { + if ($title || phutil_nonempty_string($title)) { $title = phutil_tag( 'h1', array( diff --git a/src/view/phui/PHUIObjectItemListView.php b/src/view/phui/PHUIObjectItemListView.php index fbc3904586..629feb9139 100644 --- a/src/view/phui/PHUIObjectItemListView.php +++ b/src/view/phui/PHUIObjectItemListView.php @@ -120,7 +120,7 @@ protected function getTagContent() { require_celerity_resource('phui-oi-color-css'); $header = null; - if (strlen($this->header)) { + if (phutil_nonempty_string($this->header)) { $header = phutil_tag( 'h1', array( diff --git a/src/view/phui/PHUIObjectItemView.php b/src/view/phui/PHUIObjectItemView.php index 4bb9c3ea6a..527bd51b38 100644 --- a/src/view/phui/PHUIObjectItemView.php +++ b/src/view/phui/PHUIObjectItemView.php @@ -83,11 +83,25 @@ public function getObject() { return $this->object; } + /** + * Set the href attribute + * + * @param string|PhutilURI|null $href + * @return self + */ public function setHref($href) { + PhutilURI::checkHrefType($href); + $this->href = $href; return $this; } + /** + * Get the href attribute + * + * @see PHUIObjectItemView::setHref() + * @return string|PhutilURI|null + */ public function getHref() { return $this->href; } @@ -136,7 +150,15 @@ public function setImageURI($image_uri) { return $this; } + /** + * Set the image href attribute + * + * @param string|PhutilURI|null $image_href + * @return self + */ public function setImageHref($image_href) { + PhutilURI::checkHrefType($image_href); + $this->imageHref = $image_href; return $this; } @@ -659,7 +681,8 @@ protected function getTagContent() { $this->getImageIcon()); } - if ($image && (strlen($this->href) || strlen($this->imageHref))) { + if ($image && (phutil_nonempty_stringlike($this->href) || + phutil_nonempty_stringlike($this->imageHref))) { $image_href = ($this->imageHref) ? $this->imageHref : $this->href; $image = phutil_tag( 'a', @@ -873,7 +896,7 @@ private function renderStatusIcon($icon, $label) { 'class' => 'phui-oi-status-icon', ); - if (strlen($label)) { + if (phutil_nonempty_string($label)) { $options['sigil'] = 'has-tooltip'; $options['meta'] = array('tip' => $label, 'size' => 300); } diff --git a/src/view/phui/PHUISegmentBarSegmentView.php b/src/view/phui/PHUISegmentBarSegmentView.php index 6bd3c530ef..d786e89eaa 100644 --- a/src/view/phui/PHUISegmentBarSegmentView.php +++ b/src/view/phui/PHUISegmentBarSegmentView.php @@ -26,6 +26,11 @@ public function setPosition($position) { return $this; } + /** + * Set a Tooltip. + * @param string|null $tooltip + * @return self + */ public function setTooltip($tooltip) { $this->tooltip = $tooltip; return $this; @@ -55,7 +60,7 @@ protected function getTagAttributes() { $left = sprintf('%.2f%%', $left); $tooltip = $this->tooltip; - if (strlen($tooltip)) { + if (phutil_nonempty_string($tooltip)) { Javelin::initBehavior('phabricator-tooltips'); $sigil = 'has-tooltip'; diff --git a/src/view/phui/PHUISegmentBarView.php b/src/view/phui/PHUISegmentBarView.php index 632c5327eb..8daa41c203 100644 --- a/src/view/phui/PHUISegmentBarView.php +++ b/src/view/phui/PHUISegmentBarView.php @@ -30,7 +30,7 @@ protected function getTagContent() { require_celerity_resource('phui-segment-bar-view-css'); $label = $this->label; - if (strlen($label)) { + if ($label !== null) { $label = phutil_tag( 'div', array( diff --git a/src/view/phui/PHUITagView.php b/src/view/phui/PHUITagView.php index cd6321a852..c36fbab36b 100644 --- a/src/view/phui/PHUITagView.php +++ b/src/view/phui/PHUITagView.php @@ -100,7 +100,15 @@ public function setName($name) { return $this; } + /** + * Set the href attribute + * + * @param string|PhutilURI|null $href + * @return self + */ public function setHref($href) { + PhutilURI::checkHrefType($href); + $this->href = $href; return $this; } @@ -126,7 +134,7 @@ public function setSlimShady($is_eminem) { } protected function getTagName() { - return strlen($this->href) ? 'a' : 'span'; + return phutil_nonempty_stringlike($this->href) ? 'a' : 'span'; } public function setContextObject($context_object) { diff --git a/src/view/widget/AphrontStackTraceView.php b/src/view/widget/AphrontStackTraceView.php index ff8cac43bc..15b39be878 100644 --- a/src/view/widget/AphrontStackTraceView.php +++ b/src/view/widget/AphrontStackTraceView.php @@ -15,7 +15,7 @@ public function render() { $libraries = PhutilBootloader::getInstance()->getAllLibraries(); // TODO: Make this configurable? - $path = 'https://secure.phabricator.com/diffusion/%s/browse/master/src/'; + $path = 'https://we.phorge.it/diffusion/%s/browse/master/src/'; $callsigns = array( 'arcanist' => 'ARC', diff --git a/support/aphlict/server/package-lock.json b/support/aphlict/server/package-lock.json new file mode 100644 index 0000000000..8af62ae233 --- /dev/null +++ b/support/aphlict/server/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "aphlict-server", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "ws": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz", + "integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==" + } + } +} diff --git a/support/aphlict/server/package.json b/support/aphlict/server/package.json new file mode 100644 index 0000000000..b66270b3c5 --- /dev/null +++ b/support/aphlict/server/package.json @@ -0,0 +1,12 @@ +{ + "name": "aphlict-server", + "description": "Phorge's aphlict's server", + "main": "aphlict_server.js", + "directories": { + "lib": "lib" + }, + "license": "Apache-2.0", + "dependencies": { + "ws": "^7.5.0" + } +} diff --git a/support/startup/PhabricatorStartup.php b/support/startup/PhabricatorStartup.php index 58031013ad..10c6295587 100644 --- a/support/startup/PhabricatorStartup.php +++ b/support/startup/PhabricatorStartup.php @@ -210,7 +210,7 @@ public static function loadCoreLibraries() { if (!$ok) { self::didFatal( 'Unable to load the "Arcanist" library. Put "arcanist/" next to '. - '"phabricator/" on disk.'); + '"phorge/" on disk.'); } // Load Phabricator itself using the absolute path, so we never end up doing @@ -261,10 +261,11 @@ public static function endOutputCapture() { public static function setDebugTimeLimit($limit) { self::$debugTimeLimit = $limit; - static $initialized; + static $initialized = false; if (!$initialized) { declare(ticks=1); register_tick_function(array(__CLASS__, 'onDebugTick')); + $initialized = true; } } @@ -576,7 +577,7 @@ private static function verifyPHP() { self::didFatal( 'This HTTP request included a "Proxy:" header, poisoning the '. 'environment (CVE-2016-5385 / httpoxy). Declining to process this '. - 'request. For details, see: https://phurl.io/u/httpoxy'); + 'request. For details, see: https://secure.phabricator.com/T11359'); } } diff --git a/support/startup/__tests__/PreambleUtilsTestCase.php b/support/startup/__tests__/PreambleUtilsTestCase.php new file mode 100755 index 0000000000..25062e3701 --- /dev/null +++ b/support/startup/__tests__/PreambleUtilsTestCase.php @@ -0,0 +1,123 @@ +#!/usr/bin/env php + 'abc', + $null_value => $undefined, + '' => $undefined, + + // Strange, unexpected cases: + 144 => '144', + ); + + foreach ($test_cases as $input => $expected) { + switch ($input) { + case $undefined: + unset($_SERVER['HTTP_X_FORWARDED_FOR']); + break; + + case $null_value: + $_SERVER['HTTP_X_FORWARDED_FOR'] = null; + break; + + default: + $_SERVER['HTTP_X_FORWARDED_FOR'] = $input; + break; + } + + unset($_SERVER['REMOTE_ADDR']); + + preamble_trust_x_forwarded_for_header(); + + if (!isset($_SERVER['REMOTE_ADDR'])) { + if ($expected === $undefined) { + // test pass + continue; + } else { + $this->failTest("Failed for input {$input} - result is not defined!"); + } + } + + $actual = $_SERVER['REMOTE_ADDR']; + + if ($actual !== $expected) { + var_dump($actual); + + $this->failTest( + "Failed for input {$input} - actual output is {$actual}"); + } + + } + + } + + + private function failTest($message = null) { + echo $message; + echo "\n"; + throw new Exception(); + } + + /** + * Run all tests in this class. + * + * Return: True if all tests passed; False if any test failed. + */ + final public function run() { + $reflection = new ReflectionClass($this); + $methods = $reflection->getMethods(); + + $any_fail = false; + + // Try to ensure that poorly-written tests which depend on execution order + // (and are thus not properly isolated) will fail. + shuffle($methods); + + foreach ($methods as $method) { + $name = $method->getName(); + if (!preg_match('/^test/', $name)) { + continue; + } + + try { + call_user_func_array( + array($this, $name), + array()); + echo "Test passed: {$name}\n"; + } catch (Throwable $ex) { + $any_fail = true; + echo "Failed test: {$name}\n"; + } + } + return !$any_fail; + } + +} + +require_once dirname(__DIR__).'/preamble-utils.php'; + +$test_case = new PreambleUtilsTestCase(); +$good = $test_case->run(); + +if (!$good) { + exit(3); +} diff --git a/webroot/rsrc/audio/basic/coin.license.txt b/webroot/rsrc/audio/basic/coin.license.txt new file mode 100644 index 0000000000..5db113800d --- /dev/null +++ b/webroot/rsrc/audio/basic/coin.license.txt @@ -0,0 +1,3 @@ +https://freesound.org/people/Jocabundus/sounds/678385/ + +2023, Jocabundus, Creative Commons Zero (public domain dedication) diff --git a/webroot/rsrc/audio/basic/coin.mp3 b/webroot/rsrc/audio/basic/coin.mp3 new file mode 100644 index 0000000000..5604194a4e Binary files /dev/null and b/webroot/rsrc/audio/basic/coin.mp3 differ diff --git a/webroot/rsrc/css/application/base/main-menu-view.css b/webroot/rsrc/css/application/base/main-menu-view.css index 66f81da0bc..c3e8e4267d 100644 --- a/webroot/rsrc/css/application/base/main-menu-view.css +++ b/webroot/rsrc/css/application/base/main-menu-view.css @@ -47,13 +47,13 @@ padding-left: 6px; } -.phabricator-main-menu-eye { +.phabricator-main-menu-project-logo { margin: 2px 0; width: 40px; height: 40px; float: left; display: block; - background-image: url(/rsrc/image/logo/light-eye.png); + background-image: url(/rsrc/image/logo/project-logo.png); background-size: 40px 40px; } @@ -70,7 +70,8 @@ float: left; color: #fff; font-size: 18px; - margin: 9px 4px 9px 6px; + line-height: 22px; + margin: 11px 4px 11px 6px; padding-right: 8px; max-width: 175px; overflow: hidden; @@ -301,6 +302,8 @@ a.phabricator-core-user-menu .caret:before { background-size: 30px 30px; background-repeat: no-repeat; position: relative; + overflow: hidden; + text-overflow: ellipsis; } .phabricator-main-search-typeahead-result .result-name { @@ -308,6 +311,7 @@ a.phabricator-core-user-menu .caret:before { font-size: {$normalfontsize}; font-weight: bold; color: {$darkgreytext}; + white-space: normal; } .phabricator-main-search-typeahead-result.result-closed { diff --git a/webroot/rsrc/css/application/base/standard-page-view.css b/webroot/rsrc/css/application/base/standard-page-view.css index ee74139e69..eec00154cf 100644 --- a/webroot/rsrc/css/application/base/standard-page-view.css +++ b/webroot/rsrc/css/application/base/standard-page-view.css @@ -74,6 +74,7 @@ body.white-background { text-decoration: none; border-radius: 3px; box-shadow: inset 0 -1px 0 rgba({$alphablue}, 0.08); + -webkit-user-select: none; user-select: none; color: {$darkgreytext}; background: {$lightgreybackground}; diff --git a/webroot/rsrc/css/application/chatlog/chatlog.css b/webroot/rsrc/css/application/chatlog/chatlog.css deleted file mode 100644 index b0cb05a0e8..0000000000 --- a/webroot/rsrc/css/application/chatlog/chatlog.css +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @provides phabricator-chatlog-css - */ - -.device-phone .phabricator-chat-log-wrap { - padding: 0; -} - -.phabricator-chat-log-pager-bottom { - padding: 8px 4px 16px; - font-weight: bold; - float: right; -} - -.phabricator-chat-log-pager-bottom a { - padding: 2px 3px; -} - -.phabricator-chat-log-panel { - clear: both; -} - -.phabricator-chat-log { - width: 100%; -} - -.phabricator-chat-log td { - padding: 8px; - line-height: 18px; -} - -.phabricator-chat-log tr { - background: #fff; -} - -.phabricator-chat-log tr td.author { - background: {$greybackground}; -} - -.phabricator-chat-log tr.alternate { - border-top: 1px solid {$thinblueborder}; - border-bottom: 1px solid {$thinblueborder}; -} - -.phabricator-chat-log tr.alternate td.author { - background: {$lightgreybackground}; -} - -.phabricator-chat-log tr.highlight td { - background: {$lightyellow}; -} - -.phabricator-chat-log td.timestamp { - white-space: nowrap; - text-align: right; - width: 12em; -} - -.phabricator-chat-log td.message .timestamp { - color: {$bluetext}; - font-size: {$smallestfontsize}; - float: right; - margin-left: 5px; -} - -.phabricator-chat-log td.author { - white-space: nowrap; - text-align: right; - font-weight: bold; - width: 140px; - color: {$darkbluetext}; -} - -.device-phone .phabricator-chat-log td.author { - width: 80px; -} - -.phabricator-chat-log td.message { - white-space: pre-wrap; - word-break: break-word; -} diff --git a/webroot/rsrc/css/application/config/setup-issue.css b/webroot/rsrc/css/application/config/setup-issue.css index a80b02815c..4af5e9f417 100644 --- a/webroot/rsrc/css/application/config/setup-issue.css +++ b/webroot/rsrc/css/application/config/setup-issue.css @@ -122,6 +122,11 @@ padding: 12px 0; } +.setup-issue-config > pre > tt { + user-select: none; + margin-right: 0.5em; +} + .setup-issue-config + .setup-issue-config { padding-top: 0; } diff --git a/webroot/rsrc/css/application/conpherence/message-pane.css b/webroot/rsrc/css/application/conpherence/message-pane.css index 0dc33789f3..87be9c5897 100644 --- a/webroot/rsrc/css/application/conpherence/message-pane.css +++ b/webroot/rsrc/css/application/conpherence/message-pane.css @@ -9,7 +9,7 @@ position: fixed; left: 240px; right: 240px; - top: 106px; + top: 115px; bottom: 0px; min-width: 300px; width: auto; @@ -55,8 +55,8 @@ position: fixed; left: 240px; right: 240px; - top: 106px; - bottom: 142px; + top: 115px; + bottom: 88px; overflow-x: hidden; overflow-y: auto; -webkit-overflow-scrolling: touch; @@ -95,7 +95,7 @@ .conpherence-message-pane .phui-form-view { border-width: 0; - height: 130px; + height: 76px; padding: 0 20px 12px; position: fixed; bottom: 0; @@ -347,7 +347,7 @@ body .conpherence-message-pane .aphront-form-control { } .conpherence-message-pane .remarkup-assist-textarea { - height: 88px; + height: 34px; padding: 8px; box-sizing: border-box; -moz-box-sizing: border-box; @@ -360,7 +360,6 @@ body .conpherence-message-pane .aphront-form-control { margin: 0; padding: 7px 8px 6px 38px; width: 100%; - height: 34px; resize: none; border-color: {$greyborder}; border-top-left-radius: 3px; diff --git a/webroot/rsrc/css/application/conpherence/notification.css b/webroot/rsrc/css/application/conpherence/notification.css index 21e73047a6..fd24e775f1 100644 --- a/webroot/rsrc/css/application/conpherence/notification.css +++ b/webroot/rsrc/css/application/conpherence/notification.css @@ -84,3 +84,8 @@ font-weight: normal; color: {$greytext}; } + +/* On small devices the Persistent Chat is already hidden, and so its option */ +.device .phabricator-notification-header .persistent-option { + display: none; +} diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index 5788bd68bc..a127d14648 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -183,6 +183,7 @@ should always have a boring grey background. */ cursor: pointer; border-right: 1px solid {$thinblueborder}; overflow: hidden; + white-space: nowrap; } .differential-diff td + td.n { @@ -432,37 +433,27 @@ unselectable. */ .differential-diff.copy-l > tbody > tr > td, .differential-diff.copy-r > tbody > tr > td { - -moz-user-select: none; - -ms-user-select: none; -webkit-user-select: none; user-select: none; } .differential-diff.copy-l > tbody > tr > td:nth-child(2) { - -moz-user-select: auto; - -ms-user-select: auto; -webkit-user-select: auto; user-select: auto; } .differential-diff.copy-l > tbody > tr > td.show-more:nth-child(2) { - -moz-user-select: none; - -ms-user-select: none; -webkit-user-select: none; user-select: none; } .differential-diff.copy-r > tbody > tr > td:nth-child(5) { - -moz-user-select: auto; - -ms-user-select: auto; -webkit-user-select: auto; user-select: auto; } .differential-diff.copy-l > tbody > tr.inline > td, .differential-diff.copy-r > tbody > tr.inline > td { - -moz-user-select: none; - -ms-user-select: none; -webkit-user-select: none; user-select: none; } diff --git a/webroot/rsrc/css/application/diffusion/diffusion-icons.css b/webroot/rsrc/css/application/diffusion/diffusion-icons.css index 072db01660..1b1887e904 100644 --- a/webroot/rsrc/css/application/diffusion/diffusion-icons.css +++ b/webroot/rsrc/css/application/diffusion/diffusion-icons.css @@ -2,11 +2,6 @@ * @provides diffusion-icons-css */ -input.diffusion-clone-uri { - display: block; - width: 100%; -} - .diffusion-clone-extras { font-size: 11px; text-align: right; @@ -35,21 +30,35 @@ input.diffusion-clone-uri { padding: 0; } -.diffusion-clone-uri-table { - width: 100%; +.diffusion-clone-uri-wrapper { + display: flex; } -.diffusion-clone-uri-table th { - width: 24px; - padding: 0 0 0 4px; +.diffusion-clone-uri-wrapper .diffusion-clone-uri-io { + align-items: center; + justify-content: center; + border: 1px solid {$greyborder}; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + display: flex; + width: 38px; } -.diffusion-clone-uri-table th a.button { +.diffusion-clone-uri-wrapper a.button { width: 12px; height: 19px; + margin-left: 4px; } -.diffusion-clone-uri-table th a.button .phui-icon-view { +.diffusion-clone-uri-wrapper a.button .phui-icon-view { left: 15px; top: 7px; } + +.diffusion-clone-uri-wrapper input.diffusion-clone-uri { + border-left: none; + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; + display: block; + width: 100%; +} diff --git a/webroot/rsrc/css/application/harbormaster/harbormaster.css b/webroot/rsrc/css/application/harbormaster/harbormaster.css index 9fdad43a42..b27d549b6b 100644 --- a/webroot/rsrc/css/application/harbormaster/harbormaster.css +++ b/webroot/rsrc/css/application/harbormaster/harbormaster.css @@ -44,10 +44,7 @@ background-color: {$paste.highlight}; border-right: 1px solid {$paste.border}; - -moz-user-select: -moz-none; - -khtml-user-select: none; -webkit-user-select: none; - -ms-user-select: none; user-select: none; } diff --git a/webroot/rsrc/css/application/ponder/ponder-view.css b/webroot/rsrc/css/application/ponder/ponder-view.css index 966fb6a1fb..675bc205b7 100644 --- a/webroot/rsrc/css/application/ponder/ponder-view.css +++ b/webroot/rsrc/css/application/ponder/ponder-view.css @@ -44,10 +44,17 @@ margin-top: 16px; } +/* Indent comments so that are more related to the answer */ .device-desktop .ponder-answer-view .phui-timeline-view { margin-left: 32px; } +/* Indent input box so that it's more related to answer's comments */ +.device-desktop .ponder-answer-view +.phui-comment-form-view:not(.remarkup-assist-pinned) { + margin-left: 94px; +} + .ponder-answer-view .phui-header-subheader { display: inline; margin-left: 12px; @@ -70,7 +77,7 @@ } .ponder-answer-view .ponder-answer-content { - background-color: #fff; + background-color: {$page.content}; padding: 16px 16px 0 16px; } diff --git a/webroot/rsrc/css/core/core.css b/webroot/rsrc/css/core/core.css index ccb3d1697f..4a9d3b394b 100644 --- a/webroot/rsrc/css/core/core.css +++ b/webroot/rsrc/css/core/core.css @@ -84,7 +84,7 @@ h2 { } a { - -moz-outline-style: none; + outline-style: none; text-decoration: none; color: {$anchor}; cursor: pointer; diff --git a/webroot/rsrc/css/core/remarkup.css b/webroot/rsrc/css/core/remarkup.css index 38e03290d7..be3de293b9 100644 --- a/webroot/rsrc/css/core/remarkup.css +++ b/webroot/rsrc/css/core/remarkup.css @@ -66,6 +66,7 @@ line-height: 0.6rem; border-radius: 3px; box-shadow: inset 0 -1px 0 rgba({$alphablue},0.08); + -webkit-user-select: none; user-select: none; background: {$lightgreybackground}; border: 1px solid {$lightgreyborder}; @@ -173,29 +174,29 @@ list-style-type: none; } +.phabricator-remarkup .remarkup-header { + line-height: 1.4; + margin: 1em 0 0.75em; +} + .phabricator-remarkup h2.remarkup-header { font-size: 28px; - margin: 1em 0 0.75em; } .phabricator-remarkup h3.remarkup-header { font-size: 24px; - margin: 1em 0 0.75em; } .phabricator-remarkup h4.remarkup-header { font-size: 22px; - margin: 1em 0 0.75em; } .phabricator-remarkup h5.remarkup-header { font-size: 18px; - margin: 1em 0 0.75em; } .phabricator-remarkup h6.remarkup-header { font-size: 16px; - margin: 1em 0 0.75em; } .phabricator-remarkup blockquote { @@ -473,6 +474,14 @@ video.phabricator-media { box-shadow: 1px 1px 2px rgba({$alphablack}, 0.20); } +.phabricator-remarkup-embed-image > img { + background: url('/rsrc/image/checker_light.png'); +} + +.device-desktop .phabricator-remarkup-embed-image > img:hover { + background: url('/rsrc/image/checker_dark.png'); +} + .phabricator-remarkup-embed-image-full, .phabricator-remarkup-embed-image-wide { display: inline-block; diff --git a/webroot/rsrc/css/layout/phabricator-source-code-view.css b/webroot/rsrc/css/layout/phabricator-source-code-view.css index 9b61425d63..83cda98df4 100644 --- a/webroot/rsrc/css/layout/phabricator-source-code-view.css +++ b/webroot/rsrc/css/layout/phabricator-source-code-view.css @@ -16,7 +16,7 @@ white-space: pre-wrap; padding: 2px 8px 1px; width: 100%; - background: #ffffff; + background: {$diff.background}; } .phabricator-source-line { @@ -25,6 +25,8 @@ border-right: 1px solid {$paste.border}; color: {$sh-yellowtext}; white-space: nowrap; + -webkit-user-select: none; + user-select: none; } .phabricator-source-line > a::before { @@ -74,10 +76,7 @@ th.phabricator-source-line a:hover { .phabricator-source-blame-skip, .phabricator-source-blame-info { - -moz-user-select: -moz-none; - -khtml-user-select: none; -webkit-user-select: none; - -ms-user-select: none; user-select: none; } diff --git a/webroot/rsrc/css/phui/button/phui-button.css b/webroot/rsrc/css/phui/button/phui-button.css index 22ad96779d..b84b4895f7 100644 --- a/webroot/rsrc/css/phui/button/phui-button.css +++ b/webroot/rsrc/css/phui/button/phui-button.css @@ -9,8 +9,6 @@ input[type="submit"] { font: {$basefont}; -webkit-font-smoothing: antialiased; -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; user-select: none; } diff --git a/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css b/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css index e64989f8cd..0ae87aafcf 100644 --- a/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css +++ b/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css @@ -675,8 +675,8 @@ ul.phui-oi-list-view .phui-oi-selected .phui-oi-selectable { cursor: pointer; - user-select: none; -webkit-user-select: none; + user-select: none; } /* When the list selection state can be toggled on the client (as in the bulk diff --git a/webroot/rsrc/css/phui/object-item/phui-oi-simple-ui.css b/webroot/rsrc/css/phui/object-item/phui-oi-simple-ui.css index e838f1ce3e..fcf2e7ad1b 100644 --- a/webroot/rsrc/css/phui/object-item/phui-oi-simple-ui.css +++ b/webroot/rsrc/css/phui/object-item/phui-oi-simple-ui.css @@ -4,7 +4,7 @@ */ .phui-oi-list-simple .phui-oi-with-image .phui-oi-frame { - min-height: 26px; + min-height: 36px; } .phui-oi-list-simple .phui-oi-image { diff --git a/webroot/rsrc/css/phui/phui-basic-nav-view.css b/webroot/rsrc/css/phui/phui-basic-nav-view.css index a545ad35ce..f718c673ab 100644 --- a/webroot/rsrc/css/phui/phui-basic-nav-view.css +++ b/webroot/rsrc/css/phui/phui-basic-nav-view.css @@ -23,7 +23,7 @@ overflow: hidden; } -.phabricator-home.device-phone .phabricator-nav-content { +.phabricator-home.device-phone .phui-navigation-shell .phabricator-nav-local { display: none; } diff --git a/webroot/rsrc/css/phui/phui-comment-form.css b/webroot/rsrc/css/phui/phui-comment-form.css index dcf45edb76..4ace4759c3 100644 --- a/webroot/rsrc/css/phui/phui-comment-form.css +++ b/webroot/rsrc/css/phui/phui-comment-form.css @@ -155,9 +155,12 @@ body.device .phui-box.phui-object-box.phui-comment-form-view { .phui-comment-form-view .phui-form-view label.aphront-form-label, .phui-comment-form-view .phui-form-view .aphront-form-error { - height: 28x; + height: 28px; line-height: 28px; padding: 0; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; } .phui-comment-form-view .aphront-form-error .phui-icon-view { diff --git a/webroot/rsrc/css/phui/phui-form-view.css b/webroot/rsrc/css/phui/phui-form-view.css index accce86819..0c5331551e 100644 --- a/webroot/rsrc/css/phui/phui-form-view.css +++ b/webroot/rsrc/css/phui/phui-form-view.css @@ -63,6 +63,11 @@ color:{$greytext} !important; } +.aphront-form-input > input[type="file"] { + max-width: stretch; + max-width: -moz-available; + max-width: -webkit-fill-available; +} .aphront-form-error { width: 18%; @@ -525,7 +530,7 @@ properly, and submit values. */ } .aphront-form-preview-hidden { - opacity: 0.5; + display: none; } .aphront-form-error .phui-icon-view { diff --git a/webroot/rsrc/css/phui/phui-hovercard.css b/webroot/rsrc/css/phui/phui-hovercard.css index 876b0b6e53..43c6f66df9 100644 --- a/webroot/rsrc/css/phui/phui-hovercard.css +++ b/webroot/rsrc/css/phui/phui-hovercard.css @@ -113,5 +113,26 @@ } .hovercard-task-view .phui-oi-disabled.phui-workcard { - background-color: #fff; + background-color: {$page.content}; +} + +.phui-hovercard-object-type { + font-size: 12px; +} + +.phui-hovercard-object-type .phui-icon-view { + margin-right: 6px; +} + +.phui-hovercard-object-type .phui-crumb-divider { + margin: 0px 6px; +} + +.phui-hovercard-object-type .phabricator-handle-tag-list { + margin-top: 10px; +} + +.phui-hovercard-object-type .phabricator-handle-tag-list-item { + display: inline-block; + margin: 0 4px 2px 0; } diff --git a/webroot/rsrc/css/phui/phui-icon-set-selector.css b/webroot/rsrc/css/phui/phui-icon-set-selector.css index c66c2a6e55..f3bc1b9685 100644 --- a/webroot/rsrc/css/phui/phui-icon-set-selector.css +++ b/webroot/rsrc/css/phui/phui-icon-set-selector.css @@ -3,8 +3,8 @@ */ button.icon-button { - background-color: #F7F7F9; - background-image: linear-gradient(to bottom, #ffffff, #f1f0f1); + background-image: linear-gradient(to bottom, + {$lightgreybackground}, {$greybackground}); border: 1px solid rgba({$alphablue},.2); color: {$darkgreytext}; position: relative; diff --git a/webroot/rsrc/css/phui/phui-icon.css b/webroot/rsrc/css/phui/phui-icon.css index 5436bb04b1..21097b9232 100644 --- a/webroot/rsrc/css/phui/phui-icon.css +++ b/webroot/rsrc/css/phui/phui-icon.css @@ -41,8 +41,8 @@ a.phui-icon-view:hover { img.phui-image-disabled { opacity: .8; - -webkit-filter: grayscale(100%); - filter: grayscale(100%); + -webkit-filter: blur(4px) grayscale(100%) sepia(25%); + filter: blur(4px) grayscale(100%) sepia(25%); } .phui-icon-view.bluetext { diff --git a/webroot/rsrc/css/phui/phui-list.css b/webroot/rsrc/css/phui/phui-list.css index dee105f25b..0289c58fff 100644 --- a/webroot/rsrc/css/phui/phui-list.css +++ b/webroot/rsrc/css/phui/phui-list.css @@ -287,6 +287,10 @@ border-bottom: 1px solid {$thinblueborder}; } +.dashboard-panel-disabled { + color: {$lightgreytext}; +} + /* - Info Stack ------------------------------------------------------------ */ .phui-info-view + .phui-list-view { diff --git a/webroot/rsrc/css/phui/phui-property-list-view.css b/webroot/rsrc/css/phui/phui-property-list-view.css index 4d2cbc9b50..7a5512e6f2 100644 --- a/webroot/rsrc/css/phui/phui-property-list-view.css +++ b/webroot/rsrc/css/phui/phui-property-list-view.css @@ -203,7 +203,6 @@ div.phui-property-list-stacked .phui-property-list-properties border-bottom: 1px solid {$blueborder}; } - .document-engine-image img { margin: 20px auto; background: url('/rsrc/image/checker_light.png'); diff --git a/webroot/rsrc/css/phui/phui-timeline-view.css b/webroot/rsrc/css/phui/phui-timeline-view.css index e08373d589..b9d95ccb9e 100644 --- a/webroot/rsrc/css/phui/phui-timeline-view.css +++ b/webroot/rsrc/css/phui/phui-timeline-view.css @@ -189,9 +189,30 @@ overflow-x: auto; } +/* + * Start Customization for removed comments + * https://we.phorge.it/T15192 + */ + .phui-timeline-core-content .comment-deleted { font-style: italic; } +.phui-timeline-shell-removed .phui-timeline-image, +.phui-timeline-shell-removed .phui-timeline-badges { + opacity: 0.5; +} +.phui-timeline-shell-removed, +.phui-timeline-shell-removed a, +.phui-timeline-shell-removed .phui-timeline-title { + color:#888; /* grey */ +} +.phui-timeline-shell-removed + .phui-timeline-major-event + .phui-timeline-content .phui-timeline-core-content { + padding:4px 16px; /* reduce vertical space from 16px */ +} + +/* End Customization for removed comments */ .device .phui-timeline-event-view { min-height: 23px; diff --git a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css index 9973d64979..5aae48647a 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css +++ b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css @@ -27,7 +27,7 @@ } .phui-workboard-color .phuix-dropdown-menu { - background-color: rgba({$alphawhite},.9); + background-color: rgba({$alphawhite},.95); } .phui-workboard-color .phui-workpanel-view .phui-box-grey { diff --git a/webroot/rsrc/css/phui/workboards/phui-workcard.css b/webroot/rsrc/css/phui/workboards/phui-workcard.css index d9acef12ef..eea7d7b043 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workcard.css +++ b/webroot/rsrc/css/phui/workboards/phui-workcard.css @@ -28,9 +28,6 @@ .phui-workcard.phui-oi .phui-oi-objname { -webkit-touch-callout: text; -webkit-user-select: text; - -khtml-user-select: text; - -moz-user-select: text; - -ms-user-select: text; user-select: text; } diff --git a/webroot/rsrc/css/phui/workboards/phui-workpanel.css b/webroot/rsrc/css/phui/workboards/phui-workpanel.css index 5ee54f2deb..e1f6d3296b 100644 --- a/webroot/rsrc/css/phui/workboards/phui-workpanel.css +++ b/webroot/rsrc/css/phui/workboards/phui-workpanel.css @@ -29,9 +29,6 @@ .phui-workboard-view { -webkit-touch-callout: none; -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; user-select: none; } diff --git a/webroot/rsrc/css/sprite-login.css b/webroot/rsrc/css/sprite-login.css index ec4e082031..5938ecdf81 100644 --- a/webroot/rsrc/css/sprite-login.css +++ b/webroot/rsrc/css/sprite-login.css @@ -56,22 +56,26 @@ only screen and (min-resolution: 1.5dppx) { } .login-Jira { - background-position: 0px -58px; + background-position: -116px -29px; } .login-LDAP { - background-position: -29px -58px; + background-position: 0px -58px; } .login-MediaWiki { - background-position: -58px -58px; + background-position: -29px -58px; } .login-PayPal { - background-position: -87px -58px; + background-position: -58px -58px; } .login-Phabricator { + background-position: -87px -58px; +} + +.login-Phorge { background-position: 0px -87px; } diff --git a/webroot/rsrc/css/syntax/syntax-default.css b/webroot/rsrc/css/syntax/syntax-default.css index 687698b1e6..877edac6f8 100644 --- a/webroot/rsrc/css/syntax/syntax-default.css +++ b/webroot/rsrc/css/syntax/syntax-default.css @@ -3,148 +3,243 @@ * @generated */ -.remarkup-code .bp, -.remarkup-code .cp, -.remarkup-code .kp, -.remarkup-code .kr, -.remarkup-code .nb { - color: #304a96 +.remarkup-code .bp { + color: {$syntax.builtin-pseudo} +} + +.remarkup-code .c { + color: {$syntax.comment} +} + +.remarkup-code .c1 { + color: {$syntax.comment-single} } -.remarkup-code .c, -.remarkup-code .c1, .remarkup-code .cm, +.remarkup-code .dc { + color: {$syntax.comment-multiline} +} + +.remarkup-code .cp { + color: {$syntax.comment-preproc} +} + .remarkup-code .cs { - color: #74777d + color: {$syntax.comment-special} } .remarkup-code .gd { - color: #a00000 + color: {$syntax.generic-deleted} } -.remarkup-code .gh, -.remarkup-code .gp { - color: #000080 +.remarkup-code .gh { + color: {$syntax.generic-heading} } .remarkup-code .gi { - color: #00a000 + color: {$syntax.generic-inserted} } .remarkup-code .go { - color: #808080 + color: {$syntax.generic-output} +} + +.remarkup-code .gp { + color: {$syntax.generic-prompt} } .remarkup-code .gr { - color: #ff0000 + color: {$syntax.generic-red} } .remarkup-code .gt { - color: #0040d0 + color: {$syntax.generic-traceback} } .remarkup-code .gu { - color: #800080 + color: {$syntax.generic-underline} } .remarkup-code .hll { - color: #ffffcc + color: {$syntax.highlighted-line} } -.remarkup-code .il, -.remarkup-code .m, -.remarkup-code .mf, -.remarkup-code .mh, -.remarkup-code .mi, -.remarkup-code .mo { - color: #601200 +.remarkup-code .il { + color: {$syntax.literal-number-integer-long} +} + +.remarkup-code .k { + color: {$syntax.keyword} +} + +.remarkup-code .kc { + color: {$syntax.keyword-constant} +} + +.remarkup-code .kd { + color: {$syntax.keyword-declaration} +} + +.remarkup-code .kn { + color: {$syntax.keyword-namespace} +} + +.remarkup-code .kp { + color: {$syntax.keyword-preproc} +} + +.remarkup-code .kr { + color: {$syntax.keyword-reserved} } -.remarkup-code .k, -.remarkup-code .kd, -.remarkup-code .kn, .remarkup-code .kt { - color: #aa4000 + color: {$syntax.keyword-type} } -.remarkup-code .kc, -.remarkup-code .no { - color: #000a65 +.remarkup-code .m { + color: {$syntax.literal-number} +} + +.remarkup-code .mf { + color: {$syntax.literal-number-float} +} + +.remarkup-code .mh { + color: {$syntax.literal-number-hex} +} + +.remarkup-code .mi { + color: {$syntax.literal-number-integer} +} + +.remarkup-code .mo { + color: {$syntax.literal-number-octal} } .remarkup-code .na { - color: #354bb3 + color: {$syntax.name-attribute} } -.remarkup-code .nc, -.remarkup-code .nt, -.remarkup-code .vc { - color: #00702a +.remarkup-code .nb { + color: {$syntax.name-builtin} } -.remarkup-code .nd, -.remarkup-code .ow { - color: #aa22ff +.remarkup-code .nc { + color: {$syntax.name-class} +} + +.remarkup-code .nd { + color: {$syntax.name-decorator} } .remarkup-code .ne { - color: #d2413a + color: {$syntax.name-entity} } -.remarkup-code .nf, -.remarkup-code .nx { - color: #004012 +.remarkup-code .nf { + color: {$syntax.name-function} } .remarkup-code .ni { - color: #92969d + color: {$syntax.name-identifier} } .remarkup-code .nl { - color: #a0a000 + color: {$syntax.name-label} } .remarkup-code .nn { - color: #0000ff + color: {$syntax.name-namespace} } -.remarkup-code .nv, -.remarkup-code .vg, -.remarkup-code .vi { - color: #001294 +.remarkup-code .no { + color: {$syntax.name-operator} } -.remarkup-code .o, -.remarkup-code .ss { - color: #aa2211 +.remarkup-code .nt { + color: {$syntax.name-tag} } -.remarkup-code .s, -.remarkup-code .s1, -.remarkup-code .s2, -.remarkup-code .sb, -.remarkup-code .sc, -.remarkup-code .sx { - color: #766510 +.remarkup-code .nv { + color: {$syntax.name-variable} } -.remarkup-code .sd, -.remarkup-code .sh { - color: #000000 +.remarkup-code .nx { + color: {$syntax.name-exception} +} + +.remarkup-code .o { + color: {$syntax.operator} +} + +.remarkup-code .ow { + color: {$syntax.operator-word} +} + +.remarkup-code .p { + color: {$syntax.punctuation} +} + +.remarkup-code .s { + color: {$syntax.string} +} + +.remarkup-code .s1 { + color: {$syntax.string-single} +} + +.remarkup-code .s2 { + color: {$syntax.string-double} +} + +.remarkup-code .sb { + color: {$syntax.string-backtick} +} + +.remarkup-code .sc { + color: {$syntax.literal-string-char} +} + +.remarkup-code .sd { + color: {$syntax.string-doc} } .remarkup-code .se { - color: #bb6622 + color: {$syntax.literal-string-escape} +} + +.remarkup-code .sh { + color: {$syntax.string-heredoc} } .remarkup-code .si { - color: #bb66bb + color: {$syntax.literal-string-interpol} } .remarkup-code .sr { - color: #bb6688 + color: {$syntax.string-regex} +} + +.remarkup-code .ss { + color: {$syntax.literal-string-symbol} +} + +.remarkup-code .sx { + color: {$syntax.string-other} +} + +.remarkup-code .vc { + color: {$syntax.name-variable-class} +} + +.remarkup-code .vg { + color: {$syntax.variable-global} +} + +.remarkup-code .vi { + color: {$syntax.variable-instance} } .remarkup-code .w { - color: #bbbbbb + color: {$syntax.text-whitespace} } diff --git a/webroot/rsrc/externals/javelin/docs/javelin.book b/webroot/rsrc/externals/javelin/docs/javelin.book new file mode 100644 index 0000000000..7c2a0c2274 --- /dev/null +++ b/webroot/rsrc/externals/javelin/docs/javelin.book @@ -0,0 +1,16 @@ +{ + "name" : "javelin", + "title" : "Javelin Documentation", + "short" : "Javelin Docs", + "preface" : "Documentation for developers using Javelin.", + "uri.source": + "https://we.phorge.it/diffusion/P/browse/master/%f$%l", + "rules": { + "(\\.diviner$)": "DivinerArticleAtomizer" + }, + "groups": { + "concepts": { + "name": "Concepts" + } + } +} diff --git a/webroot/rsrc/externals/javelin/lib/Workflow.js b/webroot/rsrc/externals/javelin/lib/Workflow.js index 25de547deb..6d91682dbc 100644 --- a/webroot/rsrc/externals/javelin/lib/Workflow.js +++ b/webroot/rsrc/externals/javelin/lib/Workflow.js @@ -403,6 +403,20 @@ JX.install('Workflow', { JX.$E('Response to workflow request went unhandled.'); } } + + // Only when the response is a Dialog, check if the user + // is quitting with pending changes + if (this._root) { + var form = JX.DOM.scry(this._root, 'form', 'jx-dialog'); + if (form.length) { + JX.DOM.listen(form[0], 'keydown', null, function(e) { + if (e.getSpecialKey()) { + return; + } + JX.Stratcom.addSigil(form[0], 'dialog-keydown'); + }); + } + } }, _push : function() { if (!this._pushed) { @@ -536,6 +550,22 @@ JX.install('Workflow', { return; } + // Only when the response is a Dialog, check if the user + // is quitting with pending changes + if (active._root) { + var form = JX.DOM.scry(active._root, 'form', 'jx-dialog'); + var confirmMsg = + 'Form data may have changed. ' + + 'Are you sure you want to close this dialog?'; + if ( + form.length && + JX.Stratcom.hasSigil(form[0], 'dialog-keydown') && + !window.confirm(confirmMsg) + ) { + return; + } + } + JX.Workflow._pop(); e.prevent(); } diff --git a/webroot/rsrc/externals/javelin/lib/control/typeahead/Typeahead.js b/webroot/rsrc/externals/javelin/lib/control/typeahead/Typeahead.js index 5875bf9ea6..e971ae1495 100644 --- a/webroot/rsrc/externals/javelin/lib/control/typeahead/Typeahead.js +++ b/webroot/rsrc/externals/javelin/lib/control/typeahead/Typeahead.js @@ -84,8 +84,19 @@ JX.install('Typeahead', { 'mousedown', 'tag:a', JX.bind(this, function(e) { - if (!e.isRightButton()) { + if (e.isNormalMouseEvent()) { this._choose(e.getNode('tag:a')); + } else { + // fix the middle-click and any non-normal mouse event + // in order to have an "open in a new tab" that just works natively + // or any other browser action that is supposed to be there. + // + // Probably this is one of the specific cases where kill() has + // sense instead of just stop(), since there are not much chances + // that another event listener had anything else to do + // during non-normal mousedown/click events. + // https://we.phorge.it/T15149 + e.kill(); } })); diff --git a/webroot/rsrc/image/logo/light-eye.png b/webroot/rsrc/image/logo/light-eye.png deleted file mode 100644 index 772dcb0c01..0000000000 Binary files a/webroot/rsrc/image/logo/light-eye.png and /dev/null differ diff --git a/webroot/rsrc/image/logo/project-logo.png b/webroot/rsrc/image/logo/project-logo.png new file mode 100644 index 0000000000..85d0ddad1e Binary files /dev/null and b/webroot/rsrc/image/logo/project-logo.png differ diff --git a/webroot/rsrc/image/sprite-login-X2.png b/webroot/rsrc/image/sprite-login-X2.png index 2d3c118745..4a9daf8247 100644 Binary files a/webroot/rsrc/image/sprite-login-X2.png and b/webroot/rsrc/image/sprite-login-X2.png differ diff --git a/webroot/rsrc/image/sprite-login.png b/webroot/rsrc/image/sprite-login.png index 2f79e7ad8a..49b507b15c 100644 Binary files a/webroot/rsrc/image/sprite-login.png and b/webroot/rsrc/image/sprite-login.png differ diff --git a/webroot/rsrc/js/application/diffusion/DiffusionLocateFileSource.js b/webroot/rsrc/js/application/diffusion/DiffusionLocateFileSource.js index 1f94f349d3..3a3bcb9bce 100644 --- a/webroot/rsrc/js/application/diffusion/DiffusionLocateFileSource.js +++ b/webroot/rsrc/js/application/diffusion/DiffusionLocateFileSource.js @@ -99,6 +99,10 @@ JX.install('DiffusionLocateFileSource', { return []; } + // Be nice with terminal users that may have "./" or "/" prefixes. + // Otherwise, not a single result is returned. + search = search.replace(/^\.?\//, ''); + // We know that the results for "abc" are always a subset of the results // for "a" and "ab" -- and there's a good chance we already computed // those result sets. Find the longest cached result which is a prefix diff --git a/webroot/rsrc/js/application/diffusion/behavior-locate-file.js b/webroot/rsrc/js/application/diffusion/behavior-locate-file.js index c8817428e6..c44a1c7922 100644 --- a/webroot/rsrc/js/application/diffusion/behavior-locate-file.js +++ b/webroot/rsrc/js/application/diffusion/behavior-locate-file.js @@ -17,7 +17,11 @@ JX.behavior('diffusion-locate-file', function(config) { typeahead.setDatasource(datasource); typeahead.listen('choose', function(r) { - JX.$U(config.browseBaseURI + r.ref).go(); + var browseURI = config.browseBaseURI + r.ref; + if (config.symbolicCommit) { + browseURI += ';' + config.symbolicCommit; + } + JX.$U(browseURI).go(); }); var started = false; diff --git a/webroot/rsrc/js/application/projects/WorkboardController.js b/webroot/rsrc/js/application/projects/WorkboardController.js index da5d177bb9..1b38638f3d 100644 --- a/webroot/rsrc/js/application/projects/WorkboardController.js +++ b/webroot/rsrc/js/application/projects/WorkboardController.js @@ -151,6 +151,13 @@ JX.install('WorkboardController', { }, _onaddcard: function(e) { + + // Allow CTRL+click and maybe other actions + if(!e.isNormalMouseEvent()) { + e.stop(); + return; + } + // We want the 'boards-dropdown-menu' behavior to see this event and // close the dropdown, but don't want to follow the link. e.prevent(); @@ -176,6 +183,13 @@ JX.install('WorkboardController', { }, _oneditcard: function(e) { + + // Allow CTRL+click and maybe other actions + if(!e.isNormalMouseEvent()) { + e.stop(); + return; + } + e.kill(); var column_node = e.getNode('project-column'); diff --git a/webroot/rsrc/js/core/FileUpload.js b/webroot/rsrc/js/core/FileUpload.js index 22ba33f97a..ef5089a56e 100644 --- a/webroot/rsrc/js/core/FileUpload.js +++ b/webroot/rsrc/js/core/FileUpload.js @@ -38,6 +38,20 @@ JX.install('PhabricatorFileUpload', { return this; }, + /** + * Get the File Monogram - like 'F123' + */ + getMonogram: function() { + return 'F' + this.getID(); + }, + + /** + * Get the File page URI - like '/F123' + */ + getPageURI: function() { + return '/' + this.getMonogram(); + }, + setChunks: function(chunks) { var chunk; for (var ii = 0; ii < chunks.length; ii++) { @@ -101,7 +115,19 @@ JX.install('PhabricatorFileUpload', { switch (this.getStatus()) { case 'done': - var link = JX.$N('a', {href: this.getURI()}, 'F' + this.getID()); + + // In this case the File upload was successful + + var linkAttr = {}; + linkAttr.href = this.getPageURI(); + + // External links are evil as default. + // Believe it or not, but some Phorge users brainstormed + // for one hour for this specific target="_blank". + // https://we.phorge.it/T15172 + linkAttr.target = '_blank'; + + var link = JX.$N('a', linkAttr, this.getMonogram()); content = [ JX.$N('strong', {}, ['Upload Complete (', link, ')']), diff --git a/webroot/rsrc/js/core/RemarkupMetadata.js b/webroot/rsrc/js/core/RemarkupMetadata.js new file mode 100644 index 0000000000..14469b25f5 --- /dev/null +++ b/webroot/rsrc/js/core/RemarkupMetadata.js @@ -0,0 +1,49 @@ +/** + * @requires javelin-install + * javelin-dom + * javelin-json + * @provides phabricator-remarkup-metadata + * @javelin + */ + +JX.install('RemarkupMetadata', { + + construct: function(metadataValue, metadataID) { + if (JX.RemarkupMetadata._metadataValue == null) { + JX.RemarkupMetadata._metadataValue = {}; + } + if (!JX.RemarkupMetadata._metadataValue.hasOwnProperty(metadataID)) { + JX.RemarkupMetadata._metadataValue[metadataID] = metadataValue; + } + this._metadataNode = JX.$(metadataID); + this._metadataID = metadataID; + }, + + statics: { + _metadataValue: null + }, + + members: { + _metadataNode: null, + _metadataID: null, + + _writeMetadata: function() { + this._metadataNode.value = JX.JSON.stringify( + JX.RemarkupMetadata._metadataValue[this._metadataID]); + }, + + getMetadata: function(key, default_value) { + if (JX.RemarkupMetadata._metadataValue[this._metadataID] + .hasOwnProperty(key)) { + return JX.RemarkupMetadata._metadataValue[this._metadataID][key]; + } + return default_value; + }, + + setMetadata: function(key, value) { + JX.RemarkupMetadata._metadataValue[this._metadataID][key] = value; + this._writeMetadata(); + } + } + +}); diff --git a/webroot/rsrc/js/core/behavior-copy.js b/webroot/rsrc/js/core/behavior-copy.js index 4457f6e022..210d9ebc56 100644 --- a/webroot/rsrc/js/core/behavior-copy.js +++ b/webroot/rsrc/js/core/behavior-copy.js @@ -3,41 +3,81 @@ * @requires javelin-behavior * javelin-dom * javelin-stratcom + * phabricator-notification * @javelin */ JX.behavior('phabricator-clipboard-copy', function() { - if (!document.queryCommandSupported) { - return; - } + var fallback_working = document.queryCommandSupported && + document.queryCommandSupported('copy'); - if (!document.queryCommandSupported('copy')) { + if (!navigator.clipboard && !fallback_working) { return; } JX.DOM.alterClass(document.body, 'supports-clipboard', true); - JX.Stratcom.listen('click', 'clipboard-copy', function(e) { - e.kill(); - - var data = e.getNodeData('clipboard-copy'); + var copy_fallback = function(text) { var attr = { - value: data.text || '', + value: text || '', className: 'clipboard-buffer' }; var node = JX.$N('textarea', attr); document.body.appendChild(node); - try { - node.select(); - document.execCommand('copy'); - } catch (ignored) { - // Ignore any errors we hit. + node.select(); + document.execCommand('copy'); + + JX.DOM.remove(node); + }; + + var show_success_message = function(message) { + if (!message) { + return; } + new JX.Notification() + .setContent(message) + .alterClassName('jx-notification-done', true) + .setDuration(8000) + .show(); + }; - JX.DOM.remove(node); + var show_error_message = function(message) { + if (!message) { + return; + } + new JX.Notification() + .setContent(message) + .alterClassName('jx-notification-error', true) + .setDuration(8000) + .show(); + }; + + JX.Stratcom.listen('click', 'clipboard-copy', function(e) { + var data = e.getNodeData('clipboard-copy'); + var text = data.text || ''; + + var copy = async function( // jshint ignore:line + text, + successMessage, + errorMessage + ) { + try { + if (navigator.clipboard) { + await navigator.clipboard.writeText(text); + } else { + copy_fallback(text); + } + show_success_message(successMessage); + } catch (ex) { + show_error_message(errorMessage); + } + }; + + e.kill(); + copy(text, data.successMessage, data.errorMessage); }); }); diff --git a/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js b/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js index 8cf14349b0..4d8629cd8c 100644 --- a/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js +++ b/webroot/rsrc/js/core/behavior-drag-and-drop-textarea.js @@ -2,36 +2,15 @@ * @provides javelin-behavior-aphront-drag-and-drop-textarea * @requires javelin-behavior * javelin-dom - * javelin-json * phabricator-drag-and-drop-file-upload * phabricator-textareautils + * phabricator-remarkup-metadata */ JX.behavior('aphront-drag-and-drop-textarea', function(config) { var target = JX.$(config.target); - var metadata_node = JX.$(config.remarkupMetadataID); - var metadata_value = config.remarkupMetadataValue; - - function set_metadata(key, value) { - metadata_value[key] = value; - write_metadata(); - } - - function get_metadata(key, default_value) { - if (metadata_value.hasOwnProperty(key)) { - return metadata_value[key]; - } - return default_value; - } - - function write_metadata() { - metadata_node.value = JX.JSON.stringify(metadata_value); - } - - write_metadata(); - if (JX.PhabricatorDragAndDropFileUpload.isSupported()) { var drop = new JX.PhabricatorDragAndDropFileUpload(target) .setURI(config.uri) @@ -48,9 +27,15 @@ JX.behavior('aphront-drag-and-drop-textarea', function(config) { drop.listen('didUpload', function(file) { JX.TextAreaUtils.insertFileReference(target, file); - var phids = get_metadata('attachedFilePHIDs', []); - phids.push(file.getPHID()); - set_metadata('attachedFilePHIDs', phids); + if(config.remarkupMetadataID) { + // Try to auto-attach files by default + // https://we.phorge.it/T15106 + var metadata = new JX.RemarkupMetadata(config.remarkupMetadataValue, + config.remarkupMetadataID); + var phids = metadata.getMetadata('attachedFilePHIDs', []); + phids.push(file.getPHID()); + metadata.setMetadata('attachedFilePHIDs', phids); + } }); drop.start(); diff --git a/webroot/rsrc/js/core/behavior-fancy-datepicker.js b/webroot/rsrc/js/core/behavior-fancy-datepicker.js index 8059e214fe..634125e2e6 100644 --- a/webroot/rsrc/js/core/behavior-fancy-datepicker.js +++ b/webroot/rsrc/js/core/behavior-fancy-datepicker.js @@ -13,6 +13,7 @@ JX.behavior('fancy-datepicker', function(config, statics) { } statics.initialized = true; + var pht = JX.phtize(config.pht); var picker; var anchor_node; var root; @@ -262,18 +263,18 @@ JX.behavior('fancy-datepicker', function(config, statics) { var year = valid_date.getYear() + 1900; var months = [ - 'January', - 'February', - 'March', - 'April', - 'May', - 'June', - 'July', - 'August', - 'September', - 'October', - 'November', - 'December']; + pht('January'), + pht('February'), + pht('March'), + pht('April'), + pht('May'), + pht('June'), + pht('July'), + pht('August'), + pht('September'), + pht('October'), + pht('November'), + pht('December')]; var buttons = [ cell('\u25C0', 'm:-1', false, 'lrbutton'), @@ -330,14 +331,14 @@ JX.behavior('fancy-datepicker', function(config, statics) { var weeks = []; // First, render the weekday names. - var weekdays = 'SMTWTFS'; + var weekdays = pht('S|M|T|W|T|F|S').split('|'); var weekday_names = []; var week_start = parseInt(get_week_start(), 10); var week_end = weekdays.length + week_start; for (var ii = week_start; ii < week_end; ii++) { var index = ii%7; - weekday_names.push(cell(weekdays.charAt(index), null, false, 'day-name')); + weekday_names.push(cell(weekdays[index], null, false, 'day-name')); } weeks.push(JX.$N('tr', {}, weekday_names)); diff --git a/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js b/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js index 89d23ef60f..b93075c36e 100644 --- a/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js +++ b/webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js @@ -5,6 +5,7 @@ * javelin-dom * phabricator-phtize * phabricator-textareautils + * phabricator-remarkup-metadata * javelin-workflow * javelin-vector * phuix-autocomplete @@ -255,6 +256,12 @@ JX.behavior('phabricator-remarkup-assist', function(config) { .setURI(file.uri); JX.TextAreaUtils.insertFileReference(area, upload); + + var metadata = new JX.RemarkupMetadata( + config.remarkupMetadataValue, config.remarkupMetadataID); + var phids = metadata.getMetadata('attachedFilePHIDs', []); + phids.push(file.phid); + metadata.setMetadata('attachedFilePHIDs', phids); } }) .start(); diff --git a/webroot/rsrc/js/core/behavior-select-content.js b/webroot/rsrc/js/core/behavior-select-content.js index b54b6308cf..92e4dcfc0b 100644 --- a/webroot/rsrc/js/core/behavior-select-content.js +++ b/webroot/rsrc/js/core/behavior-select-content.js @@ -16,8 +16,16 @@ JX.behavior('select-content', function() { var node = e.getNode('select-content'); var data = JX.Stratcom.getData(node); + if (data.once && data.selected) { + return; + } + var target = JX.$(data.selectID); JX.DOM.focus(target); target.select(); + + if (data.once) { + JX.Stratcom.addData(node, {selected: true}); + } }); });