[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Convenience class to perform operations on an entire field list, like reading 5 * all values from storage. 6 * 7 * $field_list = new PhabricatorCustomFieldList($fields); 8 * 9 */ 10 final class PhabricatorCustomFieldList extends Phobject { 11 12 private $fields; 13 private $viewer; 14 15 public function __construct(array $fields) { 16 assert_instances_of($fields, 'PhabricatorCustomField'); 17 $this->fields = $fields; 18 } 19 20 public function getFields() { 21 return $this->fields; 22 } 23 24 public function setViewer(PhabricatorUser $viewer) { 25 $this->viewer = $viewer; 26 foreach ($this->getFields() as $field) { 27 $field->setViewer($viewer); 28 } 29 return $this; 30 } 31 32 33 /** 34 * Read stored values for all fields which support storage. 35 * 36 * @param PhabricatorCustomFieldInterface Object to read field values for. 37 * @return void 38 */ 39 public function readFieldsFromStorage( 40 PhabricatorCustomFieldInterface $object) { 41 42 foreach ($this->fields as $field) { 43 $field->setObject($object); 44 $field->readValueFromObject($object); 45 } 46 47 $keys = array(); 48 foreach ($this->fields as $field) { 49 if ($field->shouldEnableForRole(PhabricatorCustomField::ROLE_STORAGE)) { 50 $keys[$field->getFieldIndex()] = $field; 51 } 52 } 53 54 if (!$keys) { 55 return $this; 56 } 57 58 // NOTE: We assume all fields share the same storage. This isn't guaranteed 59 // to be true, but always is for now. 60 61 $table = head($keys)->newStorageObject(); 62 63 $objects = array(); 64 if ($object->getPHID()) { 65 $objects = $table->loadAllWhere( 66 'objectPHID = %s AND fieldIndex IN (%Ls)', 67 $object->getPHID(), 68 array_keys($keys)); 69 $objects = mpull($objects, null, 'getFieldIndex'); 70 } 71 72 foreach ($keys as $key => $field) { 73 $storage = idx($objects, $key); 74 if ($storage) { 75 $field->setValueFromStorage($storage->getFieldValue()); 76 } else if ($object->getPHID()) { 77 // NOTE: We set this only if the object exists. Otherwise, we allow the 78 // field to retain any default value it may have. 79 $field->setValueFromStorage(null); 80 } 81 } 82 83 return $this; 84 } 85 86 public function appendFieldsToForm(AphrontFormView $form) { 87 $enabled = array(); 88 foreach ($this->fields as $field) { 89 if ($field->shouldEnableForRole(PhabricatorCustomField::ROLE_EDIT)) { 90 $enabled[] = $field; 91 } 92 } 93 94 $phids = array(); 95 foreach ($enabled as $field_key => $field) { 96 $phids[$field_key] = $field->getRequiredHandlePHIDsForEdit(); 97 } 98 99 $all_phids = array_mergev($phids); 100 if ($all_phids) { 101 $handles = id(new PhabricatorHandleQuery()) 102 ->setViewer($this->viewer) 103 ->withPHIDs($all_phids) 104 ->execute(); 105 } else { 106 $handles = array(); 107 } 108 109 foreach ($enabled as $field_key => $field) { 110 $field_handles = array_select_keys($handles, $phids[$field_key]); 111 112 $instructions = $field->getInstructionsForEdit(); 113 if (strlen($instructions)) { 114 $form->appendRemarkupInstructions($instructions); 115 } 116 117 $form->appendChild($field->renderEditControl($field_handles)); 118 } 119 } 120 121 public function appendFieldsToPropertyList( 122 PhabricatorCustomFieldInterface $object, 123 PhabricatorUser $viewer, 124 PHUIPropertyListView $view) { 125 126 $this->readFieldsFromStorage($object); 127 $fields = $this->fields; 128 129 foreach ($fields as $field) { 130 $field->setViewer($viewer); 131 } 132 133 // Move all the blocks to the end, regardless of their configuration order, 134 // because it always looks silly to render a block in the middle of a list 135 // of properties. 136 $head = array(); 137 $tail = array(); 138 foreach ($fields as $key => $field) { 139 $style = $field->getStyleForPropertyView(); 140 switch ($style) { 141 case 'property': 142 case 'header': 143 $head[$key] = $field; 144 break; 145 case 'block': 146 $tail[$key] = $field; 147 break; 148 default: 149 throw new Exception( 150 "Unknown field property view style '{$style}'; valid styles are ". 151 "'block' and 'property'."); 152 } 153 } 154 $fields = $head + $tail; 155 156 $add_header = null; 157 158 $phids = array(); 159 foreach ($fields as $key => $field) { 160 $phids[$key] = $field->getRequiredHandlePHIDsForPropertyView(); 161 } 162 163 $all_phids = array_mergev($phids); 164 if ($all_phids) { 165 $handles = id(new PhabricatorHandleQuery()) 166 ->setViewer($viewer) 167 ->withPHIDs($all_phids) 168 ->execute(); 169 } else { 170 $handles = array(); 171 } 172 173 foreach ($fields as $key => $field) { 174 $field_handles = array_select_keys($handles, $phids[$key]); 175 $label = $field->renderPropertyViewLabel(); 176 $value = $field->renderPropertyViewValue($field_handles); 177 if ($value !== null) { 178 switch ($field->getStyleForPropertyView()) { 179 case 'header': 180 // We want to hide headers if the fields the're assciated with 181 // don't actually produce any visible properties. For example, in a 182 // list like this: 183 // 184 // Header A 185 // Prop A: Value A 186 // Header B 187 // Prop B: Value B 188 // 189 // ...if the "Prop A" field returns `null` when rendering its 190 // property value and we rendered naively, we'd get this: 191 // 192 // Header A 193 // Header B 194 // Prop B: Value B 195 // 196 // This is silly. Instead, we hide "Header A". 197 $add_header = $value; 198 break; 199 case 'property': 200 if ($add_header !== null) { 201 // Add the most recently seen header. 202 $view->addSectionHeader($add_header); 203 $add_header = null; 204 } 205 $view->addProperty($label, $value); 206 break; 207 case 'block': 208 $icon = $field->getIconForPropertyView(); 209 $view->invokeWillRenderEvent(); 210 if ($label !== null) { 211 $view->addSectionHeader($label, $icon); 212 } 213 $view->addTextContent($value); 214 break; 215 } 216 } 217 } 218 } 219 220 public function buildFieldTransactionsFromRequest( 221 PhabricatorApplicationTransaction $template, 222 AphrontRequest $request) { 223 224 $xactions = array(); 225 226 $role = PhabricatorCustomField::ROLE_APPLICATIONTRANSACTIONS; 227 foreach ($this->fields as $field) { 228 if (!$field->shouldEnableForRole($role)) { 229 continue; 230 } 231 232 $transaction_type = $field->getApplicationTransactionType(); 233 $xaction = id(clone $template) 234 ->setTransactionType($transaction_type); 235 236 if ($transaction_type == PhabricatorTransactions::TYPE_CUSTOMFIELD) { 237 // For TYPE_CUSTOMFIELD transactions only, we provide the old value 238 // as an input. 239 $old_value = $field->getOldValueForApplicationTransactions(); 240 $xaction->setOldValue($old_value); 241 } 242 243 $field->readValueFromRequest($request); 244 245 $xaction 246 ->setNewValue($field->getNewValueForApplicationTransactions()); 247 248 if ($transaction_type == PhabricatorTransactions::TYPE_CUSTOMFIELD) { 249 // For TYPE_CUSTOMFIELD transactions, add the field key in metadata. 250 $xaction->setMetadataValue('customfield:key', $field->getFieldKey()); 251 } 252 253 $metadata = $field->getApplicationTransactionMetadata(); 254 foreach ($metadata as $key => $value) { 255 $xaction->setMetadataValue($key, $value); 256 } 257 258 $xactions[] = $xaction; 259 } 260 261 return $xactions; 262 } 263 264 265 /** 266 * Publish field indexes into index tables, so ApplicationSearch can search 267 * them. 268 * 269 * @return void 270 */ 271 public function rebuildIndexes(PhabricatorCustomFieldInterface $object) { 272 $indexes = array(); 273 $index_keys = array(); 274 275 $phid = $object->getPHID(); 276 277 $role = PhabricatorCustomField::ROLE_APPLICATIONSEARCH; 278 foreach ($this->fields as $field) { 279 if (!$field->shouldEnableForRole($role)) { 280 continue; 281 } 282 283 $index_keys[$field->getFieldIndex()] = true; 284 285 foreach ($field->buildFieldIndexes() as $index) { 286 $index->setObjectPHID($phid); 287 $indexes[$index->getTableName()][] = $index; 288 } 289 } 290 291 if (!$indexes) { 292 return; 293 } 294 295 $any_index = head(head($indexes)); 296 $conn_w = $any_index->establishConnection('w'); 297 298 foreach ($indexes as $table => $index_list) { 299 $sql = array(); 300 foreach ($index_list as $index) { 301 $sql[] = $index->formatForInsert($conn_w); 302 } 303 $indexes[$table] = $sql; 304 } 305 306 $any_index->openTransaction(); 307 308 foreach ($indexes as $table => $sql_list) { 309 queryfx( 310 $conn_w, 311 'DELETE FROM %T WHERE objectPHID = %s AND indexKey IN (%Ls)', 312 $table, 313 $phid, 314 array_keys($index_keys)); 315 316 if (!$sql_list) { 317 continue; 318 } 319 320 foreach (PhabricatorLiskDAO::chunkSQL($sql_list) as $chunk) { 321 queryfx( 322 $conn_w, 323 'INSERT INTO %T (objectPHID, indexKey, indexValue) VALUES %Q', 324 $table, 325 $chunk); 326 } 327 } 328 329 $any_index->saveTransaction(); 330 } 331 332 public function updateAbstractDocument( 333 PhabricatorSearchAbstractDocument $document) { 334 335 $role = PhabricatorCustomField::ROLE_GLOBALSEARCH; 336 foreach ($this->getFields() as $field) { 337 if (!$field->shouldEnableForRole($role)) { 338 continue; 339 } 340 $field->updateAbstractDocument($document); 341 } 342 } 343 344 345 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |