diff --git a/batch/reviewcsv.php b/batch/reviewcsv.php
index 2a0967df0..c4e037f9e 100644
--- a/batch/reviewcsv.php
+++ b/batch/reviewcsv.php
@@ -51,7 +51,7 @@ class ReviewCSV_Batch {
function __construct($conf) {
$this->conf = $conf;
$this->user = $conf->root_user();
- $this->fr = new FieldRender(FieldRender::CFLIST | FieldRender::CFCSV | FieldRender::CFHTML, $this->user);
+ $this->fr = new FieldRender(FieldRender::CFHTML | FieldRender::CFVERBOSE, $this->user);
$this->csv = new CsvGenerator;
$this->rfseen = $conf->review_form()->order_array(false);
}
diff --git a/src/fieldrender.php b/src/fieldrender.php
index 3b263b2a5..735c81e39 100644
--- a/src/fieldrender.php
+++ b/src/fieldrender.php
@@ -3,11 +3,14 @@
// Copyright (c) 2006-2022 Eddie Kohler; see LICENSE.
class FieldRender {
- /** @var ?Contact */
+ /** @var ?Contact
+ * @readonly */
public $user;
- /** @var ?PaperTable */
+ /** @var ?PaperTable
+ * @readonly */
public $table;
- /** @var int */
+ /** @var int
+ * @readonly */
public $context;
/** @var null|false|string */
public $title;
@@ -19,28 +22,39 @@ class FieldRender {
public $value_long;
const CFHTML = 0x1;
- const CFPAGE = 0x2;
- const CFLIST = 0x4;
- const CFCOLUMN = 0x8;
- const CFSUGGEST = 0x10;
- const CFCSV = 0x20;
- const CFMAIL = 0x40;
- const CFFORM = 0x80;
- const CFVERBOSE = 0x100;
+ const CFTEXT = 0x2;
- const CTEXT = 0;
- const CPAGE = 3;
+ const CFPAGE = 0x10;
+ const CFFORM = 0x20;
+ const CFLIST = 0x40;
+ const CFMAIL = 0x80;
+ const CFSUGGEST = 0x100;
+
+ const CFCSV = 0x1000;
+ const CFROW = 0x2000;
+ const CFCOLUMN = 0x4000;
+ const CFVERBOSE = 0x8000;
/** @param int $context */
function __construct($context, Contact $user = null) {
+ assert(($context & 3) !== 0 && ($context & 3) !== 3);
+ assert((($context & 0xFF0) & (($context & 0xFF0) - 1)) === 0);
$this->context = $context;
$this->user = $user;
}
- /** @param ?int $context */
- function clear($context = null) {
- if ($context !== null) {
- $this->context = $context;
- }
+ /** @param PaperTable $table
+ * @return $this
+ * @suppress PhanAccessReadOnlyProperty */
+ function make_table($table) {
+ assert(($this->context & self::CFHTML) === self::CFHTML);
+ assert(($this->context & (self::CFPAGE | self::CFFORM)) !== 0);
+ assert(!$this->table && (!$this->user || $this->user === $table->user));
+ $this->user = $table->user;
+ $this->table = $table;
+ return $this;
+ }
+
+ function clear() {
$this->title = null;
$this->value = $this->value_format = $this->value_long = null;
}
@@ -48,31 +62,43 @@ function clear($context = null) {
function is_empty() {
return (string) $this->title === "" && (string) $this->value === "";
}
- /** @return bool */
+ /** @param int $context
+ * @return bool */
+ function want($context) {
+ return ($this->context & $context) === $context;
+ }
+ /** @return bool
+ * @deprecated */
function for_page() {
return ($this->context & self::CFPAGE) !== 0;
}
- /** @return bool */
+ /** @return bool
+ * @deprecated */
function for_form() {
return ($this->context & self::CFFORM) !== 0;
}
- /** @return bool */
+ /** @return bool
+ * @deprecated */
function want_text() {
return ($this->context & self::CFHTML) === 0;
}
- /** @return bool */
+ /** @return bool
+ * @deprecated */
function want_html() {
return ($this->context & self::CFHTML) !== 0;
}
- /** @return bool */
+ /** @return bool
+ * @deprecated */
function want_list() {
return ($this->context & self::CFLIST) !== 0;
}
- /** @return bool */
+ /** @return bool
+ * @deprecated */
function want_list_row() {
return ($this->context & (self::CFLIST | self::CFCOLUMN)) === self::CFLIST;
}
- /** @return bool */
+ /** @return bool
+ * @deprecated */
function want_list_column() {
return ($this->context & (self::CFLIST | self::CFCOLUMN)) ===
(self::CFLIST | self::CFCOLUMN);
@@ -98,13 +124,12 @@ function set_html($t) {
/** @param bool $b
* @return $this */
function set_bool($b) {
- $v = $this->verbose();
- if ($this->context & self::CFHTML) {
- $this->set_text($b ? "✓" : ($v ? "✗" : ""));
- } else if ($this->context & self::CFCSV) {
- $this->set_text($b ? "Y" : ($v ? "N" : ""));
+ if (($this->context & self::CFHTML) !== 0) {
+ $this->set_text($b ? "✓" : "✗");
+ } else if (($this->context & self::CFCSV) !== 0) {
+ $this->set_text($b ? "Y" : "N");
} else {
- $this->set_text($b ? "Yes" : ($v ? "No" : ""));
+ $this->set_text($b ? "Yes" : "No");
}
return $this;
}
diff --git a/src/formula.php b/src/formula.php
index 5b29bc134..852b632cf 100644
--- a/src/formula.php
+++ b/src/formula.php
@@ -2608,7 +2608,7 @@ function unparse_text($x, $real_format) {
return $this->_format_detail->unparse_computed($rx, $real_format);
} else if ($this->_format === Fexpr::FSUBFIELD) {
$prow = $this->placeholder_prow();
- $fr = new FieldRender(FieldRender::CFCSV);
+ $fr = new FieldRender(FieldRender::CFTEXT | FieldRender::CFCSV | FieldRender::CFVERBOSE);
$this->_format_detail->render($fr, new PaperValue($prow, $x));
return $fr->value; // XXX
} else if ($this->_format === Fexpr::FPREFEXPERTISE) {
diff --git a/src/hotcrpmailer.php b/src/hotcrpmailer.php
index fb9147a37..46ba41f7d 100644
--- a/src/hotcrpmailer.php
+++ b/src/hotcrpmailer.php
@@ -469,7 +469,7 @@ function kw_paperfield($args, $isbool, $uf) {
|| !($ov = $this->row->option($uf->option))) {
return $isbool ? false : "";
} else {
- $fr = new FieldRender(FieldRender::CFMAIL, $this->permuser);
+ $fr = new FieldRender(FieldRender::CFTEXT | FieldRender::CFMAIL, $this->permuser);
$uf->option->render($fr, $ov);
if ($isbool) {
return ($fr->value ?? "") !== "";
diff --git a/src/listactions/la_getabstracts.php b/src/listactions/la_getabstracts.php
index 279e6f461..d1d6f829a 100644
--- a/src/listactions/la_getabstracts.php
+++ b/src/listactions/la_getabstracts.php
@@ -44,7 +44,7 @@ static function render(PaperInfo $prow, Contact $user) {
$n = prefix_word_wrap("", "Submission #{$prow->paperId}: {$prow->title}", 0, self::WIDTH);
$text = $n . str_repeat("=", min(self::WIDTH, strlen($n) - 1)) . "\n\n";
- $fr = new FieldRender(FieldRender::CTEXT, $user);
+ $fr = new FieldRender(FieldRender::CFTEXT, $user);
foreach ($user->conf->options()->page_fields($prow) as $o) {
if (($o->id <= 0 || $user->allow_view_option($prow, $o))
&& $o->on_page()) {
diff --git a/src/options/o_abstract.php b/src/options/o_abstract.php
index 00af142c1..2187e53b6 100644
--- a/src/options/o_abstract.php
+++ b/src/options/o_abstract.php
@@ -41,7 +41,7 @@ function print_web_edit(PaperTable $pt, $ov, $reqov) {
$this->print_web_edit_text($pt, $ov, $reqov, ["rows" => 5]);
}
function render(FieldRender $fr, PaperValue $ov) {
- if ($fr->for_page()) {
+ if ($fr->want(FieldRender::CFPAGE)) {
$fr->table->render_abstract($fr, $this);
} else {
$text = $ov->prow->abstract();
diff --git a/src/options/o_attachments.php b/src/options/o_attachments.php
index 0e845ba6a..565151158 100644
--- a/src/options/o_attachments.php
+++ b/src/options/o_attachments.php
@@ -173,13 +173,14 @@ function print_web_edit(PaperTable $pt, $ov, $reqov) {
function render(FieldRender $fr, PaperValue $ov) {
$ts = [];
foreach ($ov->document_set() as $d) {
- if ($fr->want_text()) {
+ if ($fr->want(FieldRender::CFTEXT)) {
$ts[] = $d->member_filename();
} else {
$linkname = htmlspecialchars($d->member_filename());
- if ($fr->want_list()) {
+ $dif = 0;
+ if ($fr->want(FieldRender::CFLIST)) {
$dif = DocumentInfo::L_SMALL | DocumentInfo::L_NOSIZE;
- } else if ($fr->for_form()) {
+ } else if ($fr->want(FieldRender::CFFORM)) {
$dif = 0;
} else if ($this->display() === PaperOption::DISP_TOP) {
$dif = 0;
@@ -194,24 +195,26 @@ function render(FieldRender $fr, PaperValue $ov) {
$ts[] = $t;
}
}
- if (!empty($ts)) {
- if ($fr->want_text()) {
- $fr->set_text(join("; ", $ts));
- } else if ($fr->want_list_row()) {
- $fr->set_html('
');
- } else {
- $fr->set_html('');
+ if (empty($ts)) {
+ if ($fr->verbose()) {
+ $fr->set_text("None");
}
- if ($fr->for_page() && $this->display() === PaperOption::DISP_TOP) {
- $fr->title = false;
- $v = '';
- if ($fr->table && $fr->user->view_option_state($ov->prow, $this) === 1) {
- $v = ' fx8';
- }
- $fr->value = "{$fr->value}
";
+ return;
+ }
+ if ($fr->want(FieldRender::CFTEXT)) {
+ $fr->set_text(join("; ", $ts));
+ } else if ($fr->want(FieldRender::CFLIST | FieldRender::CFROW)) {
+ $fr->set_html('');
+ } else {
+ $fr->set_html('');
+ }
+ if ($fr->want(FieldRender::CFPAGE) && $this->display() === PaperOption::DISP_TOP) {
+ $fr->title = false;
+ $v = '';
+ if ($fr->table && $fr->user->view_option_state($ov->prow, $this) === 1) {
+ $v = ' fx8';
}
- } else if ($fr->verbose()) {
- $fr->set_text("None");
+ $fr->value = "{$fr->value}
";
}
}
diff --git a/src/options/o_authors.php b/src/options/o_authors.php
index 171ce2981..e7b2aaeb6 100644
--- a/src/options/o_authors.php
+++ b/src/options/o_authors.php
@@ -284,7 +284,7 @@ function print_web_edit(PaperTable $pt, $ov, $reqov) {
}
function render(FieldRender $fr, PaperValue $ov) {
- if ($fr->for_page()) {
+ if ($fr->want(FieldRender::CFPAGE)) {
$fr->table->render_authors($fr, $this);
} else {
$names = [""];
diff --git a/src/papercolumns/pc_option.php b/src/papercolumns/pc_option.php
index 2fab1adc3..fbf3ddbb8 100644
--- a/src/papercolumns/pc_option.php
+++ b/src/papercolumns/pc_option.php
@@ -17,7 +17,7 @@ function prepare(PaperList $pl, $visible) {
return false;
}
$pl->qopts["options"] = true;
- $this->fr = new FieldRender(0, $pl->user);
+ $this->fr = new FieldRender($pl->render_context | ($this->as_row ? FieldRender::CFROW : FieldRender::CFCOLUMN), $pl->user);
if ($this->as_row) {
$this->className = ltrim(preg_replace('/(?: +|\A)(?:plrd|plr|plc)(?= |\z)/', "", $this->className));
}
@@ -43,7 +43,7 @@ function content(PaperList $pl, PaperInfo $row) {
}
$fr = $this->fr;
- $fr->clear(FieldRender::CFLIST | FieldRender::CFHTML | ($this->as_row ? 0 : FieldRender::CFCOLUMN));
+ $fr->clear();
$this->opt->render($fr, $ov);
if ((string) $fr->value === "") {
return "";
@@ -74,7 +74,7 @@ function text(PaperList $pl, PaperInfo $row) {
return "";
}
- $this->fr->clear(FieldRender::CFCSV | FieldRender::CFVERBOSE);
+ $this->fr->clear();
$this->opt->render($this->fr, $ov);
return (string) $this->fr->value;
}
diff --git a/src/paperlist.php b/src/paperlist.php
index 886499a75..dc49c856e 100644
--- a/src/paperlist.php
+++ b/src/paperlist.php
@@ -290,6 +290,9 @@ class PaperList {
// columns access
public $qopts; // set by PaperColumn::prepare
+ /** @var int
+ * @readonly */
+ public $render_context;
/** @var array> */
public $table_attr;
/** @var array */
@@ -760,8 +763,6 @@ function apply_view_qreq(Qrequest $qreq) {
/** @param bool $report_diff
* @return list */
function unparse_view($report_diff = false) {
- $this->_prepare();
-
// views
$res = [];
$nextpos = 1000000;
@@ -1254,8 +1255,17 @@ static function vcolumn_order_compare($f1, $f2) {
return strnatcasecmp($f1->name, $f2->name);
}
- private function _set_vcolumns() {
+ /** @param int $context */
+ private function _reset_vcolumns($context) {
+ // reset
+ $this->_has = [];
+ $this->count = 0;
+ $this->_bulkwarn_count = 0;
+ $this->need_render = false;
+ $this->_vcolumns = [];
$this->table_attr = [];
+ /** @phan-suppress-next-line PhanAccessReadOnlyProperty */
+ $this->render_context = $context;
assert(empty($this->row_attr));
// extract columns from _viewf
@@ -1767,14 +1777,6 @@ private function _analyze_folds() {
$this->table_attr["data-columns"] = $jscol; /* XXX backward compat */
}
- private function _prepare() {
- $this->_has = [];
- $this->count = 0;
- $this->_bulkwarn_count = 0;
- $this->need_render = false;
- $this->_vcolumns = [];
- }
-
/** @param PaperListTableRender $rstate
* @return string */
private function _statistics_rows($rstate) {
@@ -2021,14 +2023,13 @@ private function _drag_action() {
/** @return PaperListTableRender */
private function _table_render() {
- $this->_prepare();
// need tags for row coloring
if ($this->user->can_view_tags(null)) {
$this->qopts["tags"] = true;
}
// get column list
- $this->_set_vcolumns();
+ $this->_reset_vcolumns(FieldRender::CFLIST | FieldRender::CFHTML);
if (empty($this->_vcolumns)) {
return PaperListTableRender::make_error("Nothing to show");
}
@@ -2264,8 +2265,7 @@ function table_html() {
/** @return array{fields:array,data:array,attr?:array,stat?:array} */
function table_html_json() {
// get column list, check sort
- $this->_prepare();
- $this->_set_vcolumns();
+ $this->_reset_vcolumns(FieldRender::CFLIST | FieldRender::CFHTML);
if (empty($this->_vcolumns)) {
return ["fields" => [], "data" => []];
}
@@ -2327,8 +2327,7 @@ function table_html_json() {
/** @return array> */
function text_json() {
// get column list, check sort
- $this->_prepare();
- $this->_set_vcolumns();
+ $this->_reset_vcolumns(FieldRender::CFTEXT | FieldRender::CFCSV | FieldRender::CFVERBOSE);
$data = [];
if (!empty($this->_vcolumns)) {
foreach ($this->rowset() as $row) {
@@ -2380,8 +2379,7 @@ private function _mark_groups_csv($grouppos, &$body) {
/** @return array{array,list>} */
function text_csv() {
// get column list, check sort
- $this->_prepare();
- $this->_set_vcolumns();
+ $this->_reset_vcolumns(FieldRender::CFTEXT | FieldRender::CFCSV | FieldRender::CFVERBOSE);
// collect row data
$body = [];
diff --git a/src/paperoption.php b/src/paperoption.php
index bf7f396b5..4a20473b9 100644
--- a/src/paperoption.php
+++ b/src/paperoption.php
@@ -756,7 +756,7 @@ function export_setting() {
if (strcasecmp($this->description, "none") === 0) {
$sfs->description = "none";
} else {
- $fr = new FieldRender(FieldRender::CFPAGE | FieldRender::CFHTML);
+ $fr = new FieldRender(FieldRender::CFHTML);
$this->render_description($fr);
$sfs->description = $fr->value_html();
}
@@ -1052,11 +1052,13 @@ function print_web_edit(PaperTable $pt, $ov, $reqov) {
}
function render(FieldRender $fr, PaperValue $ov) {
- if ($fr->for_page() && $ov->value) {
- $fr->title = "";
- $fr->set_html('✓ ' . $this->title_html() . '');
- } else {
+ if ($ov->value || $fr->verbose()) {
$fr->set_bool(!!$ov->value);
+ if ($fr->want(FieldRender::CFPAGE)) {
+ $fr->title = "";
+ $th = $this->title_html();
+ $fr->value .= " {$th}";
+ }
}
}
@@ -1537,24 +1539,37 @@ function validate_document(DocumentInfo $doc) {
}
function render(FieldRender $fr, PaperValue $ov) {
- if ($this->id <= 0 && $fr->for_page()) {
+ if ($this->id <= 0 && $fr->want(FieldRender::CFPAGE)) {
if ($this->id === 0) {
$fr->table->render_submission($fr, $this);
}
- } else if (($d = $ov->document(0))) {
- if ($fr->want_text()) {
- $fr->set_text($d->filename);
- } else if ($fr->for_form()) {
- $fr->set_html($d->link_html(htmlspecialchars($d->filename), 0));
- } else if ($fr->for_page()) {
- $th = $this->title_html();
- $dif = $this->display() === PaperOption::DISP_TOP ? 0 : DocumentInfo::L_SMALL;
- $fr->title = "";
- $fr->set_html($d->link_html("{$th}", $dif));
+ return;
+ }
+
+ $d = $ov->document(0);
+ if (!$d) {
+ if ($fr->verbose()) {
+ $fr->set_text("None");
+ }
+ return;
+ }
+
+ if ($fr->want(FieldRender::CFTEXT)) {
+ $fr->set_text($d->export_filename());
+ } else if ($fr->want(FieldRender::CFFORM)) {
+ $fr->set_html($d->link_html(htmlspecialchars($d->filename), 0));
+ } else if ($fr->want(FieldRender::CFPAGE)) {
+ $th = $this->title_html();
+ $dif = $this->display() === PaperOption::DISP_TOP ? 0 : DocumentInfo::L_SMALL;
+ $fr->title = "";
+ $fr->set_html($d->link_html("{$th}", $dif));
+ } else {
+ if ($fr->verbose() || $fr->want(FieldRender::CFLIST | FieldRender::CFROW)) {
+ $th = $d->export_filename();
} else {
- $th = $fr->verbose() ? $d->export_filename() : "";
- $fr->set_html($d->link_html($th, DocumentInfo::L_SMALL | DocumentInfo::L_NOSIZE));
+ $th = "";
}
+ $fr->set_html($d->link_html($th, DocumentInfo::L_SMALL | DocumentInfo::L_NOSIZE));
}
}
diff --git a/src/papertable.php b/src/papertable.php
index 9d672a253..054d76762 100644
--- a/src/papertable.php
+++ b/src/papertable.php
@@ -1248,8 +1248,7 @@ private function _print_normal_body() {
htmlspecialchars($name), "";
$renders = [];
- $fr = new FieldRender(FieldRender::CPAGE, $this->user);
- $fr->table = $this;
+ $fr = (new FieldRender(FieldRender::CFHTML | FieldRender::CFPAGE))->make_table($this);
foreach ($this->prow->page_fields() as $o) {
if ($o->display() < PaperOption::DISP_TOP
|| $o->display() > PaperOption::DISP_REST
@@ -2317,8 +2316,7 @@ private function _print_editable_form() {
}
private function _print_editable_fields() {
- $fr = new FieldRender(FieldRender::CFHTML | FieldRender::CFFORM | FieldRender::CFVERBOSE, $this->user);
- $fr->table = $this;
+ $fr = (new FieldRender(FieldRender::CFHTML | FieldRender::CFFORM | FieldRender::CFVERBOSE))->make_table($this);
foreach ($this->prow->form_fields() as $o) {
if (!$this->user->can_view_option($this->prow, $o)) {
continue;
diff --git a/src/settings/s_options.php b/src/settings/s_options.php
index 6dd0b06a9..11e8248d3 100644
--- a/src/settings/s_options.php
+++ b/src/settings/s_options.php
@@ -795,7 +795,7 @@ private function _intrinsic_member_defaults($sv, $mname, $oopt, $nopt, $isfs) {
if ($mname === "name") {
return [$oopt->default_edit_title(), $nopt->default_edit_title()];
} else if ($mname === "description") {
- $fr = new FieldRender(FieldRender::CFPAGE | FieldRender::CFHTML);
+ $fr = new FieldRender(FieldRender::CFHTML);
$oopt->render_default_description($fr);
$d1 = $fr->value_html();
$fr->clear();