1<?php 2 3namespace Icinga\Module\Businessprocess\Renderer; 4 5use Icinga\Exception\ProgrammingError; 6use Icinga\Module\Businessprocess\BpNode; 7use Icinga\Module\Businessprocess\BpConfig; 8use Icinga\Module\Businessprocess\ImportedNode; 9use Icinga\Module\Businessprocess\Node; 10use Icinga\Module\Businessprocess\Web\Url; 11use ipl\Html\BaseHtmlElement; 12use ipl\Html\Html; 13use ipl\Html\HtmlDocument; 14 15abstract class Renderer extends HtmlDocument 16{ 17 /** @var BpConfig */ 18 protected $config; 19 20 /** @var BpNode */ 21 protected $parent; 22 23 /** @var bool Administrative actions are hidden unless unlocked */ 24 protected $locked = true; 25 26 /** @var Url */ 27 protected $url; 28 29 /** @var Url */ 30 protected $baseUrl; 31 32 /** @var array */ 33 protected $path = array(); 34 35 /** @var bool */ 36 protected $isBreadcrumb = false; 37 38 /** 39 * Renderer constructor. 40 * 41 * @param BpConfig $config 42 * @param BpNode|null $parent 43 */ 44 public function __construct(BpConfig $config, BpNode $parent = null) 45 { 46 $this->config = $config; 47 $this->parent = $parent; 48 } 49 50 /** 51 * @return BpConfig 52 */ 53 public function getBusinessProcess() 54 { 55 return $this->config; 56 } 57 58 /** 59 * Whether this will render all root nodes 60 * 61 * @return bool 62 */ 63 public function wantsRootNodes() 64 { 65 return $this->parent === null; 66 } 67 68 /** 69 * Whether this will only render parts of given config 70 * 71 * @return bool 72 */ 73 public function rendersSubNode() 74 { 75 return $this->parent !== null; 76 } 77 78 public function rendersImportedNode() 79 { 80 return $this->parent !== null && $this->parent->getBpConfig()->getName() !== $this->config->getName(); 81 } 82 83 public function setParentNode(BpNode $node) 84 { 85 $this->parent = $node; 86 return $this; 87 } 88 89 /** 90 * @return BpNode 91 */ 92 public function getParentNode() 93 { 94 return $this->parent; 95 } 96 97 /** 98 * @return BpNode[] 99 */ 100 public function getParentNodes() 101 { 102 if ($this->wantsRootNodes()) { 103 return array(); 104 } 105 106 return $this->parent->getParents(); 107 } 108 109 /** 110 * @return BpNode[] 111 */ 112 public function getChildNodes() 113 { 114 if ($this->wantsRootNodes()) { 115 return $this->config->getRootNodes(); 116 } else { 117 return $this->parent->getChildren(); 118 } 119 } 120 121 /** 122 * @return int 123 */ 124 public function countChildNodes() 125 { 126 if ($this->wantsRootNodes()) { 127 return $this->config->countChildren(); 128 } else { 129 return $this->parent->countChildren(); 130 } 131 } 132 133 /** 134 * @param $summary 135 * @return BaseHtmlElement 136 */ 137 public function renderStateBadges($summary) 138 { 139 $elements = []; 140 141 foreach ($summary as $state => $cnt) { 142 if ($cnt === 0 143 || $state === 'OK' 144 || $state === 'UP' 145 ) { 146 continue; 147 } 148 149 $elements[] = Html::tag( 150 'span', 151 [ 152 'class' => [ 153 'badge', 154 'badge-' . strtolower($state) 155 ], 156 // TODO: We should translate this in this module 157 'title' => mt('monitoring', $state) 158 ], 159 $cnt 160 ); 161 } 162 163 if (!empty($elements)) { 164 $container = Html::tag('div', ['class' => 'badges']); 165 foreach ($elements as $element) { 166 $container->add($element); 167 } 168 169 return $container; 170 } 171 return null; 172 } 173 174 public function getNodeClasses(Node $node) 175 { 176 if ($node->isMissing()) { 177 $classes = array('missing'); 178 } else { 179 $classes = array( 180 strtolower($node->getStateName()) 181 ); 182 if ($node->hasMissingChildren()) { 183 $classes[] = 'missing-children'; 184 } 185 } 186 187 if ($node->isHandled()) { 188 $classes[] = 'handled'; 189 } 190 191 if ($node instanceof BpNode) { 192 $classes[] = 'process-node'; 193 } else { 194 $classes[] = 'monitored-node'; 195 } 196 // TODO: problem? 197 return $classes; 198 } 199 200 /** 201 * Return the url to the given node's source configuration 202 * 203 * @param BpNode $node 204 * 205 * @return Url 206 */ 207 public function getSourceUrl(BpNode $node) 208 { 209 if ($node instanceof ImportedNode) { 210 $name = $node->getNodeName(); 211 $paths = $node->getBpConfig()->getBpNode($name)->getPaths(); 212 } else { 213 $name = $node->getName(); 214 $paths = $node->getPaths(); 215 } 216 217 $url = $this->getUrl()->setParams([ 218 'config' => $node->getBpConfig()->getName(), 219 'node' => $name 220 ]); 221 // This depends on the fact that the node's root path is the last element in $paths 222 $url->getParams()->addValues('path', array_slice(array_pop($paths), 0, -1)); 223 if (! $this->isLocked()) { 224 $url->getParams()->add('unlocked', true); 225 } 226 227 return $url; 228 } 229 230 /** 231 * @param Node $node 232 * @param $path 233 * @return string 234 */ 235 public function getId(Node $node, $path) 236 { 237 return md5((empty($path) ? '' : implode(';', $path)) . $node->getName()); 238 } 239 240 public function setPath(array $path) 241 { 242 $this->path = $path; 243 return $this; 244 } 245 246 /** 247 * @return array 248 */ 249 public function getPath() 250 { 251 return $this->path; 252 } 253 254 public function getCurrentPath() 255 { 256 $path = $this->getPath(); 257 if ($this->rendersSubNode()) { 258 $path[] = $this->rendersImportedNode() 259 ? $this->parent->getIdentifier() 260 : $this->parent->getName(); 261 } 262 263 return $path; 264 } 265 266 /** 267 * @param Url $url 268 * @return $this 269 */ 270 public function setUrl(Url $url) 271 { 272 $this->url = $url->without(array( 273 'action', 274 'deletenode', 275 'deleteparent', 276 'editnode', 277 'simulationnode', 278 'view' 279 )); 280 $this->setBaseUrl($this->url); 281 return $this; 282 } 283 284 /** 285 * @param Url $url 286 * @return $this 287 */ 288 protected function setBaseUrl(Url $url) 289 { 290 $this->baseUrl = $url->without(array('node', 'path')); 291 return $this; 292 } 293 294 public function getUrl() 295 { 296 return $this->url; 297 } 298 299 /** 300 * @return Url 301 * @throws ProgrammingError 302 */ 303 public function getBaseUrl() 304 { 305 if ($this->baseUrl === null) { 306 throw new ProgrammingError('Renderer has no baseUrl'); 307 } 308 309 return clone($this->baseUrl); 310 } 311 312 /** 313 * @return bool 314 */ 315 public function isLocked() 316 { 317 return $this->locked; 318 } 319 320 /** 321 * @return $this 322 */ 323 public function lock() 324 { 325 $this->locked = true; 326 return $this; 327 } 328 329 /** 330 * @return $this 331 */ 332 public function unlock() 333 { 334 $this->locked = false; 335 return $this; 336 } 337 338 /** 339 * TODO: Get rid of this 340 * 341 * @return $this 342 */ 343 public function setIsBreadcrumb() 344 { 345 $this->isBreadcrumb = true; 346 return $this; 347 } 348 349 public function isBreadcrumb() 350 { 351 return $this->isBreadcrumb; 352 } 353 354 protected function createUnboundParent(BpConfig $bp) 355 { 356 return $bp->getNode('__unbound__'); 357 } 358 359 /** 360 * Just to be on the safe side 361 */ 362 public function __destruct() 363 { 364 unset($this->parent); 365 unset($this->config); 366 } 367} 368