mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-27 01:02:42 +01:00
Flesh out ApplicationTransactions/CustomField integration
Summary: None of this code is reachable yet. See discussion in D6147. Ref T1703. Provide tighter integration between ApplicationTransactions and CustomField. Basically, I'm just trying to get all the shared stuff into the base implementation. Test Plan: Code not reachable. Reviewers: chad, seporaitis Reviewed By: chad CC: aran Maniphest Tasks: T1703 Differential Revision: https://secure.phabricator.com/D6149
This commit is contained in:
parent
b7c584137f
commit
524c2acb3d
8 changed files with 463 additions and 15 deletions
|
@ -921,6 +921,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorCustomFieldDataNotAvailableException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldDataNotAvailableException.php',
|
'PhabricatorCustomFieldDataNotAvailableException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldDataNotAvailableException.php',
|
||||||
'PhabricatorCustomFieldImplementationIncompleteException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldImplementationIncompleteException.php',
|
'PhabricatorCustomFieldImplementationIncompleteException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldImplementationIncompleteException.php',
|
||||||
'PhabricatorCustomFieldIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldIndexStorage.php',
|
'PhabricatorCustomFieldIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldIndexStorage.php',
|
||||||
|
'PhabricatorCustomFieldInterface' => 'infrastructure/customfield/interface/PhabricatorCustomFieldInterface.php',
|
||||||
|
'PhabricatorCustomFieldNotAttachedException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldNotAttachedException.php',
|
||||||
'PhabricatorCustomFieldNumericIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldNumericIndexStorage.php',
|
'PhabricatorCustomFieldNumericIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldNumericIndexStorage.php',
|
||||||
'PhabricatorCustomFieldStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php',
|
'PhabricatorCustomFieldStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php',
|
||||||
'PhabricatorCustomFieldStringIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStringIndexStorage.php',
|
'PhabricatorCustomFieldStringIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStringIndexStorage.php',
|
||||||
|
@ -1541,7 +1543,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorUnitsTestCase' => 'view/__tests__/PhabricatorUnitsTestCase.php',
|
'PhabricatorUnitsTestCase' => 'view/__tests__/PhabricatorUnitsTestCase.php',
|
||||||
'PhabricatorUser' => 'applications/people/storage/PhabricatorUser.php',
|
'PhabricatorUser' => 'applications/people/storage/PhabricatorUser.php',
|
||||||
'PhabricatorUserDAO' => 'applications/people/storage/PhabricatorUserDAO.php',
|
'PhabricatorUserDAO' => 'applications/people/storage/PhabricatorUserDAO.php',
|
||||||
'PhabricatorUserEditor' => 'applications/people/PhabricatorUserEditor.php',
|
'PhabricatorUserEditor' => 'applications/people/editor/PhabricatorUserEditor.php',
|
||||||
'PhabricatorUserEmail' => 'applications/people/storage/PhabricatorUserEmail.php',
|
'PhabricatorUserEmail' => 'applications/people/storage/PhabricatorUserEmail.php',
|
||||||
'PhabricatorUserLDAPInfo' => 'applications/people/storage/PhabricatorUserLDAPInfo.php',
|
'PhabricatorUserLDAPInfo' => 'applications/people/storage/PhabricatorUserLDAPInfo.php',
|
||||||
'PhabricatorUserLog' => 'applications/people/storage/PhabricatorUserLog.php',
|
'PhabricatorUserLog' => 'applications/people/storage/PhabricatorUserLog.php',
|
||||||
|
@ -1554,6 +1556,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorUserStatusInvalidEpochException' => 'applications/people/exception/PhabricatorUserStatusInvalidEpochException.php',
|
'PhabricatorUserStatusInvalidEpochException' => 'applications/people/exception/PhabricatorUserStatusInvalidEpochException.php',
|
||||||
'PhabricatorUserStatusOverlapException' => 'applications/people/exception/PhabricatorUserStatusOverlapException.php',
|
'PhabricatorUserStatusOverlapException' => 'applications/people/exception/PhabricatorUserStatusOverlapException.php',
|
||||||
'PhabricatorUserTestCase' => 'applications/people/storage/__tests__/PhabricatorUserTestCase.php',
|
'PhabricatorUserTestCase' => 'applications/people/storage/__tests__/PhabricatorUserTestCase.php',
|
||||||
|
'PhabricatorUserTransaction' => 'applications/people/storage/PhabricatorUserTransaction.php',
|
||||||
'PhabricatorWorkboardExample' => 'applications/uiexample/examples/PhabricatorWorkboardExample.php',
|
'PhabricatorWorkboardExample' => 'applications/uiexample/examples/PhabricatorWorkboardExample.php',
|
||||||
'PhabricatorWorkboardView' => 'view/layout/PhabricatorWorkboardView.php',
|
'PhabricatorWorkboardView' => 'view/layout/PhabricatorWorkboardView.php',
|
||||||
'PhabricatorWorker' => 'infrastructure/daemon/workers/PhabricatorWorker.php',
|
'PhabricatorWorker' => 'infrastructure/daemon/workers/PhabricatorWorker.php',
|
||||||
|
@ -2764,6 +2767,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorCustomFieldDataNotAvailableException' => 'Exception',
|
'PhabricatorCustomFieldDataNotAvailableException' => 'Exception',
|
||||||
'PhabricatorCustomFieldImplementationIncompleteException' => 'Exception',
|
'PhabricatorCustomFieldImplementationIncompleteException' => 'Exception',
|
||||||
'PhabricatorCustomFieldIndexStorage' => 'PhabricatorLiskDAO',
|
'PhabricatorCustomFieldIndexStorage' => 'PhabricatorLiskDAO',
|
||||||
|
'PhabricatorCustomFieldNotAttachedException' => 'Exception',
|
||||||
'PhabricatorCustomFieldNumericIndexStorage' => 'PhabricatorCustomFieldIndexStorage',
|
'PhabricatorCustomFieldNumericIndexStorage' => 'PhabricatorCustomFieldIndexStorage',
|
||||||
'PhabricatorCustomFieldStorage' => 'PhabricatorLiskDAO',
|
'PhabricatorCustomFieldStorage' => 'PhabricatorLiskDAO',
|
||||||
'PhabricatorCustomFieldStringIndexStorage' => 'PhabricatorCustomFieldIndexStorage',
|
'PhabricatorCustomFieldStringIndexStorage' => 'PhabricatorCustomFieldIndexStorage',
|
||||||
|
@ -3385,6 +3389,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorUserStatusInvalidEpochException' => 'Exception',
|
'PhabricatorUserStatusInvalidEpochException' => 'Exception',
|
||||||
'PhabricatorUserStatusOverlapException' => 'Exception',
|
'PhabricatorUserStatusOverlapException' => 'Exception',
|
||||||
'PhabricatorUserTestCase' => 'PhabricatorTestCase',
|
'PhabricatorUserTestCase' => 'PhabricatorTestCase',
|
||||||
|
'PhabricatorUserTransaction' => 'PhabricatorApplicationTransaction',
|
||||||
'PhabricatorWorkboardExample' => 'PhabricatorUIExample',
|
'PhabricatorWorkboardExample' => 'PhabricatorUIExample',
|
||||||
'PhabricatorWorkboardView' => 'AphrontView',
|
'PhabricatorWorkboardView' => 'AphrontView',
|
||||||
'PhabricatorWorkerActiveTask' => 'PhabricatorWorkerTask',
|
'PhabricatorWorkerActiveTask' => 'PhabricatorWorkerTask',
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorUserTransaction
|
||||||
|
extends PhabricatorApplicationTransaction {
|
||||||
|
|
||||||
|
public function getApplicationName() {
|
||||||
|
return 'user';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplicationTransactionType() {
|
||||||
|
return PhabricatorPHIDConstants::PHID_TYPE_USER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplicationTransactionCommentObject() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplicationObjectTypeName() {
|
||||||
|
return pht('user');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ final class PhabricatorTransactions {
|
||||||
const TYPE_VIEW_POLICY = 'core:view-policy';
|
const TYPE_VIEW_POLICY = 'core:view-policy';
|
||||||
const TYPE_EDIT_POLICY = 'core:edit-policy';
|
const TYPE_EDIT_POLICY = 'core:edit-policy';
|
||||||
const TYPE_EDGE = 'core:edge';
|
const TYPE_EDGE = 'core:edge';
|
||||||
|
const TYPE_CUSTOMFIELD = 'core:customfield';
|
||||||
|
|
||||||
const COLOR_RED = 'red';
|
const COLOR_RED = 'red';
|
||||||
const COLOR_ORANGE = 'orange';
|
const COLOR_ORANGE = 'orange';
|
||||||
|
|
|
@ -78,6 +78,10 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
$types[] = PhabricatorTransactions::TYPE_SUBSCRIBERS;
|
$types[] = PhabricatorTransactions::TYPE_SUBSCRIBERS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->object instanceof PhabricatorCustomFieldInterface) {
|
||||||
|
$types[] = PhabricatorTransactions::TYPE_CUSTOMFIELD;
|
||||||
|
}
|
||||||
|
|
||||||
return $types;
|
return $types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,6 +125,9 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
$old_edges = $old_edges[$edge_src][$edge_type];
|
$old_edges = $old_edges[$edge_src][$edge_type];
|
||||||
}
|
}
|
||||||
return $old_edges;
|
return $old_edges;
|
||||||
|
case PhabricatorTransactions::TYPE_CUSTOMFIELD:
|
||||||
|
$field = $this->getCustomFieldForTransaction($object, $xaction);
|
||||||
|
return $field->getOldValueForApplicationTransactions();
|
||||||
default:
|
default:
|
||||||
return $this->getCustomTransactionOldValue($object, $xaction);
|
return $this->getCustomTransactionOldValue($object, $xaction);
|
||||||
}
|
}
|
||||||
|
@ -137,6 +144,9 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
return $xaction->getNewValue();
|
return $xaction->getNewValue();
|
||||||
case PhabricatorTransactions::TYPE_EDGE:
|
case PhabricatorTransactions::TYPE_EDGE:
|
||||||
return $this->getEdgeTransactionNewValue($xaction);
|
return $this->getEdgeTransactionNewValue($xaction);
|
||||||
|
case PhabricatorTransactions::TYPE_CUSTOMFIELD:
|
||||||
|
$field = $this->getCustomFieldForTransaction($object, $xaction);
|
||||||
|
return $field->getNewValueForApplicationTransactions();
|
||||||
default:
|
default:
|
||||||
return $this->getCustomTransactionNewValue($object, $xaction);
|
return $this->getCustomTransactionNewValue($object, $xaction);
|
||||||
}
|
}
|
||||||
|
@ -161,6 +171,9 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
case PhabricatorTransactions::TYPE_COMMENT:
|
case PhabricatorTransactions::TYPE_COMMENT:
|
||||||
return $xaction->hasComment();
|
return $xaction->hasComment();
|
||||||
|
case PhabricatorTransactions::TYPE_CUSTOMFIELD:
|
||||||
|
$field = $this->getCustomFieldForTransaction($object, $xaction);
|
||||||
|
return $field->getApplicationTransactionHasEffect($xaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ($xaction->getOldValue() !== $xaction->getNewValue());
|
return ($xaction->getOldValue() !== $xaction->getNewValue());
|
||||||
|
@ -176,6 +189,9 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||||
$object->setEditPolicy($xaction->getNewValue());
|
$object->setEditPolicy($xaction->getNewValue());
|
||||||
break;
|
break;
|
||||||
|
case PhabricatorTransactions::TYPE_CUSTOMFIELD:
|
||||||
|
$field = $this->getCustomFieldForTransaction($object, $xaction);
|
||||||
|
return $field->applyApplicationTransactionInternalEffects($xaction);
|
||||||
}
|
}
|
||||||
return $this->applyCustomInternalTransaction($object, $xaction);
|
return $this->applyCustomInternalTransaction($object, $xaction);
|
||||||
}
|
}
|
||||||
|
@ -240,6 +256,9 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
|
|
||||||
$editor->save();
|
$editor->save();
|
||||||
break;
|
break;
|
||||||
|
case PhabricatorTransactions::TYPE_CUSTOMFIELD:
|
||||||
|
$field = $this->getCustomFieldForTransaction($object, $xaction);
|
||||||
|
return $field->applyApplicationTransactionExternalEffects($xaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->applyCustomExternalTransaction($object, $xaction);
|
return $this->applyCustomExternalTransaction($object, $xaction);
|
||||||
|
@ -1244,4 +1263,41 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Custom Fields )------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task customfield
|
||||||
|
*/
|
||||||
|
private function getCustomFieldForTransaction(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
|
||||||
|
$field_key = $xaction->getMetadataValue('customfield:key');
|
||||||
|
if (!$field_key) {
|
||||||
|
throw new Exception(
|
||||||
|
"Custom field transaction has no 'customfield:key'!");
|
||||||
|
}
|
||||||
|
|
||||||
|
$field = PhabricatorCustomField::getObjectField(
|
||||||
|
$object,
|
||||||
|
PhabricatorCustomField::ROLE_APPLICATIONTRANSACTIONS,
|
||||||
|
$field_key);
|
||||||
|
|
||||||
|
if (!$field) {
|
||||||
|
throw new Exception(
|
||||||
|
"Custom field transaction has invalid 'customfield:key'; field ".
|
||||||
|
"'{$field_key}' is disabled or does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$field->shouldAppearInApplicationTransactions()) {
|
||||||
|
throw new Exception(
|
||||||
|
"Custom field transaction '{$field_key}' does not implement ".
|
||||||
|
"integration for ApplicationTransactions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $field;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorCustomFieldNotAttachedException
|
||||||
|
extends Exception {
|
||||||
|
|
||||||
|
}
|
|
@ -1,35 +1,100 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @task apps Building Applications with Custom Fields
|
||||||
|
* @task core Core Properties and Field Identity
|
||||||
* @task context Contextual Data
|
* @task context Contextual Data
|
||||||
* @task storage Field Storage
|
* @task storage Field Storage
|
||||||
|
* @task appsearch Integration with ApplicationSearch
|
||||||
|
* @task appxaction Integration with ApplicationTransactions
|
||||||
*/
|
*/
|
||||||
abstract class PhabricatorCustomField {
|
abstract class PhabricatorCustomField {
|
||||||
|
|
||||||
private $viewer;
|
private $viewer;
|
||||||
|
private $object;
|
||||||
|
|
||||||
abstract public function getFieldKey();
|
const ROLE_APPLICATIONTRANSACTIONS = 'ApplicationTransactions';
|
||||||
|
const ROLE_APPLICAITONSEARCH = 'ApplicationSearch';
|
||||||
|
const ROLE_STORAGE = 'storage';
|
||||||
|
const ROLE_DEFAULT = 'default';
|
||||||
|
|
||||||
public function getFieldIndex() {
|
|
||||||
return PhabricatorHash::digestForIndex($this->getFieldKey());
|
/* -( Building Applications with Custom Fields )--------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task apps
|
||||||
|
*/
|
||||||
|
public static function raiseUnattachedException(
|
||||||
|
PhabricatorCustomFieldInterface $object,
|
||||||
|
$role) {
|
||||||
|
throw new PhabricatorCustomFieldNotAttachedException(
|
||||||
|
"Call attachCustomFields() before getCustomFields()!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFieldName() {
|
|
||||||
return $this->getFieldKey();
|
/**
|
||||||
|
* @task apps
|
||||||
|
*/
|
||||||
|
public static function getObjectFields(
|
||||||
|
PhabricatorCustomFieldInterface $object,
|
||||||
|
$role) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
$fields = $object->getCustomFields($role);
|
||||||
|
} catch (PhabricatorCustomFieldNotAttachedException $ex) {
|
||||||
|
$base_class = $object->getCustomFieldBaseClass();
|
||||||
|
|
||||||
|
if (!($base_class instanceof PhabricatorCustomField)) {
|
||||||
|
$obj_class = get_class($object);
|
||||||
|
throw new Exception(
|
||||||
|
"Object (of class '{$obj_class}') returned '{$base_class}' as its ".
|
||||||
|
"getCustomFieldBaseClass(), but this is not a recognized subclass ".
|
||||||
|
"of PhabricatorCustomField.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createFields() {
|
$spec = $object->getCustomFieldSpecificationForRole($role);
|
||||||
return array($this);
|
if (!is_array($spec)) {
|
||||||
|
$obj_class = get_class($object);
|
||||||
|
throw new Exception(
|
||||||
|
"Expected an array from getCustomFieldSpecificationForRole() for ".
|
||||||
|
"object of class '{$obj_class}'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isFieldEnabled() {
|
$fields = PhabricatorCustomField::buildFieldList($base_class, $spec);
|
||||||
return true;
|
|
||||||
|
foreach ($fields as $key => $field) {
|
||||||
|
if (!$field->shouldEnableForRole($role)) {
|
||||||
|
unset($fields[$key]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canDisableField() {
|
foreach ($fields as $field) {
|
||||||
return true;
|
$field->setObject($object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$object->attachCustomFields($role, $fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task apps
|
||||||
|
*/
|
||||||
|
public static function getObjectField(
|
||||||
|
PhabricatorCustomFieldInterface $object,
|
||||||
|
$role,
|
||||||
|
$field_key) {
|
||||||
|
|
||||||
|
return idx(self::getObjectFields($object, $role), $field_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task apps
|
||||||
|
*/
|
||||||
public static function buildFieldList($base_class, array $spec) {
|
public static function buildFieldList($base_class, array $spec) {
|
||||||
$this_class = __CLASS__;
|
$this_class = __CLASS__;
|
||||||
if (!($base_class instanceof $this_class)) {
|
if (!($base_class instanceof $this_class)) {
|
||||||
|
@ -81,9 +146,153 @@ abstract class PhabricatorCustomField {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Core Properties and Field Identity )--------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a key which uniquely identifies this field, like
|
||||||
|
* "mycompany.dinosaur.count". Normally you should provide some level of
|
||||||
|
* namespacing to prevent collisions.
|
||||||
|
*
|
||||||
|
* @return string String which uniquely identifies this field.
|
||||||
|
* @task core
|
||||||
|
*/
|
||||||
|
abstract public function getFieldKey();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a human-readable field name.
|
||||||
|
*
|
||||||
|
* @return string Human readable field name.
|
||||||
|
* @task core
|
||||||
|
*/
|
||||||
|
public function getFieldName() {
|
||||||
|
return $this->getFieldKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a short, human-readable description of the field's behavior. This
|
||||||
|
* provides more context to administrators when they are customizing fields.
|
||||||
|
*
|
||||||
|
* @return string|null Optional human-readable description.
|
||||||
|
* @task core
|
||||||
|
*/
|
||||||
|
public function getFieldDescription() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Most field implementations are unique, in that one class corresponds to
|
||||||
|
* one field. However, some field implementations are general and a single
|
||||||
|
* implementation may drive several fields.
|
||||||
|
*
|
||||||
|
* For general implementations, the general field implementation can return
|
||||||
|
* multiple field instances here.
|
||||||
|
*
|
||||||
|
* @return list<PhabricatorCustomField> List of fields.
|
||||||
|
* @task core
|
||||||
|
*/
|
||||||
|
public function createFields() {
|
||||||
|
return array($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You can return `false` here if the field should not be enabled for any
|
||||||
|
* role. For example, it might depend on something (like an application or
|
||||||
|
* library) which isn't installed, or might have some global configuration
|
||||||
|
* which allows it to be disabled.
|
||||||
|
*
|
||||||
|
* @return bool False to completely disable this field for all roles.
|
||||||
|
* @task core
|
||||||
|
*/
|
||||||
|
public function isFieldEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Low level selector for field availability. Fields can appear in different
|
||||||
|
* roles (like an edit view, a list view, etc.), but not every field needs
|
||||||
|
* to appear everywhere. Fields that are disabled in a role won't appear in
|
||||||
|
* that context within applications.
|
||||||
|
*
|
||||||
|
* Normally, you do not need to override this method. Instead, override the
|
||||||
|
* methods specific to roles you want to enable. For example, implement
|
||||||
|
* @{method:getStorageKey()} to activate the `'storage'` role.
|
||||||
|
*
|
||||||
|
* @return bool True to enable the field for the given role.
|
||||||
|
* @task core
|
||||||
|
*/
|
||||||
|
public function shouldEnableForRole($role) {
|
||||||
|
switch ($role) {
|
||||||
|
case self::ROLE_APPLICATIONTRANSACTIONS:
|
||||||
|
return $this->shouldAppearInApplicationTransactions();
|
||||||
|
case self::ROLE_APPLICATIONSEARCH:
|
||||||
|
return $this->shouldAppearInApplicationSearch();
|
||||||
|
case self::ROLE_STORAGE:
|
||||||
|
return ($this->getStorageKey() !== null);
|
||||||
|
case self::ROLE_DEFAULT:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
throw new Exception("Unknown field role '{$role}'!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow administrators to disable this field. Most fields should allow this,
|
||||||
|
* but some are fundamental to the behavior of the application and can be
|
||||||
|
* locked down to avoid chaos, disorder, and the decline of civilization.
|
||||||
|
*
|
||||||
|
* @return bool False to prevent this field from being disabled through
|
||||||
|
* configuration.
|
||||||
|
* @task core
|
||||||
|
*/
|
||||||
|
public function canDisableField() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an index string which uniquely identifies this field.
|
||||||
|
*
|
||||||
|
* @return string Index string which uniquely identifies this field.
|
||||||
|
* @task core
|
||||||
|
*/
|
||||||
|
final public function getFieldIndex() {
|
||||||
|
return PhabricatorHash::digestForIndex($this->getFieldKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Contextual Data )---------------------------------------------------- */
|
/* -( Contextual Data )---------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the object this field belongs to.
|
||||||
|
*
|
||||||
|
* @param PhabricatorCustomFieldInterface The object this field belongs to.
|
||||||
|
* @task context
|
||||||
|
*/
|
||||||
|
final public function setObject(PhabricatorCustomFieldInterface $object) {
|
||||||
|
$this->object = $object;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the object this field belongs to.
|
||||||
|
*
|
||||||
|
* @return PhabricatorCustomFieldInterface The object this field belongs to.
|
||||||
|
* @task context
|
||||||
|
*/
|
||||||
|
final public function getObject() {
|
||||||
|
return $this->object;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @task context
|
* @task context
|
||||||
*/
|
*/
|
||||||
|
@ -143,6 +352,19 @@ abstract class PhabricatorCustomField {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new, empty storage object. This should be a subclass of
|
||||||
|
* @{class:PhabricatorCustomFieldStorage} which is bound to the application's
|
||||||
|
* database.
|
||||||
|
*
|
||||||
|
* @return PhabricatorCustomFieldStorage New empty storage object.
|
||||||
|
* @task storage
|
||||||
|
*/
|
||||||
|
public function getStorageObject() {
|
||||||
|
throw new PhabricatorCustomFieldImplementationIncompleteException($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a serialized representation of the field value, appropriate for
|
* Return a serialized representation of the field value, appropriate for
|
||||||
* storing in auxiliary field storage. You must implement this method if
|
* storing in auxiliary field storage. You must implement this method if
|
||||||
|
@ -277,4 +499,98 @@ abstract class PhabricatorCustomField {
|
||||||
->setIndexValue($value);
|
->setIndexValue($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( ApplicationTransactions )-------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appearing in ApplicationTrasactions allows a field to be edited using
|
||||||
|
* standard workflows.
|
||||||
|
*
|
||||||
|
* @return bool True to appear in ApplicationTransactions.
|
||||||
|
* @task appxaction
|
||||||
|
*/
|
||||||
|
public function shouldAppearInApplicationTransactions() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task appxaction
|
||||||
|
*/
|
||||||
|
public function getOldValueForApplicationTransactions() {
|
||||||
|
return $this->getValueForStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task appxaction
|
||||||
|
*/
|
||||||
|
public function setValueFromApplicationTransactions($value) {
|
||||||
|
return $this->setValueFromStorage($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task appxaction
|
||||||
|
*/
|
||||||
|
public function getNewValueForApplicationTransactions(
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
return $xaction->getNewValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task appxaction
|
||||||
|
*/
|
||||||
|
public function getApplicationTransactionHasEffect(
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
return ($xaction->getOldValue() !== $xaction->getNewValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task appxaction
|
||||||
|
*/
|
||||||
|
public function applyApplicationTransactionInternalEffects(
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task appxaction
|
||||||
|
*/
|
||||||
|
public function applyApplicationTransactionExternalEffects(
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
if (!$this->shouldEnableForRole(self::ROLE_STORAGE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setValueFromApplicationTransaction($xaction->getNewValue());
|
||||||
|
$value = $this->getValueForStorage();
|
||||||
|
|
||||||
|
$table = $this->newStorageObject();
|
||||||
|
$conn_w = $table->establishConnection('w');
|
||||||
|
|
||||||
|
if ($value === null) {
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'DELETE FROM %T WHERE objectPHID = %s AND fieldIndex = %s',
|
||||||
|
$this->getObject()->getPHID(),
|
||||||
|
$this->getFieldIndex());
|
||||||
|
} else {
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'INSERT INTO %T (objectPHID, fieldIndex, fieldValue)
|
||||||
|
VALUES (%s, %s, %s)
|
||||||
|
ON DUPLICATE KEY UPDATE fieldValue = VALUES(fieldValue)',
|
||||||
|
$this->getObject()->getPHID(),
|
||||||
|
$this->getFieldIndex(),
|
||||||
|
$value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
interface PhabricatorCustomFieldInterface {
|
||||||
|
|
||||||
|
public function getCustomFieldBaseClass();
|
||||||
|
public function getCustomFieldSpecificationForRole($role);
|
||||||
|
public function getCustomFields($role);
|
||||||
|
public function attachCustomFields($role, array $fields);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TEMPLATE IMPLEMENTATION /////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
|
||||||
|
/*
|
||||||
|
|
||||||
|
private $customFields = array();
|
||||||
|
|
||||||
|
public function getCustomFieldSpecificationForRole($role) {
|
||||||
|
return PhabricatorEnv::getEnvConfig(<<<'application.fields'>>>);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCustomFieldBaseClass() {
|
||||||
|
return <<<<'YourApplicationHereCustomField'>>>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCustomFields($role) {
|
||||||
|
if (idx($this->customFields, $role) === null) {
|
||||||
|
PhabricatorCustomField::raiseUnattachedException($this, $role);
|
||||||
|
}
|
||||||
|
return $this->customFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attachCustomFields($role, array $fields) {
|
||||||
|
$this->customFields[$role] = $fields;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
Loading…
Reference in a new issue