mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-23 20:19:03 +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.css' => '0ae696de',
|
||||||
'core.pkg.js' => 'ab3502fe',
|
'core.pkg.js' => 'ab3502fe',
|
||||||
'dark-console.pkg.js' => '187792c2',
|
'dark-console.pkg.js' => '187792c2',
|
||||||
'differential.pkg.css' => '5c459f92',
|
'differential.pkg.css' => 'ffb69e3d',
|
||||||
'differential.pkg.js' => '5080baf4',
|
'differential.pkg.js' => '5986f349',
|
||||||
'diffusion.pkg.css' => '42c75c37',
|
'diffusion.pkg.css' => '42c75c37',
|
||||||
'diffusion.pkg.js' => '78c9885d',
|
'diffusion.pkg.js' => '78c9885d',
|
||||||
'maniphest.pkg.css' => '35995d6d',
|
'maniphest.pkg.css' => '35995d6d',
|
||||||
|
@ -67,7 +67,7 @@ return array(
|
||||||
'rsrc/css/application/differential/core.css' => '7300a73e',
|
'rsrc/css/application/differential/core.css' => '7300a73e',
|
||||||
'rsrc/css/application/differential/phui-inline-comment.css' => '9863a85e',
|
'rsrc/css/application/differential/phui-inline-comment.css' => '9863a85e',
|
||||||
'rsrc/css/application/differential/revision-comment.css' => '7dbc8d1d',
|
'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/revision-list.css' => '93d2df7d',
|
||||||
'rsrc/css/application/differential/table-of-contents.css' => 'bba788b9',
|
'rsrc/css/application/differential/table-of-contents.css' => 'bba788b9',
|
||||||
'rsrc/css/application/diffusion/diffusion-icons.css' => '23b31a1b',
|
'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-preview-branch.js' => '75184d68',
|
||||||
'rsrc/js/application/releeph/releeph-request-state-change.js' => '9f081f05',
|
'rsrc/js/application/releeph/releeph-request-state-change.js' => '9f081f05',
|
||||||
'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'aa3a100c',
|
'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-profile-menu-items.js' => 'e5bdb730',
|
||||||
'rsrc/js/application/search/behavior-reorder-queries.js' => 'b86f297f',
|
'rsrc/js/application/search/behavior-reorder-queries.js' => 'b86f297f',
|
||||||
'rsrc/js/application/transactions/behavior-comment-actions.js' => '4dffaeb2',
|
'rsrc/js/application/transactions/behavior-comment-actions.js' => '4dffaeb2',
|
||||||
|
@ -569,7 +569,7 @@ return array(
|
||||||
'differential-core-view-css' => '7300a73e',
|
'differential-core-view-css' => '7300a73e',
|
||||||
'differential-revision-add-comment-css' => '7e5900d9',
|
'differential-revision-add-comment-css' => '7e5900d9',
|
||||||
'differential-revision-comment-css' => '7dbc8d1d',
|
'differential-revision-comment-css' => '7dbc8d1d',
|
||||||
'differential-revision-history-css' => '8aa3eac5',
|
'differential-revision-history-css' => '237a2979',
|
||||||
'differential-revision-list-css' => '93d2df7d',
|
'differential-revision-list-css' => '93d2df7d',
|
||||||
'differential-table-of-contents-css' => 'bba788b9',
|
'differential-table-of-contents-css' => 'bba788b9',
|
||||||
'diffusion-css' => 'e46232d6',
|
'diffusion-css' => 'e46232d6',
|
||||||
|
@ -693,7 +693,7 @@ return array(
|
||||||
'javelin-behavior-reorder-applications' => 'aa371860',
|
'javelin-behavior-reorder-applications' => 'aa371860',
|
||||||
'javelin-behavior-reorder-columns' => '8ac32fd9',
|
'javelin-behavior-reorder-columns' => '8ac32fd9',
|
||||||
'javelin-behavior-reorder-profile-menu-items' => 'e5bdb730',
|
'javelin-behavior-reorder-profile-menu-items' => 'e5bdb730',
|
||||||
'javelin-behavior-repository-crossreference' => '6337cf26',
|
'javelin-behavior-repository-crossreference' => '44d48cd1',
|
||||||
'javelin-behavior-scrollbar' => '92388bae',
|
'javelin-behavior-scrollbar' => '92388bae',
|
||||||
'javelin-behavior-search-reorder-queries' => 'b86f297f',
|
'javelin-behavior-search-reorder-queries' => 'b86f297f',
|
||||||
'javelin-behavior-select-content' => 'e8240b50',
|
'javelin-behavior-select-content' => 'e8240b50',
|
||||||
|
@ -1309,6 +1309,12 @@ return array(
|
||||||
'43bc9360' => array(
|
'43bc9360' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
),
|
),
|
||||||
|
'44d48cd1' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-stratcom',
|
||||||
|
'javelin-uri',
|
||||||
|
),
|
||||||
'457f4d16' => array(
|
'457f4d16' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -1536,12 +1542,6 @@ return array(
|
||||||
'javelin-request',
|
'javelin-request',
|
||||||
'javelin-uri',
|
'javelin-uri',
|
||||||
),
|
),
|
||||||
'6337cf26' => array(
|
|
||||||
'javelin-behavior',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-stratcom',
|
|
||||||
'javelin-uri',
|
|
||||||
),
|
|
||||||
'65bb0011' => array(
|
'65bb0011' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'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.
|
# to work without modifications.
|
||||||
|
|
||||||
# NOTE: This script assumes you are running it from a directory which contains
|
# 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.
|
ROOT=`pwd` # You can hard-code the path here instead.
|
||||||
|
|
||||||
### UPDATE WORKING COPIES ######################################################
|
### UPDATE WORKING COPIES ######################################################
|
||||||
|
|
||||||
cd $ROOT/libphutil
|
|
||||||
git pull
|
|
||||||
|
|
||||||
cd $ROOT/arcanist
|
cd $ROOT/arcanist
|
||||||
git pull
|
git pull
|
||||||
|
|
||||||
|
|
|
@ -146,6 +146,14 @@ try {
|
||||||
$device_name));
|
$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
|
// We're authenticated as a device, but we're going to read the user out of
|
||||||
// the command below.
|
// the command below.
|
||||||
$is_cluster_request = true;
|
$is_cluster_request = true;
|
||||||
|
|
|
@ -62,6 +62,8 @@ phutil_register_library_map(array(
|
||||||
'AlmanacDeviceSearchConduitAPIMethod' => 'applications/almanac/conduit/AlmanacDeviceSearchConduitAPIMethod.php',
|
'AlmanacDeviceSearchConduitAPIMethod' => 'applications/almanac/conduit/AlmanacDeviceSearchConduitAPIMethod.php',
|
||||||
'AlmanacDeviceSearchEngine' => 'applications/almanac/query/AlmanacDeviceSearchEngine.php',
|
'AlmanacDeviceSearchEngine' => 'applications/almanac/query/AlmanacDeviceSearchEngine.php',
|
||||||
'AlmanacDeviceSetPropertyTransaction' => 'applications/almanac/xaction/AlmanacDeviceSetPropertyTransaction.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',
|
'AlmanacDeviceTransaction' => 'applications/almanac/storage/AlmanacDeviceTransaction.php',
|
||||||
'AlmanacDeviceTransactionQuery' => 'applications/almanac/query/AlmanacDeviceTransactionQuery.php',
|
'AlmanacDeviceTransactionQuery' => 'applications/almanac/query/AlmanacDeviceTransactionQuery.php',
|
||||||
'AlmanacDeviceTransactionType' => 'applications/almanac/xaction/AlmanacDeviceTransactionType.php',
|
'AlmanacDeviceTransactionType' => 'applications/almanac/xaction/AlmanacDeviceTransactionType.php',
|
||||||
|
@ -450,6 +452,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialActionEmailCommand' => 'applications/differential/command/DifferentialActionEmailCommand.php',
|
'DifferentialActionEmailCommand' => 'applications/differential/command/DifferentialActionEmailCommand.php',
|
||||||
'DifferentialAdjustmentMapTestCase' => 'applications/differential/storage/__tests__/DifferentialAdjustmentMapTestCase.php',
|
'DifferentialAdjustmentMapTestCase' => 'applications/differential/storage/__tests__/DifferentialAdjustmentMapTestCase.php',
|
||||||
'DifferentialAffectedPath' => 'applications/differential/storage/DifferentialAffectedPath.php',
|
'DifferentialAffectedPath' => 'applications/differential/storage/DifferentialAffectedPath.php',
|
||||||
|
'DifferentialAffectedPathEngine' => 'applications/differential/engine/DifferentialAffectedPathEngine.php',
|
||||||
'DifferentialAsanaRepresentationField' => 'applications/differential/customfield/DifferentialAsanaRepresentationField.php',
|
'DifferentialAsanaRepresentationField' => 'applications/differential/customfield/DifferentialAsanaRepresentationField.php',
|
||||||
'DifferentialAuditorsCommitMessageField' => 'applications/differential/field/DifferentialAuditorsCommitMessageField.php',
|
'DifferentialAuditorsCommitMessageField' => 'applications/differential/field/DifferentialAuditorsCommitMessageField.php',
|
||||||
'DifferentialAuditorsField' => 'applications/differential/customfield/DifferentialAuditorsField.php',
|
'DifferentialAuditorsField' => 'applications/differential/customfield/DifferentialAuditorsField.php',
|
||||||
|
@ -493,6 +496,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialCommitsSearchEngineAttachment' => 'applications/differential/engineextension/DifferentialCommitsSearchEngineAttachment.php',
|
'DifferentialCommitsSearchEngineAttachment' => 'applications/differential/engineextension/DifferentialCommitsSearchEngineAttachment.php',
|
||||||
'DifferentialConduitAPIMethod' => 'applications/differential/conduit/DifferentialConduitAPIMethod.php',
|
'DifferentialConduitAPIMethod' => 'applications/differential/conduit/DifferentialConduitAPIMethod.php',
|
||||||
'DifferentialConflictsCommitMessageField' => 'applications/differential/field/DifferentialConflictsCommitMessageField.php',
|
'DifferentialConflictsCommitMessageField' => 'applications/differential/field/DifferentialConflictsCommitMessageField.php',
|
||||||
|
'DifferentialConstantsModule' => 'applications/differential/constants/DifferentialConstantsModule.php',
|
||||||
'DifferentialController' => 'applications/differential/controller/DifferentialController.php',
|
'DifferentialController' => 'applications/differential/controller/DifferentialController.php',
|
||||||
'DifferentialCoreCustomField' => 'applications/differential/customfield/DifferentialCoreCustomField.php',
|
'DifferentialCoreCustomField' => 'applications/differential/customfield/DifferentialCoreCustomField.php',
|
||||||
'DifferentialCreateCommentConduitAPIMethod' => 'applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php',
|
'DifferentialCreateCommentConduitAPIMethod' => 'applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php',
|
||||||
|
@ -613,6 +617,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialRevisionAcceptTransaction' => 'applications/differential/xaction/DifferentialRevisionAcceptTransaction.php',
|
'DifferentialRevisionAcceptTransaction' => 'applications/differential/xaction/DifferentialRevisionAcceptTransaction.php',
|
||||||
'DifferentialRevisionActionTransaction' => 'applications/differential/xaction/DifferentialRevisionActionTransaction.php',
|
'DifferentialRevisionActionTransaction' => 'applications/differential/xaction/DifferentialRevisionActionTransaction.php',
|
||||||
'DifferentialRevisionAffectedFilesHeraldField' => 'applications/differential/herald/DifferentialRevisionAffectedFilesHeraldField.php',
|
'DifferentialRevisionAffectedFilesHeraldField' => 'applications/differential/herald/DifferentialRevisionAffectedFilesHeraldField.php',
|
||||||
|
'DifferentialRevisionAffectedPathsController' => 'applications/differential/controller/DifferentialRevisionAffectedPathsController.php',
|
||||||
'DifferentialRevisionAuthorHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorHeraldField.php',
|
'DifferentialRevisionAuthorHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorHeraldField.php',
|
||||||
'DifferentialRevisionAuthorPackagesHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorPackagesHeraldField.php',
|
'DifferentialRevisionAuthorPackagesHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorPackagesHeraldField.php',
|
||||||
'DifferentialRevisionAuthorProjectsHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorProjectsHeraldField.php',
|
'DifferentialRevisionAuthorProjectsHeraldField' => 'applications/differential/herald/DifferentialRevisionAuthorProjectsHeraldField.php',
|
||||||
|
@ -2021,6 +2026,7 @@ phutil_register_library_map(array(
|
||||||
'PHUICalendarMonthView' => 'view/phui/calendar/PHUICalendarMonthView.php',
|
'PHUICalendarMonthView' => 'view/phui/calendar/PHUICalendarMonthView.php',
|
||||||
'PHUICalendarWeekView' => 'view/phui/calendar/PHUICalendarWeekView.php',
|
'PHUICalendarWeekView' => 'view/phui/calendar/PHUICalendarWeekView.php',
|
||||||
'PHUICalendarWidgetView' => 'view/phui/calendar/PHUICalendarWidgetView.php',
|
'PHUICalendarWidgetView' => 'view/phui/calendar/PHUICalendarWidgetView.php',
|
||||||
|
'PHUIColor' => 'view/phui/PHUIColor.php',
|
||||||
'PHUIColorPalletteExample' => 'applications/uiexample/examples/PHUIColorPalletteExample.php',
|
'PHUIColorPalletteExample' => 'applications/uiexample/examples/PHUIColorPalletteExample.php',
|
||||||
'PHUICrumbView' => 'view/phui/PHUICrumbView.php',
|
'PHUICrumbView' => 'view/phui/PHUICrumbView.php',
|
||||||
'PHUICrumbsView' => 'view/phui/PHUICrumbsView.php',
|
'PHUICrumbsView' => 'view/phui/PHUICrumbsView.php',
|
||||||
|
@ -6087,6 +6093,8 @@ phutil_register_library_map(array(
|
||||||
'AlmanacDeviceSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
'AlmanacDeviceSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||||
'AlmanacDeviceSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
'AlmanacDeviceSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||||
'AlmanacDeviceSetPropertyTransaction' => 'AlmanacDeviceTransactionType',
|
'AlmanacDeviceSetPropertyTransaction' => 'AlmanacDeviceTransactionType',
|
||||||
|
'AlmanacDeviceStatus' => 'Phobject',
|
||||||
|
'AlmanacDeviceStatusTransaction' => 'AlmanacDeviceTransactionType',
|
||||||
'AlmanacDeviceTransaction' => 'AlmanacModularTransaction',
|
'AlmanacDeviceTransaction' => 'AlmanacModularTransaction',
|
||||||
'AlmanacDeviceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
'AlmanacDeviceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||||
'AlmanacDeviceTransactionType' => 'AlmanacTransactionType',
|
'AlmanacDeviceTransactionType' => 'AlmanacTransactionType',
|
||||||
|
@ -6532,6 +6540,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialActionEmailCommand' => 'MetaMTAEmailTransactionCommand',
|
'DifferentialActionEmailCommand' => 'MetaMTAEmailTransactionCommand',
|
||||||
'DifferentialAdjustmentMapTestCase' => 'PhabricatorTestCase',
|
'DifferentialAdjustmentMapTestCase' => 'PhabricatorTestCase',
|
||||||
'DifferentialAffectedPath' => 'DifferentialDAO',
|
'DifferentialAffectedPath' => 'DifferentialDAO',
|
||||||
|
'DifferentialAffectedPathEngine' => 'Phobject',
|
||||||
'DifferentialAsanaRepresentationField' => 'DifferentialCustomField',
|
'DifferentialAsanaRepresentationField' => 'DifferentialCustomField',
|
||||||
'DifferentialAuditorsCommitMessageField' => 'DifferentialCommitMessageCustomField',
|
'DifferentialAuditorsCommitMessageField' => 'DifferentialCommitMessageCustomField',
|
||||||
'DifferentialAuditorsField' => 'DifferentialStoredCustomField',
|
'DifferentialAuditorsField' => 'DifferentialStoredCustomField',
|
||||||
|
@ -6580,6 +6589,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialCommitsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
'DifferentialCommitsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||||
'DifferentialConduitAPIMethod' => 'ConduitAPIMethod',
|
'DifferentialConduitAPIMethod' => 'ConduitAPIMethod',
|
||||||
'DifferentialConflictsCommitMessageField' => 'DifferentialCommitMessageField',
|
'DifferentialConflictsCommitMessageField' => 'DifferentialCommitMessageField',
|
||||||
|
'DifferentialConstantsModule' => 'PhabricatorConfigModule',
|
||||||
'DifferentialController' => 'PhabricatorController',
|
'DifferentialController' => 'PhabricatorController',
|
||||||
'DifferentialCoreCustomField' => 'DifferentialCustomField',
|
'DifferentialCoreCustomField' => 'DifferentialCustomField',
|
||||||
'DifferentialCreateCommentConduitAPIMethod' => 'DifferentialConduitAPIMethod',
|
'DifferentialCreateCommentConduitAPIMethod' => 'DifferentialConduitAPIMethod',
|
||||||
|
@ -6733,6 +6743,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialRevisionAcceptTransaction' => 'DifferentialRevisionReviewTransaction',
|
'DifferentialRevisionAcceptTransaction' => 'DifferentialRevisionReviewTransaction',
|
||||||
'DifferentialRevisionActionTransaction' => 'DifferentialRevisionTransactionType',
|
'DifferentialRevisionActionTransaction' => 'DifferentialRevisionTransactionType',
|
||||||
'DifferentialRevisionAffectedFilesHeraldField' => 'DifferentialRevisionHeraldField',
|
'DifferentialRevisionAffectedFilesHeraldField' => 'DifferentialRevisionHeraldField',
|
||||||
|
'DifferentialRevisionAffectedPathsController' => 'DifferentialController',
|
||||||
'DifferentialRevisionAuthorHeraldField' => 'DifferentialRevisionHeraldField',
|
'DifferentialRevisionAuthorHeraldField' => 'DifferentialRevisionHeraldField',
|
||||||
'DifferentialRevisionAuthorPackagesHeraldField' => 'DifferentialRevisionHeraldField',
|
'DifferentialRevisionAuthorPackagesHeraldField' => 'DifferentialRevisionHeraldField',
|
||||||
'DifferentialRevisionAuthorProjectsHeraldField' => 'DifferentialRevisionHeraldField',
|
'DifferentialRevisionAuthorProjectsHeraldField' => 'DifferentialRevisionHeraldField',
|
||||||
|
@ -8348,6 +8359,7 @@ phutil_register_library_map(array(
|
||||||
'PHUICalendarMonthView' => 'AphrontView',
|
'PHUICalendarMonthView' => 'AphrontView',
|
||||||
'PHUICalendarWeekView' => 'AphrontView',
|
'PHUICalendarWeekView' => 'AphrontView',
|
||||||
'PHUICalendarWidgetView' => 'AphrontTagView',
|
'PHUICalendarWidgetView' => 'AphrontTagView',
|
||||||
|
'PHUIColor' => 'Phobject',
|
||||||
'PHUIColorPalletteExample' => 'PhabricatorUIExample',
|
'PHUIColorPalletteExample' => 'PhabricatorUIExample',
|
||||||
'PHUICrumbView' => 'AphrontView',
|
'PHUICrumbView' => 'AphrontView',
|
||||||
'PHUICrumbsView' => '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)
|
->setPolicyObject($device)
|
||||||
->setHeaderIcon('fa-server');
|
->setHeaderIcon('fa-server');
|
||||||
|
|
||||||
|
$status = $device->getStatusObject();
|
||||||
|
if ($status->hasStatusTag()) {
|
||||||
|
$header->setStatus(
|
||||||
|
$status->getStatusTagIcon(),
|
||||||
|
$status->getStatusTagColor(),
|
||||||
|
$status->getName());
|
||||||
|
}
|
||||||
|
|
||||||
$issue = null;
|
$issue = null;
|
||||||
if ($device->isClusterDevice()) {
|
if ($device->isClusterDevice()) {
|
||||||
$issue = $this->addClusterMessage(
|
$issue = $this->addClusterMessage(
|
||||||
|
|
|
@ -76,6 +76,8 @@ final class AlmanacDeviceEditEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildCustomEditFields($object) {
|
protected function buildCustomEditFields($object) {
|
||||||
|
$status_map = $this->getDeviceStatusMap($object);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
id(new PhabricatorTextEditField())
|
id(new PhabricatorTextEditField())
|
||||||
->setKey('name')
|
->setKey('name')
|
||||||
|
@ -84,7 +86,32 @@ final class AlmanacDeviceEditEngine
|
||||||
->setTransactionType(AlmanacDeviceNameTransaction::TRANSACTIONTYPE)
|
->setTransactionType(AlmanacDeviceNameTransaction::TRANSACTIONTYPE)
|
||||||
->setIsRequired(true)
|
->setIsRequired(true)
|
||||||
->setValue($object->getName()),
|
->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
|
final class AlmanacBindingsSearchEngineAttachment
|
||||||
extends AlmanacSearchEngineAttachment {
|
extends AlmanacSearchEngineAttachment {
|
||||||
|
|
||||||
|
private $isActive;
|
||||||
|
|
||||||
|
public function setIsActive($is_active) {
|
||||||
|
$this->isActive = $is_active;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsActive() {
|
||||||
|
return $this->isActive;
|
||||||
|
}
|
||||||
|
|
||||||
public function getAttachmentName() {
|
public function getAttachmentName() {
|
||||||
return pht('Almanac Bindings');
|
return pht('Almanac Bindings');
|
||||||
}
|
}
|
||||||
|
@ -13,12 +24,24 @@ final class AlmanacBindingsSearchEngineAttachment
|
||||||
|
|
||||||
public function willLoadAttachmentData($query, $spec) {
|
public function willLoadAttachmentData($query, $spec) {
|
||||||
$query->needProperties(true);
|
$query->needProperties(true);
|
||||||
$query->needBindings(true);
|
|
||||||
|
if ($this->getIsActive()) {
|
||||||
|
$query->needBindings(true);
|
||||||
|
} else {
|
||||||
|
$query->needActiveBindings(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAttachmentForObject($object, $data, $spec) {
|
public function getAttachmentForObject($object, $data, $spec) {
|
||||||
$bindings = array();
|
$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);
|
$bindings[] = $this->getAlmanacBindingDictionary($binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,8 @@ abstract class AlmanacSearchEngineAttachment
|
||||||
'phid' => $device->getPHID(),
|
'phid' => $device->getPHID(),
|
||||||
'name' => $device->getName(),
|
'name' => $device->getName(),
|
||||||
'properties' => $this->getAlmanacPropertyList($device),
|
'properties' => $this->getAlmanacPropertyList($device),
|
||||||
|
'status' => $device->getStatus(),
|
||||||
|
'disabled' => $device->isDisabled(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,8 @@ final class AlmanacInterfacePHIDType extends PhabricatorPHIDType {
|
||||||
|
|
||||||
$id = $interface->getID();
|
$id = $interface->getID();
|
||||||
|
|
||||||
$device_name = $interface->getDevice()->getName();
|
$device = $interface->getDevice();
|
||||||
|
$device_name = $device->getName();
|
||||||
$address = $interface->getAddress();
|
$address = $interface->getAddress();
|
||||||
$port = $interface->getPort();
|
$port = $interface->getPort();
|
||||||
$network = $interface->getNetwork()->getName();
|
$network = $interface->getNetwork()->getName();
|
||||||
|
@ -48,6 +49,10 @@ final class AlmanacInterfacePHIDType extends PhabricatorPHIDType {
|
||||||
|
|
||||||
$handle->setObjectName(pht('Interface %d', $id));
|
$handle->setObjectName(pht('Interface %d', $id));
|
||||||
$handle->setName($name);
|
$handle->setName($name);
|
||||||
|
|
||||||
|
if ($device->isDisabled()) {
|
||||||
|
$handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ final class AlmanacBindingQuery
|
||||||
private $servicePHIDs;
|
private $servicePHIDs;
|
||||||
private $devicePHIDs;
|
private $devicePHIDs;
|
||||||
private $interfacePHIDs;
|
private $interfacePHIDs;
|
||||||
|
private $isActive;
|
||||||
|
|
||||||
public function withIDs(array $ids) {
|
public function withIDs(array $ids) {
|
||||||
$this->ids = $ids;
|
$this->ids = $ids;
|
||||||
|
@ -34,6 +35,11 @@ final class AlmanacBindingQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withIsActive($active) {
|
||||||
|
$this->isActive = $active;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function newResultObject() {
|
public function newResultObject() {
|
||||||
return new AlmanacBinding();
|
return new AlmanacBinding();
|
||||||
}
|
}
|
||||||
|
@ -95,39 +101,79 @@ final class AlmanacBindingQuery
|
||||||
if ($this->ids !== null) {
|
if ($this->ids !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'id IN (%Ld)',
|
'binding.id IN (%Ld)',
|
||||||
$this->ids);
|
$this->ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->phids !== null) {
|
if ($this->phids !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'phid IN (%Ls)',
|
'binding.phid IN (%Ls)',
|
||||||
$this->phids);
|
$this->phids);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->servicePHIDs !== null) {
|
if ($this->servicePHIDs !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'servicePHID IN (%Ls)',
|
'binding.servicePHID IN (%Ls)',
|
||||||
$this->servicePHIDs);
|
$this->servicePHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->devicePHIDs !== null) {
|
if ($this->devicePHIDs !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'devicePHID IN (%Ls)',
|
'binding.devicePHID IN (%Ls)',
|
||||||
$this->devicePHIDs);
|
$this->devicePHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->interfacePHIDs !== null) {
|
if ($this->interfacePHIDs !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'interfacePHID IN (%Ls)',
|
'binding.interfacePHID IN (%Ls)',
|
||||||
$this->interfacePHIDs);
|
$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;
|
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 $namePrefix;
|
||||||
private $nameSuffix;
|
private $nameSuffix;
|
||||||
private $isClusterDevice;
|
private $isClusterDevice;
|
||||||
|
private $statuses;
|
||||||
|
|
||||||
public function withIDs(array $ids) {
|
public function withIDs(array $ids) {
|
||||||
$this->ids = $ids;
|
$this->ids = $ids;
|
||||||
|
@ -35,6 +36,11 @@ final class AlmanacDeviceQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withStatuses(array $statuses) {
|
||||||
|
$this->statuses = $statuses;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function withNameNgrams($ngrams) {
|
public function withNameNgrams($ngrams) {
|
||||||
return $this->withNgramsConstraint(
|
return $this->withNgramsConstraint(
|
||||||
new AlmanacDeviceNameNgrams(),
|
new AlmanacDeviceNameNgrams(),
|
||||||
|
@ -103,6 +109,13 @@ final class AlmanacDeviceQuery
|
||||||
(int)$this->isClusterDevice);
|
(int)$this->isClusterDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->statuses !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'device.status IN (%Ls)',
|
||||||
|
$this->statuses);
|
||||||
|
}
|
||||||
|
|
||||||
return $where;
|
return $where;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,9 @@ final class AlmanacDeviceSearchEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildCustomSearchFields() {
|
protected function buildCustomSearchFields() {
|
||||||
|
$status_options = AlmanacDeviceStatus::getStatusMap();
|
||||||
|
$status_options = mpull($status_options, 'getName');
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
id(new PhabricatorSearchTextField())
|
id(new PhabricatorSearchTextField())
|
||||||
->setLabel(pht('Name Contains'))
|
->setLabel(pht('Name Contains'))
|
||||||
|
@ -25,6 +28,11 @@ final class AlmanacDeviceSearchEngine
|
||||||
->setLabel(pht('Exact Names'))
|
->setLabel(pht('Exact Names'))
|
||||||
->setKey('names')
|
->setKey('names')
|
||||||
->setDescription(pht('Search for devices with specific 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())
|
id(new PhabricatorSearchThreeStateField())
|
||||||
->setLabel(pht('Cluster Device'))
|
->setLabel(pht('Cluster Device'))
|
||||||
->setKey('isClusterDevice')
|
->setKey('isClusterDevice')
|
||||||
|
@ -50,6 +58,10 @@ final class AlmanacDeviceSearchEngine
|
||||||
$query->withIsClusterDevice($map['isClusterDevice']);
|
$query->withIsClusterDevice($map['isClusterDevice']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($map['statuses']) {
|
||||||
|
$query->withStatuses($map['statuses']);
|
||||||
|
}
|
||||||
|
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +71,7 @@ final class AlmanacDeviceSearchEngine
|
||||||
|
|
||||||
protected function getBuiltinQueryNames() {
|
protected function getBuiltinQueryNames() {
|
||||||
$names = array(
|
$names = array(
|
||||||
|
'active' => pht('Active Devices'),
|
||||||
'all' => pht('All Devices'),
|
'all' => pht('All Devices'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -66,11 +79,13 @@ final class AlmanacDeviceSearchEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildSavedQueryFromBuiltin($query_key) {
|
public function buildSavedQueryFromBuiltin($query_key) {
|
||||||
|
|
||||||
$query = $this->newSavedQuery();
|
$query = $this->newSavedQuery();
|
||||||
$query->setQueryKey($query_key);
|
$query->setQueryKey($query_key);
|
||||||
|
|
||||||
switch ($query_key) {
|
switch ($query_key) {
|
||||||
|
case 'active':
|
||||||
|
$active_statuses = AlmanacDeviceStatus::getActiveStatusList();
|
||||||
|
return $query->setParameter('statuses', $active_statuses);
|
||||||
case 'all':
|
case 'all':
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
@ -99,6 +114,19 @@ final class AlmanacDeviceSearchEngine
|
||||||
$item->addIcon('fa-sitemap', pht('Cluster Device'));
|
$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);
|
$list->addItem($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ final class AlmanacServiceQuery
|
||||||
private $nameSuffix;
|
private $nameSuffix;
|
||||||
|
|
||||||
private $needBindings;
|
private $needBindings;
|
||||||
|
private $needActiveBindings;
|
||||||
|
|
||||||
public function withIDs(array $ids) {
|
public function withIDs(array $ids) {
|
||||||
$this->ids = $ids;
|
$this->ids = $ids;
|
||||||
|
@ -59,6 +60,11 @@ final class AlmanacServiceQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function needActiveBindings($need_active) {
|
||||||
|
$this->needActiveBindings = $need_active;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function newResultObject() {
|
public function newResultObject() {
|
||||||
return new AlmanacService();
|
return new AlmanacService();
|
||||||
}
|
}
|
||||||
|
@ -160,18 +166,35 @@ final class AlmanacServiceQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function didFilterPage(array $services) {
|
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');
|
$service_phids = mpull($services, 'getPHID');
|
||||||
$bindings = id(new AlmanacBindingQuery())
|
|
||||||
|
$bindings_query = id(new AlmanacBindingQuery())
|
||||||
->setViewer($this->getViewer())
|
->setViewer($this->getViewer())
|
||||||
->withServicePHIDs($service_phids)
|
->withServicePHIDs($service_phids)
|
||||||
->needProperties($this->getNeedProperties())
|
->needProperties($this->getNeedProperties());
|
||||||
->execute();
|
|
||||||
|
if ($only_active) {
|
||||||
|
$bindings_query->withIsActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$bindings = $bindings_query->execute();
|
||||||
$bindings = mgroup($bindings, 'getServicePHID');
|
$bindings = mgroup($bindings, 'getServicePHID');
|
||||||
|
|
||||||
foreach ($services as $service) {
|
foreach ($services as $service) {
|
||||||
$service_bindings = idx($bindings, $service->getPHID(), array());
|
$service_bindings = idx($bindings, $service->getPHID(), array());
|
||||||
$service->attachBindings($service_bindings);
|
|
||||||
|
if ($only_active) {
|
||||||
|
$service->attachActiveBindings($service_bindings);
|
||||||
|
} else {
|
||||||
|
$service->attachBindings($service_bindings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ final class AlmanacBinding
|
||||||
protected $servicePHID;
|
protected $servicePHID;
|
||||||
protected $devicePHID;
|
protected $devicePHID;
|
||||||
protected $interfacePHID;
|
protected $interfacePHID;
|
||||||
protected $mailKey;
|
|
||||||
protected $isDisabled;
|
protected $isDisabled;
|
||||||
|
|
||||||
private $service = self::ATTACHABLE;
|
private $service = self::ATTACHABLE;
|
||||||
|
@ -33,7 +32,6 @@ final class AlmanacBinding
|
||||||
return array(
|
return array(
|
||||||
self::CONFIG_AUX_PHID => true,
|
self::CONFIG_AUX_PHID => true,
|
||||||
self::CONFIG_COLUMN_SCHEMA => array(
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
'mailKey' => 'bytes20',
|
|
||||||
'isDisabled' => 'bool',
|
'isDisabled' => 'bool',
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
|
@ -51,15 +49,8 @@ final class AlmanacBinding
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generatePHID() {
|
public function getPHIDType() {
|
||||||
return PhabricatorPHID::generateNewPHID(AlmanacBindingPHIDType::TYPECONST);
|
return AlmanacBindingPHIDType::TYPECONST;
|
||||||
}
|
|
||||||
|
|
||||||
public function save() {
|
|
||||||
if (!$this->mailKey) {
|
|
||||||
$this->mailKey = Filesystem::readRandomCharacters(20);
|
|
||||||
}
|
|
||||||
return parent::save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getName() {
|
public function getName() {
|
||||||
|
@ -67,7 +58,9 @@ final class AlmanacBinding
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getURI() {
|
public function getURI() {
|
||||||
return '/almanac/binding/'.$this->getID().'/';
|
return urisprintf(
|
||||||
|
'/almanac/binding/%s/',
|
||||||
|
$this->getID());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getService() {
|
public function getService() {
|
||||||
|
|
|
@ -15,9 +15,9 @@ final class AlmanacDevice
|
||||||
|
|
||||||
protected $name;
|
protected $name;
|
||||||
protected $nameIndex;
|
protected $nameIndex;
|
||||||
protected $mailKey;
|
|
||||||
protected $viewPolicy;
|
protected $viewPolicy;
|
||||||
protected $editPolicy;
|
protected $editPolicy;
|
||||||
|
protected $status;
|
||||||
protected $isBoundToClusterService;
|
protected $isBoundToClusterService;
|
||||||
|
|
||||||
private $almanacProperties = self::ATTACHABLE;
|
private $almanacProperties = self::ATTACHABLE;
|
||||||
|
@ -26,6 +26,7 @@ final class AlmanacDevice
|
||||||
return id(new AlmanacDevice())
|
return id(new AlmanacDevice())
|
||||||
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
|
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
|
||||||
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
|
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
|
||||||
|
->setStatus(AlmanacDeviceStatus::ACTIVE)
|
||||||
->attachAlmanacProperties(array())
|
->attachAlmanacProperties(array())
|
||||||
->setIsBoundToClusterService(0);
|
->setIsBoundToClusterService(0);
|
||||||
}
|
}
|
||||||
|
@ -36,7 +37,7 @@ final class AlmanacDevice
|
||||||
self::CONFIG_COLUMN_SCHEMA => array(
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
'name' => 'text128',
|
'name' => 'text128',
|
||||||
'nameIndex' => 'bytes12',
|
'nameIndex' => 'bytes12',
|
||||||
'mailKey' => 'bytes20',
|
'status' => 'text32',
|
||||||
'isBoundToClusterService' => 'bool',
|
'isBoundToClusterService' => 'bool',
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
|
@ -51,8 +52,8 @@ final class AlmanacDevice
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generatePHID() {
|
public function getPHIDType() {
|
||||||
return PhabricatorPHID::generateNewPHID(AlmanacDevicePHIDType::TYPECONST);
|
return AlmanacDevicePHIDType::TYPECONST;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function save() {
|
public function save() {
|
||||||
|
@ -60,15 +61,13 @@ final class AlmanacDevice
|
||||||
|
|
||||||
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
|
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
|
||||||
|
|
||||||
if (!$this->mailKey) {
|
|
||||||
$this->mailKey = Filesystem::readRandomCharacters(20);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::save();
|
return parent::save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getURI() {
|
public function getURI() {
|
||||||
return '/almanac/device/view/'.$this->getName().'/';
|
return urisprintf(
|
||||||
|
'/almanac/device/view/%s/',
|
||||||
|
$this->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rebuildClusterBindingStatus() {
|
public function rebuildClusterBindingStatus() {
|
||||||
|
@ -90,8 +89,8 @@ final class AlmanacDevice
|
||||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||||
queryfx(
|
queryfx(
|
||||||
$this->establishConnection('w'),
|
$this->establishConnection('w'),
|
||||||
'UPDATE %T SET isBoundToClusterService = %d WHERE id = %d',
|
'UPDATE %R SET isBoundToClusterService = %d WHERE id = %d',
|
||||||
$this->getTableName(),
|
$this,
|
||||||
$this->getIsBoundToClusterService(),
|
$this->getIsBoundToClusterService(),
|
||||||
$this->getID());
|
$this->getID());
|
||||||
unset($unguarded);
|
unset($unguarded);
|
||||||
|
@ -104,6 +103,18 @@ final class AlmanacDevice
|
||||||
return $this->getIsBoundToClusterService();
|
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 )------------------------------------------- */
|
/* -( AlmanacPropertyInterface )------------------------------------------- */
|
||||||
|
|
||||||
|
@ -267,12 +278,27 @@ final class AlmanacDevice
|
||||||
->setKey('name')
|
->setKey('name')
|
||||||
->setType('string')
|
->setType('string')
|
||||||
->setDescription(pht('The name of the device.')),
|
->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() {
|
public function getFieldValuesForConduit() {
|
||||||
|
$status = $this->getStatusObject();
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'name' => $this->getName(),
|
'name' => $this->getName(),
|
||||||
|
'status' => array(
|
||||||
|
'value' => $status->getValue(),
|
||||||
|
'name' => $status->getName(),
|
||||||
|
),
|
||||||
|
'disabled' => $this->isDisabled(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ final class AlmanacNamespace
|
||||||
|
|
||||||
protected $name;
|
protected $name;
|
||||||
protected $nameIndex;
|
protected $nameIndex;
|
||||||
protected $mailKey;
|
|
||||||
protected $viewPolicy;
|
protected $viewPolicy;
|
||||||
protected $editPolicy;
|
protected $editPolicy;
|
||||||
|
|
||||||
|
@ -28,7 +27,6 @@ final class AlmanacNamespace
|
||||||
self::CONFIG_COLUMN_SCHEMA => array(
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
'name' => 'text128',
|
'name' => 'text128',
|
||||||
'nameIndex' => 'bytes12',
|
'nameIndex' => 'bytes12',
|
||||||
'mailKey' => 'bytes20',
|
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
'key_nameindex' => array(
|
'key_nameindex' => array(
|
||||||
|
@ -42,9 +40,8 @@ final class AlmanacNamespace
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generatePHID() {
|
public function getPHIDType() {
|
||||||
return PhabricatorPHID::generateNewPHID(
|
return AlmanacNamespacePHIDType::TYPECONST;
|
||||||
AlmanacNamespacePHIDType::TYPECONST);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function save() {
|
public function save() {
|
||||||
|
@ -52,15 +49,13 @@ final class AlmanacNamespace
|
||||||
|
|
||||||
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
|
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
|
||||||
|
|
||||||
if (!$this->mailKey) {
|
|
||||||
$this->mailKey = Filesystem::readRandomCharacters(20);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::save();
|
return parent::save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getURI() {
|
public function getURI() {
|
||||||
return '/almanac/namespace/view/'.$this->getName().'/';
|
return urisprintf(
|
||||||
|
'/almanac/namespace/view/%s/',
|
||||||
|
$this->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNameLength() {
|
public function getNameLength() {
|
||||||
|
|
|
@ -10,7 +10,6 @@ final class AlmanacNetwork
|
||||||
PhabricatorConduitResultInterface {
|
PhabricatorConduitResultInterface {
|
||||||
|
|
||||||
protected $name;
|
protected $name;
|
||||||
protected $mailKey;
|
|
||||||
protected $viewPolicy;
|
protected $viewPolicy;
|
||||||
protected $editPolicy;
|
protected $editPolicy;
|
||||||
|
|
||||||
|
@ -25,8 +24,6 @@ final class AlmanacNetwork
|
||||||
self::CONFIG_AUX_PHID => true,
|
self::CONFIG_AUX_PHID => true,
|
||||||
self::CONFIG_COLUMN_SCHEMA => array(
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
'name' => 'sort128',
|
'name' => 'sort128',
|
||||||
'mailKey' => 'bytes20',
|
|
||||||
|
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
'key_name' => array(
|
'key_name' => array(
|
||||||
|
@ -37,20 +34,14 @@ final class AlmanacNetwork
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generatePHID() {
|
public function getPHIDType() {
|
||||||
return PhabricatorPHID::generateNewPHID(AlmanacNetworkPHIDType::TYPECONST);
|
return AlmanacNetworkPHIDType::TYPECONST;
|
||||||
}
|
|
||||||
|
|
||||||
public function save() {
|
|
||||||
if (!$this->mailKey) {
|
|
||||||
$this->mailKey = Filesystem::readRandomCharacters(20);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getURI() {
|
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 $name;
|
||||||
protected $nameIndex;
|
protected $nameIndex;
|
||||||
protected $mailKey;
|
|
||||||
protected $viewPolicy;
|
protected $viewPolicy;
|
||||||
protected $editPolicy;
|
protected $editPolicy;
|
||||||
protected $serviceType;
|
protected $serviceType;
|
||||||
|
|
||||||
private $almanacProperties = self::ATTACHABLE;
|
private $almanacProperties = self::ATTACHABLE;
|
||||||
private $bindings = self::ATTACHABLE;
|
private $bindings = self::ATTACHABLE;
|
||||||
|
private $activeBindings = self::ATTACHABLE;
|
||||||
private $serviceImplementation = self::ATTACHABLE;
|
private $serviceImplementation = self::ATTACHABLE;
|
||||||
|
|
||||||
public static function initializeNewService($type) {
|
public static function initializeNewService($type) {
|
||||||
|
@ -48,7 +48,6 @@ final class AlmanacService
|
||||||
self::CONFIG_COLUMN_SCHEMA => array(
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
'name' => 'text128',
|
'name' => 'text128',
|
||||||
'nameIndex' => 'bytes12',
|
'nameIndex' => 'bytes12',
|
||||||
'mailKey' => 'bytes20',
|
|
||||||
'serviceType' => 'text64',
|
'serviceType' => 'text64',
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
|
@ -66,8 +65,8 @@ final class AlmanacService
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generatePHID() {
|
public function getPHIDType() {
|
||||||
return PhabricatorPHID::generateNewPHID(AlmanacServicePHIDType::TYPECONST);
|
return AlmanacServicePHIDType::TYPECONST;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function save() {
|
public function save() {
|
||||||
|
@ -75,10 +74,6 @@ final class AlmanacService
|
||||||
|
|
||||||
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
|
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
|
||||||
|
|
||||||
if (!$this->mailKey) {
|
|
||||||
$this->mailKey = Filesystem::readRandomCharacters(20);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::save();
|
return parent::save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,23 +86,36 @@ final class AlmanacService
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getActiveBindings() {
|
public function getActiveBindings() {
|
||||||
$bindings = $this->getBindings();
|
return $this->assertAttached($this->activeBindings);
|
||||||
|
|
||||||
// Filter out disabled bindings.
|
|
||||||
foreach ($bindings as $key => $binding) {
|
|
||||||
if ($binding->getIsDisabled()) {
|
|
||||||
unset($bindings[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $bindings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function attachBindings(array $bindings) {
|
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;
|
$this->bindings = $bindings;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function attachActiveBindings(array $bindings) {
|
||||||
|
$this->activeBindings = $bindings;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function getServiceImplementation() {
|
public function getServiceImplementation() {
|
||||||
return $this->assertAttached($this->serviceImplementation);
|
return $this->assertAttached($this->serviceImplementation);
|
||||||
}
|
}
|
||||||
|
@ -289,6 +297,9 @@ final class AlmanacService
|
||||||
->setAttachmentKey('properties'),
|
->setAttachmentKey('properties'),
|
||||||
id(new AlmanacBindingsSearchEngineAttachment())
|
id(new AlmanacBindingsSearchEngineAttachment())
|
||||||
->setAttachmentKey('bindings'),
|
->setAttachmentKey('bindings'),
|
||||||
|
id(new AlmanacBindingsSearchEngineAttachment())
|
||||||
|
->setIsActive(true)
|
||||||
|
->setAttachmentKey('activeBindings'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,9 +46,16 @@ final class AlmanacInterfaceDatasource
|
||||||
|
|
||||||
$results = array();
|
$results = array();
|
||||||
foreach ($handles as $handle) {
|
foreach ($handles as $handle) {
|
||||||
|
if ($handle->isClosed()) {
|
||||||
|
$closed = pht('Disabled');
|
||||||
|
} else {
|
||||||
|
$closed = null;
|
||||||
|
}
|
||||||
|
|
||||||
$results[] = id(new PhabricatorTypeaheadResult())
|
$results[] = id(new PhabricatorTypeaheadResult())
|
||||||
->setName($handle->getName())
|
->setName($handle->getName())
|
||||||
->setPHID($handle->getPHID());
|
->setPHID($handle->getPHID())
|
||||||
|
->setClosed($closed);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $results;
|
return $results;
|
||||||
|
|
|
@ -56,21 +56,41 @@ final class AlmanacBindingTableView extends AphrontView {
|
||||||
|
|
||||||
$icon_active = id(new PHUIIconView())
|
$icon_active = id(new PHUIIconView())
|
||||||
->setIcon('fa-check')
|
->setIcon('fa-check')
|
||||||
|
->setColor('green')
|
||||||
->addSigil('has-tooltip')
|
->addSigil('has-tooltip')
|
||||||
->setMetadata(
|
->setMetadata(
|
||||||
array(
|
array(
|
||||||
'tip' => pht('Active'),
|
'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();
|
$rows = array();
|
||||||
foreach ($bindings as $binding) {
|
foreach ($bindings as $binding) {
|
||||||
$addr = $binding->getInterface()->getAddress();
|
$addr = $binding->getInterface()->getAddress();
|
||||||
$port = $binding->getInterface()->getPort();
|
$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(
|
$rows[] = array(
|
||||||
$binding->getID(),
|
$binding->getID(),
|
||||||
($binding->getIsDisabled() ? $icon_disabled : $icon_active),
|
$binding_icon,
|
||||||
$handles->renderHandle($binding->getServicePHID()),
|
$handles->renderHandle($binding->getServicePHID()),
|
||||||
|
|
||||||
$handles->renderHandle($binding->getDevicePHID()),
|
$handles->renderHandle($binding->getDevicePHID()),
|
||||||
$handles->renderHandle($binding->getInterface()->getNetworkPHID()),
|
$handles->renderHandle($binding->getInterface()->getNetworkPHID()),
|
||||||
$binding->getInterface()->renderDisplayAddress(),
|
$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 $isCancelled;
|
||||||
protected $isAllDay;
|
protected $isAllDay;
|
||||||
protected $icon;
|
protected $icon;
|
||||||
protected $mailKey;
|
|
||||||
protected $isStub;
|
protected $isStub;
|
||||||
|
|
||||||
protected $isRecurring = 0;
|
protected $isRecurring = 0;
|
||||||
|
@ -360,10 +359,6 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
public function save() {
|
public function save() {
|
||||||
if (!$this->mailKey) {
|
|
||||||
$this->mailKey = Filesystem::readRandomCharacters(20);
|
|
||||||
}
|
|
||||||
|
|
||||||
$import_uid = $this->getImportUID();
|
$import_uid = $this->getImportUID();
|
||||||
if ($import_uid !== null) {
|
if ($import_uid !== null) {
|
||||||
$index = PhabricatorHash::digestForIndex($import_uid);
|
$index = PhabricatorHash::digestForIndex($import_uid);
|
||||||
|
@ -405,7 +400,6 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||||
'isCancelled' => 'bool',
|
'isCancelled' => 'bool',
|
||||||
'isAllDay' => 'bool',
|
'isAllDay' => 'bool',
|
||||||
'icon' => 'text32',
|
'icon' => 'text32',
|
||||||
'mailKey' => 'bytes20',
|
|
||||||
'isRecurring' => 'bool',
|
'isRecurring' => 'bool',
|
||||||
'seriesParentPHID' => 'phid?',
|
'seriesParentPHID' => 'phid?',
|
||||||
'instanceOfEventPHID' => 'phid?',
|
'instanceOfEventPHID' => 'phid?',
|
||||||
|
@ -442,9 +436,8 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generatePHID() {
|
public function getPHIDType() {
|
||||||
return PhabricatorPHID::generateNewPHID(
|
return PhabricatorCalendarEventPHIDType::TYPECONST;
|
||||||
PhabricatorCalendarEventPHIDType::TYPECONST);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMonogram() {
|
public function getMonogram() {
|
||||||
|
|
|
@ -238,6 +238,16 @@ final class PhabricatorConduitAPIController
|
||||||
if ($object instanceof PhabricatorUser) {
|
if ($object instanceof PhabricatorUser) {
|
||||||
$user = $object;
|
$user = $object;
|
||||||
} else {
|
} 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()) {
|
if (!$stored_key->getIsTrusted()) {
|
||||||
return array(
|
return array(
|
||||||
'ERR-INVALID-AUTH',
|
'ERR-INVALID-AUTH',
|
||||||
|
|
|
@ -156,7 +156,7 @@ abstract class PhabricatorConduitController extends PhabricatorController {
|
||||||
|
|
||||||
$parts = array();
|
$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[] = '<?php';
|
||||||
$parts[] = "\n\n";
|
$parts[] = "\n\n";
|
||||||
|
|
|
@ -38,7 +38,11 @@ final class PhabricatorConfigModuleController
|
||||||
$nav->selectFilter($key);
|
$nav->selectFilter($key);
|
||||||
$header = $this->buildHeaderView($title);
|
$header = $this->buildHeaderView($title);
|
||||||
|
|
||||||
$view = $this->buildConfigBoxView($title, $content);
|
if ($content instanceof AphrontTableView) {
|
||||||
|
$view = $this->buildConfigBoxView($title, $content);
|
||||||
|
} else {
|
||||||
|
$view = $content;
|
||||||
|
}
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs()
|
$crumbs = $this->buildApplicationCrumbs()
|
||||||
->addTextCrumb(pht('Extensions/Modules'), $modules_uri)
|
->addTextCrumb(pht('Extensions/Modules'), $modules_uri)
|
||||||
|
|
|
@ -74,6 +74,8 @@ final class PhabricatorDifferentialApplication
|
||||||
=> 'DifferentialRevisionOperationController',
|
=> 'DifferentialRevisionOperationController',
|
||||||
'inlines/(?P<id>[1-9]\d*)/'
|
'inlines/(?P<id>[1-9]\d*)/'
|
||||||
=> 'DifferentialRevisionInlinesController',
|
=> 'DifferentialRevisionInlinesController',
|
||||||
|
'paths/(?P<id>[1-9]\d*)/'
|
||||||
|
=> 'DifferentialRevisionAffectedPathsController',
|
||||||
),
|
),
|
||||||
'comment/' => array(
|
'comment/' => array(
|
||||||
'inline/' => array(
|
'inline/' => array(
|
||||||
|
|
|
@ -38,7 +38,7 @@ final class DifferentialQueryConduitAPIMethod
|
||||||
'authors' => 'optional list<phid>',
|
'authors' => 'optional list<phid>',
|
||||||
'ccs' => 'optional list<phid>',
|
'ccs' => 'optional list<phid>',
|
||||||
'reviewers' => 'optional list<phid>',
|
'reviewers' => 'optional list<phid>',
|
||||||
'paths' => 'optional list<pair<callsign, path>>',
|
'paths' => 'unsupported',
|
||||||
'commitHashes' => 'optional list<pair<'.$hash_const.', string>>',
|
'commitHashes' => 'optional list<pair<'.$hash_const.', string>>',
|
||||||
'status' => 'optional '.$status_const,
|
'status' => 'optional '.$status_const,
|
||||||
'order' => 'optional '.$order_const,
|
'order' => 'optional '.$order_const,
|
||||||
|
@ -92,48 +92,11 @@ final class DifferentialQueryConduitAPIMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($path_pairs) {
|
if ($path_pairs) {
|
||||||
$paths = array();
|
throw new Exception(
|
||||||
foreach ($path_pairs as $pair) {
|
pht(
|
||||||
list($callsign, $path) = $pair;
|
'Parameter "paths" to Conduit API method "differential.query" is '.
|
||||||
$paths[] = $path;
|
'no longer supported. Use the "paths" constraint to '.
|
||||||
}
|
'"differential.revision.search" instead. See T13639.'));
|
||||||
|
|
||||||
$path_map = id(new DiffusionPathIDQuery($paths))->loadPathIDs();
|
|
||||||
if (count($path_map) != count($paths)) {
|
|
||||||
$unknown_paths = array();
|
|
||||||
foreach ($paths as $p) {
|
|
||||||
if (!idx($path_map, $p)) {
|
|
||||||
$unknown_paths[] = $p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw id(new ConduitException('ERR-INVALID-PARAMETER'))
|
|
||||||
->setErrorDescription(
|
|
||||||
pht(
|
|
||||||
'Unknown paths: %s',
|
|
||||||
implode(', ', $unknown_paths)));
|
|
||||||
}
|
|
||||||
|
|
||||||
$repos = array();
|
|
||||||
foreach ($path_pairs as $pair) {
|
|
||||||
list($callsign, $path) = $pair;
|
|
||||||
if (!idx($repos, $callsign)) {
|
|
||||||
$repos[$callsign] = id(new PhabricatorRepositoryQuery())
|
|
||||||
->setViewer($request->getUser())
|
|
||||||
->withCallsigns(array($callsign))
|
|
||||||
->executeOne();
|
|
||||||
|
|
||||||
if (!$repos[$callsign]) {
|
|
||||||
throw id(new ConduitException('ERR-INVALID-PARAMETER'))
|
|
||||||
->setErrorDescription(
|
|
||||||
pht(
|
|
||||||
'Unknown repo callsign: %s',
|
|
||||||
$callsign));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$repo = $repos[$callsign];
|
|
||||||
|
|
||||||
$query->withPath($repo->getID(), idx($path_map, $path));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($commit_hashes) {
|
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_SKIP = 4;
|
||||||
const LINT_AUTO_SKIP = 6;
|
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_SKIP = 4;
|
||||||
const UNIT_AUTO_SKIP = 6;
|
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;
|
$diff = null;
|
||||||
// This object is just for policy stuff
|
// This object is just for policy stuff
|
||||||
$diff_object = DifferentialDiff::initializeNewDiff($viewer);
|
$diff_object = DifferentialDiff::initializeNewDiff($viewer);
|
||||||
$repository_phid = null;
|
|
||||||
|
if ($revision) {
|
||||||
|
$repository_phid = $revision->getRepositoryPHID();
|
||||||
|
} else {
|
||||||
|
$repository_phid = null;
|
||||||
|
}
|
||||||
|
|
||||||
$errors = array();
|
$errors = array();
|
||||||
$e_diff = null;
|
$e_diff = null;
|
||||||
$e_file = 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) {
|
PhabricatorRepository $repository) {
|
||||||
assert_instances_of($changesets, 'DifferentialChangeset');
|
assert_instances_of($changesets, 'DifferentialChangeset');
|
||||||
|
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$paths = array();
|
$paths = array();
|
||||||
foreach ($changesets as $changeset) {
|
foreach ($changesets as $changeset) {
|
||||||
$paths[] = $changeset->getAbsoluteRepositoryPath(
|
$paths[] = $changeset->getAbsoluteRepositoryPath(
|
||||||
|
@ -997,34 +999,30 @@ final class DifferentialRevisionViewController
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$path_map = id(new DiffusionPathIDQuery($paths))->loadPathIDs();
|
|
||||||
|
|
||||||
if (!$path_map) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$recent = (PhabricatorTime::getNow() - phutil_units('30 days in seconds'));
|
$recent = (PhabricatorTime::getNow() - phutil_units('30 days in seconds'));
|
||||||
|
|
||||||
$query = id(new DifferentialRevisionQuery())
|
$query = id(new DifferentialRevisionQuery())
|
||||||
->setViewer($this->getRequest()->getUser())
|
->setViewer($viewer)
|
||||||
->withIsOpen(true)
|
->withIsOpen(true)
|
||||||
->withUpdatedEpochBetween($recent, null)
|
->withUpdatedEpochBetween($recent, null)
|
||||||
->setOrder(DifferentialRevisionQuery::ORDER_MODIFIED)
|
->setOrder(DifferentialRevisionQuery::ORDER_MODIFIED)
|
||||||
->setLimit(10)
|
->setLimit(10)
|
||||||
->needFlags(true)
|
->needFlags(true)
|
||||||
->needDrafts(true)
|
->needDrafts(true)
|
||||||
->needReviewers(true);
|
->needReviewers(true)
|
||||||
|
->withRepositoryPHIDs(
|
||||||
foreach ($path_map as $path => $path_id) {
|
array(
|
||||||
$query->withPath($repository->getID(), $path_id);
|
$repository->getPHID(),
|
||||||
}
|
))
|
||||||
|
->withPaths($paths);
|
||||||
|
|
||||||
$results = $query->execute();
|
$results = $query->execute();
|
||||||
|
|
||||||
// Strip out *this* revision.
|
// Strip out *this* revision.
|
||||||
foreach ($results as $key => $result) {
|
foreach ($results as $key => $result) {
|
||||||
if ($result->getID() == $this->revisionID) {
|
if ($result->getID() == $this->revisionID) {
|
||||||
unset($results[$key]);
|
unset($results[$key]);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,6 @@ final class DifferentialLintField
|
||||||
protected function getDiffPropertyKeys() {
|
protected function getDiffPropertyKeys() {
|
||||||
return array(
|
return array(
|
||||||
'arc:lint',
|
'arc:lint',
|
||||||
'arc:lint-excuse',
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,33 +83,18 @@ final class DifferentialLintField
|
||||||
DifferentialDiff $diff,
|
DifferentialDiff $diff,
|
||||||
array $messages) {
|
array $messages) {
|
||||||
|
|
||||||
$colors = array(
|
$status_value = $diff->getLintStatus();
|
||||||
DifferentialLintStatus::LINT_NONE => 'grey',
|
$status = DifferentialLintStatus::newStatusFromValue($status_value);
|
||||||
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');
|
|
||||||
|
|
||||||
$message = DifferentialRevisionUpdateHistoryView::getDiffLintMessage($diff);
|
$status_icon = $status->getIconIcon();
|
||||||
|
$status_color = $status->getIconColor();
|
||||||
$excuse = $diff->getProperty('arc:lint-excuse');
|
$status_name = $status->getName();
|
||||||
if (strlen($excuse)) {
|
|
||||||
$excuse = array(
|
|
||||||
phutil_tag('strong', array(), pht('Excuse:')),
|
|
||||||
' ',
|
|
||||||
phutil_escape_html_newlines($excuse),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$status = id(new PHUIStatusListView())
|
$status = id(new PHUIStatusListView())
|
||||||
->addItem(
|
->addItem(
|
||||||
id(new PHUIStatusItemView())
|
id(new PHUIStatusItemView())
|
||||||
->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color)
|
->setIcon($status_icon, $status_color)
|
||||||
->setTarget($message)
|
->setTarget($status_name));
|
||||||
->setNote($excuse));
|
|
||||||
|
|
||||||
return $status;
|
return $status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,29 +72,20 @@ final class DifferentialUnitField
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderDiffPropertyViewValue(DifferentialDiff $diff) {
|
public function renderDiffPropertyViewValue(DifferentialDiff $diff) {
|
||||||
|
$status_value = $diff->getUnitStatus();
|
||||||
|
$status = DifferentialUnitStatus::newStatusFromValue($status_value);
|
||||||
|
|
||||||
$colors = array(
|
$status_icon = $status->getIconIcon();
|
||||||
DifferentialUnitStatus::UNIT_NONE => 'grey',
|
$status_color = $status->getIconColor();
|
||||||
DifferentialUnitStatus::UNIT_OKAY => 'green',
|
$status_name = $status->getName();
|
||||||
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 = id(new PHUIStatusListView())
|
$status = id(new PHUIStatusListView())
|
||||||
->addItem(
|
->addItem(
|
||||||
id(new PHUIStatusItemView())
|
id(new PHUIStatusItemView())
|
||||||
->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color)
|
->setIcon($status_icon, $status_color)
|
||||||
->setTarget($message));
|
->setTarget($status_name));
|
||||||
|
|
||||||
return $status;
|
return $status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -340,21 +340,57 @@ final class DifferentialTransactionEditor
|
||||||
pht('Failed to load revision from transaction finalization.'));
|
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->attachReviewers($new_revision->getReviewers());
|
||||||
$object->attachActiveDiff($new_revision->getActiveDiff());
|
$object->attachActiveDiff($active_diff);
|
||||||
$object->attachRepository($new_revision->getRepository());
|
$object->attachRepository($new_revision->getRepository());
|
||||||
|
|
||||||
|
$has_new_diff = false;
|
||||||
|
$should_index_paths = false;
|
||||||
|
$should_index_hashes = false;
|
||||||
|
$need_changesets = false;
|
||||||
|
|
||||||
foreach ($xactions as $xaction) {
|
foreach ($xactions as $xaction) {
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
case DifferentialRevisionUpdateTransaction::TRANSACTIONTYPE:
|
case DifferentialRevisionUpdateTransaction::TRANSACTIONTYPE:
|
||||||
$diff = $this->requireDiff($xaction->getNewValue(), true);
|
$need_changesets = true;
|
||||||
|
|
||||||
// Update these denormalized index tables when we attach a new
|
$new_diff_phid = $xaction->getNewValue();
|
||||||
// diff to a revision.
|
$has_new_diff = true;
|
||||||
|
|
||||||
$this->updateRevisionHashTable($object, $diff);
|
$should_index_paths = true;
|
||||||
$this->updateAffectedPathTable($object, $diff);
|
$should_index_hashes = true;
|
||||||
break;
|
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;
|
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
|
* Update the table connecting revisions to DVCS local hashes, so we can
|
||||||
* identify revisions by commit/tree hashes.
|
* 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
|
final class DifferentialRevisionQuery
|
||||||
extends PhabricatorCursorPagedPolicyAwareQuery {
|
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
|
|
||||||
private $pathIDs = array();
|
|
||||||
|
|
||||||
private $authors = array();
|
private $authors = array();
|
||||||
private $draftAuthors = array();
|
private $draftAuthors = array();
|
||||||
private $ccs = array();
|
private $ccs = array();
|
||||||
|
@ -27,6 +25,7 @@ final class DifferentialRevisionQuery
|
||||||
private $createdEpochMin;
|
private $createdEpochMin;
|
||||||
private $createdEpochMax;
|
private $createdEpochMax;
|
||||||
private $noReviewers;
|
private $noReviewers;
|
||||||
|
private $paths;
|
||||||
|
|
||||||
const ORDER_MODIFIED = 'order-modified';
|
const ORDER_MODIFIED = 'order-modified';
|
||||||
const ORDER_CREATED = 'order-created';
|
const ORDER_CREATED = 'order-created';
|
||||||
|
@ -43,22 +42,15 @@ final class DifferentialRevisionQuery
|
||||||
|
|
||||||
/* -( Query Configuration )------------------------------------------------ */
|
/* -( Query Configuration )------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter results to revisions which affect a Diffusion path ID in a given
|
* Find revisions affecting one or more items in a list of paths.
|
||||||
* repository. You can call this multiple times to select revisions for
|
|
||||||
* several paths.
|
|
||||||
*
|
*
|
||||||
* @param int Diffusion repository ID.
|
* @param list<string> List of file paths.
|
||||||
* @param int Diffusion path ID.
|
|
||||||
* @return this
|
* @return this
|
||||||
* @task config
|
* @task config
|
||||||
*/
|
*/
|
||||||
public function withPath($repository_id, $path_id) {
|
public function withPaths(array $paths) {
|
||||||
$this->pathIDs[] = array(
|
$this->paths = $paths;
|
||||||
'repositoryID' => $repository_id,
|
|
||||||
'pathID' => $path_id,
|
|
||||||
);
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,12 +560,13 @@ final class DifferentialRevisionQuery
|
||||||
*/
|
*/
|
||||||
private function buildJoinsClause(AphrontDatabaseConnection $conn) {
|
private function buildJoinsClause(AphrontDatabaseConnection $conn) {
|
||||||
$joins = array();
|
$joins = array();
|
||||||
if ($this->pathIDs) {
|
|
||||||
|
if ($this->paths) {
|
||||||
$path_table = new DifferentialAffectedPath();
|
$path_table = new DifferentialAffectedPath();
|
||||||
$joins[] = qsprintf(
|
$joins[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'JOIN %T p ON p.revisionID = r.id',
|
'JOIN %R paths ON paths.revisionID = r.id',
|
||||||
$path_table->getTableName());
|
$path_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->commitHashes) {
|
if ($this->commitHashes) {
|
||||||
|
@ -635,20 +628,46 @@ final class DifferentialRevisionQuery
|
||||||
* @task internal
|
* @task internal
|
||||||
*/
|
*/
|
||||||
protected function buildWhereClause(AphrontDatabaseConnection $conn) {
|
protected function buildWhereClause(AphrontDatabaseConnection $conn) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
$where = array();
|
$where = array();
|
||||||
|
|
||||||
if ($this->pathIDs) {
|
if ($this->paths !== null) {
|
||||||
$path_clauses = array();
|
$paths = $this->paths;
|
||||||
$repo_info = igroup($this->pathIDs, 'repositoryID');
|
|
||||||
foreach ($repo_info as $repository_id => $paths) {
|
$path_map = id(new DiffusionPathIDQuery($paths))
|
||||||
$path_clauses[] = qsprintf(
|
->loadPathIDs();
|
||||||
$conn,
|
|
||||||
'(p.repositoryID = %d AND p.pathID IN (%Ld))',
|
if (!$path_map) {
|
||||||
$repository_id,
|
// If none of the paths have entries in the PathID table, we can not
|
||||||
ipull($paths, 'pathID'));
|
// 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) {
|
if ($this->authors) {
|
||||||
|
@ -778,7 +797,9 @@ final class DifferentialRevisionQuery
|
||||||
*/
|
*/
|
||||||
protected function shouldGroupQueryResultRows() {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,10 @@ final class DifferentialRevisionSearchEngine
|
||||||
$map['modifiedEnd']);
|
$map['modifiedEnd']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($map['affectedPaths']) {
|
||||||
|
$query->withPaths($map['affectedPaths']);
|
||||||
|
}
|
||||||
|
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +122,12 @@ final class DifferentialRevisionSearchEngine
|
||||||
->setIsHidden(true)
|
->setIsHidden(true)
|
||||||
->setDescription(
|
->setDescription(
|
||||||
pht('Find revisions modified at or before a particular time.')),
|
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 $repositoryID;
|
||||||
protected $pathID;
|
protected $pathID;
|
||||||
protected $epoch;
|
|
||||||
protected $revisionID;
|
protected $revisionID;
|
||||||
|
|
||||||
protected function getConfiguration() {
|
protected function getConfiguration() {
|
||||||
|
@ -16,15 +15,16 @@ final class DifferentialAffectedPath extends DifferentialDAO {
|
||||||
self::CONFIG_TIMESTAMPS => false,
|
self::CONFIG_TIMESTAMPS => false,
|
||||||
self::CONFIG_COLUMN_SCHEMA => array(
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
'id' => null,
|
'id' => null,
|
||||||
|
'repositoryID' => 'id?',
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
'PRIMARY' => null,
|
'PRIMARY' => null,
|
||||||
'repositoryID' => array(
|
|
||||||
'columns' => array('repositoryID', 'pathID', 'epoch'),
|
|
||||||
),
|
|
||||||
'revisionID' => array(
|
'revisionID' => array(
|
||||||
'columns' => array('revisionID'),
|
'columns' => array('revisionID'),
|
||||||
),
|
),
|
||||||
|
'key_path' => array(
|
||||||
|
'columns' => array('pathID', 'repositoryID'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1022,16 +1022,9 @@ final class DifferentialRevision extends DifferentialDAO
|
||||||
$engine->destroyObject($diff);
|
$engine->destroyObject($diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
$conn_w = $this->establishConnection('w');
|
id(new DifferentialAffectedPathEngine())
|
||||||
|
->setRevision($this)
|
||||||
// we have to do paths a little differently as they do not have
|
->destroyAffectedPaths();
|
||||||
// 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());
|
|
||||||
|
|
||||||
$viewstate_query = id(new DifferentialViewStateQuery())
|
$viewstate_query = id(new DifferentialViewStateQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
|
|
|
@ -246,6 +246,7 @@ final class DifferentialChangesetDetailView extends AphrontView {
|
||||||
'displayPath' => hsprintf('%s', $display_parts),
|
'displayPath' => hsprintf('%s', $display_parts),
|
||||||
'icon' => $display_icon,
|
'icon' => $display_icon,
|
||||||
'pathParts' => $path_parts,
|
'pathParts' => $path_parts,
|
||||||
|
'symbolPath' => $display_filename,
|
||||||
|
|
||||||
'pathIconIcon' => $changeset->getPathIconIcon(),
|
'pathIconIcon' => $changeset->getPathIconIcon(),
|
||||||
'pathIconColor' => $changeset->getPathIconColor(),
|
'pathIconColor' => $changeset->getPathIconColor(),
|
||||||
|
|
|
@ -139,29 +139,8 @@ final class DifferentialRevisionUpdateHistoryView extends AphrontView {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($diff) {
|
if ($diff) {
|
||||||
$unit_status = idx(
|
$lint = $this->newLintStatusView($diff);
|
||||||
$this->unitStatus,
|
$unit = $this->newUnitStatusView($diff);
|
||||||
$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);
|
|
||||||
|
|
||||||
$base = $this->renderBaseRevision($diff);
|
$base = $this->renderBaseRevision($diff);
|
||||||
} else {
|
} else {
|
||||||
$lint = null;
|
$lint = null;
|
||||||
|
@ -282,86 +261,6 @@ final class DifferentialRevisionUpdateHistoryView extends AphrontView {
|
||||||
return $content;
|
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) {
|
private function renderBaseRevision(DifferentialDiff $diff) {
|
||||||
switch ($diff->getSourceControlSystem()) {
|
switch ($diff->getSourceControlSystem()) {
|
||||||
case 'git':
|
case 'git':
|
||||||
|
@ -401,4 +300,42 @@ final class DifferentialRevisionUpdateHistoryView extends AphrontView {
|
||||||
}
|
}
|
||||||
return $link;
|
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();
|
$repository = $drequest->getRepository();
|
||||||
$path = $drequest->getPath();
|
$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'));
|
$recent = (PhabricatorTime::getNow() - phutil_units('30 days in seconds'));
|
||||||
|
|
||||||
$revisions = id(new DifferentialRevisionQuery())
|
$revisions = id(new DifferentialRevisionQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withPath($repository->getID(), $path_id)
|
->withPaths(array($path))
|
||||||
|
->withRepositoryPHIDs(array($repository->getPHID()))
|
||||||
->withIsOpen(true)
|
->withIsOpen(true)
|
||||||
->withUpdatedEpochBetween($recent, null)
|
->withUpdatedEpochBetween($recent, null)
|
||||||
->setOrder(DifferentialRevisionQuery::ORDER_MODIFIED)
|
->setOrder(DifferentialRevisionQuery::ORDER_MODIFIED)
|
||||||
|
@ -963,7 +958,7 @@ final class DiffusionBrowseController extends DiffusionController {
|
||||||
}
|
}
|
||||||
|
|
||||||
$header = id(new PHUIHeaderView())
|
$header = id(new PHUIHeaderView())
|
||||||
->setHeader(pht('Recently Open Revisions'));
|
->setHeader(pht('Recent Open Revisions'));
|
||||||
|
|
||||||
$list = id(new DifferentialRevisionListView())
|
$list = id(new DifferentialRevisionListView())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
|
|
|
@ -4,7 +4,10 @@ final class DiffusionSymbolController extends DiffusionController {
|
||||||
|
|
||||||
public function handleRequest(AphrontRequest $request) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
// See T13638 for discussion of escaping.
|
||||||
$name = $request->getURIData('name');
|
$name = $request->getURIData('name');
|
||||||
|
$name = phutil_unescape_uri_path_component($name);
|
||||||
|
|
||||||
$query = id(new DiffusionSymbolQuery())
|
$query = id(new DiffusionSymbolQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
|
|
|
@ -66,12 +66,12 @@ final class DiffusionDatasourceEngineExtension
|
||||||
$parts = null;
|
$parts = null;
|
||||||
if (preg_match('/(.*)(?:\\.|::|->)(.*)/', $symbol, $parts)) {
|
if (preg_match('/(.*)(?:\\.|::|->)(.*)/', $symbol, $parts)) {
|
||||||
return urisprintf(
|
return urisprintf(
|
||||||
'/diffusion/symbol/%s/?jump=true&context=%s',
|
'/diffusion/symbol/%p/?jump=true&context=%s',
|
||||||
$parts[2],
|
$parts[2],
|
||||||
$parts[1]);
|
$parts[1]);
|
||||||
} else {
|
} else {
|
||||||
return urisprintf(
|
return urisprintf(
|
||||||
'/diffusion/symbol/%s/?jump=true',
|
'/diffusion/symbol/%p/?jump=true',
|
||||||
$symbol);
|
$symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ final class DiffusionRepositoryStorageManagementPanel
|
||||||
AlmanacClusterRepositoryServiceType::SERVICETYPE,
|
AlmanacClusterRepositoryServiceType::SERVICETYPE,
|
||||||
))
|
))
|
||||||
->withPHIDs(array($service_phid))
|
->withPHIDs(array($service_phid))
|
||||||
->needBindings(true)
|
->needActiveBindings(true)
|
||||||
->executeOne();
|
->executeOne();
|
||||||
if (!$service) {
|
if (!$service) {
|
||||||
// TODO: Viewer may not have permission to see the service, or it may
|
// TODO: Viewer may not have permission to see the service, or it may
|
||||||
|
@ -104,7 +104,7 @@ final class DiffusionRepositoryStorageManagementPanel
|
||||||
|
|
||||||
$rows = array();
|
$rows = array();
|
||||||
if ($service) {
|
if ($service) {
|
||||||
$bindings = $service->getBindings();
|
$bindings = $service->getActiveBindings();
|
||||||
$bindings = mgroup($bindings, 'getDevicePHID');
|
$bindings = mgroup($bindings, 'getDevicePHID');
|
||||||
|
|
||||||
// This is an unusual read which always comes from the master.
|
// This is an unusual read which always comes from the master.
|
||||||
|
@ -117,29 +117,19 @@ final class DiffusionRepositoryStorageManagementPanel
|
||||||
|
|
||||||
$versions = mpull($versions, null, 'getDevicePHID');
|
$versions = mpull($versions, null, 'getDevicePHID');
|
||||||
|
|
||||||
// List enabled devices first, then sort devices in each group by name.
|
|
||||||
$sort = array();
|
$sort = array();
|
||||||
foreach ($bindings as $key => $binding_group) {
|
foreach ($bindings as $key => $binding_group) {
|
||||||
$all_disabled = $this->isDisabledGroup($binding_group);
|
|
||||||
|
|
||||||
$sort[$key] = id(new PhutilSortVector())
|
$sort[$key] = id(new PhutilSortVector())
|
||||||
->addInt($all_disabled ? 1 : 0)
|
|
||||||
->addString(head($binding_group)->getDevice()->getName());
|
->addString(head($binding_group)->getDevice()->getName());
|
||||||
}
|
}
|
||||||
$sort = msortv($sort, 'getSelf');
|
$sort = msortv($sort, 'getSelf');
|
||||||
$bindings = array_select_keys($bindings, array_keys($sort)) + $bindings;
|
$bindings = array_select_keys($bindings, array_keys($sort)) + $bindings;
|
||||||
|
|
||||||
foreach ($bindings as $binding_group) {
|
foreach ($bindings as $binding_group) {
|
||||||
$all_disabled = $this->isDisabledGroup($binding_group);
|
|
||||||
$any_binding = head($binding_group);
|
$any_binding = head($binding_group);
|
||||||
|
|
||||||
if ($all_disabled) {
|
$binding_icon = 'fa-folder-open green';
|
||||||
$binding_icon = 'fa-times grey';
|
$binding_tip = pht('Active');
|
||||||
$binding_tip = pht('Disabled');
|
|
||||||
} else {
|
|
||||||
$binding_icon = 'fa-folder-open green';
|
|
||||||
$binding_tip = pht('Active');
|
|
||||||
}
|
|
||||||
|
|
||||||
$binding_icon = id(new PHUIIconView())
|
$binding_icon = id(new PHUIIconView())
|
||||||
->setIcon($binding_icon)
|
->setIcon($binding_icon)
|
||||||
|
@ -376,17 +366,4 @@ final class DiffusionRepositoryStorageManagementPanel
|
||||||
return $box_view;
|
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,
|
DrydockBlueprint $blueprint,
|
||||||
DrydockLease $lease) {
|
DrydockLease $lease) {
|
||||||
$services = $this->loadServices($blueprint);
|
$services = $this->loadServices($blueprint);
|
||||||
$bindings = $this->loadAllBindings($services);
|
$bindings = $this->getActiveBindings($services);
|
||||||
|
|
||||||
if (!$bindings) {
|
if (!$bindings) {
|
||||||
// If there are no devices bound to the services for this blueprint,
|
// If there are no devices bound to the services for this blueprint,
|
||||||
|
@ -222,7 +222,7 @@ final class DrydockAlmanacServiceHostBlueprintImplementation
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withPHIDs($service_phids)
|
->withPHIDs($service_phids)
|
||||||
->withServiceTypes($this->getAlmanacServiceTypes())
|
->withServiceTypes($this->getAlmanacServiceTypes())
|
||||||
->needBindings(true)
|
->needActiveBindings(true)
|
||||||
->execute();
|
->execute();
|
||||||
$services = mpull($services, null, 'getPHID');
|
$services = mpull($services, null, 'getPHID');
|
||||||
|
|
||||||
|
@ -242,9 +242,9 @@ final class DrydockAlmanacServiceHostBlueprintImplementation
|
||||||
return $this->services;
|
return $this->services;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function loadAllBindings(array $services) {
|
private function getActiveBindings(array $services) {
|
||||||
assert_instances_of($services, 'AlmanacService');
|
assert_instances_of($services, 'AlmanacService');
|
||||||
$bindings = array_mergev(mpull($services, 'getBindings'));
|
$bindings = array_mergev(mpull($services, 'getActiveBindings'));
|
||||||
return mpull($bindings, null, 'getPHID');
|
return mpull($bindings, null, 'getPHID');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,15 +271,10 @@ final class DrydockAlmanacServiceHostBlueprintImplementation
|
||||||
$allocated_phids = array_fuse($allocated_phids);
|
$allocated_phids = array_fuse($allocated_phids);
|
||||||
|
|
||||||
$services = $this->loadServices($blueprint);
|
$services = $this->loadServices($blueprint);
|
||||||
$bindings = $this->loadAllBindings($services);
|
$bindings = $this->getActiveBindings($services);
|
||||||
|
|
||||||
$free = array();
|
$free = array();
|
||||||
foreach ($bindings as $binding) {
|
foreach ($bindings as $binding) {
|
||||||
// Don't consider disabled bindings to be available.
|
|
||||||
if ($binding->getIsDisabled()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($allocated_phids[$binding->getPHID()])) {
|
if (empty($allocated_phids[$binding->getPHID()])) {
|
||||||
$free[] = $binding;
|
$free[] = $binding;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ final class FundInitiative extends FundDAO
|
||||||
protected $editPolicy;
|
protected $editPolicy;
|
||||||
protected $status;
|
protected $status;
|
||||||
protected $totalAsCurrency;
|
protected $totalAsCurrency;
|
||||||
protected $mailKey;
|
|
||||||
|
|
||||||
private $projectPHIDs = self::ATTACHABLE;
|
private $projectPHIDs = self::ATTACHABLE;
|
||||||
|
|
||||||
|
@ -62,7 +61,6 @@ final class FundInitiative extends FundDAO
|
||||||
'status' => 'text32',
|
'status' => 'text32',
|
||||||
'merchantPHID' => 'phid?',
|
'merchantPHID' => 'phid?',
|
||||||
'totalAsCurrency' => 'text64',
|
'totalAsCurrency' => 'text64',
|
||||||
'mailKey' => 'bytes20',
|
|
||||||
),
|
),
|
||||||
self::CONFIG_APPLICATION_SERIALIZERS => array(
|
self::CONFIG_APPLICATION_SERIALIZERS => array(
|
||||||
'totalAsCurrency' => new PhortuneCurrencySerializer(),
|
'totalAsCurrency' => new PhortuneCurrencySerializer(),
|
||||||
|
@ -78,8 +76,8 @@ final class FundInitiative extends FundDAO
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generatePHID() {
|
public function getPHIDType() {
|
||||||
return PhabricatorPHID::generateNewPHID(FundInitiativePHIDType::TYPECONST);
|
return FundInitiativePHIDType::TYPECONST;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMonogram() {
|
public function getMonogram() {
|
||||||
|
@ -103,13 +101,6 @@ final class FundInitiative extends FundDAO
|
||||||
return ($this->getStatus() == self::STATUS_CLOSED);
|
return ($this->getStatus() == self::STATUS_CLOSED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function save() {
|
|
||||||
if (!$this->mailKey) {
|
|
||||||
$this->mailKey = Filesystem::readRandomCharacters(20);
|
|
||||||
}
|
|
||||||
return parent::save();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -197,6 +197,10 @@ final class PhabricatorObjectHandle
|
||||||
return $this->status;
|
return $this->status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isClosed() {
|
||||||
|
return ($this->status === self::STATUS_CLOSED);
|
||||||
|
}
|
||||||
|
|
||||||
public function setFullName($full_name) {
|
public function setFullName($full_name) {
|
||||||
$this->fullName = $full_name;
|
$this->fullName = $full_name;
|
||||||
return $this;
|
return $this;
|
||||||
|
|
|
@ -61,7 +61,7 @@ final class PhabricatorRepositoryManagementClusterizeWorkflow
|
||||||
array(
|
array(
|
||||||
AlmanacClusterRepositoryServiceType::SERVICETYPE,
|
AlmanacClusterRepositoryServiceType::SERVICETYPE,
|
||||||
))
|
))
|
||||||
->needBindings(true)
|
->needActiveBindings(true)
|
||||||
->executeOne();
|
->executeOne();
|
||||||
if (!$service) {
|
if (!$service) {
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
|
|
|
@ -1151,7 +1151,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
/**
|
/**
|
||||||
* Get a parsed object representation of the repository's remote URI..
|
* 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
|
* @task uri
|
||||||
*/
|
*/
|
||||||
public function getRemoteURIObject() {
|
public function getRemoteURIObject() {
|
||||||
|
@ -2109,7 +2109,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'The Almanac service for this repository is not bound to any '.
|
'The Almanac service for this repository is not bound to any '.
|
||||||
'interfaces.'));
|
'active interfaces.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$uris = array();
|
$uris = array();
|
||||||
|
@ -2531,7 +2531,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
$service = id(new AlmanacServiceQuery())
|
$service = id(new AlmanacServiceQuery())
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
->withPHIDs(array($service_phid))
|
->withPHIDs(array($service_phid))
|
||||||
->needBindings(true)
|
->needActiveBindings(true)
|
||||||
->needProperties(true)
|
->needProperties(true)
|
||||||
->executeOne();
|
->executeOne();
|
||||||
if (!$service) {
|
if (!$service) {
|
||||||
|
|
|
@ -169,8 +169,8 @@ this:
|
||||||
{
|
{
|
||||||
...
|
...
|
||||||
"constraints": {
|
"constraints": {
|
||||||
"authors": ["PHID-USER-1111", "PHID-USER-2222"],
|
"authorPHIDs": ["PHID-USER-1111", "PHID-USER-2222"],
|
||||||
"statuses": ["open", "closed"],
|
"flavors": ["cherry", "orange"],
|
||||||
...
|
...
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
|
|
|
@ -44,6 +44,16 @@ final class PhabricatorSystemDebugUIEventListener
|
||||||
->setName(pht('View Hovercard'))
|
->setName(pht('View Hovercard'))
|
||||||
->setHref(urisprintf('/search/hovercard/?names=%s', $phid));
|
->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())
|
$developer_action = id(new PhabricatorActionView())
|
||||||
->setName(pht('Advanced/Developer...'))
|
->setName(pht('Advanced/Developer...'))
|
||||||
->setIcon('fa-magic')
|
->setIcon('fa-magic')
|
||||||
|
|
|
@ -21,7 +21,7 @@ available by looking at all of the subclasses of
|
||||||
@{class@phabricator:PhabricatorApplication}. It
|
@{class@phabricator:PhabricatorApplication}. It
|
||||||
discovers available workflows in `arc` by looking at all of the subclasses of
|
discovers available workflows in `arc` by looking at all of the subclasses of
|
||||||
@{class@arcanist:ArcanistWorkflow}. It discovers available locales
|
@{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
|
This pattern holds in many cases, so you can often add functionality by adding
|
||||||
new classes with no other work. Phabricator will automatically discover and
|
new classes with no other work. 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
|
features, or get started on a larger project. Extending Phabricator like this
|
||||||
imposes a small performance penalty compared to using a library.
|
imposes a small performance penalty compared to using a library.
|
||||||
|
|
||||||
This directory exists in all libphutil libraries, so you can find similar
|
This directory exists in all libphutil libraries, so you can find a similar
|
||||||
directories in `arcanist/src/extensions/` and `libphutil/src/extensions/`.
|
directory in `arcanist/src/extensions/`.
|
||||||
|
|
||||||
For example, to add a new application, create a file like this one and add it
|
For example, to add a new application, create a file like this one and add it
|
||||||
to `phabricator/src/extensions/`.
|
to `phabricator/src/extensions/`.
|
||||||
|
@ -171,8 +171,8 @@ performing static analysis.
|
||||||
NOTE: If Phabricator isn't located next to your custom library, specify a
|
NOTE: If Phabricator isn't located next to your custom library, specify a
|
||||||
path which actually points to the `phabricator/` directory.
|
path which actually points to the `phabricator/` directory.
|
||||||
|
|
||||||
You do not need to declare dependencies on `arcanist` or `libphutil`,
|
You do not need to declare dependencies on `arcanist`, since `arc liberate`
|
||||||
since `arc liberate` automatically loads them.
|
automatically loads them.
|
||||||
|
|
||||||
Finally, edit your Phabricator config to tell it to load your library at
|
Finally, edit your Phabricator config to tell it to load your library at
|
||||||
runtime, by adding it to `load-libraries`:
|
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
|
What You Can Extend And Invoke
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
libphutil, Arcanist and Phabricator are strict about extensibility of classes
|
Arcanist and Phabricator are strict about extensibility of classes and
|
||||||
and visibility of methods and properties. Most classes are marked `final`, and
|
visibility of methods and properties. Most classes are marked `final`, and
|
||||||
methods have the minimum required visibility (protected or private). The goal
|
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
|
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.
|
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
|
IMPORTANT: We'll still break APIs frequently. The upstream does not support
|
||||||
extension development, and none of these APIs are stable.
|
extension development, and none of these APIs are stable.
|
||||||
|
|
||||||
When developing libraries to work with libphutil, Arcanist and Phabricator, you
|
When developing libraries to work with Arcanist and Phabricator, you should
|
||||||
should respect method and property visibility.
|
respect method and property visibility.
|
||||||
|
|
||||||
If you want to add features but can't figure out how to do it without changing
|
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:
|
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
|
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
|
you should update again. If you aren't sure how to update, see the next
|
||||||
section.
|
section.
|
||||||
- **Update Libraries**: Make sure `libphutil/`, `arcanist/` and
|
- **Update Libraries**: Make sure `arcanist/` and `phabricator/` are all up
|
||||||
`phabricator/` are all up to date. Users often update `phabricator/` but
|
to date. Users often update `phabricator/` but forget to update `arcanist/`.
|
||||||
forget to update `arcanist/` or `libphutil/`. When you update, make sure you
|
When you update, make sure you update all three libraries.
|
||||||
update all three libraries.
|
|
||||||
- **Restart Apache or PHP-FPM**: Phabricator uses caches which don't get
|
- **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
|
reset until you restart Apache or PHP-FPM. After updating, make sure you
|
||||||
restart.
|
restart.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
@title Contributor Introduction
|
@title Contributor Introduction
|
||||||
@group contrib
|
@group contrib
|
||||||
|
|
||||||
Introduction to contributing to Phabricator, Arcanist and libphutil.
|
Introduction to contributing to Phabricator and Arcanist.
|
||||||
|
|
||||||
Overview
|
Overview
|
||||||
========
|
========
|
||||||
|
|
|
@ -212,9 +212,9 @@ outside of our comfort zone.
|
||||||
Writing and Submitting Patches
|
Writing and Submitting Patches
|
||||||
==================
|
==================
|
||||||
|
|
||||||
To actually submit a patch, run `arc diff` in `phabricator/`, `arcanist/`, or
|
To actually submit a patch, run `arc diff` in `phabricator/` or `arcanist/`.
|
||||||
`libphutil/`. When executed in these directories, `arc` should automatically
|
When executed in these directories, `arc` should automatically talk to the
|
||||||
talk to the upstream install. You can add `epriestley` as a reviewer.
|
upstream install. You can add `epriestley` as a reviewer.
|
||||||
|
|
||||||
You should read the relevant coding convention documents before you submit a
|
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
|
change. If you're a new contributor, you don't need to worry about this too
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
@group standards
|
@group standards
|
||||||
|
|
||||||
This document is a general coding standard for contributing to Phabricator,
|
This document is a general coding standard for contributing to Phabricator,
|
||||||
Arcanist, libphutil and Diviner.
|
Arcanist, and Diviner.
|
||||||
|
|
||||||
= Overview =
|
= Overview =
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ handling and makes it easier to get right than wrong:
|
||||||
Filesystem::writeFile('file.bak', $data); // Best
|
Filesystem::writeFile('file.bak', $data); // Best
|
||||||
do_something_dangerous();
|
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.
|
example.
|
||||||
|
|
||||||
= Documentation, Comments and Formatting =
|
= Documentation, Comments and Formatting =
|
||||||
|
|
|
@ -44,7 +44,7 @@ For instructions on adding new classes, see
|
||||||
Writing Translatable Code
|
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 `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
|
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.
|
- Use parameters to create strings containing user names, object names, etc.
|
||||||
- Translate full sentences, not sentence fragments.
|
- Translate full sentences, not sentence fragments.
|
||||||
- Let the translation framework handle plural rules.
|
- 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.
|
- Let the translation framework handle subject gender rules.
|
||||||
- Translate all human-readable text, even exceptions and error messages.
|
- 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.
|
languages, and languages like Czech also require verb agreement.
|
||||||
|
|
||||||
When a parameter refers to a gendered person, pass an object which implements
|
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.
|
gendered translation variants.
|
||||||
|
|
||||||
```lang=php
|
```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
|
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
|
probably appropriate to define an exception for that category of error rather
|
||||||
than write the text out repeatedly, anyway. Two examples are
|
than write the text out repeatedly, anyway. Two examples are
|
||||||
@{class@libphutil:PhutilInvalidStateException} and
|
@{class@arcanist:PhutilInvalidStateException} and
|
||||||
@{class@libphutil:PhutilMethodNotImplementedException}, which mostly exist to
|
@{class@arcanist:PhutilMethodNotImplementedException}, which mostly exist to
|
||||||
produce a consistent message about a common error state in a convenient way.
|
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
|
There are a handful of error strings in the codebase which may be used before
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
@group standards
|
@group standards
|
||||||
|
|
||||||
This document describes PHP coding standards for Phabricator and related
|
This document describes PHP coding standards for Phabricator and related
|
||||||
projects (like Arcanist and libphutil).
|
projects (like Arcanist).
|
||||||
|
|
||||||
= Overview =
|
= Overview =
|
||||||
|
|
||||||
This document outlines technical and style guidelines which are followed in
|
This document outlines technical and style guidelines which are followed in
|
||||||
libphutil. Contributors should also follow these guidelines. Many of these
|
Phabricator and Arcanist. Contributors should also follow these guidelines.
|
||||||
guidelines are automatically enforced by lint.
|
Many of these guidelines are automatically enforced by lint.
|
||||||
|
|
||||||
These guidelines are essentially identical to the Facebook guidelines, since I
|
These guidelines are essentially identical to the Facebook guidelines, since I
|
||||||
basically copy-pasted them. If you are already familiar with the Facebook
|
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
|
This document describes the right way to build HTML components so they are safe
|
||||||
from XSS and render correctly. Broadly:
|
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.
|
tags.
|
||||||
- Use @{function@libphutil:hsprintf} where @{function@libphutil:phutil_tag}
|
- Use @{function@arcanist:hsprintf} where @{function@arcanist:phutil_tag}
|
||||||
is awkward.
|
is awkward.
|
||||||
- Combine elements with arrays, not string concatenation.
|
- Combine elements with arrays, not string concatenation.
|
||||||
- @{class:AphrontView} subclasses should return a
|
- @{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.
|
- @{class:AphrontView} subclasses act like tags when rendering.
|
||||||
- @{function:pht} has some special rules.
|
- @{function:pht} has some special rules.
|
||||||
- There are some other things that you should be aware of.
|
- There are some other things that you should be aware of.
|
||||||
|
@ -28,7 +28,7 @@ See below for discussion.
|
||||||
|
|
||||||
= Building Tags: phutil_tag() =
|
= 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(
|
phutil_tag(
|
||||||
'div',
|
'div',
|
||||||
|
@ -37,10 +37,10 @@ Build HTML tags with @{function@libphutil:phutil_tag}. For example:
|
||||||
),
|
),
|
||||||
$content);
|
$content);
|
||||||
|
|
||||||
@{function@libphutil:phutil_tag} will properly escape the content and all the
|
@{function@arcanist:phutil_tag} will properly escape the content and all the
|
||||||
attributes, and return a @{class@libphutil:PhutilSafeHTML} object. The rendering
|
attributes, and return a @{class@arcanist:PhutilSafeHTML} object. The rendering
|
||||||
pipeline knows that this object represents a properly escaped HTML tag. This
|
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):
|
content correctly (without double-escaping):
|
||||||
|
|
||||||
phutil_tag(
|
phutil_tag(
|
||||||
|
@ -52,14 +52,14 @@ content correctly (without double-escaping):
|
||||||
$content));
|
$content));
|
||||||
|
|
||||||
In Phabricator, the @{function:javelin_tag} function is similar to
|
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.
|
`sigil` and `meta` attributes.
|
||||||
|
|
||||||
= Building Blocks: hsprintf() =
|
= Building Blocks: hsprintf() =
|
||||||
|
|
||||||
Sometimes, @{function@libphutil:phutil_tag} can be particularly awkward to
|
Sometimes, @{function@arcanist:phutil_tag} can be particularly awkward to
|
||||||
use. You can use @{function@libphutil:hsprintf} to build larger and more
|
use. You can use @{function@arcanist:hsprintf} to build larger and more
|
||||||
complex blocks of HTML, when @{function@libphutil:phutil_tag} is a poor fit.
|
complex blocks of HTML, when @{function@arcanist:phutil_tag} is a poor fit.
|
||||||
@{function:hsprintf} has `sprintf()` semantics, but `%s` escapes HTML:
|
@{function:hsprintf} has `sprintf()` semantics, but `%s` escapes HTML:
|
||||||
|
|
||||||
// Safely build fragments or unwieldy blocks.
|
// 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
|
- You need to build a block with a lot of tags, like a table with rows and
|
||||||
cells.
|
cells.
|
||||||
- You need to build part of a tag (usually you should avoid this, but if you
|
- 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
|
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
|
Like @{function@arcanist:phutil_tag}, this function returns a
|
||||||
@{class@libphutil:PhutilSafeHTML} object.
|
@{class@arcanist:PhutilSafeHTML} object.
|
||||||
|
|
||||||
= Composing Tags =
|
= Composing Tags =
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ Instead, use an array:
|
||||||
// Render a tag containing other tags safely.
|
// Render a tag containing other tags safely.
|
||||||
phutil_tag('div', array(), array($header, $body));
|
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.
|
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
|
(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 =
|
= AphrontView Classes =
|
||||||
|
|
||||||
Subclasses of @{class:AphrontView} in Phabricator should return a
|
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()` or `javelin_tag()`:
|
||||||
|
|
||||||
return phutil_tag('div', ...);
|
return phutil_tag('div', ...);
|
||||||
|
@ -130,8 +130,8 @@ You can use an @{class:AphrontView} subclass like you would a tag:
|
||||||
= Internationalization: pht() =
|
= Internationalization: pht() =
|
||||||
|
|
||||||
The @{function:pht} function has some special rules. If any input to
|
The @{function:pht} function has some special rules. If any input to
|
||||||
@{function:pht} is a @{class@libphutil:PhutilSafeHTML} object, @{function:pht}
|
@{function:pht} is a @{class@arcanist:PhutilSafeHTML} object, @{function:pht}
|
||||||
returns a @{class@libphutil:PhutilSafeHTML} object itself. Otherwise, it returns
|
returns a @{class@arcanist:PhutilSafeHTML} object itself. Otherwise, it returns
|
||||||
normal text.
|
normal text.
|
||||||
|
|
||||||
This is generally safe because translations are not permitted to have more tags
|
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.
|
NOTE: This section describes dangerous methods which can bypass XSS protections.
|
||||||
If possible, do not use them.
|
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
|
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
|
you are wrong and the string is not actually safe, you have introduced an XSS
|
||||||
vulnerability. Consequently, you should avoid calling this if possible.
|
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
|
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
|
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,
|
object, you should call `getHTMLContent()`, not cast it to a string. Eventually,
|
||||||
we would like to remove the string cast entirely.
|
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
|
are not safe if you pass the user input for the tag or attribute name. All the
|
||||||
following examples are dangerous:
|
following examples are dangerous:
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
@title Writing Unit Tests
|
@title Writing Unit Tests
|
||||||
@group developer
|
@group developer
|
||||||
|
|
||||||
Simple guide to libphutil, Arcanist and Phabricator unit tests.
|
Simple guide to Arcanist and Phabricator unit tests.
|
||||||
|
|
||||||
= Overview =
|
= Overview =
|
||||||
|
|
||||||
libphutil, Arcanist and Phabricator provide and use a simple unit test
|
Arcanist and Phabricator provide and use a simple unit test framework. This
|
||||||
framework. This document is aimed at project contributors and describes how to
|
document is aimed at project contributors and describes how to use it to add
|
||||||
use it to add and run tests in these projects or other libphutil libraries.
|
and run tests in these projects or other libphutil libraries.
|
||||||
|
|
||||||
In the general case, you can integrate `arc` with a custom unit test engine
|
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.
|
(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 =
|
= 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.
|
- Create a `__tests__/` directory in the module if it doesn't exist yet.
|
||||||
- Add classes to the `__tests__/` directory which extend from
|
- 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
|
intermediate arrays and copies every element it has previously seen each time
|
||||||
you iterate.
|
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.
|
instead.
|
||||||
|
|
||||||
= `var_export()` Hates Baby Animals =
|
= `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.
|
instead, and use it to reorder the original array.
|
||||||
|
|
||||||
In a libphutil environment, you can often do this easily with
|
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 =
|
= `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
|
...you'll probably invent a very interesting, very novel solution that is very
|
||||||
wrong. In a libphutil environment, solve this problem with
|
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 =
|
= 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**:
|
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;
|
developing daemon infrastructure or debugging a daemon problem;
|
||||||
- **PhabricatorTaskmasterDaemon** performs work from a task queue;
|
- **PhabricatorTaskmasterDaemon** performs work from a task queue;
|
||||||
- **PhabricatorRepositoryPullLocalDaemon** daemons track repositories, for
|
- **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
|
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.
|
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
|
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
|
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,
|
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
|
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.
|
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:
|
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
|
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.
|
Now add `some_install_path/arcanist/bin/` to your PATH environment variable.
|
||||||
When you type "arc", you should see something like this:
|
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 Windows: @{article:Arcanist User Guide: Windows}
|
||||||
- On Mac OS X: @{article:Arcanist User Guide: Mac OS X}
|
- On Mac OS X: @{article:Arcanist User Guide: Mac OS X}
|
||||||
|
|
||||||
You can later upgrade Arcanist and libphutil to the latest versions with
|
You can later upgrade Arcanist to the latest version with `arc upgrade`:
|
||||||
`arc upgrade`:
|
|
||||||
|
|
||||||
$ 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:
|
able to use:
|
||||||
|
|
||||||
- Facebook does most development on development servers, which have a standard
|
- 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
|
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
|
mount source updates everyone's versions, and new employees have a working
|
||||||
`arc` when they first log in.
|
`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
|
If the test engine enables coverage by default, it will be uploaded to
|
||||||
Differential and displayed in the right gutter when viewing diffs.
|
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:
|
you install Xdebug:
|
||||||
|
|
||||||
http://xdebug.org/
|
http://xdebug.org/
|
||||||
|
|
|
@ -20,9 +20,6 @@ First, install dependencies:
|
||||||
|
|
||||||
Then install Arcanist itself:
|
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
|
somewhere/ $ git clone https://github.com/phacility/arcanist.git
|
||||||
|
|
||||||
Add `arc` to your path:
|
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 and making calls. This is the best starting point for learning about
|
||||||
the API. See the next section for details.
|
the API. See the next section for details.
|
||||||
|
|
||||||
`ConduitClient`: This is the official client available in `libphutil`, and
|
`ConduitClient`: This is the official client available in `arcanist`.
|
||||||
the one used by `arc`.
|
|
||||||
|
|
||||||
`arc call-conduit`: You can use this `arc` command to execute low-level
|
`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
|
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
|
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
|
single-letter callsigns for brevity. For example, Facebook uses "E" for the
|
||||||
Engineering repository, "O" for the Ops repository, "Y" for a Yum package
|
Engineering repository, "O" for the Ops repository, "Y" for a Yum package
|
||||||
repository, and so on, while Phabricator uses "P", "ARC", "PHU" for libphutil,
|
repository, and so on, while Phabricator uses "P" and Arcanist uses "ARC".
|
||||||
and "J" for Javelin. Keeping callsigns brief will make them easier to use, and
|
Keeping callsigns brief will make them easier to use, and the use of
|
||||||
the use of one-character callsigns is encouraged if they are reasonably
|
one-character callsigns is encouraged if they are reasonably evocative.
|
||||||
evocative.
|
|
||||||
|
|
||||||
If you configure a callsign like `XYZ`, Phabricator will activate callsign URIs
|
If you configure a callsign like `XYZ`, Phabricator will activate callsign URIs
|
||||||
and activate the callsign identifier (like `rXYZ`) for the repository. These
|
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".
|
You can leave this blank for "All languages".
|
||||||
- **Uses Symbols From**: If this project depends on other repositories, add
|
- **Uses Symbols From**: If this project depends on other repositories, add
|
||||||
the other repositories which symbols should be looked for here. For example,
|
the other repositories which symbols should be looked for here. For example,
|
||||||
Phabricator lists "Arcanist" and "libphutil" because it uses classes and
|
Phabricator lists "Arcanist" because it uses classes and functions defined
|
||||||
functions from these repositories.
|
in `arcanist/`.
|
||||||
|
|
||||||
== External Symbols ==
|
== 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`
|
properly with any software you need, and have tools like `git`, `hg` or `svn`
|
||||||
that may be required to interact with working copies.
|
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.
|
hosts unless you are specifically running `arc` commands.
|
||||||
|
|
||||||
**You must configure authentication.** Drydock also does not handle credentials
|
**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:
|
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,
|
- Add it to a libphutil library, or create a new library (for instructions,
|
||||||
see @{article@phabcontrib:Adding New Classes}.
|
see @{article@phabcontrib:Adding New Classes}.
|
||||||
- Configure Phabricator to load the library by adding it to `load-libraries`
|
- 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:
|
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,
|
- Add it to a libphutil library, or create a new library (for instructions,
|
||||||
see @{article@phabcontrib:Adding New Classes}.
|
see @{article@phabcontrib:Adding New Classes}.
|
||||||
- Configure Phabricator to load the library by adding it to `load`
|
- 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
|
Encodings" below). This is not completely supported, and repositories with
|
||||||
files that have multiple encodings are not supported.
|
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 =
|
= Support for Alternate Encodings =
|
||||||
|
|
||||||
Phabricator has some support for encodings other than UTF-8.
|
Phabricator has some support for encodings other than UTF-8.
|
||||||
|
|
|
@ -66,7 +66,8 @@ abstract class PhabricatorWorkerTask extends PhabricatorWorkerDAO {
|
||||||
$class = $this->getTaskClass();
|
$class = $this->getTaskClass();
|
||||||
|
|
||||||
try {
|
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);
|
class_exists($class);
|
||||||
} catch (PhutilMissingSymbolException $ex) {
|
} catch (PhutilMissingSymbolException $ex) {
|
||||||
throw new PhabricatorWorkerPermanentFailureException(
|
throw new PhabricatorWorkerPermanentFailureException(
|
||||||
|
|
|
@ -116,7 +116,7 @@
|
||||||
* $pugs = $dog->loadAllWhere('breed = %s', 'Pug');
|
* $pugs = $dog->loadAllWhere('breed = %s', 'Pug');
|
||||||
* $sawyer = $dog->loadOneWhere('name = %s', 'Sawyer');
|
* $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,
|
* 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.
|
* columns, and object construction; you are responsible for the rest of it.
|
||||||
* @{method:loadAllWhere} returns a list of objects, while
|
* @{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() {
|
public static function getMicrosecondsSinceStart() {
|
||||||
// This is the same as "phutil_microseconds_since()", but we may not have
|
// 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()));
|
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