@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 upstream/main 225 lines 5.3 kB view raw
1<?php 2 3/** 4 * @task children Managing Children 5 */ 6abstract class AphrontView extends Phobject 7 implements PhutilSafeHTMLProducerInterface { 8 9 private $viewer; 10 protected $children = array(); 11 12 13/* -( Configuration )------------------------------------------------------ */ 14 15 16 /** 17 * Set the user viewing this element. 18 * 19 * @param PhabricatorUser $viewer Viewing user. 20 * @return $this 21 */ 22 public function setViewer(PhabricatorUser $viewer) { 23 $this->viewer = $viewer; 24 return $this; 25 } 26 27 28 /** 29 * Get the user viewing this element. 30 * 31 * Throws an exception if no viewer has been set. 32 * 33 * @return PhabricatorUser Viewing user. 34 */ 35 public function getViewer() { 36 if (!$this->viewer) { 37 throw new PhutilInvalidStateException('setViewer'); 38 } 39 40 return $this->viewer; 41 } 42 43 44 /** 45 * Test if a viewer has been set on this element. 46 * 47 * @return bool True if a viewer is available. 48 */ 49 public function hasViewer() { 50 return (bool)$this->viewer; 51 } 52 53 54 /** 55 * Deprecated, use @{method:setViewer}. 56 * 57 * @task config 58 * @deprecated 59 */ 60 public function setUser(PhabricatorUser $user) { 61 return $this->setViewer($user); 62 } 63 64 65 /** 66 * Deprecated, use @{method:getViewer}. 67 * 68 * @task config 69 * @deprecated 70 */ 71 protected function getUser() { 72 if (!$this->hasViewer()) { 73 return null; 74 } 75 return $this->getViewer(); 76 } 77 78 79/* -( Managing Children )-------------------------------------------------- */ 80 81 82 /** 83 * Test if this View accepts children. 84 * 85 * By default, views accept children, but subclases may override this method 86 * to prevent children from being appended. Doing so will cause 87 * @{method:appendChild} to throw exceptions instead of appending children. 88 * 89 * @return bool True if the View should accept children. 90 * @task children 91 */ 92 protected function canAppendChild() { 93 return true; 94 } 95 96 97 /** 98 * Append a child to the list of children. 99 * 100 * This method will only work if the view supports children, which is 101 * determined by @{method:canAppendChild}. 102 * 103 * @param mixed $child Something renderable. 104 * @return $this 105 */ 106 final public function appendChild($child) { 107 if (!$this->canAppendChild()) { 108 $class = get_class($this); 109 throw new Exception( 110 pht("View '%s' does not support children.", $class)); 111 } 112 113 $this->children[] = $child; 114 115 return $this; 116 } 117 118 119 /** 120 * Produce children for rendering. 121 * 122 * Historically, this method reduced children to a string representation, 123 * but it no longer does. 124 * 125 * @return mixed Renderable children. 126 * @task 127 */ 128 final protected function renderChildren() { 129 return $this->children; 130 } 131 132 133 /** 134 * Test if an element has no children. 135 * 136 * @return bool True if this element has children. 137 * @task children 138 */ 139 final public function hasChildren() { 140 if ($this->children) { 141 $this->children = $this->reduceChildren($this->children); 142 } 143 return (bool)$this->children; 144 } 145 146 147 /** 148 * Reduce effectively-empty lists of children to be actually empty. This 149 * recursively removes `null`, `''`, and `array()` from the list of children 150 * so that @{method:hasChildren} can more effectively align with expectations. 151 * 152 * NOTE: Because View children are not rendered, a View which renders down 153 * to nothing will not be reduced by this method. 154 * 155 * @param list<mixed> $children Renderable children. 156 * @return list<mixed> Reduced list of children. 157 * @task children 158 */ 159 private function reduceChildren(array $children) { 160 foreach ($children as $key => $child) { 161 if ($child === null) { 162 unset($children[$key]); 163 } else if ($child === '') { 164 unset($children[$key]); 165 } else if (is_array($child)) { 166 $child = $this->reduceChildren($child); 167 if ($child) { 168 $children[$key] = $child; 169 } else { 170 unset($children[$key]); 171 } 172 } 173 } 174 return $children; 175 } 176 177 public function getDefaultResourceSource() { 178 return 'phabricator'; 179 } 180 181 public function requireResource($symbol) { 182 $response = CelerityAPI::getStaticResourceResponse(); 183 $response->requireResource($symbol, $this->getDefaultResourceSource()); 184 return $this; 185 } 186 187 public function initBehavior($name, $config = array()) { 188 Javelin::initBehavior( 189 $name, 190 $config, 191 $this->getDefaultResourceSource()); 192 return $this; 193 } 194 195 196/* -( Rendering )---------------------------------------------------------- */ 197 198 199 /** 200 * Inconsistent, unreliable pre-rendering hook. 201 * 202 * This hook //may// fire before views render. It is not fired reliably, and 203 * may fire multiple times. 204 * 205 * If it does fire, views might use it to register data for later loads, but 206 * almost no datasources support this now; this is currently only useful for 207 * tokenizers. This mechanism might eventually see wider support or might be 208 * removed. 209 */ 210 public function willRender() { 211 return; 212 } 213 214 215 abstract public function render(); 216 217 218/* -( PhutilSafeHTMLProducerInterface )------------------------------------ */ 219 220 221 public function producePhutilSafeHTML() { 222 return $this->render(); 223 } 224 225}