1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-25 08:12:40 +01:00

(stable) Promote 2016 Week 9

This commit is contained in:
epriestley 2016-02-27 04:17:11 -08:00
commit 9d051cddd3
178 changed files with 4373 additions and 2469 deletions

View file

@ -7,7 +7,7 @@
*/
return array(
'names' => array(
'core.pkg.css' => '7935f211',
'core.pkg.css' => 'ecdca229',
'core.pkg.js' => '7d8faf57',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '2de124c9',
@ -35,7 +35,7 @@ return array(
'rsrc/css/application/base/main-menu-view.css' => 'd00a795a',
'rsrc/css/application/base/notification-menu.css' => 'f31c0bde',
'rsrc/css/application/base/phabricator-application-launch-view.css' => '95351601',
'rsrc/css/application/base/phui-theme.css' => 'ab7b848c',
'rsrc/css/application/base/phui-theme.css' => '027ba77e',
'rsrc/css/application/base/standard-page-view.css' => 'e709f6d0',
'rsrc/css/application/chatlog/chatlog.css' => 'd295b020',
'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4',
@ -81,7 +81,7 @@ return array(
'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b',
'rsrc/css/application/paste/paste.css' => 'a5157c48',
'rsrc/css/application/people/people-profile.css' => '2473d929',
'rsrc/css/application/phame/phame.css' => '4ca6fd6c',
'rsrc/css/application/phame/phame.css' => '737792d6',
'rsrc/css/application/pholio/pholio-edit.css' => '3ad9d1ee',
'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
'rsrc/css/application/pholio/pholio.css' => 'ca89d380',
@ -92,9 +92,9 @@ return array(
'rsrc/css/application/policy/policy-edit.css' => '815c66f7',
'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43',
'rsrc/css/application/policy/policy.css' => '957ea14c',
'rsrc/css/application/ponder/ponder-view.css' => 'b40dc156',
'rsrc/css/application/ponder/ponder-view.css' => '212495e0',
'rsrc/css/application/project/project-card-view.css' => '9418c97d',
'rsrc/css/application/project/project-view.css' => '83bb6654',
'rsrc/css/application/project/project-view.css' => '298b7c5b',
'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733',
'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5',
'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd',
@ -127,14 +127,14 @@ return array(
'rsrc/css/phui/phui-button.css' => 'edf464e9',
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
'rsrc/css/phui/phui-crumbs-view.css' => '79d536e5',
'rsrc/css/phui/phui-document-pro.css' => 'a8872307',
'rsrc/css/phui/phui-document-pro.css' => '92d5b648',
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
'rsrc/css/phui/phui-document.css' => '9c71d2bf',
'rsrc/css/phui/phui-feed-story.css' => '04aec08f',
'rsrc/css/phui/phui-fontkit.css' => '9cda225e',
'rsrc/css/phui/phui-form-view.css' => '4a1a0f5e',
'rsrc/css/phui/phui-form.css' => 'aac1d51d',
'rsrc/css/phui/phui-header-view.css' => '50c5cb6a',
'rsrc/css/phui/phui-header-view.css' => 'a6d7b20d',
'rsrc/css/phui/phui-hovercard.css' => 'de1a2119',
'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad',
'rsrc/css/phui/phui-icon.css' => '3f33ab57',
@ -154,7 +154,7 @@ return array(
'rsrc/css/phui/phui-status.css' => '888cedb8',
'rsrc/css/phui/phui-tag-view.css' => '9d5d4400',
'rsrc/css/phui/phui-timeline-view.css' => '2efceff8',
'rsrc/css/phui/phui-two-column-view.css' => '0763177e',
'rsrc/css/phui/phui-two-column-view.css' => 'a317616a',
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7',
'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647',
'rsrc/css/phui/workboards/phui-workcard.css' => '3646fb96',
@ -790,7 +790,7 @@ return array(
'phabricator-uiexample-reactor-sendclass' => '1def2711',
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
'phabricator-zindex-css' => '5b6fcf3f',
'phame-css' => '4ca6fd6c',
'phame-css' => '737792d6',
'pholio-css' => 'ca89d380',
'pholio-edit-css' => '3ad9d1ee',
'pholio-inline-comments-css' => '8e545e49',
@ -812,13 +812,13 @@ return array(
'phui-crumbs-view-css' => '79d536e5',
'phui-document-summary-view-css' => '9ca48bdf',
'phui-document-view-css' => '9c71d2bf',
'phui-document-view-pro-css' => 'a8872307',
'phui-document-view-pro-css' => '92d5b648',
'phui-feed-story-css' => '04aec08f',
'phui-font-icon-base-css' => 'ecbbb4c2',
'phui-fontkit-css' => '9cda225e',
'phui-form-css' => 'aac1d51d',
'phui-form-view-css' => '4a1a0f5e',
'phui-header-view-css' => '50c5cb6a',
'phui-header-view-css' => 'a6d7b20d',
'phui-hovercard' => '1bd28176',
'phui-hovercard-view-css' => 'de1a2119',
'phui-icon-set-selector-css' => '1ab67aad',
@ -839,9 +839,9 @@ return array(
'phui-spacing-css' => '042804d6',
'phui-status-list-view-css' => '888cedb8',
'phui-tag-view-css' => '9d5d4400',
'phui-theme-css' => 'ab7b848c',
'phui-theme-css' => '027ba77e',
'phui-timeline-view-css' => '2efceff8',
'phui-two-column-view-css' => '0763177e',
'phui-two-column-view-css' => 'a317616a',
'phui-workboard-color-css' => 'ac6fe6a7',
'phui-workboard-view-css' => 'e6d89647',
'phui-workcard-view-css' => '3646fb96',
@ -855,9 +855,9 @@ return array(
'policy-css' => '957ea14c',
'policy-edit-css' => '815c66f7',
'policy-transaction-detail-css' => '82100a43',
'ponder-view-css' => 'b40dc156',
'ponder-view-css' => '212495e0',
'project-card-view-css' => '9418c97d',
'project-view-css' => '83bb6654',
'project-view-css' => '298b7c5b',
'releeph-core' => '9b3c5733',
'releeph-preview-branch' => 'b7a6f4a5',
'releeph-request-differential-create-dialog' => '8d8b92cd',

View file

@ -0,0 +1,7 @@
CREATE TABLE {$NAMESPACE}_almanac.almanac_devicename_ngrams (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
objectID INT UNSIGNED NOT NULL,
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
KEY `key_object` (objectID),
KEY `key_ngram` (ngram, objectID)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,11 @@
<?php
$table = new AlmanacDevice();
foreach (new LiskMigrationIterator($table) as $device) {
PhabricatorSearchWorker::queueDocumentForIndexing(
$device->getPHID(),
array(
'force' => true,
));
}

View file

@ -0,0 +1,7 @@
CREATE TABLE {$NAMESPACE}_almanac.almanac_servicename_ngrams (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
objectID INT UNSIGNED NOT NULL,
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
KEY `key_object` (objectID),
KEY `key_ngram` (ngram, objectID)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,11 @@
<?php
$table = new AlmanacService();
foreach (new LiskMigrationIterator($table) as $service) {
PhabricatorSearchWorker::queueDocumentForIndexing(
$service->getPHID(),
array(
'force' => true,
));
}

View file

@ -0,0 +1,7 @@
CREATE TABLE {$NAMESPACE}_almanac.almanac_networkname_ngrams (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
objectID INT UNSIGNED NOT NULL,
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
KEY `key_object` (objectID),
KEY `key_ngram` (ngram, objectID)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,11 @@
<?php
$table = new AlmanacNetwork();
foreach (new LiskMigrationIterator($table) as $network) {
PhabricatorSearchWorker::queueDocumentForIndexing(
$network->getPHID(),
array(
'force' => true,
));
}

View file

@ -0,0 +1,7 @@
CREATE TABLE {$NAMESPACE}_almanac.almanac_namespacename_ngrams (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
objectID INT UNSIGNED NOT NULL,
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
KEY `key_object` (objectID),
KEY `key_ngram` (ngram, objectID)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,14 @@
CREATE TABLE {$NAMESPACE}_almanac.almanac_namespace (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
name VARCHAR(128) NOT NULL COLLATE {$COLLATE_TEXT},
nameIndex BINARY(12) NOT NULL,
mailKey BINARY(20) NOT NULL,
viewPolicy VARBINARY(64) NOT NULL,
editPolicy VARBINARY(64) NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (phid),
UNIQUE KEY `key_nameindex` (nameIndex),
KEY `key_name` (name)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,19 @@
CREATE TABLE {$NAMESPACE}_almanac.almanac_namespacetransaction (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
authorPHID VARBINARY(64) NOT NULL,
objectPHID VARBINARY(64) NOT NULL,
viewPolicy VARBINARY(64) NOT NULL,
editPolicy VARBINARY(64) NOT NULL,
commentPHID VARBINARY(64) DEFAULT NULL,
commentVersion INT UNSIGNED NOT NULL,
transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (`phid`),
KEY `key_object` (`objectPHID`)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,28 @@
<?php
$table = new AlmanacProperty();
$conn_w = $table->establishConnection('w');
// We're going to JSON-encode the value in each row: previously rows stored
// plain strings, but now they store JSON, so we need to update them.
foreach (new LiskMigrationIterator($table) as $property) {
$key = $property->getFieldName();
$current_row = queryfx_one(
$conn_w,
'SELECT fieldValue FROM %T WHERE id = %d',
$table->getTableName(),
$property->getID());
if (!$current_row) {
continue;
}
queryfx(
$conn_w,
'UPDATE %T SET fieldValue = %s WHERE id = %d',
$table->getTableName(),
phutil_json_encode($current_row['fieldValue']),
$property->getID());
}

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_almanac.almanac_device
ADD isBoundToClusterService BOOL NOT NULL;

View file

@ -0,0 +1,2 @@
UPDATE {$NAMESPACE}_almanac.almanac_device
SET isBoundToClusterService = isLocked;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_almanac.almanac_device
DROP isLocked;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_almanac.almanac_service
DROP isLocked;

View file

@ -0,0 +1,21 @@
<?php
// For a while in November 2015, attachment edges between pastes and their
// underlying file data were not written correctly. This restores edges for
// any missing pastes.
$table = new PhabricatorPaste();
$edge_type = PhabricatorObjectHasFileEdgeType::EDGECONST;
foreach (new LiskMigrationIterator($table) as $paste) {
$paste_phid = $paste->getPHID();
$file_phid = $paste->getFilePHID();
if (!$file_phid) {
continue;
}
id(new PhabricatorEdgeEditor())
->addEdge($paste_phid, $edge_type, $file_phid)
->save();
}

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_almanac.almanac_binding
ADD isDisabled BOOL NOT NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_almanac.almanac_service
CHANGE serviceClass serviceType VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,30 @@
<?php
// Previously, Almanac services stored a "serviceClass". Migrate these to
// new "serviceType" values.
$table = new AlmanacService();
$conn_w = $table->establishConnection('w');
foreach (new LiskMigrationIterator($table) as $service) {
$new_type = null;
try {
$old_type = $service->getServiceType();
$object = newv($old_type, array());
$new_type = $object->getServiceTypeConstant();
} catch (Exception $ex) {
continue;
}
if (!$new_type) {
continue;
}
queryfx(
$conn_w,
'UPDATE %T SET serviceType = %s WHERE id = %d',
$table->getTableName(),
$new_type,
$service->getID());
}

View file

@ -11,26 +11,26 @@ phutil_register_library_map(array(
'class' => array(
'AlmanacAddress' => 'applications/almanac/util/AlmanacAddress.php',
'AlmanacBinding' => 'applications/almanac/storage/AlmanacBinding.php',
'AlmanacBindingDisableController' => 'applications/almanac/controller/AlmanacBindingDisableController.php',
'AlmanacBindingEditController' => 'applications/almanac/controller/AlmanacBindingEditController.php',
'AlmanacBindingEditor' => 'applications/almanac/editor/AlmanacBindingEditor.php',
'AlmanacBindingPHIDType' => 'applications/almanac/phid/AlmanacBindingPHIDType.php',
'AlmanacBindingPropertyEditEngine' => 'applications/almanac/editor/AlmanacBindingPropertyEditEngine.php',
'AlmanacBindingQuery' => 'applications/almanac/query/AlmanacBindingQuery.php',
'AlmanacBindingTableView' => 'applications/almanac/view/AlmanacBindingTableView.php',
'AlmanacBindingTransaction' => 'applications/almanac/storage/AlmanacBindingTransaction.php',
'AlmanacBindingTransactionQuery' => 'applications/almanac/query/AlmanacBindingTransactionQuery.php',
'AlmanacBindingViewController' => 'applications/almanac/controller/AlmanacBindingViewController.php',
'AlmanacBindingsSearchEngineAttachment' => 'applications/almanac/engineextension/AlmanacBindingsSearchEngineAttachment.php',
'AlmanacClusterDatabaseServiceType' => 'applications/almanac/servicetype/AlmanacClusterDatabaseServiceType.php',
'AlmanacClusterRepositoryServiceType' => 'applications/almanac/servicetype/AlmanacClusterRepositoryServiceType.php',
'AlmanacClusterServiceType' => 'applications/almanac/servicetype/AlmanacClusterServiceType.php',
'AlmanacConduitAPIMethod' => 'applications/almanac/conduit/AlmanacConduitAPIMethod.php',
'AlmanacConsoleController' => 'applications/almanac/controller/AlmanacConsoleController.php',
'AlmanacController' => 'applications/almanac/controller/AlmanacController.php',
'AlmanacCoreCustomField' => 'applications/almanac/customfield/AlmanacCoreCustomField.php',
'AlmanacCreateClusterServicesCapability' => 'applications/almanac/capability/AlmanacCreateClusterServicesCapability.php',
'AlmanacCreateDevicesCapability' => 'applications/almanac/capability/AlmanacCreateDevicesCapability.php',
'AlmanacCreateNamespacesCapability' => 'applications/almanac/capability/AlmanacCreateNamespacesCapability.php',
'AlmanacCreateNetworksCapability' => 'applications/almanac/capability/AlmanacCreateNetworksCapability.php',
'AlmanacCreateServicesCapability' => 'applications/almanac/capability/AlmanacCreateServicesCapability.php',
'AlmanacCustomField' => 'applications/almanac/customfield/AlmanacCustomField.php',
'AlmanacCustomServiceType' => 'applications/almanac/servicetype/AlmanacCustomServiceType.php',
'AlmanacDAO' => 'applications/almanac/storage/AlmanacDAO.php',
'AlmanacDevice' => 'applications/almanac/storage/AlmanacDevice.php',
@ -38,33 +38,52 @@ phutil_register_library_map(array(
'AlmanacDeviceEditController' => 'applications/almanac/controller/AlmanacDeviceEditController.php',
'AlmanacDeviceEditor' => 'applications/almanac/editor/AlmanacDeviceEditor.php',
'AlmanacDeviceListController' => 'applications/almanac/controller/AlmanacDeviceListController.php',
'AlmanacDeviceNameNgrams' => 'applications/almanac/storage/AlmanacDeviceNameNgrams.php',
'AlmanacDevicePHIDType' => 'applications/almanac/phid/AlmanacDevicePHIDType.php',
'AlmanacDevicePropertyEditEngine' => 'applications/almanac/editor/AlmanacDevicePropertyEditEngine.php',
'AlmanacDeviceQuery' => 'applications/almanac/query/AlmanacDeviceQuery.php',
'AlmanacDeviceSearchConduitAPIMethod' => 'applications/almanac/conduit/AlmanacDeviceSearchConduitAPIMethod.php',
'AlmanacDeviceSearchEngine' => 'applications/almanac/query/AlmanacDeviceSearchEngine.php',
'AlmanacDeviceTransaction' => 'applications/almanac/storage/AlmanacDeviceTransaction.php',
'AlmanacDeviceTransactionQuery' => 'applications/almanac/query/AlmanacDeviceTransactionQuery.php',
'AlmanacDeviceViewController' => 'applications/almanac/controller/AlmanacDeviceViewController.php',
'AlmanacDrydockPoolServiceType' => 'applications/almanac/servicetype/AlmanacDrydockPoolServiceType.php',
'AlmanacEditor' => 'applications/almanac/editor/AlmanacEditor.php',
'AlmanacInterface' => 'applications/almanac/storage/AlmanacInterface.php',
'AlmanacInterfaceDatasource' => 'applications/almanac/typeahead/AlmanacInterfaceDatasource.php',
'AlmanacInterfaceDeleteController' => 'applications/almanac/controller/AlmanacInterfaceDeleteController.php',
'AlmanacInterfaceEditController' => 'applications/almanac/controller/AlmanacInterfaceEditController.php',
'AlmanacInterfacePHIDType' => 'applications/almanac/phid/AlmanacInterfacePHIDType.php',
'AlmanacInterfaceQuery' => 'applications/almanac/query/AlmanacInterfaceQuery.php',
'AlmanacInterfaceTableView' => 'applications/almanac/view/AlmanacInterfaceTableView.php',
'AlmanacKeys' => 'applications/almanac/util/AlmanacKeys.php',
'AlmanacManagementLockWorkflow' => 'applications/almanac/management/AlmanacManagementLockWorkflow.php',
'AlmanacManageClusterServicesCapability' => 'applications/almanac/capability/AlmanacManageClusterServicesCapability.php',
'AlmanacManagementRegisterWorkflow' => 'applications/almanac/management/AlmanacManagementRegisterWorkflow.php',
'AlmanacManagementTrustKeyWorkflow' => 'applications/almanac/management/AlmanacManagementTrustKeyWorkflow.php',
'AlmanacManagementUnlockWorkflow' => 'applications/almanac/management/AlmanacManagementUnlockWorkflow.php',
'AlmanacManagementUntrustKeyWorkflow' => 'applications/almanac/management/AlmanacManagementUntrustKeyWorkflow.php',
'AlmanacManagementWorkflow' => 'applications/almanac/management/AlmanacManagementWorkflow.php',
'AlmanacNames' => 'applications/almanac/util/AlmanacNames.php',
'AlmanacNamesTestCase' => 'applications/almanac/util/__tests__/AlmanacNamesTestCase.php',
'AlmanacNamespace' => 'applications/almanac/storage/AlmanacNamespace.php',
'AlmanacNamespaceController' => 'applications/almanac/controller/AlmanacNamespaceController.php',
'AlmanacNamespaceEditController' => 'applications/almanac/controller/AlmanacNamespaceEditController.php',
'AlmanacNamespaceEditEngine' => 'applications/almanac/editor/AlmanacNamespaceEditEngine.php',
'AlmanacNamespaceEditor' => 'applications/almanac/editor/AlmanacNamespaceEditor.php',
'AlmanacNamespaceListController' => 'applications/almanac/controller/AlmanacNamespaceListController.php',
'AlmanacNamespaceNameNgrams' => 'applications/almanac/storage/AlmanacNamespaceNameNgrams.php',
'AlmanacNamespacePHIDType' => 'applications/almanac/phid/AlmanacNamespacePHIDType.php',
'AlmanacNamespaceQuery' => 'applications/almanac/query/AlmanacNamespaceQuery.php',
'AlmanacNamespaceSearchEngine' => 'applications/almanac/query/AlmanacNamespaceSearchEngine.php',
'AlmanacNamespaceTransaction' => 'applications/almanac/storage/AlmanacNamespaceTransaction.php',
'AlmanacNamespaceTransactionQuery' => 'applications/almanac/query/AlmanacNamespaceTransactionQuery.php',
'AlmanacNamespaceViewController' => 'applications/almanac/controller/AlmanacNamespaceViewController.php',
'AlmanacNetwork' => 'applications/almanac/storage/AlmanacNetwork.php',
'AlmanacNetworkController' => 'applications/almanac/controller/AlmanacNetworkController.php',
'AlmanacNetworkEditController' => 'applications/almanac/controller/AlmanacNetworkEditController.php',
'AlmanacNetworkEditEngine' => 'applications/almanac/editor/AlmanacNetworkEditEngine.php',
'AlmanacNetworkEditor' => 'applications/almanac/editor/AlmanacNetworkEditor.php',
'AlmanacNetworkListController' => 'applications/almanac/controller/AlmanacNetworkListController.php',
'AlmanacNetworkNameNgrams' => 'applications/almanac/storage/AlmanacNetworkNameNgrams.php',
'AlmanacNetworkPHIDType' => 'applications/almanac/phid/AlmanacNetworkPHIDType.php',
'AlmanacNetworkQuery' => 'applications/almanac/query/AlmanacNetworkQuery.php',
'AlmanacNetworkSearchEngine' => 'applications/almanac/query/AlmanacNetworkSearchEngine.php',
@ -72,30 +91,36 @@ phutil_register_library_map(array(
'AlmanacNetworkTransactionQuery' => 'applications/almanac/query/AlmanacNetworkTransactionQuery.php',
'AlmanacNetworkViewController' => 'applications/almanac/controller/AlmanacNetworkViewController.php',
'AlmanacPropertiesDestructionEngineExtension' => 'applications/almanac/engineextension/AlmanacPropertiesDestructionEngineExtension.php',
'AlmanacPropertiesSearchEngineAttachment' => 'applications/almanac/engineextension/AlmanacPropertiesSearchEngineAttachment.php',
'AlmanacProperty' => 'applications/almanac/storage/AlmanacProperty.php',
'AlmanacPropertyController' => 'applications/almanac/controller/AlmanacPropertyController.php',
'AlmanacPropertyDeleteController' => 'applications/almanac/controller/AlmanacPropertyDeleteController.php',
'AlmanacPropertyEditController' => 'applications/almanac/controller/AlmanacPropertyEditController.php',
'AlmanacPropertyEditEngine' => 'applications/almanac/editor/AlmanacPropertyEditEngine.php',
'AlmanacPropertyInterface' => 'applications/almanac/property/AlmanacPropertyInterface.php',
'AlmanacPropertyQuery' => 'applications/almanac/query/AlmanacPropertyQuery.php',
'AlmanacQuery' => 'applications/almanac/query/AlmanacQuery.php',
'AlmanacQueryDevicesConduitAPIMethod' => 'applications/almanac/conduit/AlmanacQueryDevicesConduitAPIMethod.php',
'AlmanacQueryServicesConduitAPIMethod' => 'applications/almanac/conduit/AlmanacQueryServicesConduitAPIMethod.php',
'AlmanacSchemaSpec' => 'applications/almanac/storage/AlmanacSchemaSpec.php',
'AlmanacSearchEngineAttachment' => 'applications/almanac/engineextension/AlmanacSearchEngineAttachment.php',
'AlmanacService' => 'applications/almanac/storage/AlmanacService.php',
'AlmanacServiceController' => 'applications/almanac/controller/AlmanacServiceController.php',
'AlmanacServiceDatasource' => 'applications/almanac/typeahead/AlmanacServiceDatasource.php',
'AlmanacServiceEditController' => 'applications/almanac/controller/AlmanacServiceEditController.php',
'AlmanacServiceEditor' => 'applications/almanac/editor/AlmanacServiceEditor.php',
'AlmanacServiceListController' => 'applications/almanac/controller/AlmanacServiceListController.php',
'AlmanacServiceNameNgrams' => 'applications/almanac/storage/AlmanacServiceNameNgrams.php',
'AlmanacServicePHIDType' => 'applications/almanac/phid/AlmanacServicePHIDType.php',
'AlmanacServicePropertyEditEngine' => 'applications/almanac/editor/AlmanacServicePropertyEditEngine.php',
'AlmanacServiceQuery' => 'applications/almanac/query/AlmanacServiceQuery.php',
'AlmanacServiceSearchConduitAPIMethod' => 'applications/almanac/conduit/AlmanacServiceSearchConduitAPIMethod.php',
'AlmanacServiceSearchEngine' => 'applications/almanac/query/AlmanacServiceSearchEngine.php',
'AlmanacServiceTransaction' => 'applications/almanac/storage/AlmanacServiceTransaction.php',
'AlmanacServiceTransactionQuery' => 'applications/almanac/query/AlmanacServiceTransactionQuery.php',
'AlmanacServiceType' => 'applications/almanac/servicetype/AlmanacServiceType.php',
'AlmanacServiceTypeDatasource' => 'applications/almanac/typeahead/AlmanacServiceTypeDatasource.php',
'AlmanacServiceTypeTestCase' => 'applications/almanac/servicetype/__tests__/AlmanacServiceTypeTestCase.php',
'AlmanacServiceViewController' => 'applications/almanac/controller/AlmanacServiceViewController.php',
'AlmanacTransaction' => 'applications/almanac/storage/AlmanacTransaction.php',
'AphlictDropdownDataQuery' => 'applications/aphlict/query/AphlictDropdownDataQuery.php',
'Aphront304Response' => 'aphront/response/Aphront304Response.php',
'Aphront400Response' => 'aphront/response/Aphront400Response.php',
@ -858,6 +883,7 @@ phutil_register_library_map(array(
'DrydockBlueprintTransactionQuery' => 'applications/drydock/query/DrydockBlueprintTransactionQuery.php',
'DrydockBlueprintViewController' => 'applications/drydock/controller/DrydockBlueprintViewController.php',
'DrydockCommand' => 'applications/drydock/storage/DrydockCommand.php',
'DrydockCommandError' => 'applications/drydock/DrydockCommandError/DrydockCommandError.php',
'DrydockCommandInterface' => 'applications/drydock/interface/command/DrydockCommandInterface.php',
'DrydockCommandQuery' => 'applications/drydock/query/DrydockCommandQuery.php',
'DrydockConsoleController' => 'applications/drydock/controller/DrydockConsoleController.php',
@ -1068,7 +1094,6 @@ phutil_register_library_map(array(
'HarbormasterBuildableTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildableTransactionQuery.php',
'HarbormasterBuildableViewController' => 'applications/harbormaster/controller/HarbormasterBuildableViewController.php',
'HarbormasterBuiltinBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterBuiltinBuildStepGroup.php',
'HarbormasterCommandBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterCommandBuildStepImplementation.php',
'HarbormasterConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterConduitAPIMethod.php',
'HarbormasterController' => 'applications/harbormaster/controller/HarbormasterController.php',
'HarbormasterCreateArtifactConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterCreateArtifactConduitAPIMethod.php',
@ -1082,7 +1107,6 @@ phutil_register_library_map(array(
'HarbormasterFileArtifact' => 'applications/harbormaster/artifact/HarbormasterFileArtifact.php',
'HarbormasterHTTPRequestBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php',
'HarbormasterHostArtifact' => 'applications/harbormaster/artifact/HarbormasterHostArtifact.php',
'HarbormasterLeaseHostBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php',
'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseWorkingCopyBuildStepImplementation.php',
'HarbormasterLintMessagesController' => 'applications/harbormaster/controller/HarbormasterLintMessagesController.php',
'HarbormasterLintPropertyView' => 'applications/harbormaster/view/HarbormasterLintPropertyView.php',
@ -3783,7 +3807,6 @@ phutil_register_library_map(array(
'PonderAnswerCommentController' => 'applications/ponder/controller/PonderAnswerCommentController.php',
'PonderAnswerEditController' => 'applications/ponder/controller/PonderAnswerEditController.php',
'PonderAnswerEditor' => 'applications/ponder/editor/PonderAnswerEditor.php',
'PonderAnswerHasVotingUserEdgeType' => 'applications/ponder/edge/PonderAnswerHasVotingUserEdgeType.php',
'PonderAnswerHistoryController' => 'applications/ponder/controller/PonderAnswerHistoryController.php',
'PonderAnswerMailReceiver' => 'applications/ponder/mail/PonderAnswerMailReceiver.php',
'PonderAnswerPHIDType' => 'applications/ponder/phid/PonderAnswerPHIDType.php',
@ -3801,7 +3824,6 @@ phutil_register_library_map(array(
'PonderDefaultViewCapability' => 'applications/ponder/capability/PonderDefaultViewCapability.php',
'PonderEditor' => 'applications/ponder/editor/PonderEditor.php',
'PonderFooterView' => 'applications/ponder/view/PonderFooterView.php',
'PonderHelpfulSaveController' => 'applications/ponder/controller/PonderHelpfulSaveController.php',
'PonderModerateCapability' => 'applications/ponder/capability/PonderModerateCapability.php',
'PonderQuestion' => 'applications/ponder/storage/PonderQuestion.php',
'PonderQuestionCommentController' => 'applications/ponder/controller/PonderQuestionCommentController.php',
@ -3823,10 +3845,6 @@ phutil_register_library_map(array(
'PonderQuestionViewController' => 'applications/ponder/controller/PonderQuestionViewController.php',
'PonderRemarkupRule' => 'applications/ponder/remarkup/PonderRemarkupRule.php',
'PonderSchemaSpec' => 'applications/ponder/storage/PonderSchemaSpec.php',
'PonderVotableInterface' => 'applications/ponder/storage/PonderVotableInterface.php',
'PonderVote' => 'applications/ponder/constants/PonderVote.php',
'PonderVoteEditor' => 'applications/ponder/editor/PonderVoteEditor.php',
'PonderVotingUserHasAnswerEdgeType' => 'applications/ponder/edge/PonderVotingUserHasAnswerEdgeType.php',
'ProjectAddProjectsEmailCommand' => 'applications/project/command/ProjectAddProjectsEmailCommand.php',
'ProjectBoardTaskCard' => 'applications/project/view/ProjectBoardTaskCard.php',
'ProjectCanLockProjectsCapability' => 'applications/project/capability/ProjectCanLockProjectsCapability.php',
@ -3972,127 +3990,163 @@ phutil_register_library_map(array(
'AlmanacBinding' => array(
'AlmanacDAO',
'PhabricatorPolicyInterface',
'PhabricatorCustomFieldInterface',
'PhabricatorApplicationTransactionInterface',
'AlmanacPropertyInterface',
'PhabricatorDestructibleInterface',
'PhabricatorExtendedPolicyInterface',
),
'AlmanacBindingDisableController' => 'AlmanacServiceController',
'AlmanacBindingEditController' => 'AlmanacServiceController',
'AlmanacBindingEditor' => 'PhabricatorApplicationTransactionEditor',
'AlmanacBindingEditor' => 'AlmanacEditor',
'AlmanacBindingPHIDType' => 'PhabricatorPHIDType',
'AlmanacBindingPropertyEditEngine' => 'AlmanacPropertyEditEngine',
'AlmanacBindingQuery' => 'AlmanacQuery',
'AlmanacBindingTableView' => 'AphrontView',
'AlmanacBindingTransaction' => 'PhabricatorApplicationTransaction',
'AlmanacBindingTransaction' => 'AlmanacTransaction',
'AlmanacBindingTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'AlmanacBindingViewController' => 'AlmanacServiceController',
'AlmanacBindingsSearchEngineAttachment' => 'AlmanacSearchEngineAttachment',
'AlmanacClusterDatabaseServiceType' => 'AlmanacClusterServiceType',
'AlmanacClusterRepositoryServiceType' => 'AlmanacClusterServiceType',
'AlmanacClusterServiceType' => 'AlmanacServiceType',
'AlmanacConduitAPIMethod' => 'ConduitAPIMethod',
'AlmanacConsoleController' => 'AlmanacController',
'AlmanacController' => 'PhabricatorController',
'AlmanacCoreCustomField' => array(
'AlmanacCustomField',
'PhabricatorStandardCustomFieldInterface',
),
'AlmanacCreateClusterServicesCapability' => 'PhabricatorPolicyCapability',
'AlmanacCreateDevicesCapability' => 'PhabricatorPolicyCapability',
'AlmanacCreateNamespacesCapability' => 'PhabricatorPolicyCapability',
'AlmanacCreateNetworksCapability' => 'PhabricatorPolicyCapability',
'AlmanacCreateServicesCapability' => 'PhabricatorPolicyCapability',
'AlmanacCustomField' => 'PhabricatorCustomField',
'AlmanacCustomServiceType' => 'AlmanacServiceType',
'AlmanacDAO' => 'PhabricatorLiskDAO',
'AlmanacDevice' => array(
'AlmanacDAO',
'PhabricatorPolicyInterface',
'PhabricatorCustomFieldInterface',
'PhabricatorApplicationTransactionInterface',
'PhabricatorProjectInterface',
'PhabricatorSSHPublicKeyInterface',
'AlmanacPropertyInterface',
'PhabricatorDestructibleInterface',
'PhabricatorNgramsInterface',
'PhabricatorConduitResultInterface',
'PhabricatorExtendedPolicyInterface',
),
'AlmanacDeviceController' => 'AlmanacController',
'AlmanacDeviceEditController' => 'AlmanacDeviceController',
'AlmanacDeviceEditor' => 'PhabricatorApplicationTransactionEditor',
'AlmanacDeviceEditor' => 'AlmanacEditor',
'AlmanacDeviceListController' => 'AlmanacDeviceController',
'AlmanacDeviceNameNgrams' => 'PhabricatorSearchNgrams',
'AlmanacDevicePHIDType' => 'PhabricatorPHIDType',
'AlmanacDevicePropertyEditEngine' => 'AlmanacPropertyEditEngine',
'AlmanacDeviceQuery' => 'AlmanacQuery',
'AlmanacDeviceSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'AlmanacDeviceSearchEngine' => 'PhabricatorApplicationSearchEngine',
'AlmanacDeviceTransaction' => 'PhabricatorApplicationTransaction',
'AlmanacDeviceTransaction' => 'AlmanacTransaction',
'AlmanacDeviceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'AlmanacDeviceViewController' => 'AlmanacDeviceController',
'AlmanacDrydockPoolServiceType' => 'AlmanacServiceType',
'AlmanacEditor' => 'PhabricatorApplicationTransactionEditor',
'AlmanacInterface' => array(
'AlmanacDAO',
'PhabricatorPolicyInterface',
'PhabricatorDestructibleInterface',
'PhabricatorExtendedPolicyInterface',
),
'AlmanacInterfaceDatasource' => 'PhabricatorTypeaheadDatasource',
'AlmanacInterfaceDeleteController' => 'AlmanacDeviceController',
'AlmanacInterfaceEditController' => 'AlmanacDeviceController',
'AlmanacInterfacePHIDType' => 'PhabricatorPHIDType',
'AlmanacInterfaceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'AlmanacInterfaceQuery' => 'AlmanacQuery',
'AlmanacInterfaceTableView' => 'AphrontView',
'AlmanacKeys' => 'Phobject',
'AlmanacManagementLockWorkflow' => 'AlmanacManagementWorkflow',
'AlmanacManageClusterServicesCapability' => 'PhabricatorPolicyCapability',
'AlmanacManagementRegisterWorkflow' => 'AlmanacManagementWorkflow',
'AlmanacManagementTrustKeyWorkflow' => 'AlmanacManagementWorkflow',
'AlmanacManagementUnlockWorkflow' => 'AlmanacManagementWorkflow',
'AlmanacManagementUntrustKeyWorkflow' => 'AlmanacManagementWorkflow',
'AlmanacManagementWorkflow' => 'PhabricatorManagementWorkflow',
'AlmanacNames' => 'Phobject',
'AlmanacNamesTestCase' => 'PhabricatorTestCase',
'AlmanacNamespace' => array(
'AlmanacDAO',
'PhabricatorPolicyInterface',
'PhabricatorApplicationTransactionInterface',
'PhabricatorProjectInterface',
'AlmanacPropertyInterface',
'PhabricatorDestructibleInterface',
'PhabricatorNgramsInterface',
),
'AlmanacNamespaceController' => 'AlmanacController',
'AlmanacNamespaceEditController' => 'AlmanacNamespaceController',
'AlmanacNamespaceEditEngine' => 'PhabricatorEditEngine',
'AlmanacNamespaceEditor' => 'PhabricatorApplicationTransactionEditor',
'AlmanacNamespaceListController' => 'AlmanacNamespaceController',
'AlmanacNamespaceNameNgrams' => 'PhabricatorSearchNgrams',
'AlmanacNamespacePHIDType' => 'PhabricatorPHIDType',
'AlmanacNamespaceQuery' => 'AlmanacQuery',
'AlmanacNamespaceSearchEngine' => 'PhabricatorApplicationSearchEngine',
'AlmanacNamespaceTransaction' => 'PhabricatorApplicationTransaction',
'AlmanacNamespaceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'AlmanacNamespaceViewController' => 'AlmanacNamespaceController',
'AlmanacNetwork' => array(
'AlmanacDAO',
'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface',
'PhabricatorDestructibleInterface',
'PhabricatorNgramsInterface',
),
'AlmanacNetworkController' => 'AlmanacController',
'AlmanacNetworkEditController' => 'AlmanacNetworkController',
'AlmanacNetworkEditEngine' => 'PhabricatorEditEngine',
'AlmanacNetworkEditor' => 'PhabricatorApplicationTransactionEditor',
'AlmanacNetworkListController' => 'AlmanacNetworkController',
'AlmanacNetworkNameNgrams' => 'PhabricatorSearchNgrams',
'AlmanacNetworkPHIDType' => 'PhabricatorPHIDType',
'AlmanacNetworkQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'AlmanacNetworkQuery' => 'AlmanacQuery',
'AlmanacNetworkSearchEngine' => 'PhabricatorApplicationSearchEngine',
'AlmanacNetworkTransaction' => 'PhabricatorApplicationTransaction',
'AlmanacNetworkTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'AlmanacNetworkViewController' => 'AlmanacNetworkController',
'AlmanacPropertiesDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
'AlmanacPropertiesSearchEngineAttachment' => 'AlmanacSearchEngineAttachment',
'AlmanacProperty' => array(
'PhabricatorCustomFieldStorage',
'AlmanacDAO',
'PhabricatorPolicyInterface',
),
'AlmanacPropertyController' => 'AlmanacController',
'AlmanacPropertyDeleteController' => 'AlmanacDeviceController',
'AlmanacPropertyEditController' => 'AlmanacDeviceController',
'AlmanacPropertyDeleteController' => 'AlmanacPropertyController',
'AlmanacPropertyEditController' => 'AlmanacPropertyController',
'AlmanacPropertyEditEngine' => 'PhabricatorEditEngine',
'AlmanacPropertyQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'AlmanacQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'AlmanacQueryDevicesConduitAPIMethod' => 'AlmanacConduitAPIMethod',
'AlmanacQueryServicesConduitAPIMethod' => 'AlmanacConduitAPIMethod',
'AlmanacSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'AlmanacSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
'AlmanacService' => array(
'AlmanacDAO',
'PhabricatorPolicyInterface',
'PhabricatorCustomFieldInterface',
'PhabricatorApplicationTransactionInterface',
'PhabricatorProjectInterface',
'AlmanacPropertyInterface',
'PhabricatorDestructibleInterface',
'PhabricatorNgramsInterface',
'PhabricatorConduitResultInterface',
'PhabricatorExtendedPolicyInterface',
),
'AlmanacServiceController' => 'AlmanacController',
'AlmanacServiceDatasource' => 'PhabricatorTypeaheadDatasource',
'AlmanacServiceEditController' => 'AlmanacServiceController',
'AlmanacServiceEditor' => 'PhabricatorApplicationTransactionEditor',
'AlmanacServiceEditor' => 'AlmanacEditor',
'AlmanacServiceListController' => 'AlmanacServiceController',
'AlmanacServiceNameNgrams' => 'PhabricatorSearchNgrams',
'AlmanacServicePHIDType' => 'PhabricatorPHIDType',
'AlmanacServicePropertyEditEngine' => 'AlmanacPropertyEditEngine',
'AlmanacServiceQuery' => 'AlmanacQuery',
'AlmanacServiceSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'AlmanacServiceSearchEngine' => 'PhabricatorApplicationSearchEngine',
'AlmanacServiceTransaction' => 'PhabricatorApplicationTransaction',
'AlmanacServiceTransaction' => 'AlmanacTransaction',
'AlmanacServiceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'AlmanacServiceType' => 'Phobject',
'AlmanacServiceTypeDatasource' => 'PhabricatorTypeaheadDatasource',
'AlmanacServiceTypeTestCase' => 'PhabricatorTestCase',
'AlmanacServiceViewController' => 'AlmanacServiceController',
'AlmanacTransaction' => 'PhabricatorApplicationTransaction',
'AphlictDropdownDataQuery' => 'Phobject',
'Aphront304Response' => 'AphrontResponse',
'Aphront400Response' => 'AphrontResponse',
@ -5203,7 +5257,6 @@ phutil_register_library_map(array(
'HarbormasterBuildableTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'HarbormasterBuildableViewController' => 'HarbormasterController',
'HarbormasterBuiltinBuildStepGroup' => 'HarbormasterBuildStepGroup',
'HarbormasterCommandBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterConduitAPIMethod' => 'ConduitAPIMethod',
'HarbormasterController' => 'PhabricatorController',
'HarbormasterCreateArtifactConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
@ -5217,7 +5270,6 @@ phutil_register_library_map(array(
'HarbormasterFileArtifact' => 'HarbormasterArtifact',
'HarbormasterHTTPRequestBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterHostArtifact' => 'HarbormasterDrydockLeaseArtifact',
'HarbormasterLeaseHostBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterLintMessagesController' => 'HarbormasterController',
'HarbormasterLintPropertyView' => 'AphrontView',
@ -8418,7 +8470,6 @@ phutil_register_library_map(array(
'PonderDAO',
'PhabricatorApplicationTransactionInterface',
'PhabricatorMarkupInterface',
'PonderVotableInterface',
'PhabricatorPolicyInterface',
'PhabricatorFlaggableInterface',
'PhabricatorSubscribableInterface',
@ -8427,7 +8478,6 @@ phutil_register_library_map(array(
'PonderAnswerCommentController' => 'PonderController',
'PonderAnswerEditController' => 'PonderController',
'PonderAnswerEditor' => 'PonderEditor',
'PonderAnswerHasVotingUserEdgeType' => 'PhabricatorEdgeType',
'PonderAnswerHistoryController' => 'PonderController',
'PonderAnswerMailReceiver' => 'PhabricatorObjectMailReceiver',
'PonderAnswerPHIDType' => 'PhabricatorPHIDType',
@ -8445,7 +8495,6 @@ phutil_register_library_map(array(
'PonderDefaultViewCapability' => 'PhabricatorPolicyCapability',
'PonderEditor' => 'PhabricatorApplicationTransactionEditor',
'PonderFooterView' => 'AphrontTagView',
'PonderHelpfulSaveController' => 'PonderController',
'PonderModerateCapability' => 'PhabricatorPolicyCapability',
'PonderQuestion' => array(
'PonderDAO',
@ -8479,9 +8528,6 @@ phutil_register_library_map(array(
'PonderQuestionViewController' => 'PonderController',
'PonderRemarkupRule' => 'PhabricatorObjectRemarkupRule',
'PonderSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'PonderVote' => 'PonderConstants',
'PonderVoteEditor' => 'PhabricatorEditor',
'PonderVotingUserHasAnswerEdgeType' => 'PhabricatorEdgeType',
'ProjectAddProjectsEmailCommand' => 'MetaMTAEmailTransactionCommand',
'ProjectBoardTaskCard' => 'Phobject',
'ProjectCanLockProjectsCapability' => 'PhabricatorPolicyCapability',

View file

@ -29,7 +29,7 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
public function getHelpDocumentationArticles(PhabricatorUser $viewer) {
return array(
array(
'name' => pht('Alamanac User Guide'),
'name' => pht('Almanac User Guide'),
'href' => PhabricatorEnv::getDoclink('Almanac User Guide'),
),
);
@ -43,37 +43,56 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
return array(
'/almanac/' => array(
'' => 'AlmanacConsoleController',
'service/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacServiceListController',
'(?P<objectType>service)/' => array(
$this->getQueryRoutePattern() => 'AlmanacServiceListController',
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacServiceEditController',
'view/(?P<name>[^/]+)/' => 'AlmanacServiceViewController',
),
'device/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacDeviceListController',
'(?P<objectType>device)/' => array(
$this->getQueryRoutePattern() => 'AlmanacDeviceListController',
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacDeviceEditController',
'view/(?P<name>[^/]+)/' => 'AlmanacDeviceViewController',
),
'interface/' => array(
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacInterfaceEditController',
'delete/(?:(?P<id>\d+)/)?' => 'AlmanacInterfaceDeleteController',
),
'binding/' => array(
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacBindingEditController',
'disable/(?:(?P<id>\d+)/)?' => 'AlmanacBindingDisableController',
'(?P<id>\d+)/' => 'AlmanacBindingViewController',
),
'network/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacNetworkListController',
$this->getQueryRoutePattern() => 'AlmanacNetworkListController',
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacNetworkEditController',
'(?P<id>\d+)/' => 'AlmanacNetworkViewController',
),
'namespace/' => array(
$this->getQueryRoutePattern() => 'AlmanacNamespaceListController',
$this->getEditRoutePattern('edit/')
=> 'AlmanacNamespaceEditController',
'(?P<id>\d+)/' => 'AlmanacNamespaceViewController',
),
'property/' => array(
'edit/' => 'AlmanacPropertyEditController',
'delete/' => 'AlmanacPropertyDeleteController',
'update/' => 'AlmanacPropertyEditController',
),
),
);
}
protected function getCustomCapabilities() {
$cluster_caption = pht(
'This permission is very dangerous. %s',
phutil_tag(
'a',
array(
'href' => PhabricatorEnv::getDoclink(
'User Guide: Phabricator Clusters'),
'target' => '_blank',
),
pht('Learn More')));
return array(
AlmanacCreateServicesCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
@ -84,9 +103,13 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
AlmanacCreateNetworksCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
),
AlmanacCreateClusterServicesCapability::CAPABILITY => array(
AlmanacCreateNamespacesCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
),
AlmanacManageClusterServicesCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_NOONE,
'caption' => $cluster_caption,
),
);
}

View file

@ -0,0 +1,16 @@
<?php
final class AlmanacCreateNamespacesCapability
extends PhabricatorPolicyCapability {
const CAPABILITY = 'almanac.namespaces';
public function getCapabilityName() {
return pht('Can Create Namespaces');
}
public function describeCapabilityRejection() {
return pht('You do not have permission to create Almanac namespaces.');
}
}

View file

@ -1,17 +1,17 @@
<?php
final class AlmanacCreateClusterServicesCapability
final class AlmanacManageClusterServicesCapability
extends PhabricatorPolicyCapability {
const CAPABILITY = 'almanac.cluster';
public function getCapabilityName() {
return pht('Can Create Cluster Services');
return pht('Can Manage Cluster Services');
}
public function describeCapabilityRejection() {
return pht(
'You do not have permission to create Almanac cluster services.');
'You do not have permission to manage Almanac cluster services.');
}
}

View file

@ -1,73 +0,0 @@
<?php
abstract class AlmanacConduitAPIMethod extends ConduitAPIMethod {
final public function getApplication() {
return PhabricatorApplication::getByClass(
'PhabricatorAlmanacApplication');
}
public function getMethodStatus() {
return self::METHOD_STATUS_UNSTABLE;
}
public function getMethodStatusDescription() {
return pht(
'Almanac is a prototype application and its APIs are '.
'subject to change.');
}
protected function getServiceDictionary(AlmanacService $service) {
return array(
'id' => (int)$service->getID(),
'phid' => $service->getPHID(),
'name' => $service->getName(),
'uri' => PhabricatorEnv::getProductionURI($service->getURI()),
'serviceClass' => $service->getServiceClass(),
'properties' => $this->getPropertiesDictionary($service),
);
}
protected function getBindingDictionary(AlmanacBinding $binding) {
return array(
'id' => (int)$binding->getID(),
'phid' => $binding->getPHID(),
'properties' => $this->getPropertiesDictionary($binding),
'interface' => $this->getInterfaceDictionary($binding->getInterface()),
);
}
protected function getPropertiesDictionary(AlmanacPropertyInterface $obj) {
$properties = $obj->getAlmanacProperties();
return (object)mpull($properties, 'getFieldValue', 'getFieldName');
}
protected function getInterfaceDictionary(AlmanacInterface $interface) {
return array(
'id' => (int)$interface->getID(),
'phid' => $interface->getPHID(),
'address' => $interface->getAddress(),
'port' => (int)$interface->getPort(),
'device' => $this->getDeviceDictionary($interface->getDevice()),
'network' => $this->getNetworkDictionary($interface->getNetwork()),
);
}
protected function getDeviceDictionary(AlmanacDevice $device) {
return array(
'id' => (int)$device->getID(),
'phid' => $device->getPHID(),
'name' => $device->getName(),
'properties' => $this->getPropertiesDictionary($device),
);
}
protected function getNetworkDictionary(AlmanacNetwork $network) {
return array(
'id' => (int)$network->getID(),
'phid' => $network->getPHID(),
'name' => $network->getName(),
);
}
}

View file

@ -0,0 +1,18 @@
<?php
final class AlmanacDeviceSearchConduitAPIMethod
extends PhabricatorSearchEngineAPIMethod {
public function getAPIMethodName() {
return 'almanac.device.search';
}
public function newSearchEngine() {
return new AlmanacDeviceSearchEngine();
}
public function getMethodSummary() {
return pht('Read information about Almanac devices.');
}
}

View file

@ -1,63 +0,0 @@
<?php
final class AlmanacQueryDevicesConduitAPIMethod
extends AlmanacConduitAPIMethod {
public function getAPIMethodName() {
return 'almanac.querydevices';
}
public function getMethodDescription() {
return pht('Query Almanac devices.');
}
protected function defineParamTypes() {
return array(
'ids' => 'optional list<id>',
'phids' => 'optional list<phid>',
'names' => 'optional list<phid>',
) + self::getPagerParamTypes();
}
protected function defineReturnType() {
return 'list<wild>';
}
protected function execute(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$query = id(new AlmanacDeviceQuery())
->setViewer($viewer);
$ids = $request->getValue('ids');
if ($ids !== null) {
$query->withIDs($ids);
}
$phids = $request->getValue('phids');
if ($phids !== null) {
$query->withPHIDs($phids);
}
$names = $request->getValue('names');
if ($names !== null) {
$query->withNames($names);
}
$pager = $this->newPager($request);
$devices = $query->executeWithCursorPager($pager);
$data = array();
foreach ($devices as $device) {
$data[] = $this->getDeviceDictionary($device);
}
$results = array(
'data' => $data,
);
return $this->addPagerResults($results, $pager);
}
}

View file

@ -1,86 +0,0 @@
<?php
final class AlmanacQueryServicesConduitAPIMethod
extends AlmanacConduitAPIMethod {
public function getAPIMethodName() {
return 'almanac.queryservices';
}
public function getMethodDescription() {
return pht('Query Almanac services.');
}
protected function defineParamTypes() {
return array(
'ids' => 'optional list<id>',
'phids' => 'optional list<phid>',
'names' => 'optional list<phid>',
'devicePHIDs' => 'optional list<phid>',
'serviceClasses' => 'optional list<string>',
) + self::getPagerParamTypes();
}
protected function defineReturnType() {
return 'list<wild>';
}
protected function execute(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$query = id(new AlmanacServiceQuery())
->setViewer($viewer)
->needBindings(true);
$ids = $request->getValue('ids');
if ($ids !== null) {
$query->withIDs($ids);
}
$phids = $request->getValue('phids');
if ($phids !== null) {
$query->withPHIDs($phids);
}
$names = $request->getValue('names');
if ($names !== null) {
$query->withNames($names);
}
$classes = $request->getValue('serviceClasses');
if ($classes !== null) {
$query->withServiceClasses($classes);
}
$device_phids = $request->getValue('devicePHIDs');
if ($device_phids !== null) {
$query->withDevicePHIDs($device_phids);
}
$pager = $this->newPager($request);
$services = $query->executeWithCursorPager($pager);
$data = array();
foreach ($services as $service) {
$phid = $service->getPHID();
$service_bindings = $service->getBindings();
$service_bindings = array_values($service_bindings);
foreach ($service_bindings as $key => $service_binding) {
$service_bindings[$key] = $this->getBindingDictionary($service_binding);
}
$data[] = $this->getServiceDictionary($service) + array(
'bindings' => $service_bindings,
);
}
$results = array(
'data' => $data,
);
return $this->addPagerResults($results, $pager);
}
}

View file

@ -0,0 +1,18 @@
<?php
final class AlmanacServiceSearchConduitAPIMethod
extends PhabricatorSearchEngineAPIMethod {
public function getAPIMethodName() {
return 'almanac.service.search';
}
public function newSearchEngine() {
return new AlmanacServiceSearchEngine();
}
public function getMethodSummary() {
return pht('Read information about Almanac services.');
}
}

View file

@ -0,0 +1,69 @@
<?php
final class AlmanacBindingDisableController
extends AlmanacServiceController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$binding = id(new AlmanacBindingQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$binding) {
return new Aphront404Response();
}
$id = $binding->getID();
$is_disable = !$binding->getIsDisabled();
$done_uri = $binding->getURI();
if ($is_disable) {
$disable_title = pht('Disable Binding');
$disable_body = pht('Disable this binding?');
$disable_button = pht('Disable Binding');
$v_disable = 1;
} else {
$disable_title = pht('Enable Binding');
$disable_body = pht('Enable this binding?');
$disable_button = pht('Enable Binding');
$v_disable = 0;
}
if ($request->isFormPost()) {
$type_disable = AlmanacBindingTransaction::TYPE_DISABLE;
$xactions = array();
$xactions[] = id(new AlmanacBindingTransaction())
->setTransactionType($type_disable)
->setNewValue($v_disable);
$editor = id(new AlmanacBindingEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$editor->applyTransactions($binding, $xactions);
return id(new AphrontRedirectResponse())->setURI($done_uri);
}
return $this->newDialog()
->setTitle($disable_title)
->appendParagraph($disable_body)
->addSubmitButton($disable_button)
->addCancelButton($done_uri);
}
}

View file

@ -15,6 +15,7 @@ final class AlmanacBindingViewController
$binding = id(new AlmanacBindingQuery())
->setViewer($viewer)
->withIDs(array($id))
->needProperties(true)
->executeOne();
if (!$binding) {
return new Aphront404Response();
@ -34,15 +35,21 @@ final class AlmanacBindingViewController
->setHeader($title)
->setPolicyObject($binding);
if ($binding->getIsDisabled()) {
$header->setStatus('fa-ban', 'red', pht('Disabled'));
}
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($property_list);
if ($binding->getService()->getIsLocked()) {
$this->addLockMessage(
if ($binding->getService()->isClusterService()) {
$this->addClusterMessage(
$box,
pht('The service for this binding is a cluster service.'),
pht(
'This service for this binding is locked, so the binding can '.
'The service for this binding is a cluster service. You do not '.
'have permission to manage cluster services, so this binding can '.
'not be edited.'));
}
@ -111,6 +118,24 @@ final class AlmanacBindingViewController
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit));
if ($binding->getIsDisabled()) {
$disable_icon = 'fa-check';
$disable_text = pht('Enable Binding');
} else {
$disable_icon = 'fa-ban';
$disable_text = pht('Disable Binding');
}
$disable_href = $this->getApplicationURI("binding/disable/{$id}/");
$actions->addAction(
id(new PhabricatorActionView())
->setIcon($disable_icon)
->setName($disable_text)
->setHref($disable_href)
->setWorkflow(true)
->setDisabled(!$can_edit));
return $actions;
}

View file

@ -12,26 +12,52 @@ final class AlmanacConsoleController extends AlmanacController {
$menu = id(new PHUIObjectItemListView())
->setUser($viewer);
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Services'))
->setHref($this->getApplicationURI('service/'))
->setIcon('fa-plug')
->addAttribute(pht('Manage Almanac services.')));
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Devices'))
->setHref($this->getApplicationURI('device/'))
->setIcon('fa-server')
->addAttribute(pht('Manage Almanac devices.')));
->addAttribute(
pht(
'Create an inventory of physical and virtual hosts and '.
'devices.')));
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Services'))
->setHref($this->getApplicationURI('service/'))
->setIcon('fa-plug')
->addAttribute(
pht(
'Create and update services, and map them to interfaces on '.
'devices.')));
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Networks'))
->setHref($this->getApplicationURI('network/'))
->setIcon('fa-globe')
->addAttribute(pht('Manage Almanac networks.')));
->addAttribute(
pht(
'Manage public and private networks.')));
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Namespaces'))
->setHref($this->getApplicationURI('namespace/'))
->setIcon('fa-asterisk')
->addAttribute(
pht('Control who can create new named services and devices.')));
$docs_uri = PhabricatorEnv::getDoclink(
'Almanac User Guide');
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Documentation'))
->setHref($docs_uri)
->setIcon('fa-book')
->addAttribute(pht('Browse documentation for Almanac.')));
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Console'));

View file

@ -10,27 +10,14 @@ abstract class AlmanacController
$properties = $object->getAlmanacProperties();
$this->requireResource('almanac-css');
Javelin::initBehavior('phabricator-tooltips', array());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$object,
PhabricatorPolicyCapability::CAN_EDIT);
$field_list = PhabricatorCustomField::getObjectFields(
$object,
PhabricatorCustomField::ROLE_DEFAULT);
// Before reading values from the object, read defaults.
$defaults = mpull(
$field_list->getFields(),
'getValueForStorage',
'getFieldKey');
$field_list
->setViewer($viewer)
->readFieldsFromStorage($object);
Javelin::initBehavior('phabricator-tooltips', array());
$properties = $object->getAlmanacProperties();
$icon_builtin = id(new PHUIIconView())
->setIcon('fa-circle')
@ -51,45 +38,46 @@ abstract class AlmanacController
));
$builtins = $object->getAlmanacPropertyFieldSpecifications();
$defaults = mpull($builtins, null, 'getValueForTransaction');
// Sort fields so builtin fields appear first, then fields are ordered
// alphabetically.
$fields = $field_list->getFields();
$fields = msort($fields, 'getFieldKey');
$properties = msort($properties, 'getFieldName');
$head = array();
$tail = array();
foreach ($fields as $field) {
$key = $field->getFieldKey();
foreach ($properties as $property) {
$key = $property->getFieldName();
if (isset($builtins[$key])) {
$head[$key] = $field;
$head[$key] = $property;
} else {
$tail[$key] = $field;
$tail[$key] = $property;
}
}
$fields = $head + $tail;
$properties = $head + $tail;
$delete_base = $this->getApplicationURI('property/delete/');
$edit_base = $this->getApplicationURI('property/update/');
$rows = array();
foreach ($fields as $key => $field) {
$value = $field->getValueForStorage();
foreach ($properties as $key => $property) {
$value = $property->getFieldValue();
$is_builtin = isset($builtins[$key]);
$delete_uri = $this->getApplicationURI('property/delete/');
$delete_uri = id(new PhutilURI($delete_uri))
$delete_uri = id(new PhutilURI($delete_base))
->setQueryParams(
array(
'objectPHID' => $object->getPHID(),
'key' => $key,
'objectPHID' => $object->getPHID(),
));
$edit_uri = $this->getApplicationURI('property/edit/');
$edit_uri = id(new PhutilURI($edit_uri))
$edit_uri = id(new PhutilURI($edit_base))
->setQueryParams(
array(
'objectPHID' => $object->getPHID(),
'key' => $key,
'objectPHID' => $object->getPHID(),
));
$delete = javelin_tag(
@ -153,7 +141,8 @@ abstract class AlmanacController
));
$phid = $object->getPHID();
$add_uri = $this->getApplicationURI("property/edit/?objectPHID={$phid}");
$add_uri = id(new PhutilURI($edit_base))
->setQueryParam('objectPHID', $object->getPHID());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
@ -177,23 +166,50 @@ abstract class AlmanacController
->setTable($table);
}
protected function addLockMessage(PHUIObjectBoxView $box, $message) {
protected function addClusterMessage(
PHUIObjectBoxView $box,
$positive,
$negative) {
$can_manage = $this->hasApplicationCapability(
AlmanacManageClusterServicesCapability::CAPABILITY);
$doc_link = phutil_tag(
'a',
array(
'href' => PhabricatorEnv::getDoclink('Almanac User Guide'),
'href' => PhabricatorEnv::getDoclink(
'User Guide: Phabricator Clusters'),
'target' => '_blank',
),
pht('Learn More'));
if ($can_manage) {
$severity = PHUIInfoView::SEVERITY_NOTICE;
$message = $positive;
} else {
$severity = PHUIInfoView::SEVERITY_WARNING;
$message = $negative;
}
$icon = id(new PHUIIconView())
->setIcon('fa-sitemap');
$error_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setSeverity($severity)
->setErrors(
array(
array($message, ' ', $doc_link),
array($icon, ' ', $message, ' ', $doc_link),
));
$box->setInfoView($error_view);
}
protected function getPropertyDeleteURI($object) {
return null;
}
protected function getPropertyUpdateURI($object) {
return null;
}
}

View file

@ -15,15 +15,12 @@ final class AlmanacDeviceViewController
$device = id(new AlmanacDeviceQuery())
->setViewer($viewer)
->withNames(array($name))
->needProperties(true)
->executeOne();
if (!$device) {
return new Aphront404Response();
}
// We rebuild locks on a device when viewing the detail page, so they
// automatically get corrected if they fall out of sync.
$device->rebuildDeviceLocks();
$title = pht('Device %s', $device->getName());
$property_list = $this->buildPropertyList($device);
@ -39,12 +36,14 @@ final class AlmanacDeviceViewController
->setHeader($header)
->addPropertyList($property_list);
if ($device->getIsLocked()) {
$this->addLockMessage(
if ($device->isClusterDevice()) {
$this->addClusterMessage(
$box,
pht('This device is bound to a cluster service.'),
pht(
'This device is bound to a locked service, so it can not be '.
'edited.'));
'This device is bound to a cluster service. You do not have '.
'permission to manage cluster services, so the device can not '.
'be edited.'));
}
$interfaces = $this->buildInterfaceList($device);
@ -218,14 +217,14 @@ final class AlmanacDeviceViewController
$handles = $viewer->loadHandles(mpull($services, 'getPHID'));
$icon_lock = id(new PHUIIconView())
->setIcon('fa-lock');
$icon_cluster = id(new PHUIIconView())
->setIcon('fa-sitemap');
$rows = array();
foreach ($services as $service) {
$rows[] = array(
($service->getIsLocked()
? $icon_lock
($service->isClusterService()
? $icon_cluster
: null),
$handles->renderHandle($service->getPHID()),
);

View file

@ -0,0 +1,72 @@
<?php
final class AlmanacInterfaceDeleteController
extends AlmanacDeviceController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$interface = id(new AlmanacInterfaceQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$interface) {
return new Aphront404Response();
}
$device = $interface->getDevice();
$device_uri = $device->getURI();
if ($interface->loadIsInUse()) {
return $this->newDialog()
->setTitle(pht('Interface In Use'))
->appendParagraph(
pht(
'You can not delete this interface because it is currently in '.
'use. One or more services are bound to it.'))
->addCancelButton($device_uri);
}
if ($request->isFormPost()) {
$type_interface = AlmanacDeviceTransaction::TYPE_INTERFACE;
$xactions = array();
$v_old = array(
'id' => $interface->getID(),
) + $interface->toAddress()->toDictionary();
$xactions[] = id(new AlmanacDeviceTransaction())
->setTransactionType($type_interface)
->setOldValue($v_old)
->setNewValue(null);
$editor = id(new AlmanacDeviceEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$editor->applyTransactions($device, $xactions);
return id(new AphrontRedirectResponse())->setURI($device_uri);
}
return $this->newDialog()
->setTitle(pht('Delete Interface'))
->appendParagraph(
pht(
'Remove interface %s on device %s?',
phutil_tag('strong', array(), $interface->renderDisplayAddress()),
phutil_tag('strong', array(), $device->getName())))
->addCancelButton($device_uri)
->addSubmitButton(pht('Delete Interface'));
}
}

View file

@ -0,0 +1,14 @@
<?php
abstract class AlmanacNamespaceController extends AlmanacController {
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$list_uri = $this->getApplicationURI('namespace/');
$crumbs->addTextCrumb(pht('Namespaces'), $list_uri);
return $crumbs;
}
}

View file

@ -0,0 +1,11 @@
<?php
final class AlmanacNamespaceEditController extends AlmanacNamespaceController {
public function handleRequest(AphrontRequest $request) {
return id(new AlmanacNamespaceEditEngine())
->setController($this)
->buildResponse();
}
}

View file

@ -0,0 +1,26 @@
<?php
final class AlmanacNamespaceListController
extends AlmanacNamespaceController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
return id(new AlmanacNamespaceSearchEngine())
->setController($this)
->buildResponse();
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
id(new AlmanacNamespaceEditEngine())
->setViewer($this->getViewer())
->addActionToCrumbs($crumbs);
return $crumbs;
}
}

View file

@ -0,0 +1,87 @@
<?php
final class AlmanacNamespaceViewController
extends AlmanacNamespaceController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$namespace = id(new AlmanacNamespaceQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$namespace) {
return new Aphront404Response();
}
$title = pht('Namespace %s', $namespace->getName());
$property_list = $this->buildPropertyList($namespace);
$action_list = $this->buildActionList($namespace);
$property_list->setActionList($action_list);
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($namespace->getName())
->setPolicyObject($namespace);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($property_list);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($namespace->getName());
$timeline = $this->buildTransactionTimeline(
$namespace,
new AlmanacNamespaceTransactionQuery());
$timeline->setShouldTerminate(true);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild(
array(
$box,
$timeline,
));
}
private function buildPropertyList(AlmanacNamespace $namespace) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
return $properties;
}
private function buildActionList(AlmanacNamespace $namespace) {
$viewer = $this->getViewer();
$id = $namespace->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$namespace,
PhabricatorPolicyCapability::CAN_EDIT);
$actions = id(new PhabricatorActionListView())
->setUser($viewer);
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Namespace'))
->setHref($this->getApplicationURI("namespace/edit/{$id}/"))
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit));
return $actions;
}
}

View file

@ -1,142 +1,11 @@
<?php
final class AlmanacNetworkEditController
extends AlmanacNetworkController {
final class AlmanacNetworkEditController extends AlmanacNetworkController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$list_uri = $this->getApplicationURI('network/');
$id = $request->getURIData('id');
if ($id) {
$network = id(new AlmanacNetworkQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$network) {
return new Aphront404Response();
}
$is_new = false;
$network_uri = $this->getApplicationURI('network/'.$network->getID().'/');
$cancel_uri = $network_uri;
$title = pht('Edit Network');
$save_button = pht('Save Changes');
} else {
$this->requireApplicationCapability(
AlmanacCreateNetworksCapability::CAPABILITY);
$network = AlmanacNetwork::initializeNewNetwork();
$is_new = true;
$cancel_uri = $list_uri;
$title = pht('Create Network');
$save_button = pht('Create Network');
}
$v_name = $network->getName();
$e_name = true;
$validation_exception = null;
if ($request->isFormPost()) {
$v_name = $request->getStr('name');
$v_view = $request->getStr('viewPolicy');
$v_edit = $request->getStr('editPolicy');
$type_name = AlmanacNetworkTransaction::TYPE_NAME;
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
$xactions = array();
$xactions[] = id(new AlmanacNetworkTransaction())
->setTransactionType($type_name)
->setNewValue($v_name);
$xactions[] = id(new AlmanacNetworkTransaction())
->setTransactionType($type_view)
->setNewValue($v_view);
$xactions[] = id(new AlmanacNetworkTransaction())
->setTransactionType($type_edit)
->setNewValue($v_edit);
$editor = id(new AlmanacNetworkEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
try {
$editor->applyTransactions($network, $xactions);
$id = $network->getID();
$network_uri = $this->getApplicationURI("network/{$id}/");
return id(new AphrontRedirectResponse())->setURI($network_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_name = $ex->getShortMessage($type_name);
$network->setViewPolicy($v_view);
$network->setEditPolicy($v_edit);
}
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($network)
->execute();
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name'))
->setName('name')
->setValue($v_name)
->setError($e_name))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('viewPolicy')
->setPolicyObject($network)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicies($policies))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('editPolicy')
->setPolicyObject($network)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicies($policies))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri)
->setValue($save_button));
$box = id(new PHUIObjectBoxView())
->setValidationException($validation_exception)
->setHeaderText($title)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
if ($is_new) {
$crumbs->addTextCrumb(pht('Create Network'));
} else {
$crumbs->addTextCrumb($network->getName(), $network_uri);
$crumbs->addTextCrumb(pht('Edit'));
}
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild(
array(
$box,
));
return id(new AlmanacNetworkEditEngine())
->setController($this)
->buildResponse();
}
}

View file

@ -1,3 +1,56 @@
<?php
abstract class AlmanacPropertyController extends AlmanacController {}
abstract class AlmanacPropertyController extends AlmanacController {
private $propertyObject;
public function getPropertyObject() {
return $this->propertyObject;
}
protected function loadPropertyObject() {
$viewer = $this->getViewer();
$request = $this->getRequest();
$object_phid = $request->getStr('objectPHID');
switch (phid_get_type($object_phid)) {
case AlmanacBindingPHIDType::TYPECONST:
$query = new AlmanacBindingQuery();
break;
case AlmanacDevicePHIDType::TYPECONST:
$query = new AlmanacDeviceQuery();
break;
case AlmanacServicePHIDType::TYPECONST:
$query = new AlmanacServiceQuery();
break;
default:
return new Aphront404Response();
}
$object = $query
->setViewer($viewer)
->withPHIDs(array($object_phid))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->needProperties(true)
->executeOne();
if (!$object) {
return new Aphront404Response();
}
if (!($object instanceof AlmanacPropertyInterface)) {
return new Aphront404Response();
}
$this->propertyObject = $object;
return null;
}
}

View file

@ -1,27 +1,17 @@
<?php
final class AlmanacPropertyDeleteController
extends AlmanacDeviceController {
extends AlmanacPropertyController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withPHIDs(array($request->getStr('objectPHID')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$object) {
return new Aphront404Response();
$response = $this->loadPropertyObject();
if ($response) {
return $response;
}
if (!($object instanceof AlmanacPropertyInterface)) {
return new Aphront404Response();
}
$object = $this->getPropertyObject();
$key = $request->getStr('key');
if (!strlen($key)) {
@ -34,53 +24,24 @@ final class AlmanacPropertyDeleteController
$is_builtin = isset($builtins[$key]);
if ($is_builtin) {
// This is a builtin property, so we're going to reset it to the
// default value.
$field_list = PhabricatorCustomField::getObjectFields(
$object,
PhabricatorCustomField::ROLE_DEFAULT);
// Note that we're NOT loading field values from the object: we just want
// to get the field's default value so we can reset it.
$fields = $field_list->getFields();
$field = $fields[$key];
$is_delete = false;
$new_value = $field->getValueForStorage();
// Now, load the field to get the old value.
$field_list
->setViewer($viewer)
->readFieldsFromStorage($object);
$old_value = $field->getValueForStorage();
$title = pht('Reset Property');
$body = pht('Reset this property to its default value?');
$submit_text = pht('Reset');
$body = pht(
'Reset property "%s" to its default value?',
$key);
$submit_text = pht('Reset Property');
} else {
// This is a custom property, so we're going to delete it outright.
$is_delete = true;
$old_value = $object->getAlmanacPropertyValue($key);
$new_value = null;
$title = pht('Delete Property');
$body = pht('Delete this property? TODO: DOES NOT WORK YET');
$submit_text = pht('Delete');
$body = pht(
'Delete property "%s"?',
$key);
$submit_text = pht('Delete Property');
}
$validation_exception = null;
if ($request->isFormPost()) {
$xaction = $object->getApplicationTransactionTemplate()
->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD)
->setMetadataValue('customfield:key', $key)
->setOldValue($old_value)
->setNewValue($new_value);
// TODO: We aren't really deleting properties that we claim to delete
// yet, but that needs to be specialized a little bit.
->setTransactionType(AlmanacTransaction::TYPE_PROPERTY_REMOVE)
->setMetadataValue('almanac.property', $key);
$editor = $object->getApplicationTransactionEditor()
->setActor($viewer)

View file

@ -1,157 +1,95 @@
<?php
final class AlmanacPropertyEditController
extends AlmanacDeviceController {
extends AlmanacPropertyController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withPHIDs(array($request->getStr('objectPHID')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$object) {
return new Aphront404Response();
$response = $this->loadPropertyObject();
if ($response) {
return $response;
}
if (!($object instanceof AlmanacPropertyInterface)) {
return new Aphront404Response();
}
$object = $this->getPropertyObject();
$cancel_uri = $object->getURI();
$property_key = $request->getStr('key');
$key = $request->getStr('key');
if ($key) {
$property_key = $key;
$is_new = false;
$title = pht('Edit Property');
$save_button = pht('Save Changes');
if (!strlen($property_key)) {
return $this->buildPropertyKeyResponse($cancel_uri, null);
} else {
$property_key = null;
$is_new = true;
$title = pht('Add Property');
$save_button = pht('Add Property');
}
if ($is_new) {
$errors = array();
$property = null;
$v_name = null;
$e_name = true;
if ($request->isFormPost()) {
$name = $request->getStr('name');
if (!strlen($name)) {
$e_name = pht('Required');
$errors[] = pht('You must provide a property name.');
} else {
$caught = null;
$error = null;
try {
AlmanacNames::validateServiceOrDeviceName($name);
AlmanacNames::validateName($property_key);
} catch (Exception $ex) {
$caught = $ex;
$error = $ex->getMessage();
}
// NOTE: If you enter an existing name, we're just treating it as an
// edit operation. This might be a little confusing.
if ($error !== null) {
if ($request->isFormPost()) {
// The user is creating a new property and picked a bad name. Give
// them an opportunity to fix it.
return $this->buildPropertyKeyResponse($cancel_uri, $error);
} else {
// The user is editing an invalid property.
return $this->newDialog()
->setTitle(pht('Invalid Property'))
->appendParagraph(
pht(
'The property name "%s" is invalid. This property can not '.
'be edited.',
$property_key))
->appendParagraph($error)
->addCancelButton($cancel_uri);
}
if ($caught) {
$e_name = pht('Invalid');
$errors[] = $caught->getMessage();
}
}
if (!$errors) {
$property_key = $name;
}
return $object->newAlmanacPropertyEditEngine()
->addContextParameter('objectPHID')
->addContextParameter('key')
->setTargetObject($object)
->setPropertyKey($property_key)
->setController($this)
->buildResponse();
}
private function buildPropertyKeyResponse($cancel_uri, $error) {
$viewer = $this->getViewer();
$request = $this->getRequest();
$v_key = $request->getStr('key');
if ($error !== null) {
$e_key = pht('Invalid');
} else {
$e_key = true;
}
if ($property_key === null) {
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setName('key')
->setLabel(pht('Name'))
->setValue($v_name)
->setError($e_name));
->setValue($v_key)
->setError($e_key));
$errors = array();
if ($error !== null) {
$errors[] = $error;
}
return $this->newDialog()
->setTitle($title)
->setErrors($errors)
->setTitle(pht('Add Property'))
->addHiddenInput('objectPHID', $request->getStr('objectPHID'))
->setErrors($errors)
->appendForm($form)
->addSubmitButton(pht('Continue'))
->addCancelButton($cancel_uri);
}
}
// Make sure property key is appropriate.
// TODO: It would be cleaner to put this safety check in the Editor.
AlmanacNames::validateServiceOrDeviceName($property_key);
// If we're adding a new property, put a placeholder on the object so
// that we can build a CustomField for it.
if (!$object->hasAlmanacProperty($property_key)) {
$temporary_property = id(new AlmanacProperty())
->setObjectPHID($object->getPHID())
->setFieldName($property_key);
$object->attachAlmanacProperties(array($temporary_property));
}
$field_list = PhabricatorCustomField::getObjectFields(
$object,
PhabricatorCustomField::ROLE_DEFAULT);
// Select only the field being edited.
$fields = $field_list->getFields();
$fields = array_select_keys($fields, array($property_key));
$field_list = new PhabricatorCustomFieldList($fields);
$field_list
->setViewer($viewer)
->readFieldsFromStorage($object);
$validation_exception = null;
if ($request->isFormPost() && $request->getStr('isValueEdit')) {
$xactions = $field_list->buildFieldTransactionsFromRequest(
$object->getApplicationTransactionTemplate(),
$request);
$editor = $object->getApplicationTransactionEditor()
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
try {
$editor->applyTransactions($object, $xactions);
return id(new AphrontRedirectResponse())->setURI($cancel_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
}
}
$form = id(new AphrontFormView())
->setUser($viewer)
->addHiddenInput('objectPHID', $request->getStr('objectPHID'))
->addHiddenInput('key', $request->getStr('key'))
->addHiddenInput('name', $property_key)
->addHiddenInput('isValueEdit', true);
$field_list->appendFieldsToForm($form);
return $this->newDialog()
->setTitle($title)
->setValidationException($validation_exception)
->appendForm($form)
->addSubmitButton($save_button)
->addCancelButton($cancel_uri);
}
}

View file

@ -11,4 +11,14 @@ abstract class AlmanacServiceController extends AlmanacController {
return $crumbs;
}
protected function getPropertyDeleteURI($object) {
$id = $object->getID();
return "/almanac/service/delete/{$id}/";
}
protected function getPropertyUpdateURI($object) {
$id = $object->getID();
return "/almanac/service/property/{$id}/";
}
}

View file

@ -34,21 +34,19 @@ final class AlmanacServiceEditController
$this->requireApplicationCapability(
AlmanacCreateServicesCapability::CAPABILITY);
$service_class = $request->getStr('serviceClass');
$service_types = AlmanacServiceType::getAllServiceTypes();
if (empty($service_types[$service_class])) {
return $this->buildServiceTypeResponse($service_types, $cancel_uri);
$service_type = $request->getStr('serviceType');
try {
$service = AlmanacService::initializeNewService($service_type);
} catch (Exception $ex) {
return $this->buildServiceTypeResponse($cancel_uri);
}
$service_type = $service_types[$service_class];
if ($service_type->isClusterServiceType()) {
if ($service->isClusterService()) {
$this->requireApplicationCapability(
AlmanacCreateClusterServicesCapability::CAPABILITY);
AlmanacManageClusterServicesCapability::CAPABILITY);
}
$service = AlmanacService::initializeNewService();
$service->setServiceClass($service_class);
$service->attachServiceType($service_type);
$is_new = true;
$title = pht('Create Service');
@ -125,7 +123,7 @@ final class AlmanacServiceEditController
$form = id(new AphrontFormView())
->setUser($viewer)
->addHiddenInput('edit', true)
->addHiddenInput('serviceClass', $service->getServiceClass())
->addHiddenInput('serviceType', $service->getServiceType())
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name'))
@ -177,7 +175,9 @@ final class AlmanacServiceEditController
));
}
private function buildServiceTypeResponse(array $service_types, $cancel_uri) {
private function buildServiceTypeResponse($cancel_uri) {
$service_types = AlmanacServiceType::getAllServiceTypes();
$request = $this->getRequest();
$viewer = $this->getViewer();
@ -190,14 +190,14 @@ final class AlmanacServiceEditController
}
list($can_cluster, $cluster_link) = $this->explainApplicationCapability(
AlmanacCreateClusterServicesCapability::CAPABILITY,
AlmanacManageClusterServicesCapability::CAPABILITY,
pht('You have permission to create cluster services.'),
pht('You do not have permission to create new cluster services.'));
$type_control = id(new AphrontFormRadioButtonControl())
->setLabel(pht('Service Type'))
->setName('serviceClass')
->setName('serviceType')
->setError($e_service);
foreach ($service_types as $service_type) {
@ -211,7 +211,7 @@ final class AlmanacServiceEditController
}
$type_control->addButton(
get_class($service_type),
$service_type->getServiceTypeConstant(),
$service_type->getServiceTypeName(),
array(
$service_type->getServiceTypeDescription(),

View file

@ -15,6 +15,7 @@ final class AlmanacServiceViewController
$service = id(new AlmanacServiceQuery())
->setViewer($viewer)
->withNames(array($name))
->needProperties(true)
->executeOne();
if (!$service) {
return new Aphront404Response();
@ -35,15 +36,13 @@ final class AlmanacServiceViewController
->setHeader($header)
->addPropertyList($property_list);
$messages = $service->getServiceType()->getStatusMessages($service);
if ($messages) {
$box->setFormErrors($messages);
}
if ($service->getIsLocked()) {
$this->addLockMessage(
if ($service->isClusterService()) {
$this->addClusterMessage(
$box,
pht('This service is locked, and can not be edited.'));
pht('This is a cluster service.'),
pht(
'This service is a cluster service. You do not have permission to '.
'edit cluster services, so you can not edit this service.'));
}
$bindings = $this->buildBindingList($service);
@ -77,7 +76,7 @@ final class AlmanacServiceViewController
$properties->addProperty(
pht('Service Type'),
$service->getServiceType()->getServiceTypeShortName());
$service->getServiceImplementation()->getServiceTypeShortName());
return $properties;
}
@ -123,7 +122,8 @@ final class AlmanacServiceViewController
->setNoDataString(
pht('This service has not been bound to any device interfaces yet.'))
->setUser($viewer)
->setBindings($bindings);
->setBindings($bindings)
->setHideServiceColumn(true);
$header = id(new PHUIHeaderView())
->setHeader(pht('Service Bindings'))

View file

@ -1,77 +0,0 @@
<?php
final class AlmanacCoreCustomField
extends AlmanacCustomField
implements PhabricatorStandardCustomFieldInterface {
public function getStandardCustomFieldNamespace() {
return 'almanac:core';
}
public function getFieldKey() {
return $this->getProxy()->getRawStandardFieldKey();
}
public function getFieldName() {
return $this->getFieldKey();
}
public function createFields($object) {
$specs = $object->getAlmanacPropertyFieldSpecifications();
$default_specs = array();
foreach ($object->getAlmanacProperties() as $property) {
$default_specs[$property->getFieldName()] = array(
'name' => $property->getFieldName(),
'type' => 'text',
);
}
return PhabricatorStandardCustomField::buildStandardFields(
$this,
$specs + $default_specs);
}
public function shouldUseStorage() {
return false;
}
public function readValueFromObject(PhabricatorCustomFieldInterface $object) {
$key = $this->getFieldKey();
if ($object->hasAlmanacProperty($key)) {
$this->setValueFromStorage($object->getAlmanacPropertyValue($key));
}
}
public function applyApplicationTransactionInternalEffects(
PhabricatorApplicationTransaction $xaction) {
return;
}
public function applyApplicationTransactionExternalEffects(
PhabricatorApplicationTransaction $xaction) {
$object = $this->getObject();
$phid = $object->getPHID();
$key = $this->getFieldKey();
$property = id(new AlmanacPropertyQuery())
->setViewer($this->getViewer())
->withObjectPHIDs(array($phid))
->withNames(array($key))
->executeOne();
if (!$property) {
$property = id(new AlmanacProperty())
->setObjectPHID($phid)
->setFieldIndex(PhabricatorHash::digestForIndex($key))
->setFieldName($key);
}
$property
->setFieldValue($xaction->getNewValue())
->save();
}
}

View file

@ -1,4 +0,0 @@
<?php
abstract class AlmanacCustomField
extends PhabricatorCustomField {}

View file

@ -1,11 +1,9 @@
<?php
final class AlmanacBindingEditor
extends PhabricatorApplicationTransactionEditor {
extends AlmanacEditor {
public function getEditorApplicationClass() {
return 'PhabricatorAlmanacApplication';
}
private $devicePHID;
public function getEditorObjectsDescription() {
return pht('Almanac Binding');
@ -15,6 +13,7 @@ final class AlmanacBindingEditor
$types = parent::getTransactionTypes();
$types[] = AlmanacBindingTransaction::TYPE_INTERFACE;
$types[] = AlmanacBindingTransaction::TYPE_DISABLE;
return $types;
}
@ -25,6 +24,8 @@ final class AlmanacBindingEditor
switch ($xaction->getTransactionType()) {
case AlmanacBindingTransaction::TYPE_INTERFACE:
return $object->getInterfacePHID();
case AlmanacBindingTransaction::TYPE_DISABLE:
return $object->getIsDisabled();
}
return parent::getCustomTransactionOldValue($object, $xaction);
@ -37,6 +38,8 @@ final class AlmanacBindingEditor
switch ($xaction->getTransactionType()) {
case AlmanacBindingTransaction::TYPE_INTERFACE:
return $xaction->getNewValue();
case AlmanacBindingTransaction::TYPE_DISABLE:
return (int)$xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
@ -55,6 +58,9 @@ final class AlmanacBindingEditor
$object->setDevicePHID($interface->getDevicePHID());
$object->setInterfacePHID($interface->getPHID());
return;
case AlmanacBindingTransaction::TYPE_DISABLE:
$object->setIsDisabled($xaction->getNewValue());
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
@ -65,7 +71,37 @@ final class AlmanacBindingEditor
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case AlmanacBindingTransaction::TYPE_DISABLE:
return;
case AlmanacBindingTransaction::TYPE_INTERFACE:
$interface_phids = array();
$interface_phids[] = $xaction->getOldValue();
$interface_phids[] = $xaction->getNewValue();
$interface_phids = array_filter($interface_phids);
$interface_phids = array_unique($interface_phids);
$interfaces = id(new AlmanacInterfaceQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs($interface_phids)
->execute();
$device_phids = array();
foreach ($interfaces as $interface) {
$device_phids[] = $interface->getDevicePHID();
}
$device_phids = array_unique($device_phids);
$devices = id(new AlmanacDeviceQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs($device_phids)
->execute();
foreach ($devices as $device) {
$device->rebuildClusterBindingStatus();
}
return;
}

View file

@ -0,0 +1,16 @@
<?php
final class AlmanacBindingPropertyEditEngine
extends AlmanacPropertyEditEngine {
const ENGINECONST = 'almanac.binding.property';
protected function newObjectQuery() {
return new AlmanacBindingQuery();
}
protected function getObjectViewURI($object) {
return $object->getURI();
}
}

View file

@ -1,11 +1,7 @@
<?php
final class AlmanacDeviceEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorAlmanacApplication';
}
extends AlmanacEditor {
public function getEditorObjectsDescription() {
return pht('Almanac Device');
@ -132,7 +128,7 @@ final class AlmanacDeviceEditor
$name = $xaction->getNewValue();
try {
AlmanacNames::validateServiceOrDeviceName($name);
AlmanacNames::validateName($name);
} catch (Exception $ex) {
$message = $ex->getMessage();
}
@ -144,22 +140,42 @@ final class AlmanacDeviceEditor
$message,
$xaction);
$errors[] = $error;
}
}
continue;
}
if ($xactions) {
$duplicate = id(new AlmanacDeviceQuery())
$other = id(new AlmanacDeviceQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withNames(array(last($xactions)->getNewValue()))
->withNames(array($name))
->executeOne();
if ($duplicate && ($duplicate->getID() != $object->getID())) {
if ($other && ($other->getID() != $object->getID())) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Not Unique'),
pht('Almanac devices must have unique names.'),
last($xactions));
$xaction);
$errors[] = $error;
continue;
}
if ($name === $object->getName()) {
continue;
}
$namespace = AlmanacNamespace::loadRestrictedNamespace(
$this->getActor(),
$name);
if ($namespace) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Restricted'),
pht(
'You do not have permission to create Almanac devices '.
'within the "%s" namespace.',
$namespace->getName()),
$xaction);
$errors[] = $error;
continue;
}
}
}
@ -294,6 +310,19 @@ final class AlmanacDeviceEditor
pht('You can not edit an invalid or restricted interface.'),
$xaction);
$errors[] = $error;
continue;
}
$new = $xaction->getNewValue();
if (!$new) {
if ($interface->loadIsInUse()) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('In Use'),
pht('You can not delete an interface which is still in use.'),
$xaction);
$errors[] = $error;
}
}
}
}
@ -303,6 +332,4 @@ final class AlmanacDeviceEditor
return $errors;
}
}

View file

@ -0,0 +1,16 @@
<?php
final class AlmanacDevicePropertyEditEngine
extends AlmanacPropertyEditEngine {
const ENGINECONST = 'almanac.device.property';
protected function newObjectQuery() {
return new AlmanacDeviceQuery();
}
protected function getObjectViewURI($object) {
return $object->getURI();
}
}

View file

@ -0,0 +1,156 @@
<?php
abstract class AlmanacEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorAlmanacApplication';
}
protected function supportsSearch() {
return true;
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = AlmanacTransaction::TYPE_PROPERTY_UPDATE;
$types[] = AlmanacTransaction::TYPE_PROPERTY_REMOVE;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case AlmanacTransaction::TYPE_PROPERTY_UPDATE:
case AlmanacTransaction::TYPE_PROPERTY_REMOVE:
$property_key = $xaction->getMetadataValue('almanac.property');
$exists = $object->hasAlmanacProperty($property_key);
$value = $object->getAlmanacPropertyValue($property_key);
return array(
'existed' => $exists,
'value' => $value,
);
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case AlmanacTransaction::TYPE_PROPERTY_UPDATE:
case AlmanacTransaction::TYPE_PROPERTY_REMOVE:
return $xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case AlmanacTransaction::TYPE_PROPERTY_UPDATE:
case AlmanacTransaction::TYPE_PROPERTY_REMOVE:
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case AlmanacTransaction::TYPE_PROPERTY_UPDATE:
$property_key = $xaction->getMetadataValue('almanac.property');
if ($object->hasAlmanacProperty($property_key)) {
$property = $object->getAlmanacProperty($property_key);
} else {
$property = id(new AlmanacProperty())
->setObjectPHID($object->getPHID())
->setFieldName($property_key);
}
$property
->setFieldValue($xaction->getNewValue())
->save();
return;
case AlmanacTransaction::TYPE_PROPERTY_REMOVE:
$property_key = $xaction->getMetadataValue('almanac.property');
if ($object->hasAlmanacProperty($property_key)) {
$property = $object->getAlmanacProperty($property_key);
$property->delete();
}
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case AlmanacTransaction::TYPE_PROPERTY_UPDATE:
foreach ($xactions as $xaction) {
$property_key = $xaction->getMetadataValue('almanac.property');
$message = null;
try {
AlmanacNames::validateName($property_key);
} catch (Exception $ex) {
$message = $ex->getMessage();
}
if ($message !== null) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
$message,
$xaction);
$errors[] = $error;
continue;
}
$new_value = $xaction->getNewValue();
try {
phutil_json_encode($new_value);
} catch (Exception $ex) {
$message = pht(
'Almanac property values must be representable in JSON. %s',
$ex->getMessage());
}
if ($message !== null) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
$message,
$xaction);
$errors[] = $error;
continue;
}
}
break;
case AlmanacTransaction::TYPE_PROPERTY_REMOVE:
// NOTE: No name validation on removals since it's OK to delete
// an invalid property that somehow came into existence.
break;
}
return $errors;
}
}

View file

@ -0,0 +1,86 @@
<?php
final class AlmanacNamespaceEditEngine
extends PhabricatorEditEngine {
const ENGINECONST = 'almanac.namespace';
public function isEngineConfigurable() {
return false;
}
public function getEngineName() {
return pht('Almanac Namespaces');
}
public function getSummaryHeader() {
return pht('Edit Almanac Namespace Configurations');
}
public function getSummaryText() {
return pht('This engine is used to edit Almanac namespaces.');
}
public function getEngineApplicationClass() {
return 'PhabricatorAlmanacApplication';
}
protected function newEditableObject() {
return AlmanacNamespace::initializeNewNamespace();
}
protected function newObjectQuery() {
return new AlmanacNamespaceQuery();
}
protected function getObjectCreateTitleText($object) {
return pht('Create Namespace');
}
protected function getObjectCreateButtonText($object) {
return pht('Create Namespace');
}
protected function getObjectEditTitleText($object) {
return pht('Edit Namespace: %s', $object->getName());
}
protected function getObjectEditShortText($object) {
return pht('Edit Namespace');
}
protected function getObjectCreateShortText() {
return pht('Create Namespace');
}
protected function getEditorURI() {
return '/almanac/namespace/edit/';
}
protected function getObjectCreateCancelURI($object) {
return '/almanac/namespace/';
}
protected function getObjectViewURI($object) {
$id = $object->getID();
return "/almanac/namespace/{$id}/";
}
protected function getCreateNewObjectPolicy() {
return $this->getApplication()->getPolicy(
AlmanacCreateNamespacesCapability::CAPABILITY);
}
protected function buildCustomEditFields($object) {
return array(
id(new PhabricatorTextEditField())
->setKey('name')
->setLabel(pht('Name'))
->setDescription(pht('Name of the namespace.'))
->setTransactionType(AlmanacNamespaceTransaction::TYPE_NAME)
->setIsRequired(true)
->setValue($object->getName()),
);
}
}

View file

@ -0,0 +1,182 @@
<?php
final class AlmanacNamespaceEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorAlmanacApplication';
}
public function getEditorObjectsDescription() {
return pht('Almanac Namespace');
}
protected function supportsSearch() {
return true;
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = AlmanacNamespaceTransaction::TYPE_NAME;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case AlmanacNamespaceTransaction::TYPE_NAME:
return $object->getName();
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case AlmanacNamespaceTransaction::TYPE_NAME:
return $xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case AlmanacNamespaceTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case AlmanacNamespaceTransaction::TYPE_NAME:
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case AlmanacNamespaceTransaction::TYPE_NAME:
$missing = $this->validateIsEmptyTextField(
$object->getName(),
$xactions);
if ($missing) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('Namespace name is required.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$errors[] = $error;
} else {
foreach ($xactions as $xaction) {
$name = $xaction->getNewValue();
$message = null;
try {
AlmanacNames::validateName($name);
} catch (Exception $ex) {
$message = $ex->getMessage();
}
if ($message !== null) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
$message,
$xaction);
$errors[] = $error;
continue;
}
$other = id(new AlmanacNamespaceQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withNames(array($name))
->executeOne();
if ($other && ($other->getID() != $object->getID())) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Not Unique'),
pht(
'The namespace name "%s" is already in use by another '.
'namespace. Each namespace must have a unique name.',
$name),
$xaction);
$errors[] = $error;
continue;
}
if ($name === $object->getName()) {
continue;
}
$namespace = AlmanacNamespace::loadRestrictedNamespace(
$this->getActor(),
$name);
if ($namespace) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Restricted'),
pht(
'You do not have permission to create Almanac namespaces '.
'within the "%s" namespace.',
$namespace->getName()),
$xaction);
$errors[] = $error;
continue;
}
}
}
break;
}
return $errors;
}
protected function didCatchDuplicateKeyException(
PhabricatorLiskDAO $object,
array $xactions,
Exception $ex) {
$errors = array();
$errors[] = new PhabricatorApplicationTransactionValidationError(
null,
pht('Invalid'),
pht(
'Another namespace with this name already exists. Each namespace '.
'must have a unique name.'),
null);
throw new PhabricatorApplicationTransactionValidationException($errors);
}
}

View file

@ -0,0 +1,86 @@
<?php
final class AlmanacNetworkEditEngine
extends PhabricatorEditEngine {
const ENGINECONST = 'almanac.network';
public function isEngineConfigurable() {
return false;
}
public function getEngineName() {
return pht('Almanac Networks');
}
public function getSummaryHeader() {
return pht('Edit Almanac Network Configurations');
}
public function getSummaryText() {
return pht('This engine is used to edit Almanac networks.');
}
public function getEngineApplicationClass() {
return 'PhabricatorAlmanacApplication';
}
protected function newEditableObject() {
return AlmanacNetwork::initializeNewNetwork();
}
protected function newObjectQuery() {
return new AlmanacNetworkQuery();
}
protected function getObjectCreateTitleText($object) {
return pht('Create Network');
}
protected function getObjectCreateButtonText($object) {
return pht('Create Network');
}
protected function getObjectEditTitleText($object) {
return pht('Edit Network: %s', $object->getName());
}
protected function getObjectEditShortText($object) {
return pht('Edit Network');
}
protected function getObjectCreateShortText() {
return pht('Create Network');
}
protected function getEditorURI() {
return '/almanac/network/edit/';
}
protected function getObjectCreateCancelURI($object) {
return '/almanac/network/';
}
protected function getObjectViewURI($object) {
$id = $object->getID();
return "/almanac/network/{$id}/";
}
protected function getCreateNewObjectPolicy() {
return $this->getApplication()->getPolicy(
AlmanacCreateNetworksCapability::CAPABILITY);
}
protected function buildCustomEditFields($object) {
return array(
id(new PhabricatorTextEditField())
->setKey('name')
->setLabel(pht('Name'))
->setDescription(pht('Name of the network.'))
->setTransactionType(AlmanacNetworkTransaction::TYPE_NAME)
->setIsRequired(true)
->setValue($object->getName()),
);
}
}

View file

@ -11,6 +11,10 @@ final class AlmanacNetworkEditor
return pht('Almanac Network');
}
protected function supportsSearch() {
return true;
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();

View file

@ -0,0 +1,79 @@
<?php
abstract class AlmanacPropertyEditEngine
extends PhabricatorEditEngine {
private $propertyKey;
public function setPropertyKey($property_key) {
$this->propertyKey = $property_key;
return $this;
}
public function getPropertyKey() {
return $this->propertyKey;
}
public function isEngineConfigurable() {
return false;
}
public function isEngineExtensible() {
return false;
}
public function getEngineName() {
return pht('Almanac Properties');
}
public function getSummaryHeader() {
return pht('Edit Almanac Property Configurations');
}
public function getSummaryText() {
return pht('This engine is used to edit Almanac properties.');
}
public function getEngineApplicationClass() {
return 'PhabricatorAlmanacApplication';
}
protected function newEditableObject() {
throw new PhutilMethodNotImplementedException();
}
protected function getObjectCreateTitleText($object) {
return pht('Create Property');
}
protected function getObjectCreateButtonText($object) {
return pht('Create Property');
}
protected function getObjectEditTitleText($object) {
return pht('Edit Property: %s', $object->getName());
}
protected function getObjectEditShortText($object) {
return pht('Edit Property');
}
protected function getObjectCreateShortText() {
return pht('Create Property');
}
protected function buildCustomEditFields($object) {
$property_key = $this->getPropertyKey();
$xaction_type = AlmanacTransaction::TYPE_PROPERTY_UPDATE;
return array(
id(new PhabricatorTextEditField())
->setKey('value')
->setMetadataValue('almanac.property', $property_key)
->setLabel($property_key)
->setTransactionType($xaction_type)
->setValue($object->getAlmanacPropertyValue($property_key)),
);
}
}

View file

@ -1,11 +1,7 @@
<?php
final class AlmanacServiceEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorAlmanacApplication';
}
extends AlmanacEditor {
public function getEditorObjectsDescription() {
return pht('Almanac Service');
@ -15,7 +11,6 @@ final class AlmanacServiceEditor
$types = parent::getTransactionTypes();
$types[] = AlmanacServiceTransaction::TYPE_NAME;
$types[] = AlmanacServiceTransaction::TYPE_LOCK;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
@ -29,8 +24,6 @@ final class AlmanacServiceEditor
switch ($xaction->getTransactionType()) {
case AlmanacServiceTransaction::TYPE_NAME:
return $object->getName();
case AlmanacServiceTransaction::TYPE_LOCK:
return (bool)$object->getIsLocked();
}
return parent::getCustomTransactionOldValue($object, $xaction);
@ -43,8 +36,6 @@ final class AlmanacServiceEditor
switch ($xaction->getTransactionType()) {
case AlmanacServiceTransaction::TYPE_NAME:
return $xaction->getNewValue();
case AlmanacServiceTransaction::TYPE_LOCK:
return (bool)$xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
@ -58,9 +49,6 @@ final class AlmanacServiceEditor
case AlmanacServiceTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
return;
case AlmanacServiceTransaction::TYPE_LOCK:
$object->setIsLocked((int)$xaction->getNewValue());
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
@ -73,23 +61,6 @@ final class AlmanacServiceEditor
switch ($xaction->getTransactionType()) {
case AlmanacServiceTransaction::TYPE_NAME:
return;
case AlmanacServiceTransaction::TYPE_LOCK:
$service = id(new AlmanacServiceQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($object->getPHID()))
->needBindings(true)
->executeOne();
$devices = array();
foreach ($service->getBindings() as $binding) {
$device = $binding->getInterface()->getDevice();
$devices[$device->getPHID()] = $device;
}
foreach ($devices as $device) {
$device->rebuildDeviceLocks();
}
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
@ -124,7 +95,7 @@ final class AlmanacServiceEditor
$name = $xaction->getNewValue();
try {
AlmanacNames::validateServiceOrDeviceName($name);
AlmanacNames::validateName($name);
} catch (Exception $ex) {
$message = $ex->getMessage();
}
@ -136,22 +107,42 @@ final class AlmanacServiceEditor
$message,
$xaction);
$errors[] = $error;
}
}
continue;
}
if ($xactions) {
$duplicate = id(new AlmanacServiceQuery())
$other = id(new AlmanacServiceQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withNames(array(last($xactions)->getNewValue()))
->withNames(array($name))
->executeOne();
if ($duplicate && ($duplicate->getID() != $object->getID())) {
if ($other && ($other->getID() != $object->getID())) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Not Unique'),
pht('Almanac services must have unique names.'),
last($xactions));
$errors[] = $error;
continue;
}
if ($name === $object->getName()) {
continue;
}
$namespace = AlmanacNamespace::loadRestrictedNamespace(
$this->getActor(),
$name);
if ($namespace) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Restricted'),
pht(
'You do not have permission to create Almanac services '.
'within the "%s" namespace.',
$namespace->getName()),
$xaction);
$errors[] = $error;
continue;
}
}
}
@ -161,6 +152,4 @@ final class AlmanacServiceEditor
return $errors;
}
}

View file

@ -0,0 +1,16 @@
<?php
final class AlmanacServicePropertyEditEngine
extends AlmanacPropertyEditEngine {
const ENGINECONST = 'almanac.service.property';
protected function newObjectQuery() {
return new AlmanacServiceQuery();
}
protected function getObjectViewURI($object) {
return $object->getURI();
}
}

View file

@ -0,0 +1,30 @@
<?php
final class AlmanacBindingsSearchEngineAttachment
extends AlmanacSearchEngineAttachment {
public function getAttachmentName() {
return pht('Almanac Bindings');
}
public function getAttachmentDescription() {
return pht('Get Almanac bindings for the service.');
}
public function willLoadAttachmentData($query, $spec) {
$query->needProperties(true);
$query->needBindings(true);
}
public function getAttachmentForObject($object, $data, $spec) {
$bindings = array();
foreach ($object->getBindings() as $binding) {
$bindings[] = $this->getAlmanacBindingDictionary($binding);
}
return array(
'bindings' => $bindings,
);
}
}

View file

@ -0,0 +1,26 @@
<?php
final class AlmanacPropertiesSearchEngineAttachment
extends AlmanacSearchEngineAttachment {
public function getAttachmentName() {
return pht('Almanac Properties');
}
public function getAttachmentDescription() {
return pht('Get Almanac properties for the object.');
}
public function willLoadAttachmentData($query, $spec) {
$query->needProperties(true);
}
public function getAttachmentForObject($object, $data, $spec) {
$properties = $this->getAlmanacPropertyList($object);
return array(
'properties' => $properties,
);
}
}

View file

@ -0,0 +1,65 @@
<?php
abstract class AlmanacSearchEngineAttachment
extends PhabricatorSearchEngineAttachment {
protected function getAlmanacPropertyList($object) {
$builtins = $object->getAlmanacPropertyFieldSpecifications();
$properties = array();
foreach ($object->getAlmanacProperties() as $key => $property) {
$is_builtin = isset($builtins[$key]);
$properties[] = array(
'key' => $key,
'value' => $property->getFieldValue(),
'builtin' => $is_builtin,
);
}
return $properties;
}
protected function getAlmanacBindingDictionary(AlmanacBinding $binding) {
$interface = $binding->getInterface();
return array(
'id' => (int)$binding->getID(),
'phid' => $binding->getPHID(),
'properties' => $this->getAlmanacPropertyList($binding),
'interface' => $this->getAlmanacInterfaceDictionary($interface),
'disabled' => (bool)$binding->getIsDisabled(),
);
}
protected function getAlmanacInterfaceDictionary(
AlmanacInterface $interface) {
return array(
'id' => (int)$interface->getID(),
'phid' => $interface->getPHID(),
'address' => $interface->getAddress(),
'port' => (int)$interface->getPort(),
'device' => $this->getAlmanacDeviceDictionary($interface->getDevice()),
'network' => $this->getAlmanacNetworkDictionary($interface->getNetwork()),
);
}
protected function getAlmanacDeviceDictionary(AlmanacDevice $device) {
return array(
'id' => (int)$device->getID(),
'phid' => $device->getPHID(),
'name' => $device->getName(),
'properties' => $this->getAlmanacPropertyList($device),
);
}
protected function getAlmanacNetworkDictionary(AlmanacNetwork $network) {
return array(
'id' => (int)$network->getID(),
'phid' => $network->getPHID(),
'name' => $network->getName(),
);
}
}

View file

@ -1,49 +0,0 @@
<?php
final class AlmanacManagementLockWorkflow
extends AlmanacManagementWorkflow {
protected function didConstruct() {
$this
->setName('lock')
->setSynopsis(pht('Lock a service to prevent it from being edited.'))
->setArguments(
array(
array(
'name' => 'services',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$services = $this->loadServices($args->getArg('services'));
if (!$services) {
throw new PhutilArgumentUsageException(
pht('Specify at least one service to lock.'));
}
foreach ($services as $service) {
if ($service->getIsLocked()) {
throw new PhutilArgumentUsageException(
pht(
'Service "%s" is already locked!',
$service->getName()));
}
}
foreach ($services as $service) {
$this->updateServiceLock($service, true);
$console->writeOut(
"**<bg:green> %s </bg>** %s\n",
pht('LOCKED'),
pht('Service "%s" was locked.', $service->getName()));
}
return 0;
}
}

View file

@ -1,49 +0,0 @@
<?php
final class AlmanacManagementUnlockWorkflow
extends AlmanacManagementWorkflow {
protected function didConstruct() {
$this
->setName('unlock')
->setSynopsis(pht('Unlock a service to allow it to be edited.'))
->setArguments(
array(
array(
'name' => 'services',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$services = $this->loadServices($args->getArg('services'));
if (!$services) {
throw new PhutilArgumentUsageException(
pht('Specify at least one service to unlock.'));
}
foreach ($services as $service) {
if (!$service->getIsLocked()) {
throw new PhutilArgumentUsageException(
pht(
'Service "%s" is not locked!',
$service->getName()));
}
}
foreach ($services as $service) {
$this->updateServiceLock($service, false);
$console->writeOut(
"**<bg:green> %s </bg>** %s\n",
pht('UNLOCKED'),
pht('Service "%s" was unlocked.', $service->getName()));
}
return 0;
}
}

View file

@ -0,0 +1,44 @@
<?php
final class AlmanacNamespacePHIDType extends PhabricatorPHIDType {
const TYPECONST = 'ANAM';
public function getTypeName() {
return pht('Almanac Namespace');
}
public function newObject() {
return new AlmanacNamespace();
}
public function getPHIDTypeApplicationClass() {
return 'PhabricatorAlmanacApplication';
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new AlmanacNamespaceQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$namespace = $objects[$phid];
$id = $namespace->getID();
$name = $namespace->getName();
$handle->setObjectName(pht('Namespace %d', $id));
$handle->setName($name);
$handle->setURI($namespace->getURI());
}
}
}

View file

@ -8,5 +8,6 @@ interface AlmanacPropertyInterface {
public function getAlmanacProperty($key);
public function getAlmanacPropertyValue($key, $default = null);
public function getAlmanacPropertyFieldSpecifications();
public function newAlmanacPropertyEditEngine();
}

View file

@ -34,19 +34,12 @@ final class AlmanacBindingQuery
return $this;
}
public function newResultObject() {
return new AlmanacBinding();
}
protected function loadPage() {
$table = new AlmanacBinding();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
return $this->loadStandardPage($this->newResultObject());
}
protected function willFilterPage(array $bindings) {
@ -58,6 +51,7 @@ final class AlmanacBindingQuery
->setParentQuery($this)
->setViewer($this->getViewer())
->withPHIDs($service_phids)
->needProperties($this->getNeedProperties())
->execute();
$services = mpull($services, null, 'getPHID');
@ -65,6 +59,7 @@ final class AlmanacBindingQuery
->setParentQuery($this)
->setViewer($this->getViewer())
->withPHIDs($device_phids)
->needProperties($this->getNeedProperties())
->execute();
$devices = mpull($devices, null, 'getPHID');
@ -72,6 +67,7 @@ final class AlmanacBindingQuery
->setParentQuery($this)
->setViewer($this->getViewer())
->withPHIDs($interface_phids)
->needProperties($this->getNeedProperties())
->execute();
$interfaces = mpull($interfaces, null, 'getPHID');
@ -93,47 +89,45 @@ final class AlmanacBindingQuery
return $bindings;
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'phid IN (%Ls)',
$this->phids);
}
if ($this->servicePHIDs !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'servicePHID IN (%Ls)',
$this->servicePHIDs);
}
if ($this->devicePHIDs !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'devicePHID IN (%Ls)',
$this->devicePHIDs);
}
if ($this->interfacePHIDs !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'interfacePHID IN (%Ls)',
$this->interfacePHIDs);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
return $where;
}
}

View file

@ -34,35 +34,34 @@ final class AlmanacDeviceQuery
return $this;
}
protected function loadPage() {
$table = new AlmanacDevice();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
public function withNameNgrams($ngrams) {
return $this->withNgramsConstraint(
new AlmanacDeviceNameNgrams(),
$ngrams);
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
public function newResultObject() {
return new AlmanacDevice();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$conn,
'device.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$conn,
'device.phid IN (%Ls)',
$this->phids);
}
@ -72,28 +71,59 @@ final class AlmanacDeviceQuery
$hashes[] = PhabricatorHash::digestForIndex($name);
}
$where[] = qsprintf(
$conn_r,
'nameIndex IN (%Ls)',
$conn,
'device.nameIndex IN (%Ls)',
$hashes);
}
if ($this->namePrefix !== null) {
$where[] = qsprintf(
$conn_r,
'name LIKE %>',
$conn,
'device.name LIKE %>',
$this->namePrefix);
}
if ($this->nameSuffix !== null) {
$where[] = qsprintf(
$conn_r,
'name LIKE %<',
$conn,
'device.name LIKE %<',
$this->nameSuffix);
}
$where[] = $this->buildPagingClause($conn_r);
return $where;
}
return $this->formatWhereClause($where);
protected function getPrimaryTableAlias() {
return 'device';
}
public function getOrderableColumns() {
return parent::getOrderableColumns() + array(
'name' => array(
'table' => $this->getPrimaryTableAlias(),
'column' => 'name',
'type' => 'string',
'unique' => true,
'reverse' => true,
),
);
}
protected function getPagingValueMap($cursor, array $keys) {
$device = $this->loadCursorObject($cursor);
return array(
'id' => $device->getID(),
'name' => $device->getName(),
);
}
public function getBuiltinOrders() {
return array(
'name' => array(
'vector' => array('name'),
'name' => pht('Device Name'),
),
) + parent::getBuiltinOrders();
}
public function getQueryApplicationClass() {

View file

@ -11,22 +11,37 @@ final class AlmanacDeviceSearchEngine
return 'PhabricatorAlmanacApplication';
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
return $saved;
public function newQuery() {
return new AlmanacDeviceQuery();
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new AlmanacDeviceQuery());
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorSearchTextField())
->setLabel(pht('Name Contains'))
->setKey('match')
->setDescription(pht('Search for devices by name substring.')),
id(new PhabricatorSearchStringListField())
->setLabel(pht('Exact Names'))
->setKey('names')
->setDescription(pht('Search for devices with specific names.')),
);
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['match'] !== null) {
$query->withNameNgrams($map['match']);
}
if ($map['names']) {
$query->withNames($map['names']);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {}
protected function getURI($path) {
return '/almanac/device/'.$path;
}
@ -52,12 +67,6 @@ final class AlmanacDeviceSearchEngine
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $devices,
PhabricatorSavedQuery $query) {
return array();
}
protected function renderResultList(
array $devices,
PhabricatorSavedQuery $query,
@ -75,6 +84,10 @@ final class AlmanacDeviceSearchEngine
->setHref($device->getURI())
->setObject($device);
if ($device->isClusterDevice()) {
$item->addIcon('fa-sitemap', pht('Cluster Device'));
}
$list->addItem($item);
}

View file

@ -1,7 +1,7 @@
<?php
final class AlmanacInterfaceQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
extends AlmanacQuery {
private $ids;
private $phids;
@ -34,19 +34,12 @@ final class AlmanacInterfaceQuery
return $this;
}
public function newResultObject() {
return new AlmanacInterface();
}
protected function loadPage() {
$table = new AlmanacInterface();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
return $this->loadStandardPage($this->newResultObject());
}
protected function willFilterPage(array $interfaces) {
@ -57,6 +50,7 @@ final class AlmanacInterfaceQuery
->setParentQuery($this)
->setViewer($this->getViewer())
->withPHIDs($network_phids)
->needProperties($this->getNeedProperties())
->execute();
$networks = mpull($networks, null, 'getPHID');
@ -64,6 +58,7 @@ final class AlmanacInterfaceQuery
->setParentQuery($this)
->setViewer($this->getViewer())
->withPHIDs($device_phids)
->needProperties($this->getNeedProperties())
->execute();
$devices = mpull($devices, null, 'getPHID');
@ -83,34 +78,34 @@ final class AlmanacInterfaceQuery
return $interfaces;
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$conn,
'interface.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$conn,
'interface.phid IN (%Ls)',
$this->phids);
}
if ($this->networkPHIDs !== null) {
$where[] = qsprintf(
$conn_r,
'networkPHID IN (%Ls)',
$conn,
'interface.networkPHID IN (%Ls)',
$this->networkPHIDs);
}
if ($this->devicePHIDs !== null) {
$where[] = qsprintf(
$conn_r,
'devicePHID IN (%Ls)',
$conn,
'interface.devicePHID IN (%Ls)',
$this->devicePHIDs);
}
@ -118,8 +113,10 @@ final class AlmanacInterfaceQuery
$parts = array();
foreach ($this->addresses as $address) {
$parts[] = qsprintf(
$conn_r,
'(networkPHID = %s AND address = %s AND port = %d)',
$conn,
'(interface.networkPHID = %s '.
'AND interface.address = %s '.
'AND interface.port = %d)',
$address->getNetworkPHID(),
$address->getAddress(),
$address->getPort());
@ -127,13 +124,77 @@ final class AlmanacInterfaceQuery
$where[] = implode(' OR ', $parts);
}
$where[] = $this->buildPagingClause($conn_r);
return $where;
}
return $this->formatWhereClause($where);
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = parent::buildJoinClauseParts($conn);
if ($this->shouldJoinDeviceTable()) {
$joins[] = qsprintf(
$conn,
'JOIN %T device ON device.phid = interface.devicePHID',
id(new AlmanacDevice())->getTableName());
}
return $joins;
}
protected function shouldGroupQueryResultRows() {
if ($this->shouldJoinDeviceTable()) {
return true;
}
return parent::shouldGroupQueryResultRows();
}
private function shouldJoinDeviceTable() {
$vector = $this->getOrderVector();
if ($vector->containsKey('name')) {
return true;
}
return false;
}
protected function getPrimaryTableAlias() {
return 'interface';
}
public function getQueryApplicationClass() {
return 'PhabricatorAlmanacApplication';
}
public function getBuiltinOrders() {
return array(
'name' => array(
'vector' => array('name', 'id'),
'name' => pht('Device Name'),
),
) + parent::getBuiltinOrders();
}
public function getOrderableColumns() {
return parent::getOrderableColumns() + array(
'name' => array(
'table' => 'device',
'column' => 'name',
'type' => 'string',
'reverse' => true,
),
);
}
protected function getPagingValueMap($cursor, array $keys) {
$interface = $this->loadCursorObject($cursor);
$map = array(
'id' => $interface->getID(),
'name' => $interface->getDevice()->getName(),
);
return $map;
}
}

View file

@ -0,0 +1,103 @@
<?php
final class AlmanacNamespaceQuery
extends AlmanacQuery {
private $ids;
private $phids;
private $names;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withNames(array $names) {
$this->names = $names;
return $this;
}
public function withNameNgrams($ngrams) {
return $this->withNgramsConstraint(
new AlmanacNamespaceNameNgrams(),
$ngrams);
}
public function newResultObject() {
return new AlmanacNamespace();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'namespace.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'namespace.phid IN (%Ls)',
$this->phids);
}
if ($this->names !== null) {
$where[] = qsprintf(
$conn,
'namespace.name IN (%Ls)',
$this->names);
}
return $where;
}
protected function getPrimaryTableAlias() {
return 'namespace';
}
public function getOrderableColumns() {
return parent::getOrderableColumns() + array(
'name' => array(
'table' => $this->getPrimaryTableAlias(),
'column' => 'name',
'type' => 'string',
'unique' => true,
'reverse' => true,
),
);
}
protected function getPagingValueMap($cursor, array $keys) {
$namespace = $this->loadCursorObject($cursor);
return array(
'id' => $namespace->getID(),
'name' => $namespace->getName(),
);
}
public function getBuiltinOrders() {
return array(
'name' => array(
'vector' => array('name'),
'name' => pht('Namespace Name'),
),
) + parent::getBuiltinOrders();
}
public function getQueryApplicationClass() {
return 'PhabricatorAlmanacApplication';
}
}

View file

@ -0,0 +1,90 @@
<?php
final class AlmanacNamespaceSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Almanac Namespaces');
}
public function getApplicationClassName() {
return 'PhabricatorAlmanacApplication';
}
public function newQuery() {
return new AlmanacNamespaceQuery();
}
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorSearchTextField())
->setLabel(pht('Name Contains'))
->setKey('match')
->setDescription(pht('Search for namespaces by name substring.')),
);
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['match'] !== null) {
$query->withNameNgrams($map['match']);
}
return $query;
}
protected function getURI($path) {
return '/almanac/namespace/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'all' => pht('All Namespaces'),
);
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $namespaces,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($namespaces, 'AlmanacNamespace');
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
$list->setUser($viewer);
foreach ($namespaces as $namespace) {
$id = $namespace->getID();
$item = id(new PHUIObjectItemView())
->setObjectName(pht('Namespace %d', $id))
->setHeader($namespace->getName())
->setHref($this->getApplicationURI("namespace/{$id}/"))
->setObject($namespace);
$list->addItem($item);
}
$result = new PhabricatorApplicationSearchResultView();
$result->setObjectList($list);
$result->setNoDataString(pht('No Almanac namespaces found.'));
return $result;
}
}

View file

@ -0,0 +1,10 @@
<?php
final class AlmanacNamespaceTransactionQuery
extends PhabricatorApplicationTransactionQuery {
public function getTemplateApplicationTransaction() {
return new AlmanacNamespaceTransaction();
}
}

View file

@ -1,7 +1,7 @@
<?php
final class AlmanacNetworkQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
extends AlmanacQuery {
private $ids;
private $phids;
@ -16,41 +16,42 @@ final class AlmanacNetworkQuery
return $this;
}
protected function loadPage() {
$table = new AlmanacNetwork();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
public function newResultObject() {
return new AlmanacNetwork();
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
public function withNameNgrams($ngrams) {
return $this->withNgramsConstraint(
new AlmanacNetworkNameNgrams(),
$ngrams);
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$conn,
'network.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$conn,
'network.phid IN (%Ls)',
$this->phids);
}
$where[] = $this->buildPagingClause($conn_r);
return $where;
}
return $this->formatWhereClause($where);
protected function getPrimaryTableAlias() {
return 'network';
}
public function getQueryApplicationClass() {

View file

@ -11,22 +11,29 @@ final class AlmanacNetworkSearchEngine
return 'PhabricatorAlmanacApplication';
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
return $saved;
public function newQuery() {
return new AlmanacNetworkQuery();
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new AlmanacNetworkQuery());
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorSearchTextField())
->setLabel(pht('Name Contains'))
->setKey('match')
->setDescription(pht('Search for networks by name substring.')),
);
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['match'] !== null) {
$query->withNameNgrams($map['match']);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {}
protected function getURI($path) {
return '/almanac/network/'.$path;
}
@ -52,12 +59,6 @@ final class AlmanacNetworkSearchEngine
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $networks,
PhabricatorSavedQuery $query) {
return array();
}
protected function renderResultList(
array $networks,
PhabricatorSavedQuery $query,

View file

@ -5,8 +5,8 @@ final class AlmanacPropertyQuery
private $ids;
private $objectPHIDs;
private $objects;
private $names;
private $disablePolicyFilteringAndAttachment;
public function withIDs(array $ids) {
$this->ids = $ids;
@ -18,45 +18,46 @@ final class AlmanacPropertyQuery
return $this;
}
public function withObjects(array $objects) {
$this->objects = mpull($objects, null, 'getPHID');
$this->objectPHIDs = array_keys($this->objects);
return $this;
}
public function withNames(array $names) {
$this->names = $names;
return $this;
}
public function setDisablePolicyFilteringAndAttachment($disable) {
$this->disablePolicyFilteringAndAttachment = $disable;
return $this;
}
protected function shouldDisablePolicyFiltering() {
return $this->disablePolicyFilteringAndAttachment;
public function newResultObject() {
return new AlmanacProperty();
}
protected function loadPage() {
$table = new AlmanacProperty();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
return $this->loadStandardPage($this->newResultObject());
}
protected function willFilterPage(array $properties) {
if (!$this->disablePolicyFilteringAndAttachment) {
$object_phids = mpull($properties, 'getObjectPHID');
$object_phids = array_fuse($object_phids);
if ($this->objects !== null) {
$object_phids = array_diff_key($object_phids, $this->objects);
}
if ($object_phids) {
$objects = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->setParentQuery($this)
->withPHIDs($object_phids)
->execute();
$objects = mpull($objects, null, 'getPHID');
} else {
$objects = array();
}
$objects += $this->objects;
foreach ($properties as $key => $property) {
$object = idx($objects, $property->getObjectPHID());
@ -66,24 +67,23 @@ final class AlmanacPropertyQuery
}
$property->attachObject($object);
}
}
return $properties;
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->objectPHIDs !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'objectPHID IN (%Ls)',
$this->objectPHIDs);
}
@ -94,14 +94,12 @@ final class AlmanacPropertyQuery
$hashes[] = PhabricatorHash::digestForIndex($name);
}
$where[] = qsprintf(
$conn_r,
$conn,
'fieldIndex IN (%Ls)',
$hashes);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
return $where;
}
public function getQueryApplicationClass() {

View file

@ -3,31 +3,48 @@
abstract class AlmanacQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
protected function didFilterPage(array $objects) {
if (head($objects) instanceof AlmanacPropertyInterface) {
// NOTE: We load properties unconditionally because CustomField assumes
// it can always generate a list of fields on an object. It may make
// sense to re-examine that assumption eventually.
private $needProperties;
public function needProperties($need_properties) {
$this->needProperties = $need_properties;
return $this;
}
protected function getNeedProperties() {
return $this->needProperties;
}
protected function didFilterPage(array $objects) {
$has_properties = (head($objects) instanceof AlmanacPropertyInterface);
if ($has_properties && $this->needProperties) {
$property_query = id(new AlmanacPropertyQuery())
->setViewer($this->getViewer())
->setParentQuery($this)
->withObjectPHIDs(mpull($objects, 'getPHID'));
// NOTE: We disable policy filtering and object attachment to avoid
// a cyclic dependency where objects need their properties and properties
// need their objects. We'll attach the objects below, and have already
// implicitly checked the necessary policies.
$property_query->setDisablePolicyFilteringAndAttachment(true);
->withObjects($objects);
$properties = $property_query->execute();
$properties = mgroup($properties, 'getObjectPHID');
foreach ($objects as $object) {
$object_properties = idx($properties, $object->getPHID(), array());
$object_properties = mpull($object_properties, null, 'getFieldName');
// Create synthetic properties for defaults on the object itself.
$specs = $object->getAlmanacPropertyFieldSpecifications();
foreach ($specs as $key => $spec) {
if (empty($object_properties[$key])) {
$object_properties[$key] = id(new AlmanacProperty())
->setObjectPHID($object->getPHID())
->setFieldName($key)
->setFieldValue($spec->getValueForTransaction());
}
}
foreach ($object_properties as $property) {
$property->attachObject($object);
}
$object->attachAlmanacProperties($object_properties);
}
}

View file

@ -6,9 +6,8 @@ final class AlmanacServiceQuery
private $ids;
private $phids;
private $names;
private $serviceClasses;
private $serviceTypes;
private $devicePHIDs;
private $locked;
private $namePrefix;
private $nameSuffix;
@ -29,8 +28,8 @@ final class AlmanacServiceQuery
return $this;
}
public function withServiceClasses(array $classes) {
$this->serviceClasses = $classes;
public function withServiceTypes(array $types) {
$this->serviceTypes = $types;
return $this;
}
@ -39,11 +38,6 @@ final class AlmanacServiceQuery
return $this;
}
public function withLocked($locked) {
$this->locked = $locked;
return $this;
}
public function withNamePrefix($prefix) {
$this->namePrefix = $prefix;
return $this;
@ -54,19 +48,29 @@ final class AlmanacServiceQuery
return $this;
}
public function withNameNgrams($ngrams) {
return $this->withNgramsConstraint(
new AlmanacServiceNameNgrams(),
$ngrams);
}
public function needBindings($need_bindings) {
$this->needBindings = $need_bindings;
return $this;
}
public function newResultObject() {
return new AlmanacService();
}
protected function loadPage() {
return $this->loadStandardPage(new AlmanacService());
return $this->loadStandardPage($this->newResultObject());
}
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = parent::buildJoinClauseParts($conn);
if ($this->devicePHIDs !== null) {
if ($this->shouldJoinBindingTable()) {
$joins[] = qsprintf(
$conn,
'JOIN %T binding ON service.phid = binding.servicePHID',
@ -105,11 +109,11 @@ final class AlmanacServiceQuery
$hashes);
}
if ($this->serviceClasses !== null) {
if ($this->serviceTypes !== null) {
$where[] = qsprintf(
$conn,
'service.serviceClass IN (%Ls)',
$this->serviceClasses);
'service.serviceType IN (%Ls)',
$this->serviceTypes);
}
if ($this->devicePHIDs !== null) {
@ -119,13 +123,6 @@ final class AlmanacServiceQuery
$this->devicePHIDs);
}
if ($this->locked !== null) {
$where[] = qsprintf(
$conn,
'service.isLocked = %d',
(int)$this->locked);
}
if ($this->namePrefix !== null) {
$where[] = qsprintf(
$conn,
@ -144,17 +141,19 @@ final class AlmanacServiceQuery
}
protected function willFilterPage(array $services) {
$service_types = AlmanacServiceType::getAllServiceTypes();
$service_map = AlmanacServiceType::getAllServiceTypes();
foreach ($services as $key => $service) {
$service_class = $service->getServiceClass();
$service_type = idx($service_types, $service_class);
if (!$service_type) {
$implementation = idx($service_map, $service->getServiceType());
if (!$implementation) {
$this->didRejectResult($service);
unset($services[$key]);
continue;
}
$service->attachServiceType($service_type);
$implementation = clone $implementation;
$service->attachServiceImplementation($implementation);
}
return $services;
@ -166,6 +165,7 @@ final class AlmanacServiceQuery
$bindings = id(new AlmanacBindingQuery())
->setViewer($this->getViewer())
->withServicePHIDs($service_phids)
->needProperties($this->getNeedProperties())
->execute();
$bindings = mgroup($bindings, 'getServicePHID');
@ -178,6 +178,18 @@ final class AlmanacServiceQuery
return parent::didFilterPage($services);
}
private function shouldJoinBindingTable() {
return ($this->devicePHIDs !== null);
}
protected function shouldGroupQueryResultRows() {
if ($this->shouldJoinBindingTable()) {
return true;
}
return parent::shouldGroupQueryResultRows();
}
protected function getPrimaryTableAlias() {
return 'service';
}

View file

@ -16,21 +16,53 @@ final class AlmanacServiceSearchEngine
}
public function newResultObject() {
// NOTE: We need to attach a service type in order to generate custom
// field definitions.
return AlmanacService::initializeNewService()
->attachServiceType(new AlmanacCustomServiceType());
return new AlmanacService();
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['match'] !== null) {
$query->withNameNgrams($map['match']);
}
if ($map['names']) {
$query->withNames($map['names']);
}
if ($map['devicePHIDs']) {
$query->withDevicePHIDs($map['devicePHIDs']);
}
if ($map['serviceTypes']) {
$query->withServiceTypes($map['serviceTypes']);
}
return $query;
}
protected function buildCustomSearchFields() {
return array();
return array(
id(new PhabricatorSearchTextField())
->setLabel(pht('Name Contains'))
->setKey('match')
->setDescription(pht('Search for services by name substring.')),
id(new PhabricatorSearchStringListField())
->setLabel(pht('Exact Names'))
->setKey('names')
->setDescription(pht('Search for services with specific names.')),
id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Service Types'))
->setKey('serviceTypes')
->setDescription(pht('Find services by type.'))
->setDatasource(id(new AlmanacServiceTypeDatasource())),
id(new PhabricatorPHIDsSearchField())
->setLabel(pht('Devices'))
->setKey('devicePHIDs')
->setDescription(
pht('Search for services bound to particular devices.')),
);
}
protected function getURI($path) {
@ -75,17 +107,8 @@ final class AlmanacServiceSearchEngine
->setHref($service->getURI())
->setObject($service)
->addIcon(
$service->getServiceType()->getServiceTypeIcon(),
$service->getServiceType()->getServiceTypeShortName());
if ($service->getIsLocked() ||
$service->getServiceType()->isClusterServiceType()) {
if ($service->getIsLocked()) {
$item->addIcon('fa-lock', pht('Locked'));
} else {
$item->addIcon('fa-unlock-alt red', pht('Unlocked'));
}
}
$service->getServiceImplementation()->getServiceTypeIcon(),
$service->getServiceImplementation()->getServiceTypeShortName());
$list->addItem($item);
}

View file

@ -3,6 +3,8 @@
final class AlmanacClusterDatabaseServiceType
extends AlmanacClusterServiceType {
const SERVICETYPE = 'cluster.database';
public function getServiceTypeShortName() {
return pht('Cluster Database');
}
@ -16,8 +18,4 @@ final class AlmanacClusterDatabaseServiceType
'Defines a database service for use in a Phabricator cluster.');
}
public function getFieldSpecifications() {
return array();
}
}

View file

@ -3,6 +3,8 @@
final class AlmanacClusterRepositoryServiceType
extends AlmanacClusterServiceType {
const SERVICETYPE = 'cluster.repository';
public function getServiceTypeShortName() {
return pht('Cluster Repository');
}
@ -18,16 +20,7 @@ final class AlmanacClusterRepositoryServiceType
public function getFieldSpecifications() {
return array(
'closed' => array(
'type' => 'bool',
'name' => pht('Closed'),
'default' => false,
'strings' => array(
'edit.checkbox' => pht(
'Prevent new repositories from being allocated on this '.
'service.'),
),
),
'closed' => id(new PhabricatorTextEditField()),
);
}

View file

@ -11,28 +11,4 @@ abstract class AlmanacClusterServiceType
return 'fa-sitemap';
}
public function getStatusMessages(AlmanacService $service) {
$messages = parent::getStatusMessages($service);
if (!$service->getIsLocked()) {
$doc_href = PhabricatorEnv::getDoclink(
'User Guide: Phabricator Clusters');
$doc_link = phutil_tag(
'a',
array(
'href' => $doc_href,
'target' => '_blank',
),
pht('Learn More'));
$messages[] = pht(
'This is an unlocked cluster service. After you finish editing '.
'it, you should lock it. %s.',
$doc_link);
}
return $messages;
}
}

View file

@ -2,6 +2,8 @@
final class AlmanacCustomServiceType extends AlmanacServiceType {
const SERVICETYPE = 'almanac.custom';
public function getServiceTypeShortName() {
return pht('Custom');
}

View file

@ -2,6 +2,8 @@
final class AlmanacDrydockPoolServiceType extends AlmanacServiceType {
const SERVICETYPE = 'drydock.pool';
public function getServiceTypeShortName() {
return pht('Drydock Pool');
}

View file

@ -30,6 +30,11 @@ abstract class AlmanacServiceType extends Phobject {
abstract public function getServiceTypeDescription();
final public function getServiceTypeConstant() {
return $this->getPhobjectClassConstant('SERVICETYPE', 64);
}
public function getServiceTypeIcon() {
return 'fa-cog';
}
@ -38,7 +43,7 @@ abstract class AlmanacServiceType extends Phobject {
* Return `true` if this service type is a Phabricator cluster service type.
*
* These special services change the behavior of Phabricator, and require
* elevated permission to create.
* elevated permission to create and edit.
*
* @return bool True if this is a Phabricator cluster service type.
*/
@ -55,10 +60,6 @@ abstract class AlmanacServiceType extends Phobject {
return array();
}
public function getStatusMessages(AlmanacService $service) {
return array();
}
/**
* List all available service type implementations.
*
@ -67,6 +68,7 @@ abstract class AlmanacServiceType extends Phobject {
public static function getAllServiceTypes() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getServiceTypeConstant')
->setSortMethod('getServiceTypeName')
->execute();
}

View file

@ -4,26 +4,27 @@ final class AlmanacBinding
extends AlmanacDAO
implements
PhabricatorPolicyInterface,
PhabricatorCustomFieldInterface,
PhabricatorApplicationTransactionInterface,
AlmanacPropertyInterface,
PhabricatorDestructibleInterface {
PhabricatorDestructibleInterface,
PhabricatorExtendedPolicyInterface {
protected $servicePHID;
protected $devicePHID;
protected $interfacePHID;
protected $mailKey;
protected $isDisabled;
private $service = self::ATTACHABLE;
private $device = self::ATTACHABLE;
private $interface = self::ATTACHABLE;
private $customFields = self::ATTACHABLE;
private $almanacProperties = self::ATTACHABLE;
public static function initializeNewBinding(AlmanacService $service) {
return id(new AlmanacBinding())
->setServicePHID($service->getPHID())
->attachAlmanacProperties(array());
->attachAlmanacProperties(array())
->setIsDisabled(0);
}
protected function getConfiguration() {
@ -31,6 +32,7 @@ final class AlmanacBinding
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'mailKey' => 'bytes20',
'isDisabled' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
'key_service' => array(
@ -58,6 +60,10 @@ final class AlmanacBinding
return parent::save();
}
public function getName() {
return pht('Binding %s', $this->getID());
}
public function getURI() {
return '/almanac/binding/'.$this->getID().'/';
}
@ -124,6 +130,10 @@ final class AlmanacBinding
return array();
}
public function newAlmanacPropertyEditEngine() {
return new AlmanacBindingPropertyEditEngine();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
@ -151,38 +161,30 @@ final class AlmanacBinding
'interface.'),
);
if ($capability === PhabricatorPolicyCapability::CAN_EDIT) {
if ($this->getService()->getIsLocked()) {
$notes[] = pht(
'The service for this binding is locked, so it can not be edited.');
}
}
return $notes;
}
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
public function getCustomFieldSpecificationForRole($role) {
public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_EDIT:
if ($this->getService()->isClusterService()) {
return array(
array(
new PhabricatorAlmanacApplication(),
AlmanacManageClusterServicesCapability::CAPABILITY,
),
);
}
break;
}
return array();
}
public function getCustomFieldBaseClass() {
return 'AlmanacCustomField';
}
public function getCustomFields() {
return $this->assertAttached($this->customFields);
}
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
$this->customFields = $fields;
return $this;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */

View file

@ -1,9 +1,10 @@
<?php
final class AlmanacBindingTransaction
extends PhabricatorApplicationTransaction {
extends AlmanacTransaction {
const TYPE_INTERFACE = 'almanac:binding:interface';
const TYPE_DISABLE = 'almanac:binding:disable';
public function getApplicationName() {
return 'almanac';
@ -57,6 +58,17 @@ final class AlmanacBindingTransaction
$this->renderHandleLink($new));
}
break;
case self::TYPE_DISABLE:
if ($new) {
return pht(
'%s disabled this binding.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s enabled this binding.',
$this->renderHandleLink($author_phid));
}
break;
}
return parent::getTitle();

View file

@ -4,21 +4,22 @@ final class AlmanacDevice
extends AlmanacDAO
implements
PhabricatorPolicyInterface,
PhabricatorCustomFieldInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorProjectInterface,
PhabricatorSSHPublicKeyInterface,
AlmanacPropertyInterface,
PhabricatorDestructibleInterface {
PhabricatorDestructibleInterface,
PhabricatorNgramsInterface,
PhabricatorConduitResultInterface,
PhabricatorExtendedPolicyInterface {
protected $name;
protected $nameIndex;
protected $mailKey;
protected $viewPolicy;
protected $editPolicy;
protected $isLocked;
protected $isBoundToClusterService;
private $customFields = self::ATTACHABLE;
private $almanacProperties = self::ATTACHABLE;
public static function initializeNewDevice() {
@ -26,7 +27,7 @@ final class AlmanacDevice
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
->attachAlmanacProperties(array())
->setIsLocked(0);
->setIsBoundToClusterService(0);
}
protected function getConfiguration() {
@ -36,7 +37,7 @@ final class AlmanacDevice
'name' => 'text128',
'nameIndex' => 'bytes12',
'mailKey' => 'bytes20',
'isLocked' => 'bool',
'isBoundToClusterService' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
'key_name' => array(
@ -55,7 +56,7 @@ final class AlmanacDevice
}
public function save() {
AlmanacNames::validateServiceOrDeviceName($this->getName());
AlmanacNames::validateName($this->getName());
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
@ -70,30 +71,28 @@ final class AlmanacDevice
return '/almanac/device/view/'.$this->getName().'/';
}
/**
* Find locked services which are bound to this device, updating the device
* lock flag if necessary.
*
* @return list<phid> List of locking service PHIDs.
*/
public function rebuildDeviceLocks() {
public function rebuildClusterBindingStatus() {
$services = id(new AlmanacServiceQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withDevicePHIDs(array($this->getPHID()))
->withLocked(true)
->execute();
$locked = (bool)count($services);
$is_cluster = false;
foreach ($services as $service) {
if ($service->isClusterService()) {
$is_cluster = true;
break;
}
}
if ($locked != $this->getIsLocked()) {
$this->setIsLocked((int)$locked);
if ($is_cluster != $this->getIsBoundToClusterService()) {
$this->setIsBoundToClusterService((int)$is_cluster);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
queryfx(
$this->establishConnection('w'),
'UPDATE %T SET isLocked = %d WHERE id = %d',
'UPDATE %T SET isBoundToClusterService = %d WHERE id = %d',
$this->getTableName(),
$this->getIsLocked(),
$this->getIsBoundToClusterService(),
$this->getID());
unset($unguarded);
}
@ -101,6 +100,10 @@ final class AlmanacDevice
return $this;
}
public function isClusterDevice() {
return $this->getIsBoundToClusterService();
}
/* -( AlmanacPropertyInterface )------------------------------------------- */
@ -136,6 +139,10 @@ final class AlmanacDevice
return array();
}
public function newAlmanacPropertyEditEngine() {
return new AlmanacDevicePropertyEditEngine();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
@ -152,51 +159,39 @@ final class AlmanacDevice
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
if ($this->getIsLocked()) {
return PhabricatorPolicies::POLICY_NOONE;
} else {
return $this->getEditPolicy();
}
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
public function describeAutomaticCapability($capability) {
if ($capability === PhabricatorPolicyCapability::CAN_EDIT) {
if ($this->getIsLocked()) {
return pht(
'This device is bound to a locked service, so it can not '.
'be edited.');
}
}
return null;
}
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
public function getCustomFieldSpecificationForRole($role) {
public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_EDIT:
if ($this->isClusterDevice()) {
return array(
array(
new PhabricatorAlmanacApplication(),
AlmanacManageClusterServicesCapability::CAPABILITY,
),
);
}
break;
}
return array();
}
public function getCustomFieldBaseClass() {
return 'AlmanacCustomField';
}
public function getCustomFields() {
return $this->assertAttached($this->customFields);
}
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
$this->customFields = $fields;
return $this;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
@ -250,4 +245,41 @@ final class AlmanacDevice
$this->delete();
}
/* -( PhabricatorNgramInterface )------------------------------------------ */
public function newNgrams() {
return array(
id(new AlmanacDeviceNameNgrams())
->setValue($this->getName()),
);
}
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('name')
->setType('string')
->setDescription(pht('The name of the device.')),
);
}
public function getFieldValuesForConduit() {
return array(
'name' => $this->getName(),
);
}
public function getConduitSearchAttachments() {
return array(
id(new AlmanacPropertiesSearchEngineAttachment())
->setAttachmentKey('properties'),
);
}
}

View file

@ -0,0 +1,18 @@
<?php
final class AlmanacDeviceNameNgrams
extends PhabricatorSearchNgrams {
public function getNgramKey() {
return 'devicename';
}
public function getColumnName() {
return 'name';
}
public function getApplicationName() {
return 'almanac';
}
}

View file

@ -1,7 +1,7 @@
<?php
final class AlmanacDeviceTransaction
extends PhabricatorApplicationTransaction {
extends AlmanacTransaction {
const TYPE_NAME = 'almanac:device:name';
const TYPE_INTERFACE = 'almanac:device:interface';
@ -69,7 +69,7 @@ final class AlmanacDeviceTransaction
return pht(
'%s removed the interface %s from this device.',
$this->renderHandleLink($author_phid),
$this->describeInterface($new));
$this->describeInterface($old));
} else if ($new) {
return pht(
'%s added the interface %s to this device.',

View file

@ -4,7 +4,8 @@ final class AlmanacInterface
extends AlmanacDAO
implements
PhabricatorPolicyInterface,
PhabricatorDestructibleInterface {
PhabricatorDestructibleInterface,
PhabricatorExtendedPolicyInterface {
protected $devicePHID;
protected $networkPHID;
@ -74,6 +75,16 @@ final class AlmanacInterface
return $this->getAddress().':'.$this->getPort();
}
public function loadIsInUse() {
$binding = id(new AlmanacBindingQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withInterfacePHIDs(array($this->getPHID()))
->setLimit(1)
->executeOne();
return (bool)$binding;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
@ -101,14 +112,28 @@ final class AlmanacInterface
'view the interface.'),
);
if ($capability === PhabricatorPolicyCapability::CAN_EDIT) {
if ($this->getDevice()->getIsLocked()) {
$notes[] = pht(
'The device for this interface is locked, so it can not be edited.');
}
return $notes;
}
return $notes;
/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_EDIT:
if ($this->getDevice()->isClusterDevice()) {
return array(
array(
new PhabricatorAlmanacApplication(),
AlmanacManageClusterServicesCapability::CAPABILITY,
),
);
}
break;
}
return array();
}

View file

@ -0,0 +1,223 @@
<?php
final class AlmanacNamespace
extends AlmanacDAO
implements
PhabricatorPolicyInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorProjectInterface,
AlmanacPropertyInterface,
PhabricatorDestructibleInterface,
PhabricatorNgramsInterface {
protected $name;
protected $nameIndex;
protected $mailKey;
protected $viewPolicy;
protected $editPolicy;
private $almanacProperties = self::ATTACHABLE;
public static function initializeNewNamespace() {
return id(new self())
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
->attachAlmanacProperties(array());
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'text128',
'nameIndex' => 'bytes12',
'mailKey' => 'bytes20',
),
self::CONFIG_KEY_SCHEMA => array(
'key_nameindex' => array(
'columns' => array('nameIndex'),
'unique' => true,
),
'key_name' => array(
'columns' => array('name'),
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
AlmanacNamespacePHIDType::TYPECONST);
}
public function save() {
AlmanacNames::validateName($this->getName());
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
if (!$this->mailKey) {
$this->mailKey = Filesystem::readRandomCharacters(20);
}
return parent::save();
}
public function getURI() {
return '/almanac/namespace/view/'.$this->getName().'/';
}
public function getNameLength() {
return strlen($this->getName());
}
/**
* Load the namespace which prevents use of an Almanac name, if one exists.
*/
public static function loadRestrictedNamespace(
PhabricatorUser $viewer,
$name) {
// For a name like "x.y.z", produce a list of controlling namespaces like
// ("z", "y.x", "x.y.z").
$names = array();
$parts = explode('.', $name);
for ($ii = 0; $ii < count($parts); $ii++) {
$names[] = implode('.', array_slice($parts, -($ii + 1)));
}
// Load all the possible controlling namespaces.
$namespaces = id(new AlmanacNamespaceQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withNames($names)
->execute();
if (!$namespaces) {
return null;
}
// Find the "nearest" (longest) namespace that exists. If both
// "sub.domain.com" and "domain.com" exist, we only care about the policy
// on the former.
$namespaces = msort($namespaces, 'getNameLength');
$namespace = last($namespaces);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$namespace,
PhabricatorPolicyCapability::CAN_EDIT);
if ($can_edit) {
return null;
}
return $namespace;
}
/* -( AlmanacPropertyInterface )------------------------------------------- */
public function attachAlmanacProperties(array $properties) {
assert_instances_of($properties, 'AlmanacProperty');
$this->almanacProperties = mpull($properties, null, 'getFieldName');
return $this;
}
public function getAlmanacProperties() {
return $this->assertAttached($this->almanacProperties);
}
public function hasAlmanacProperty($key) {
$this->assertAttached($this->almanacProperties);
return isset($this->almanacProperties[$key]);
}
public function getAlmanacProperty($key) {
return $this->assertAttachedKey($this->almanacProperties, $key);
}
public function getAlmanacPropertyValue($key, $default = null) {
if ($this->hasAlmanacProperty($key)) {
return $this->getAlmanacProperty($key)->getFieldValue();
} else {
return $default;
}
}
public function getAlmanacPropertyFieldSpecifications() {
return array();
}
public function newAlmanacPropertyEditEngine() {
throw new PhutilMethodNotImplementedException();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
public function describeAutomaticCapability($capability) {
return null;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new AlmanacNamespaceEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new AlmanacNamespaceTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->delete();
}
/* -( PhabricatorNgramInterface )------------------------------------------ */
public function newNgrams() {
return array(
id(new AlmanacNamespaceNameNgrams())
->setValue($this->getName()),
);
}
}

View file

@ -0,0 +1,18 @@
<?php
final class AlmanacNamespaceNameNgrams
extends PhabricatorSearchNgrams {
public function getNgramKey() {
return 'namespacename';
}
public function getColumnName() {
return 'name';
}
public function getApplicationName() {
return 'almanac';
}
}

View file

@ -0,0 +1,43 @@
<?php
final class AlmanacNamespaceTransaction
extends PhabricatorApplicationTransaction {
const TYPE_NAME = 'almanac:namespace:name';
public function getApplicationName() {
return 'almanac';
}
public function getApplicationTransactionType() {
return AlmanacNamespacePHIDType::TYPECONST;
}
public function getApplicationTransactionCommentObject() {
return null;
}
public function getTitle() {
$author_phid = $this->getAuthorPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case PhabricatorTransactions::TYPE_CREATE:
return pht(
'%s created this namespace.',
$this->renderHandleLink($author_phid));
break;
case self::TYPE_NAME:
return pht(
'%s renamed this namespace from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
return parent::getTitle();
}
}

View file

@ -5,7 +5,8 @@ final class AlmanacNetwork
implements
PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface,
PhabricatorDestructibleInterface {
PhabricatorDestructibleInterface,
PhabricatorNgramsInterface {
protected $name;
protected $mailKey;
@ -114,4 +115,15 @@ final class AlmanacNetwork
$this->delete();
}
/* -( PhabricatorNgramInterface )------------------------------------------ */
public function newNgrams() {
return array(
id(new AlmanacNetworkNameNgrams())
->setValue($this->getName()),
);
}
}

View file

@ -0,0 +1,18 @@
<?php
final class AlmanacNetworkNameNgrams
extends PhabricatorSearchNgrams {
public function getNgramKey() {
return 'networkname';
}
public function getColumnName() {
return 'name';
}
public function getApplicationName() {
return 'almanac';
}
}

View file

@ -24,20 +24,17 @@ final class AlmanacNetworkTransaction
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_NAME:
if ($old === null) {
case PhabricatorTransactions::TYPE_CREATE:
return pht(
'%s created this network.',
$this->renderHandleLink($author_phid));
} else {
case self::TYPE_NAME:
return pht(
'%s renamed this network from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
break;
}
return parent::getTitle();
}

View file

@ -1,25 +1,33 @@
<?php
final class AlmanacProperty
extends PhabricatorCustomFieldStorage
extends AlmanacDAO
implements PhabricatorPolicyInterface {
protected $objectPHID;
protected $fieldIndex;
protected $fieldName;
protected $fieldValue;
private $object = self::ATTACHABLE;
public function getApplicationName() {
return 'almanac';
}
protected function getConfiguration() {
$config = parent::getConfiguration();
$config[self::CONFIG_COLUMN_SCHEMA] += array(
return array(
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_SERIALIZATION => array(
'fieldValue' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'fieldIndex' => 'bytes12',
'fieldName' => 'text128',
);
return $config;
),
self::CONFIG_KEY_SCHEMA => array(
'objectPHID' => array(
'columns' => array('objectPHID', 'fieldIndex'),
'unique' => true,
),
),
) + parent::getConfiguration();
}
public function getObject() {
@ -31,39 +39,53 @@ final class AlmanacProperty
return $this;
}
public static function buildTransactions(
public static function newPropertyUpdateTransactions(
AlmanacPropertyInterface $object,
array $properties,
$only_builtins = false) {
$template = $object->getApplicationTransactionTemplate();
$builtins = $object->getAlmanacPropertyFieldSpecifications();
$xactions = array();
foreach ($properties as $name => $property) {
if ($only_builtins && empty($builtins[$name])) {
continue;
}
$xactions[] = id(clone $template)
->setTransactionType(AlmanacTransaction::TYPE_PROPERTY_UPDATE)
->setMetadataValue('almanac.property', $name)
->setNewValue($property);
}
return $xactions;
}
public static function newPropertyRemoveTransactions(
AlmanacPropertyInterface $object,
array $properties) {
$template = $object->getApplicationTransactionTemplate();
$attached_properties = $object->getAlmanacProperties();
foreach ($properties as $key => $value) {
if (empty($attached_properties[$key])) {
$attached_properties[] = id(new AlmanacProperty())
->setObjectPHID($object->getPHID())
->setFieldName($key);
}
}
$object->attachAlmanacProperties($attached_properties);
$field_list = PhabricatorCustomField::getObjectFields(
$object,
PhabricatorCustomField::ROLE_DEFAULT);
$fields = $field_list->getFields();
$xactions = array();
foreach ($properties as $name => $property) {
foreach ($properties as $property) {
$xactions[] = id(clone $template)
->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD)
->setMetadataValue('customfield:key', $name)
->setOldValue($object->getAlmanacPropertyValue($name))
->setNewValue($property);
->setTransactionType(AlmanacTransaction::TYPE_PROPERTY_REMOVE)
->setMetadataValue('almanac.property', $property)
->setNewValue(null);
}
return $xactions;
}
public function save() {
$hash = PhabricatorHash::digestForIndex($this->getFieldName());
$this->setFieldIndex($hash);
return parent::save();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */

View file

@ -4,31 +4,42 @@ final class AlmanacService
extends AlmanacDAO
implements
PhabricatorPolicyInterface,
PhabricatorCustomFieldInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorProjectInterface,
AlmanacPropertyInterface,
PhabricatorDestructibleInterface {
PhabricatorDestructibleInterface,
PhabricatorNgramsInterface,
PhabricatorConduitResultInterface,
PhabricatorExtendedPolicyInterface {
protected $name;
protected $nameIndex;
protected $mailKey;
protected $viewPolicy;
protected $editPolicy;
protected $serviceClass;
protected $isLocked;
protected $serviceType;
private $customFields = self::ATTACHABLE;
private $almanacProperties = self::ATTACHABLE;
private $bindings = self::ATTACHABLE;
private $serviceType = self::ATTACHABLE;
private $serviceImplementation = self::ATTACHABLE;
public static function initializeNewService($type) {
$type_map = AlmanacServiceType::getAllServiceTypes();
$implementation = idx($type_map, $type);
if (!$implementation) {
throw new Exception(
pht(
'No Almanac service type "%s" exists!',
$type));
}
public static function initializeNewService() {
return id(new AlmanacService())
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
->attachAlmanacProperties(array())
->setIsLocked(0);
->setServiceType($type)
->attachServiceImplementation($implementation);
}
protected function getConfiguration() {
@ -38,8 +49,7 @@ final class AlmanacService
'name' => 'text128',
'nameIndex' => 'bytes12',
'mailKey' => 'bytes20',
'serviceClass' => 'text64',
'isLocked' => 'bool',
'serviceType' => 'text64',
),
self::CONFIG_KEY_SCHEMA => array(
'key_name' => array(
@ -49,8 +59,8 @@ final class AlmanacService
'key_nametext' => array(
'columns' => array('name'),
),
'key_class' => array(
'columns' => array('serviceClass'),
'key_servicetype' => array(
'columns' => array('serviceType'),
),
),
) + parent::getConfiguration();
@ -61,7 +71,7 @@ final class AlmanacService
}
public function save() {
AlmanacNames::validateServiceOrDeviceName($this->getName());
AlmanacNames::validateName($this->getName());
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
@ -80,20 +90,37 @@ final class AlmanacService
return $this->assertAttached($this->bindings);
}
public function getActiveBindings() {
$bindings = $this->getBindings();
// Filter out disabled bindings.
foreach ($bindings as $key => $binding) {
if ($binding->getIsDisabled()) {
unset($bindings[$key]);
}
}
return $bindings;
}
public function attachBindings(array $bindings) {
$this->bindings = $bindings;
return $this;
}
public function getServiceType() {
return $this->assertAttached($this->serviceType);
public function getServiceImplementation() {
return $this->assertAttached($this->serviceImplementation);
}
public function attachServiceType(AlmanacServiceType $type) {
$this->serviceType = $type;
public function attachServiceImplementation(AlmanacServiceType $type) {
$this->serviceImplementation = $type;
return $this;
}
public function isClusterService() {
return $this->getServiceImplementation()->isClusterServiceType();
}
/* -( AlmanacPropertyInterface )------------------------------------------- */
@ -126,7 +153,11 @@ final class AlmanacService
}
public function getAlmanacPropertyFieldSpecifications() {
return $this->getServiceType()->getFieldSpecifications();
return $this->getServiceImplementation()->getFieldSpecifications();
}
public function newAlmanacPropertyEditEngine() {
return new AlmanacServicePropertyEditEngine();
}
@ -145,51 +176,39 @@ final class AlmanacService
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
if ($this->getIsLocked()) {
return PhabricatorPolicies::POLICY_NOONE;
} else {
return $this->getEditPolicy();
}
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
public function describeAutomaticCapability($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_EDIT:
if ($this->getIsLocked()) {
return pht('This service is locked and can not be edited.');
}
break;
}
return null;
}
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
public function getCustomFieldSpecificationForRole($role) {
public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_EDIT:
if ($this->isClusterService()) {
return array(
array(
new PhabricatorAlmanacApplication(),
AlmanacManageClusterServicesCapability::CAPABILITY,
),
);
}
break;
}
return array();
}
public function getCustomFieldBaseClass() {
return 'AlmanacCustomField';
}
public function getCustomFields() {
return $this->assertAttached($this->customFields);
}
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
$this->customFields = $fields;
return $this;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
@ -231,4 +250,48 @@ final class AlmanacService
$this->delete();
}
/* -( PhabricatorNgramInterface )------------------------------------------ */
public function newNgrams() {
return array(
id(new AlmanacServiceNameNgrams())
->setValue($this->getName()),
);
}
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('name')
->setType('string')
->setDescription(pht('The name of the service.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('serviceType')
->setType('string')
->setDescription(pht('The service type constant.')),
);
}
public function getFieldValuesForConduit() {
return array(
'name' => $this->getName(),
'serviceType' => $this->getServiceType(),
);
}
public function getConduitSearchAttachments() {
return array(
id(new AlmanacPropertiesSearchEngineAttachment())
->setAttachmentKey('properties'),
id(new AlmanacBindingsSearchEngineAttachment())
->setAttachmentKey('bindings'),
);
}
}

View file

@ -0,0 +1,18 @@
<?php
final class AlmanacServiceNameNgrams
extends PhabricatorSearchNgrams {
public function getNgramKey() {
return 'servicename';
}
public function getColumnName() {
return 'name';
}
public function getApplicationName() {
return 'almanac';
}
}

Some files were not shown because too many files have changed in this diff Show more