1<?php 2// messageset.php -- HotCRP sets of messages by fields 3// Copyright (c) 2006-2018 Eddie Kohler; see LICENSE. 4 5class MessageSet { 6 public $ignore_msgs = false; 7 private $allow_error; 8 private $werror; 9 private $errf; 10 private $msgs; 11 private $canonfield; 12 public $has_warning; 13 public $has_error; 14 15 const INFO = 0; 16 const WARNING = 1; 17 const ERROR = 2; 18 19 function __construct() { 20 $this->clear(); 21 } 22 function clear_messages() { 23 $this->errf = $this->msgs = []; 24 $this->has_warning = $this->has_error = 0; 25 } 26 function clear() { 27 $this->clear_messages(); 28 } 29 30 function translate_field($src, $dst) { 31 $this->canonfield[$src] = $this->canonical_field($dst); 32 } 33 function canonical_field($field) { 34 if ($field && $this->canonfield && isset($this->canonfield[$field])) 35 $field = $this->canonfield[$field]; 36 return $field; 37 } 38 function allow_error_at($field, $set = null) { 39 $field = $this->canonical_field($field); 40 if ($set === null) 41 return $this->allow_error && isset($this->allow_error[$field]); 42 else if ($set) 43 $this->allow_error[$field] = true; 44 else if ($this->allow_error) 45 unset($this->allow_error[$field]); 46 } 47 function werror_at($field, $set = null) { 48 $field = $this->canonical_field($field); 49 if ($set === null) 50 return $this->werror && isset($this->werror[$field]); 51 else if ($set) 52 $this->werror[$field] = true; 53 else if ($this->werror) 54 unset($this->werror[$field]); 55 } 56 57 function msg($field, $msg, $status) { 58 if ($this->ignore_msgs) 59 return; 60 $this->canonfield && ($field = $this->canonical_field($field)); 61 if ($status == self::WARNING 62 && $field && $this->werror && isset($this->werror[$field])) 63 $status = self::ERROR; 64 if ($field) 65 $this->errf[$field] = max(get($this->errf, $field, 0), $status); 66 if ($msg) 67 $this->msgs[] = [$field, $msg, $status]; 68 if ($status == self::WARNING) 69 ++$this->has_warning; 70 if ($status == self::ERROR 71 && !($field && $this->allow_error && isset($this->allow_error[$field]))) 72 ++$this->has_error; 73 } 74 function error_at($field, $msg) { 75 $this->msg($field, $msg, self::ERROR); 76 } 77 function warning_at($field, $msg) { 78 $this->msg($field, $msg, self::WARNING); 79 } 80 function info_at($field, $msg) { 81 $this->msg($field, $msg, self::INFO); 82 } 83 84 function has_error() { 85 return $this->has_error > 0; 86 } 87 function nerrors() { 88 return $this->has_error; 89 } 90 function has_warning() { 91 return $this->has_warning > 0; 92 } 93 function nwarnings() { 94 return $this->has_warning; 95 } 96 function has_problem() { 97 return $this->has_warning > 0 || $this->has_error > 0; 98 } 99 function has_messages() { 100 return !empty($this->msgs); 101 } 102 function problem_status() { 103 if ($this->has_error > 0) 104 return self::ERROR; 105 else 106 return $this->has_warning > 0 ? self::WARNING : self::INFO; 107 } 108 function has_error_at($field) { 109 $this->canonfield && ($field = $this->canonical_field($field)); 110 return get($this->errf, $field, 0) > 1; 111 } 112 function has_problem_at($field) { 113 $this->canonfield && ($field = $this->canonical_field($field)); 114 return get($this->errf, $field, 0) > 0; 115 } 116 function problem_status_at($field) { 117 $this->canonfield && ($field = $this->canonical_field($field)); 118 return get($this->errf, $field, 0); 119 } 120 function control_class($field, $rest = "") { 121 $x = $field ? get($this->errf, $field, 0) : 0; 122 if ($x >= self::ERROR) 123 return $rest === "" ? "has-error" : $rest . " has-error"; 124 else if ($x === self::WARNING) 125 return $rest === "" ? "has-warning" : $rest . " has-warning"; 126 else 127 return $rest; 128 } 129 130 static private function filter_msgs($ms, $include_fields) { 131 if ($include_fields || empty($ms)) 132 return $ms ? : []; 133 else 134 return array_map(function ($mx) { return $mx[1]; }, $ms); 135 } 136 function message_field_map() { 137 return $this->errf; 138 } 139 function message_fields() { 140 return array_keys($this->errf); 141 } 142 function error_fields() { 143 if (!$this->has_error) 144 return []; 145 return array_keys(array_filter($this->errf, function ($v) { return $v >= self::ERROR; })); 146 } 147 function problem_fields() { 148 return array_keys(array_filter($this->errf, function ($v) { return $v >= self::WARNING; })); 149 } 150 function messages($include_fields = false) { 151 return self::filter_msgs($this->msgs, $include_fields); 152 } 153 function errors($include_fields = false) { 154 if (!$this->has_error) 155 return []; 156 $ms = array_filter($this->msgs, function ($mx) { return $mx[2] >= self::ERROR; }); 157 return self::filter_msgs($ms, $include_fields); 158 } 159 function warnings($include_fields = false) { 160 if (!$this->has_warning) 161 return []; 162 $ms = array_filter($this->msgs, function ($mx) { return $mx[2] == self::WARNING; }); 163 return self::filter_msgs($ms, $include_fields); 164 } 165 function messages_at($field, $include_fields = false) { 166 if (empty($this->msgs) || !isset($this->errf[$field])) 167 return []; 168 $this->canonfield && ($field = $this->canonical_field($field)); 169 $ms = array_filter($this->msgs, function ($mx) use ($field) { return $mx[0] === $field; }); 170 if ($include_fields) 171 return $ms; 172 else 173 return array_map(function ($mx) { return $mx[1]; }, $ms); 174 } 175} 176