[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/dashboard/engine/ -> PhabricatorDashboardPanelRenderingEngine.php (source)

   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  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1