Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/feed/PhabricatorFeedStoryPublisher.php b/src/applications/feed/PhabricatorFeedStoryPublisher.php
index 74828c5237..87909af544 100644
--- a/src/applications/feed/PhabricatorFeedStoryPublisher.php
+++ b/src/applications/feed/PhabricatorFeedStoryPublisher.php
@@ -1,194 +1,195 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorFeedStoryPublisher {
private $relatedPHIDs;
private $storyType;
private $storyData;
private $storyTime;
private $storyAuthorPHID;
private $primaryObjectPHID;
private $subscribedPHIDs;
public function setRelatedPHIDs(array $phids) {
$this->relatedPHIDs = $phids;
return $this;
}
public function setSubscribedPHIDs(array $phids) {
$this->subscribedPHIDs = $phids;
return $this;
}
public function setPrimaryObjectPHID($phid) {
$this->primaryObjectPHID = $phid;
return $this;
}
public function setStoryType($story_type) {
$this->storyType = $story_type;
return $this;
}
public function setStoryData(array $data) {
$this->storyData = $data;
return $this;
}
public function setStoryTime($time) {
$this->storyTime = $time;
return $this;
}
public function setStoryAuthorPHID($phid) {
$this->storyAuthorPHID = $phid;
return $this;
}
public function publish() {
if (!$this->relatedPHIDs) {
throw new Exception("There are no PHIDs related to this story!");
}
if (!$this->storyType) {
throw new Exception("Call setStoryType() before publishing!");
}
$chrono_key = $this->generateChronologicalKey();
$story = new PhabricatorFeedStoryData();
$story->setStoryType($this->storyType);
$story->setStoryData($this->storyData);
$story->setAuthorPHID($this->storyAuthorPHID);
$story->setChronologicalKey($chrono_key);
$story->save();
$ref = new PhabricatorFeedStoryReference();
$sql = array();
$conn = $ref->establishConnection('w');
foreach (array_unique($this->relatedPHIDs) as $phid) {
$sql[] = qsprintf(
$conn,
'(%s, %s)',
$phid,
$chrono_key);
}
queryfx(
$conn,
'INSERT INTO %T (objectPHID, chronologicalKey) VALUES %Q',
$ref->getTableName(),
implode(', ', $sql));
if (PhabricatorEnv::getEnvConfig('notification.enabled')) {
$this->insertNotifications($chrono_key);
$this->sendNotification($chrono_key);
}
return $story;
}
private function insertNotifications($chrono_key) {
if (!$this->subscribedPHIDs) {
return;
}
if (!$this->primaryObjectPHID) {
throw new Exception(
"You must call setPrimaryObjectPHID() if you setSubscribedPHIDs()!");
}
$notif = new PhabricatorFeedStoryNotification();
$sql = array();
$conn = $notif->establishConnection('w');
foreach (array_unique($this->subscribedPHIDs) as $user_phid) {
$sql[] = qsprintf(
$conn,
'(%s, %s, %s, %d)',
$this->primaryObjectPHID,
$user_phid,
$chrono_key,
0);
}
queryfx(
$conn,
'INSERT INTO %T
(primaryObjectPHID, userPHID, chronologicalKey, hasViewed)
VALUES %Q',
$notif->getTableName(),
implode(', ', $sql));
}
private function sendNotification($chrono_key) {
- $aphlict_url = 'http://127.0.0.1:22281/push?'; //TODO: make configurable
- $future = new HTTPFuture($aphlict_url, array(
- "key" => (string)$chrono_key,
- // TODO: fix. \r\n appears to be appended to the final value here.
- // this is a temporary workaround
- "nothing" => "",
- ));
- $future->setMethod('POST');
- $future->resolve();
+ $server_uri = PhabricatorEnv::getEnvConfig('notification.server-uri');
+
+ $data = array(
+ 'key' => (string)$chrono_key,
+ );
+
+ id(new HTTPSFuture($server_uri, $data))
+ ->setMethod('POST')
+ ->setTimeout(1)
+ ->resolve();
}
/**
* We generate a unique chronological key for each story type because we want
* to be able to page through the stream with a cursor (i.e., select stories
* after ID = X) so we can efficiently perform filtering after selecting data,
* and multiple stories with the same ID make this cumbersome without putting
* a bunch of logic in the client. We could use the primary key, but that
* would prevent publishing stories which happened in the past. Since it's
* potentially useful to do that (e.g., if you're importing another data
* source) build a unique key for each story which has chronological ordering.
*
* @return string A unique, time-ordered key which identifies the story.
*/
private function generateChronologicalKey() {
// Use the epoch timestamp for the upper 32 bits of the key. Default to
// the current time if the story doesn't have an explicit timestamp.
$time = nonempty($this->storyTime, time());
// Generate a random number for the lower 32 bits of the key.
$rand = head(unpack('L', Filesystem::readRandomBytes(4)));
// On 32-bit machines, we have to get creative.
if (PHP_INT_SIZE < 8) {
// We're on a 32-bit machine.
if (function_exists('bcadd')) {
// Try to use the 'bc' extension.
return bcadd(bcmul($time, bcpow(2, 32)), $rand);
} else {
// Do the math in MySQL. TODO: If we formalize a bc dependency, get
// rid of this.
$conn_r = id(new PhabricatorFeedStoryData())->establishConnection('r');
$result = queryfx_one(
$conn_r,
'SELECT (%d << 32) + %d as N',
$time,
$rand);
return $result['N'];
}
} else {
// This is a 64 bit machine, so we can just do the math.
return ($time << 32) + $rand;
}
}
}
diff --git a/src/docs/userguide/notifications.diviner b/src/docs/userguide/notifications.diviner
new file mode 100644
index 0000000000..cb9088033b
--- /dev/null
+++ b/src/docs/userguide/notifications.diviner
@@ -0,0 +1,83 @@
+@title Notifications User Guide: Setup and Configuration
+@group userguide
+
+Guide to setting up notifications.
+
+= Overview =
+
+Phabricator can be configured to notify users when events happen in real time,
+so they'll get a message in their browser window if something has happened or
+the object they're looking at has been updated.
+
+NOTE: This feature is new and still needs some work.
+
+= Enabling Notifications =
+
+To enable notifications, set `notification.enabled` to `true` in your
+configuration. This will enable the notification menu in the menu bar, and
+notifications will be published when users take actions.
+
+NOTE: This setting will be turned on for everyone soon.
+
+= Running the Aphlict Server =
+
+Phabricator implements realtime notifications using a Node.js server called
+"Aphlict". To run it:
+
+ - Install node.js.
+ - Run `bin/aphlict` (this script must be run as root).
+
+Since the script needs `PHABRICATOR_ENV` to be defined, you may need to use
+the `-E` flag to `sudo` to preserve the environment if you have
+`PHABRICATOR_ENV` defined in your `.bashrc` or similar:
+
+ phabricator/ $ sudo -E ./bin/aphlict
+
+The server must be able to listen on port **843** and port **22280** for Aphlict
+to work. You can change the latter port in the `notification.client-uri` config,
+but port 843 is used by Flash and can not be changed. In particular, if you're
+running in EC2, you need to unblock both of these ports in the server's security
+group configuration.
+
+You may want to adjust these settings:
+
+ - `notification.client-uri` Externally-facing host and port that browsers will
+ connect to in order to listen for notifications.
+ - `notification.server-uri` Internally-facing host and port that Phabricator
+ will connect to in order to publish notifications.
+ - `notification.log` Log file location for the server.
+ - `notification.user` Non-root user to drop permissions to after binding to
+ privileged ports.
+ - `notification.pid` Pidfile location used to stop any running server when
+ aphlict is restarted.
+
+In most cases, the defaults are appropriate, except that you should set
+`notification.user` to some valid user so Aphlict isn't running as root.
+
+== Verifying Server Status ==
+
+Access `/notifications/status/` to verify the server is operational. You should
+see a table showing stats like "uptime" and connection/message counts if the
+server is working. If it isn't working, you should see an error.
+
+== Testing the Server ==
+
+The easiest way to test the server is to open a Maniphest Task or Differential
+Revision in two browser windows at the same time, then comment on it with one.
+You should get a notification in the other window.
+
+NOTE: This will stop working soon, since we'll soon stop delivering
+notifications about your own actions. We'll provide an alternate way to test
+operation when this stops working.
+
+== Debugging Server Problems ==
+
+You can run `aphlict` in the foreground to get output to your console:
+
+ phabricator/ $ ./bin/aphlict --foreground
+
+This may help identify and resolve problems.
+
+The server also generates a log, by default in `/var/log/aphlict.log`. You can
+change this location by changing `notification.log` in your configuration. The
+log may contain information useful in resolving issues.

File Metadata

Mime Type
text/x-diff
Expires
Wed, Dec 3, 12:26 PM (12 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
433360
Default Alt Text
(9 KB)

Event Timeline