[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class PhabricatorDashboardPanelRenderingEngine extends Phobject { 4 5 const HEADER_MODE_NORMAL = 'normal'; 6 const HEADER_MODE_NONE = 'none'; 7 const HEADER_MODE_EDIT = 'edit'; 8 9 private $panel; 10 private $viewer; 11 private $enableAsyncRendering; 12 private $parentPanelPHIDs; 13 private $headerMode = self::HEADER_MODE_NORMAL; 14 private $dashboardID; 15 16 public function setDashboardID($id) { 17 $this->dashboardID = $id; 18 return $this; 19 } 20 21 public function getDashboardID() { 22 return $this->dashboardID; 23 } 24 25 public function setHeaderMode($header_mode) { 26 $this->headerMode = $header_mode; 27 return $this; 28 } 29 30 public function getHeaderMode() { 31 return $this->headerMode; 32 } 33 34 /** 35 * Allow the engine to render the panel via Ajax. 36 */ 37 public function setEnableAsyncRendering($enable) { 38 $this->enableAsyncRendering = $enable; 39 return $this; 40 } 41 42 public function setParentPanelPHIDs(array $parents) { 43 $this->parentPanelPHIDs = $parents; 44 return $this; 45 } 46 47 public function getParentPanelPHIDs() { 48 return $this->parentPanelPHIDs; 49 } 50 51 public function setViewer(PhabricatorUser $viewer) { 52 $this->viewer = $viewer; 53 return $this; 54 } 55 56 public function getViewer() { 57 return $this->viewer; 58 } 59 60 public function setPanel(PhabricatorDashboardPanel $panel) { 61 $this->panel = $panel; 62 return $this; 63 } 64 65 public function getPanel() { 66 return $this->panel; 67 } 68 69 public function renderPanel() { 70 $panel = $this->getPanel(); 71 $viewer = $this->getViewer(); 72 73 if (!$panel) { 74 return $this->renderErrorPanel( 75 pht('Missing Panel'), 76 pht('This panel does not exist.')); 77 } 78 79 $panel_type = $panel->getImplementation(); 80 if (!$panel_type) { 81 return $this->renderErrorPanel( 82 $panel->getName(), 83 pht( 84 'This panel has type "%s", but that panel type is not known to '. 85 'Phabricator.', 86 $panel->getPanelType())); 87 } 88 89 try { 90 $this->detectRenderingCycle($panel); 91 92 if ($this->enableAsyncRendering) { 93 if ($panel_type->shouldRenderAsync()) { 94 return $this->renderAsyncPanel(); 95 } 96 } 97 98 return $this->renderNormalPanel($viewer, $panel, $this); 99 } catch (Exception $ex) { 100 return $this->renderErrorPanel( 101 $panel->getName(), 102 pht( 103 '%s: %s', 104 phutil_tag('strong', array(), get_class($ex)), 105 $ex->getMessage())); 106 } 107 } 108 109 private function renderNormalPanel() { 110 $panel = $this->getPanel(); 111 $panel_type = $panel->getImplementation(); 112 113 $content = $panel_type->renderPanelContent( 114 $this->getViewer(), 115 $panel, 116 $this); 117 $header = $this->renderPanelHeader(); 118 119 return $this->renderPanelDiv( 120 $content, 121 $header); 122 } 123 124 125 private function renderAsyncPanel() { 126 $panel = $this->getPanel(); 127 128 $panel_id = celerity_generate_unique_node_id(); 129 $dashboard_id = $this->getDashboardID(); 130 131 Javelin::initBehavior( 132 'dashboard-async-panel', 133 array( 134 'panelID' => $panel_id, 135 'parentPanelPHIDs' => $this->getParentPanelPHIDs(), 136 'headerMode' => $this->getHeaderMode(), 137 'dashboardID' => $dashboard_id, 138 'uri' => '/dashboard/panel/render/'.$panel->getID().'/', 139 )); 140 141 $header = $this->renderPanelHeader(); 142 $content = id(new PHUIPropertyListView()) 143 ->addTextContent(pht('Loading...')); 144 145 return $this->renderPanelDiv( 146 $content, 147 $header, 148 $panel_id); 149 } 150 151 private function renderErrorPanel($title, $body) { 152 switch ($this->getHeaderMode()) { 153 case self::HEADER_MODE_NONE: 154 $header = null; 155 break; 156 case self::HEADER_MODE_EDIT: 157 $header = id(new PHUIActionHeaderView()) 158 ->setHeaderTitle($title) 159 ->setHeaderColor(PHUIActionHeaderView::HEADER_LIGHTBLUE); 160 $header = $this->addPanelHeaderActions($header); 161 break; 162 case self::HEADER_MODE_NORMAL: 163 default: 164 $header = id(new PHUIActionHeaderView()) 165 ->setHeaderTitle($title) 166 ->setHeaderColor(PHUIActionHeaderView::HEADER_LIGHTBLUE); 167 break; 168 } 169 $icon = id(new PHUIIconView()) 170 ->setIconFont('fa-warning red msr'); 171 $content = id(new PHUIBoxView()) 172 ->addClass('dashboard-box') 173 ->appendChild($icon) 174 ->appendChild($body); 175 return $this->renderPanelDiv( 176 $content, 177 $header); 178 } 179 180 private function renderPanelDiv( 181 $content, 182 $header = null, 183 $id = null) { 184 require_celerity_resource('phabricator-dashboard-css'); 185 186 $panel = $this->getPanel(); 187 if (!$id) { 188 $id = celerity_generate_unique_node_id(); 189 } 190 return javelin_tag( 191 'div', 192 array( 193 'id' => $id, 194 'sigil' => 'dashboard-panel', 195 'meta' => array( 196 'objectPHID' => $panel->getPHID(), 197 ), 198 'class' => 'dashboard-panel', 199 ), 200 array( 201 $header, 202 $content, 203 )); 204 } 205 206 207 private function renderPanelHeader() { 208 209 $panel = $this->getPanel(); 210 switch ($this->getHeaderMode()) { 211 case self::HEADER_MODE_NONE: 212 $header = null; 213 break; 214 case self::HEADER_MODE_EDIT: 215 $header = id(new PHUIActionHeaderView()) 216 ->setHeaderTitle($panel->getName()) 217 ->setHeaderColor(PHUIActionHeaderView::HEADER_LIGHTBLUE); 218 $header = $this->addPanelHeaderActions($header); 219 break; 220 case self::HEADER_MODE_NORMAL: 221 default: 222 $header = id(new PHUIActionHeaderView()) 223 ->setHeaderTitle($panel->getName()) 224 ->setHeaderColor(PHUIActionHeaderView::HEADER_LIGHTBLUE); 225 break; 226 } 227 return $header; 228 } 229 230 private function addPanelHeaderActions( 231 PHUIActionHeaderView $header) { 232 $panel = $this->getPanel(); 233 234 $dashboard_id = $this->getDashboardID(); 235 $edit_uri = id(new PhutilURI( 236 '/dashboard/panel/edit/'.$panel->getID().'/')); 237 if ($dashboard_id) { 238 $edit_uri->setQueryParam('dashboardID', $dashboard_id); 239 } 240 $action_edit = id(new PHUIIconView()) 241 ->setIconFont('fa-pencil') 242 ->setWorkflow(true) 243 ->setHref((string) $edit_uri); 244 $header->addAction($action_edit); 245 246 if ($dashboard_id) { 247 $uri = id(new PhutilURI( 248 '/dashboard/removepanel/'.$dashboard_id.'/')) 249 ->setQueryParam('panelPHID', $panel->getPHID()); 250 $action_remove = id(new PHUIIconView()) 251 ->setIconFont('fa-trash-o') 252 ->setHref((string) $uri) 253 ->setWorkflow(true); 254 $header->addAction($action_remove); 255 } 256 return $header; 257 } 258 259 /** 260 * Detect graph cycles in panels, and deeply nested panels. 261 * 262 * This method throws if the current rendering stack is too deep or contains 263 * a cycle. This can happen if you embed layout panels inside each other, 264 * build a big stack of panels, or embed a panel in remarkup inside another 265 * panel. Generally, all of this stuff is ridiculous and we just want to 266 * shut it down. 267 * 268 * @param PhabricatorDashboardPanel Panel being rendered. 269 * @return void 270 */ 271 private function detectRenderingCycle(PhabricatorDashboardPanel $panel) { 272 if ($this->parentPanelPHIDs === null) { 273 throw new Exception( 274 pht( 275 'You must call setParentPanelPHIDs() before rendering panels.')); 276 } 277 278 $max_depth = 4; 279 if (count($this->parentPanelPHIDs) >= $max_depth) { 280 throw new Exception( 281 pht( 282 'To render more than %s levels of panels nested inside other '. 283 'panels, purchase a subscription to Phabricator Gold.', 284 new PhutilNumber($max_depth))); 285 } 286 287 if (in_array($panel->getPHID(), $this->parentPanelPHIDs)) { 288 throw new Exception( 289 pht( 290 'You awake in a twisting maze of mirrors, all alike. '. 291 'You are likely to be eaten by a graph cycle. '. 292 'Should you escape alive, you resolve to be more careful about '. 293 'putting dashboard panels inside themselves.')); 294 } 295 } 296 297 298 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |