mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-23 12:09:12 +01:00
(stable) Promote 2021 Week 13
This commit is contained in:
commit
da8a579600
104 changed files with 1696 additions and 787 deletions
|
@ -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',
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_differential.differential_affectedpath
|
||||
DROP epoch;
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_differential.differential_affectedpath
|
||||
CHANGE repositoryID repositoryID INT UNSIGNED;
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
$device_table = new AlmanacDevice();
|
||||
$device_conn = $device_table->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());
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_almanac.almanac_device
|
||||
DROP mailKey;
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_almanac.almanac_device
|
||||
ADD status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,2 @@
|
|||
UPDATE {$NAMESPACE}_almanac.almanac_device
|
||||
SET status = 'active' WHERE status = '';
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
$service_table = new AlmanacService();
|
||||
$service_conn = $service_table->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());
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_almanac.almanac_service
|
||||
DROP mailKey;
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
$binding_table = new AlmanacBinding();
|
||||
$binding_conn = $binding_table->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());
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_almanac.almanac_binding
|
||||
DROP mailKey;
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
$namespace_table = new AlmanacNamespace();
|
||||
$namespace_conn = $namespace_table->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());
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_almanac.almanac_namespace
|
||||
DROP mailKey;
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
$network_table = new AlmanacNetwork();
|
||||
$network_conn = $network_table->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());
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_almanac.almanac_network
|
||||
DROP mailKey;
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
$event_table = new PhabricatorCalendarEvent();
|
||||
$event_conn = $event_table->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());
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
|
||||
DROP mailKey;
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
$initiative_table = new FundInitiative();
|
||||
$initiative_conn = $initiative_table->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());
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_fund.fund_initiative
|
||||
DROP mailKey;
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* NOTE: This is an ADVANCED feature that improves performance but adds a lot
|
||||
* of complexity! This is only suitable for production servers because workers
|
||||
* won't pick up changes between when they spawn and when they handle a request.
|
||||
*
|
||||
* Phabricator spends a significant portion of its runtime loading classes
|
||||
* and functions, even with APC enabled. Since we have very rigidly-defined
|
||||
* rules about what can go in a module (specifically: no side effects), it
|
||||
* is safe to load all the libraries *before* we receive a request.
|
||||
*
|
||||
* Normally, SAPIs don't provide a way to do this, but with a patched PHP-FPM
|
||||
* SAPI you can provide a warmup file that it will execute before a request
|
||||
* is received.
|
||||
*
|
||||
* We're limited in what we can do here, since request information won't
|
||||
* exist yet, but we can load class and function definitions, which is what
|
||||
* we're really interested in.
|
||||
*
|
||||
* Once this file exists, the FCGI process will drop into its normal accept loop
|
||||
* and eventually process a request.
|
||||
*/
|
||||
function __warmup__() {
|
||||
$root = dirname(dirname(dirname(dirname(__FILE__))));
|
||||
require_once $root.'/libphutil/src/__phutil_library_init__.php';
|
||||
require_once $root.'/arcanist/src/__phutil_library_init__.php';
|
||||
require_once $root.'/phabricator/src/__phutil_library_init__.php';
|
||||
|
||||
// Load every symbol. We could possibly refine this -- we don't need to load
|
||||
// every Controller, for instance.
|
||||
$loader = new PhutilSymbolLoader();
|
||||
$loader->selectAndLoadSymbols();
|
||||
|
||||
define('__WARMUP__', true);
|
||||
}
|
||||
|
||||
__warmup__();
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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',
|
||||
|
|
110
src/applications/almanac/constants/AlmanacDeviceStatus.php
Normal file
110
src/applications/almanac/constants/AlmanacDeviceStatus.php
Normal file
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
final class AlmanacDeviceStatus
|
||||
extends Phobject {
|
||||
|
||||
const ACTIVE = 'active';
|
||||
const DISABLED = 'disabled';
|
||||
|
||||
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->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,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,8 @@ abstract class AlmanacSearchEngineAttachment
|
|||
'phid' => $device->getPHID(),
|
||||
'name' => $device->getName(),
|
||||
'properties' => $this->getAlmanacPropertyList($device),
|
||||
'status' => $device->getStatus(),
|
||||
'disabled' => $device->isDisabled(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,20 +166,37 @@ 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());
|
||||
|
||||
if ($only_active) {
|
||||
$service->attachActiveBindings($service_bindings);
|
||||
} else {
|
||||
$service->attachBindings($service_bindings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent::didFilterPage($services);
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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<string, wild>')
|
||||
->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(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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'),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
final class AlmanacDeviceStatusTransaction
|
||||
extends AlmanacDeviceTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'almanac:device:status';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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[] = '<?php';
|
||||
$parts[] = "\n\n";
|
||||
|
|
|
@ -38,7 +38,11 @@ final class PhabricatorConfigModuleController
|
|||
$nav->selectFilter($key);
|
||||
$header = $this->buildHeaderView($title);
|
||||
|
||||
if ($content instanceof AphrontTableView) {
|
||||
$view = $this->buildConfigBoxView($title, $content);
|
||||
} else {
|
||||
$view = $content;
|
||||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs()
|
||||
->addTextCrumb(pht('Extensions/Modules'), $modules_uri)
|
||||
|
|
|
@ -74,6 +74,8 @@ final class PhabricatorDifferentialApplication
|
|||
=> 'DifferentialRevisionOperationController',
|
||||
'inlines/(?P<id>[1-9]\d*)/'
|
||||
=> 'DifferentialRevisionInlinesController',
|
||||
'paths/(?P<id>[1-9]\d*)/'
|
||||
=> 'DifferentialRevisionAffectedPathsController',
|
||||
),
|
||||
'comment/' => array(
|
||||
'inline/' => array(
|
||||
|
|
|
@ -38,7 +38,7 @@ final class DifferentialQueryConduitAPIMethod
|
|||
'authors' => 'optional list<phid>',
|
||||
'ccs' => 'optional list<phid>',
|
||||
'reviewers' => 'optional list<phid>',
|
||||
'paths' => 'optional list<pair<callsign, path>>',
|
||||
'paths' => 'unsupported',
|
||||
'commitHashes' => 'optional list<pair<'.$hash_const.', string>>',
|
||||
'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(
|
||||
throw new Exception(
|
||||
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));
|
||||
}
|
||||
'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) {
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialConstantsModule
|
||||
extends PhabricatorConfigModule {
|
||||
|
||||
public function getModuleKey() {
|
||||
return 'constants.differential';
|
||||
}
|
||||
|
||||
public function getModuleName() {
|
||||
return pht('Constants: Differential');
|
||||
}
|
||||
|
||||
public function renderModuleStatus(AphrontRequest $request) {
|
||||
$viewer = $request->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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,7 +27,13 @@ final class DifferentialDiffCreateController extends DifferentialController {
|
|||
$diff = null;
|
||||
// This object is just for policy stuff
|
||||
$diff_object = DifferentialDiff::initializeNewDiff($viewer);
|
||||
|
||||
if ($revision) {
|
||||
$repository_phid = $revision->getRepositoryPHID();
|
||||
} else {
|
||||
$repository_phid = null;
|
||||
}
|
||||
|
||||
$errors = array();
|
||||
$e_diff = null;
|
||||
$e_file = null;
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialRevisionAffectedPathsController
|
||||
extends DifferentialController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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,27 +999,22 @@ 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();
|
||||
|
||||
|
@ -1025,6 +1022,7 @@ final class DifferentialRevisionViewController
|
|||
foreach ($results as $key => $result) {
|
||||
if ($result->getID() == $this->revisionID) {
|
||||
unset($results[$key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialAffectedPathEngine
|
||||
extends Phobject {
|
||||
|
||||
private $revision;
|
||||
private $diff;
|
||||
|
||||
public function setRevision(DifferentialRevision $revision) {
|
||||
$this->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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<string> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 )----------------------------------------- */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -61,7 +61,7 @@ final class PhabricatorRepositoryManagementClusterizeWorkflow
|
|||
array(
|
||||
AlmanacClusterRepositoryServiceType::SERVICETYPE,
|
||||
))
|
||||
->needBindings(true)
|
||||
->needActiveBindings(true)
|
||||
->executeOne();
|
||||
if (!$service) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"],
|
||||
...
|
||||
},
|
||||
...
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
========
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 `<br />`. 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:
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 =
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -90,15 +90,8 @@ have PHP installed, you can download it from <http://www.php.net/>.
|
|||
|
||||
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.
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ==
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
13
src/view/phui/PHUIColor.php
Normal file
13
src/view/phui/PHUIColor.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
final class PHUIColor extends Phobject {
|
||||
|
||||
public static function getWebColorFromANSIColor($ansi_color) {
|
||||
$map = array(
|
||||
'cyan' => 'sky',
|
||||
'magenta' => 'pink',
|
||||
);
|
||||
|
||||
return idx($map, $ansi_color, $ansi_color);
|
||||
}
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue