mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 23:02:42 +01:00
Allow Almanac service types to define default properties
Summary: Ref T5833. This allows Almanac ServiceTypes to define default properties for a service, which show up in the UI and are more easily editable. Overall, this makes it much easier to make structured/usable/consistent service records: you can check a checkbox that says "prevent new allocations" instead of needing to know the meaning of a key. Test Plan: {F251593} Reviewers: chad, btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T5833 Differential Revision: https://secure.phabricator.com/D10996
This commit is contained in:
parent
c85327ca3e
commit
3fa519da74
19 changed files with 361 additions and 77 deletions
|
@ -34,6 +34,7 @@ return array(
|
||||||
'rsrc/css/aphront/transaction.css' => '5d0cae25',
|
'rsrc/css/aphront/transaction.css' => '5d0cae25',
|
||||||
'rsrc/css/aphront/two-column.css' => '16ab3ad2',
|
'rsrc/css/aphront/two-column.css' => '16ab3ad2',
|
||||||
'rsrc/css/aphront/typeahead.css' => 'a989b5b3',
|
'rsrc/css/aphront/typeahead.css' => 'a989b5b3',
|
||||||
|
'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af',
|
||||||
'rsrc/css/application/auth/auth.css' => '1e655982',
|
'rsrc/css/application/auth/auth.css' => '1e655982',
|
||||||
'rsrc/css/application/base/main-menu-view.css' => '33e5f2f6',
|
'rsrc/css/application/base/main-menu-view.css' => '33e5f2f6',
|
||||||
'rsrc/css/application/base/notification-menu.css' => '6aa0a74b',
|
'rsrc/css/application/base/notification-menu.css' => '6aa0a74b',
|
||||||
|
@ -497,6 +498,7 @@ return array(
|
||||||
'rsrc/swf/aphlict.swf' => 'f19daffb',
|
'rsrc/swf/aphlict.swf' => 'f19daffb',
|
||||||
),
|
),
|
||||||
'symbols' => array(
|
'symbols' => array(
|
||||||
|
'almanac-css' => 'dbb9b3af',
|
||||||
'aphront-bars' => '231ac33c',
|
'aphront-bars' => '231ac33c',
|
||||||
'aphront-contextbar-view-css' => '1c3b0529',
|
'aphront-contextbar-view-css' => '1c3b0529',
|
||||||
'aphront-dark-console-css' => '6378ef3d',
|
'aphront-dark-console-css' => '6378ef3d',
|
||||||
|
|
|
@ -67,6 +67,7 @@ phutil_register_library_map(array(
|
||||||
'AlmanacNetworkViewController' => 'applications/almanac/controller/AlmanacNetworkViewController.php',
|
'AlmanacNetworkViewController' => 'applications/almanac/controller/AlmanacNetworkViewController.php',
|
||||||
'AlmanacProperty' => 'applications/almanac/storage/AlmanacProperty.php',
|
'AlmanacProperty' => 'applications/almanac/storage/AlmanacProperty.php',
|
||||||
'AlmanacPropertyController' => 'applications/almanac/controller/AlmanacPropertyController.php',
|
'AlmanacPropertyController' => 'applications/almanac/controller/AlmanacPropertyController.php',
|
||||||
|
'AlmanacPropertyDeleteController' => 'applications/almanac/controller/AlmanacPropertyDeleteController.php',
|
||||||
'AlmanacPropertyEditController' => 'applications/almanac/controller/AlmanacPropertyEditController.php',
|
'AlmanacPropertyEditController' => 'applications/almanac/controller/AlmanacPropertyEditController.php',
|
||||||
'AlmanacPropertyInterface' => 'applications/almanac/property/AlmanacPropertyInterface.php',
|
'AlmanacPropertyInterface' => 'applications/almanac/property/AlmanacPropertyInterface.php',
|
||||||
'AlmanacPropertyQuery' => 'applications/almanac/query/AlmanacPropertyQuery.php',
|
'AlmanacPropertyQuery' => 'applications/almanac/query/AlmanacPropertyQuery.php',
|
||||||
|
@ -3092,6 +3093,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
),
|
),
|
||||||
'AlmanacPropertyController' => 'AlmanacController',
|
'AlmanacPropertyController' => 'AlmanacController',
|
||||||
|
'AlmanacPropertyDeleteController' => 'AlmanacDeviceController',
|
||||||
'AlmanacPropertyEditController' => 'AlmanacDeviceController',
|
'AlmanacPropertyEditController' => 'AlmanacDeviceController',
|
||||||
'AlmanacPropertyQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'AlmanacPropertyQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'AlmanacQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'AlmanacQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
|
|
|
@ -57,7 +57,8 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
|
||||||
'(?P<id>\d+)/' => 'AlmanacNetworkViewController',
|
'(?P<id>\d+)/' => 'AlmanacNetworkViewController',
|
||||||
),
|
),
|
||||||
'property/' => array(
|
'property/' => array(
|
||||||
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacPropertyEditController',
|
'edit/' => 'AlmanacPropertyEditController',
|
||||||
|
'delete/' => 'AlmanacPropertyDeleteController',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,13 +9,129 @@ abstract class AlmanacController
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
$properties = $object->getAlmanacProperties();
|
$properties = $object->getAlmanacProperties();
|
||||||
|
|
||||||
|
$this->requireResource('almanac-css');
|
||||||
|
|
||||||
|
$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());
|
||||||
|
|
||||||
|
$icon_builtin = id(new PHUIIconView())
|
||||||
|
->setIconFont('fa-circle')
|
||||||
|
->addSigil('has-tooltip')
|
||||||
|
->setMetadata(
|
||||||
|
array(
|
||||||
|
'tip' => pht('Builtin Property'),
|
||||||
|
'align' => 'E',
|
||||||
|
));
|
||||||
|
|
||||||
|
$icon_custom = id(new PHUIIconView())
|
||||||
|
->setIconFont('fa-circle-o grey')
|
||||||
|
->addSigil('has-tooltip')
|
||||||
|
->setMetadata(
|
||||||
|
array(
|
||||||
|
'tip' => pht('Custom Property'),
|
||||||
|
'align' => 'E',
|
||||||
|
));
|
||||||
|
|
||||||
|
$builtins = $object->getAlmanacPropertyFieldSpecifications();
|
||||||
|
|
||||||
|
// Sort fields so builtin fields appear first, then fields are ordered
|
||||||
|
// alphabetically.
|
||||||
|
$fields = $field_list->getFields();
|
||||||
|
$fields = msort($fields, 'getFieldKey');
|
||||||
|
|
||||||
|
$head = array();
|
||||||
|
$tail = array();
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
$key = $field->getFieldKey();
|
||||||
|
if (isset($builtins[$key])) {
|
||||||
|
$head[$key] = $field;
|
||||||
|
} else {
|
||||||
|
$tail[$key] = $field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = $head + $tail;
|
||||||
|
|
||||||
$rows = array();
|
$rows = array();
|
||||||
foreach ($properties as $property) {
|
foreach ($fields as $key => $field) {
|
||||||
$value = $property->getFieldValue();
|
$value = $field->getValueForStorage();
|
||||||
|
|
||||||
|
$is_builtin = isset($builtins[$key]);
|
||||||
|
|
||||||
|
$delete_uri = $this->getApplicationURI('property/delete/');
|
||||||
|
$delete_uri = id(new PhutilURI($delete_uri))
|
||||||
|
->setQueryParams(
|
||||||
|
array(
|
||||||
|
'objectPHID' => $object->getPHID(),
|
||||||
|
'key' => $key,
|
||||||
|
));
|
||||||
|
|
||||||
|
$edit_uri = $this->getApplicationURI('property/edit/');
|
||||||
|
$edit_uri = id(new PhutilURI($edit_uri))
|
||||||
|
->setQueryParams(
|
||||||
|
array(
|
||||||
|
'objectPHID' => $object->getPHID(),
|
||||||
|
'key' => $key,
|
||||||
|
));
|
||||||
|
|
||||||
|
$delete = javelin_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'class' => ($can_edit
|
||||||
|
? 'button grey small'
|
||||||
|
: 'button grey small disabled'),
|
||||||
|
'sigil' => 'workflow',
|
||||||
|
'href' => $delete_uri,
|
||||||
|
),
|
||||||
|
$is_builtin ? pht('Reset') : pht('Delete'));
|
||||||
|
|
||||||
|
$default = idx($defaults, $key);
|
||||||
|
$is_default = ($default !== null && $default === $value);
|
||||||
|
|
||||||
|
$display_value = PhabricatorConfigJSON::prettyPrintJSON($value);
|
||||||
|
if ($is_default) {
|
||||||
|
$display_value = phutil_tag(
|
||||||
|
'span',
|
||||||
|
array(
|
||||||
|
'class' => 'almanac-default-property-value',
|
||||||
|
),
|
||||||
|
$display_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$display_key = $key;
|
||||||
|
if ($can_edit) {
|
||||||
|
$display_key = javelin_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => $edit_uri,
|
||||||
|
'sigil' => 'workflow',
|
||||||
|
),
|
||||||
|
$display_key);
|
||||||
|
}
|
||||||
|
|
||||||
$rows[] = array(
|
$rows[] = array(
|
||||||
$property->getFieldName(),
|
($is_builtin ? $icon_builtin : $icon_custom),
|
||||||
PhabricatorConfigJSON::prettyPrintJSON($value),
|
$display_key,
|
||||||
|
$display_value,
|
||||||
|
$delete,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,13 +139,17 @@ abstract class AlmanacController
|
||||||
->setNoDataString(pht('No properties.'))
|
->setNoDataString(pht('No properties.'))
|
||||||
->setHeaders(
|
->setHeaders(
|
||||||
array(
|
array(
|
||||||
|
null,
|
||||||
pht('Name'),
|
pht('Name'),
|
||||||
pht('Value'),
|
pht('Value'),
|
||||||
|
null,
|
||||||
))
|
))
|
||||||
->setColumnClasses(
|
->setColumnClasses(
|
||||||
array(
|
array(
|
||||||
|
null,
|
||||||
null,
|
null,
|
||||||
'wide',
|
'wide',
|
||||||
|
'action',
|
||||||
));
|
));
|
||||||
|
|
||||||
$phid = $object->getPHID();
|
$phid = $object->getPHID();
|
||||||
|
|
|
@ -11,7 +11,6 @@ final class AlmanacNetworkViewController
|
||||||
$viewer = $request->getViewer();
|
$viewer = $request->getViewer();
|
||||||
|
|
||||||
$id = $request->getURIData('id');
|
$id = $request->getURIData('id');
|
||||||
|
|
||||||
$network = id(new AlmanacNetworkQuery())
|
$network = id(new AlmanacNetworkQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withIDs(array($id))
|
->withIDs(array($id))
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class AlmanacPropertyDeleteController
|
||||||
|
extends AlmanacDeviceController {
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!($object instanceof AlmanacPropertyInterface)) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = $request->getStr('key');
|
||||||
|
if (!strlen($key)) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$cancel_uri = $object->getURI();
|
||||||
|
|
||||||
|
$builtins = $object->getAlmanacPropertyFieldSpecifications();
|
||||||
|
$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');
|
||||||
|
} 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');
|
||||||
|
}
|
||||||
|
|
||||||
|
$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.
|
||||||
|
|
||||||
|
$editor = $object->getApplicationTransactionEditor()
|
||||||
|
->setActor($viewer)
|
||||||
|
->setContentSourceFromRequest($request)
|
||||||
|
->setContinueOnNoEffect(true)
|
||||||
|
->setContinueOnMissingFields(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$editor->applyTransactions($object, array($xaction));
|
||||||
|
return id(new AphrontRedirectResponse())->setURI($cancel_uri);
|
||||||
|
} catch (PhabricatorApplicationTransactionValidationException $ex) {
|
||||||
|
$validation_exception = $ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->newDialog()
|
||||||
|
->setTitle($title)
|
||||||
|
->setValidationException($validation_exception)
|
||||||
|
->addHiddenInput('objectPHID', $object->getPHID())
|
||||||
|
->addHiddenInput('key', $key)
|
||||||
|
->appendParagraph($body)
|
||||||
|
->addCancelButton($cancel_uri)
|
||||||
|
->addSubmitButton($submit_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,27 +6,6 @@ final class AlmanacPropertyEditController
|
||||||
public function handleRequest(AphrontRequest $request) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$viewer = $request->getViewer();
|
$viewer = $request->getViewer();
|
||||||
|
|
||||||
$id = $request->getURIData('id');
|
|
||||||
if ($id) {
|
|
||||||
$property = id(new AlmanacPropertyQuery())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->withIDs(array($id))
|
|
||||||
->requireCapabilities(
|
|
||||||
array(
|
|
||||||
PhabricatorPolicyCapability::CAN_VIEW,
|
|
||||||
PhabricatorPolicyCapability::CAN_EDIT,
|
|
||||||
))
|
|
||||||
->executeOne();
|
|
||||||
if (!$property) {
|
|
||||||
return new Aphront404Response();
|
|
||||||
}
|
|
||||||
|
|
||||||
$object = $property->getObject();
|
|
||||||
|
|
||||||
$is_new = false;
|
|
||||||
$title = pht('Edit Property');
|
|
||||||
$save_button = pht('Save Changes');
|
|
||||||
} else {
|
|
||||||
$object = id(new PhabricatorObjectQuery())
|
$object = id(new PhabricatorObjectQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withPHIDs(array($request->getStr('objectPHID')))
|
->withPHIDs(array($request->getStr('objectPHID')))
|
||||||
|
@ -40,17 +19,27 @@ final class AlmanacPropertyEditController
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
$is_new = true;
|
|
||||||
$title = pht('Add Property');
|
|
||||||
$save_button = pht('Add Property');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!($object instanceof AlmanacPropertyInterface)) {
|
if (!($object instanceof AlmanacPropertyInterface)) {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
$cancel_uri = $object->getURI();
|
$cancel_uri = $object->getURI();
|
||||||
|
|
||||||
|
$key = $request->getStr('key');
|
||||||
|
if ($key) {
|
||||||
|
$property_key = $key;
|
||||||
|
|
||||||
|
$is_new = false;
|
||||||
|
$title = pht('Edit Property');
|
||||||
|
$save_button = pht('Save Changes');
|
||||||
|
} else {
|
||||||
|
$property_key = null;
|
||||||
|
|
||||||
|
$is_new = true;
|
||||||
|
$title = pht('Add Property');
|
||||||
|
$save_button = pht('Add Property');
|
||||||
|
}
|
||||||
|
|
||||||
if ($is_new) {
|
if ($is_new) {
|
||||||
$errors = array();
|
$errors = array();
|
||||||
$property = null;
|
$property = null;
|
||||||
|
@ -77,25 +66,11 @@ final class AlmanacPropertyEditController
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$errors) {
|
if (!$errors) {
|
||||||
$property = id(new AlmanacPropertyQuery())
|
$property_key = $name;
|
||||||
->setViewer($viewer)
|
|
||||||
->withObjectPHIDs(array($object->getPHID()))
|
|
||||||
->withNames(array($name))
|
|
||||||
->requireCapabilities(
|
|
||||||
array(
|
|
||||||
PhabricatorPolicyCapability::CAN_VIEW,
|
|
||||||
PhabricatorPolicyCapability::CAN_EDIT,
|
|
||||||
))
|
|
||||||
->executeOne();
|
|
||||||
if (!$property) {
|
|
||||||
$property = id(new AlmanacProperty())
|
|
||||||
->setObjectPHID($object->getPHID())
|
|
||||||
->setFieldName($name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$property) {
|
if ($property_key === null) {
|
||||||
$form = id(new AphrontFormView())
|
$form = id(new AphrontFormView())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->appendChild(
|
->appendChild(
|
||||||
|
@ -115,17 +90,29 @@ final class AlmanacPropertyEditController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$v_name = $property->getFieldName();
|
// Make sure property key is appropriate.
|
||||||
$e_name = true;
|
// TODO: It would be cleaner to put this safety check in the Editor.
|
||||||
|
AlmanacNames::validateServiceOrDeviceName($property_key);
|
||||||
|
|
||||||
$v_value = $property->getFieldValue();
|
// If we're adding a new property, put a placeholder on the object so
|
||||||
$e_value = null;
|
// 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($property));
|
$object->attachAlmanacProperties(array($temporary_property));
|
||||||
|
}
|
||||||
|
|
||||||
$field_list = PhabricatorCustomField::getObjectFields(
|
$field_list = PhabricatorCustomField::getObjectFields(
|
||||||
$object,
|
$object,
|
||||||
PhabricatorCustomField::ROLE_EDIT);
|
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
|
$field_list
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->readFieldsFromStorage($object);
|
->readFieldsFromStorage($object);
|
||||||
|
@ -153,7 +140,8 @@ final class AlmanacPropertyEditController
|
||||||
$form = id(new AphrontFormView())
|
$form = id(new AphrontFormView())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->addHiddenInput('objectPHID', $request->getStr('objectPHID'))
|
->addHiddenInput('objectPHID', $request->getStr('objectPHID'))
|
||||||
->addHiddenInput('name', $request->getStr('name'))
|
->addHiddenInput('key', $request->getStr('key'))
|
||||||
|
->addHiddenInput('name', $property_key)
|
||||||
->addHiddenInput('isValueEdit', true);
|
->addHiddenInput('isValueEdit', true);
|
||||||
|
|
||||||
$field_list->appendFieldsToForm($form);
|
$field_list->appendFieldsToForm($form);
|
||||||
|
|
|
@ -8,17 +8,29 @@ final class AlmanacCoreCustomField
|
||||||
return 'almanac:core';
|
return 'almanac:core';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createFields($object) {
|
public function getFieldKey() {
|
||||||
$specs = array();
|
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) {
|
foreach ($object->getAlmanacProperties() as $property) {
|
||||||
$specs[$property->getFieldName()] = array(
|
$default_specs[$property->getFieldName()] = array(
|
||||||
'name' => $property->getFieldName(),
|
'name' => $property->getFieldName(),
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PhabricatorStandardCustomField::buildStandardFields($this, $specs);
|
return PhabricatorStandardCustomField::buildStandardFields(
|
||||||
|
$this,
|
||||||
|
$specs + $default_specs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function shouldUseStorage() {
|
public function shouldUseStorage() {
|
||||||
|
@ -26,9 +38,12 @@ final class AlmanacCoreCustomField
|
||||||
}
|
}
|
||||||
|
|
||||||
public function readValueFromObject(PhabricatorCustomFieldInterface $object) {
|
public function readValueFromObject(PhabricatorCustomFieldInterface $object) {
|
||||||
$key = $this->getProxy()->getRawStandardFieldKey();
|
$key = $this->getFieldKey();
|
||||||
|
|
||||||
|
if ($object->hasAlmanacProperty($key)) {
|
||||||
$this->setValueFromStorage($object->getAlmanacPropertyValue($key));
|
$this->setValueFromStorage($object->getAlmanacPropertyValue($key));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function applyApplicationTransactionInternalEffects(
|
public function applyApplicationTransactionInternalEffects(
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
@ -40,7 +55,7 @@ final class AlmanacCoreCustomField
|
||||||
|
|
||||||
$object = $this->getObject();
|
$object = $this->getObject();
|
||||||
$phid = $object->getPHID();
|
$phid = $object->getPHID();
|
||||||
$key = $this->getProxy()->getRawStandardFieldKey();
|
$key = $this->getFieldKey();
|
||||||
|
|
||||||
$property = id(new AlmanacPropertyQuery())
|
$property = id(new AlmanacPropertyQuery())
|
||||||
->setViewer($this->getViewer())
|
->setViewer($this->getViewer())
|
||||||
|
|
|
@ -7,5 +7,6 @@ interface AlmanacPropertyInterface {
|
||||||
public function hasAlmanacProperty($key);
|
public function hasAlmanacProperty($key);
|
||||||
public function getAlmanacProperty($key);
|
public function getAlmanacProperty($key);
|
||||||
public function getAlmanacPropertyValue($key, $default = null);
|
public function getAlmanacPropertyValue($key, $default = null);
|
||||||
|
public function getAlmanacPropertyFieldSpecifications();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ final class AlmanacPropertyQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
public function withObjectPHIDs(array $phids) {
|
public function withObjectPHIDs(array $phids) {
|
||||||
$this->phids = $phids;
|
$this->objectPHIDs = $phids;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ abstract class AlmanacQuery
|
||||||
$property_query = id(new AlmanacPropertyQuery())
|
$property_query = id(new AlmanacPropertyQuery())
|
||||||
->setViewer($this->getViewer())
|
->setViewer($this->getViewer())
|
||||||
->setParentQuery($this)
|
->setParentQuery($this)
|
||||||
->withObjectPHIDs(mpull($objects, null, 'getPHID'));
|
->withObjectPHIDs(mpull($objects, 'getPHID'));
|
||||||
|
|
||||||
// NOTE: We disable policy filtering and object attachment to avoid
|
// NOTE: We disable policy filtering and object attachment to avoid
|
||||||
// a cyclic dependency where objects need their properties and properties
|
// a cyclic dependency where objects need their properties and properties
|
||||||
|
@ -21,6 +21,7 @@ abstract class AlmanacQuery
|
||||||
$property_query->setDisablePolicyFilteringAndAttachment(true);
|
$property_query->setDisablePolicyFilteringAndAttachment(true);
|
||||||
|
|
||||||
$properties = $property_query->execute();
|
$properties = $property_query->execute();
|
||||||
|
|
||||||
$properties = mgroup($properties, 'getObjectPHID');
|
$properties = mgroup($properties, 'getObjectPHID');
|
||||||
foreach ($objects as $object) {
|
foreach ($objects as $object) {
|
||||||
$object_properties = idx($properties, $object->getPHID(), array());
|
$object_properties = idx($properties, $object->getPHID(), array());
|
||||||
|
|
|
@ -16,4 +16,19 @@ final class AlmanacClusterRepositoryServiceType
|
||||||
'Defines a repository service for use in a Phabricator cluster.');
|
'Defines a repository service for use in a Phabricator cluster.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,14 @@ abstract class AlmanacServiceType extends Phobject {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getDefaultPropertyMap() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFieldSpecifications() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List all available service type implementations.
|
* List all available service type implementations.
|
||||||
*
|
*
|
||||||
|
|
|
@ -119,6 +119,10 @@ final class AlmanacBinding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAlmanacPropertyFieldSpecifications() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,10 @@ final class AlmanacDevice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAlmanacPropertyFieldSpecifications() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ final class AlmanacNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getURI() {
|
public function getURI() {
|
||||||
return '/almanac/network/view/'.$this->getName().'/';
|
return '/almanac/network/'.$this->getID().'/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,10 @@ final class AlmanacService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAlmanacPropertyFieldSpecifications() {
|
||||||
|
return $this->getServiceType()->getFieldSpecifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,13 @@ final class PhabricatorStandardCustomFieldBool
|
||||||
return $this->newNumericIndex(0);
|
return $this->newNumericIndex(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function readValueFromRequest(AphrontRequest $request) {
|
||||||
|
$this->setFieldValue((bool)$request->getBool($this->getFieldKey()));
|
||||||
|
}
|
||||||
|
|
||||||
public function getValueForStorage() {
|
public function getValueForStorage() {
|
||||||
$value = $this->getFieldValue();
|
$value = $this->getFieldValue();
|
||||||
if (strlen($value)) {
|
if ($value !== null) {
|
||||||
return (int)$value;
|
return (int)$value;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
|
7
webroot/rsrc/css/application/almanac/almanac.css
Normal file
7
webroot/rsrc/css/application/almanac/almanac.css
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
/**
|
||||||
|
* @provides almanac-css
|
||||||
|
*/
|
||||||
|
|
||||||
|
.almanac-default-property-value {
|
||||||
|
color: {$lightgreytext};
|
||||||
|
}
|
Loading…
Reference in a new issue