Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/nuance/controller/NuanceItemViewController.php b/src/applications/nuance/controller/NuanceItemViewController.php
index 0d20c03cda..fd8fbc4563 100644
--- a/src/applications/nuance/controller/NuanceItemViewController.php
+++ b/src/applications/nuance/controller/NuanceItemViewController.php
@@ -1,85 +1,72 @@
<?php
final class NuanceItemViewController extends NuanceController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$id = $request->getURIData('id');
$item = id(new NuanceItemQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$item) {
return new Aphront404Response();
}
$title = pht('Item %d', $item->getID());
+ $name = $item->getDisplayName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Items'),
$this->getApplicationURI('item/'));
$crumbs->addTextCrumb($title);
$crumbs->setBorder(true);
- $properties = $this->buildPropertyView($item);
$curtain = $this->buildCurtain($item);
+ $content = $this->buildContent($item);
$header = id(new PHUIHeaderView())
- ->setHeader($title);
+ ->setHeader($name);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
- ->addPropertySection(pht('DETAILS'), $properties);
+ ->setMainColumn($content);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
- private function buildPropertyView(NuanceItem $item) {
- $viewer = $this->getViewer();
-
- $properties = id(new PHUIPropertyListView())
- ->setUser($viewer);
-
- $properties->addProperty(
- pht('Date Created'),
- phabricator_datetime($item->getDateCreated(), $viewer));
-
- $source = $item->getSource();
- $definition = $source->getDefinition();
-
- $definition->renderItemViewProperties(
- $viewer,
- $item,
- $properties);
-
- return $properties;
- }
-
private function buildCurtain(NuanceItem $item) {
$viewer = $this->getViewer();
$id = $item->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$item,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain = $this->newCurtainView($item);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Manage Item'))
->setIcon('fa-cogs')
->setHref($this->getApplicationURI("item/manage/{$id}/")));
return $curtain;
}
+ private function buildContent(NuanceItem $item) {
+ $viewer = $this->getViewer();
+ $impl = $item->getImplementation();
+
+ $impl->setViewer($viewer);
+ return $impl->buildItemView($item);
+ }
}
diff --git a/src/applications/nuance/cursor/NuanceGitHubRepositoryImportCursor.php b/src/applications/nuance/cursor/NuanceGitHubRepositoryImportCursor.php
index e65863d4af..69e53b8dd0 100644
--- a/src/applications/nuance/cursor/NuanceGitHubRepositoryImportCursor.php
+++ b/src/applications/nuance/cursor/NuanceGitHubRepositoryImportCursor.php
@@ -1,258 +1,261 @@
<?php
final class NuanceGitHubRepositoryImportCursor
extends NuanceImportCursor {
const CURSORTYPE = 'github.repository';
protected function shouldPullDataFromSource() {
$now = PhabricatorTime::getNow();
// Respect GitHub's poll interval header. If we made a request recently,
// don't make another one until we've waited long enough.
$ttl = $this->getCursorProperty('github.poll.ttl');
if ($ttl && ($ttl >= $now)) {
$this->logInfo(
pht(
'Respecting "%s": waiting for %s second(s) to poll GitHub.',
'X-Poll-Interval',
new PhutilNumber(1 + ($ttl - $now))));
return false;
}
// Respect GitHub's API rate limiting. If we've exceeded the rate limit,
// wait until it resets to try again.
$limit = $this->getCursorProperty('github.limit.ttl');
if ($limit && ($limit >= $now)) {
$this->logInfo(
pht(
'Respecting "%s": waiting for %s second(s) to poll GitHub.',
'X-RateLimit-Reset',
new PhutilNumber(1 + ($limit - $now))));
return false;
}
return true;
}
protected function pullDataFromSource() {
$viewer = $this->getViewer();
$now = PhabricatorTime::getNow();
$source = $this->getSource();
$user = $source->getSourceProperty('github.user');
$repository = $source->getSourceProperty('github.repository');
$api_token = $source->getSourceProperty('github.token');
// This API only supports fetching 10 pages of 30 events each, for a total
// of 300 events.
$etag = null;
$new_items = array();
$hit_known_items = false;
for ($page = 1; $page <= 10; $page++) {
$uri = "/repos/{$user}/{$repository}/events";
$data = array(
'page' => $page,
);
$future = id(new PhutilGitHubFuture())
->setAccessToken($api_token)
->setRawGitHubQuery($uri, $data);
if ($page == 1) {
$cursor_etag = $this->getCursorProperty('github.poll.etag');
if ($cursor_etag) {
$future->addHeader('If-None-Match', $cursor_etag);
}
}
$this->logInfo(
pht(
'Polling GitHub Repository API endpoint "%s".',
$uri));
$response = $future->resolve();
// Do this first: if we hit the rate limit, we get a response but the
// body isn't valid.
$this->updateRateLimits($response);
if ($response->getStatus()->getStatusCode() == 304) {
$this->logInfo(
pht(
'Received a 304 Not Modified from GitHub, no new events.'));
}
// This means we hit a rate limit or a "Not Modified" because of the
// "ETag" header. In either case, we should bail out.
if ($response->getStatus()->isError()) {
$this->updatePolling($response, $now, false);
$this->getCursorData()->save();
return false;
}
if ($page == 1) {
$etag = $response->getHeaderValue('ETag');
}
$records = $response->getBody();
foreach ($records as $record) {
$item = $this->newNuanceItemFromGitHubEvent($record);
$item_key = $item->getItemKey();
$this->logInfo(
pht(
'Fetched event "%s".',
$item_key));
$new_items[$item->getItemKey()] = $item;
}
if ($new_items) {
$existing = id(new NuanceItemQuery())
->setViewer($viewer)
->withSourcePHIDs(array($source->getPHID()))
->withItemKeys(array_keys($new_items))
->execute();
$existing = mpull($existing, null, 'getItemKey');
foreach ($new_items as $key => $new_item) {
if (isset($existing[$key])) {
unset($new_items[$key]);
$hit_known_items = true;
$this->logInfo(
pht(
'Event "%s" is previously known.',
$key));
}
}
}
if ($hit_known_items) {
break;
}
if (count($records) < 30) {
break;
}
}
// TODO: When we go through the whole queue without hitting anything we
// have seen before, we should record some sort of global event so we
// can tell the user when the bridging started or was interrupted?
if (!$hit_known_items) {
$already_polled = $this->getCursorProperty('github.polled');
if ($already_polled) {
// TODO: This is bad: we missed some items, maybe because too much
// stuff happened too fast or the daemons were broken for a long
// time.
} else {
// TODO: This is OK, we're doing the initial import.
}
}
if ($etag !== null) {
$this->updateETag($etag);
}
$this->updatePolling($response, $now, true);
+ // Reverse the new items so we insert them in chronological order.
+ $new_items = array_reverse($new_items);
+
$source->openTransaction();
foreach ($new_items as $new_item) {
$new_item->save();
}
$this->getCursorData()->save();
$source->saveTransaction();
foreach ($new_items as $new_item) {
$new_item->scheduleUpdate();
}
return false;
}
private function updateRateLimits(PhutilGitHubResponse $response) {
$remaining = $response->getHeaderValue('X-RateLimit-Remaining');
$limit_reset = $response->getHeaderValue('X-RateLimit-Reset');
$now = PhabricatorTime::getNow();
$limit_ttl = null;
if (strlen($remaining)) {
$remaining = (int)$remaining;
if (!$remaining) {
$limit_ttl = (int)$limit_reset;
}
}
$this->setCursorProperty('github.limit.ttl', $limit_ttl);
$this->logInfo(
pht(
'This key has %s remaining API request(s), '.
'limit resets in %s second(s).',
new PhutilNumber($remaining),
new PhutilNumber($limit_reset - $now)));
}
private function updateETag($etag) {
$this->setCursorProperty('github.poll.etag', $etag);
$this->logInfo(
pht(
'ETag for this request was "%s".',
$etag));
}
private function updatePolling(
PhutilGitHubResponse $response,
$start,
$success) {
if ($success) {
$this->setCursorProperty('github.polled', true);
}
$poll_interval = (int)$response->getHeaderValue('X-Poll-Interval');
$poll_ttl = $start + $poll_interval;
$this->setCursorProperty('github.poll.ttl', $poll_ttl);
$now = PhabricatorTime::getNow();
$this->logInfo(
pht(
'Set API poll TTL to +%s second(s) (%s second(s) from now).',
new PhutilNumber($poll_interval),
new PhutilNumber($poll_ttl - $now)));
}
private function newNuanceItemFromGitHubEvent(array $record) {
$source = $this->getSource();
$id = $record['id'];
$item_key = "github.event.{$id}";
$container_key = null;
$issue_id = idxv(
$record,
array(
'payload',
'issue',
'id',
));
if ($issue_id) {
$container_key = "github.issue.{$issue_id}";
}
return NuanceItem::initializeNewItem()
->setStatus(NuanceItem::STATUS_IMPORTING)
->setSourcePHID($source->getPHID())
->setItemType(NuanceGitHubEventItemType::ITEMTYPE)
->setItemKey($item_key)
->setItemContainerKey($container_key)
->setItemProperty('api.raw', $record);
}
}
diff --git a/src/applications/nuance/item/NuanceGitHubEventItemType.php b/src/applications/nuance/item/NuanceGitHubEventItemType.php
index d292846e83..41e1e40a27 100644
--- a/src/applications/nuance/item/NuanceGitHubEventItemType.php
+++ b/src/applications/nuance/item/NuanceGitHubEventItemType.php
@@ -1,22 +1,65 @@
<?php
final class NuanceGitHubEventItemType
extends NuanceItemType {
const ITEMTYPE = 'github.event';
+ public function getItemTypeDisplayName() {
+ return pht('GitHub Event');
+ }
+
+ public function getItemTypeDisplayIcon() {
+ return 'fa-github';
+ }
+
+ public function getItemDisplayName(NuanceItem $item) {
+ $raw = $item->getItemProperty('api.raw', array());
+
+ $repo = idxv($raw, array('repo', 'name'), pht('<unknown/unknown>'));
+
+ $type = idx($raw, 'type');
+ switch ($type) {
+ case 'PushEvent':
+ $head = idxv($raw, array('payload', 'head'));
+ $head = substr($head, 0, 8);
+ $name = pht('Push %s', $head);
+ break;
+ case 'IssuesEvent':
+ $action = idxv($raw, array('payload', 'action'));
+ $number = idxv($raw, array('payload', 'issue', 'number'));
+ $name = pht('Issue #%d (%s)', $number, $action);
+ break;
+ case 'IssueCommentEvent':
+ $action = idxv($raw, array('payload', 'action'));
+ $number = idxv($raw, array('payload', 'issue', 'number'));
+ $name = pht('Issue #%d (Comment, %s)', $number, $action);
+ break;
+ case 'PullRequestEvent':
+ $action = idxv($raw, array('payload', 'action'));
+ $number = idxv($raw, array('payload', 'pull_request', 'number'));
+ $name = pht('Pull Request #%d (%s)', $number, $action);
+ break;
+ default:
+ $name = pht('Unknown Event ("%s")', $type);
+ break;
+ }
+
+ return pht('GitHub %s %s', $repo, $name);
+ }
+
public function canUpdateItems() {
return true;
}
protected function updateItemFromSource(NuanceItem $item) {
// TODO: Link up the requestor, etc.
if ($item->getStatus() == NuanceItem::STATUS_IMPORTING) {
$item
->setStatus(NuanceItem::STATUS_ROUTING)
->save();
}
}
}
diff --git a/src/applications/nuance/item/NuanceItemType.php b/src/applications/nuance/item/NuanceItemType.php
index e0e0c739f1..1e0cc7a95c 100644
--- a/src/applications/nuance/item/NuanceItemType.php
+++ b/src/applications/nuance/item/NuanceItemType.php
@@ -1,37 +1,63 @@
<?php
abstract class NuanceItemType
extends Phobject {
+ private $viewer;
+
+ public function setViewer(PhabricatorUser $viewer) {
+ $this->viewer = $viewer;
+ return $this;
+ }
+
+ public function getViewer() {
+ return $this->viewer;
+ }
+
public function canUpdateItems() {
return false;
}
+ final public function buildItemView(NuanceItem $item) {
+ return $this->newItemView($item);
+ }
+
+ protected function newItemView() {
+ return null;
+ }
+
+ public function getItemTypeDisplayIcon() {
+ return null;
+ }
+
+ abstract public function getItemTypeDisplayName();
+ abstract public function getItemDisplayName(NuanceItem $item);
+
final public function updateItem(NuanceItem $item) {
if (!$this->canUpdateItems()) {
throw new Exception(
pht(
'This item type ("%s", of class "%s") can not update items.',
$this->getItemTypeConstant(),
get_class($this)));
}
$this->updateItemFromSource($item);
}
protected function updateItemFromSource(NuanceItem $item) {
throw new PhutilMethodNotImplementedException();
}
final public function getItemTypeConstant() {
return $this->getPhobjectClassConstant('ITEMTYPE', 64);
}
final public static function getAllItemTypes() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getItemTypeConstant')
->execute();
}
}
diff --git a/src/applications/nuance/phid/NuanceItemPHIDType.php b/src/applications/nuance/phid/NuanceItemPHIDType.php
index f401c63594..ee51633ea9 100644
--- a/src/applications/nuance/phid/NuanceItemPHIDType.php
+++ b/src/applications/nuance/phid/NuanceItemPHIDType.php
@@ -1,41 +1,41 @@
<?php
final class NuanceItemPHIDType extends PhabricatorPHIDType {
const TYPECONST = 'NUAI';
public function getTypeName() {
return pht('Item');
}
public function newObject() {
return new NuanceItem();
}
public function getPHIDTypeApplicationClass() {
return 'PhabricatorNuanceApplication';
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new NuanceItemQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
$viewer = $query->getViewer();
foreach ($handles as $phid => $handle) {
$item = $objects[$phid];
- $handle->setName($item->getLabel($viewer));
+ $handle->setName($item->getItemDisplayName());
$handle->setURI($item->getURI());
}
}
}
diff --git a/src/applications/nuance/query/NuanceItemSearchEngine.php b/src/applications/nuance/query/NuanceItemSearchEngine.php
index cbc0e9f321..2f0951b4e0 100644
--- a/src/applications/nuance/query/NuanceItemSearchEngine.php
+++ b/src/applications/nuance/query/NuanceItemSearchEngine.php
@@ -1,81 +1,85 @@
<?php
final class NuanceItemSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getApplicationClassName() {
return 'PhabricatorNuanceApplication';
}
public function getResultTypeDescription() {
return pht('Nuance Items');
}
public function newQuery() {
return new NuanceItemQuery();
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
return $query;
}
protected function buildCustomSearchFields() {
return array(
);
}
protected function getURI($path) {
return '/nuance/item/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'all' => pht('All Items'),
);
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $items,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($items, 'NuanceItem');
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
$list->setUser($viewer);
foreach ($items as $item) {
+ $impl = $item->getImplementation();
+
$view = id(new PHUIObjectItemView())
->setObjectName(pht('Item %d', $item->getID()))
->setHeader($item->getDisplayName())
->setHref($item->getURI());
- $view->addIcon('none', $item->getItemType());
+ $view->addIcon(
+ $impl->getItemTypeDisplayIcon(),
+ $impl->getItemTypeDisplayName());
$list->addItem($view);
}
$result = new PhabricatorApplicationSearchResultView();
$result->setObjectList($list);
$result->setNoDataString(pht('No items found.'));
return $result;
}
}
diff --git a/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php b/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php
index ce7891f61e..a54211ee95 100644
--- a/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php
+++ b/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php
@@ -1,101 +1,94 @@
<?php
final class NuancePhabricatorFormSourceDefinition
extends NuanceSourceDefinition {
public function getName() {
return pht('Phabricator Form');
}
public function getSourceDescription() {
return pht('Create a web form that submits into a Nuance queue.');
}
public function getSourceTypeConstant() {
return 'phabricator-form';
}
public function getSourceViewActions(AphrontRequest $request) {
$actions = array();
$actions[] = id(new PhabricatorActionView())
->setName(pht('View Form'))
->setIcon('fa-align-justify')
->setHref($this->getActionURI());
return $actions;
}
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('complaint'),
);
$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;
}
- public function renderItemViewProperties(
- PhabricatorUser $viewer,
- NuanceItem $item,
- PHUIPropertyListView $view) {
- $this->renderItemCommonProperties($viewer, $item, $view);
- }
-
public function renderItemEditProperties(
PhabricatorUser $viewer,
NuanceItem $item,
PHUIPropertyListView $view) {
$this->renderItemCommonProperties($viewer, $item, $view);
}
private function renderItemCommonProperties(
PhabricatorUser $viewer,
NuanceItem $item,
PHUIPropertyListView $view) {
$complaint = $item->getNuanceProperty('complaint');
$complaint = new PHUIRemarkupView($viewer, $complaint);
$view->addSectionHeader(
pht('Complaint'), 'fa-exclamation-circle');
$view->addTextContent($complaint);
}
}
diff --git a/src/applications/nuance/source/NuanceSourceDefinition.php b/src/applications/nuance/source/NuanceSourceDefinition.php
index e5168c3040..3552c8fe28 100644
--- a/src/applications/nuance/source/NuanceSourceDefinition.php
+++ b/src/applications/nuance/source/NuanceSourceDefinition.php
@@ -1,223 +1,216 @@
<?php
/**
* @task action Handling Action Requests
*/
abstract class NuanceSourceDefinition extends Phobject {
private $viewer;
private $source;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
if (!$this->viewer) {
throw new PhutilInvalidStateException('setViewer');
}
return $this->viewer;
}
public function setSource(NuanceSource $source) {
$this->source = $source;
return $this;
}
public function getSource() {
if (!$this->source) {
throw new PhutilInvalidStateException('setSource');
}
return $this->source;
}
public function getSourceViewActions(AphrontRequest $request) {
return array();
}
public static function getAllDefinitions() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getSourceTypeConstant')
->execute();
}
public function hasImportCursors() {
return false;
}
final public function getImportCursors() {
if (!$this->hasImportCursors()) {
throw new Exception(
pht('This source has no input cursors.'));
}
$viewer = PhabricatorUser::getOmnipotentUser();
$source = $this->getSource();
$cursors = $this->newImportCursors();
$data = id(new NuanceImportCursorDataQuery())
->setViewer($viewer)
->withSourcePHIDs(array($source->getPHID()))
->execute();
$data = mpull($data, null, 'getCursorKey');
$map = array();
foreach ($cursors as $cursor) {
if (!($cursor instanceof NuanceImportCursor)) {
throw new Exception(
pht(
'Source "%s" (of class "%s") returned an invalid value from '.
'method "%s": all values must be objects of class "%s".',
$this->getName(),
get_class($this),
'newImportCursors()',
'NuanceImportCursor'));
}
$key = $cursor->getCursorKey();
if (!strlen($key)) {
throw new Exception(
pht(
'Source "%s" (of class "%s") returned an import cursor with '.
'a missing key from "%s". Each cursor must have a unique, '.
'nonempty key.',
$this->getName(),
get_class($this),
'newImportCursors()'));
}
$other = idx($map, $key);
if ($other) {
throw new Exception(
pht(
'Source "%s" (of class "%s") returned two cursors from method '.
'"%s" with the same key ("%s"). Each cursor must have a unique '.
'key.',
$this->getName(),
get_class($this),
'newImportCursors()',
$key));
}
$map[$key] = $cursor;
$cursor_data = idx($data, $key);
if (!$cursor_data) {
$cursor_data = $cursor->newEmptyCursorData($source);
}
$cursor
->setViewer($viewer)
->setSource($source)
->setCursorData($cursor_data);
}
return $cursors;
}
protected function newImportCursors() {
throw new PhutilMethodNotImplementedException();
}
/**
* A human readable string like "Twitter" or "Phabricator Form".
*/
abstract public function getName();
/**
* Human readable description of this source, a sentence or two long.
*/
abstract public function getSourceDescription();
/**
* This should be a any VARCHAR(32).
*
* @{method:getAllDefinitions} will throw if you choose a string that
* collides with another @{class:NuanceSourceDefinition} class.
*/
abstract public function getSourceTypeConstant();
public function renderView() {
return null;
}
public function renderListView() {
return null;
}
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->getSource();
$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());
// TODO: Eventually, apply real routing rules. For now, just put everything
// in the default queue for the source.
$xactions[] = id(new NuanceItemTransaction())
->setTransactionType(NuanceItemTransaction::TYPE_QUEUE)
->setNewValue($source->getDefaultQueuePHID());
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;
}
- public function renderItemViewProperties(
- PhabricatorUser $viewer,
- NuanceItem $item,
- PHUIPropertyListView $view) {
- return;
- }
-
public function renderItemEditProperties(
PhabricatorUser $viewer,
NuanceItem $item,
PHUIPropertyListView $view) {
return;
}
/* -( Handling Action Requests )------------------------------------------- */
public function handleActionRequest(AphrontRequest $request) {
return new Aphront404Response();
}
public function getActionURI($path = null) {
$source_id = $this->getSource()->getID();
return '/action/'.$source_id.'/'.ltrim($path, '/');
}
}
diff --git a/src/applications/nuance/storage/NuanceItem.php b/src/applications/nuance/storage/NuanceItem.php
index 1e2380822b..a83db3ec70 100644
--- a/src/applications/nuance/storage/NuanceItem.php
+++ b/src/applications/nuance/storage/NuanceItem.php
@@ -1,192 +1,188 @@
<?php
final class NuanceItem
extends NuanceDAO
implements
PhabricatorPolicyInterface,
PhabricatorApplicationTransactionInterface {
const STATUS_IMPORTING = 'importing';
const STATUS_ROUTING = 'routing';
const STATUS_OPEN = 'open';
const STATUS_ASSIGNED = 'assigned';
const STATUS_CLOSED = 'closed';
protected $status;
protected $ownerPHID;
protected $requestorPHID;
protected $sourcePHID;
protected $queuePHID;
protected $itemType;
protected $itemKey;
protected $itemContainerKey;
protected $data = array();
protected $mailKey;
private $source = self::ATTACHABLE;
private $implementation = self::ATTACHABLE;
public static function initializeNewItem() {
return id(new NuanceItem())
->setStatus(self::STATUS_OPEN);
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'data' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'ownerPHID' => 'phid?',
'requestorPHID' => 'phid?',
'queuePHID' => 'phid?',
'itemType' => 'text64',
'itemKey' => 'text64',
'itemContainerKey' => 'text64?',
'status' => 'text32',
'mailKey' => 'bytes20',
),
self::CONFIG_KEY_SCHEMA => array(
'key_source' => array(
'columns' => array('sourcePHID', 'status'),
),
'key_owner' => array(
'columns' => array('ownerPHID', 'status'),
),
'key_requestor' => array(
'columns' => array('requestorPHID', 'status'),
),
'key_queue' => array(
'columns' => array('queuePHID', 'status'),
),
'key_container' => array(
'columns' => array('sourcePHID', 'itemContainerKey'),
),
'key_item' => array(
'columns' => array('sourcePHID', 'itemKey'),
'unique' => true,
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
NuanceItemPHIDType::TYPECONST);
}
public function save() {
if (!$this->getMailKey()) {
$this->setMailKey(Filesystem::readRandomCharacters(20));
}
return parent::save();
}
public function getURI() {
return '/nuance/item/view/'.$this->getID().'/';
}
- public function getLabel(PhabricatorUser $viewer) {
- return pht('TODO: An Item');
- }
-
public function getRequestor() {
return $this->assertAttached($this->requestor);
}
public function attachRequestor(NuanceRequestor $requestor) {
return $this->requestor = $requestor;
}
public function getSource() {
return $this->assertAttached($this->source);
}
public function attachSource(NuanceSource $source) {
$this->source = $source;
}
public function getItemProperty($key, $default = null) {
return idx($this->data, $key, $default);
}
public function setItemProperty($key, $value) {
$this->data[$key] = $value;
return $this;
}
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
// TODO - this should be based on the queues the item currently resides in
return PhabricatorPolicies::POLICY_USER;
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
// TODO - requestors should get auto access too!
return $viewer->getPHID() == $this->ownerPHID;
}
public function describeAutomaticCapability($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return pht('Owners of an item can always view it.');
case PhabricatorPolicyCapability::CAN_EDIT:
return pht('Owners of an item can always edit it.');
}
return null;
}
public function getDisplayName() {
- return pht('An Item');
+ return $this->getImplementation()->getItemDisplayName($this);
}
public function scheduleUpdate() {
PhabricatorWorker::scheduleTask(
'NuanceItemUpdateWorker',
array(
'itemPHID' => $this->getPHID(),
),
array(
'objectPHID' => $this->getPHID(),
));
}
public function getImplementation() {
return $this->assertAttached($this->implementation);
}
public function attachImplementation(NuanceItemType $type) {
$this->implementation = $type;
return $this;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new NuanceItemEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new NuanceItemTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jul 1, 3:24 PM (14 h, 34 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
164408
Default Alt Text
(32 KB)

Event Timeline