protected function applyBuiltinExternalTransaction(PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction)
 {
     switch ($xaction->getTransactionType()) {
         case PhabricatorTransactions::TYPE_INLINESTATE:
             $table = new DifferentialTransactionComment();
             $conn_w = $table->establishConnection('w');
             foreach ($xaction->getNewValue() as $phid => $state) {
                 queryfx($conn_w, 'UPDATE %T SET fixedState = %s WHERE phid = %s', $table->getTableName(), $state, $phid);
             }
             break;
     }
     return parent::applyBuiltinExternalTransaction($object, $xaction);
 }
 protected function applyBuiltinExternalTransaction(PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction)
 {
     switch ($xaction->getTransactionType()) {
         case PhabricatorTransactions::TYPE_EDGE:
             $edge_type = $xaction->getMetadataValue('edge:type');
             switch ($edge_type) {
                 case PhabricatorProjectProjectHasMemberEdgeType::EDGECONST:
                 case PhabricatorObjectHasWatcherEdgeType::EDGECONST:
                     $old = $xaction->getOldValue();
                     $new = $xaction->getNewValue();
                     // When adding members or watchers, we add subscriptions.
                     $add = array_keys(array_diff_key($new, $old));
                     // When removing members, we remove their subscription too.
                     // When unwatching, we leave subscriptions, since it's fine to be
                     // subscribed to a project but not be a member of it.
                     $edge_const = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST;
                     if ($edge_type == $edge_const) {
                         $rem = array_keys(array_diff_key($old, $new));
                     } else {
                         $rem = array();
                     }
                     // NOTE: The subscribe is "explicit" because there's no implicit
                     // unsubscribe, so Join -> Leave -> Join doesn't resubscribe you
                     // if we use an implicit subscribe, even though you never willfully
                     // unsubscribed. Not sure if adding implicit unsubscribe (which
                     // would not write the unsubscribe row) is justified to deal with
                     // this, which is a fairly weird edge case and pretty arguable both
                     // ways.
                     // Subscriptions caused by watches should also clearly be explicit,
                     // and that case is unambiguous.
                     id(new PhabricatorSubscriptionsEditor())->setActor($this->requireActor())->setObject($object)->subscribeExplicit($add)->unsubscribe($rem)->save();
                     if ($rem) {
                         // When removing members, also remove any watches on the project.
                         $edge_editor = new PhabricatorEdgeEditor();
                         foreach ($rem as $rem_phid) {
                             $edge_editor->removeEdge($object->getPHID(), PhabricatorObjectHasWatcherEdgeType::EDGECONST, $rem_phid);
                         }
                         $edge_editor->save();
                     }
                     break;
             }
             break;
     }
     return parent::applyBuiltinExternalTransaction($object, $xaction);
 }