[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/lib/plugins/extension/helper/ -> list.php (source)

   1  <?php
   2  /**
   3   * DokuWiki Plugin extension (Helper Component)
   4   *
   5   * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
   6   * @author  Michael Hamann <michael@content-space.de>
   7   */
   8  
   9  /**
  10   * Class helper_plugin_extension_list takes care of creating a HTML list of extensions
  11   */
  12  class helper_plugin_extension_list extends DokuWiki_Plugin
  13  {
  14      protected $form = '';
  15      /** @var  helper_plugin_extension_gui */
  16      protected $gui;
  17  
  18      /**
  19       * Constructor
  20       *
  21       * loads additional helpers
  22       */
  23      public function __construct()
  24      {
  25          $this->gui = plugin_load('helper', 'extension_gui');
  26      }
  27  
  28      /**
  29       * Initialize the extension table form
  30       */
  31      public function startForm()
  32      {
  33          $this->form .= '<ul class="extensionList">';
  34      }
  35  
  36      /**
  37       * Build single row of extension table
  38       *
  39       * @param helper_plugin_extension_extension  $extension The extension that shall be added
  40       * @param bool                               $showinfo  Show the info area
  41       */
  42      public function addRow(helper_plugin_extension_extension $extension, $showinfo = false)
  43      {
  44          $this->startRow($extension);
  45          $this->populateColumn('legend', $this->makeLegend($extension, $showinfo));
  46          $this->populateColumn('actions', $this->makeActions($extension));
  47          $this->endRow();
  48      }
  49  
  50      /**
  51       * Adds a header to the form
  52       *
  53       * @param string $id     The id of the header
  54       * @param string $header The content of the header
  55       * @param int    $level  The level of the header
  56       */
  57      public function addHeader($id, $header, $level = 2)
  58      {
  59          $this->form .='<h'.$level.' id="'.$id.'">'.hsc($header).'</h'.$level.'>'.DOKU_LF;
  60      }
  61  
  62      /**
  63       * Adds a paragraph to the form
  64       *
  65       * @param string $data The content
  66       */
  67      public function addParagraph($data)
  68      {
  69          $this->form .= '<p>'.hsc($data).'</p>'.DOKU_LF;
  70      }
  71  
  72      /**
  73       * Add hidden fields to the form with the given data
  74       *
  75       * @param array $data key-value list of fields and their values to add
  76       */
  77      public function addHidden(array $data)
  78      {
  79          $this->form .= '<div class="no">';
  80          foreach ($data as $key => $value) {
  81              $this->form .= '<input type="hidden" name="'.hsc($key).'" value="'.hsc($value).'" />';
  82          }
  83          $this->form .= '</div>'.DOKU_LF;
  84      }
  85  
  86      /**
  87       * Add closing tags
  88       */
  89      public function endForm()
  90      {
  91          $this->form .= '</ul>';
  92      }
  93  
  94      /**
  95       * Show message when no results are found
  96       */
  97      public function nothingFound()
  98      {
  99          global $lang;
 100          $this->form .= '<li class="notfound">'.$lang['nothingfound'].'</li>';
 101      }
 102  
 103      /**
 104       * Print the form
 105       *
 106       * @param bool $returnonly whether to return html or print
 107       */
 108      public function render($returnonly = false)
 109      {
 110          if ($returnonly) return $this->form;
 111          echo $this->form;
 112      }
 113  
 114      /**
 115       * Start the HTML for the row for the extension
 116       *
 117       * @param helper_plugin_extension_extension $extension The extension
 118       */
 119      private function startRow(helper_plugin_extension_extension $extension)
 120      {
 121          $this->form .= '<li id="extensionplugin__'.hsc($extension->getID()).
 122              '" class="'.$this->makeClass($extension).'">';
 123      }
 124  
 125      /**
 126       * Add a column with the given class and content
 127       * @param string $class The class name
 128       * @param string $html  The content
 129       */
 130      private function populateColumn($class, $html)
 131      {
 132          $this->form .= '<div class="'.$class.' col">'.$html.'</div>'.DOKU_LF;
 133      }
 134  
 135      /**
 136       * End the row
 137       */
 138      private function endRow()
 139      {
 140          $this->form .= '</li>'.DOKU_LF;
 141      }
 142  
 143      /**
 144       * Generate the link to the plugin homepage
 145       *
 146       * @param helper_plugin_extension_extension $extension The extension
 147       * @return string The HTML code
 148       */
 149      public function makeHomepageLink(helper_plugin_extension_extension $extension)
 150      {
 151          global $conf;
 152          $url = $extension->getURL();
 153          if (strtolower(parse_url($url, PHP_URL_HOST)) == 'www.dokuwiki.org') {
 154              $linktype = 'interwiki';
 155          } else {
 156              $linktype = 'extern';
 157          }
 158          $param = array(
 159              'href'   => $url,
 160              'title'  => $url,
 161              'class'  => ($linktype == 'extern') ? 'urlextern' : 'interwiki iw_doku',
 162              'target' => $conf['target'][$linktype],
 163              'rel'    => ($linktype == 'extern') ? 'noopener' : '',
 164          );
 165          if ($linktype == 'extern' && $conf['relnofollow']) {
 166              $param['rel'] = implode(' ', [$param['rel'], 'ugc nofollow']);
 167          }
 168          $html = ' <a '. buildAttributes($param, true).'>'.
 169              $this->getLang('homepage_link').'</a>';
 170          return $html;
 171      }
 172  
 173      /**
 174       * Generate the class name for the row of the extension
 175       *
 176       * @param helper_plugin_extension_extension $extension The extension object
 177       * @return string The class name
 178       */
 179      public function makeClass(helper_plugin_extension_extension $extension)
 180      {
 181          $class = ($extension->isTemplate()) ? 'template' : 'plugin';
 182          if ($extension->isInstalled()) {
 183              $class.=' installed';
 184              $class.= ($extension->isEnabled()) ? ' enabled':' disabled';
 185              if ($extension->updateAvailable()) $class .= ' updatable';
 186          }
 187          if (!$extension->canModify()) $class.= ' notselect';
 188          if ($extension->isProtected()) $class.=  ' protected';
 189          //if($this->showinfo) $class.= ' showinfo';
 190          return $class;
 191      }
 192  
 193      /**
 194       * Generate a link to the author of the extension
 195       *
 196       * @param helper_plugin_extension_extension $extension The extension object
 197       * @return string The HTML code of the link
 198       */
 199      public function makeAuthor(helper_plugin_extension_extension $extension)
 200      {
 201          if ($extension->getAuthor()) {
 202              $mailid = $extension->getEmailID();
 203              if ($mailid) {
 204                  $url = $this->gui->tabURL('search', array('q' => 'authorid:'.$mailid));
 205                  $html = '<a href="'.$url.'" class="author" title="'.$this->getLang('author_hint').'" >'.
 206                      '<img src="//www.gravatar.com/avatar/'.$mailid.
 207                      '?s=20&amp;d=mm" width="20" height="20" alt="" /> '.
 208                      hsc($extension->getAuthor()).'</a>';
 209              } else {
 210                  $html = '<span class="author">'.hsc($extension->getAuthor()).'</span>';
 211              }
 212              $html = '<bdi>'.$html.'</bdi>';
 213          } else {
 214              $html = '<em class="author">'.$this->getLang('unknown_author').'</em>'.DOKU_LF;
 215          }
 216          return $html;
 217      }
 218  
 219      /**
 220       * Get the link and image tag for the screenshot/thumbnail
 221       *
 222       * @param helper_plugin_extension_extension $extension The extension object
 223       * @return string The HTML code
 224       */
 225      public function makeScreenshot(helper_plugin_extension_extension $extension)
 226      {
 227          $screen = $extension->getScreenshotURL();
 228          $thumb = $extension->getThumbnailURL();
 229  
 230          if ($screen) {
 231              // use protocol independent URLs for images coming from us #595
 232              $screen = str_replace('http://www.dokuwiki.org', '//www.dokuwiki.org', $screen);
 233              $thumb = str_replace('http://www.dokuwiki.org', '//www.dokuwiki.org', $thumb);
 234  
 235              $title = sprintf($this->getLang('screenshot'), hsc($extension->getDisplayName()));
 236              $img = '<a href="'.hsc($screen).'" target="_blank" class="extension_screenshot">'.
 237                  '<img alt="'.$title.'" width="120" height="70" src="'.hsc($thumb).'" />'.
 238                  '</a>';
 239          } elseif ($extension->isTemplate()) {
 240              $img = '<img alt="" width="120" height="70" src="'.DOKU_BASE.
 241                  'lib/plugins/extension/images/template.png" />';
 242          } else {
 243              $img = '<img alt="" width="120" height="70" src="'.DOKU_BASE.
 244                  'lib/plugins/extension/images/plugin.png" />';
 245          }
 246          $html = '<div class="screenshot" >'.$img.'<span></span></div>'.DOKU_LF;
 247          return $html;
 248      }
 249  
 250      /**
 251       * Extension main description
 252       *
 253       * @param helper_plugin_extension_extension $extension The extension object
 254       * @param bool                              $showinfo  Show the info section
 255       * @return string The HTML code
 256       */
 257      public function makeLegend(helper_plugin_extension_extension $extension, $showinfo = false)
 258      {
 259          $html  = '<div>';
 260          $html .= '<h2>';
 261          $html .= sprintf(
 262              $this->getLang('extensionby'),
 263              '<bdi>'.hsc($extension->getDisplayName()).'</bdi>',
 264              $this->makeAuthor($extension)
 265          );
 266          $html .= '</h2>'.DOKU_LF;
 267  
 268          $html .= $this->makeScreenshot($extension);
 269  
 270          $popularity = $extension->getPopularity();
 271          if ($popularity !== false && !$extension->isBundled()) {
 272              $popularityText = sprintf($this->getLang('popularity'), round($popularity*100, 2));
 273              $html .= '<div class="popularity" title="'.$popularityText.'">'.
 274                  '<div style="width: '.($popularity * 100).'%;">'.
 275                  '<span class="a11y">'.$popularityText.'</span>'.
 276                  '</div></div>'.DOKU_LF;
 277          }
 278  
 279          if ($extension->getDescription()) {
 280              $html .= '<p><bdi>';
 281              $html .=  hsc($extension->getDescription()).' ';
 282              $html .= '</bdi></p>'.DOKU_LF;
 283          }
 284  
 285          $html .= $this->makeLinkbar($extension);
 286  
 287          if ($showinfo) {
 288              $url = $this->gui->tabURL('');
 289              $class = 'close';
 290          } else {
 291              $url = $this->gui->tabURL('', array('info' => $extension->getID()));
 292              $class = '';
 293          }
 294          $html .= ' <a href="'.$url.'#extensionplugin__'.$extension->getID().
 295              '" class="info '.$class.'" title="'.$this->getLang('btn_info').
 296              '" data-extid="'.$extension->getID().'">'.$this->getLang('btn_info').'</a>';
 297  
 298          if ($showinfo) {
 299              $html .= $this->makeInfo($extension);
 300          }
 301          $html .= $this->makeNoticeArea($extension);
 302          $html .= '</div>'.DOKU_LF;
 303          return $html;
 304      }
 305  
 306      /**
 307       * Generate the link bar HTML code
 308       *
 309       * @param helper_plugin_extension_extension $extension The extension instance
 310       * @return string The HTML code
 311       */
 312      public function makeLinkbar(helper_plugin_extension_extension $extension)
 313      {
 314          global $conf;
 315          $html  = '<div class="linkbar">';
 316          $html .= $this->makeHomepageLink($extension);
 317  
 318          $bugtrackerURL = $extension->getBugtrackerURL();
 319          if ($bugtrackerURL) {
 320              if (strtolower(parse_url($bugtrackerURL, PHP_URL_HOST)) == 'www.dokuwiki.org') {
 321                  $linktype = 'interwiki';
 322              } else {
 323                  $linktype = 'extern';
 324              }
 325              $param = array(
 326                  'href'   => $bugtrackerURL,
 327                  'title'  => $bugtrackerURL,
 328                  'class'  => 'bugs',
 329                  'target' => $conf['target'][$linktype],
 330                  'rel'    => ($linktype == 'extern') ? 'noopener' : '',
 331              );
 332              if ($conf['relnofollow']) {
 333                  $param['rel'] = implode(' ', [$param['rel'], 'ugc nofollow']);
 334              }
 335              $html .= ' <a '.buildAttributes($param, true).'>'.
 336                    $this->getLang('bugs_features').'</a>';
 337          }
 338          if ($extension->getTags()) {
 339              $first = true;
 340              $html .= ' <span class="tags">'.$this->getLang('tags').' ';
 341              foreach ($extension->getTags() as $tag) {
 342                  if (!$first) {
 343                      $html .= ', ';
 344                  } else {
 345                      $first = false;
 346                  }
 347                  $url = $this->gui->tabURL('search', ['q' => 'tag:'.$tag]);
 348                  $html .= '<bdi><a href="'.$url.'">'.hsc($tag).'</a></bdi>';
 349              }
 350              $html .= '</span>';
 351          }
 352          $html .= '</div>'.DOKU_LF;
 353          return $html;
 354      }
 355  
 356      /**
 357       * Notice area
 358       *
 359       * @param helper_plugin_extension_extension $extension The extension
 360       * @return string The HTML code
 361       */
 362      public function makeNoticeArea(helper_plugin_extension_extension $extension)
 363      {
 364          $html = '';
 365          $missing_dependencies = $extension->getMissingDependencies();
 366          if (!empty($missing_dependencies)) {
 367              $html .= '<div class="msg error">' .
 368                  sprintf(
 369                      $this->getLang('missing_dependency'),
 370                      '<bdi>' . implode(', ', $missing_dependencies) . '</bdi>'
 371                  ) .
 372                  '</div>';
 373          }
 374          if ($extension->isInWrongFolder()) {
 375              $html .= '<div class="msg error">' .
 376                  sprintf(
 377                      $this->getLang('wrong_folder'),
 378                      '<bdi>' . hsc($extension->getInstallName()) . '</bdi>',
 379                      '<bdi>' . hsc($extension->getBase()) . '</bdi>'
 380                  ) .
 381                  '</div>';
 382          }
 383          if (($securityissue = $extension->getSecurityIssue()) !== false) {
 384              $html .= '<div class="msg error">'.
 385                  sprintf($this->getLang('security_issue'), '<bdi>'.hsc($securityissue).'</bdi>').
 386                  '</div>';
 387          }
 388          if (($securitywarning = $extension->getSecurityWarning()) !== false) {
 389              $html .= '<div class="msg notify">'.
 390                  sprintf($this->getLang('security_warning'), '<bdi>'.hsc($securitywarning).'</bdi>').
 391                  '</div>';
 392          }
 393          if ($extension->updateAvailable()) {
 394              $html .=  '<div class="msg notify">'.
 395                  sprintf($this->getLang('update_available'), hsc($extension->getLastUpdate())).
 396                  '</div>';
 397          }
 398          if ($extension->hasDownloadURLChanged()) {
 399              $html .= '<div class="msg notify">' .
 400                  sprintf(
 401                      $this->getLang('url_change'),
 402                      '<bdi>' . hsc($extension->getDownloadURL()) . '</bdi>',
 403                      '<bdi>' . hsc($extension->getLastDownloadURL()) . '</bdi>'
 404                  ) .
 405                  '</div>';
 406          }
 407          return $html.DOKU_LF;
 408      }
 409  
 410      /**
 411       * Create a link from the given URL
 412       *
 413       * Shortens the URL for display
 414       *
 415       * @param string $url
 416       * @return string  HTML link
 417       */
 418      public function shortlink($url)
 419      {
 420          $link = parse_url($url);
 421  
 422          $base = $link['host'];
 423          if (!empty($link['port'])) $base .= $base.':'.$link['port'];
 424          $long = $link['path'];
 425          if (!empty($link['query'])) $long .= $link['query'];
 426  
 427          $name = shorten($base, $long, 55);
 428  
 429          $html = '<a href="'.hsc($url).'" class="urlextern">'.hsc($name).'</a>';
 430          return $html;
 431      }
 432  
 433      /**
 434       * Plugin/template details
 435       *
 436       * @param helper_plugin_extension_extension $extension The extension
 437       * @return string The HTML code
 438       */
 439      public function makeInfo(helper_plugin_extension_extension $extension)
 440      {
 441          $default = $this->getLang('unknown');
 442          $html = '<dl class="details">';
 443  
 444          $html .= '<dt>'.$this->getLang('status').'</dt>';
 445          $html .= '<dd>'.$this->makeStatus($extension).'</dd>';
 446  
 447          if ($extension->getDonationURL()) {
 448              $html .= '<dt>'.$this->getLang('donate').'</dt>';
 449              $html .= '<dd>';
 450              $html .= '<a href="'.$extension->getDonationURL().'" class="donate">'.
 451                  $this->getLang('donate_action').'</a>';
 452              $html .= '</dd>';
 453          }
 454  
 455          if (!$extension->isBundled()) {
 456              $html .= '<dt>'.$this->getLang('downloadurl').'</dt>';
 457              $html .= '<dd><bdi>';
 458              $html .= ($extension->getDownloadURL()
 459                  ? $this->shortlink($extension->getDownloadURL())
 460                  : $default);
 461              $html .= '</bdi></dd>';
 462  
 463              $html .= '<dt>'.$this->getLang('repository').'</dt>';
 464              $html .= '<dd><bdi>';
 465              $html .= ($extension->getSourcerepoURL()
 466                  ? $this->shortlink($extension->getSourcerepoURL())
 467                  : $default);
 468              $html .= '</bdi></dd>';
 469          }
 470  
 471          if ($extension->isInstalled()) {
 472              if ($extension->getInstalledVersion()) {
 473                  $html .= '<dt>'.$this->getLang('installed_version').'</dt>';
 474                  $html .= '<dd>';
 475                  $html .= hsc($extension->getInstalledVersion());
 476                  $html .= '</dd>';
 477              }
 478              if (!$extension->isBundled()) {
 479                  $html .= '<dt>'.$this->getLang('install_date').'</dt>';
 480                  $html .= '<dd>';
 481                  $html .= ($extension->getUpdateDate()
 482                      ? hsc($extension->getUpdateDate())
 483                      : $this->getLang('unknown'));
 484                  $html .= '</dd>';
 485              }
 486          }
 487          if (!$extension->isInstalled() || $extension->updateAvailable()) {
 488              $html .= '<dt>'.$this->getLang('available_version').'</dt>';
 489              $html .= '<dd>';
 490              $html .= ($extension->getLastUpdate()
 491                  ? hsc($extension->getLastUpdate())
 492                  : $this->getLang('unknown'));
 493              $html .= '</dd>';
 494          }
 495  
 496          $html .= '<dt>'.$this->getLang('provides').'</dt>';
 497          $html .= '<dd><bdi>';
 498          $html .= ($extension->getTypes()
 499              ? hsc(implode(', ', $extension->getTypes()))
 500              : $default);
 501          $html .= '</bdi></dd>';
 502  
 503          if (!$extension->isBundled() && $extension->getCompatibleVersions()) {
 504              $html .= '<dt>'.$this->getLang('compatible').'</dt>';
 505              $html .= '<dd>';
 506              foreach ($extension->getCompatibleVersions() as $date => $version) {
 507                  $html .= '<bdi>'.$version['label'].' ('.$date.')</bdi>, ';
 508              }
 509              $html = rtrim($html, ', ');
 510              $html .= '</dd>';
 511          }
 512          if ($extension->getDependencies()) {
 513              $html .= '<dt>'.$this->getLang('depends').'</dt>';
 514              $html .= '<dd>';
 515              $html .= $this->makeLinkList($extension->getDependencies());
 516              $html .= '</dd>';
 517          }
 518  
 519          if ($extension->getSimilarExtensions()) {
 520              $html .= '<dt>'.$this->getLang('similar').'</dt>';
 521              $html .= '<dd>';
 522              $html .= $this->makeLinkList($extension->getSimilarExtensions());
 523              $html .= '</dd>';
 524          }
 525  
 526          if ($extension->getConflicts()) {
 527              $html .= '<dt>'.$this->getLang('conflicts').'</dt>';
 528              $html .= '<dd>';
 529              $html .= $this->makeLinkList($extension->getConflicts());
 530              $html .= '</dd>';
 531          }
 532          $html .= '</dl>'.DOKU_LF;
 533          return $html;
 534      }
 535  
 536      /**
 537       * Generate a list of links for extensions
 538       *
 539       * @param array $ext The extensions
 540       * @return string The HTML code
 541       */
 542      public function makeLinkList($ext)
 543      {
 544          $html = '';
 545          foreach ($ext as $link) {
 546              $html .= '<bdi><a href="'.
 547                  $this->gui->tabURL('search', array('q'=>'ext:'.$link)).'">'.
 548                  hsc($link).'</a></bdi>, ';
 549          }
 550          return rtrim($html, ', ');
 551      }
 552  
 553      /**
 554       * Display the action buttons if they are possible
 555       *
 556       * @param helper_plugin_extension_extension $extension The extension
 557       * @return string The HTML code
 558       */
 559      public function makeActions(helper_plugin_extension_extension $extension)
 560      {
 561          global $conf;
 562          $html   = '';
 563          $errors = '';
 564  
 565          if ($extension->isInstalled()) {
 566              if (($canmod = $extension->canModify()) === true) {
 567                  if (!$extension->isProtected()) {
 568                      $html .= $this->makeAction('uninstall', $extension);
 569                  }
 570                  if ($extension->getDownloadURL()) {
 571                      if ($extension->updateAvailable()) {
 572                          $html .= $this->makeAction('update', $extension);
 573                      } else {
 574                          $html .= $this->makeAction('reinstall', $extension);
 575                      }
 576                  }
 577              } else {
 578                  $errors .= '<p class="permerror">'.$this->getLang($canmod).'</p>';
 579              }
 580  
 581              if (!$extension->isProtected() && !$extension->isTemplate()) { // no enable/disable for templates
 582                  if ($extension->isEnabled()) {
 583                      $html .= $this->makeAction('disable', $extension);
 584                  } else {
 585                      $html .= $this->makeAction('enable', $extension);
 586                  }
 587              }
 588  
 589              if ($extension->isGitControlled()) {
 590                  $errors .= '<p class="permerror">'.$this->getLang('git').'</p>';
 591              }
 592  
 593              if ($extension->isEnabled() &&
 594                  in_array('Auth', $extension->getTypes()) &&
 595                  $conf['authtype'] != $extension->getID()
 596              ) {
 597                  $errors .= '<p class="permerror">'.$this->getLang('auth').'</p>';
 598              }
 599          } else {
 600              if (($canmod = $extension->canModify()) === true) {
 601                  if ($extension->getDownloadURL()) {
 602                      $html .= $this->makeAction('install', $extension);
 603                  }
 604              } else {
 605                  $errors .= '<div class="permerror">'.$this->getLang($canmod).'</div>';
 606              }
 607          }
 608  
 609          if (!$extension->isInstalled() && $extension->getDownloadURL()) {
 610              $html .= ' <span class="version">'.$this->getLang('available_version').' ';
 611              $html .= ($extension->getLastUpdate()
 612                      ? hsc($extension->getLastUpdate())
 613                      : $this->getLang('unknown')).'</span>';
 614          }
 615  
 616          return $html.' '.$errors.DOKU_LF;
 617      }
 618  
 619      /**
 620       * Display an action button for an extension
 621       *
 622       * @param string                            $action    The action
 623       * @param helper_plugin_extension_extension $extension The extension
 624       * @return string The HTML code
 625       */
 626      public function makeAction($action, $extension)
 627      {
 628          $title = '';
 629  
 630          switch ($action) {
 631              case 'install':
 632              case 'reinstall':
 633                  $title = 'title="'.hsc($extension->getDownloadURL()).'"';
 634                  break;
 635          }
 636  
 637          $classes = 'button '.$action;
 638          $name    = 'fn['.$action.']['.hsc($extension->getID()).']';
 639  
 640          $html = '<button class="'.$classes.'" name="'.$name.'" type="submit" '.$title.'>'.
 641              $this->getLang('btn_'.$action).'</button> ';
 642          return $html;
 643      }
 644  
 645      /**
 646       * Plugin/template status
 647       *
 648       * @param helper_plugin_extension_extension $extension The extension
 649       * @return string The description of all relevant statusses
 650       */
 651      public function makeStatus(helper_plugin_extension_extension $extension)
 652      {
 653          $status = array();
 654  
 655          if ($extension->isInstalled()) {
 656              $status[] = $this->getLang('status_installed');
 657              if ($extension->isProtected()) {
 658                  $status[] = $this->getLang('status_protected');
 659              } else {
 660                  $status[] = $extension->isEnabled()
 661                      ? $this->getLang('status_enabled')
 662                      : $this->getLang('status_disabled');
 663              }
 664          } else {
 665              $status[] = $this->getLang('status_not_installed');
 666          }
 667          if (!$extension->canModify()) $status[] = $this->getLang('status_unmodifiable');
 668          if ($extension->isBundled()) $status[] = $this->getLang('status_bundled');
 669          $status[] = $extension->isTemplate()
 670              ? $this->getLang('status_template')
 671              : $this->getLang('status_plugin');
 672          return implode(', ', $status);
 673      }
 674  }