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