2012-02-17 19:21:38 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class PhabricatorChatLogChannelLogController
|
|
|
|
extends PhabricatorChatLogController {
|
|
|
|
|
|
|
|
private $channel;
|
|
|
|
|
|
|
|
public function willProcessRequest(array $data) {
|
|
|
|
$this->channel = $data['channel'];
|
|
|
|
}
|
|
|
|
|
|
|
|
public function processRequest() {
|
2012-06-02 23:00:08 +02:00
|
|
|
$request = $this->getRequest();
|
|
|
|
$user = $request->getUser();
|
|
|
|
|
|
|
|
$uri = clone $request->getRequestURI();
|
|
|
|
$uri->setQueryParams(array());
|
|
|
|
|
Rename "IDPaged" to "CursorPaged", "executeWithPager" to "executeWith[Cursor|Offset]Pager"
Summary:
I'm trying to make progress on the policy/visibility stuff since it's a blocker for Wikimedia.
First, I want to improve Projects so they can serve as policy groups (e.g., an object can have a visibility policy like "Visible to: members of project 'security'"). However, doing this without breaking anything or snowballing into a bigger change is a bit awkward because Projects are name-ordered and we have a Conduit API which does offset paging. Rather than breaking or rewriting this stuff, I want to just continue offset paging them for now.
So I'm going to make PhabricatorPolicyQuery extend PhabricatorOffsetPagedQuery, but can't currently since the `executeWithPager` methods would clash. These methods do different things anyway and are probably better with different names.
This also generally improves the names of these classes, since cursors are not necessarily IDs (in the feed case, they're "chronlogicalKeys", for example). I did leave some of the interals as "ID" since calling them "Cursor"s (e.g., `setAfterCursor()`) seemed a little wrong -- it should maybe be `setAfterCursorPosition()`. These APIs have very limited use and can easily be made more consistent later.
Test Plan: Browsed around various affected tools; any issues here should throw/fail in a loud/obvious way.
Reviewers: vrana, btrahan
Reviewed By: vrana
CC: aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D3177
2012-08-07 20:54:06 +02:00
|
|
|
$pager = new AphrontCursorPagerView();
|
2012-06-02 23:00:08 +02:00
|
|
|
$pager->setURI($uri);
|
|
|
|
$pager->setPageSize(250);
|
|
|
|
|
|
|
|
$query = id(new PhabricatorChatLogQuery())
|
|
|
|
->setViewer($user)
|
|
|
|
->withChannels(array($this->channel));
|
|
|
|
|
|
|
|
|
|
|
|
list($after, $before, $map) = $this->getPagingParameters($request, $query);
|
|
|
|
|
|
|
|
$pager->setAfterID($after);
|
|
|
|
$pager->setBeforeID($before);
|
2012-02-17 19:21:38 +01:00
|
|
|
|
Rename "IDPaged" to "CursorPaged", "executeWithPager" to "executeWith[Cursor|Offset]Pager"
Summary:
I'm trying to make progress on the policy/visibility stuff since it's a blocker for Wikimedia.
First, I want to improve Projects so they can serve as policy groups (e.g., an object can have a visibility policy like "Visible to: members of project 'security'"). However, doing this without breaking anything or snowballing into a bigger change is a bit awkward because Projects are name-ordered and we have a Conduit API which does offset paging. Rather than breaking or rewriting this stuff, I want to just continue offset paging them for now.
So I'm going to make PhabricatorPolicyQuery extend PhabricatorOffsetPagedQuery, but can't currently since the `executeWithPager` methods would clash. These methods do different things anyway and are probably better with different names.
This also generally improves the names of these classes, since cursors are not necessarily IDs (in the feed case, they're "chronlogicalKeys", for example). I did leave some of the interals as "ID" since calling them "Cursor"s (e.g., `setAfterCursor()`) seemed a little wrong -- it should maybe be `setAfterCursorPosition()`. These APIs have very limited use and can easily be made more consistent later.
Test Plan: Browsed around various affected tools; any issues here should throw/fail in a loud/obvious way.
Reviewers: vrana, btrahan
Reviewed By: vrana
CC: aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D3177
2012-08-07 20:54:06 +02:00
|
|
|
$logs = $query->executeWithCursorPager($pager);
|
2012-02-17 19:21:38 +01:00
|
|
|
|
2012-06-02 23:00:08 +02:00
|
|
|
// Show chat logs oldest-first.
|
|
|
|
$logs = array_reverse($logs);
|
2012-02-17 19:21:38 +01:00
|
|
|
|
|
|
|
|
2012-06-02 23:00:08 +02:00
|
|
|
// Divide all the logs into blocks, where a block is the same author saying
|
|
|
|
// several things in a row. A block ends when another user speaks, or when
|
|
|
|
// two minutes pass without the author speaking.
|
2012-02-17 19:21:38 +01:00
|
|
|
|
2012-06-02 23:00:08 +02:00
|
|
|
$blocks = array();
|
|
|
|
$block = null;
|
|
|
|
|
|
|
|
$last_author = null;
|
|
|
|
$last_epoch = null;
|
2012-02-17 19:21:38 +01:00
|
|
|
foreach ($logs as $log) {
|
|
|
|
$this_author = $log->getAuthor();
|
|
|
|
$this_epoch = $log->getEpoch();
|
|
|
|
|
2012-06-02 23:00:08 +02:00
|
|
|
// Decide whether we should start a new block or not.
|
|
|
|
$new_block = ($this_author !== $last_author) ||
|
|
|
|
($this_epoch - (60 * 2) > $last_epoch);
|
|
|
|
|
|
|
|
if ($new_block) {
|
|
|
|
if ($block) {
|
|
|
|
$blocks[] = $block;
|
|
|
|
}
|
|
|
|
$block = array(
|
|
|
|
'id' => $log->getID(),
|
|
|
|
'epoch' => $this_epoch,
|
|
|
|
'author' => $this_author,
|
|
|
|
'logs' => array($log),
|
|
|
|
);
|
2012-02-17 19:21:38 +01:00
|
|
|
} else {
|
2012-06-02 23:00:08 +02:00
|
|
|
$block['logs'][] = $log;
|
2012-02-17 19:21:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$last_author = $this_author;
|
2012-06-02 23:00:08 +02:00
|
|
|
$last_epoch = $this_epoch;
|
|
|
|
}
|
|
|
|
if ($block) {
|
|
|
|
$blocks[] = $block;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Figure out CSS classes for the blocks. We alternate colors between
|
|
|
|
// lines, and highlight the entire block which contains the target ID or
|
|
|
|
// date, if applicable.
|
|
|
|
|
|
|
|
foreach ($blocks as $key => $block) {
|
|
|
|
$classes = array();
|
|
|
|
if ($key % 2) {
|
|
|
|
$classes[] = 'alternate';
|
|
|
|
}
|
|
|
|
$ids = mpull($block['logs'], 'getID', 'getID');
|
|
|
|
if (array_intersect_key($ids, $map)) {
|
|
|
|
$classes[] = 'highlight';
|
|
|
|
}
|
|
|
|
$blocks[$key]['class'] = $classes ? implode(' ', $classes) : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
require_celerity_resource('phabricator-chatlog-css');
|
|
|
|
|
|
|
|
$out = array();
|
|
|
|
$out[] = '<table class="phabricator-chat-log">';
|
|
|
|
foreach ($blocks as $block) {
|
|
|
|
$author = $block['author'];
|
|
|
|
$author = phutil_utf8_shorten($author, 18);
|
2013-01-18 09:32:58 +01:00
|
|
|
$author = phutil_tag('td', array('class' => 'author'), $author);
|
2012-06-02 23:00:08 +02:00
|
|
|
|
|
|
|
$message = mpull($block['logs'], 'getMessage');
|
|
|
|
$message = implode("\n", $message);
|
2013-01-18 09:32:58 +01:00
|
|
|
$message = phutil_tag('td', array('class' => 'message'), $message);
|
2012-06-02 23:00:08 +02:00
|
|
|
|
|
|
|
$href = $uri->alter('at', $block['id']);
|
|
|
|
$timestamp = $block['epoch'];
|
|
|
|
$timestamp = phabricator_datetime($timestamp, $user);
|
2013-01-18 09:32:58 +01:00
|
|
|
$timestamp = phutil_tag('a', array('href' => $href), $timestamp);
|
|
|
|
$timestamp = phutil_tag(
|
2012-06-02 23:00:08 +02:00
|
|
|
'td',
|
|
|
|
array(
|
|
|
|
'class' => 'timestamp',
|
|
|
|
),
|
|
|
|
$timestamp);
|
|
|
|
|
2013-01-18 09:32:58 +01:00
|
|
|
$out[] = phutil_tag(
|
2012-06-02 23:00:08 +02:00
|
|
|
'tr',
|
|
|
|
array(
|
|
|
|
'class' => $block['class'],
|
|
|
|
),
|
2013-01-18 09:32:58 +01:00
|
|
|
array($author, $message, $timestamp));
|
2012-02-17 19:21:38 +01:00
|
|
|
}
|
|
|
|
$out[] = '</table>';
|
|
|
|
|
2012-06-02 23:00:08 +02:00
|
|
|
$form = id(new AphrontFormView())
|
|
|
|
->setUser($user)
|
|
|
|
->setMethod('GET')
|
|
|
|
->setAction($uri)
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTextControl())
|
|
|
|
->setLabel('Date')
|
|
|
|
->setName('date')
|
|
|
|
->setValue($request->getStr('date')))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSubmitControl())
|
|
|
|
->setValue('Jump'));
|
|
|
|
|
2012-02-17 19:21:38 +01:00
|
|
|
|
|
|
|
return $this->buildStandardPageResponse(
|
2012-03-28 01:53:47 +02:00
|
|
|
array(
|
2012-06-02 23:00:08 +02:00
|
|
|
'<div class="phabricator-chat-log-panel">',
|
|
|
|
$form,
|
|
|
|
'<br />',
|
2012-03-28 01:53:47 +02:00
|
|
|
implode("\n", $out),
|
2012-06-02 23:00:08 +02:00
|
|
|
$pager,
|
|
|
|
'</div>',
|
2012-03-28 01:53:47 +02:00
|
|
|
),
|
2012-02-17 19:21:38 +01:00
|
|
|
array(
|
|
|
|
'title' => 'Channel Log',
|
|
|
|
));
|
|
|
|
}
|
2012-06-02 23:00:08 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* From request parameters, figure out where we should jump to in the log.
|
|
|
|
* We jump to either a date or log ID, but load a few lines of context before
|
|
|
|
* it so the user can see the nearby conversation.
|
|
|
|
*/
|
|
|
|
private function getPagingParameters(
|
|
|
|
AphrontRequest $request,
|
|
|
|
PhabricatorChatLogQuery $query) {
|
|
|
|
|
|
|
|
$user = $request->getUser();
|
|
|
|
|
|
|
|
$at_id = $request->getInt('at');
|
|
|
|
$at_date = $request->getStr('date');
|
|
|
|
|
|
|
|
$context_log = null;
|
|
|
|
$map = array();
|
|
|
|
|
|
|
|
$query = clone $query;
|
|
|
|
$query->setLimit(8);
|
|
|
|
|
|
|
|
if ($at_id) {
|
|
|
|
// Jump to the log in question, and load a few lines of context before
|
|
|
|
// it.
|
|
|
|
$context_logs = $query
|
|
|
|
->setAfterID($at_id)
|
|
|
|
->execute();
|
|
|
|
|
|
|
|
$context_log = last($context_logs);
|
|
|
|
|
|
|
|
$map = array(
|
|
|
|
$at_id => true,
|
|
|
|
);
|
|
|
|
|
|
|
|
} else if ($at_date) {
|
|
|
|
$timezone = new DateTimeZone($user->getTimezoneIdentifier());
|
|
|
|
try {
|
|
|
|
$date = new DateTime($at_date, $timezone);
|
|
|
|
$timestamp = $date->format('U');
|
|
|
|
} catch (Exception $e) {
|
|
|
|
$timestamp = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($timestamp) {
|
|
|
|
$context_logs = $query
|
|
|
|
->withMaximumEpoch($timestamp)
|
|
|
|
->execute();
|
|
|
|
|
|
|
|
$context_log = last($context_logs);
|
|
|
|
|
|
|
|
$target_log = head($context_logs);
|
|
|
|
if ($target_log) {
|
|
|
|
$map = array(
|
|
|
|
$target_log->getID() => true,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($context_log) {
|
|
|
|
$after = null;
|
|
|
|
$before = $context_log->getID() - 1;
|
|
|
|
} else {
|
|
|
|
$after = $request->getInt('after');
|
|
|
|
$before = $request->getInt('before');
|
|
|
|
}
|
|
|
|
|
|
|
|
return array($after, $before, $map);
|
|
|
|
}
|
|
|
|
|
2012-02-17 19:21:38 +01:00
|
|
|
}
|