@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at recaptime-dev/main 307 lines 7.1 kB view raw
1<?php 2 3abstract class AphrontFormControl extends AphrontView { 4 5 private $label; 6 private $ariaLabel; 7 private $caption; 8 private $error; 9 private $name; 10 private $value; 11 private $disabled; 12 private $id; 13 private $controlID; 14 private $controlStyle; 15 private $required; 16 private $hidden; 17 private $classes; 18 private $hasCopyButton; 19 20 public function setHidden($hidden) { 21 $this->hidden = $hidden; 22 return $this; 23 } 24 25 public function setID($id) { 26 $this->id = $id; 27 return $this; 28 } 29 30 public function getID() { 31 return $this->id; 32 } 33 34 public function setControlID($control_id) { 35 $this->controlID = $control_id; 36 return $this; 37 } 38 39 public function getControlID() { 40 return $this->controlID; 41 } 42 43 public function setControlStyle($control_style) { 44 $this->controlStyle = $control_style; 45 return $this; 46 } 47 48 public function getControlStyle() { 49 return $this->controlStyle; 50 } 51 52 public function setLabel($label) { 53 $this->label = $label; 54 return $this; 55 } 56 57 /** 58 * Explicitly set an aria-label attribute for accessibility. Only to be used 59 * when no visible label is already set via setLabel(). 60 * @param string $aria_label aria-label text to add to the form control 61 */ 62 public function setAriaLabel($aria_label) { 63 $this->ariaLabel = $aria_label; 64 return $this; 65 } 66 67 public function getAriaLabel() { 68 return $this->ariaLabel; 69 } 70 71 public function getLabel() { 72 return $this->label; 73 } 74 75 /** 76 * Set the Caption 77 * The Caption shows a tip usually nearby the related input field. 78 * @param string|PhutilSafeHTML|null $caption 79 * @return self 80 */ 81 public function setCaption($caption) { 82 $this->caption = $caption; 83 return $this; 84 } 85 86 /** 87 * Get the Caption 88 * The Caption shows a tip usually nearby the related input field. 89 * @return string|PhutilSafeHTML|null 90 */ 91 public function getCaption() { 92 return $this->caption; 93 } 94 95 public function setError($error) { 96 $this->error = $error; 97 return $this; 98 } 99 100 public function getError() { 101 return $this->error; 102 } 103 104 public function setName($name) { 105 $this->name = $name; 106 return $this; 107 } 108 109 public function getName() { 110 return $this->name; 111 } 112 113 public function setValue($value) { 114 $this->value = $value; 115 return $this; 116 } 117 118 /** 119 * Whether a button is displayed next to the control which allows the user to 120 * copy the value in the form control. Common use cases include <input> 121 * (AphrontFormTextControl) and <textarea> (AphrontFormTextAreaControl) 122 * elements displaying read-only data such as tokens or passphrases. This is 123 * only to get the CSS right; actual button implementation is in subclasses. 124 * 125 * @return bool 126 */ 127 public function getHasCopyButton() { 128 return $this->hasCopyButton; 129 } 130 131 /** 132 * Whether to display a button next to the control which allows the user to 133 * copy the value in the form control. Common use cases include <input> 134 * (AphrontFormTextControl) and <textarea> (AphrontFormTextAreaControl) 135 * elements displaying read-only data such as tokens or passphrases. This is 136 * only to get the CSS right; actual button implementation is in subclasses. 137 * 138 * @param bool $has_copy_button 139 */ 140 public function setHasCopyButton($has_copy_button) { 141 $this->hasCopyButton = $has_copy_button; 142 return $this; 143 } 144 145 public function getValue() { 146 return $this->value; 147 } 148 149 public function isValid() { 150 if ($this->error && $this->error !== true) { 151 return false; 152 } 153 154 if ($this->isRequired() && $this->isEmpty()) { 155 return false; 156 } 157 158 return true; 159 } 160 161 public function isRequired() { 162 return $this->required; 163 } 164 165 public function isEmpty() { 166 return !strlen($this->getValue()); 167 } 168 169 public function getSerializedValue() { 170 return $this->getValue(); 171 } 172 173 public function readSerializedValue($value) { 174 $this->setValue($value); 175 return $this; 176 } 177 178 public function readValueFromRequest(AphrontRequest $request) { 179 $this->setValue($request->getStr($this->getName())); 180 return $this; 181 } 182 183 public function readValueFromDictionary(array $dictionary) { 184 $this->setValue(idx($dictionary, $this->getName())); 185 return $this; 186 } 187 188 public function setDisabled($disabled) { 189 $this->disabled = $disabled; 190 return $this; 191 } 192 193 public function getDisabled() { 194 return $this->disabled; 195 } 196 197 abstract protected function renderInput(); 198 abstract protected function getCustomControlClass(); 199 200 protected function shouldRender() { 201 return true; 202 } 203 204 public function addClass($class) { 205 $this->classes[] = $class; 206 return $this; 207 } 208 209 final public function render() { 210 if (!$this->shouldRender()) { 211 return null; 212 } 213 214 $custom_class = $this->getCustomControlClass(); 215 216 // If we don't have an ID yet, assign an automatic one so we can associate 217 // the label with the control. This allows assistive technologies to read 218 // form labels. 219 if (!$this->getID()) { 220 $this->setID(celerity_generate_unique_node_id()); 221 } 222 223 $class = 'aphront-form-input'; 224 if ($this->getHasCopyButton()) { 225 $class = $class.' has-copy-button'; 226 } 227 228 $input = phutil_tag( 229 'div', 230 array('class' => $class), 231 $this->renderInput()); 232 233 $error = null; 234 if ($this->getError()) { 235 $error = $this->getError(); 236 if ($error === true) { 237 $error = phutil_tag( 238 'span', 239 array('class' => 'aphront-form-error aphront-form-required'), 240 pht('Required')); 241 } else { 242 $error = phutil_tag( 243 'span', 244 array('class' => 'aphront-form-error'), 245 $error); 246 } 247 } 248 249 if (phutil_nonempty_string($this->getLabel())) { 250 $label = phutil_tag( 251 'label', 252 array( 253 'class' => 'aphront-form-label', 254 'for' => $this->getID(), 255 ), 256 array( 257 $this->getLabel(), 258 $error, 259 )); 260 } else { 261 $label = null; 262 $custom_class .= ' aphront-form-control-nolabel'; 263 } 264 265 // The Caption can be stuff like PhutilSafeHTML and other objects that 266 // can be casted to a string. After this cast we have never null. 267 $has_caption = phutil_string_cast($this->getCaption()) !== ''; 268 269 if ($has_caption) { 270 $caption = phutil_tag( 271 'div', 272 array('class' => 'aphront-form-caption'), 273 $this->getCaption()); 274 } else { 275 $caption = null; 276 } 277 278 $classes = array(); 279 $classes[] = 'aphront-form-control'; 280 $classes[] = 'grouped'; 281 $classes[] = $custom_class; 282 if ($this->classes) { 283 foreach ($this->classes as $class) { 284 $classes[] = $class; 285 } 286 } 287 288 $style = $this->controlStyle; 289 if ($this->hidden) { 290 $style = 'display: none; '.$style; 291 } 292 293 return phutil_tag( 294 'div', 295 array( 296 'class' => implode(' ', $classes), 297 'id' => $this->controlID, 298 'style' => $style, 299 ), 300 array( 301 $label, 302 $error, 303 $input, 304 $caption, 305 )); 306 } 307}