Path: blob/master/src/view/control/AphrontTableView.php
12241 views
<?php12final class AphrontTableView extends AphrontView {34protected $data;5protected $headers;6protected $shortHeaders = array();7protected $rowClasses = array();8protected $columnClasses = array();9protected $cellClasses = array();10protected $zebraStripes = true;11protected $noDataString;12protected $className;13protected $notice;14protected $columnVisibility = array();15private $deviceVisibility = array();1617private $columnWidths = array();1819protected $sortURI;20protected $sortParam;21protected $sortSelected;22protected $sortReverse;23protected $sortValues = array();24private $deviceReadyTable;2526private $rowDividers = array();2728public function __construct(array $data) {29$this->data = $data;30}3132public function setHeaders(array $headers) {33$this->headers = $headers;34return $this;35}3637public function setColumnClasses(array $column_classes) {38$this->columnClasses = $column_classes;39return $this;40}4142public function setRowClasses(array $row_classes) {43$this->rowClasses = $row_classes;44return $this;45}4647public function setCellClasses(array $cell_classes) {48$this->cellClasses = $cell_classes;49return $this;50}5152public function setColumnWidths(array $widths) {53$this->columnWidths = $widths;54return $this;55}5657public function setRowDividers(array $dividers) {58$this->rowDividers = $dividers;59return $this;60}6162public function setNoDataString($no_data_string) {63$this->noDataString = $no_data_string;64return $this;65}6667public function setClassName($class_name) {68$this->className = $class_name;69return $this;70}7172public function setNotice($notice) {73$this->notice = $notice;74return $this;75}7677public function setZebraStripes($zebra_stripes) {78$this->zebraStripes = $zebra_stripes;79return $this;80}8182public function setColumnVisibility(array $visibility) {83$this->columnVisibility = $visibility;84return $this;85}8687public function setDeviceVisibility(array $device_visibility) {88$this->deviceVisibility = $device_visibility;89return $this;90}9192public function setDeviceReadyTable($ready) {93$this->deviceReadyTable = $ready;94return $this;95}9697public function setShortHeaders(array $short_headers) {98$this->shortHeaders = $short_headers;99return $this;100}101102/**103* Parse a sorting parameter:104*105* list($sort, $reverse) = AphrontTableView::parseSortParam($sort_param);106*107* @param string Sort request parameter.108* @return pair Sort value, sort direction.109*/110public static function parseSort($sort) {111return array(ltrim($sort, '-'), preg_match('/^-/', $sort));112}113114public function makeSortable(115PhutilURI $base_uri,116$param,117$selected,118$reverse,119array $sort_values) {120121$this->sortURI = $base_uri;122$this->sortParam = $param;123$this->sortSelected = $selected;124$this->sortReverse = $reverse;125$this->sortValues = array_values($sort_values);126127return $this;128}129130public function render() {131require_celerity_resource('aphront-table-view-css');132133$table = array();134135$col_classes = array();136foreach ($this->columnClasses as $key => $class) {137if ($class !== null && strlen($class)) {138$col_classes[] = $class;139} else {140$col_classes[] = null;141}142}143144$visibility = array_values($this->columnVisibility);145$device_visibility = array_values($this->deviceVisibility);146147$column_widths = $this->columnWidths;148149$headers = $this->headers;150$short_headers = $this->shortHeaders;151$sort_values = $this->sortValues;152if ($headers) {153while (count($headers) > count($visibility)) {154$visibility[] = true;155}156while (count($headers) > count($device_visibility)) {157$device_visibility[] = true;158}159while (count($headers) > count($short_headers)) {160$short_headers[] = null;161}162while (count($headers) > count($sort_values)) {163$sort_values[] = null;164}165166$tr = array();167foreach ($headers as $col_num => $header) {168if (!$visibility[$col_num]) {169continue;170}171172$classes = array();173174if (!empty($col_classes[$col_num])) {175$classes[] = $col_classes[$col_num];176}177178if (empty($device_visibility[$col_num])) {179$classes[] = 'aphront-table-view-nodevice';180}181182if ($sort_values[$col_num] !== null) {183$classes[] = 'aphront-table-view-sortable';184185$sort_value = $sort_values[$col_num];186$sort_glyph_class = 'aphront-table-down-sort';187if ($sort_value == $this->sortSelected) {188if ($this->sortReverse) {189$sort_glyph_class = 'aphront-table-up-sort';190} else if (!$this->sortReverse) {191$sort_value = '-'.$sort_value;192}193$classes[] = 'aphront-table-view-sortable-selected';194}195196$sort_glyph = phutil_tag(197'span',198array(199'class' => $sort_glyph_class,200),201'');202203$header = phutil_tag(204'a',205array(206'href' => $this->sortURI->alter($this->sortParam, $sort_value),207'class' => 'aphront-table-view-sort-link',208),209array(210$header,211' ',212$sort_glyph,213));214}215216if ($classes) {217$class = implode(' ', $classes);218} else {219$class = null;220}221222if ($short_headers[$col_num] !== null) {223$header_nodevice = phutil_tag(224'span',225array(226'class' => 'aphront-table-view-nodevice',227),228$header);229$header_device = phutil_tag(230'span',231array(232'class' => 'aphront-table-view-device',233),234$short_headers[$col_num]);235236$header = hsprintf('%s %s', $header_nodevice, $header_device);237}238239$style = null;240if (isset($column_widths[$col_num])) {241$style = 'width: '.$column_widths[$col_num].';';242}243244$tr[] = phutil_tag(245'th',246array(247'class' => $class,248'style' => $style,249),250$header);251}252$table[] = phutil_tag('tr', array(), $tr);253}254255foreach ($col_classes as $key => $value) {256257if (isset($sort_values[$key]) &&258($sort_values[$key] == $this->sortSelected)) {259$value = trim($value.' sorted-column');260}261262if ($value !== null) {263$col_classes[$key] = $value;264}265}266267$dividers = $this->rowDividers;268269$data = $this->data;270if ($data) {271$row_num = 0;272$row_idx = 0;273foreach ($data as $row) {274$is_divider = !empty($dividers[$row_num]);275276$row_size = count($row);277while (count($row) > count($col_classes)) {278$col_classes[] = null;279}280while (count($row) > count($visibility)) {281$visibility[] = true;282}283while (count($row) > count($device_visibility)) {284$device_visibility[] = true;285}286$tr = array();287// NOTE: Use of a separate column counter is to allow this to work288// correctly if the row data has string or non-sequential keys.289$col_num = 0;290foreach ($row as $value) {291if (!$visibility[$col_num]) {292++$col_num;293continue;294}295$class = $col_classes[$col_num];296if (empty($device_visibility[$col_num])) {297$class = trim($class.' aphront-table-view-nodevice');298}299if (!empty($this->cellClasses[$row_num][$col_num])) {300$class = trim($class.' '.$this->cellClasses[$row_num][$col_num]);301}302303if ($is_divider) {304$tr[] = phutil_tag(305'td',306array(307'class' => 'row-divider',308'colspan' => count($visibility),309),310$value);311$row_idx = -1;312break;313}314315$tr[] = phutil_tag(316'td',317array(318'class' => $class,319),320$value);321++$col_num;322}323324$class = idx($this->rowClasses, $row_num);325if ($this->zebraStripes && ($row_idx % 2)) {326if ($class !== null) {327$class = 'alt alt-'.$class;328} else {329$class = 'alt';330}331}332333$table[] = phutil_tag('tr', array('class' => $class), $tr);334++$row_num;335++$row_idx;336}337} else {338$colspan = max(count(array_filter($visibility)), 1);339$table[] = phutil_tag(340'tr',341array('class' => 'no-data'),342phutil_tag(343'td',344array('colspan' => $colspan),345coalesce($this->noDataString, pht('No data available.'))));346}347348$classes = array();349$classes[] = 'aphront-table-view';350if ($this->className !== null) {351$classes[] = $this->className;352}353354if ($this->deviceReadyTable) {355$classes[] = 'aphront-table-view-device-ready';356}357358if ($this->columnWidths) {359$classes[] = 'aphront-table-view-fixed';360}361362$notice = null;363if ($this->notice) {364$notice = phutil_tag(365'div',366array(367'class' => 'aphront-table-notice',368),369$this->notice);370}371372$html = phutil_tag(373'table',374array(375'class' => implode(' ', $classes),376),377$table);378379return phutil_tag_div(380'aphront-table-wrap',381array(382$notice,383$html,384));385}386387public static function renderSingleDisplayLine($line) {388389// TODO: Is there a cleaner way to do this? We use a relative div with390// overflow hidden to provide the bounds, and an absolute span with391// white-space: pre to prevent wrapping. We need to append a character392// ( -- nonbreaking space) afterward to give the bounds div height393// (alternatively, we could hard-code the line height). This is gross but394// it's not clear that there's a better appraoch.395396return phutil_tag(397'div',398array(399'class' => 'single-display-line-bounds',400),401array(402phutil_tag(403'span',404array(405'class' => 'single-display-line-content',406),407$line),408"\xC2\xA0",409));410}411412413}414415416