Correct Aphlict websocket URI construction after PHP8 compatibility changes
[phabricator.git] / src / applications / chatlog / controller / PhabricatorChatLogChannelLogController.php
blobb9893f6924ce752b7cc9be32ff33fcd0e2d9a8dc
1 <?php
3 final class PhabricatorChatLogChannelLogController
4 extends PhabricatorChatLogController {
6 public function shouldAllowPublic() {
7 return true;
10 public function handleRequest(AphrontRequest $request) {
11 $viewer = $request->getViewer();
12 $id = $request->getURIData('channelID');
14 $uri = new PhutilURI($request->getPath());
16 $pager = new AphrontCursorPagerView();
17 $pager->setURI($uri);
18 $pager->setPageSize(250);
20 $query = id(new PhabricatorChatLogQuery())
21 ->setViewer($viewer)
22 ->withChannelIDs(array($id));
24 $channel = id(new PhabricatorChatLogChannelQuery())
25 ->setViewer($viewer)
26 ->withIDs(array($id))
27 ->executeOne();
29 if (!$channel) {
30 return new Aphront404Response();
33 list($after, $before, $map) = $this->getPagingParameters($request, $query);
35 $pager->setAfterID($after);
36 $pager->setBeforeID($before);
38 $logs = $query->executeWithCursorPager($pager);
40 // Show chat logs oldest-first.
41 $logs = array_reverse($logs);
44 // Divide all the logs into blocks, where a block is the same author saying
45 // several things in a row. A block ends when another user speaks, or when
46 // two minutes pass without the author speaking.
48 $blocks = array();
49 $block = null;
51 $last_author = null;
52 $last_epoch = null;
53 foreach ($logs as $log) {
54 $this_author = $log->getAuthor();
55 $this_epoch = $log->getEpoch();
57 // Decide whether we should start a new block or not.
58 $new_block = ($this_author !== $last_author) ||
59 ($this_epoch - (60 * 2) > $last_epoch);
61 if ($new_block) {
62 if ($block) {
63 $blocks[] = $block;
65 $block = array(
66 'id' => $log->getID(),
67 'epoch' => $this_epoch,
68 'author' => $this_author,
69 'logs' => array($log),
71 } else {
72 $block['logs'][] = $log;
75 $last_author = $this_author;
76 $last_epoch = $this_epoch;
78 if ($block) {
79 $blocks[] = $block;
82 // Figure out CSS classes for the blocks. We alternate colors between
83 // lines, and highlight the entire block which contains the target ID or
84 // date, if applicable.
86 foreach ($blocks as $key => $block) {
87 $classes = array();
88 if ($key % 2) {
89 $classes[] = 'alternate';
91 $ids = mpull($block['logs'], 'getID', 'getID');
92 if (array_intersect_key($ids, $map)) {
93 $classes[] = 'highlight';
95 $blocks[$key]['class'] = $classes ? implode(' ', $classes) : null;
99 require_celerity_resource('phabricator-chatlog-css');
101 $out = array();
102 foreach ($blocks as $block) {
103 $author = $block['author'];
104 $author = id(new PhutilUTF8StringTruncator())
105 ->setMaximumGlyphs(18)
106 ->truncateString($author);
107 $author = phutil_tag('td', array('class' => 'author'), $author);
109 $href = $uri->alter('at', $block['id']);
110 $timestamp = $block['epoch'];
111 $timestamp = phabricator_datetime($timestamp, $viewer);
112 $timestamp = phutil_tag(
113 'a',
114 array(
115 'href' => $href,
116 'class' => 'timestamp',
118 $timestamp);
120 $message = mpull($block['logs'], 'getMessage');
121 $message = implode("\n", $message);
122 $message = phutil_tag(
123 'td',
124 array(
125 'class' => 'message',
127 array(
128 $timestamp,
129 $message,
132 $out[] = phutil_tag(
133 'tr',
134 array(
135 'class' => $block['class'],
137 array(
138 $author,
139 $message,
143 $links = array();
145 $first_uri = $pager->getFirstPageURI();
146 if ($first_uri) {
147 $links[] = phutil_tag(
148 'a',
149 array(
150 'href' => $first_uri,
152 "\xC2\xAB ".pht('Newest'));
155 $prev_uri = $pager->getPrevPageURI();
156 if ($prev_uri) {
157 $links[] = phutil_tag(
158 'a',
159 array(
160 'href' => $prev_uri,
162 "\xE2\x80\xB9 ".pht('Newer'));
165 $next_uri = $pager->getNextPageURI();
166 if ($next_uri) {
167 $links[] = phutil_tag(
168 'a',
169 array(
170 'href' => $next_uri,
172 pht('Older')." \xE2\x80\xBA");
175 $pager_bottom = phutil_tag(
176 'div',
177 array('class' => 'phabricator-chat-log-pager-bottom'),
178 $links);
180 $crumbs = $this
181 ->buildApplicationCrumbs()
182 ->addTextCrumb($channel->getChannelName(), $uri);
184 $form = id(new AphrontFormView())
185 ->setUser($viewer)
186 ->setMethod('GET')
187 ->setAction($uri)
188 ->appendChild(
189 id(new AphrontFormTextControl())
190 ->setLabel(pht('Date'))
191 ->setName('date')
192 ->setValue($request->getStr('date')))
193 ->appendChild(
194 id(new AphrontFormSubmitControl())
195 ->setValue(pht('Jump')));
197 $table = phutil_tag(
198 'table',
199 array(
200 'class' => 'phabricator-chat-log',
202 $out);
204 $log = phutil_tag(
205 'div',
206 array(
207 'class' => 'phabricator-chat-log-panel',
209 $table);
211 $jump_link = id(new PHUIButtonView())
212 ->setTag('a')
213 ->setHref('#latest')
214 ->setText(pht('Jump to Bottom'))
215 ->setIcon('fa-arrow-circle-down');
217 $jump_target = phutil_tag(
218 'div',
219 array(
220 'id' => 'latest',
223 $content = phutil_tag(
224 'div',
225 array(
226 'class' => 'phabricator-chat-log-wrap',
228 array(
229 $log,
230 $jump_target,
231 $pager_bottom,
234 $header = id(new PHUIHeaderView())
235 ->setHeader($channel->getChannelName())
236 ->setSubHeader($channel->getServiceName())
237 ->addActionLink($jump_link);
239 $box = id(new PHUIObjectBoxView())
240 ->setHeader($header)
241 ->setCollapsed(true)
242 ->appendChild($content);
244 $box->setShowHide(
245 pht('Search Dates'),
246 pht('Hide Dates'),
247 $form,
248 '#');
250 return $this->newPage()
251 ->setTitle(pht('Channel Log'))
252 ->setCrumbs($crumbs)
253 ->appendChild($box);
258 * From request parameters, figure out where we should jump to in the log.
259 * We jump to either a date or log ID, but load a few lines of context before
260 * it so the user can see the nearby conversation.
262 private function getPagingParameters(
263 AphrontRequest $request,
264 PhabricatorChatLogQuery $query) {
266 $viewer = $request->getViewer();
268 $at_id = $request->getInt('at');
269 $at_date = $request->getStr('date');
271 $context_log = null;
272 $map = array();
274 $query = clone $query;
275 $query->setLimit(8);
277 if ($at_id) {
278 // Jump to the log in question, and load a few lines of context before
279 // it.
280 $context_logs = $query
281 ->setAfterID($at_id)
282 ->execute();
284 $context_log = last($context_logs);
286 $map = array(
287 $at_id => true,
290 } else if ($at_date) {
291 $timestamp = PhabricatorTime::parseLocalTime($at_date, $viewer);
293 if ($timestamp) {
294 $context_logs = $query
295 ->withMaximumEpoch($timestamp)
296 ->execute();
298 $context_log = last($context_logs);
300 $target_log = head($context_logs);
301 if ($target_log) {
302 $map = array(
303 $target_log->getID() => true,
309 if ($context_log) {
310 $after = null;
311 $before = $context_log->getID() - 1;
312 } else {
313 $after = $request->getInt('after');
314 $before = $request->getInt('before');
317 return array($after, $before, $map);