[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/infrastructure/customfield/field/ -> PhabricatorCustomFieldList.php (source)

   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  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1