Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13958101
D7723.id17438.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
34 KB
Referenced Files
None
Subscribers
None
D7723.id17438.diff
View Options
Index: src/__celerity_resource_map__.php
===================================================================
--- src/__celerity_resource_map__.php
+++ src/__celerity_resource_map__.php
@@ -1969,6 +1969,20 @@
),
'disk' => '/rsrc/js/application/maniphest/behavior-transaction-preview.js',
),
+ 'javelin-behavior-nuance-source-editor' =>
+ array(
+ 'uri' => '/res/b201c675/rsrc/js/application/nuance/nuance-source-editor.js',
+ 'type' => 'js',
+ 'requires' =>
+ array(
+ 0 => 'javelin-dom',
+ 1 => 'javelin-workflow',
+ 2 => 'javelin-behavior',
+ 3 => 'javelin-stratcom',
+ 4 => 'javelin-uri',
+ ),
+ 'disk' => '/rsrc/js/application/nuance/nuance-source-editor.js',
+ ),
'javelin-behavior-owners-path-editor' =>
array(
'uri' => '/res/9cf78ffc/rsrc/js/application/owners/owners-path-editor.js',
Index: src/__phutil_library_map__.php
===================================================================
--- src/__phutil_library_map__.php
+++ src/__phutil_library_map__.php
@@ -886,7 +886,7 @@
'NuancePHIDTypeQueue' => 'applications/nuance/phid/NuancePHIDTypeQueue.php',
'NuancePHIDTypeRequestor' => 'applications/nuance/phid/NuancePHIDTypeRequestor.php',
'NuancePHIDTypeSource' => 'applications/nuance/phid/NuancePHIDTypeSource.php',
- 'NuancePhabricatorFormSourceDefinition' => 'applications/nuance/source/NuancePhabricatorFormSourceDefinition.php',
+ 'NuancePhabricatorFormSourceDefinition' => 'applications/nuance/source/definition/NuancePhabricatorFormSourceDefinition.php',
'NuanceQuery' => 'applications/nuance/query/NuanceQuery.php',
'NuanceQueue' => 'applications/nuance/storage/NuanceQueue.php',
'NuanceQueueEditController' => 'applications/nuance/controller/NuanceQueueEditController.php',
@@ -907,7 +907,7 @@
'NuanceRequestorTransactionQuery' => 'applications/nuance/query/NuanceRequestorTransactionQuery.php',
'NuanceRequestorViewController' => 'applications/nuance/controller/NuanceRequestorViewController.php',
'NuanceSource' => 'applications/nuance/storage/NuanceSource.php',
- 'NuanceSourceDefinition' => 'applications/nuance/source/NuanceSourceDefinition.php',
+ 'NuanceSourceDefinition' => 'applications/nuance/source/definition/NuanceSourceDefinition.php',
'NuanceSourceEditController' => 'applications/nuance/controller/NuanceSourceEditController.php',
'NuanceSourceEditor' => 'applications/nuance/editor/NuanceSourceEditor.php',
'NuanceSourceQuery' => 'applications/nuance/query/NuanceSourceQuery.php',
@@ -916,6 +916,10 @@
'NuanceSourceTransactionQuery' => 'applications/nuance/query/NuanceSourceTransactionQuery.php',
'NuanceSourceViewController' => 'applications/nuance/controller/NuanceSourceViewController.php',
'NuanceTransaction' => 'applications/nuance/storage/NuanceTransaction.php',
+ 'NuanceTwitterPublicStreamSourceDefinition' => 'applications/nuance/source/definition/NuanceTwitterPublicStreamSourceDefinition.php',
+ 'NuanceTwitterSourceBot' => 'applications/nuance/source/bot/NuanceTwitterSourceBot.php',
+ 'NuanceTwitterSourceDefinition' => 'applications/nuance/source/definition/NuanceTwitterSourceDefinition.php',
+ 'NuanceTwitterUserStreamSourceDefinition' => 'applications/nuance/source/definition/NuanceTwitterUserStreamSourceDefinition.php',
'OwnersPackageReplyHandler' => 'applications/owners/mail/OwnersPackageReplyHandler.php',
'PHUI' => 'view/phui/PHUI.php',
'PHUIBoxExample' => 'applications/uiexample/examples/PHUIBoxExample.php',
@@ -3340,6 +3344,10 @@
'NuanceSourceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'NuanceSourceViewController' => 'NuanceController',
'NuanceTransaction' => 'PhabricatorApplicationTransaction',
+ 'NuanceTwitterPublicStreamSourceDefinition' => 'NuanceTwitterSourceDefinition',
+ 'NuanceTwitterSourceBot' => 'PhabricatorDaemon',
+ 'NuanceTwitterSourceDefinition' => 'NuanceSourceDefinition',
+ 'NuanceTwitterUserStreamSourceDefinition' => 'NuanceTwitterSourceDefinition',
'OwnersPackageReplyHandler' => 'PhabricatorMailReplyHandler',
'PHUIBoxExample' => 'PhabricatorUIExample',
'PHUIBoxView' => 'AphrontTagView',
Index: src/applications/nuance/controller/NuanceSourceEditController.php
===================================================================
--- src/applications/nuance/controller/NuanceSourceEditController.php
+++ src/applications/nuance/controller/NuanceSourceEditController.php
@@ -19,32 +19,67 @@
public function processRequest() {
$can_edit = $this->requireApplicationCapability(
NuanceCapabilitySourceManage::CAPABILITY);
-
$request = $this->getRequest();
$user = $request->getUser();
+ $source = $this->loadOrCreateSourceObject($user);
+ if (!$source) {
+ return new Aphront404Response();
+ }
- $source_id = $this->getSourceID();
- $is_new = !$source_id;
+ if ($request->isAjax()) {
+ return $this->processAjaxRequest($source);
+ }
- if ($is_new) {
- $source = NuanceSource::initializeNewSource($user);
- } else {
- $source = id(new NuanceSourceQuery())
- ->setViewer($user)
- ->withIDs(array($source_id))
- ->requireCapabilities(
- array(
- PhabricatorPolicyCapability::CAN_VIEW,
- PhabricatorPolicyCapability::CAN_EDIT,
- ))
- ->executeOne();
+ return $this->processEditRequest($source);
+ }
+
+ /**
+ * This is just to dynamically update the edit form.
+ */
+ private function processAjaxRequest(NuanceSource $source) {
+ $request = $this->getRequest();
+ $user = $request->getUser();
+
+ if ($request->getBool('warn', false)) {
+ $dialog = id(new AphrontDialogView())
+ ->setUser($user)
+ ->setTitle(pht('Warning - form data could be lost.'))
+ ->appendParagraph(pht(
+ 'Edits to this form other than the name will not be preserved if '.
+ 'you switch source type. '.
+ 'However, changes will not be saved until you click "save". '.
+ 'Are you sure you want to change source type?'))
+ ->addSubmitButton('I am sure.')
+ ->addCancelButton($source->getEditURI());
+ return id(new AphrontDialogResponse())
+ ->setDialog($dialog);
}
- if (!$source) {
- return new Aphront404Response();
+ return id(new AphrontAjaxResponse())
+ ->setContent(pht('You are cleared for submission ghost rider.'));
+ }
+
+ private function processEditRequest(NuanceSource $source) {
+ $request = $this->getRequest();
+ $user = $request->getUser();
+
+ // this handles if the user just changes the source type, which does a
+ // quick re-direct and preserves the new type and name
+ if (!$request->isFormPost() && $request->getExists('redraw')) {
+ $source->setType($request->getStr('type'));
+ $source->setName($request->getStr('name'));
}
- $definition = NuanceSourceDefinition::getDefinitionForSource($source);
+ // this handles if the user just changed the source type *AND* is trying
+ // to save the update. we need to use the new definition immediately, but
+ // need to let the editor handle actually update the type on the source.
+ if ($request->isFormPost()) {
+ $definition = NuanceSourceDefinition::getDefinitionForSourceType(
+ $request->getStr('type'));
+ $definition->setSourceObject($source);
+ } else {
+ $definition = NuanceSourceDefinition::getDefinitionForSource($source);
+ }
$definition->setActor($user);
$response = $definition->buildEditLayout($request);
@@ -63,4 +98,26 @@
'title' => $definition->getEditTitle(),
'device' => true));
}
+
+ private function loadOrCreateSourceObject(PhabricatorUser $user) {
+ $source_id = $this->getSourceID();
+ $is_new = !$source_id;
+
+ if (!$source_id) {
+ $source = NuanceSource::initializeNewSource($user);
+ } else {
+ $source = id(new NuanceSourceQuery())
+ ->setViewer($user)
+ ->withIDs(array($source_id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ }
+
+ return $source;
+ }
+
}
Index: src/applications/nuance/editor/NuanceSourceEditor.php
===================================================================
--- src/applications/nuance/editor/NuanceSourceEditor.php
+++ src/applications/nuance/editor/NuanceSourceEditor.php
@@ -7,6 +7,8 @@
$types = parent::getTransactionTypes();
$types[] = NuanceSourceTransaction::TYPE_NAME;
+ $types[] = NuanceSourceTransaction::TYPE_SOURCE_TYPE;
+ $types[] = NuanceSourceTransaction::TYPE_METADATA;
$types[] = PhabricatorTransactions::TYPE_EDGE;
$types[] = PhabricatorTransactions::TYPE_COMMENT;
@@ -23,6 +25,16 @@
switch ($xaction->getTransactionType()) {
case NuanceSourceTransaction::TYPE_NAME:
return $object->getName();
+ case NuanceSourceTransaction::TYPE_SOURCE_TYPE:
+ if ($this->getIsNewObject()) {
+ return null;
+ }
+ return $object->getType();
+ case NuanceSourceTransaction::TYPE_METADATA:
+ if ($this->getIsNewObject()) {
+ return null;
+ }
+ return $object->getData();
}
return parent::getCustomTransactionOldValue($object, $xaction);
@@ -34,6 +46,8 @@
switch ($xaction->getTransactionType()) {
case NuanceSourceTransaction::TYPE_NAME:
+ case NuanceSourceTransaction::TYPE_SOURCE_TYPE:
+ case NuanceSourceTransaction::TYPE_METADATA:
return $xaction->getNewValue();
}
@@ -48,6 +62,12 @@
case NuanceSourceTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
break;
+ case NuanceSourceTransaction::TYPE_SOURCE_TYPE:
+ $object->setType($xaction->getNewValue());
+ break;
+ case NuanceSourceTransaction::TYPE_METADATA:
+ $object->setData($xaction->getNewValue());
+ break;
}
}
@@ -57,6 +77,9 @@
switch ($xaction->getTransactionType()) {
case NuanceSourceTransaction::TYPE_NAME:
+ case NuanceSourceTransaction::TYPE_SOURCE_TYPE:
+ return;
+ case NuanceSourceTransaction::TYPE_METADATA:
return;
}
@@ -87,6 +110,13 @@
$errors[] = $error;
}
break;
+ case NuanceSourceTransaction::TYPE_METADATA:
+ $definition = NuanceSourceDefinition::getDefinitionForSource($object);
+ $error = $definition->validateTransaction($type, $xactions);
+ if ($error) {
+ $errors[] = $error;
+ }
+ break;
}
return $errors;
Index: src/applications/nuance/query/NuanceSourceQuery.php
===================================================================
--- src/applications/nuance/query/NuanceSourceQuery.php
+++ src/applications/nuance/query/NuanceSourceQuery.php
@@ -28,7 +28,6 @@
return $this;
}
-
public function loadPage() {
$table = new NuanceSource();
$conn_r = $table->establishConnection('r');
@@ -59,7 +58,7 @@
if ($this->types) {
$where[] = qsprintf(
$conn_r,
- 'type IN (%Ld)',
+ 'type IN (%Ls)',
$this->types);
}
Index: src/applications/nuance/source/bot/NuanceTwitterSourceBot.php
===================================================================
--- /dev/null
+++ src/applications/nuance/source/bot/NuanceTwitterSourceBot.php
@@ -0,0 +1,107 @@
+<?php
+
+/**
+ * Continuously loads all known @{class:NuanceSource} objects of
+ * @{class:NuanceTwitterSourceDefinition} type, calling each objects
+ * @{method:updateItems} method.
+ */
+final class NuanceTwitterSourceBot extends PhabricatorDaemon {
+
+ const PUBLIC_STREAM_URI =
+ 'https://stream.twitter.com/1.1/statuses/filter.json';
+ const USER_STREAM_URI = 'https://userstream.twitter.com/1.1/user.json';
+
+ public function run() {
+ $argv = $this->getArgv();
+ if (count($argv) !== 0) {
+ throw new Exception("usage: NuanceTwitterSourceBot");
+ }
+
+ // $this->runLoop();
+ $future = $this->buildTestTwitterFuture();
+
+ var_dump(array(
+ 'test_result' => $future->resolveJSON()));
+
+ }
+
+ private function runLoop() {
+ do {
+ $this->stillWorking();
+
+ $sources = $this->reloadSources();
+ $streams = $this->reconnectStreams($sources);
+ $this->readStreams($streams);
+
+ } while (true);
+ }
+
+ private function reloadSources() {
+ return id(new NuanceSourceQuery())
+ ->withTypes(array(
+ NuanceTwitterPublicStreamSourceDefinition::getSourceTypeConstant(),
+ NuanceTwitterUserStreamSourceDefinition::getSourceTypeConstant()))
+ ->setViewer($this->getViewer())
+ ->execute();
+ }
+
+ private function reconnectStreams(array $sources) {
+
+ // TODO - foreach source, build a future
+
+ return array();
+ }
+
+ private function readStreams(array $futures) {
+ // TODO - foreach future, read that bad boy
+ }
+
+ private function getHackedViewer() {
+ return id(new PhabricatorUser())
+ ->loadOneWhere('phid = "PHID-USER-xee4ju2teq7mflitwfcs"');
+ }
+
+ private function buildTestTwitterFuture(array $sources) {
+ assert_instances_of($sources, 'NuanceSource');
+
+ $viewer = $this->getHackedViewer();
+ $twitter = null;
+ $providers = PhabricatorAuthProvider::getAllEnabledProviders();
+ foreach ($providers as $provider) {
+ if ($provider instanceof PhabricatorAuthProviderOAuth1Twitter) {
+ $twitter = $provider;
+ break;
+ }
+ }
+
+ if (!$twitter) {
+ throw new Exception("No Twitter OAuth on this install!");
+ }
+
+ $external = id(new PhabricatorExternalAccountQuery())
+ ->setViewer($viewer)
+ ->withAccountTypes(array($provider->getProviderType()))
+ ->withAccountDomains(array($provider->getProviderDomain()))
+ ->withUserPHIDs(array($viewer->getPHID()))
+ ->executeOne();
+
+ if (!$external) {
+ throw new Exception("No external Twitter account on this account!");
+ }
+
+ $token = $external->getProperty('oauth1.token');
+ $secret = $external->getProperty('oauth1.token.secret');
+
+ $adapter = $twitter->getAdapter();
+ $adapter->setToken($token);
+ $adapter->setTokenSecret($secret);
+
+ $uri = new PhutilURI(self::PUBLIC_STREAM_URI);
+ $uri->setQueryParam('track', 'twitter');
+
+ $future = $adapter->newOAuth1Future($uri);
+
+ return $future;
+ }
+
+}
Index: src/applications/nuance/source/definition/NuancePhabricatorFormSourceDefinition.php
===================================================================
--- src/applications/nuance/source/definition/NuancePhabricatorFormSourceDefinition.php
+++ src/applications/nuance/source/definition/NuancePhabricatorFormSourceDefinition.php
@@ -15,7 +15,14 @@
return null;
}
+ protected function getEditFormDescription() {
+ return pht(
+ 'This source type creates a form, accessible to Phabricator users, that '.
+ 'creates Nuance issues.');
+ }
+
protected function augmentEditForm(
+ AphrontRequest $request,
AphrontFormView $form,
PhabricatorApplicationTransactionValidationException $ex = null) {
Index: src/applications/nuance/source/definition/NuanceSourceDefinition.php
===================================================================
--- src/applications/nuance/source/definition/NuanceSourceDefinition.php
+++ src/applications/nuance/source/definition/NuanceSourceDefinition.php
@@ -56,13 +56,25 @@
*/
public static function getDefinitionForSource(NuanceSource $source) {
$definitions = self::getAllDefinitions();
- $map = mpull($definitions, null, 'getSourceTypeConstant');
- $definition = $map[$source->getType()];
+ if (!$source->getType()) {
+ $definition = reset($definitions);
+ } else {
+ $map = mpull($definitions, null, 'getSourceTypeConstant');
+ $definition = $map[$source->getType()];
+ }
$definition->setSourceObject($source);
return $definition;
}
+ public static function getDefinitionForSourceType($type) {
+ $definitions = self::getAllDefinitions();
+ $map = mpull($definitions, null, 'getSourceTypeConstant');
+ $definition = $map[$type];
+
+ return $definition;
+ }
+
public static function getAllDefinitions() {
static $definitions;
@@ -128,7 +140,7 @@
if ($source->getPHID()) {
$title = pht('Edit "%s" source.', $source->getName());
} else {
- $title = pht('Create a new "%s" source.', $this->getName());
+ $title = pht('Create a new source.');
}
return $title;
@@ -157,10 +169,15 @@
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
}
-
}
- $form = $this->renderEditForm($validation_exception);
+ $form = $this->renderEditForm($request, $validation_exception);
+ Javelin::initBehavior('nuance-source-editor',
+ array(
+ 'redrawURI' => $source->getEditURI(),
+ 'isNew' => $source->getPHID() ? false : true,
+ 'baseValueKeys' => array('name', 'type'),
+ ));
$layout = id(new PHUIObjectBoxView())
->setHeaderText($this->getEditTitle())
->setValidationException($validation_exception)
@@ -172,12 +189,15 @@
return $layout;
}
+ abstract protected function getEditFormDescription();
+
/**
* Code to create a form to edit the @{class:NuanceItem} you are defining.
*
* return @{class:AphrontFormView}
*/
- private function renderEditForm(
+ public function renderEditForm(
+ AphrontRequest $request,
PhabricatorApplicationTransactionValidationException $ex = null) {
$user = $this->requireActor();
$source = $this->requireSourceObject();
@@ -187,22 +207,40 @@
$e_name = $ex->getShortMessage(NuanceSourceTransaction::TYPE_NAME);
}
+ $v_name = $request->getStr('name', $source->getName());
+ $v_type = $request->getStr('type', $source->getType());
+ if ($request->isFormPost()) {
+ $source->setViewPolicy($request->getStr('viewPolicy'));
+ $source->setEditPolicy($request->getStr('editPolicy'));
+ }
+
+ $form_id = $source->getPHID() ? $source->getPHID() : 'new-source-form';
$form = id(new AphrontFormView())
->setUser($user)
- ->appendChild(
+ ->setAction($source->getEditURI())
+ ->setID($form_id)
+ ->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name'))
->setName('name')
->setError($e_name)
- ->setValue($source->getName()))
+ ->setValue($v_name))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Type'))
->setName('type')
->setOptions(self::getSelectOptions())
- ->setValue($source->getType()));
+ ->setValue($v_type)
+ ->setMetadata(array(
+ 'form_id' => $form_id,
+ 'selected' => $v_type))
+ ->addSigil('source-type-select'))
+ ->appendChild(
+ id(new AphrontFormStaticControl())
+ ->setLabel(pht('Type Description'))
+ ->setValue($this->getEditFormDescription()));
- $form = $this->augmentEditForm($form, $ex);
+ $form = $this->augmentEditForm($request, $form, $ex);
$form
->appendChild(
@@ -231,6 +269,7 @@
* return @{class:AphrontFormView}
*/
protected function augmentEditForm(
+ AphrontRequest $request,
AphrontFormView $form,
PhabricatorApplicationTransactionValidationException $ex = null) {
@@ -263,10 +302,22 @@
$transactions[] = id(new NuanceSourceTransaction())
->setTransactionType(NuanceSourceTransaction::TYPE_NAME)
->setNewvalue($request->getStr('name'));
+ $transactions[] = id(new NuanceSourceTransaction())
+ ->setTransactionType(NuanceSourceTransaction::TYPE_SOURCE_TYPE)
+ ->setNewvalue($request->getStr('type'));
return $transactions;
}
+ /**
+ * Hook to validate @{class:PhabricatorTransactions} on a per-definition
+ * basis. Useful for validating metadata, which tends to vary from definition
+ * to definition.
+ */
+ public function validateTransaction($type, array $type_xactions) {
+ return null;
+ }
+
abstract public function renderView();
abstract public function renderListView();
Index: src/applications/nuance/source/definition/NuanceTwitterPublicStreamSourceDefinition.php
===================================================================
--- /dev/null
+++ src/applications/nuance/source/definition/NuanceTwitterPublicStreamSourceDefinition.php
@@ -0,0 +1,108 @@
+<?php
+
+final class NuanceTwitterPublicStreamSourceDefinition
+ extends NuanceTwitterSourceDefinition {
+
+ public function getName() {
+ return pht('Twitter Public Stream');
+ }
+
+ public function getSourceTypeConstant() {
+ return 'twitter-public-stream';
+ }
+
+ protected function getEditFormDescription() {
+ return pht(
+ 'This source type uses the Twitter Public Steam API to create items '.
+ 'from the stream of public tweets that match the specified keywords.');
+ }
+
+ protected function augmentEditForm(
+ AphrontRequest $request,
+ AphrontFormView $form,
+ PhabricatorApplicationTransactionValidationException $ex = null) {
+
+ $source = $this->requireSourceObject();
+ $data = $source->getData();
+ $v_keywords = null;
+ if ($request->isFormPost()) {
+ $v_keywords = $request->getStr('keywords');
+ } else if ($data) {
+ $v_keywords = implode(' ', idx($data, 'keywords', array()));
+ }
+ $e_keywords = null;
+ if ($ex) {
+ $e_keywords =
+ $ex->getShortMessage(NuanceSourceTransaction::TYPE_METADATA);
+ }
+ $form
+ ->appendChild(
+ id(new AphrontFormTextAreaControl())
+ ->setLabel(pht('Keywords'))
+ ->setName('keywords')
+ ->setValue($v_keywords)
+ ->setError($e_keywords)
+ ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
+ ->setCaption(
+ pht(
+ 'Space-delimited keywords this source should track. '.
+ 'Keywords are "OR\'d" and match regardless of capitalization.')));
+
+ // See https://dev.twitter.com/docs/streaming-apis/parameters#track for
+ // more details on keywords.
+
+ return $form;
+ }
+
+ protected function buildTransactions(AphrontRequest $request) {
+ $transactions = parent::buildTransactions($request);
+
+ $keyword_string = trim($request->getStr('keywords'));
+ if ($keyword_string) {
+ $keywords = explode(' ', $keyword_string);
+ } else {
+ $keywords = array();
+ }
+ $metadata = array(
+ 'keywords' => $keywords);
+
+ $transactions[] = id(new NuanceSourceTransaction())
+ ->setTransactionType(NuanceSourceTransaction::TYPE_METADATA)
+ ->setNewValue($metadata);
+
+ return $transactions;
+ }
+
+ public function validateTransaction($type, array $type_xactions) {
+ $error = null;
+ switch ($type) {
+ case NuanceSourceTransaction::TYPE_METADATA:
+ $xaction = last($type_xactions);
+ $data = $xaction->getNewValue();
+ $keywords = $data['keywords'];
+ if (empty($keywords)) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Required'),
+ pht('At least one keyword is required.'),
+ $xaction);
+ $error->setIsMissingFieldError(true);
+ } else {
+ foreach ($keywords as $keyword) {
+ if (phutil_utf8_strlen($keyword) > 60) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Too long'),
+ pht(
+ 'At least one keyword ("%s") is too long; each keyword must '.
+ 'be 60 characters or less.', $keyword));
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return $error;
+ }
+
+}
Index: src/applications/nuance/source/definition/NuanceTwitterSourceDefinition.php
===================================================================
--- /dev/null
+++ src/applications/nuance/source/definition/NuanceTwitterSourceDefinition.php
@@ -0,0 +1,16 @@
+<?php
+
+abstract class NuanceTwitterSourceDefinition
+ extends NuanceSourceDefinition {
+
+ public function updateItems() {
+ return null;
+ }
+
+ public function renderView() {
+ }
+
+ public function renderListView() {
+ }
+
+}
Index: src/applications/nuance/source/definition/NuanceTwitterUserStreamSourceDefinition.php
===================================================================
--- /dev/null
+++ src/applications/nuance/source/definition/NuanceTwitterUserStreamSourceDefinition.php
@@ -0,0 +1,112 @@
+<?php
+
+final class NuanceTwitterUserStreamSourceDefinition
+ extends NuanceTwitterSourceDefinition {
+
+ public function getName() {
+ return pht('Twitter User Stream');
+ }
+
+ public function getSourceTypeConstant() {
+ return 'twitter-user-stream';
+ }
+
+ protected function getEditFormDescription() {
+ return pht(
+ 'This source type uses the Twitter User Stream API to create items from '.
+ 'the stream of twitter events specific to the authenticated user.');
+ }
+
+ protected function augmentEditForm(
+ AphrontRequest $request,
+ AphrontFormView $form,
+ PhabricatorApplicationTransactionValidationException $ex = null) {
+
+ $source = $this->requireSourceObject();
+ $data = $source->getData();
+ $v_tweeted = false;
+ $v_mentioned = true;
+ $v_direct_messaged = true;
+ $v_followed = true;
+ if ($request->isFormPost()) {
+ $v_tweeted = $request->getExists('tweeted');
+ $v_mentioned = $request->getExists('mentioned');
+ $v_direct_messaged = $request->getExists('direct_messaged');
+ $v_followed = $request->getExists('followed');
+ } else if ($data) {
+ $v_tweeted = idx($data, 'tweeted', false);
+ $v_mentioned = idx($data, 'mentioned', true);
+ $v_direct_messaged = idx($data, 'direct_messaged', true);
+ $v_followed = idx($data, 'followed', true);
+ }
+ $e_checkboxes = null;
+ if ($ex) {
+ $e_checkboxes =
+ $ex->getShortMessage(NuanceSourceTransaction::TYPE_METADATA);
+ }
+
+ $form
+ ->appendChild(
+ id(new AphrontFormCheckboxControl())
+ ->setError($e_checkboxes)
+ ->setLabel(pht('Events'))
+ ->setName('events')
+ ->addCheckbox('tweeted',
+ 'tweeted',
+ 'User tweeted',
+ $v_tweeted)
+ ->addCheckbox('mentioned',
+ 'mentioned',
+ 'User is mentioned',
+ $v_mentioned)
+ ->addCheckbox('direct_messaged',
+ 'direct_messaged',
+ 'User is direct messaged',
+ $v_direct_messaged)
+ ->addCheckbox('followed',
+ 'followed',
+ 'User is followed',
+ $v_followed));
+
+ return $form;
+ }
+
+ protected function buildTransactions(AphrontRequest $request) {
+
+ $transactions = parent::buildTransactions($request);
+
+ $metadata = array(
+ 'tweeted' => $request->getExists('tweeted'),
+ 'mentioned' => $request->getExists('mentioned'),
+ 'direct_messaged' => $request->getExists('direct_messaged'),
+ 'followed' => $request->getExists('followed'));
+
+ $transactions[] = id(new NuanceSourceTransaction())
+ ->setTransactionType(NuanceSourceTransaction::TYPE_METADATA)
+ ->setNewValue($metadata);
+
+ return $transactions;
+ }
+
+ public function validateTransaction($type, array $type_xactions) {
+ $error = null;
+ switch ($type) {
+ case NuanceSourceTransaction::TYPE_METADATA:
+ $xaction = last($type_xactions);
+ $data = $xaction->getNewValue();
+ if (!$data['tweeted'] &&
+ !$data['mentioned'] &&
+ !$data['direct_messaged'] &&
+ !$data['followed']) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Required'),
+ pht('At least one event is required.'),
+ $xaction);
+ $error->setIsMissingFieldError(true);
+ }
+ break;
+ }
+ return $error;
+ }
+}
Index: src/applications/nuance/storage/NuanceSource.php
===================================================================
--- src/applications/nuance/storage/NuanceSource.php
+++ src/applications/nuance/storage/NuanceSource.php
@@ -6,7 +6,7 @@
protected $name;
protected $type;
- protected $data;
+ protected $data = array();
protected $mailKey;
protected $viewPolicy;
protected $editPolicy;
@@ -36,6 +36,16 @@
return '/nuance/source/view/'.$this->getID().'/';
}
+ public function getEditURI() {
+ if ($this->getID()) {
+ $uri = '/nuance/source/edit/'.$this->getID().'/';
+ } else {
+ $uri = '/nuance/source/new/';
+ }
+
+ return $uri;
+ }
+
public static function initializeNewSource(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
@@ -47,13 +57,9 @@
$edit_policy = $app->getPolicy(
NuanceCapabilitySourceDefaultEdit::CAPABILITY);
- $definitions = NuanceSourceDefinition::getAllDefinitions();
- $lucky_definition = head($definitions);
-
return id(new NuanceSource())
->setViewPolicy($view_policy)
- ->setEditPolicy($edit_policy)
- ->setType($lucky_definition->getSourceTypeConstant());
+ ->setEditPolicy($edit_policy);
}
Index: src/applications/nuance/storage/NuanceSourceTransaction.php
===================================================================
--- src/applications/nuance/storage/NuanceSourceTransaction.php
+++ src/applications/nuance/storage/NuanceSourceTransaction.php
@@ -3,7 +3,9 @@
final class NuanceSourceTransaction
extends NuanceTransaction {
- const TYPE_NAME = 'name-source';
+ const TYPE_NAME = 'name-source';
+ const TYPE_SOURCE_TYPE = 'source-type-source';
+ const TYPE_METADATA = 'source-metadata';
public function getApplicationTransactionType() {
return NuancePHIDTypeSource::TYPECONST;
@@ -32,8 +34,67 @@
$new);
}
break;
+ case self::TYPE_SOURCE_TYPE:
+ if ($old === null) {
+ return null;
+ } else {
+ $old_definition = NuanceSourceDefinition::getDefinitionForSourceType(
+ $old);
+ $new_definition = NuanceSourceDefinition::getDefinitionForSourceType(
+ $new);
+ return pht(
+ '%s changed the source type from "%s" to "%s".',
+ $this->renderHandleLink($author_phid),
+ $old_definition->getName(),
+ $new_definition->getName());
+ break;
+ }
+ case self::TYPE_METADATA:
+ if ($old === null) {
+ return null;
+ } else {
+ return pht(
+ '%s updated the metadata.',
+ $this->renderHandleLink($author_phid));
+ break;
+ }
}
}
+ public function shouldHide() {
+ switch ($this->getTransactionType()) {
+ case self::TYPE_SOURCE_TYPE:
+ case self::TYPE_METADATA:
+ if ($this->getOldValue() === null) {
+ return true;
+ } else {
+ return false;
+ }
+ break;
+ }
+
+ return false;
+ }
+
+ public function hasChangeDetails() {
+ switch ($this->getTransactionType()) {
+ case self::TYPE_METADATA:
+ return true;
+ }
+ return parent::hasChangeDetails();
+ }
+
+ public function renderChangeDetails(PhabricatorUser $viewer) {
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ $view = id(new PhabricatorApplicationTransactionTextDiffDetailView())
+ ->setUser($viewer)
+ ->setOldText(json_encode($old))
+ ->setNewText(json_encode($new));
+
+ return $view->render();
+ }
+
}
Index: src/view/form/control/AphrontFormSelectControl.php
===================================================================
--- src/view/form/control/AphrontFormSelectControl.php
+++ src/view/form/control/AphrontFormSelectControl.php
@@ -7,6 +7,18 @@
}
private $options;
+ private $sigils = array();
+ private $metadata;
+
+ public function addSigil($sigil) {
+ $this->sigils[] = $sigil;
+ return $this;
+ }
+
+ public function setMetadata(array $data) {
+ $this->metadata = $data;
+ return $this;
+ }
public function setOptions(array $options) {
$this->options = $options;
@@ -25,6 +37,8 @@
'name' => $this->getName(),
'disabled' => $this->getDisabled() ? 'disabled' : null,
'id' => $this->getID(),
+ 'sigil' => $this->sigils ? implode(' ', $this->sigils) : null,
+ 'meta' => $this->metadata,
));
}
Index: webroot/rsrc/js/application/nuance/nuance-source-editor.js
===================================================================
--- /dev/null
+++ webroot/rsrc/js/application/nuance/nuance-source-editor.js
@@ -0,0 +1,49 @@
+/**
+ * @requires javelin-dom
+ * javelin-workflow
+ * javelin-behavior
+ * javelin-stratcom
+ * javelin-uri
+ * @provides javelin-behavior-nuance-source-editor
+ * @javelin
+ */
+
+JX.behavior('nuance-source-editor', function(config) {
+
+ JX.Stratcom.listen(
+ 'change',
+ 'source-type-select',
+ redrawSourceEditForm);
+
+ /**
+ * Since different source types require different data, warn the user
+ * they are going to blow away their changes if they change the type.
+ */
+ function redrawSourceEditForm(e) {
+ var form_id = e.getNodeData('source-type-select').form_id;
+ var form = JX.$(form_id);
+ var form_data = JX.DOM.convertFormToDictionary(form);
+ var data = { warn : !config.isNew };
+ new JX.Workflow.newFromForm(form, data)
+ .setHandler(function (r) {
+ var uri = JX.$U(config.redrawURI);
+ var params = {};
+ var key = null;
+ for (var i = 0; i < config.baseValueKeys.length; i++) {
+ key = config.baseValueKeys[i];
+ if (form_data[key]) {
+ params[key] = form_data[key];
+ }
+ }
+ params.redraw = true;
+ uri.addQueryParams(params);
+ uri.go();
+ })
+ .setCloseHandler(function (r) {
+ var select = e.getNode('source-type-select');
+ select.value = e.getNodeData('source-type-select').selected;
+ })
+ .start();
+ }
+
+});
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Oct 15, 12:54 PM (3 w, 3 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6712903
Default Alt Text
D7723.id17438.diff (34 KB)
Attached To
Mode
D7723: Nuance Source stuff - Twitter Public Stream and Twitter User Stream
Attached
Detach File
Event Timeline
Log In to Comment