Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/harbormaster/controller/HarbormasterBuildLogRenderController.php b/src/applications/harbormaster/controller/HarbormasterBuildLogRenderController.php
index c1c4275982..50b17523ca 100644
--- a/src/applications/harbormaster/controller/HarbormasterBuildLogRenderController.php
+++ b/src/applications/harbormaster/controller/HarbormasterBuildLogRenderController.php
@@ -1,668 +1,668 @@
<?php
final class HarbormasterBuildLogRenderController
extends HarbormasterController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$id = $request->getURIData('id');
$log = id(new HarbormasterBuildLogQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$log) {
return new Aphront404Response();
}
$log_size = $this->getTotalByteLength($log);
$head_lines = $request->getInt('head');
if ($head_lines === null) {
$head_lines = 8;
}
$head_lines = min($head_lines, 100);
$head_lines = max($head_lines, 0);
$tail_lines = $request->getInt('tail');
if ($tail_lines === null) {
$tail_lines = 16;
}
$tail_lines = min($tail_lines, 100);
$tail_lines = max($tail_lines, 0);
$head_offset = $request->getInt('headOffset');
if ($head_offset === null) {
$head_offset = 0;
}
$tail_offset = $request->getInt('tailOffset');
if ($tail_offset === null) {
$tail_offset = $log_size;
}
// Figure out which ranges we're actually going to read. We'll read either
// one range (either just at the head, or just at the tail) or two ranges
// (one at the head and one at the tail).
// This gets a little bit tricky because: the ranges may overlap; we just
// want to do one big read if there is only a little bit of text left
// between the ranges; we may not know where the tail range ends; and we
// can only read forward from line map markers, not from any arbitrary
// position in the file.
$bytes_per_line = 140;
$body_lines = 8;
$views = array();
if ($head_lines > 0) {
$views[] = array(
'offset' => $head_offset,
'lines' => $head_lines,
'direction' => 1,
'limit' => $tail_offset,
);
}
if ($tail_lines > 0) {
$views[] = array(
'offset' => $tail_offset,
'lines' => $tail_lines,
'direction' => -1,
'limit' => $head_offset,
);
}
$reads = $views;
foreach ($reads as $key => $read) {
$offset = $read['offset'];
$lines = $read['lines'];
$read_length = 0;
$read_length += ($lines * $bytes_per_line);
$read_length += ($body_lines * $bytes_per_line);
$direction = $read['direction'];
if ($direction < 0) {
$offset -= $read_length;
if ($offset < 0) {
$offset = 0;
$read_length = $log_size;
}
}
$position = $log->getReadPosition($offset);
list($position_offset, $position_line) = $position;
$read_length += ($offset - $position_offset);
$reads[$key]['fetchOffset'] = $position_offset;
$reads[$key]['fetchLength'] = $read_length;
$reads[$key]['fetchLine'] = $position_line;
}
$reads = $this->mergeOverlappingReads($reads);
foreach ($reads as $key => $read) {
$data = $log->loadData($read['fetchOffset'], $read['fetchLength']);
$offset = $read['fetchOffset'];
$line = $read['fetchLine'];
$lines = $this->getLines($data);
$line_data = array();
foreach ($lines as $line_text) {
$length = strlen($line_text);
$line_data[] = array(
'offset' => $offset,
'length' => $length,
'line' => $line,
'data' => $line_text,
);
$line += 1;
$offset += $length;
}
$reads[$key]['data'] = $data;
$reads[$key]['lines'] = $line_data;
}
foreach ($views as $view_key => $view) {
$anchor_byte = $view['offset'];
if ($view['direction'] < 0) {
$anchor_byte = $anchor_byte - 1;
}
$data_key = null;
foreach ($reads as $read_key => $read) {
$s = $read['fetchOffset'];
$e = $s + $read['fetchLength'];
if (($s <= $anchor_byte) && ($e >= $anchor_byte)) {
$data_key = $read_key;
break;
}
}
if ($data_key === null) {
throw new Exception(
pht('Unable to find fetch!'));
}
$anchor_key = null;
foreach ($reads[$data_key]['lines'] as $line_key => $line) {
$s = $line['offset'];
$e = $s + $line['length'];
if (($s <= $anchor_byte) && ($e > $anchor_byte)) {
$anchor_key = $line_key;
break;
}
}
if ($anchor_key === null) {
throw new Exception(
pht(
'Unable to find lines.'));
}
- if ($direction > 0) {
+ if ($view['direction'] > 0) {
$slice_offset = $anchor_key;
} else {
$slice_offset = max(0, $anchor_key - ($view['lines'] - 1));
}
$slice_length = $view['lines'];
$views[$view_key] += array(
'sliceKey' => $data_key,
'sliceOffset' => $slice_offset,
'sliceLength' => $slice_length,
);
}
foreach ($views as $view_key => $view) {
$slice_key = $view['sliceKey'];
$lines = array_slice(
$reads[$slice_key]['lines'],
$view['sliceOffset'],
$view['sliceLength']);
$data_offset = null;
$data_length = null;
foreach ($lines as $line) {
if ($data_offset === null) {
$data_offset = $line['offset'];
}
$data_length += $line['length'];
}
// If the view cursor starts in the middle of a line, we're going to
// strip part of the line.
$direction = $view['direction'];
if ($direction > 0) {
$view_offset = $view['offset'];
$view_length = $data_length;
if ($data_offset < $view_offset) {
$trim = ($view_offset - $data_offset);
$view_length -= $trim;
}
$limit = $view['limit'];
if ($limit < ($view_offset + $view_length)) {
$view_length = ($limit - $view_offset);
}
} else {
$view_offset = $data_offset;
$view_length = $data_length;
if ($data_offset + $data_length > $view['offset']) {
$view_length -= (($data_offset + $data_length) - $view['offset']);
}
$limit = $view['limit'];
if ($limit > $view_offset) {
$view_length -= ($limit - $view_offset);
$view_offset = $limit;
}
}
$views[$view_key] += array(
'viewOffset' => $view_offset,
'viewLength' => $view_length,
);
}
$views = $this->mergeOverlappingViews($views);
foreach ($views as $view_key => $view) {
$slice_key = $view['sliceKey'];
$lines = array_slice(
$reads[$slice_key]['lines'],
$view['sliceOffset'],
$view['sliceLength']);
$view_offset = $view['viewOffset'];
foreach ($lines as $line_key => $line) {
$line_offset = $line['offset'];
if ($line_offset >= $view_offset) {
break;
}
$trim = ($view_offset - $line_offset);
if ($trim && ($trim >= strlen($line['data']))) {
unset($lines[$line_key]);
continue;
}
$line_data = substr($line['data'], $trim);
$lines[$line_key]['data'] = $line_data;
$lines[$line_key]['length'] = strlen($line_data);
$lines[$line_key]['offset'] += $trim;
break;
}
$view_end = $view['viewOffset'] + $view['viewLength'];
foreach ($lines as $line_key => $line) {
$line_end = $line['offset'] + $line['length'];
if ($line_end <= $view_end) {
continue;
}
$trim = ($line_end - $view_end);
if ($trim && ($trim >= strlen($line['data']))) {
unset($lines[$line_key]);
continue;
}
$line_data = substr($line['data'], -$trim);
$lines[$line_key]['data'] = $line_data;
$lines[$line_key]['length'] = strlen($line_data);
}
$views[$view_key]['viewData'] = $lines;
}
$spacer = null;
$render = array();
$head_view = head($views);
if ($head_view['viewOffset'] > $head_offset) {
$render[] = array(
'spacer' => true,
'head' => $head_offset,
'tail' => $head_view['viewOffset'],
);
}
foreach ($views as $view) {
if ($spacer) {
$spacer['tail'] = $view['viewOffset'];
$render[] = $spacer;
}
$render[] = $view;
$spacer = array(
'spacer' => true,
'head' => ($view['viewOffset'] + $view['viewLength']),
);
}
$tail_view = last($views);
if ($tail_view['viewOffset'] + $tail_view['viewLength'] < $tail_offset) {
$render[] = array(
'spacer' => true,
'head' => $tail_view['viewOffset'] + $tail_view['viewLength'],
'tail' => $tail_offset,
);
}
$uri = $log->getURI();
$highlight_range = $request->getURIData('lines');
$rows = array();
foreach ($render as $range) {
if (isset($range['spacer'])) {
$rows[] = $this->renderExpandRow($range);
continue;
}
$lines = $range['viewData'];
foreach ($lines as $line) {
$display_line = ($line['line'] + 1);
$display_text = ($line['data']);
$display_line = phutil_tag(
'a',
array(
'href' => $uri.'$'.$display_line,
),
$display_line);
$line_cell = phutil_tag('th', array(), $display_line);
$text_cell = phutil_tag('td', array(), $display_text);
$rows[] = phutil_tag(
'tr',
array(),
array(
$line_cell,
$text_cell,
));
}
}
$table = phutil_tag(
'table',
array(
'class' => 'harbormaster-log-table PhabricatorMonospaced',
),
$rows);
// When this is a normal AJAX request, return the rendered log fragment
// in an AJAX payload.
if ($request->isAjax()) {
return id(new AphrontAjaxResponse())
->setContent(
array(
'markup' => hsprintf('%s', $table),
));
}
// If the page is being accessed as a standalone page, present a
// readable version of the fragment for debugging.
require_celerity_resource('harbormaster-css');
$header = pht('Standalone Log Fragment');
$render_view = id(new PHUIObjectBoxView())
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setHeaderText($header)
->appendChild($table);
$page_view = id(new PHUITwoColumnView())
->setFooter($render_view);
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Build Log %d', $log->getID()), $log->getURI())
->addTextCrumb(pht('Fragment'))
->setBorder(true);
return $this->newPage()
->setTitle(
array(
pht('Build Log %d', $log->getID()),
pht('Standalone Fragment'),
))
->setCrumbs($crumbs)
->appendChild($page_view);
}
private function getTotalByteLength(HarbormasterBuildLog $log) {
$total_bytes = $log->getByteLength();
if ($total_bytes) {
return (int)$total_bytes;
}
// TODO: Remove this after enough time has passed for installs to run
// log rebuilds or decide they don't care about older logs.
// Older logs don't have this data denormalized onto the log record unless
// an administrator has run `bin/harbormaster rebuild-log --all` or
// similar. Try to figure it out by summing up the size of each chunk.
// Note that the log may also be legitimately empty and have actual size
// zero.
$chunk = new HarbormasterBuildLogChunk();
$conn = $chunk->establishConnection('r');
$row = queryfx_one(
$conn,
'SELECT SUM(size) total FROM %T WHERE logID = %d',
$chunk->getTableName(),
$log->getID());
return (int)$row['total'];
}
private function getLines($data) {
$parts = preg_split("/(\r\n|\r|\n)/", $data, 0, PREG_SPLIT_DELIM_CAPTURE);
if (last($parts) === '') {
array_pop($parts);
}
$lines = array();
for ($ii = 0; $ii < count($parts); $ii += 2) {
$line = $parts[$ii];
if (isset($parts[$ii + 1])) {
$line .= $parts[$ii + 1];
}
$lines[] = $line;
}
return $lines;
}
private function mergeOverlappingReads(array $reads) {
// Find planned reads which will overlap and merge them into a single
// larger read.
$uk = array_keys($reads);
$vk = array_keys($reads);
foreach ($uk as $ukey) {
foreach ($vk as $vkey) {
// Don't merge a range into itself, even though they do technically
// overlap.
if ($ukey === $vkey) {
continue;
}
$uread = idx($reads, $ukey);
if ($uread === null) {
continue;
}
$vread = idx($reads, $vkey);
if ($vread === null) {
continue;
}
$us = $uread['fetchOffset'];
$ue = $us + $uread['fetchLength'];
$vs = $vread['fetchOffset'];
$ve = $vs + $vread['fetchLength'];
if (($vs > $ue) || ($ve < $us)) {
continue;
}
$min = min($us, $vs);
$max = max($ue, $ve);
$reads[$ukey]['fetchOffset'] = $min;
$reads[$ukey]['fetchLength'] = ($max - $min);
$reads[$ukey]['fetchLine'] = min(
$uread['fetchLine'],
$vread['fetchLine']);
unset($reads[$vkey]);
}
}
return $reads;
}
private function mergeOverlappingViews(array $views) {
$uk = array_keys($views);
$vk = array_keys($views);
$body_lines = 8;
$body_bytes = ($body_lines * 140);
foreach ($uk as $ukey) {
foreach ($vk as $vkey) {
if ($ukey === $vkey) {
continue;
}
$uview = idx($views, $ukey);
if ($uview === null) {
continue;
}
$vview = idx($views, $vkey);
if ($vview === null) {
continue;
}
// If these views don't use the same line data, don't try to
// merge them.
if ($uview['sliceKey'] != $vview['sliceKey']) {
continue;
}
// If these views are overlapping or separated by only a few bytes,
// merge them into a single view.
$us = $uview['viewOffset'];
$ue = $us + $uview['viewLength'];
$vs = $vview['viewOffset'];
$ve = $vs + $vview['viewLength'];
$uss = $uview['sliceOffset'];
$use = $uss + $uview['sliceLength'];
$vss = $vview['sliceOffset'];
$vse = $vss + $vview['sliceLength'];
if ($ue <= $vs) {
if (($ue + $body_bytes) >= $vs) {
if (($use + $body_lines) >= $vss) {
$views[$ukey] = array(
'sliceLength' => ($vse - $uss),
'viewLength' => ($ve - $us),
) + $views[$ukey];
unset($views[$vkey]);
continue;
}
}
}
}
}
return $views;
}
private function renderExpandRow($range) {
$icon_up = id(new PHUIIconView())
->setIcon('fa-chevron-up');
$icon_down = id(new PHUIIconView())
->setIcon('fa-chevron-down');
$up_text = array(
pht('Show More Above'),
' ',
$icon_up,
);
$expand_up = javelin_tag(
'a',
array(
'sigil' => 'harbormaster-log-expand',
'meta' => array(
'headOffset' => $range['head'],
'tailOffset' => $range['tail'],
'head' => 4,
'tail' => 0,
),
),
$up_text);
$mid_text = pht(
- 'Show More (%s bytes Hidden)',
+ 'Show More (%s Bytes)',
new PhutilNumber($range['tail'] - $range['head']));
$expand_mid = javelin_tag(
'a',
array(
'sigil' => 'harbormaster-log-expand',
'meta' => array(
'headOffset' => $range['head'],
'tailOffset' => $range['tail'],
'head' => 2,
'tail' => 2,
),
),
$mid_text);
$down_text = array(
$icon_down,
' ',
pht('Show More Below'),
);
$expand_down = javelin_tag(
'a',
array(
'sigil' => 'harbormaster-log-expand',
'meta' => array(
'headOffset' => $range['head'],
'tailOffset' => $range['tail'],
'head' => 0,
'tail' => 4,
),
),
$down_text);
$expand_cells = array(
phutil_tag(
'td',
array(
'class' => 'harbormaster-log-expand-up',
),
$expand_up),
phutil_tag(
'td',
array(
'class' => 'harbormaster-log-expand-mid',
),
$expand_mid),
phutil_tag(
'td',
array(
'class' => 'harbormaster-log-expand-down',
),
$expand_down),
);
$expand_row = phutil_tag('tr', array(), $expand_cells);
$expand_table = phutil_tag(
'table',
array(
'class' => 'harbormaster-log-expand-table',
),
$expand_row);
$cells = array(
phutil_tag('th', array()),
phutil_tag(
'td',
array(
'class' => 'harbormaster-log-expand-cell',
),
$expand_table),
);
return phutil_tag('tr', array(), $cells);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Mar 14, 8:00 AM (4 h, 17 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
71699
Default Alt Text
(17 KB)

Event Timeline