I am trying to update a CustomField using a PHP script, but run into some unexpected trouble.
I had a peek at processRequest() in phabricator/src/applications/people/controller/PhabricatorPeopleProfileEditController.php and then modelled my code after buildFieldTransactionsFromRequest() from phabricator/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php.
Thus my code looks like this:
function setUserField($user, $field_key, $field_value, $viewer) { $fields = PhabricatorCustomField::getObjectFields($user, PhabricatorCustomField::ROLE_DEFAULT) ->setViewer($viewer) ->readFieldsFromStorage($user) ->getFields(); $field = idx($fields, $field_key); $transaction_type = $field->getApplicationTransactionType(); $xaction = id(new PhabricatorUserTransaction()) ->setTransactionType($transaction_type); if ($transaction_type == PhabricatorTransactions::TYPE_CUSTOMFIELD) { // For TYPE_CUSTOMFIELD transactions only, we provide the old value // as an input. $old_value = $field->getOldValueForApplicationTransactions(); $xaction->setOldValue($old_value); } $field->getProxy()->setFieldValue($field_value); $new_value = $field->getNewValueForApplicationTransactions(); $xaction->setNewValue($new_value); if ($transaction_type == PhabricatorTransactions::TYPE_CUSTOMFIELD) { // For TYPE_CUSTOMFIELD transactions, add the field key in metadata. $xaction->setMetadataValue('customfield:key', $field->getFieldKey()); } $metadata = $field->getApplicationTransactionMetadata(); foreach ($metadata as $key => $value) { $xaction->setMetadataValue($key, $value); } $xactions[] = $xaction; $editor = id(new PhabricatorUserProfileEditor()) ->setActor($viewer) ->setContentSource(PhabricatorContentSource::newConsoleSource()) ->setContinueOnNoEffect(true) ->setContinueOnMissingFields(true) ->applyTransactions($user, $xactions); }
I retrieve and change the users like this:
$phab_users = id(new PhabricatorPeopleQuery()) ->setViewer($phab_admin) ->withIsSystemAgent(false) ->withIsMailingList(false) ->execute(); foreach ($phab_users as $user) { $user->loadUserProfile(); setUserField($user, "std:user:mycustomfield", 1234, $phab_admin); }
This is almost the same as in the files I referenced above, but I always get an error:
# ./phabricator-apply-policy.php [2015-06-11 21:47:06] EXCEPTION: (AphrontQueryException) #1048: Column 'title' cannot be null at [<phutil>/src/aphront/storage/connection/mysql/AphrontBaseMySQLDatabaseConnection.php:311] arcanist(head=master, ref.master=7d15b85a1bc0), libphremoteuser(head=master, ref.master=1def4e2d7f07), phabricator(head=master, ref.master=146e2baa2032), phutil(head=master, ref.master=92882eb9404d), sprint(head=master, ref.master=ecd2a064d3c2) #0 AphrontBaseMySQLDatabaseConnection::throwQueryCodeException(integer, string) called at [<phutil>/src/aphront/storage/connection/mysql/AphrontBaseMySQLDatabaseConnection.php:275] #1 AphrontBaseMySQLDatabaseConnection::throwQueryException(mysqli) called at [<phutil>/src/aphront/storage/connection/mysql/AphrontBaseMySQLDatabaseConnection.php:181] #2 AphrontBaseMySQLDatabaseConnection::executeRawQuery(string) called at [<phutil>/src/xsprintf/queryfx.php:6] #3 queryfx(AphrontMySQLiDatabaseConnection, string, string, string, array, string) #4 call_user_func_array(string, array) called at [<phutil>/src/aphront/storage/connection/AphrontDatabaseConnection.php:26] #5 AphrontDatabaseConnection::query(string, string, string, array, string) called at [<phabricator>/src/infrastructure/storage/lisk/LiskDAO.php:1261] #6 LiskDAO::insertRecordIntoDatabase(string) called at [<phabricator>/src/infrastructure/storage/lisk/LiskDAO.php:1106] #7 LiskDAO::insert() called at [<phabricator>/src/infrastructure/storage/lisk/LiskDAO.php:1075] #8 LiskDAO::save() called at [<phabricator>/src/applications/people/storage/PhabricatorUser.php:269] #9 PhabricatorUser::save() called at [<phabricator>/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php:843] #10 PhabricatorApplicationTransactionEditor::applyTransactions(PhabricatorUser, array) called at [<phabricator>-apply-policy/phabricator-apply-policy.inc.php:79] #11 setUserField(PhabricatorUser, string, integer, PhabricatorUser) called at [<phabricator>-apply-policy/phabricator-apply-policy.inc.php:150] #12 apply_policies_to_users(array, array) called at [<phabricator>-apply-policy/phabricator-apply-policy.php:49] PHP Fatal error: Uncaught exception 'Exception' with message 'Process exited with an open transaction! The transaction will be implicitly rolled back. Calls to openTransaction() must always be paired with a call to saveTransaction() or killTransaction().' in /opt/phabricator/libphutil/src/aphront/storage/connection/AphrontDatabaseTransactionState.php:78 Stack trace: #0 [internal function]: AphrontDatabaseTransactionState->__destruct() #1 {main} thrown in /opt/phabricator/libphutil/src/aphront/storage/connection/AphrontDatabaseTransactionState.php on line 78 Fatal error: Uncaught exception 'Exception' with message 'Process exited with an open transaction! The transaction will be implicitly rolled back. Calls to openTransaction() must always be paired with a call to saveTransaction() or killTransaction().' in /opt/phabricator/libphutil/src/aphront/storage/connection/AphrontDatabaseTransactionState.php:78 Stack trace: #0 [internal function]: AphrontDatabaseTransactionState->__destruct() #1 {main} thrown in /opt/phabricator/libphutil/src/aphront/storage/connection/AphrontDatabaseTransactionState.php on line 78
Under some circumstances the user or the transaction seems to be malformed. But I don't yet see why.