public function publish()
 {
     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((string) $this->storyAuthorPHID);
     $story->setChronologicalKey($chrono_key);
     $story->save();
     if ($this->relatedPHIDs) {
         $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 buildJoinClause(AphrontDatabaseConnection $conn_r)
 {
     // NOTE: We perform this join unconditionally (even if we have no filter
     // PHIDs) to omit rows which have no story references. These story data
     // rows are notifications or realtime alerts.
     $ref_table = new PhabricatorFeedStoryReference();
     return qsprintf($conn_r, 'JOIN %T ref ON ref.chronologicalKey = story.chronologicalKey', $ref_table->getTableName());
 }
 public function execute()
 {
     $ref_table = new PhabricatorFeedStoryReference();
     $story_table = new PhabricatorFeedStoryData();
     $conn = $story_table->establishConnection('r');
     $where = array();
     if ($this->filterPHIDs) {
         $where[] = qsprintf($conn, 'ref.objectPHID IN (%Ls)', $this->filterPHIDs);
     }
     // For "before" queries, we can just add a constraint to the WHERE clause.
     // For "after" queries, we must also reverse the result ordering, since
     // otherwise we'll always grab the first page of results if there's a limit.
     // After MySQL applies the limit, we reverse the page in PHP (below) to
     // ensure consistent ordering.
     $order = 'DESC';
     if ($this->after) {
         $where[] = qsprintf($conn, 'ref.chronologicalKey > %s', $this->after);
         $order = 'ASC';
     }
     if ($this->before) {
         $where[] = qsprintf($conn, 'ref.chronologicalKey < %s', $this->before);
     }
     if ($where) {
         $where = 'WHERE (' . implode(') AND (', $where) . ')';
     } else {
         $where = '';
     }
     $data = queryfx_all($conn, 'SELECT story.* FROM %T ref
     JOIN %T story ON ref.chronologicalKey = story.chronologicalKey
     %Q
     GROUP BY ref.chronologicalKey
     ORDER BY ref.chronologicalKey %Q
     LIMIT %d', $ref_table->getTableName(), $story_table->getTableName(), $where, $order, $this->limit);
     if ($order != 'DESC') {
         // If we did order ASC to pull 'after' data, reverse the result set so
         // that stories are returned in a consistent (descending) order.
         $data = array_reverse($data);
     }
     $data = $story_table->loadAllFromArray($data);
     $stories = array();
     foreach ($data as $story_data) {
         $class = $story_data->getStoryType();
         try {
             if (!class_exists($class) || !is_subclass_of($class, 'PhabricatorFeedStory')) {
                 $class = 'PhabricatorFeedStoryUnknown';
             }
         } catch (PhutilMissingSymbolException $ex) {
             // If the class can't be loaded, libphutil will throw an exception.
             // Render the story using the unknown story view.
             $class = 'PhabricatorFeedStoryUnknown';
         }
         $stories[] = newv($class, array($story_data));
     }
     return $stories;
 }
 public function execute()
 {
     $ref_table = new PhabricatorFeedStoryReference();
     $story_table = new PhabricatorFeedStoryData();
     $conn = $story_table->establishConnection('r');
     $where = array();
     if ($this->filterPHIDs) {
         $where[] = qsprintf($conn, 'ref.objectPHID IN (%Ls)', $this->filterPHIDs);
     }
     if ($where) {
         $where = 'WHERE (' . implode(') AND (', $where) . ')';
     } else {
         $where = '';
     }
     $data = queryfx_all($conn, 'SELECT story.* FROM %T ref
     JOIN %T story ON ref.chronologicalKey = story.chronologicalKey
     %Q
     GROUP BY story.chronologicalKey
     ORDER BY story.chronologicalKey DESC
     LIMIT %d', $ref_table->getTableName(), $story_table->getTableName(), $where, $this->limit);
     $data = $story_table->loadAllFromArray($data);
     $stories = array();
     foreach ($data as $story_data) {
         $class = $story_data->getStoryType();
         try {
             if (!class_exists($class) || !is_subclass_of($class, 'PhabricatorFeedStory')) {
                 $class = 'PhabricatorFeedStoryUnknown';
             }
         } catch (PhutilMissingSymbolException $ex) {
             // If the class can't be loaded, libphutil will throw an exception.
             // Render the story using the unknown story view.
             $class = 'PhabricatorFeedStoryUnknown';
         }
         $stories[] = newv($class, array($story_data));
     }
     return $stories;
 }
 public function publish()
 {
     $class = $this->storyType;
     if (!$class) {
         throw new Exception(pht('Call %s before publishing!', 'setStoryType()'));
     }
     if (!class_exists($class)) {
         throw new Exception(pht("Story type must be a valid class name and must subclass %s. " . "'%s' is not a loadable class.", 'PhabricatorFeedStory', $class));
     }
     if (!is_subclass_of($class, 'PhabricatorFeedStory')) {
         throw new Exception(pht("Story type must be a valid class name and must subclass %s. " . "'%s' is not a subclass of %s.", 'PhabricatorFeedStory', $class, 'PhabricatorFeedStory'));
     }
     $chrono_key = $this->generateChronologicalKey();
     $story = new PhabricatorFeedStoryData();
     $story->setStoryType($this->storyType);
     $story->setStoryData($this->storyData);
     $story->setAuthorPHID((string) $this->storyAuthorPHID);
     $story->setChronologicalKey($chrono_key);
     $story->save();
     if ($this->relatedPHIDs) {
         $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));
     }
     $subscribed_phids = $this->subscribedPHIDs;
     if ($subscribed_phids) {
         $subscribed_phids = $this->filterSubscribedPHIDs($subscribed_phids);
         $this->insertNotifications($chrono_key, $subscribed_phids);
         $this->sendNotification($chrono_key, $subscribed_phids);
     }
     PhabricatorWorker::scheduleTask('FeedPublisherWorker', array('key' => $chrono_key));
     return $story;
 }