1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-19 05:12:41 +01:00

Give Nuance form sources a web UI

Summary:
Ref T8434. Hard-codes form sources as a complaint form.

This form is close to perfect and I'm not actually sure we need to let users customize it at all.

Test Plan: {F473587}

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T8434

Differential Revision: https://secure.phabricator.com/D13166
This commit is contained in:
epriestley 2015-06-05 11:01:06 -07:00
parent f529ade9f7
commit fc3d11809f
13 changed files with 362 additions and 39 deletions

View file

@ -1140,6 +1140,7 @@ phutil_register_library_map(array(
'NuanceRequestorViewController' => 'applications/nuance/controller/NuanceRequestorViewController.php', 'NuanceRequestorViewController' => 'applications/nuance/controller/NuanceRequestorViewController.php',
'NuanceSchemaSpec' => 'applications/nuance/storage/NuanceSchemaSpec.php', 'NuanceSchemaSpec' => 'applications/nuance/storage/NuanceSchemaSpec.php',
'NuanceSource' => 'applications/nuance/storage/NuanceSource.php', 'NuanceSource' => 'applications/nuance/storage/NuanceSource.php',
'NuanceSourceActionController' => 'applications/nuance/controller/NuanceSourceActionController.php',
'NuanceSourceCreateController' => 'applications/nuance/controller/NuanceSourceCreateController.php', 'NuanceSourceCreateController' => 'applications/nuance/controller/NuanceSourceCreateController.php',
'NuanceSourceDefaultEditCapability' => 'applications/nuance/capability/NuanceSourceDefaultEditCapability.php', 'NuanceSourceDefaultEditCapability' => 'applications/nuance/capability/NuanceSourceDefaultEditCapability.php',
'NuanceSourceDefaultViewCapability' => 'applications/nuance/capability/NuanceSourceDefaultViewCapability.php', 'NuanceSourceDefaultViewCapability' => 'applications/nuance/capability/NuanceSourceDefaultViewCapability.php',
@ -4473,7 +4474,11 @@ phutil_register_library_map(array(
'NuanceQueueTransactionComment' => 'PhabricatorApplicationTransactionComment', 'NuanceQueueTransactionComment' => 'PhabricatorApplicationTransactionComment',
'NuanceQueueTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'NuanceQueueTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'NuanceQueueViewController' => 'NuanceController', 'NuanceQueueViewController' => 'NuanceController',
'NuanceRequestor' => 'NuanceDAO', 'NuanceRequestor' => array(
'NuanceDAO',
'PhabricatorPolicyInterface',
'PhabricatorApplicationTransactionInterface',
),
'NuanceRequestorEditController' => 'NuanceController', 'NuanceRequestorEditController' => 'NuanceController',
'NuanceRequestorEditor' => 'PhabricatorApplicationTransactionEditor', 'NuanceRequestorEditor' => 'PhabricatorApplicationTransactionEditor',
'NuanceRequestorPHIDType' => 'PhabricatorPHIDType', 'NuanceRequestorPHIDType' => 'PhabricatorPHIDType',
@ -4489,6 +4494,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionInterface', 'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',
), ),
'NuanceSourceActionController' => 'NuanceController',
'NuanceSourceCreateController' => 'NuanceController', 'NuanceSourceCreateController' => 'NuanceController',
'NuanceSourceDefaultEditCapability' => 'PhabricatorPolicyCapability', 'NuanceSourceDefaultEditCapability' => 'PhabricatorPolicyCapability',
'NuanceSourceDefaultViewCapability' => 'PhabricatorPolicyCapability', 'NuanceSourceDefaultViewCapability' => 'PhabricatorPolicyCapability',

View file

@ -62,6 +62,9 @@ final class PhabricatorNuanceApplication extends PhabricatorApplication {
'new/' => 'NuanceRequestorEditController', 'new/' => 'NuanceRequestorEditController',
), ),
), ),
'/action/' => array(
'(?P<id>[1-9]\d*)/(?P<path>.*)' => 'NuanceSourceActionController',
),
); );
} }

View file

@ -36,7 +36,7 @@ final class NuanceCreateItemConduitAPIMethod extends NuanceConduitAPIMethod {
$user = $request->getUser(); $user = $request->getUser();
$item = NuanceItem::initializeNewItem($user); $item = NuanceItem::initializeNewItem();
$xactions = array(); $xactions = array();
if ($source_phid) { if ($source_phid) {

View file

@ -0,0 +1,38 @@
<?php
final class NuanceSourceActionController extends NuanceController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->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,
));
}
}

View file

@ -17,6 +17,7 @@ final class NuanceItemEditor
$types[] = NuanceItemTransaction::TYPE_OWNER; $types[] = NuanceItemTransaction::TYPE_OWNER;
$types[] = NuanceItemTransaction::TYPE_SOURCE; $types[] = NuanceItemTransaction::TYPE_SOURCE;
$types[] = NuanceItemTransaction::TYPE_REQUESTOR; $types[] = NuanceItemTransaction::TYPE_REQUESTOR;
$types[] = NuanceItemTransaction::TYPE_PROPERTY;
$types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorTransactions::TYPE_EDGE;
$types[] = PhabricatorTransactions::TYPE_COMMENT; $types[] = PhabricatorTransactions::TYPE_COMMENT;
@ -37,6 +38,10 @@ final class NuanceItemEditor
return $object->getSourcePHID(); return $object->getSourcePHID();
case NuanceItemTransaction::TYPE_OWNER: case NuanceItemTransaction::TYPE_OWNER:
return $object->getOwnerPHID(); return $object->getOwnerPHID();
case NuanceItemTransaction::TYPE_PROPERTY:
$key = $xaction->getMetadataValue(
NuanceItemTransaction::PROPERTY_KEY);
return $object->getNuanceProperty($key);
} }
return parent::getCustomTransactionOldValue($object, $xaction); return parent::getCustomTransactionOldValue($object, $xaction);
@ -50,6 +55,7 @@ final class NuanceItemEditor
case NuanceItemTransaction::TYPE_REQUESTOR: case NuanceItemTransaction::TYPE_REQUESTOR:
case NuanceItemTransaction::TYPE_SOURCE: case NuanceItemTransaction::TYPE_SOURCE:
case NuanceItemTransaction::TYPE_OWNER: case NuanceItemTransaction::TYPE_OWNER:
case NuanceItemTransaction::TYPE_PROPERTY:
return $xaction->getNewValue(); return $xaction->getNewValue();
} }
@ -70,6 +76,11 @@ final class NuanceItemEditor
case NuanceItemTransaction::TYPE_OWNER: case NuanceItemTransaction::TYPE_OWNER:
$object->setOwnerPHID($xaction->getNewValue()); $object->setOwnerPHID($xaction->getNewValue());
break; break;
case NuanceItemTransaction::TYPE_PROPERTY:
$key = $xaction->getMetadataValue(
NuanceItemTransaction::PROPERTY_KEY);
$object->setNuanceProperty($key, $xaction->getNewValue());
break;
} }
} }
@ -81,6 +92,7 @@ final class NuanceItemEditor
case NuanceItemTransaction::TYPE_REQUESTOR: case NuanceItemTransaction::TYPE_REQUESTOR:
case NuanceItemTransaction::TYPE_SOURCE: case NuanceItemTransaction::TYPE_SOURCE:
case NuanceItemTransaction::TYPE_OWNER: case NuanceItemTransaction::TYPE_OWNER:
case NuanceItemTransaction::TYPE_PROPERTY:
return; return;
} }

View file

@ -14,12 +14,62 @@ final class NuanceRequestorEditor
public function getTransactionTypes() { public function getTransactionTypes() {
$types = parent::getTransactionTypes(); $types = parent::getTransactionTypes();
$types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = NuanceRequestorTransaction::TYPE_PROPERTY;
$types[] = PhabricatorTransactions::TYPE_COMMENT;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
return $types; 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);
}
} }

View file

@ -5,7 +5,7 @@ final class NuanceItemQuery
private $ids; private $ids;
private $phids; private $phids;
private $sourceIDs; private $sourcePHIDs;
public function withIDs(array $ids) { public function withIDs(array $ids) {
$this->ids = $ids; $this->ids = $ids;
@ -17,54 +17,52 @@ final class NuanceItemQuery
return $this; return $this;
} }
public function withSourceIDs($source_ids) { public function withSourcePHIDs($source_phids) {
$this->sourceIDs = $source_ids; $this->sourcePHIDs = $source_phids;
return $this; return $this;
} }
protected function loadPage() { protected function loadPage() {
$table = new NuanceItem(); $table = new NuanceItem();
$conn_r = $table->establishConnection('r'); $conn = $table->establishConnection('r');
$data = queryfx_all( $data = queryfx_all(
$conn_r, $conn,
'SELECT FROM %T %Q %Q %Q', '%Q FROM %T %Q %Q %Q',
$this->buildSelectClause($conn),
$table->getTableName(), $table->getTableName(),
$this->buildWhereClause($conn_r), $this->buildWhereClause($conn),
$this->buildOrderClause($conn_r), $this->buildOrderClause($conn),
$this->buildLimitClause($conn_r)); $this->buildLimitClause($conn));
return $table->loadAllFromArray($data); return $table->loadAllFromArray($data);
} }
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = array(); $where = parent::buildWhereClauseParts($conn);
$where[] = $this->buildPagingClause($conn_r); if ($this->sourcePHIDs !== null) {
if ($this->sourceID) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'sourceID IN (%Ld)', 'sourcePHID IN (%Ls)',
$this->sourceIDs); $this->sourcePHIDs);
} }
if ($this->ids) { if ($this->ids !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'id IN (%Ld)', 'id IN (%Ld)',
$this->ids); $this->ids);
} }
if ($this->phids) { if ($this->phids !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'phid IN (%Ls)', 'phid IN (%Ls)',
$this->phids); $this->phids);
} }
return $this->formatWhereClause($where); return $where;
} }
} }

View file

@ -42,4 +42,51 @@ final class NuancePhabricatorFormSourceDefinition
public function renderListView() {} 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;
}
} }

View file

@ -1,5 +1,8 @@
<?php <?php
/**
* @task action Handling Action Requests
*/
abstract class NuanceSourceDefinition extends Phobject { abstract class NuanceSourceDefinition extends Phobject {
private $actor; private $actor;
@ -254,4 +257,54 @@ abstract class NuanceSourceDefinition extends Phobject {
abstract public function renderView(); abstract public function renderView();
abstract public function renderListView(); abstract public function renderListView();
protected function newItemFromProperties(
NuanceRequestor $requestor,
array $properties,
PhabricatorContentSource $content_source) {
// TODO: Should we have a tighter actor/viewer model? Requestors will
// often have no real user associated with them...
$actor = PhabricatorUser::getOmnipotentUser();
$source = $this->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();
}
} }

View file

@ -13,11 +13,11 @@ final class NuanceItem
protected $requestorPHID; protected $requestorPHID;
protected $sourcePHID; protected $sourcePHID;
protected $sourceLabel; protected $sourceLabel;
protected $data; protected $data = array();
protected $mailKey; protected $mailKey;
protected $dateNuanced; protected $dateNuanced;
public static function initializeNewItem(PhabricatorUser $user) { public static function initializeNewItem() {
return id(new NuanceItem()) return id(new NuanceItem())
->setDateNuanced(time()) ->setDateNuanced(time())
->setStatus(self::STATUS_OPEN); ->setStatus(self::STATUS_OPEN);
@ -94,6 +94,15 @@ final class NuanceItem
$this->source = $source; $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() { public function getCapabilities() {
return array( return array(
PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_VIEW,

View file

@ -3,9 +3,12 @@
final class NuanceItemTransaction final class NuanceItemTransaction
extends NuanceTransaction { extends NuanceTransaction {
const TYPE_OWNER = 'item-owner'; const PROPERTY_KEY = 'property.key';
const TYPE_REQUESTOR = 'item-requestor';
const TYPE_SOURCE = 'item-source'; 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() { public function getApplicationTransactionType() {
return NuanceItemPHIDType::TYPECONST; return NuanceItemPHIDType::TYPECONST;

View file

@ -1,9 +1,12 @@
<?php <?php
final class NuanceRequestor final class NuanceRequestor
extends NuanceDAO { extends NuanceDAO
implements
PhabricatorPolicyInterface,
PhabricatorApplicationTransactionInterface {
protected $data; protected $data = array();
protected function getConfiguration() { protected function getConfiguration() {
return array( return array(
@ -19,11 +22,8 @@ final class NuanceRequestor
NuanceRequestorPHIDType::TYPECONST); NuanceRequestorPHIDType::TYPECONST);
} }
public function save() { public static function initializeNewRequestor() {
if (!$this->getMailKey()) { return new NuanceRequestor();
$this->setMailKey(Filesystem::readRandomCharacters(20));
}
return parent::save();
} }
public function getURI() { public function getURI() {
@ -34,4 +34,104 @@ final class NuanceRequestor
return idx($this->getData(), 'phabricatorUserPHID'); 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;
}
} }

View file

@ -3,6 +3,10 @@
final class NuanceRequestorTransaction final class NuanceRequestorTransaction
extends NuanceTransaction { extends NuanceTransaction {
const PROPERTY_KEY = 'property.key';
const TYPE_PROPERTY = 'nuance.requestor.property';
public function getApplicationTransactionType() { public function getApplicationTransactionType() {
return NuanceRequestorPHIDType::TYPECONST; return NuanceRequestorPHIDType::TYPECONST;
} }