1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-21 21:10:56 +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',
'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 @@ phutil_register_library_map(array(
'NuanceQueueTransactionComment' => 'PhabricatorApplicationTransactionComment',
'NuanceQueueTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'NuanceQueueViewController' => 'NuanceController',
'NuanceRequestor' => 'NuanceDAO',
'NuanceRequestor' => array(
'NuanceDAO',
'PhabricatorPolicyInterface',
'PhabricatorApplicationTransactionInterface',
),
'NuanceRequestorEditController' => 'NuanceController',
'NuanceRequestorEditor' => 'PhabricatorApplicationTransactionEditor',
'NuanceRequestorPHIDType' => 'PhabricatorPHIDType',
@ -4489,6 +4494,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface',
),
'NuanceSourceActionController' => 'NuanceController',
'NuanceSourceCreateController' => 'NuanceController',
'NuanceSourceDefaultEditCapability' => 'PhabricatorPolicyCapability',
'NuanceSourceDefaultViewCapability' => 'PhabricatorPolicyCapability',

View file

@ -62,6 +62,9 @@ final class PhabricatorNuanceApplication extends PhabricatorApplication {
'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();
$item = NuanceItem::initializeNewItem($user);
$item = NuanceItem::initializeNewItem();
$xactions = array();
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_SOURCE;
$types[] = NuanceItemTransaction::TYPE_REQUESTOR;
$types[] = NuanceItemTransaction::TYPE_PROPERTY;
$types[] = PhabricatorTransactions::TYPE_EDGE;
$types[] = PhabricatorTransactions::TYPE_COMMENT;
@ -37,6 +38,10 @@ final class NuanceItemEditor
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 @@ final class NuanceItemEditor
case NuanceItemTransaction::TYPE_REQUESTOR:
case NuanceItemTransaction::TYPE_SOURCE:
case NuanceItemTransaction::TYPE_OWNER:
case NuanceItemTransaction::TYPE_PROPERTY:
return $xaction->getNewValue();
}
@ -70,6 +76,11 @@ final class NuanceItemEditor
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 @@ final class NuanceItemEditor
case NuanceItemTransaction::TYPE_REQUESTOR:
case NuanceItemTransaction::TYPE_SOURCE:
case NuanceItemTransaction::TYPE_OWNER:
case NuanceItemTransaction::TYPE_PROPERTY:
return;
}

View file

@ -14,12 +14,62 @@ final class NuanceRequestorEditor
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);
}
}

View file

@ -5,7 +5,7 @@ final class NuanceItemQuery
private $ids;
private $phids;
private $sourceIDs;
private $sourcePHIDs;
public function withIDs(array $ids) {
$this->ids = $ids;
@ -17,54 +17,52 @@ final class NuanceItemQuery
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();
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
$where[] = $this->buildPagingClause($conn_r);
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;
}
}

View file

@ -42,4 +42,51 @@ final class NuancePhabricatorFormSourceDefinition
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
/**
* @task action Handling Action Requests
*/
abstract class NuanceSourceDefinition extends Phobject {
private $actor;
@ -254,4 +257,54 @@ abstract class NuanceSourceDefinition extends Phobject {
abstract public function renderView();
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 $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 @@ final class NuanceItem
$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,

View file

@ -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;

View file

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