diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1140,6 +1140,7 @@ 'NuanceRequestorViewController' => 'applications/nuance/controller/NuanceRequestorViewController.php', 'NuanceSchemaSpec' => 'applications/nuance/storage/NuanceSchemaSpec.php', 'NuanceSource' => 'applications/nuance/storage/NuanceSource.php', + 'NuanceSourceActionController' => 'applications/nuance/controller/NuanceSourceActionController.php', 'NuanceSourceCreateController' => 'applications/nuance/controller/NuanceSourceCreateController.php', 'NuanceSourceDefaultEditCapability' => 'applications/nuance/capability/NuanceSourceDefaultEditCapability.php', 'NuanceSourceDefaultViewCapability' => 'applications/nuance/capability/NuanceSourceDefaultViewCapability.php', @@ -4473,7 +4474,11 @@ 'NuanceQueueTransactionComment' => 'PhabricatorApplicationTransactionComment', 'NuanceQueueTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'NuanceQueueViewController' => 'NuanceController', - 'NuanceRequestor' => 'NuanceDAO', + 'NuanceRequestor' => array( + 'NuanceDAO', + 'PhabricatorPolicyInterface', + 'PhabricatorApplicationTransactionInterface', + ), 'NuanceRequestorEditController' => 'NuanceController', 'NuanceRequestorEditor' => 'PhabricatorApplicationTransactionEditor', 'NuanceRequestorPHIDType' => 'PhabricatorPHIDType', @@ -4489,6 +4494,7 @@ 'PhabricatorApplicationTransactionInterface', 'PhabricatorPolicyInterface', ), + 'NuanceSourceActionController' => 'NuanceController', 'NuanceSourceCreateController' => 'NuanceController', 'NuanceSourceDefaultEditCapability' => 'PhabricatorPolicyCapability', 'NuanceSourceDefaultViewCapability' => 'PhabricatorPolicyCapability', diff --git a/src/applications/nuance/application/PhabricatorNuanceApplication.php b/src/applications/nuance/application/PhabricatorNuanceApplication.php --- a/src/applications/nuance/application/PhabricatorNuanceApplication.php +++ b/src/applications/nuance/application/PhabricatorNuanceApplication.php @@ -62,6 +62,9 @@ 'new/' => 'NuanceRequestorEditController', ), ), + '/action/' => array( + '(?P[1-9]\d*)/(?P.*)' => 'NuanceSourceActionController', + ), ); } diff --git a/src/applications/nuance/conduit/NuanceCreateItemConduitAPIMethod.php b/src/applications/nuance/conduit/NuanceCreateItemConduitAPIMethod.php --- a/src/applications/nuance/conduit/NuanceCreateItemConduitAPIMethod.php +++ b/src/applications/nuance/conduit/NuanceCreateItemConduitAPIMethod.php @@ -36,7 +36,7 @@ $user = $request->getUser(); - $item = NuanceItem::initializeNewItem($user); + $item = NuanceItem::initializeNewItem(); $xactions = array(); if ($source_phid) { diff --git a/src/applications/nuance/controller/NuanceSourceActionController.php b/src/applications/nuance/controller/NuanceSourceActionController.php new file mode 100644 --- /dev/null +++ b/src/applications/nuance/controller/NuanceSourceActionController.php @@ -0,0 +1,38 @@ +getViewer(); + + $source = id(new NuanceSourceQuery()) + ->setViewer($viewer) + ->withIDs(array($request->getURIData('id'))) + ->executeOne(); + if (!$source) { + return new Aphront404Response(); + } + + $def = NuanceSourceDefinition::getDefinitionForSource($source); + $def->setActor($viewer); + + $response = $def->handleActionRequest($request); + if ($response instanceof AphrontResponse) { + return $response; + } + + $title = $source->getName(); + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addTextCrumb($title); + + return $this->buildApplicationPage( + array( + $crumbs, + $response, + ), + array( + 'title' => $title, + )); + } + +} diff --git a/src/applications/nuance/editor/NuanceItemEditor.php b/src/applications/nuance/editor/NuanceItemEditor.php --- a/src/applications/nuance/editor/NuanceItemEditor.php +++ b/src/applications/nuance/editor/NuanceItemEditor.php @@ -17,6 +17,7 @@ $types[] = NuanceItemTransaction::TYPE_OWNER; $types[] = NuanceItemTransaction::TYPE_SOURCE; $types[] = NuanceItemTransaction::TYPE_REQUESTOR; + $types[] = NuanceItemTransaction::TYPE_PROPERTY; $types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorTransactions::TYPE_COMMENT; @@ -37,6 +38,10 @@ return $object->getSourcePHID(); case NuanceItemTransaction::TYPE_OWNER: return $object->getOwnerPHID(); + case NuanceItemTransaction::TYPE_PROPERTY: + $key = $xaction->getMetadataValue( + NuanceItemTransaction::PROPERTY_KEY); + return $object->getNuanceProperty($key); } return parent::getCustomTransactionOldValue($object, $xaction); @@ -50,6 +55,7 @@ case NuanceItemTransaction::TYPE_REQUESTOR: case NuanceItemTransaction::TYPE_SOURCE: case NuanceItemTransaction::TYPE_OWNER: + case NuanceItemTransaction::TYPE_PROPERTY: return $xaction->getNewValue(); } @@ -70,6 +76,11 @@ case NuanceItemTransaction::TYPE_OWNER: $object->setOwnerPHID($xaction->getNewValue()); break; + case NuanceItemTransaction::TYPE_PROPERTY: + $key = $xaction->getMetadataValue( + NuanceItemTransaction::PROPERTY_KEY); + $object->setNuanceProperty($key, $xaction->getNewValue()); + break; } } @@ -81,6 +92,7 @@ case NuanceItemTransaction::TYPE_REQUESTOR: case NuanceItemTransaction::TYPE_SOURCE: case NuanceItemTransaction::TYPE_OWNER: + case NuanceItemTransaction::TYPE_PROPERTY: return; } diff --git a/src/applications/nuance/editor/NuanceRequestorEditor.php b/src/applications/nuance/editor/NuanceRequestorEditor.php --- a/src/applications/nuance/editor/NuanceRequestorEditor.php +++ b/src/applications/nuance/editor/NuanceRequestorEditor.php @@ -14,12 +14,62 @@ public function getTransactionTypes() { $types = parent::getTransactionTypes(); - $types[] = PhabricatorTransactions::TYPE_EDGE; - $types[] = PhabricatorTransactions::TYPE_COMMENT; + $types[] = NuanceRequestorTransaction::TYPE_PROPERTY; + $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; return $types; } + protected function getCustomTransactionOldValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case NuanceRequestorTransaction::TYPE_PROPERTY: + $key = $xaction->getMetadataValue( + NuanceRequestorTransaction::PROPERTY_KEY); + return $object->getNuanceProperty($key); + } + + return parent::getCustomTransactionOldValue($object, $xaction); + } + + protected function getCustomTransactionNewValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case NuanceRequestorTransaction::TYPE_PROPERTY: + return $xaction->getNewValue(); + } + + return parent::getCustomTransactionNewValue($object, $xaction); + } + + protected function applyCustomInternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case NuanceRequestorTransaction::TYPE_PROPERTY: + $key = $xaction->getMetadataValue( + NuanceRequestorTransaction::PROPERTY_KEY); + $object->setNuanceProperty($key, $xaction->getNewValue()); + break; + } + } + + protected function applyCustomExternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case NuanceRequestorTransaction::TYPE_PROPERTY: + return; + } + + return parent::applyCustomExternalTransaction($object, $xaction); + } } diff --git a/src/applications/nuance/query/NuanceItemQuery.php b/src/applications/nuance/query/NuanceItemQuery.php --- a/src/applications/nuance/query/NuanceItemQuery.php +++ b/src/applications/nuance/query/NuanceItemQuery.php @@ -5,7 +5,7 @@ private $ids; private $phids; - private $sourceIDs; + private $sourcePHIDs; public function withIDs(array $ids) { $this->ids = $ids; @@ -17,54 +17,52 @@ return $this; } - public function withSourceIDs($source_ids) { - $this->sourceIDs = $source_ids; + public function withSourcePHIDs($source_phids) { + $this->sourcePHIDs = $source_phids; return $this; } - protected function loadPage() { $table = new NuanceItem(); - $conn_r = $table->establishConnection('r'); + $conn = $table->establishConnection('r'); $data = queryfx_all( - $conn_r, - 'SELECT FROM %T %Q %Q %Q', + $conn, + '%Q FROM %T %Q %Q %Q', + $this->buildSelectClause($conn), $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); + $this->buildWhereClause($conn), + $this->buildOrderClause($conn), + $this->buildLimitClause($conn)); return $table->loadAllFromArray($data); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - $where = array(); - - $where[] = $this->buildPagingClause($conn_r); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); - if ($this->sourceID) { + if ($this->sourcePHIDs !== null) { $where[] = qsprintf( - $conn_r, - 'sourceID IN (%Ld)', - $this->sourceIDs); + $conn, + 'sourcePHID IN (%Ls)', + $this->sourcePHIDs); } - if ($this->ids) { + if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } - if ($this->phids) { + if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } - return $this->formatWhereClause($where); + return $where; } } diff --git a/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php b/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php --- a/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php +++ b/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php @@ -42,4 +42,51 @@ public function renderListView() {} + + public function handleActionRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + + // TODO: As above, this would eventually be driven by custom logic. + + if ($request->isFormPost()) { + $properties = array( + 'complaint' => (string)$request->getStr('text'), + ); + + $content_source = PhabricatorContentSource::newFromRequest($request); + + $requestor = NuanceRequestor::newFromPhabricatorUser( + $viewer, + $content_source); + + $item = $this->newItemFromProperties( + $requestor, + $properties, + $content_source); + + $uri = $item->getURI(); + return id(new AphrontRedirectResponse())->setURI($uri); + } + + $form = id(new AphrontFormView()) + ->setUser($viewer) + ->appendRemarkupInstructions( + pht('IMPORTANT: This is a very rough prototype.')) + ->appendRemarkupInstructions( + pht('Got a complaint? Complain here! We love complaints.')) + ->appendChild( + id(new AphrontFormTextAreaControl()) + ->setName('complaint') + ->setLabel(pht('Complaint'))) + ->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue(pht('Submit Complaint'))); + + $box = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Complaint Form')) + ->appendChild($form); + + return $box; + } + } diff --git a/src/applications/nuance/source/NuanceSourceDefinition.php b/src/applications/nuance/source/NuanceSourceDefinition.php --- a/src/applications/nuance/source/NuanceSourceDefinition.php +++ b/src/applications/nuance/source/NuanceSourceDefinition.php @@ -1,5 +1,8 @@ requireSourceObject(); + + $item = NuanceItem::initializeNewItem(); + + $xactions = array(); + + $xactions[] = id(new NuanceItemTransaction()) + ->setTransactionType(NuanceItemTransaction::TYPE_SOURCE) + ->setNewValue($source->getPHID()); + + $xactions[] = id(new NuanceItemTransaction()) + ->setTransactionType(NuanceItemTransaction::TYPE_REQUESTOR) + ->setNewValue($requestor->getPHID()); + + foreach ($properties as $key => $property) { + $xactions[] = id(new NuanceItemTransaction()) + ->setTransactionType(NuanceItemTransaction::TYPE_PROPERTY) + ->setMetadataValue(NuanceItemTransaction::PROPERTY_KEY, $key) + ->setNewValue($property); + } + + $editor = id(new NuanceItemEditor()) + ->setActor($actor) + ->setActingAsPHID($requestor->getActingAsPHID()) + ->setContentSource($content_source); + + $editor->applyTransactions($item, $xactions); + + return $item; + } + + +/* -( Handling Action Requests )------------------------------------------- */ + + + public function handleActionRequest(AphrontRequest $request) { + return new Aphront404Response(); + } + } diff --git a/src/applications/nuance/storage/NuanceItem.php b/src/applications/nuance/storage/NuanceItem.php --- a/src/applications/nuance/storage/NuanceItem.php +++ b/src/applications/nuance/storage/NuanceItem.php @@ -13,11 +13,11 @@ protected $requestorPHID; protected $sourcePHID; protected $sourceLabel; - protected $data; + protected $data = array(); protected $mailKey; protected $dateNuanced; - public static function initializeNewItem(PhabricatorUser $user) { + public static function initializeNewItem() { return id(new NuanceItem()) ->setDateNuanced(time()) ->setStatus(self::STATUS_OPEN); @@ -94,6 +94,15 @@ $this->source = $source; } + public function getNuanceProperty($key, $default = null) { + return idx($this->data, $key, $default); + } + + public function setNuanceProperty($key, $value) { + $this->data[$key] = $value; + return $this; + } + public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, diff --git a/src/applications/nuance/storage/NuanceItemTransaction.php b/src/applications/nuance/storage/NuanceItemTransaction.php --- a/src/applications/nuance/storage/NuanceItemTransaction.php +++ b/src/applications/nuance/storage/NuanceItemTransaction.php @@ -3,9 +3,12 @@ final class NuanceItemTransaction extends NuanceTransaction { - const TYPE_OWNER = 'item-owner'; - const TYPE_REQUESTOR = 'item-requestor'; - const TYPE_SOURCE = 'item-source'; + const PROPERTY_KEY = 'property.key'; + + const TYPE_OWNER = 'nuance.item.owner'; + const TYPE_REQUESTOR = 'nuance.item.requestor'; + const TYPE_SOURCE = 'nuance.item.source'; + const TYPE_PROPERTY = 'nuance.item.property'; public function getApplicationTransactionType() { return NuanceItemPHIDType::TYPECONST; diff --git a/src/applications/nuance/storage/NuanceRequestor.php b/src/applications/nuance/storage/NuanceRequestor.php --- a/src/applications/nuance/storage/NuanceRequestor.php +++ b/src/applications/nuance/storage/NuanceRequestor.php @@ -1,9 +1,12 @@ getMailKey()) { - $this->setMailKey(Filesystem::readRandomCharacters(20)); - } - return parent::save(); + public static function initializeNewRequestor() { + return new NuanceRequestor(); } public function getURI() { @@ -34,4 +34,104 @@ return idx($this->getData(), 'phabricatorUserPHID'); } + public function getActingAsPHID() { + $user_phid = $this->getPhabricatorUserPHID(); + if ($user_phid) { + return $user_phid; + } + + return id(new PhabricatorNuanceApplication())->getPHID(); + } + + public static function newFromPhabricatorUser( + PhabricatorUser $viewer, + PhabricatorContentSource $content_source) { + + // TODO: This is real sketchy and creates a new requestor every time. It + // shouldn't do that. + + $requestor = self::initializeNewRequestor(); + + $xactions = array(); + + $properties = array( + 'phabricatorUserPHID' => $viewer->getPHID(), + ); + + foreach ($properties as $key => $value) { + $xactions[] = id(new NuanceRequestorTransaction()) + ->setTransactionType(NuanceRequestorTransaction::TYPE_PROPERTY) + ->setMetadataValue(NuanceRequestorTransaction::PROPERTY_KEY, $key) + ->setNewValue($value); + } + + $editor = id(new NuanceRequestorEditor()) + ->setActor($viewer) + ->setContentSource($content_source); + + $editor->applyTransactions($requestor, $xactions); + + return $requestor; + } + + public function getNuanceProperty($key, $default = null) { + return idx($this->data, $key, $default); + } + + public function setNuanceProperty($key, $value) { + $this->data[$key] = $value; + return $this; + } + + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + ); + } + + public function getPolicy($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return PhabricatorPolicies::POLICY_USER; + case PhabricatorPolicyCapability::CAN_EDIT: + return PhabricatorPolicies::POLICY_USER; + } + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return false; + } + + public function describeAutomaticCapability($capability) { + return null; + } + + +/* -( PhabricatorApplicationTransactionInterface )------------------------- */ + + + public function getApplicationTransactionEditor() { + return new NuanceRequestorEditor(); + } + + public function getApplicationTransactionObject() { + return $this; + } + + public function getApplicationTransactionTemplate() { + return new NuanceRequestorTransaction(); + } + + public function willRenderTimeline( + PhabricatorApplicationTransactionView $timeline, + AphrontRequest $request) { + + return $timeline; + } + } diff --git a/src/applications/nuance/storage/NuanceRequestorTransaction.php b/src/applications/nuance/storage/NuanceRequestorTransaction.php --- a/src/applications/nuance/storage/NuanceRequestorTransaction.php +++ b/src/applications/nuance/storage/NuanceRequestorTransaction.php @@ -3,6 +3,10 @@ final class NuanceRequestorTransaction extends NuanceTransaction { + const PROPERTY_KEY = 'property.key'; + + const TYPE_PROPERTY = 'nuance.requestor.property'; + public function getApplicationTransactionType() { return NuanceRequestorPHIDType::TYPECONST; }