diff --git a/resources/celerity/map.php b/resources/celerity/map.php index e52ef012ab..c7ac86ab3c 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -12,8 +12,8 @@ return array( 'core.pkg.css' => '0ae696de', 'core.pkg.js' => 'ab3502fe', 'dark-console.pkg.js' => '187792c2', - 'differential.pkg.css' => '5c459f92', - 'differential.pkg.js' => '5080baf4', + 'differential.pkg.css' => 'ffb69e3d', + 'differential.pkg.js' => '5986f349', 'diffusion.pkg.css' => '42c75c37', 'diffusion.pkg.js' => '78c9885d', 'maniphest.pkg.css' => '35995d6d', @@ -67,7 +67,7 @@ return array( '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' => '8aa3eac5', + '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', @@ -437,7 +437,7 @@ return array( 'rsrc/js/application/releeph/releeph-preview-branch.js' => '75184d68', 'rsrc/js/application/releeph/releeph-request-state-change.js' => '9f081f05', 'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'aa3a100c', - 'rsrc/js/application/repository/repository-crossreference.js' => '6337cf26', + 'rsrc/js/application/repository/repository-crossreference.js' => '44d48cd1', 'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e5bdb730', 'rsrc/js/application/search/behavior-reorder-queries.js' => 'b86f297f', 'rsrc/js/application/transactions/behavior-comment-actions.js' => '4dffaeb2', @@ -569,7 +569,7 @@ return array( 'differential-core-view-css' => '7300a73e', 'differential-revision-add-comment-css' => '7e5900d9', 'differential-revision-comment-css' => '7dbc8d1d', - 'differential-revision-history-css' => '8aa3eac5', + 'differential-revision-history-css' => '237a2979', 'differential-revision-list-css' => '93d2df7d', 'differential-table-of-contents-css' => 'bba788b9', 'diffusion-css' => 'e46232d6', @@ -693,7 +693,7 @@ return array( 'javelin-behavior-reorder-applications' => 'aa371860', 'javelin-behavior-reorder-columns' => '8ac32fd9', 'javelin-behavior-reorder-profile-menu-items' => 'e5bdb730', - 'javelin-behavior-repository-crossreference' => '6337cf26', + 'javelin-behavior-repository-crossreference' => '44d48cd1', 'javelin-behavior-scrollbar' => '92388bae', 'javelin-behavior-search-reorder-queries' => 'b86f297f', 'javelin-behavior-select-content' => 'e8240b50', @@ -1309,6 +1309,12 @@ return array( '43bc9360' => array( 'javelin-install', ), + '44d48cd1' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + 'javelin-uri', + ), '457f4d16' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1536,12 +1542,6 @@ return array( 'javelin-request', 'javelin-uri', ), - '6337cf26' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'javelin-uri', - ), '65bb0011' => array( 'javelin-behavior', 'javelin-dom', diff --git a/resources/sql/autopatches/20210315.affectedpath.01.epoch.sql b/resources/sql/autopatches/20210315.affectedpath.01.epoch.sql new file mode 100644 index 0000000000..80c337fd94 --- /dev/null +++ b/resources/sql/autopatches/20210315.affectedpath.01.epoch.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_differential.differential_affectedpath + DROP epoch; diff --git a/resources/sql/autopatches/20210315.affectedpath.02.repositoryid.sql b/resources/sql/autopatches/20210315.affectedpath.02.repositoryid.sql new file mode 100644 index 0000000000..1975b7c071 --- /dev/null +++ b/resources/sql/autopatches/20210315.affectedpath.02.repositoryid.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_differential.differential_affectedpath + CHANGE repositoryID repositoryID INT UNSIGNED; diff --git a/resources/sql/autopatches/20210316.almanac.01.device-mailkey.php b/resources/sql/autopatches/20210316.almanac.01.device-mailkey.php new file mode 100644 index 0000000000..7c10d1ae76 --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.01.device-mailkey.php @@ -0,0 +1,28 @@ +establishConnection('w'); + +$properties_table = new PhabricatorMetaMTAMailProperties(); +$conn = $properties_table->establishConnection('w'); + +$iterator = new LiskRawMigrationIterator( + $device_conn, + $device_table->getTableName()); + +foreach ($iterator as $row) { + queryfx( + $conn, + 'INSERT IGNORE INTO %R + (objectPHID, mailProperties, dateCreated, dateModified) + VALUES + (%s, %s, %d, %d)', + $properties_table, + $row['phid'], + phutil_json_encode( + array( + 'mailKey' => $row['mailKey'], + )), + PhabricatorTime::getNow(), + PhabricatorTime::getNow()); +} diff --git a/resources/sql/autopatches/20210316.almanac.02.device-dropmailkey.sql b/resources/sql/autopatches/20210316.almanac.02.device-dropmailkey.sql new file mode 100644 index 0000000000..47079ef296 --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.02.device-dropmailkey.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_almanac.almanac_device + DROP mailKey; diff --git a/resources/sql/autopatches/20210316.almanac.03.device-status.sql b/resources/sql/autopatches/20210316.almanac.03.device-status.sql new file mode 100644 index 0000000000..08618acb8f --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.03.device-status.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_almanac.almanac_device + ADD status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20210316.almanac.04.device-status-value.sql b/resources/sql/autopatches/20210316.almanac.04.device-status-value.sql new file mode 100644 index 0000000000..659bc7850f --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.04.device-status-value.sql @@ -0,0 +1,2 @@ +UPDATE {$NAMESPACE}_almanac.almanac_device + SET status = 'active' WHERE status = ''; diff --git a/resources/sql/autopatches/20210316.almanac.05.service-mailkey.php b/resources/sql/autopatches/20210316.almanac.05.service-mailkey.php new file mode 100644 index 0000000000..33318eb663 --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.05.service-mailkey.php @@ -0,0 +1,28 @@ +establishConnection('w'); + +$properties_table = new PhabricatorMetaMTAMailProperties(); +$conn = $properties_table->establishConnection('w'); + +$iterator = new LiskRawMigrationIterator( + $service_conn, + $service_table->getTableName()); + +foreach ($iterator as $row) { + queryfx( + $conn, + 'INSERT IGNORE INTO %R + (objectPHID, mailProperties, dateCreated, dateModified) + VALUES + (%s, %s, %d, %d)', + $properties_table, + $row['phid'], + phutil_json_encode( + array( + 'mailKey' => $row['mailKey'], + )), + PhabricatorTime::getNow(), + PhabricatorTime::getNow()); +} diff --git a/resources/sql/autopatches/20210316.almanac.06.service-dropmailkey.sql b/resources/sql/autopatches/20210316.almanac.06.service-dropmailkey.sql new file mode 100644 index 0000000000..b9966eddf9 --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.06.service-dropmailkey.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_almanac.almanac_service + DROP mailKey; diff --git a/resources/sql/autopatches/20210316.almanac.07.binding-mailkey.php b/resources/sql/autopatches/20210316.almanac.07.binding-mailkey.php new file mode 100644 index 0000000000..84c1725876 --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.07.binding-mailkey.php @@ -0,0 +1,28 @@ +establishConnection('w'); + +$properties_table = new PhabricatorMetaMTAMailProperties(); +$conn = $properties_table->establishConnection('w'); + +$iterator = new LiskRawMigrationIterator( + $binding_conn, + $binding_table->getTableName()); + +foreach ($iterator as $row) { + queryfx( + $conn, + 'INSERT IGNORE INTO %R + (objectPHID, mailProperties, dateCreated, dateModified) + VALUES + (%s, %s, %d, %d)', + $properties_table, + $row['phid'], + phutil_json_encode( + array( + 'mailKey' => $row['mailKey'], + )), + PhabricatorTime::getNow(), + PhabricatorTime::getNow()); +} diff --git a/resources/sql/autopatches/20210316.almanac.08.binding-dropmailkey.sql b/resources/sql/autopatches/20210316.almanac.08.binding-dropmailkey.sql new file mode 100644 index 0000000000..ebcf31254d --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.08.binding-dropmailkey.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_almanac.almanac_binding + DROP mailKey; diff --git a/resources/sql/autopatches/20210316.almanac.09.namespace-mailkey.php b/resources/sql/autopatches/20210316.almanac.09.namespace-mailkey.php new file mode 100644 index 0000000000..261181324e --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.09.namespace-mailkey.php @@ -0,0 +1,28 @@ +establishConnection('w'); + +$properties_table = new PhabricatorMetaMTAMailProperties(); +$conn = $properties_table->establishConnection('w'); + +$iterator = new LiskRawMigrationIterator( + $namespace_conn, + $namespace_table->getTableName()); + +foreach ($iterator as $row) { + queryfx( + $conn, + 'INSERT IGNORE INTO %R + (objectPHID, mailProperties, dateCreated, dateModified) + VALUES + (%s, %s, %d, %d)', + $properties_table, + $row['phid'], + phutil_json_encode( + array( + 'mailKey' => $row['mailKey'], + )), + PhabricatorTime::getNow(), + PhabricatorTime::getNow()); +} diff --git a/resources/sql/autopatches/20210316.almanac.10.namespace-dropmailkey.sql b/resources/sql/autopatches/20210316.almanac.10.namespace-dropmailkey.sql new file mode 100644 index 0000000000..867c42fbc2 --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.10.namespace-dropmailkey.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_almanac.almanac_namespace + DROP mailKey; diff --git a/resources/sql/autopatches/20210316.almanac.11.network-mailkey.php b/resources/sql/autopatches/20210316.almanac.11.network-mailkey.php new file mode 100644 index 0000000000..1ece4e2353 --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.11.network-mailkey.php @@ -0,0 +1,28 @@ +establishConnection('w'); + +$properties_table = new PhabricatorMetaMTAMailProperties(); +$conn = $properties_table->establishConnection('w'); + +$iterator = new LiskRawMigrationIterator( + $network_conn, + $network_table->getTableName()); + +foreach ($iterator as $row) { + queryfx( + $conn, + 'INSERT IGNORE INTO %R + (objectPHID, mailProperties, dateCreated, dateModified) + VALUES + (%s, %s, %d, %d)', + $properties_table, + $row['phid'], + phutil_json_encode( + array( + 'mailKey' => $row['mailKey'], + )), + PhabricatorTime::getNow(), + PhabricatorTime::getNow()); +} diff --git a/resources/sql/autopatches/20210316.almanac.12.network-dropmailkey.sql b/resources/sql/autopatches/20210316.almanac.12.network-dropmailkey.sql new file mode 100644 index 0000000000..266f1250f0 --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.12.network-dropmailkey.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_almanac.almanac_network + DROP mailKey; diff --git a/resources/sql/autopatches/20210316.almanac.13.event-mailkey.php b/resources/sql/autopatches/20210316.almanac.13.event-mailkey.php new file mode 100644 index 0000000000..7436ce3591 --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.13.event-mailkey.php @@ -0,0 +1,28 @@ +establishConnection('w'); + +$properties_table = new PhabricatorMetaMTAMailProperties(); +$conn = $properties_table->establishConnection('w'); + +$iterator = new LiskRawMigrationIterator( + $event_conn, + $event_table->getTableName()); + +foreach ($iterator as $row) { + queryfx( + $conn, + 'INSERT IGNORE INTO %R + (objectPHID, mailProperties, dateCreated, dateModified) + VALUES + (%s, %s, %d, %d)', + $properties_table, + $row['phid'], + phutil_json_encode( + array( + 'mailKey' => $row['mailKey'], + )), + PhabricatorTime::getNow(), + PhabricatorTime::getNow()); +} diff --git a/resources/sql/autopatches/20210316.almanac.14.event-dropmailkey.sql b/resources/sql/autopatches/20210316.almanac.14.event-dropmailkey.sql new file mode 100644 index 0000000000..8a7e2de519 --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.14.event-dropmailkey.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_calendar.calendar_event + DROP mailKey; diff --git a/resources/sql/autopatches/20210316.almanac.15.intiative-mailkey.php b/resources/sql/autopatches/20210316.almanac.15.intiative-mailkey.php new file mode 100644 index 0000000000..03215ef78f --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.15.intiative-mailkey.php @@ -0,0 +1,28 @@ +establishConnection('w'); + +$properties_table = new PhabricatorMetaMTAMailProperties(); +$conn = $properties_table->establishConnection('w'); + +$iterator = new LiskRawMigrationIterator( + $initiative_conn, + $initiative_table->getTableName()); + +foreach ($iterator as $row) { + queryfx( + $conn, + 'INSERT IGNORE INTO %R + (objectPHID, mailProperties, dateCreated, dateModified) + VALUES + (%s, %s, %d, %d)', + $properties_table, + $row['phid'], + phutil_json_encode( + array( + 'mailKey' => $row['mailKey'], + )), + PhabricatorTime::getNow(), + PhabricatorTime::getNow()); +} diff --git a/resources/sql/autopatches/20210316.almanac.16.initiative-dropmailkey.sql b/resources/sql/autopatches/20210316.almanac.16.initiative-dropmailkey.sql new file mode 100644 index 0000000000..9de5e9c224 --- /dev/null +++ b/resources/sql/autopatches/20210316.almanac.16.initiative-dropmailkey.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_fund.fund_initiative + DROP mailKey; diff --git a/scripts/fpm/warmup.php b/scripts/fpm/warmup.php deleted file mode 100644 index 956a474131..0000000000 --- a/scripts/fpm/warmup.php +++ /dev/null @@ -1,38 +0,0 @@ -selectAndLoadSymbols(); - - define('__WARMUP__', true); -} - -__warmup__(); diff --git a/scripts/install/update_phabricator.sh b/scripts/install/update_phabricator.sh index 5a3950088e..3831acd963 100755 --- a/scripts/install/update_phabricator.sh +++ b/scripts/install/update_phabricator.sh @@ -9,15 +9,12 @@ set -x # to work without modifications. # NOTE: This script assumes you are running it from a directory which contains -# arcanist/, libphutil/, and phabricator/. +# arcanist/ and phabricator/. ROOT=`pwd` # You can hard-code the path here instead. ### UPDATE WORKING COPIES ###################################################### -cd $ROOT/libphutil -git pull - cd $ROOT/arcanist git pull diff --git a/scripts/ssh/ssh-exec.php b/scripts/ssh/ssh-exec.php index 4a46e7562d..c3a85b399c 100755 --- a/scripts/ssh/ssh-exec.php +++ b/scripts/ssh/ssh-exec.php @@ -146,6 +146,14 @@ try { $device_name)); } + if ($device->isDisabled()) { + throw new Exception( + pht( + 'This request has authenticated as a device ("%s"), but this '. + 'device is disabled.', + $device->getName())); + } + // We're authenticated as a device, but we're going to read the user out of // the command below. $is_cluster_request = true; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 3cae40d75e..e811ddbc92 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -62,6 +62,8 @@ phutil_register_library_map(array( 'AlmanacDeviceSearchConduitAPIMethod' => 'applications/almanac/conduit/AlmanacDeviceSearchConduitAPIMethod.php', 'AlmanacDeviceSearchEngine' => 'applications/almanac/query/AlmanacDeviceSearchEngine.php', 'AlmanacDeviceSetPropertyTransaction' => 'applications/almanac/xaction/AlmanacDeviceSetPropertyTransaction.php', + 'AlmanacDeviceStatus' => 'applications/almanac/constants/AlmanacDeviceStatus.php', + 'AlmanacDeviceStatusTransaction' => 'applications/almanac/xaction/AlmanacDeviceStatusTransaction.php', 'AlmanacDeviceTransaction' => 'applications/almanac/storage/AlmanacDeviceTransaction.php', 'AlmanacDeviceTransactionQuery' => 'applications/almanac/query/AlmanacDeviceTransactionQuery.php', 'AlmanacDeviceTransactionType' => 'applications/almanac/xaction/AlmanacDeviceTransactionType.php', @@ -450,6 +452,7 @@ phutil_register_library_map(array( 'DifferentialActionEmailCommand' => 'applications/differential/command/DifferentialActionEmailCommand.php', 'DifferentialAdjustmentMapTestCase' => 'applications/differential/storage/__tests__/DifferentialAdjustmentMapTestCase.php', 'DifferentialAffectedPath' => 'applications/differential/storage/DifferentialAffectedPath.php', + 'DifferentialAffectedPathEngine' => 'applications/differential/engine/DifferentialAffectedPathEngine.php', 'DifferentialAsanaRepresentationField' => 'applications/differential/customfield/DifferentialAsanaRepresentationField.php', 'DifferentialAuditorsCommitMessageField' => 'applications/differential/field/DifferentialAuditorsCommitMessageField.php', 'DifferentialAuditorsField' => 'applications/differential/customfield/DifferentialAuditorsField.php', @@ -493,6 +496,7 @@ phutil_register_library_map(array( 'DifferentialCommitsSearchEngineAttachment' => 'applications/differential/engineextension/DifferentialCommitsSearchEngineAttachment.php', 'DifferentialConduitAPIMethod' => 'applications/differential/conduit/DifferentialConduitAPIMethod.php', 'DifferentialConflictsCommitMessageField' => 'applications/differential/field/DifferentialConflictsCommitMessageField.php', + 'DifferentialConstantsModule' => 'applications/differential/constants/DifferentialConstantsModule.php', 'DifferentialController' => 'applications/differential/controller/DifferentialController.php', 'DifferentialCoreCustomField' => 'applications/differential/customfield/DifferentialCoreCustomField.php', 'DifferentialCreateCommentConduitAPIMethod' => 'applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php', @@ -613,6 +617,7 @@ phutil_register_library_map(array( 'DifferentialRevisionAcceptTransaction' => 'applications/differential/xaction/DifferentialRevisionAcceptTransaction.php', 'DifferentialRevisionActionTransaction' => 'applications/differential/xaction/DifferentialRevisionActionTransaction.php', 'DifferentialRevisionAffectedFilesHeraldField' => 'applications/differential/herald/DifferentialRevisionAffectedFilesHeraldField.php', + 'DifferentialRevisionAffectedPathsController' => 'applications/differential/controller/DifferentialRevisionAffectedPathsController.php', 'DifferentialRevisionAuthorHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorHeraldField.php', 'DifferentialRevisionAuthorPackagesHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorPackagesHeraldField.php', 'DifferentialRevisionAuthorProjectsHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorProjectsHeraldField.php', @@ -2021,6 +2026,7 @@ phutil_register_library_map(array( 'PHUICalendarMonthView' => 'view/phui/calendar/PHUICalendarMonthView.php', 'PHUICalendarWeekView' => 'view/phui/calendar/PHUICalendarWeekView.php', 'PHUICalendarWidgetView' => 'view/phui/calendar/PHUICalendarWidgetView.php', + 'PHUIColor' => 'view/phui/PHUIColor.php', 'PHUIColorPalletteExample' => 'applications/uiexample/examples/PHUIColorPalletteExample.php', 'PHUICrumbView' => 'view/phui/PHUICrumbView.php', 'PHUICrumbsView' => 'view/phui/PHUICrumbsView.php', @@ -6087,6 +6093,8 @@ phutil_register_library_map(array( 'AlmanacDeviceSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'AlmanacDeviceSearchEngine' => 'PhabricatorApplicationSearchEngine', 'AlmanacDeviceSetPropertyTransaction' => 'AlmanacDeviceTransactionType', + 'AlmanacDeviceStatus' => 'Phobject', + 'AlmanacDeviceStatusTransaction' => 'AlmanacDeviceTransactionType', 'AlmanacDeviceTransaction' => 'AlmanacModularTransaction', 'AlmanacDeviceTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'AlmanacDeviceTransactionType' => 'AlmanacTransactionType', @@ -6532,6 +6540,7 @@ phutil_register_library_map(array( 'DifferentialActionEmailCommand' => 'MetaMTAEmailTransactionCommand', 'DifferentialAdjustmentMapTestCase' => 'PhabricatorTestCase', 'DifferentialAffectedPath' => 'DifferentialDAO', + 'DifferentialAffectedPathEngine' => 'Phobject', 'DifferentialAsanaRepresentationField' => 'DifferentialCustomField', 'DifferentialAuditorsCommitMessageField' => 'DifferentialCommitMessageCustomField', 'DifferentialAuditorsField' => 'DifferentialStoredCustomField', @@ -6580,6 +6589,7 @@ phutil_register_library_map(array( 'DifferentialCommitsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 'DifferentialConduitAPIMethod' => 'ConduitAPIMethod', 'DifferentialConflictsCommitMessageField' => 'DifferentialCommitMessageField', + 'DifferentialConstantsModule' => 'PhabricatorConfigModule', 'DifferentialController' => 'PhabricatorController', 'DifferentialCoreCustomField' => 'DifferentialCustomField', 'DifferentialCreateCommentConduitAPIMethod' => 'DifferentialConduitAPIMethod', @@ -6733,6 +6743,7 @@ phutil_register_library_map(array( 'DifferentialRevisionAcceptTransaction' => 'DifferentialRevisionReviewTransaction', 'DifferentialRevisionActionTransaction' => 'DifferentialRevisionTransactionType', 'DifferentialRevisionAffectedFilesHeraldField' => 'DifferentialRevisionHeraldField', + 'DifferentialRevisionAffectedPathsController' => 'DifferentialController', 'DifferentialRevisionAuthorHeraldField' => 'DifferentialRevisionHeraldField', 'DifferentialRevisionAuthorPackagesHeraldField' => 'DifferentialRevisionHeraldField', 'DifferentialRevisionAuthorProjectsHeraldField' => 'DifferentialRevisionHeraldField', @@ -8348,6 +8359,7 @@ phutil_register_library_map(array( 'PHUICalendarMonthView' => 'AphrontView', 'PHUICalendarWeekView' => 'AphrontView', 'PHUICalendarWidgetView' => 'AphrontTagView', + 'PHUIColor' => 'Phobject', 'PHUIColorPalletteExample' => 'PhabricatorUIExample', 'PHUICrumbView' => 'AphrontView', 'PHUICrumbsView' => 'AphrontView', diff --git a/src/applications/almanac/constants/AlmanacDeviceStatus.php b/src/applications/almanac/constants/AlmanacDeviceStatus.php new file mode 100644 index 0000000000..0bd27dde86 --- /dev/null +++ b/src/applications/almanac/constants/AlmanacDeviceStatus.php @@ -0,0 +1,110 @@ +value = $value; + return $status; + } + + public function getValue() { + return $this->value; + } + + public function getName() { + $name = $this->getDeviceStatusProperty('name'); + + if ($name === null) { + $name = pht('Unknown Almanac Device Status ("%s")', $this->getValue()); + } + + return $name; + } + + public function getIconIcon() { + return $this->getDeviceStatusProperty('icon.icon'); + } + + public function getIconColor() { + return $this->getDeviceStatusProperty('icon.color'); + } + + public function isDisabled() { + return ($this->getValue() === self::DISABLED); + } + + public function hasStatusTag() { + return ($this->getStatusTagIcon() !== null); + } + + public function getStatusTagIcon() { + return $this->getDeviceStatusProperty('status-tag.icon'); + } + + public function getStatusTagColor() { + return $this->getDeviceStatusProperty('status-tag.color'); + } + + public static function getStatusMap() { + $result = array(); + + foreach (self::newDeviceStatusMap() as $status_value => $ignored) { + $result[$status_value] = self::newStatusFromValue($status_value); + } + + return $result; + } + + public static function getActiveStatusList() { + $results = array(); + foreach (self::newDeviceStatusMap() as $status_value => $status) { + if (empty($status['disabled'])) { + $results[] = $status_value; + } + } + return $results; + } + + public static function getDisabledStatusList() { + $results = array(); + foreach (self::newDeviceStatusMap() as $status_value => $status) { + if (!empty($status['disabled'])) { + $results[] = $status_value; + } + } + return $results; + } + + private function getDeviceStatusProperty($key, $default = null) { + $map = self::newDeviceStatusMap(); + $properties = idx($map, $this->getValue(), array()); + return idx($properties, $key, $default); + } + + private static function newDeviceStatusMap() { + return array( + self::ACTIVE => array( + 'name' => pht('Active'), + 'icon.icon' => 'fa-server', + 'icon.color' => 'green', + ), + self::DISABLED => array( + 'name' => pht('Disabled'), + 'icon.icon' => 'fa-times', + 'icon.color' => 'grey', + 'status-tag.icon' => 'fa-times', + 'status-tag.color' => 'indigo', + 'disabled' => true, + ), + ); + } + + +} diff --git a/src/applications/almanac/controller/AlmanacDeviceViewController.php b/src/applications/almanac/controller/AlmanacDeviceViewController.php index 99cc9c2526..39939abfa7 100644 --- a/src/applications/almanac/controller/AlmanacDeviceViewController.php +++ b/src/applications/almanac/controller/AlmanacDeviceViewController.php @@ -31,6 +31,14 @@ final class AlmanacDeviceViewController ->setPolicyObject($device) ->setHeaderIcon('fa-server'); + $status = $device->getStatusObject(); + if ($status->hasStatusTag()) { + $header->setStatus( + $status->getStatusTagIcon(), + $status->getStatusTagColor(), + $status->getName()); + } + $issue = null; if ($device->isClusterDevice()) { $issue = $this->addClusterMessage( diff --git a/src/applications/almanac/editor/AlmanacDeviceEditEngine.php b/src/applications/almanac/editor/AlmanacDeviceEditEngine.php index fa0625e6ed..d0c2b48f7a 100644 --- a/src/applications/almanac/editor/AlmanacDeviceEditEngine.php +++ b/src/applications/almanac/editor/AlmanacDeviceEditEngine.php @@ -76,6 +76,8 @@ final class AlmanacDeviceEditEngine } protected function buildCustomEditFields($object) { + $status_map = $this->getDeviceStatusMap($object); + return array( id(new PhabricatorTextEditField()) ->setKey('name') @@ -84,7 +86,32 @@ final class AlmanacDeviceEditEngine ->setTransactionType(AlmanacDeviceNameTransaction::TRANSACTIONTYPE) ->setIsRequired(true) ->setValue($object->getName()), + id(new PhabricatorSelectEditField()) + ->setKey('status') + ->setLabel(pht('Status')) + ->setDescription(pht('Device status.')) + ->setTransactionType(AlmanacDeviceStatusTransaction::TRANSACTIONTYPE) + ->setOptions($status_map) + ->setValue($object->getStatus()), ); } + + private function getDeviceStatusMap(AlmanacDevice $device) { + $status_map = AlmanacDeviceStatus::getStatusMap(); + + // If the device currently has an unknown status, add it to the list for + // the dropdown. + $status_value = $device->getStatus(); + if (!isset($status_map[$status_value])) { + $status_map = array( + $status_value => AlmanacDeviceStatus::newStatusFromValue($status_value), + ) + $status_map; + } + + $status_map = mpull($status_map, 'getName'); + + return $status_map; + } + } diff --git a/src/applications/almanac/engineextension/AlmanacBindingsSearchEngineAttachment.php b/src/applications/almanac/engineextension/AlmanacBindingsSearchEngineAttachment.php index a3afaf3251..c4f6b823ee 100644 --- a/src/applications/almanac/engineextension/AlmanacBindingsSearchEngineAttachment.php +++ b/src/applications/almanac/engineextension/AlmanacBindingsSearchEngineAttachment.php @@ -3,6 +3,17 @@ final class AlmanacBindingsSearchEngineAttachment extends AlmanacSearchEngineAttachment { + private $isActive; + + public function setIsActive($is_active) { + $this->isActive = $is_active; + return $this; + } + + public function getIsActive() { + return $this->isActive; + } + public function getAttachmentName() { return pht('Almanac Bindings'); } @@ -13,12 +24,24 @@ final class AlmanacBindingsSearchEngineAttachment public function willLoadAttachmentData($query, $spec) { $query->needProperties(true); - $query->needBindings(true); + + if ($this->getIsActive()) { + $query->needBindings(true); + } else { + $query->needActiveBindings(true); + } } public function getAttachmentForObject($object, $data, $spec) { $bindings = array(); - foreach ($object->getBindings() as $binding) { + + if ($this->getIsActive()) { + $service_bindings = $object->getActiveBindings(); + } else { + $service_bindings = $object->getBindings(); + } + + foreach ($service_bindings as $binding) { $bindings[] = $this->getAlmanacBindingDictionary($binding); } diff --git a/src/applications/almanac/engineextension/AlmanacSearchEngineAttachment.php b/src/applications/almanac/engineextension/AlmanacSearchEngineAttachment.php index 266b281990..97672a2af0 100644 --- a/src/applications/almanac/engineextension/AlmanacSearchEngineAttachment.php +++ b/src/applications/almanac/engineextension/AlmanacSearchEngineAttachment.php @@ -51,6 +51,8 @@ abstract class AlmanacSearchEngineAttachment 'phid' => $device->getPHID(), 'name' => $device->getName(), 'properties' => $this->getAlmanacPropertyList($device), + 'status' => $device->getStatus(), + 'disabled' => $device->isDisabled(), ); } diff --git a/src/applications/almanac/phid/AlmanacInterfacePHIDType.php b/src/applications/almanac/phid/AlmanacInterfacePHIDType.php index 581e86e3be..c7fe73183a 100644 --- a/src/applications/almanac/phid/AlmanacInterfacePHIDType.php +++ b/src/applications/almanac/phid/AlmanacInterfacePHIDType.php @@ -34,7 +34,8 @@ final class AlmanacInterfacePHIDType extends PhabricatorPHIDType { $id = $interface->getID(); - $device_name = $interface->getDevice()->getName(); + $device = $interface->getDevice(); + $device_name = $device->getName(); $address = $interface->getAddress(); $port = $interface->getPort(); $network = $interface->getNetwork()->getName(); @@ -48,6 +49,10 @@ final class AlmanacInterfacePHIDType extends PhabricatorPHIDType { $handle->setObjectName(pht('Interface %d', $id)); $handle->setName($name); + + if ($device->isDisabled()) { + $handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED); + } } } diff --git a/src/applications/almanac/query/AlmanacBindingQuery.php b/src/applications/almanac/query/AlmanacBindingQuery.php index 5d51d2ba35..574967f7bb 100644 --- a/src/applications/almanac/query/AlmanacBindingQuery.php +++ b/src/applications/almanac/query/AlmanacBindingQuery.php @@ -8,6 +8,7 @@ final class AlmanacBindingQuery private $servicePHIDs; private $devicePHIDs; private $interfacePHIDs; + private $isActive; public function withIDs(array $ids) { $this->ids = $ids; @@ -34,6 +35,11 @@ final class AlmanacBindingQuery return $this; } + public function withIsActive($active) { + $this->isActive = $active; + return $this; + } + public function newResultObject() { return new AlmanacBinding(); } @@ -95,39 +101,79 @@ final class AlmanacBindingQuery if ($this->ids !== null) { $where[] = qsprintf( $conn, - 'id IN (%Ld)', + 'binding.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, - 'phid IN (%Ls)', + 'binding.phid IN (%Ls)', $this->phids); } if ($this->servicePHIDs !== null) { $where[] = qsprintf( $conn, - 'servicePHID IN (%Ls)', + 'binding.servicePHID IN (%Ls)', $this->servicePHIDs); } if ($this->devicePHIDs !== null) { $where[] = qsprintf( $conn, - 'devicePHID IN (%Ls)', + 'binding.devicePHID IN (%Ls)', $this->devicePHIDs); } if ($this->interfacePHIDs !== null) { $where[] = qsprintf( $conn, - 'interfacePHID IN (%Ls)', + 'binding.interfacePHID IN (%Ls)', $this->interfacePHIDs); } + if ($this->isActive !== null) { + if ($this->isActive) { + $where[] = qsprintf( + $conn, + '(binding.isDisabled = 0) AND (device.status IN (%Ls))', + AlmanacDeviceStatus::getActiveStatusList()); + } else { + $where[] = qsprintf( + $conn, + '(binding.isDisabled = 1) OR (device.status IN (%Ls))', + AlmanacDeviceStatus::getDisabledStatusList()); + } + } + return $where; } + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { + $joins = parent::buildJoinClauseParts($conn); + + if ($this->shouldJoinDeviceTable()) { + $device_table = new AlmanacDevice(); + $joins[] = qsprintf( + $conn, + 'JOIN %R device ON binding.devicePHID = device.phid', + $device_table); + } + + return $joins; + } + + private function shouldJoinDeviceTable() { + if ($this->isActive !== null) { + return true; + } + + return false; + } + + protected function getPrimaryTableAlias() { + return 'binding'; + } + } diff --git a/src/applications/almanac/query/AlmanacDeviceQuery.php b/src/applications/almanac/query/AlmanacDeviceQuery.php index 21f8c952ef..7fcf219571 100644 --- a/src/applications/almanac/query/AlmanacDeviceQuery.php +++ b/src/applications/almanac/query/AlmanacDeviceQuery.php @@ -9,6 +9,7 @@ final class AlmanacDeviceQuery private $namePrefix; private $nameSuffix; private $isClusterDevice; + private $statuses; public function withIDs(array $ids) { $this->ids = $ids; @@ -35,6 +36,11 @@ final class AlmanacDeviceQuery return $this; } + public function withStatuses(array $statuses) { + $this->statuses = $statuses; + return $this; + } + public function withNameNgrams($ngrams) { return $this->withNgramsConstraint( new AlmanacDeviceNameNgrams(), @@ -103,6 +109,13 @@ final class AlmanacDeviceQuery (int)$this->isClusterDevice); } + if ($this->statuses !== null) { + $where[] = qsprintf( + $conn, + 'device.status IN (%Ls)', + $this->statuses); + } + return $where; } diff --git a/src/applications/almanac/query/AlmanacDeviceSearchEngine.php b/src/applications/almanac/query/AlmanacDeviceSearchEngine.php index 1d0a1d8475..9f98abb292 100644 --- a/src/applications/almanac/query/AlmanacDeviceSearchEngine.php +++ b/src/applications/almanac/query/AlmanacDeviceSearchEngine.php @@ -16,6 +16,9 @@ final class AlmanacDeviceSearchEngine } protected function buildCustomSearchFields() { + $status_options = AlmanacDeviceStatus::getStatusMap(); + $status_options = mpull($status_options, 'getName'); + return array( id(new PhabricatorSearchTextField()) ->setLabel(pht('Name Contains')) @@ -25,6 +28,11 @@ final class AlmanacDeviceSearchEngine ->setLabel(pht('Exact Names')) ->setKey('names') ->setDescription(pht('Search for devices with specific names.')), + id(new PhabricatorSearchCheckboxesField()) + ->setLabel(pht('Statuses')) + ->setKey('statuses') + ->setDescription(pht('Search for devices with given statuses.')) + ->setOptions($status_options), id(new PhabricatorSearchThreeStateField()) ->setLabel(pht('Cluster Device')) ->setKey('isClusterDevice') @@ -50,6 +58,10 @@ final class AlmanacDeviceSearchEngine $query->withIsClusterDevice($map['isClusterDevice']); } + if ($map['statuses']) { + $query->withStatuses($map['statuses']); + } + return $query; } @@ -59,6 +71,7 @@ final class AlmanacDeviceSearchEngine protected function getBuiltinQueryNames() { $names = array( + 'active' => pht('Active Devices'), 'all' => pht('All Devices'), ); @@ -66,11 +79,13 @@ final class AlmanacDeviceSearchEngine } public function buildSavedQueryFromBuiltin($query_key) { - $query = $this->newSavedQuery(); $query->setQueryKey($query_key); switch ($query_key) { + case 'active': + $active_statuses = AlmanacDeviceStatus::getActiveStatusList(); + return $query->setParameter('statuses', $active_statuses); case 'all': return $query; } @@ -99,6 +114,19 @@ final class AlmanacDeviceSearchEngine $item->addIcon('fa-sitemap', pht('Cluster Device')); } + if ($device->isDisabled()) { + $item->setDisabled(true); + } + + $status = $device->getStatusObject(); + $icon_icon = $status->getIconIcon(); + $icon_color = $status->getIconColor(); + $icon_label = $status->getName(); + + $item->setStatusIcon( + "{$icon_icon} {$icon_color}", + $icon_label); + $list->addItem($item); } diff --git a/src/applications/almanac/query/AlmanacServiceQuery.php b/src/applications/almanac/query/AlmanacServiceQuery.php index edc55276a9..4e374ec90c 100644 --- a/src/applications/almanac/query/AlmanacServiceQuery.php +++ b/src/applications/almanac/query/AlmanacServiceQuery.php @@ -12,6 +12,7 @@ final class AlmanacServiceQuery private $nameSuffix; private $needBindings; + private $needActiveBindings; public function withIDs(array $ids) { $this->ids = $ids; @@ -59,6 +60,11 @@ final class AlmanacServiceQuery return $this; } + public function needActiveBindings($need_active) { + $this->needActiveBindings = $need_active; + return $this; + } + public function newResultObject() { return new AlmanacService(); } @@ -160,18 +166,35 @@ final class AlmanacServiceQuery } protected function didFilterPage(array $services) { - if ($this->needBindings) { + $need_all = $this->needBindings; + $need_active = $this->needActiveBindings; + + $need_any = ($need_all || $need_active); + $only_active = ($need_active && !$need_all); + + if ($need_any) { $service_phids = mpull($services, 'getPHID'); - $bindings = id(new AlmanacBindingQuery()) + + $bindings_query = id(new AlmanacBindingQuery()) ->setViewer($this->getViewer()) ->withServicePHIDs($service_phids) - ->needProperties($this->getNeedProperties()) - ->execute(); + ->needProperties($this->getNeedProperties()); + + if ($only_active) { + $bindings_query->withIsActive(true); + } + + $bindings = $bindings_query->execute(); $bindings = mgroup($bindings, 'getServicePHID'); foreach ($services as $service) { $service_bindings = idx($bindings, $service->getPHID(), array()); - $service->attachBindings($service_bindings); + + if ($only_active) { + $service->attachActiveBindings($service_bindings); + } else { + $service->attachBindings($service_bindings); + } } } diff --git a/src/applications/almanac/storage/AlmanacBinding.php b/src/applications/almanac/storage/AlmanacBinding.php index a7096fc51f..011a085151 100644 --- a/src/applications/almanac/storage/AlmanacBinding.php +++ b/src/applications/almanac/storage/AlmanacBinding.php @@ -13,7 +13,6 @@ final class AlmanacBinding protected $servicePHID; protected $devicePHID; protected $interfacePHID; - protected $mailKey; protected $isDisabled; private $service = self::ATTACHABLE; @@ -33,7 +32,6 @@ final class AlmanacBinding return array( self::CONFIG_AUX_PHID => true, self::CONFIG_COLUMN_SCHEMA => array( - 'mailKey' => 'bytes20', 'isDisabled' => 'bool', ), self::CONFIG_KEY_SCHEMA => array( @@ -51,15 +49,8 @@ final class AlmanacBinding ) + parent::getConfiguration(); } - public function generatePHID() { - return PhabricatorPHID::generateNewPHID(AlmanacBindingPHIDType::TYPECONST); - } - - public function save() { - if (!$this->mailKey) { - $this->mailKey = Filesystem::readRandomCharacters(20); - } - return parent::save(); + public function getPHIDType() { + return AlmanacBindingPHIDType::TYPECONST; } public function getName() { @@ -67,7 +58,9 @@ final class AlmanacBinding } public function getURI() { - return '/almanac/binding/'.$this->getID().'/'; + return urisprintf( + '/almanac/binding/%s/', + $this->getID()); } public function getService() { diff --git a/src/applications/almanac/storage/AlmanacDevice.php b/src/applications/almanac/storage/AlmanacDevice.php index 1d1010733a..87704d89d8 100644 --- a/src/applications/almanac/storage/AlmanacDevice.php +++ b/src/applications/almanac/storage/AlmanacDevice.php @@ -15,9 +15,9 @@ final class AlmanacDevice protected $name; protected $nameIndex; - protected $mailKey; protected $viewPolicy; protected $editPolicy; + protected $status; protected $isBoundToClusterService; private $almanacProperties = self::ATTACHABLE; @@ -26,6 +26,7 @@ final class AlmanacDevice return id(new AlmanacDevice()) ->setViewPolicy(PhabricatorPolicies::POLICY_USER) ->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN) + ->setStatus(AlmanacDeviceStatus::ACTIVE) ->attachAlmanacProperties(array()) ->setIsBoundToClusterService(0); } @@ -36,7 +37,7 @@ final class AlmanacDevice self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'text128', 'nameIndex' => 'bytes12', - 'mailKey' => 'bytes20', + 'status' => 'text32', 'isBoundToClusterService' => 'bool', ), self::CONFIG_KEY_SCHEMA => array( @@ -51,8 +52,8 @@ final class AlmanacDevice ) + parent::getConfiguration(); } - public function generatePHID() { - return PhabricatorPHID::generateNewPHID(AlmanacDevicePHIDType::TYPECONST); + public function getPHIDType() { + return AlmanacDevicePHIDType::TYPECONST; } public function save() { @@ -60,15 +61,13 @@ final class AlmanacDevice $this->nameIndex = PhabricatorHash::digestForIndex($this->getName()); - if (!$this->mailKey) { - $this->mailKey = Filesystem::readRandomCharacters(20); - } - return parent::save(); } public function getURI() { - return '/almanac/device/view/'.$this->getName().'/'; + return urisprintf( + '/almanac/device/view/%s/', + $this->getName()); } public function rebuildClusterBindingStatus() { @@ -90,8 +89,8 @@ final class AlmanacDevice $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); queryfx( $this->establishConnection('w'), - 'UPDATE %T SET isBoundToClusterService = %d WHERE id = %d', - $this->getTableName(), + 'UPDATE %R SET isBoundToClusterService = %d WHERE id = %d', + $this, $this->getIsBoundToClusterService(), $this->getID()); unset($unguarded); @@ -104,6 +103,18 @@ final class AlmanacDevice return $this->getIsBoundToClusterService(); } + public function getStatusObject() { + return $this->newStatusObject(); + } + + private function newStatusObject() { + return AlmanacDeviceStatus::newStatusFromValue($this->getStatus()); + } + + public function isDisabled() { + return $this->getStatusObject()->isDisabled(); + } + /* -( AlmanacPropertyInterface )------------------------------------------- */ @@ -267,12 +278,27 @@ final class AlmanacDevice ->setKey('name') ->setType('string') ->setDescription(pht('The name of the device.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('status') + ->setType('map') + ->setDescription(pht('Device status information.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('disabled') + ->setType('bool') + ->setDescription(pht('True if device is disabled.')), ); } public function getFieldValuesForConduit() { + $status = $this->getStatusObject(); + return array( 'name' => $this->getName(), + 'status' => array( + 'value' => $status->getValue(), + 'name' => $status->getName(), + ), + 'disabled' => $this->isDisabled(), ); } diff --git a/src/applications/almanac/storage/AlmanacNamespace.php b/src/applications/almanac/storage/AlmanacNamespace.php index f3745b1d29..f6e3719057 100644 --- a/src/applications/almanac/storage/AlmanacNamespace.php +++ b/src/applications/almanac/storage/AlmanacNamespace.php @@ -12,7 +12,6 @@ final class AlmanacNamespace protected $name; protected $nameIndex; - protected $mailKey; protected $viewPolicy; protected $editPolicy; @@ -28,7 +27,6 @@ final class AlmanacNamespace self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'text128', 'nameIndex' => 'bytes12', - 'mailKey' => 'bytes20', ), self::CONFIG_KEY_SCHEMA => array( 'key_nameindex' => array( @@ -42,9 +40,8 @@ final class AlmanacNamespace ) + parent::getConfiguration(); } - public function generatePHID() { - return PhabricatorPHID::generateNewPHID( - AlmanacNamespacePHIDType::TYPECONST); + public function getPHIDType() { + return AlmanacNamespacePHIDType::TYPECONST; } public function save() { @@ -52,15 +49,13 @@ final class AlmanacNamespace $this->nameIndex = PhabricatorHash::digestForIndex($this->getName()); - if (!$this->mailKey) { - $this->mailKey = Filesystem::readRandomCharacters(20); - } - return parent::save(); } public function getURI() { - return '/almanac/namespace/view/'.$this->getName().'/'; + return urisprintf( + '/almanac/namespace/view/%s/', + $this->getName()); } public function getNameLength() { diff --git a/src/applications/almanac/storage/AlmanacNetwork.php b/src/applications/almanac/storage/AlmanacNetwork.php index 78313fad77..a3dadee69b 100644 --- a/src/applications/almanac/storage/AlmanacNetwork.php +++ b/src/applications/almanac/storage/AlmanacNetwork.php @@ -10,7 +10,6 @@ final class AlmanacNetwork PhabricatorConduitResultInterface { protected $name; - protected $mailKey; protected $viewPolicy; protected $editPolicy; @@ -25,8 +24,6 @@ final class AlmanacNetwork self::CONFIG_AUX_PHID => true, self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'sort128', - 'mailKey' => 'bytes20', - ), self::CONFIG_KEY_SCHEMA => array( 'key_name' => array( @@ -37,20 +34,14 @@ final class AlmanacNetwork ) + parent::getConfiguration(); } - public function generatePHID() { - return PhabricatorPHID::generateNewPHID(AlmanacNetworkPHIDType::TYPECONST); - } - - public function save() { - if (!$this->mailKey) { - $this->mailKey = Filesystem::readRandomCharacters(20); - } - - return parent::save(); + public function getPHIDType() { + return AlmanacNetworkPHIDType::TYPECONST; } public function getURI() { - return '/almanac/network/'.$this->getID().'/'; + return urisprintf( + '/almanac/network/%s/', + $this->getID()); } diff --git a/src/applications/almanac/storage/AlmanacService.php b/src/applications/almanac/storage/AlmanacService.php index 2979b74367..58a3f681df 100644 --- a/src/applications/almanac/storage/AlmanacService.php +++ b/src/applications/almanac/storage/AlmanacService.php @@ -14,13 +14,13 @@ final class AlmanacService protected $name; protected $nameIndex; - protected $mailKey; protected $viewPolicy; protected $editPolicy; protected $serviceType; private $almanacProperties = self::ATTACHABLE; private $bindings = self::ATTACHABLE; + private $activeBindings = self::ATTACHABLE; private $serviceImplementation = self::ATTACHABLE; public static function initializeNewService($type) { @@ -48,7 +48,6 @@ final class AlmanacService self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'text128', 'nameIndex' => 'bytes12', - 'mailKey' => 'bytes20', 'serviceType' => 'text64', ), self::CONFIG_KEY_SCHEMA => array( @@ -66,8 +65,8 @@ final class AlmanacService ) + parent::getConfiguration(); } - public function generatePHID() { - return PhabricatorPHID::generateNewPHID(AlmanacServicePHIDType::TYPECONST); + public function getPHIDType() { + return AlmanacServicePHIDType::TYPECONST; } public function save() { @@ -75,10 +74,6 @@ final class AlmanacService $this->nameIndex = PhabricatorHash::digestForIndex($this->getName()); - if (!$this->mailKey) { - $this->mailKey = Filesystem::readRandomCharacters(20); - } - return parent::save(); } @@ -91,23 +86,36 @@ final class AlmanacService } public function getActiveBindings() { - $bindings = $this->getBindings(); - - // Filter out disabled bindings. - foreach ($bindings as $key => $binding) { - if ($binding->getIsDisabled()) { - unset($bindings[$key]); - } - } - - return $bindings; + return $this->assertAttached($this->activeBindings); } public function attachBindings(array $bindings) { + $active_bindings = array(); + foreach ($bindings as $key => $binding) { + // Filter out disabled bindings. + if ($binding->getIsDisabled()) { + continue; + } + + // Filter out bindings to disabled devices. + if ($binding->getDevice()->isDisabled()) { + continue; + } + + $active_bindings[$key] = $binding; + } + + $this->attachActiveBindings($active_bindings); + $this->bindings = $bindings; return $this; } + public function attachActiveBindings(array $bindings) { + $this->activeBindings = $bindings; + return $this; + } + public function getServiceImplementation() { return $this->assertAttached($this->serviceImplementation); } @@ -289,6 +297,9 @@ final class AlmanacService ->setAttachmentKey('properties'), id(new AlmanacBindingsSearchEngineAttachment()) ->setAttachmentKey('bindings'), + id(new AlmanacBindingsSearchEngineAttachment()) + ->setIsActive(true) + ->setAttachmentKey('activeBindings'), ); } diff --git a/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php b/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php index 15e50f5993..dca5dd986b 100644 --- a/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php +++ b/src/applications/almanac/typeahead/AlmanacInterfaceDatasource.php @@ -46,9 +46,16 @@ final class AlmanacInterfaceDatasource $results = array(); foreach ($handles as $handle) { + if ($handle->isClosed()) { + $closed = pht('Disabled'); + } else { + $closed = null; + } + $results[] = id(new PhabricatorTypeaheadResult()) ->setName($handle->getName()) - ->setPHID($handle->getPHID()); + ->setPHID($handle->getPHID()) + ->setClosed($closed); } return $results; diff --git a/src/applications/almanac/view/AlmanacBindingTableView.php b/src/applications/almanac/view/AlmanacBindingTableView.php index 746d3de5c2..a74cb4f9c3 100644 --- a/src/applications/almanac/view/AlmanacBindingTableView.php +++ b/src/applications/almanac/view/AlmanacBindingTableView.php @@ -56,21 +56,41 @@ final class AlmanacBindingTableView extends AphrontView { $icon_active = id(new PHUIIconView()) ->setIcon('fa-check') + ->setColor('green') ->addSigil('has-tooltip') ->setMetadata( array( 'tip' => pht('Active'), )); + $icon_device_disabled = id(new PHUIIconView()) + ->setIcon('fa-times') + ->setColor('grey') + ->addSigil('has-tooltip') + ->setMetadata( + array( + 'tip' => pht('Device Disabled'), + )); + $rows = array(); foreach ($bindings as $binding) { $addr = $binding->getInterface()->getAddress(); $port = $binding->getInterface()->getPort(); + $device = $binding->getDevice(); + if ($device->isDisabled()) { + $binding_icon = $icon_device_disabled; + } else if ($binding->getIsDisabled()) { + $binding_icon = $icon_disabled; + } else { + $binding_icon = $icon_active; + } + $rows[] = array( $binding->getID(), - ($binding->getIsDisabled() ? $icon_disabled : $icon_active), + $binding_icon, $handles->renderHandle($binding->getServicePHID()), + $handles->renderHandle($binding->getDevicePHID()), $handles->renderHandle($binding->getInterface()->getNetworkPHID()), $binding->getInterface()->renderDisplayAddress(), diff --git a/src/applications/almanac/xaction/AlmanacDeviceStatusTransaction.php b/src/applications/almanac/xaction/AlmanacDeviceStatusTransaction.php new file mode 100644 index 0000000000..57c7c05507 --- /dev/null +++ b/src/applications/almanac/xaction/AlmanacDeviceStatusTransaction.php @@ -0,0 +1,61 @@ +getStatus(); + } + + public function applyInternalEffects($object, $value) { + $object->setStatus($value); + } + + public function getTitle() { + $old_value = $this->getOldValue(); + $new_value = $this->getNewValue(); + + $old_status = AlmanacDeviceStatus::newStatusFromValue($old_value); + $new_status = AlmanacDeviceStatus::newStatusFromValue($new_value); + + $old_name = $old_status->getName(); + $new_name = $new_status->getName(); + + return pht( + '%s changed the status of this device from %s to %s.', + $this->renderAuthor(), + $this->renderValue($old_name), + $this->renderValue($new_name)); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + $status_map = AlmanacDeviceStatus::getStatusMap(); + + $old_value = $this->generateOldValue($object); + foreach ($xactions as $xaction) { + $new_value = $xaction->getNewValue(); + + if ($new_value === $old_value) { + continue; + } + + if (!isset($status_map[$new_value])) { + $errors[] = $this->newInvalidError( + pht( + 'Almanac device status "%s" is unrecognized. Valid status '. + 'values are: %s.', + $new_value, + implode(', ', array_keys($status_map))), + $xaction); + continue; + } + } + + return $errors; + } + +} diff --git a/src/applications/calendar/storage/PhabricatorCalendarEvent.php b/src/applications/calendar/storage/PhabricatorCalendarEvent.php index a8092aaa88..941ec6220d 100644 --- a/src/applications/calendar/storage/PhabricatorCalendarEvent.php +++ b/src/applications/calendar/storage/PhabricatorCalendarEvent.php @@ -24,7 +24,6 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO protected $isCancelled; protected $isAllDay; protected $icon; - protected $mailKey; protected $isStub; protected $isRecurring = 0; @@ -360,10 +359,6 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO } public function save() { - if (!$this->mailKey) { - $this->mailKey = Filesystem::readRandomCharacters(20); - } - $import_uid = $this->getImportUID(); if ($import_uid !== null) { $index = PhabricatorHash::digestForIndex($import_uid); @@ -405,7 +400,6 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO 'isCancelled' => 'bool', 'isAllDay' => 'bool', 'icon' => 'text32', - 'mailKey' => 'bytes20', 'isRecurring' => 'bool', 'seriesParentPHID' => 'phid?', 'instanceOfEventPHID' => 'phid?', @@ -442,9 +436,8 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO ) + parent::getConfiguration(); } - public function generatePHID() { - return PhabricatorPHID::generateNewPHID( - PhabricatorCalendarEventPHIDType::TYPECONST); + public function getPHIDType() { + return PhabricatorCalendarEventPHIDType::TYPECONST; } public function getMonogram() { diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php index 9ca84fff3d..bc200e7078 100644 --- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php +++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php @@ -238,6 +238,16 @@ final class PhabricatorConduitAPIController if ($object instanceof PhabricatorUser) { $user = $object; } else { + if ($object->isDisabled()) { + return array( + 'ERR-INVALID-AUTH', + pht( + 'The key which signed this request is associated with a '. + 'disabled device ("%s").', + $object->getName()), + ); + } + if (!$stored_key->getIsTrusted()) { return array( 'ERR-INVALID-AUTH', diff --git a/src/applications/conduit/controller/PhabricatorConduitController.php b/src/applications/conduit/controller/PhabricatorConduitController.php index 0f7b2ef54b..e66c1f8a0b 100644 --- a/src/applications/conduit/controller/PhabricatorConduitController.php +++ b/src/applications/conduit/controller/PhabricatorConduitController.php @@ -156,7 +156,7 @@ abstract class PhabricatorConduitController extends PhabricatorController { $parts = array(); - $libphutil_path = 'path/to/libphutil/src/__phutil_library_init__.php'; + $libphutil_path = 'path/to/arcanist/support/init/init-script.php'; $parts[] = 'selectFilter($key); $header = $this->buildHeaderView($title); - $view = $this->buildConfigBoxView($title, $content); + if ($content instanceof AphrontTableView) { + $view = $this->buildConfigBoxView($title, $content); + } else { + $view = $content; + } $crumbs = $this->buildApplicationCrumbs() ->addTextCrumb(pht('Extensions/Modules'), $modules_uri) diff --git a/src/applications/differential/application/PhabricatorDifferentialApplication.php b/src/applications/differential/application/PhabricatorDifferentialApplication.php index b1312228d1..5980f738b0 100644 --- a/src/applications/differential/application/PhabricatorDifferentialApplication.php +++ b/src/applications/differential/application/PhabricatorDifferentialApplication.php @@ -74,6 +74,8 @@ final class PhabricatorDifferentialApplication => 'DifferentialRevisionOperationController', 'inlines/(?P[1-9]\d*)/' => 'DifferentialRevisionInlinesController', + 'paths/(?P[1-9]\d*)/' + => 'DifferentialRevisionAffectedPathsController', ), 'comment/' => array( 'inline/' => array( diff --git a/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php index eeef36ee69..c8dd77ac8a 100644 --- a/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php @@ -38,7 +38,7 @@ final class DifferentialQueryConduitAPIMethod 'authors' => 'optional list', 'ccs' => 'optional list', 'reviewers' => 'optional list', - 'paths' => 'optional list>', + 'paths' => 'unsupported', 'commitHashes' => 'optional list>', 'status' => 'optional '.$status_const, 'order' => 'optional '.$order_const, @@ -92,48 +92,11 @@ final class DifferentialQueryConduitAPIMethod } if ($path_pairs) { - $paths = array(); - foreach ($path_pairs as $pair) { - list($callsign, $path) = $pair; - $paths[] = $path; - } - - $path_map = id(new DiffusionPathIDQuery($paths))->loadPathIDs(); - if (count($path_map) != count($paths)) { - $unknown_paths = array(); - foreach ($paths as $p) { - if (!idx($path_map, $p)) { - $unknown_paths[] = $p; - } - } - throw id(new ConduitException('ERR-INVALID-PARAMETER')) - ->setErrorDescription( - pht( - 'Unknown paths: %s', - implode(', ', $unknown_paths))); - } - - $repos = array(); - foreach ($path_pairs as $pair) { - list($callsign, $path) = $pair; - if (!idx($repos, $callsign)) { - $repos[$callsign] = id(new PhabricatorRepositoryQuery()) - ->setViewer($request->getUser()) - ->withCallsigns(array($callsign)) - ->executeOne(); - - if (!$repos[$callsign]) { - throw id(new ConduitException('ERR-INVALID-PARAMETER')) - ->setErrorDescription( - pht( - 'Unknown repo callsign: %s', - $callsign)); - } - } - $repo = $repos[$callsign]; - - $query->withPath($repo->getID(), idx($path_map, $path)); - } + throw new Exception( + pht( + 'Parameter "paths" to Conduit API method "differential.query" is '. + 'no longer supported. Use the "paths" constraint to '. + '"differential.revision.search" instead. See T13639.')); } if ($commit_hashes) { diff --git a/src/applications/differential/constants/DifferentialConstantsModule.php b/src/applications/differential/constants/DifferentialConstantsModule.php new file mode 100644 index 0000000000..05a5f06c55 --- /dev/null +++ b/src/applications/differential/constants/DifferentialConstantsModule.php @@ -0,0 +1,180 @@ +getViewer(); + + return array( + $this->renderRevisionStatuses($viewer), + $this->renderUnitStatuses($viewer), + $this->renderLintStatuses($viewer), + ); + } + + private function renderRevisionStatuses(PhabricatorUser $viewer) { + $statuses = DifferentialRevisionStatus::getAll(); + + $rows = array(); + foreach ($statuses as $status) { + $icon = id(new PHUIIconView()) + ->setIcon( + $status->getIcon(), + $status->getIconColor()); + + $timeline_icon = $status->getTimelineIcon(); + if ($timeline_icon !== null) { + $timeline_view = id(new PHUIIconView()) + ->setIcon( + $status->getTimelineIcon(), + $status->getTimelineColor()); + } else { + $timeline_view = null; + } + + if ($status->isClosedStatus()) { + $is_open = pht('Closed'); + } else { + $is_open = pht('Open'); + } + + $tag_color = $status->getTagColor(); + if ($tag_color !== null) { + $tag_view = id(new PHUIIconView()) + ->seticon('fa-tag', $tag_color); + } else { + $tag_view = null; + } + + $ansi_color = $status->getAnsiColor(); + if ($ansi_color !== null) { + $web_color = PHUIColor::getWebColorFromANSIColor($ansi_color); + $ansi_view = id(new PHUIIconView()) + ->setIcon('fa-stop', $web_color); + } else { + $ansi_view = null; + } + + + $rows[] = array( + $status->getKey(), + $status->getLegacyKey(), + $icon, + $timeline_view, + $tag_view, + $ansi_view, + $is_open, + $status->getDisplayName(), + ); + } + + $table = id(new AphrontTableView($rows)) + ->setHeaders( + array( + pht('Value'), + pht('Legacy Value'), + pht('Icon'), + pht('Timeline Icon'), + pht('Tag Color'), + pht('ANSI Color'), + pht('Open/Closed'), + pht('Name'), + )) + ->setColumnClasses( + array( + null, + null, + null, + null, + null, + null, + null, + 'wide pri', + )); + + $view = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Differential Revision Statuses')) + ->setTable($table); + + return $view; + } + + private function renderUnitStatuses(PhabricatorUser $viewer) { + $statuses = DifferentialUnitStatus::getStatusMap(); + + $rows = array(); + foreach ($statuses as $status) { + $rows[] = array( + $status->getValue(), + id(new PHUIIconView()) + ->setIcon($status->getIconIcon(), $status->getIconColor()), + $status->getName(), + ); + } + + $table = id(new AphrontTableView($rows)) + ->setHeaders( + array( + pht('Value'), + pht('Icon'), + pht('Name'), + )) + ->setColumnClasses( + array( + null, + null, + 'wide pri', + )); + + $view = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Differential Unit Statuses')) + ->setTable($table); + + return $view; + } + + private function renderLintStatuses(PhabricatorUser $viewer) { + $statuses = DifferentialLintStatus::getStatusMap(); + + $rows = array(); + foreach ($statuses as $status) { + $rows[] = array( + $status->getValue(), + id(new PHUIIconView()) + ->setIcon($status->getIconIcon(), $status->getIconColor()), + $status->getName(), + ); + } + + $table = id(new AphrontTableView($rows)) + ->setHeaders( + array( + pht('Value'), + pht('Icon'), + pht('Name'), + )) + ->setColumnClasses( + array( + null, + null, + 'wide pri', + )); + + $view = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Differential Lint Statuses')) + ->setTable($table); + + return $view; + } + + +} diff --git a/src/applications/differential/constants/DifferentialLintStatus.php b/src/applications/differential/constants/DifferentialLintStatus.php index 72709b8eef..8f2dedbe84 100644 --- a/src/applications/differential/constants/DifferentialLintStatus.php +++ b/src/applications/differential/constants/DifferentialLintStatus.php @@ -9,4 +9,85 @@ final class DifferentialLintStatus extends Phobject { const LINT_SKIP = 4; const LINT_AUTO_SKIP = 6; + private $value; + + public static function newStatusFromValue($value) { + $status = new self(); + $status->value = $value; + return $status; + } + + public function getValue() { + return $this->value; + } + + public function getName() { + $name = $this->getLintStatusProperty('name'); + + if ($name === null) { + $name = pht('Unknown Lint Status ("%s")', $this->getValue()); + } + + return $name; + } + + public function getIconIcon() { + return $this->getLintStatusProperty('icon.icon'); + } + + public function getIconColor() { + return $this->getLintStatusProperty('icon.color'); + } + + public static function getStatusMap() { + $results = array(); + + foreach (self::newLintStatusMap() as $value => $ignored) { + $results[$value] = self::newStatusFromValue($value); + } + + return $results; + } + + private function getLintStatusProperty($key, $default = null) { + $map = self::newLintStatusMap(); + $properties = idx($map, $this->getValue(), array()); + return idx($properties, $key, $default); + } + + private static function newLintStatusMap() { + return array( + self::LINT_NONE => array( + 'name' => pht('No Lint Coverage'), + 'icon.icon' => 'fa-ban', + 'icon.color' => 'grey', + ), + self::LINT_OKAY => array( + 'name' => pht('Lint Passed'), + 'icon.icon' => 'fa-check', + 'icon.color' => 'green', + ), + self::LINT_WARN => array( + 'name' => pht('Lint Warnings'), + 'icon.icon' => 'fa-exclamation-triangle', + 'icon.color' => 'yellow', + ), + self::LINT_FAIL => array( + 'name' => pht('Lint Errors'), + 'icon.icon' => 'fa-times', + 'icon.color' => 'red', + ), + self::LINT_SKIP => array( + 'name' => pht('Lint Skipped'), + 'icon.icon' => 'fa-fast-forward', + 'icon.color' => 'blue', + ), + self::LINT_AUTO_SKIP => array( + 'name' => pht('Lint Not Applicable'), + 'icon.icon' => 'fa-code', + 'icon.color' => 'grey', + ), + ); + } + } diff --git a/src/applications/differential/constants/DifferentialUnitStatus.php b/src/applications/differential/constants/DifferentialUnitStatus.php index 2b69853dfb..186f476800 100644 --- a/src/applications/differential/constants/DifferentialUnitStatus.php +++ b/src/applications/differential/constants/DifferentialUnitStatus.php @@ -9,4 +9,85 @@ final class DifferentialUnitStatus extends Phobject { const UNIT_SKIP = 4; const UNIT_AUTO_SKIP = 6; + private $value; + + public static function newStatusFromValue($value) { + $status = new self(); + $status->value = $value; + return $status; + } + + public function getValue() { + return $this->value; + } + + public function getName() { + $name = $this->getUnitStatusProperty('name'); + + if ($name === null) { + $name = pht('Unknown Unit Status ("%s")', $this->getValue()); + } + + return $name; + } + + public function getIconIcon() { + return $this->getUnitStatusProperty('icon.icon'); + } + + public function getIconColor() { + return $this->getUnitStatusProperty('icon.color'); + } + + public static function getStatusMap() { + $results = array(); + + foreach (self::newUnitStatusMap() as $value => $ignored) { + $results[$value] = self::newStatusFromValue($value); + } + + return $results; + } + + private function getUnitStatusProperty($key, $default = null) { + $map = self::newUnitStatusMap(); + $properties = idx($map, $this->getValue(), array()); + return idx($properties, $key, $default); + } + + private static function newUnitStatusMap() { + return array( + self::UNIT_NONE => array( + 'name' => pht('No Test Coverage'), + 'icon.icon' => 'fa-ban', + 'icon.color' => 'grey', + ), + self::UNIT_OKAY => array( + 'name' => pht('Tests Passed'), + 'icon.icon' => 'fa-check', + 'icon.color' => 'green', + ), + self::UNIT_WARN => array( + 'name' => pht('Test Warnings'), + 'icon.icon' => 'fa-exclamation-triangle', + 'icon.color' => 'yellow', + ), + self::UNIT_FAIL => array( + 'name' => pht('Test Failures'), + 'icon.icon' => 'fa-times', + 'icon.color' => 'red', + ), + self::UNIT_SKIP => array( + 'name' => pht('Tests Skipped'), + 'icon.icon' => 'fa-fast-forward', + 'icon.color' => 'blue', + ), + self::UNIT_AUTO_SKIP => array( + 'name' => pht('Tests Not Applicable'), + 'icon.icon' => 'fa-code', + 'icon.color' => 'grey', + ), + ); + } + } diff --git a/src/applications/differential/controller/DifferentialDiffCreateController.php b/src/applications/differential/controller/DifferentialDiffCreateController.php index 9aaf407e28..f9d1006555 100644 --- a/src/applications/differential/controller/DifferentialDiffCreateController.php +++ b/src/applications/differential/controller/DifferentialDiffCreateController.php @@ -27,7 +27,13 @@ final class DifferentialDiffCreateController extends DifferentialController { $diff = null; // This object is just for policy stuff $diff_object = DifferentialDiff::initializeNewDiff($viewer); - $repository_phid = null; + + if ($revision) { + $repository_phid = $revision->getRepositoryPHID(); + } else { + $repository_phid = null; + } + $errors = array(); $e_diff = null; $e_file = null; diff --git a/src/applications/differential/controller/DifferentialRevisionAffectedPathsController.php b/src/applications/differential/controller/DifferentialRevisionAffectedPathsController.php new file mode 100644 index 0000000000..a485d5ecfe --- /dev/null +++ b/src/applications/differential/controller/DifferentialRevisionAffectedPathsController.php @@ -0,0 +1,127 @@ +getViewer(); + $id = $request->getURIData('id'); + + $revision = id(new DifferentialRevisionQuery()) + ->withIDs(array($id)) + ->setViewer($viewer) + ->executeOne(); + if (!$revision) { + return new Aphront404Response(); + } + + $table = new DifferentialAffectedPath(); + $conn = $table->establishConnection('r'); + + $paths = queryfx_all( + $conn, + 'SELECT * FROM %R WHERE revisionID = %d', + $table, + $revision->getID()); + + $repository_ids = array(); + $path_ids = array(); + + foreach ($paths as $path) { + $repository_id = $path['repositoryID']; + $path_id = $path['pathID']; + + $repository_ids[] = $repository_id; + $path_ids[] = $path_id; + } + + $repository_ids = array_fuse($repository_ids); + + if ($repository_ids) { + $repositories = id(new PhabricatorRepositoryQuery()) + ->setViewer($viewer) + ->withIDs($repository_ids) + ->execute(); + $repositories = mpull($repositories, null, 'getID'); + } else { + $repositories = array(); + } + + $handles = $viewer->loadHandles(mpull($repositories, 'getPHID')); + + $path_ids = array_fuse($path_ids); + if ($path_ids) { + $path_names = id(new DiffusionPathQuery()) + ->withPathIDs($path_ids) + ->execute(); + } else { + $path_names = array(); + } + + $rows = array(); + foreach ($paths as $path) { + $repository_id = $path['repositoryID']; + $path_id = $path['pathID']; + + $repository = idx($repositories, $repository_id); + if ($repository) { + $repository_phid = $repository->getPHID(); + $repository_link = $handles[$repository_phid]->renderLink(); + } else { + $repository_link = null; + } + + $path_name = idx($path_names, $path_id); + if ($path_name !== null) { + $path_view = $path_name['path']; + } else { + $path_view = null; + } + + $rows[] = array( + $repository_id, + $repository_link, + $path_id, + $path_view, + ); + } + + // Sort rows by path name. + $rows = isort($rows, 3); + + $table_view = id(new AphrontTableView($rows)) + ->setNoDataString(pht('This revision has no indexed affected paths.')) + ->setHeaders( + array( + pht('Repository ID'), + pht('Repository'), + pht('Path ID'), + pht('Path'), + )) + ->setColumnClasses( + array( + null, + null, + null, + 'wide', + )); + + $box_view = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Affected Path Index')) + ->setTable($table_view); + + $crumbs = $this->buildApplicationCrumbs() + ->addTextCrumb($revision->getMonogram(), $revision->getURI()) + ->addTextCrumb(pht('Affected Path Index')); + + return $this->newPage() + ->setCrumbs($crumbs) + ->setTitle( + array( + $revision->getMonogram(), + pht('Affected Path Index'), + )) + ->appendChild($box_view); + } + +} diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index e37fa2b529..13ab8a8954 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -986,6 +986,8 @@ final class DifferentialRevisionViewController PhabricatorRepository $repository) { assert_instances_of($changesets, 'DifferentialChangeset'); + $viewer = $this->getViewer(); + $paths = array(); foreach ($changesets as $changeset) { $paths[] = $changeset->getAbsoluteRepositoryPath( @@ -997,34 +999,30 @@ final class DifferentialRevisionViewController return array(); } - $path_map = id(new DiffusionPathIDQuery($paths))->loadPathIDs(); - - if (!$path_map) { - return array(); - } - $recent = (PhabricatorTime::getNow() - phutil_units('30 days in seconds')); $query = id(new DifferentialRevisionQuery()) - ->setViewer($this->getRequest()->getUser()) + ->setViewer($viewer) ->withIsOpen(true) ->withUpdatedEpochBetween($recent, null) ->setOrder(DifferentialRevisionQuery::ORDER_MODIFIED) ->setLimit(10) ->needFlags(true) ->needDrafts(true) - ->needReviewers(true); - - foreach ($path_map as $path => $path_id) { - $query->withPath($repository->getID(), $path_id); - } + ->needReviewers(true) + ->withRepositoryPHIDs( + array( + $repository->getPHID(), + )) + ->withPaths($paths); $results = $query->execute(); // Strip out *this* revision. foreach ($results as $key => $result) { if ($result->getID() == $this->revisionID) { - unset($results[$key]); + unset($results[$key]); + break; } } diff --git a/src/applications/differential/customfield/DifferentialLintField.php b/src/applications/differential/customfield/DifferentialLintField.php index 62b94c51eb..faf065a8d7 100644 --- a/src/applications/differential/customfield/DifferentialLintField.php +++ b/src/applications/differential/customfield/DifferentialLintField.php @@ -38,7 +38,6 @@ final class DifferentialLintField protected function getDiffPropertyKeys() { return array( 'arc:lint', - 'arc:lint-excuse', ); } @@ -84,33 +83,18 @@ final class DifferentialLintField DifferentialDiff $diff, array $messages) { - $colors = array( - DifferentialLintStatus::LINT_NONE => 'grey', - DifferentialLintStatus::LINT_OKAY => 'green', - DifferentialLintStatus::LINT_WARN => 'yellow', - DifferentialLintStatus::LINT_FAIL => 'red', - DifferentialLintStatus::LINT_SKIP => 'blue', - DifferentialLintStatus::LINT_AUTO_SKIP => 'blue', - ); - $icon_color = idx($colors, $diff->getLintStatus(), 'grey'); + $status_value = $diff->getLintStatus(); + $status = DifferentialLintStatus::newStatusFromValue($status_value); - $message = DifferentialRevisionUpdateHistoryView::getDiffLintMessage($diff); - - $excuse = $diff->getProperty('arc:lint-excuse'); - if (strlen($excuse)) { - $excuse = array( - phutil_tag('strong', array(), pht('Excuse:')), - ' ', - phutil_escape_html_newlines($excuse), - ); - } + $status_icon = $status->getIconIcon(); + $status_color = $status->getIconColor(); + $status_name = $status->getName(); $status = id(new PHUIStatusListView()) ->addItem( id(new PHUIStatusItemView()) - ->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color) - ->setTarget($message) - ->setNote($excuse)); + ->setIcon($status_icon, $status_color) + ->setTarget($status_name)); return $status; } diff --git a/src/applications/differential/customfield/DifferentialUnitField.php b/src/applications/differential/customfield/DifferentialUnitField.php index 4c1eb72cc8..6d16b2904d 100644 --- a/src/applications/differential/customfield/DifferentialUnitField.php +++ b/src/applications/differential/customfield/DifferentialUnitField.php @@ -72,29 +72,20 @@ final class DifferentialUnitField } public function renderDiffPropertyViewValue(DifferentialDiff $diff) { + $status_value = $diff->getUnitStatus(); + $status = DifferentialUnitStatus::newStatusFromValue($status_value); - $colors = array( - DifferentialUnitStatus::UNIT_NONE => 'grey', - DifferentialUnitStatus::UNIT_OKAY => 'green', - DifferentialUnitStatus::UNIT_WARN => 'yellow', - DifferentialUnitStatus::UNIT_FAIL => 'red', - DifferentialUnitStatus::UNIT_SKIP => 'blue', - DifferentialUnitStatus::UNIT_AUTO_SKIP => 'blue', - ); - $icon_color = idx($colors, $diff->getUnitStatus(), 'grey'); - - $message = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage( - $diff->getUnitStatus()); + $status_icon = $status->getIconIcon(); + $status_color = $status->getIconColor(); + $status_name = $status->getName(); $status = id(new PHUIStatusListView()) ->addItem( id(new PHUIStatusItemView()) - ->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color) - ->setTarget($message)); + ->setIcon($status_icon, $status_color) + ->setTarget($status_name)); return $status; } - - } diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php index 964cf38722..345fdff72a 100644 --- a/src/applications/differential/editor/DifferentialTransactionEditor.php +++ b/src/applications/differential/editor/DifferentialTransactionEditor.php @@ -340,21 +340,57 @@ final class DifferentialTransactionEditor pht('Failed to load revision from transaction finalization.')); } + $active_diff = $new_revision->getActiveDiff(); + $new_diff_phid = $active_diff->getPHID(); + $object->attachReviewers($new_revision->getReviewers()); - $object->attachActiveDiff($new_revision->getActiveDiff()); + $object->attachActiveDiff($active_diff); $object->attachRepository($new_revision->getRepository()); + $has_new_diff = false; + $should_index_paths = false; + $should_index_hashes = false; + $need_changesets = false; + foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { case DifferentialRevisionUpdateTransaction::TRANSACTIONTYPE: - $diff = $this->requireDiff($xaction->getNewValue(), true); + $need_changesets = true; - // Update these denormalized index tables when we attach a new - // diff to a revision. + $new_diff_phid = $xaction->getNewValue(); + $has_new_diff = true; - $this->updateRevisionHashTable($object, $diff); - $this->updateAffectedPathTable($object, $diff); + $should_index_paths = true; + $should_index_hashes = true; break; + case DifferentialRevisionRepositoryTransaction::TRANSACTIONTYPE: + // The "AffectedPath" table denormalizes the repository, so we + // want to update the index if the repository changes. + + $need_changesets = true; + + $should_index_paths = true; + break; + } + } + + if ($need_changesets) { + $new_diff = $this->requireDiff($new_diff_phid, true); + + if ($should_index_paths) { + id(new DifferentialAffectedPathEngine()) + ->setRevision($object) + ->setDiff($new_diff) + ->updateAffectedPaths(); + } + + if ($should_index_hashes) { + $this->updateRevisionHashTable($object, $new_diff); + } + + if ($has_new_diff) { + $this->ownersDiff = $new_diff; + $this->ownersChangesets = $new_diff->getChangesets(); } } @@ -1255,99 +1291,6 @@ final class DifferentialTransactionEditor return $adapter; } - /** - * Update the table which links Differential revisions to paths they affect, - * so Diffusion can efficiently find pending revisions for a given file. - */ - private function updateAffectedPathTable( - DifferentialRevision $revision, - DifferentialDiff $diff) { - - $repository = $revision->getRepository(); - if (!$repository) { - // The repository where the code lives is untracked. - return; - } - - $path_prefix = null; - - $local_root = $diff->getSourceControlPath(); - if ($local_root) { - // We're in a working copy which supports subdirectory checkouts (e.g., - // SVN) so we need to figure out what prefix we should add to each path - // (e.g., trunk/projects/example/) to get the absolute path from the - // root of the repository. DVCS systems like Git and Mercurial are not - // affected. - - // Normalize both paths and check if the repository root is a prefix of - // the local root. If so, throw it away. Note that this correctly handles - // the case where the remote path is "/". - $local_root = id(new PhutilURI($local_root))->getPath(); - $local_root = rtrim($local_root, '/'); - - $repo_root = id(new PhutilURI($repository->getRemoteURI()))->getPath(); - $repo_root = rtrim($repo_root, '/'); - - if (!strncmp($repo_root, $local_root, strlen($repo_root))) { - $path_prefix = substr($local_root, strlen($repo_root)); - } - } - - $changesets = $diff->getChangesets(); - $paths = array(); - foreach ($changesets as $changeset) { - $paths[] = $path_prefix.'/'.$changeset->getFilename(); - } - - // If this change affected paths, save the changesets so we can apply - // Owners rules to them later. - if ($paths) { - $this->ownersDiff = $diff; - $this->ownersChangesets = $changesets; - } - - // Mark this as also touching all parent paths, so you can see all pending - // changes to any file within a directory. - $all_paths = array(); - foreach ($paths as $local) { - foreach (DiffusionPathIDQuery::expandPathToRoot($local) as $path) { - $all_paths[$path] = true; - } - } - $all_paths = array_keys($all_paths); - - $path_ids = - PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths( - $all_paths); - - $table = new DifferentialAffectedPath(); - $conn_w = $table->establishConnection('w'); - - $sql = array(); - foreach ($path_ids as $path_id) { - $sql[] = qsprintf( - $conn_w, - '(%d, %d, %d, %d)', - $repository->getID(), - $path_id, - time(), - $revision->getID()); - } - - queryfx( - $conn_w, - 'DELETE FROM %T WHERE revisionID = %d', - $table->getTableName(), - $revision->getID()); - foreach (array_chunk($sql, 256) as $chunk) { - queryfx( - $conn_w, - 'INSERT INTO %T (repositoryID, pathID, epoch, revisionID) VALUES %LQ', - $table->getTableName(), - $chunk); - } - } - /** * Update the table connecting revisions to DVCS local hashes, so we can * identify revisions by commit/tree hashes. diff --git a/src/applications/differential/engine/DifferentialAffectedPathEngine.php b/src/applications/differential/engine/DifferentialAffectedPathEngine.php new file mode 100644 index 0000000000..0ac85e5900 --- /dev/null +++ b/src/applications/differential/engine/DifferentialAffectedPathEngine.php @@ -0,0 +1,136 @@ +revision = $revision; + return $this; + } + + public function getRevision() { + return $this->revision; + } + + public function setDiff(DifferentialDiff $diff) { + $this->diff = $diff; + return $this; + } + + public function getDiff() { + return $this->diff; + } + + public function updateAffectedPaths() { + $revision = $this->getRevision(); + $diff = $this->getDiff(); + $repository = $revision->getRepository(); + + if ($repository) { + $repository_id = $repository->getID(); + } else { + $repository_id = null; + } + + $paths = $this->getAffectedPaths(); + + $path_ids = + PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths( + $paths); + + $table = new DifferentialAffectedPath(); + $conn = $table->establishConnection('w'); + + $sql = array(); + foreach ($path_ids as $path_id) { + $sql[] = qsprintf( + $conn, + '(%nd, %d, %d)', + $repository_id, + $path_id, + $revision->getID()); + } + + queryfx( + $conn, + 'DELETE FROM %R WHERE revisionID = %d', + $table, + $revision->getID()); + if ($sql) { + foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { + queryfx( + $conn, + 'INSERT INTO %R (repositoryID, pathID, revisionID) VALUES %LQ', + $table, + $chunk); + } + } + } + + public function destroyAffectedPaths() { + $revision = $this->getRevision(); + + $table = new DifferentialAffectedPath(); + $conn = $table->establishConnection('w'); + + queryfx( + $conn, + 'DELETE FROM %R WHERE revisionID = %d', + $table, + $revision->getID()); + } + + public function getAffectedPaths() { + $revision = $this->getRevision(); + $diff = $this->getDiff(); + $repository = $revision->getRepository(); + + $path_prefix = null; + if ($repository) { + $local_root = $diff->getSourceControlPath(); + if ($local_root) { + // We're in a working copy which supports subdirectory checkouts (e.g., + // SVN) so we need to figure out what prefix we should add to each path + // (e.g., trunk/projects/example/) to get the absolute path from the + // root of the repository. DVCS systems like Git and Mercurial are not + // affected. + + // Normalize both paths and check if the repository root is a prefix of + // the local root. If so, throw it away. Note that this correctly + // handles the case where the remote path is "/". + $local_root = id(new PhutilURI($local_root))->getPath(); + $local_root = rtrim($local_root, '/'); + + $repo_root = id(new PhutilURI($repository->getRemoteURI()))->getPath(); + $repo_root = rtrim($repo_root, '/'); + + if (!strncmp($repo_root, $local_root, strlen($repo_root))) { + $path_prefix = substr($local_root, strlen($repo_root)); + } + } + } + + $changesets = $diff->getChangesets(); + + $paths = array(); + foreach ($changesets as $changeset) { + $paths[] = $path_prefix.'/'.$changeset->getFilename(); + } + + // Mark this as also touching all parent paths, so you can see all pending + // changes to any file within a directory. + $all_paths = array(); + foreach ($paths as $local) { + foreach (DiffusionPathIDQuery::expandPathToRoot($local) as $path) { + $all_paths[$path] = true; + } + } + $all_paths = array_keys($all_paths); + + return $all_paths; + } + +} diff --git a/src/applications/differential/query/DifferentialRevisionQuery.php b/src/applications/differential/query/DifferentialRevisionQuery.php index 0f6d5d944b..75d3e052e4 100644 --- a/src/applications/differential/query/DifferentialRevisionQuery.php +++ b/src/applications/differential/query/DifferentialRevisionQuery.php @@ -8,8 +8,6 @@ final class DifferentialRevisionQuery extends PhabricatorCursorPagedPolicyAwareQuery { - private $pathIDs = array(); - private $authors = array(); private $draftAuthors = array(); private $ccs = array(); @@ -27,6 +25,7 @@ final class DifferentialRevisionQuery private $createdEpochMin; private $createdEpochMax; private $noReviewers; + private $paths; const ORDER_MODIFIED = 'order-modified'; const ORDER_CREATED = 'order-created'; @@ -43,22 +42,15 @@ final class DifferentialRevisionQuery /* -( Query Configuration )------------------------------------------------ */ - /** - * Filter results to revisions which affect a Diffusion path ID in a given - * repository. You can call this multiple times to select revisions for - * several paths. + * Find revisions affecting one or more items in a list of paths. * - * @param int Diffusion repository ID. - * @param int Diffusion path ID. + * @param list List of file paths. * @return this * @task config */ - public function withPath($repository_id, $path_id) { - $this->pathIDs[] = array( - 'repositoryID' => $repository_id, - 'pathID' => $path_id, - ); + public function withPaths(array $paths) { + $this->paths = $paths; return $this; } @@ -568,12 +560,13 @@ final class DifferentialRevisionQuery */ private function buildJoinsClause(AphrontDatabaseConnection $conn) { $joins = array(); - if ($this->pathIDs) { + + if ($this->paths) { $path_table = new DifferentialAffectedPath(); $joins[] = qsprintf( $conn, - 'JOIN %T p ON p.revisionID = r.id', - $path_table->getTableName()); + 'JOIN %R paths ON paths.revisionID = r.id', + $path_table); } if ($this->commitHashes) { @@ -635,20 +628,46 @@ final class DifferentialRevisionQuery * @task internal */ protected function buildWhereClause(AphrontDatabaseConnection $conn) { + $viewer = $this->getViewer(); $where = array(); - if ($this->pathIDs) { - $path_clauses = array(); - $repo_info = igroup($this->pathIDs, 'repositoryID'); - foreach ($repo_info as $repository_id => $paths) { - $path_clauses[] = qsprintf( - $conn, - '(p.repositoryID = %d AND p.pathID IN (%Ld))', - $repository_id, - ipull($paths, 'pathID')); + if ($this->paths !== null) { + $paths = $this->paths; + + $path_map = id(new DiffusionPathIDQuery($paths)) + ->loadPathIDs(); + + if (!$path_map) { + // If none of the paths have entries in the PathID table, we can not + // possibly find any revisions affecting them. + throw new PhabricatorEmptyQueryException(); + } + + $where[] = qsprintf( + $conn, + 'paths.pathID IN (%Ld)', + array_fuse($path_map)); + + // If we have repository PHIDs, additionally constrain this query to + // try to help MySQL execute it efficiently. + if ($this->repositoryPHIDs !== null) { + $repositories = id(new PhabricatorRepositoryQuery()) + ->setViewer($viewer) + ->setParentQuery($this) + ->withPHIDs($this->repositoryPHIDs) + ->execute(); + + if (!$repositories) { + throw new PhabricatorEmptyQueryException(); + } + + $repository_ids = mpull($repositories, 'getID'); + + $where[] = qsprintf( + $conn, + 'paths.repositoryID IN (%Ld)', + $repository_ids); } - $path_clauses = qsprintf($conn, '%LO', $path_clauses); - $where[] = $path_clauses; } if ($this->authors) { @@ -778,7 +797,9 @@ final class DifferentialRevisionQuery */ protected function shouldGroupQueryResultRows() { - if (count($this->pathIDs) > 1) { + if ($this->paths) { + // (If we have exactly one repository and exactly one path, we don't + // technically need to group, but it's simpler to always group.) return true; } diff --git a/src/applications/differential/query/DifferentialRevisionSearchEngine.php b/src/applications/differential/query/DifferentialRevisionSearchEngine.php index 2bf196db0c..1ec5265630 100644 --- a/src/applications/differential/query/DifferentialRevisionSearchEngine.php +++ b/src/applications/differential/query/DifferentialRevisionSearchEngine.php @@ -57,6 +57,10 @@ final class DifferentialRevisionSearchEngine $map['modifiedEnd']); } + if ($map['affectedPaths']) { + $query->withPaths($map['affectedPaths']); + } + return $query; } @@ -118,6 +122,12 @@ final class DifferentialRevisionSearchEngine ->setIsHidden(true) ->setDescription( pht('Find revisions modified at or before a particular time.')), + id(new PhabricatorSearchStringListField()) + ->setKey('affectedPaths') + ->setLabel(pht('Affected Paths')) + ->setDescription( + pht('Search for revisions affecting particular paths.')) + ->setIsHidden(true), ); } diff --git a/src/applications/differential/storage/DifferentialAffectedPath.php b/src/applications/differential/storage/DifferentialAffectedPath.php index b8de95629b..400a09fe82 100644 --- a/src/applications/differential/storage/DifferentialAffectedPath.php +++ b/src/applications/differential/storage/DifferentialAffectedPath.php @@ -8,7 +8,6 @@ final class DifferentialAffectedPath extends DifferentialDAO { protected $repositoryID; protected $pathID; - protected $epoch; protected $revisionID; protected function getConfiguration() { @@ -16,15 +15,16 @@ final class DifferentialAffectedPath extends DifferentialDAO { self::CONFIG_TIMESTAMPS => false, self::CONFIG_COLUMN_SCHEMA => array( 'id' => null, + 'repositoryID' => 'id?', ), self::CONFIG_KEY_SCHEMA => array( 'PRIMARY' => null, - 'repositoryID' => array( - 'columns' => array('repositoryID', 'pathID', 'epoch'), - ), 'revisionID' => array( 'columns' => array('revisionID'), ), + 'key_path' => array( + 'columns' => array('pathID', 'repositoryID'), + ), ), ) + parent::getConfiguration(); } diff --git a/src/applications/differential/storage/DifferentialRevision.php b/src/applications/differential/storage/DifferentialRevision.php index 15cf219f7b..23f5db9151 100644 --- a/src/applications/differential/storage/DifferentialRevision.php +++ b/src/applications/differential/storage/DifferentialRevision.php @@ -1022,16 +1022,9 @@ final class DifferentialRevision extends DifferentialDAO $engine->destroyObject($diff); } - $conn_w = $this->establishConnection('w'); - - // we have to do paths a little differently as they do not have - // an id or phid column for delete() to act on - $dummy_path = new DifferentialAffectedPath(); - queryfx( - $conn_w, - 'DELETE FROM %T WHERE revisionID = %d', - $dummy_path->getTableName(), - $this->getID()); + id(new DifferentialAffectedPathEngine()) + ->setRevision($this) + ->destroyAffectedPaths(); $viewstate_query = id(new DifferentialViewStateQuery()) ->setViewer($viewer) diff --git a/src/applications/differential/view/DifferentialChangesetDetailView.php b/src/applications/differential/view/DifferentialChangesetDetailView.php index d23ddc62a4..9d04d081ac 100644 --- a/src/applications/differential/view/DifferentialChangesetDetailView.php +++ b/src/applications/differential/view/DifferentialChangesetDetailView.php @@ -246,6 +246,7 @@ final class DifferentialChangesetDetailView extends AphrontView { 'displayPath' => hsprintf('%s', $display_parts), 'icon' => $display_icon, 'pathParts' => $path_parts, + 'symbolPath' => $display_filename, 'pathIconIcon' => $changeset->getPathIconIcon(), 'pathIconColor' => $changeset->getPathIconColor(), diff --git a/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php b/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php index 07ca983bc0..2e074a2f7e 100644 --- a/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php +++ b/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php @@ -139,29 +139,8 @@ final class DifferentialRevisionUpdateHistoryView extends AphrontView { } if ($diff) { - $unit_status = idx( - $this->unitStatus, - $diff->getPHID(), - $diff->getUnitStatus()); - - $lint = self::renderDiffLintStar($row['obj']); - $lint = phutil_tag( - 'div', - array( - 'class' => 'lintunit-star', - 'title' => self::getDiffLintMessage($diff), - ), - $lint); - - $unit = self::renderDiffUnitStar($unit_status); - $unit = phutil_tag( - 'div', - array( - 'class' => 'lintunit-star', - 'title' => self::getDiffUnitMessage($unit_status), - ), - $unit); - + $lint = $this->newLintStatusView($diff); + $unit = $this->newUnitStatusView($diff); $base = $this->renderBaseRevision($diff); } else { $lint = null; @@ -282,86 +261,6 @@ final class DifferentialRevisionUpdateHistoryView extends AphrontView { return $content; } - const STAR_NONE = 'none'; - const STAR_OKAY = 'okay'; - const STAR_WARN = 'warn'; - const STAR_FAIL = 'fail'; - const STAR_SKIP = 'skip'; - - private static function renderDiffLintStar(DifferentialDiff $diff) { - static $map = array( - DifferentialLintStatus::LINT_NONE => self::STAR_NONE, - DifferentialLintStatus::LINT_OKAY => self::STAR_OKAY, - DifferentialLintStatus::LINT_WARN => self::STAR_WARN, - DifferentialLintStatus::LINT_FAIL => self::STAR_FAIL, - DifferentialLintStatus::LINT_SKIP => self::STAR_SKIP, - DifferentialLintStatus::LINT_AUTO_SKIP => self::STAR_SKIP, - ); - - $star = idx($map, $diff->getLintStatus(), self::STAR_FAIL); - - return self::renderDiffStar($star); - } - - private static function renderDiffUnitStar($unit_status) { - static $map = array( - DifferentialUnitStatus::UNIT_NONE => self::STAR_NONE, - DifferentialUnitStatus::UNIT_OKAY => self::STAR_OKAY, - DifferentialUnitStatus::UNIT_WARN => self::STAR_WARN, - DifferentialUnitStatus::UNIT_FAIL => self::STAR_FAIL, - DifferentialUnitStatus::UNIT_SKIP => self::STAR_SKIP, - DifferentialUnitStatus::UNIT_AUTO_SKIP => self::STAR_SKIP, - ); - $star = idx($map, $unit_status, self::STAR_FAIL); - - return self::renderDiffStar($star); - } - - public static function getDiffLintMessage(DifferentialDiff $diff) { - switch ($diff->getLintStatus()) { - case DifferentialLintStatus::LINT_NONE: - return pht('No Linters Available'); - case DifferentialLintStatus::LINT_OKAY: - return pht('Lint OK'); - case DifferentialLintStatus::LINT_WARN: - return pht('Lint Warnings'); - case DifferentialLintStatus::LINT_FAIL: - return pht('Lint Errors'); - case DifferentialLintStatus::LINT_SKIP: - return pht('Lint Skipped'); - case DifferentialLintStatus::LINT_AUTO_SKIP: - return pht('Automatic diff as part of commit; lint not applicable.'); - } - return pht('Unknown'); - } - - public static function getDiffUnitMessage($unit_status) { - switch ($unit_status) { - case DifferentialUnitStatus::UNIT_NONE: - return pht('No Unit Test Coverage'); - case DifferentialUnitStatus::UNIT_OKAY: - return pht('Unit Tests OK'); - case DifferentialUnitStatus::UNIT_WARN: - return pht('Unit Test Warnings'); - case DifferentialUnitStatus::UNIT_FAIL: - return pht('Unit Test Errors'); - case DifferentialUnitStatus::UNIT_SKIP: - return pht('Unit Tests Skipped'); - case DifferentialUnitStatus::UNIT_AUTO_SKIP: - return pht( - 'Automatic diff as part of commit; unit tests not applicable.'); - } - return pht('Unknown'); - } - - private static function renderDiffStar($star) { - $class = 'diff-star-'.$star; - return phutil_tag( - 'span', - array('class' => $class), - "\xE2\x98\x85"); - } - private function renderBaseRevision(DifferentialDiff $diff) { switch ($diff->getSourceControlSystem()) { case 'git': @@ -401,4 +300,42 @@ final class DifferentialRevisionUpdateHistoryView extends AphrontView { } return $link; } + + private function newLintStatusView(DifferentialDiff $diff) { + $value = $diff->getLintStatus(); + $status = DifferentialLintStatus::newStatusFromValue($value); + + $icon = $status->getIconIcon(); + $color = $status->getIconColor(); + $name = $status->getName(); + + return $this->newDiffStatusIconView($icon, $color, $name); + } + + private function newUnitStatusView(DifferentialDiff $diff) { + $value = $diff->getUnitStatus(); + + // NOTE: We may be overriding the value on the diff with a value from + // Harbormaster. + $value = idx($this->unitStatus, $diff->getPHID(), $value); + + $status = DifferentialUnitStatus::newStatusFromValue($value); + + $icon = $status->getIconIcon(); + $color = $status->getIconColor(); + $name = $status->getName(); + + return $this->newDiffStatusIconView($icon, $color, $name); + } + + private function newDiffStatusIconView($icon, $color, $name) { + return id(new PHUIIconView()) + ->setIcon($icon, $color) + ->addSigil('has-tooltip') + ->setMetadata( + array( + 'tip' => $name, + )); + } + } diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php index 76efa41866..549c0d649f 100644 --- a/src/applications/diffusion/controller/DiffusionBrowseController.php +++ b/src/applications/diffusion/controller/DiffusionBrowseController.php @@ -938,17 +938,12 @@ final class DiffusionBrowseController extends DiffusionController { $repository = $drequest->getRepository(); $path = $drequest->getPath(); - $path_map = id(new DiffusionPathIDQuery(array($path)))->loadPathIDs(); - $path_id = idx($path_map, $path); - if (!$path_id) { - return null; - } - $recent = (PhabricatorTime::getNow() - phutil_units('30 days in seconds')); $revisions = id(new DifferentialRevisionQuery()) ->setViewer($viewer) - ->withPath($repository->getID(), $path_id) + ->withPaths(array($path)) + ->withRepositoryPHIDs(array($repository->getPHID())) ->withIsOpen(true) ->withUpdatedEpochBetween($recent, null) ->setOrder(DifferentialRevisionQuery::ORDER_MODIFIED) @@ -963,7 +958,7 @@ final class DiffusionBrowseController extends DiffusionController { } $header = id(new PHUIHeaderView()) - ->setHeader(pht('Recently Open Revisions')); + ->setHeader(pht('Recent Open Revisions')); $list = id(new DifferentialRevisionListView()) ->setViewer($viewer) diff --git a/src/applications/diffusion/controller/DiffusionSymbolController.php b/src/applications/diffusion/controller/DiffusionSymbolController.php index b855f967db..c4ce99fa87 100644 --- a/src/applications/diffusion/controller/DiffusionSymbolController.php +++ b/src/applications/diffusion/controller/DiffusionSymbolController.php @@ -4,7 +4,10 @@ final class DiffusionSymbolController extends DiffusionController { public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); + + // See T13638 for discussion of escaping. $name = $request->getURIData('name'); + $name = phutil_unescape_uri_path_component($name); $query = id(new DiffusionSymbolQuery()) ->setViewer($viewer) diff --git a/src/applications/diffusion/engineextension/DiffusionDatasourceEngineExtension.php b/src/applications/diffusion/engineextension/DiffusionDatasourceEngineExtension.php index 9678b627b1..70e5b35726 100644 --- a/src/applications/diffusion/engineextension/DiffusionDatasourceEngineExtension.php +++ b/src/applications/diffusion/engineextension/DiffusionDatasourceEngineExtension.php @@ -66,12 +66,12 @@ final class DiffusionDatasourceEngineExtension $parts = null; if (preg_match('/(.*)(?:\\.|::|->)(.*)/', $symbol, $parts)) { return urisprintf( - '/diffusion/symbol/%s/?jump=true&context=%s', + '/diffusion/symbol/%p/?jump=true&context=%s', $parts[2], $parts[1]); } else { return urisprintf( - '/diffusion/symbol/%s/?jump=true', + '/diffusion/symbol/%p/?jump=true', $symbol); } } diff --git a/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php index 86ff93816c..f657a81641 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php @@ -89,7 +89,7 @@ final class DiffusionRepositoryStorageManagementPanel AlmanacClusterRepositoryServiceType::SERVICETYPE, )) ->withPHIDs(array($service_phid)) - ->needBindings(true) + ->needActiveBindings(true) ->executeOne(); if (!$service) { // TODO: Viewer may not have permission to see the service, or it may @@ -104,7 +104,7 @@ final class DiffusionRepositoryStorageManagementPanel $rows = array(); if ($service) { - $bindings = $service->getBindings(); + $bindings = $service->getActiveBindings(); $bindings = mgroup($bindings, 'getDevicePHID'); // This is an unusual read which always comes from the master. @@ -117,29 +117,19 @@ final class DiffusionRepositoryStorageManagementPanel $versions = mpull($versions, null, 'getDevicePHID'); - // List enabled devices first, then sort devices in each group by name. $sort = array(); foreach ($bindings as $key => $binding_group) { - $all_disabled = $this->isDisabledGroup($binding_group); - $sort[$key] = id(new PhutilSortVector()) - ->addInt($all_disabled ? 1 : 0) ->addString(head($binding_group)->getDevice()->getName()); } $sort = msortv($sort, 'getSelf'); $bindings = array_select_keys($bindings, array_keys($sort)) + $bindings; foreach ($bindings as $binding_group) { - $all_disabled = $this->isDisabledGroup($binding_group); $any_binding = head($binding_group); - if ($all_disabled) { - $binding_icon = 'fa-times grey'; - $binding_tip = pht('Disabled'); - } else { - $binding_icon = 'fa-folder-open green'; - $binding_tip = pht('Active'); - } + $binding_icon = 'fa-folder-open green'; + $binding_tip = pht('Active'); $binding_icon = id(new PHUIIconView()) ->setIcon($binding_icon) @@ -376,17 +366,4 @@ final class DiffusionRepositoryStorageManagementPanel return $box_view; } - - private function isDisabledGroup(array $binding_group) { - assert_instances_of($binding_group, 'AlmanacBinding'); - - foreach ($binding_group as $binding) { - if (!$binding->getIsDisabled()) { - return false; - } - } - - return true; - } - } diff --git a/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php index 12ccf8a343..290fae8c63 100644 --- a/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php +++ b/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php @@ -34,7 +34,7 @@ final class DrydockAlmanacServiceHostBlueprintImplementation DrydockBlueprint $blueprint, DrydockLease $lease) { $services = $this->loadServices($blueprint); - $bindings = $this->loadAllBindings($services); + $bindings = $this->getActiveBindings($services); if (!$bindings) { // If there are no devices bound to the services for this blueprint, @@ -222,7 +222,7 @@ final class DrydockAlmanacServiceHostBlueprintImplementation ->setViewer($viewer) ->withPHIDs($service_phids) ->withServiceTypes($this->getAlmanacServiceTypes()) - ->needBindings(true) + ->needActiveBindings(true) ->execute(); $services = mpull($services, null, 'getPHID'); @@ -242,9 +242,9 @@ final class DrydockAlmanacServiceHostBlueprintImplementation return $this->services; } - private function loadAllBindings(array $services) { + private function getActiveBindings(array $services) { assert_instances_of($services, 'AlmanacService'); - $bindings = array_mergev(mpull($services, 'getBindings')); + $bindings = array_mergev(mpull($services, 'getActiveBindings')); return mpull($bindings, null, 'getPHID'); } @@ -271,15 +271,10 @@ final class DrydockAlmanacServiceHostBlueprintImplementation $allocated_phids = array_fuse($allocated_phids); $services = $this->loadServices($blueprint); - $bindings = $this->loadAllBindings($services); + $bindings = $this->getActiveBindings($services); $free = array(); foreach ($bindings as $binding) { - // Don't consider disabled bindings to be available. - if ($binding->getIsDisabled()) { - continue; - } - if (empty($allocated_phids[$binding->getPHID()])) { $free[] = $binding; } diff --git a/src/applications/fund/storage/FundInitiative.php b/src/applications/fund/storage/FundInitiative.php index 1ebbb35ef1..7503083c23 100644 --- a/src/applications/fund/storage/FundInitiative.php +++ b/src/applications/fund/storage/FundInitiative.php @@ -22,7 +22,6 @@ final class FundInitiative extends FundDAO protected $editPolicy; protected $status; protected $totalAsCurrency; - protected $mailKey; private $projectPHIDs = self::ATTACHABLE; @@ -62,7 +61,6 @@ final class FundInitiative extends FundDAO 'status' => 'text32', 'merchantPHID' => 'phid?', 'totalAsCurrency' => 'text64', - 'mailKey' => 'bytes20', ), self::CONFIG_APPLICATION_SERIALIZERS => array( 'totalAsCurrency' => new PhortuneCurrencySerializer(), @@ -78,8 +76,8 @@ final class FundInitiative extends FundDAO ) + parent::getConfiguration(); } - public function generatePHID() { - return PhabricatorPHID::generateNewPHID(FundInitiativePHIDType::TYPECONST); + public function getPHIDType() { + return FundInitiativePHIDType::TYPECONST; } public function getMonogram() { @@ -103,13 +101,6 @@ final class FundInitiative extends FundDAO return ($this->getStatus() == self::STATUS_CLOSED); } - public function save() { - if (!$this->mailKey) { - $this->mailKey = Filesystem::readRandomCharacters(20); - } - return parent::save(); - } - /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/phid/PhabricatorObjectHandle.php b/src/applications/phid/PhabricatorObjectHandle.php index 966c1a7db9..95c40ffa2b 100644 --- a/src/applications/phid/PhabricatorObjectHandle.php +++ b/src/applications/phid/PhabricatorObjectHandle.php @@ -197,6 +197,10 @@ final class PhabricatorObjectHandle return $this->status; } + public function isClosed() { + return ($this->status === self::STATUS_CLOSED); + } + public function setFullName($full_name) { $this->fullName = $full_name; return $this; diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php index 2e2c3fcc92..c3de833a1f 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php @@ -61,7 +61,7 @@ final class PhabricatorRepositoryManagementClusterizeWorkflow array( AlmanacClusterRepositoryServiceType::SERVICETYPE, )) - ->needBindings(true) + ->needActiveBindings(true) ->executeOne(); if (!$service) { throw new PhutilArgumentUsageException( diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 5e169fda6a..8c98dd8756 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -1151,7 +1151,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO /** * Get a parsed object representation of the repository's remote URI.. * - * @return wild A @{class@libphutil:PhutilURI}. + * @return wild A @{class@arcanist:PhutilURI}. * @task uri */ public function getRemoteURIObject() { @@ -2109,7 +2109,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO throw new Exception( pht( 'The Almanac service for this repository is not bound to any '. - 'interfaces.')); + 'active interfaces.')); } $uris = array(); @@ -2531,7 +2531,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO $service = id(new AlmanacServiceQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withPHIDs(array($service_phid)) - ->needBindings(true) + ->needActiveBindings(true) ->needProperties(true) ->executeOne(); if (!$service) { diff --git a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php index 3af4f349fa..aecc44699c 100644 --- a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php +++ b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php @@ -169,8 +169,8 @@ this: { ... "constraints": { - "authors": ["PHID-USER-1111", "PHID-USER-2222"], - "statuses": ["open", "closed"], + "authorPHIDs": ["PHID-USER-1111", "PHID-USER-2222"], + "flavors": ["cherry", "orange"], ... }, ... diff --git a/src/applications/system/events/PhabricatorSystemDebugUIEventListener.php b/src/applications/system/events/PhabricatorSystemDebugUIEventListener.php index 5bfb6843c4..58b390fb23 100644 --- a/src/applications/system/events/PhabricatorSystemDebugUIEventListener.php +++ b/src/applications/system/events/PhabricatorSystemDebugUIEventListener.php @@ -44,6 +44,16 @@ final class PhabricatorSystemDebugUIEventListener ->setName(pht('View Hovercard')) ->setHref(urisprintf('/search/hovercard/?names=%s', $phid)); + if ($object instanceof DifferentialRevision) { + $submenu[] = id(new PhabricatorActionView()) + ->setIcon('fa-database') + ->setName(pht('View Affected Path Index')) + ->setHref( + urisprintf( + '/differential/revision/paths/%s/', + $object->getID())); + } + $developer_action = id(new PhabricatorActionView()) ->setName(pht('Advanced/Developer...')) ->setIcon('fa-magic') diff --git a/src/docs/contributor/adding_new_classes.diviner b/src/docs/contributor/adding_new_classes.diviner index beb4567210..ea932eba5c 100644 --- a/src/docs/contributor/adding_new_classes.diviner +++ b/src/docs/contributor/adding_new_classes.diviner @@ -21,7 +21,7 @@ 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 @{class@arcanist:ArcanistWorkflow}. It discovers available locales -by looking at all of the subclasses of @{class@libphutil:PhutilLocale}. +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 @@ -49,8 +49,8 @@ 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 imposes a small performance penalty compared to using a library. -This directory exists in all libphutil libraries, so you can find similar -directories in `arcanist/src/extensions/` and `libphutil/src/extensions/`. +This directory exists in all libphutil libraries, 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/`. @@ -171,8 +171,8 @@ performing static analysis. NOTE: If Phabricator isn't located next to your custom library, specify a path which actually points to the `phabricator/` directory. -You do not need to declare dependencies on `arcanist` or `libphutil`, -since `arc liberate` automatically loads them. +You do not need to declare dependencies on `arcanist`, since `arc liberate` +automatically loads them. Finally, edit your Phabricator config to tell it to load your library at runtime, by adding it to `load-libraries`: @@ -206,8 +206,8 @@ This will automatically regenerate the static map of the library. What You Can Extend And Invoke ============================== -libphutil, Arcanist and Phabricator are strict about extensibility of classes -and visibility of methods and properties. Most classes are marked `final`, and +Arcanist and Phabricator are strict about extensibility of classes and +visibility of methods and properties. Most classes are marked `final`, and methods have the minimum required visibility (protected or private). The goal of this strictness is to make it clear what you can safely extend, access, and invoke, so your code will keep working as the upstream changes. @@ -215,8 +215,8 @@ invoke, so your code will keep working as the upstream changes. IMPORTANT: We'll still break APIs frequently. The upstream does not support extension development, and none of these APIs are stable. -When developing libraries to work with libphutil, Arcanist and Phabricator, you -should respect method and property visibility. +When developing libraries to work with Arcanist and Phabricator, you should +respect method and property visibility. If you want to add features but can't figure out how to do it without changing Phabricator code, here are some approaches you may be able to take: diff --git a/src/docs/contributor/bug_reports.diviner b/src/docs/contributor/bug_reports.diviner index fcf3eac5c5..59cb5fe715 100644 --- a/src/docs/contributor/bug_reports.diviner +++ b/src/docs/contributor/bug_reports.diviner @@ -47,10 +47,9 @@ Before you file a report, here are some common solutions to problems: issues to be fixed in less than 24 hours, so even if you've updated recently you should update again. If you aren't sure how to update, see the next section. - - **Update Libraries**: Make sure `libphutil/`, `arcanist/` and - `phabricator/` are all up to date. Users often update `phabricator/` but - forget to update `arcanist/` or `libphutil/`. When you update, make sure you - update all three libraries. + - **Update Libraries**: Make sure `arcanist/` and `phabricator/` are all up + to date. Users often update `phabricator/` but forget to update `arcanist/`. + When you update, make sure you update all three libraries. - **Restart Apache or PHP-FPM**: Phabricator uses caches which don't get reset until you restart Apache or PHP-FPM. After updating, make sure you restart. diff --git a/src/docs/contributor/contrib_intro.diviner b/src/docs/contributor/contrib_intro.diviner index e298edc080..59ad9b44df 100644 --- a/src/docs/contributor/contrib_intro.diviner +++ b/src/docs/contributor/contrib_intro.diviner @@ -1,7 +1,7 @@ @title Contributor Introduction @group contrib -Introduction to contributing to Phabricator, Arcanist and libphutil. +Introduction to contributing to Phabricator and Arcanist. Overview ======== diff --git a/src/docs/contributor/contributing_code.diviner b/src/docs/contributor/contributing_code.diviner index faaf5b16da..accb55c8d1 100644 --- a/src/docs/contributor/contributing_code.diviner +++ b/src/docs/contributor/contributing_code.diviner @@ -212,9 +212,9 @@ outside of our comfort zone. Writing and Submitting Patches ================== -To actually submit a patch, run `arc diff` in `phabricator/`, `arcanist/`, or -`libphutil/`. When executed in these directories, `arc` should automatically -talk to the upstream install. You can add `epriestley` as a reviewer. +To actually submit a patch, run `arc diff` in `phabricator/` or `arcanist/`. +When executed in these directories, `arc` should automatically talk to the +upstream install. You can add `epriestley` 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 diff --git a/src/docs/contributor/general_coding_standards.diviner b/src/docs/contributor/general_coding_standards.diviner index 45081231bb..9b151312fd 100644 --- a/src/docs/contributor/general_coding_standards.diviner +++ b/src/docs/contributor/general_coding_standards.diviner @@ -2,7 +2,7 @@ @group standards This document is a general coding standard for contributing to Phabricator, -Arcanist, libphutil and Diviner. +Arcanist, and Diviner. = Overview = @@ -136,7 +136,7 @@ handling and makes it easier to get right than wrong: Filesystem::writeFile('file.bak', $data); // Best do_something_dangerous(); -See @{article@libphutil:Command Execution} for details on the APIs used in this +See @{article@arcanist:Command Execution} for details on the APIs used in this example. = Documentation, Comments and Formatting = diff --git a/src/docs/contributor/internationalization.diviner b/src/docs/contributor/internationalization.diviner index 9c78eb60fb..99c35e675e 100644 --- a/src/docs/contributor/internationalization.diviner +++ b/src/docs/contributor/internationalization.diviner @@ -44,7 +44,7 @@ For instructions on adding new classes, see Writing Translatable Code ========================= -Strings are marked for translation with @{function@libphutil:pht}. +Strings are marked for translation with @{function@arcanist:pht}. The `pht()` function takes a string (and possibly some parameters) and returns the translated version of that string in the current viewer's locale, if a @@ -68,7 +68,7 @@ the major rules are: - Use parameters to create strings containing user names, object names, etc. - Translate full sentences, not sentence fragments. - Let the translation framework handle plural rules. - - Use @{class@libphutil:PhutilNumber} for numbers. + - Use @{class@arcanist:PhutilNumber} for numbers. - Let the translation framework handle subject gender rules. - Translate all human-readable text, even exceptions and error messages. @@ -323,7 +323,7 @@ pronouns (like "he" and "she") but there are more complex rules in other languages, and languages like Czech also require verb agreement. When a parameter refers to a gendered person, pass an object which implements -@{interface@libphutil:PhutilPerson} to `pht()` so translators can provide +@{interface@arcanist:PhutilPerson} to `pht()` so translators can provide gendered translation variants. ```lang=php @@ -361,8 +361,8 @@ all human-readable text. This rule is unambiguous and easy to follow. In cases where similar error or exception text is often repeated, it is probably appropriate to define an exception for that category of error rather than write the text out repeatedly, anyway. Two examples are -@{class@libphutil:PhutilInvalidStateException} and -@{class@libphutil:PhutilMethodNotImplementedException}, which mostly exist to +@{class@arcanist:PhutilInvalidStateException} and +@{class@arcanist:PhutilMethodNotImplementedException}, which mostly exist to produce a consistent message about a common error state in a convenient way. There are a handful of error strings in the codebase which may be used before diff --git a/src/docs/contributor/php_coding_standards.diviner b/src/docs/contributor/php_coding_standards.diviner index ff62ebe98e..a14acf17f2 100644 --- a/src/docs/contributor/php_coding_standards.diviner +++ b/src/docs/contributor/php_coding_standards.diviner @@ -2,13 +2,13 @@ @group standards This document describes PHP coding standards for Phabricator and related -projects (like Arcanist and libphutil). +projects (like Arcanist). = Overview = This document outlines technical and style guidelines which are followed in -libphutil. Contributors should also follow these guidelines. Many of these -guidelines are automatically enforced by lint. +Phabricator 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 basically copy-pasted them. If you are already familiar with the Facebook diff --git a/src/docs/contributor/rendering_html.diviner b/src/docs/contributor/rendering_html.diviner index 70401d8bcf..a8fe5a899d 100644 --- a/src/docs/contributor/rendering_html.diviner +++ b/src/docs/contributor/rendering_html.diviner @@ -13,13 +13,13 @@ pipeline, and the browser will treat it as plain text, not HTML. This document describes the right way to build HTML components so they are safe from XSS and render correctly. Broadly: - - Use @{function@libphutil:phutil_tag} (and @{function:javelin_tag}) to build + - Use @{function@arcanist:phutil_tag} (and @{function:javelin_tag}) to build tags. - - Use @{function@libphutil:hsprintf} where @{function@libphutil:phutil_tag} + - Use @{function@arcanist:hsprintf} where @{function@arcanist:phutil_tag} is awkward. - Combine elements with arrays, not string concatenation. - @{class:AphrontView} subclasses should return a - @{class@libphutil:PhutilSafeHTML} object from their `render()` method. + @{class@arcanist:PhutilSafeHTML} object from their `render()` method. - @{class:AphrontView} subclasses act like tags when rendering. - @{function:pht} has some special rules. - There are some other things that you should be aware of. @@ -28,7 +28,7 @@ See below for discussion. = Building Tags: phutil_tag() = -Build HTML tags with @{function@libphutil:phutil_tag}. For example: +Build HTML tags with @{function@arcanist:phutil_tag}. For example: phutil_tag( 'div', @@ -37,10 +37,10 @@ Build HTML tags with @{function@libphutil:phutil_tag}. For example: ), $content); -@{function@libphutil:phutil_tag} will properly escape the content and all the -attributes, and return a @{class@libphutil:PhutilSafeHTML} object. The rendering +@{function@arcanist:phutil_tag} will properly escape the content and all the +attributes, and return a @{class@arcanist:PhutilSafeHTML} object. The rendering pipeline knows that this object represents a properly escaped HTML tag. This -allows @{function@libphutil:phutil_tag} to render tags with other tags as +allows @{function@arcanist:phutil_tag} to render tags with other tags as content correctly (without double-escaping): phutil_tag( @@ -52,14 +52,14 @@ content correctly (without double-escaping): $content)); In Phabricator, the @{function:javelin_tag} function is similar to -@{function@libphutil:phutil_tag}, but provides special handling for the +@{function@arcanist:phutil_tag}, but provides special handling for the `sigil` and `meta` attributes. = Building Blocks: hsprintf() = -Sometimes, @{function@libphutil:phutil_tag} can be particularly awkward to -use. You can use @{function@libphutil:hsprintf} to build larger and more -complex blocks of HTML, when @{function@libphutil:phutil_tag} is a poor fit. +Sometimes, @{function@arcanist:phutil_tag} can be particularly awkward to +use. You can use @{function@arcanist:hsprintf} to build larger and more +complex blocks of HTML, when @{function@arcanist:phutil_tag} is a poor fit. @{function:hsprintf} has `sprintf()` semantics, but `%s` escapes HTML: // Safely build fragments or unwieldy blocks. @@ -72,13 +72,13 @@ complex blocks of HTML, when @{function@libphutil:phutil_tag} is a poor fit. - You need to build a block with a lot of tags, like a table with rows and cells. - You need to build part of a tag (usually you should avoid this, but if you - do need to, @{function@libphutil:phutil_tag} can not do it). + do need to, @{function@arcanist:phutil_tag} can not do it). Note that it is unsafe to provide any user-controlled data to the first -parameter of @{function@libphutil:hsprintf} (the `sprintf()`-style pattern). +parameter of @{function@arcanist:hsprintf} (the `sprintf()`-style pattern). -Like @{function@libphutil:phutil_tag}, this function returns a -@{class@libphutil:PhutilSafeHTML} object. +Like @{function@arcanist:phutil_tag}, this function returns a +@{class@arcanist:PhutilSafeHTML} object. = Composing Tags = @@ -99,7 +99,7 @@ Instead, use an array: // Render a tag containing other tags safely. phutil_tag('div', array(), array($header, $body)); -If you concatenate @{class@libphutil:PhutilSafeHTML} objects, they revert to +If you concatenate @{class@arcanist:PhutilSafeHTML} objects, they revert to normal strings and are no longer marked as properly escaped tags. (In the future, these objects may stop converting to strings, but for now they @@ -118,7 +118,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 -@{class@libphutil:PhutilSafeHTML} object. The easiest way to do this is to +@{class@arcanist:PhutilSafeHTML} object. The easiest way to do this is to return `phutil_tag()` or `javelin_tag()`: return phutil_tag('div', ...); @@ -130,8 +130,8 @@ You can use an @{class:AphrontView} subclass like you would a tag: = Internationalization: pht() = The @{function:pht} function has some special rules. If any input to -@{function:pht} is a @{class@libphutil:PhutilSafeHTML} object, @{function:pht} -returns a @{class@libphutil:PhutilSafeHTML} object itself. Otherwise, it returns +@{function:pht} is a @{class@arcanist:PhutilSafeHTML} object, @{function:pht} +returns a @{class@arcanist:PhutilSafeHTML} object itself. Otherwise, it returns normal text. This is generally safe because translations are not permitted to have more tags @@ -146,23 +146,23 @@ like you would expect, but it is worth being aware of. NOTE: This section describes dangerous methods which can bypass XSS protections. If possible, do not use them. -You can build @{class@libphutil:PhutilSafeHTML} out of a string explicitly by +You can build @{class@arcanist:PhutilSafeHTML} out of a string explicitly by calling @{function:phutil_safe_html} on it. This is **dangerous**, because if you are wrong and the string is not actually safe, you have introduced an XSS vulnerability. Consequently, you should avoid calling this if possible. -You can use @{function@libphutil:phutil_escape_html_newlines} to escape HTML +You can use @{function@arcanist:phutil_escape_html_newlines} to escape HTML while converting newlines to `
`. You should not need to explicitly use -@{function@libphutil:phutil_escape_html} anywhere. +@{function@arcanist:phutil_escape_html} anywhere. If you need to apply a string function (such as `trim()`) to safe HTML, use -@{method@libphutil:PhutilSafeHTML::applyFunction}. +@{method@arcanist:PhutilSafeHTML::applyFunction}. -If you need to extract the content of a @{class@libphutil:PhutilSafeHTML} +If you need to extract the content of a @{class@arcanist:PhutilSafeHTML} object, you should call `getHTMLContent()`, not cast it to a string. Eventually, we would like to remove the string cast entirely. -Functions @{function@libphutil:phutil_tag} and @{function@libphutil:hsprintf} +Functions @{function@arcanist:phutil_tag} and @{function@arcanist:hsprintf} are not safe if you pass the user input for the tag or attribute name. All the following examples are dangerous: diff --git a/src/docs/contributor/unit_tests.diviner b/src/docs/contributor/unit_tests.diviner index 3ac14b3e00..7977a4a876 100644 --- a/src/docs/contributor/unit_tests.diviner +++ b/src/docs/contributor/unit_tests.diviner @@ -1,13 +1,13 @@ @title Writing Unit Tests @group developer -Simple guide to libphutil, Arcanist and Phabricator unit tests. +Simple guide to Arcanist and Phabricator unit tests. = Overview = -libphutil, Arcanist and Phabricator 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. +Arcanist and Phabricator 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. In the general case, you can integrate `arc` with a custom unit test engine (like PHPUnit or any other unit testing library) to run tests in other projects. @@ -16,7 +16,7 @@ for information on customizing engines. = Adding Tests = -To add new tests to a libphutil, Arcanist or Phabricator module: +To add new tests to a Arcanist or Phabricator module: - Create a `__tests__/` directory in the module if it doesn't exist yet. - Add classes to the `__tests__/` directory which extend from diff --git a/src/docs/flavor/php_pitfalls.diviner b/src/docs/flavor/php_pitfalls.diviner index 0ffcaa42da..3f4be45dd7 100644 --- a/src/docs/flavor/php_pitfalls.diviner +++ b/src/docs/flavor/php_pitfalls.diviner @@ -18,7 +18,7 @@ If you merge a list of arrays like this: intermediate arrays and copies every element it has previously seen each time you iterate. -In a libphutil environment, you can use @{function@libphutil:array_mergev} +In a libphutil environment, you can use @{function@arcanist:array_mergev} instead. = `var_export()` Hates Baby Animals = @@ -147,7 +147,7 @@ keys that are naturally sortable with a function that uses native comparison instead, and use it to reorder the original array. In a libphutil environment, you can often do this easily with -@{function@libphutil:isort} or @{function@libphutil:msort}. +@{function@arcanist:isort} or @{function@arcanist:msort}. = `array_intersect()` and `array_diff()` are Also Slow = @@ -270,7 +270,7 @@ new $class_name($argv[0], $argv[1], ...); ...you'll probably invent a very interesting, very novel solution that is very wrong. In a libphutil environment, solve this problem with -@{function@libphutil:newv}. Elsewhere, copy `newv()`'s implementation. +@{function@arcanist:newv}. Elsewhere, copy `newv()`'s implementation. = Equality is not Transitive = diff --git a/src/docs/user/configuration/managing_daemons.diviner b/src/docs/user/configuration/managing_daemons.diviner index 0a732d5836..cf2ba85ea2 100644 --- a/src/docs/user/configuration/managing_daemons.diviner +++ b/src/docs/user/configuration/managing_daemons.diviner @@ -65,7 +65,7 @@ daemonizing it, so you can see output in your console. You can get a list of launchable daemons with **phd list**: - - **libphutil test daemons** are not generally useful unless you are + - **test daemons** are not generally useful unless you are developing daemon infrastructure or debugging a daemon problem; - **PhabricatorTaskmasterDaemon** performs work from a task queue; - **PhabricatorRepositoryPullLocalDaemon** daemons track repositories, for diff --git a/src/docs/user/configuration/troubleshooting_https.diviner b/src/docs/user/configuration/troubleshooting_https.diviner index 6b93a4f690..bdc3439d7d 100644 --- a/src/docs/user/configuration/troubleshooting_https.diviner +++ b/src/docs/user/configuration/troubleshooting_https.diviner @@ -32,7 +32,7 @@ authority and clients have a list of trusted authorities. You can self-sign a certificate by creating your own CA, but clients will not trust it by default. They need to add the CA as a trusted authority. -For instructions on adding CAs, see `libphutil/resources/ssl/README`. +For instructions on adding CAs, see `arcanist/resources/ssl/README`. If you'd prefer that `arc` not verify the identity of the server whatsoever, you can use the `https.blindly-trust-domains` setting. This will make it diff --git a/src/docs/user/field/darkconsole.diviner b/src/docs/user/field/darkconsole.diviner index cbdfb9bda5..065be2d8f1 100644 --- a/src/docs/user/field/darkconsole.diviner +++ b/src/docs/user/field/darkconsole.diviner @@ -48,7 +48,7 @@ Plugin: Error Log The "Error Log" plugin shows errors that occurred while generating the page, similar to the httpd `error.log`. You can send information to the error log -explicitly with the @{function@libphutil:phlog} function. +explicitly with the @{function@arcanist:phlog} function. If errors occurred, a red dot will appear on the plugin tab. diff --git a/src/docs/user/userguide/arcanist.diviner b/src/docs/user/userguide/arcanist.diviner index e8d6bcd5ed..0de18a9358 100644 --- a/src/docs/user/userguide/arcanist.diviner +++ b/src/docs/user/userguide/arcanist.diviner @@ -90,15 +90,8 @@ 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/libphutil.git some_install_path/ $ git clone https://github.com/phacility/arcanist.git -This should leave you with a directory structure like this - - some_install_path/ # Wherever you chose to install it. - arcanist/ # Arcanist-specific code and libraries. - libphutil/ # A shared library Arcanist depends upon. - Now add `some_install_path/arcanist/bin/` to your PATH environment variable. When you type "arc", you should see something like this: @@ -110,8 +103,7 @@ trouble getting this far, see these detailed guides: - On Windows: @{article:Arcanist User Guide: Windows} - On Mac OS X: @{article:Arcanist User Guide: Mac OS X} -You can later upgrade Arcanist and libphutil to the latest versions with -`arc upgrade`: +You can later upgrade Arcanist to the latest version with `arc upgrade`: $ arc upgrade @@ -122,7 +114,7 @@ installed and keep people up to date. Here are some approaches you might be able to use: - Facebook does most development on development servers, which have a standard - environment and NFS mounts. Arcanist and libphutil themselves live on an + environment and NFS mounts. Arcanist lives on an NFS mount, and the default `.bashrc` adds them to the PATH. Updating the mount source updates everyone's versions, and new employees have a working `arc` when they first log in. diff --git a/src/docs/user/userguide/arcanist_coverage.diviner b/src/docs/user/userguide/arcanist_coverage.diviner index a734e5dd80..cb25c0cc74 100644 --- a/src/docs/user/userguide/arcanist_coverage.diviner +++ b/src/docs/user/userguide/arcanist_coverage.diviner @@ -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 libphutil, Arcanist and Phabricator = += Enabling Coverage for Arcanist and Phabricator = -If you're contributing, libphutil, Arcanist and Phabricator support coverage if +If you're contributing, Arcanist and Phabricator support coverage if you install Xdebug: http://xdebug.org/ diff --git a/src/docs/user/userguide/arcanist_quick_start.diviner b/src/docs/user/userguide/arcanist_quick_start.diviner index 743afe4a11..25847ab8a6 100644 --- a/src/docs/user/userguide/arcanist_quick_start.diviner +++ b/src/docs/user/userguide/arcanist_quick_start.diviner @@ -20,9 +20,6 @@ First, install dependencies: Then install Arcanist itself: - $ mkdir somewhere/ - $ cd somewhere/ - somewhere/ $ git clone https://github.com/phacility/libphutil.git somewhere/ $ git clone https://github.com/phacility/arcanist.git Add `arc` to your path: diff --git a/src/docs/user/userguide/conduit.diviner b/src/docs/user/userguide/conduit.diviner index 5784d8cd01..35daee505f 100644 --- a/src/docs/user/userguide/conduit.diviner +++ b/src/docs/user/userguide/conduit.diviner @@ -19,8 +19,7 @@ The primary ways to make Conduit calls are: the API and making calls. This is the best starting point for learning about the API. See the next section for details. -`ConduitClient`: This is the official client available in `libphutil`, and -the one used by `arc`. +`ConduitClient`: This is the official client available in `arcanist`. `arc call-conduit`: You can use this `arc` command to execute low-level Conduit calls by piping JSON in to stdin. This can provide a simple way diff --git a/src/docs/user/userguide/diffusion_managing.diviner b/src/docs/user/userguide/diffusion_managing.diviner index 138bc918bc..e3743526e9 100644 --- a/src/docs/user/userguide/diffusion_managing.diviner +++ b/src/docs/user/userguide/diffusion_managing.diviner @@ -55,10 +55,9 @@ 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", "ARC", "PHU" for libphutil, -and "J" for Javelin. Keeping callsigns brief will make them easier to use, and -the use of one-character callsigns is encouraged if they are reasonably -evocative. +repository, and so on, while Phabricator 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 and activate the callsign identifier (like `rXYZ`) for the repository. These diff --git a/src/docs/user/userguide/diffusion_symbols.diviner b/src/docs/user/userguide/diffusion_symbols.diviner index f5da8aefe0..7d14ad92b2 100644 --- a/src/docs/user/userguide/diffusion_symbols.diviner +++ b/src/docs/user/userguide/diffusion_symbols.diviner @@ -83,8 +83,8 @@ 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" and "libphutil" because it uses classes and - functions from these repositories. + Phabricator lists "Arcanist" because it uses classes and functions defined + in `arcanist/`. == External Symbols == diff --git a/src/docs/user/userguide/drydock_hosts.diviner b/src/docs/user/userguide/drydock_hosts.diviner index 8bfed7dc60..1b8f22cce1 100644 --- a/src/docs/user/userguide/drydock_hosts.diviner +++ b/src/docs/user/userguide/drydock_hosts.diviner @@ -41,7 +41,7 @@ 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, libphutil or Phabricator on the +You do **not** need to install PHP, arcanist, or Phabricator on the hosts unless you are specifically running `arc` commands. **You must configure authentication.** Drydock also does not handle credentials diff --git a/src/docs/user/userguide/events.diviner b/src/docs/user/userguide/events.diviner index ea66448c8a..e18578288b 100644 --- a/src/docs/user/userguide/events.diviner +++ b/src/docs/user/userguide/events.diviner @@ -23,7 +23,7 @@ the most direct and powerful way to respond to events. To install event listeners in Phabricator, follow these steps: - - Write a listener class which extends @{class@libphutil:PhutilEventListener}. + - 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` @@ -40,7 +40,7 @@ see any events the page emitted there. For details on DarkConsole, see To install event listeners in Arcanist, follow these steps: - - Write a listener class which extends @{class@libphutil:PhutilEventListener}. + - 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` diff --git a/src/docs/user/userguide/utf8.diviner b/src/docs/user/userguide/utf8.diviner index a604599e9f..b6742f0c36 100644 --- a/src/docs/user/userguide/utf8.diviner +++ b/src/docs/user/userguide/utf8.diviner @@ -22,48 +22,6 @@ options: Encodings" below). This is not completely supported, and repositories with files that have multiple encodings are not supported. -= Detecting and Repairing Files = - -It is recommended that you write source files only in ASCII text, but -Phabricator fully supports UTF-8 source files. - -If you have a project which isn't valid UTF-8 because a few files have random -binary nonsense in them, there is a script in libphutil which can help you -identify and fix them: - - project/ $ libphutil/scripts/utils/utf8.php - -Generally, run this script on all source files with "-t" to find files with bad -byte ranges, and then run it without "-t" on each file to identify where there -are problems. For example: - - project/ $ find . -type f -name '*.c' -print0 | xargs -0 -n256 ./utf8 -t - ./hello_world.c - -If this script exits without output, you're in good shape and all the files that -were identified are valid UTF-8. If it found some problems, you need to repair -them. You can identify the specific problems by omitting the "-t" flag: - - project/ $ ./utf8.php hello_world.c - FAIL hello_world.c - - 3 main() - 4 { - 5 printf ("Hello World<0xE9><0xD6>!\n"); - 6 } - 7 - -This shows the offending bytes on line 5 (in the actual console display, they'll -be highlighted). Often a codebase will mostly be valid UTF-8 but have a few -scattered files that have other things in them, like curly quotes which someone -copy-pasted from Word into a comment. In these cases, you can just manually -identify and fix the problems pretty easily. - -If you have a prohibitively large number of UTF-8 issues in your source code, -Phabricator doesn't include any default tools to help you process them in a -systematic way. You could hack up `utf8.php` as a starting point, or use other -tools to batch-process your source files. - = Support for Alternate Encodings = Phabricator has some support for encodings other than UTF-8. diff --git a/src/infrastructure/daemon/workers/storage/PhabricatorWorkerTask.php b/src/infrastructure/daemon/workers/storage/PhabricatorWorkerTask.php index 4faae5c83b..480a9d8614 100644 --- a/src/infrastructure/daemon/workers/storage/PhabricatorWorkerTask.php +++ b/src/infrastructure/daemon/workers/storage/PhabricatorWorkerTask.php @@ -66,7 +66,8 @@ abstract class PhabricatorWorkerTask extends PhabricatorWorkerDAO { $class = $this->getTaskClass(); try { - // NOTE: If the class does not exist, libphutil will throw an exception. + // NOTE: If the class does not exist, the autoloader will throw an + // exception. class_exists($class); } catch (PhutilMissingSymbolException $ex) { throw new PhabricatorWorkerPermanentFailureException( diff --git a/src/infrastructure/storage/lisk/LiskDAO.php b/src/infrastructure/storage/lisk/LiskDAO.php index 03b735d315..13b2f8d319 100644 --- a/src/infrastructure/storage/lisk/LiskDAO.php +++ b/src/infrastructure/storage/lisk/LiskDAO.php @@ -116,7 +116,7 @@ * $pugs = $dog->loadAllWhere('breed = %s', 'Pug'); * $sawyer = $dog->loadOneWhere('name = %s', 'Sawyer'); * - * These methods work like @{function@libphutil:queryfx}, but only take half of + * These methods work like @{function@arcanist:queryfx}, but only take half of * a query (the part after the WHERE keyword). Lisk will handle the connection, * columns, and object construction; you are responsible for the rest of it. * @{method:loadAllWhere} returns a list of objects, while diff --git a/src/view/phui/PHUIColor.php b/src/view/phui/PHUIColor.php new file mode 100644 index 0000000000..dc96b10914 --- /dev/null +++ b/src/view/phui/PHUIColor.php @@ -0,0 +1,13 @@ + 'sky', + 'magenta' => 'pink', + ); + + return idx($map, $ansi_color, $ansi_color); + } +} diff --git a/support/startup/PhabricatorStartup.php b/support/startup/PhabricatorStartup.php index bf66f8839b..b687f27bb0 100644 --- a/support/startup/PhabricatorStartup.php +++ b/support/startup/PhabricatorStartup.php @@ -67,7 +67,7 @@ final class PhabricatorStartup { */ public static function getMicrosecondsSinceStart() { // This is the same as "phutil_microseconds_since()", but we may not have - // loaded libphutil yet. + // loaded libraries yet. return (int)(1000000 * (microtime(true) - self::getStartTime())); } diff --git a/webroot/rsrc/css/application/differential/revision-history.css b/webroot/rsrc/css/application/differential/revision-history.css index eca1a95ceb..0a2cc3b381 100644 --- a/webroot/rsrc/css/application/differential/revision-history.css +++ b/webroot/rsrc/css/application/differential/revision-history.css @@ -54,8 +54,3 @@ td.differential-update-history-new { background: #aaffaa; } - -.lintunit-star { - text-align: center; - padding: 0 16px; -} diff --git a/webroot/rsrc/externals/javelin/docs/concepts/behaviors.diviner b/webroot/rsrc/externals/javelin/docs/concepts/behaviors.diviner index 0ff27d912a..f3cea9cda6 100644 --- a/webroot/rsrc/externals/javelin/docs/concepts/behaviors.diviner +++ b/webroot/rsrc/externals/javelin/docs/concepts/behaviors.diviner @@ -124,7 +124,7 @@ This has a wide array of technical and architectural problems: the full power of arbitrary JS execution. - It's utterly hideous. -In 2007/2008, we introduced @{function@libphutil:jsprintf} and a function called +In 2007/2008, we introduced @{function@arcanist:jsprintf} and a function called onloadRegister() to solve some of the obvious problems: lang=php diff --git a/webroot/rsrc/externals/javelin/docs/facebook.diviner b/webroot/rsrc/externals/javelin/docs/facebook.diviner deleted file mode 100644 index 628ec5cfdb..0000000000 --- a/webroot/rsrc/externals/javelin/docs/facebook.diviner +++ /dev/null @@ -1,82 +0,0 @@ -@title Javelin at Facebook -@group facebook - -Information specific to Javelin at Facebook. - -= Building Support Scripts = - -Javelin now ships with the source to build several libfbjs-based binaries, which -serve to completely sever its dependencies on trunk: - - - `javelinsymbols`: used for lint - - `jsast`: used for documentation generation - - `jsxmin`: used to crush packages - -To build these, first build libfbjs: - - javelin/ $ cd externals/libfbjs - javelin/externals/libfbjs/ $ CXX=/usr/bin/g++ make - -Note that **you must specify CXX explicitly because the default CXX is broken**. - -Now you should be able to build the individual binaries: - - javelin/ $ cd support/javelinsymbols - javelin/support/javelinsymbols $ CXX=/usr/bin/g++ make - - javelin/ $ cd support/jsast - javelin/support/jsast $ CXX=/usr/bin/g++ make - - javelin/ $ cd support/jsxmin - javelin/support/jsxmin $ CXX=/usr/bin/g++ make - -= Synchronizing Javelin = - -To synchronize Javelin **from** Facebook trunk, run the synchronize script: - - javelin/ $ ./scripts/sync-from-facebook.php ~/www - -...where `~/www` is the root you want to pull Javelin files from. The script -will copy files out of `html/js/javelin` and build packages, and leave the -results in your working copy. From there you can review changes and commit, and -then push, diff, or send a pull request. - -To synchronize Javelin **to** Facebook trunk, run the, uh, reverse-synchronize -script: - - javelin/ $ ./scripts/sync-to-facebook.php ~/www - -...where `~/www` is the root you want to push Javelin files to. The script -will copy files out of the working copy into your `www` and leave you with a -dirty `www`. From there you can review changes. - -Once Facebook moves to pure git for `www` we can probably just submodule -Javelin into it and get rid of all this nonsense, but the mixed SVN/git -environment makes that difficult until then. - -= Building Documentation = - -Check out `diviner` and `libphutil` from Facebook github, and put them in a -directory with `javelin`: - - somewhere/ $ ls - diviner/ - javelin/ - libphutil/ - somewhere/ $ - -Now run `diviner` on `javelin`: - - somewhere/ $ cd javelin - somewhere/javelin/ $ ../diviner/bin/diviner . - [DivinerArticleEngine] Generating documentation for 48 files... - [JavelinDivinerEngine] Generating documentation for 74 files... - somewhere/javelin/ $ - -Documentation is now available in `javelin/docs/`. - -= Editing javelinjs.com = - -The source for javelinjs.com lives in `javelin/support/webroot/`. The site -itself is served off the phabricator.com host. You need access to that host to -push it. diff --git a/webroot/rsrc/js/application/repository/repository-crossreference.js b/webroot/rsrc/js/application/repository/repository-crossreference.js index 8158e71485..a1b501124e 100644 --- a/webroot/rsrc/js/application/repository/repository-crossreference.js +++ b/webroot/rsrc/js/application/repository/repository-crossreference.js @@ -66,13 +66,8 @@ JX.behavior('repository-crossreference', function(config, statics) { var target = e.getTarget(); - try { - // If we're in an inline comment, don't link symbols. - if (JX.DOM.findAbove(target, 'div', 'differential-inline-comment')) { - return; - } - } catch (ex) { - // Continue if we're not inside an inline comment. + if (!canLinkNode(target)) { + return; } // If only part of the symbol was edited, the symbol name itself will @@ -97,6 +92,29 @@ JX.behavior('repository-crossreference', function(config, statics) { } }); } + + function canLinkNode(node) { + try { + // If we're in an inline comment, don't link symbols. + if (JX.DOM.findAbove(node, 'div', 'differential-inline-comment')) { + return false; + } + } catch (ex) { + // Continue if we're not inside an inline comment. + } + + // See T13644. Don't open symbols if we're inside a changeset header. + try { + if (JX.DOM.findAbove(node, 'h1')) { + return false; + } + } catch (ex) { + // Continue if not inside a header. + } + + return true; + } + function unhighlight() { highlighted && JX.DOM.alterClass(highlighted, classHighlight, false); highlighted = null; @@ -159,6 +177,9 @@ JX.behavior('repository-crossreference', function(config, statics) { uri_symbol = uri_symbol.trim(); // See T13437. Symbols like "#define" need to be encoded. + // See T13644. Symbols like "a/b" must be double-encoded to survive + // one layer of decoding by the webserver. + uri_symbol = encodeURIComponent(uri_symbol); uri_symbol = encodeURIComponent(uri_symbol); var uri = JX.$U('/diffusion/symbol/' + uri_symbol + '/'); @@ -223,7 +244,7 @@ JX.behavior('repository-crossreference', function(config, statics) { var changeset; try { changeset = JX.DOM.findAbove(target, 'div', 'differential-changeset'); - return JX.Stratcom.getData(changeset).path; + return JX.Stratcom.getData(changeset).symbolPath || null; } catch (ex) { // Ignore. } @@ -315,13 +336,8 @@ JX.behavior('repository-crossreference', function(config, statics) { var target = e.getTarget(); - try { - // If we're in an inline comment, don't link symbols. - if (JX.DOM.findAbove(target, 'div', 'differential-inline-comment')) { - return; - } - } catch (ex) { - // Continue if we're not inside an inline comment. + if (!canLinkNode(target)) { + return; } // If only part of the symbol was edited, the symbol name itself will