1*4fd6a1d7SAndreas Gohr<?php 2*4fd6a1d7SAndreas Gohr 3*4fd6a1d7SAndreas Gohrnamespace dokuwiki\plugin\extension; 4*4fd6a1d7SAndreas Gohr 5*4fd6a1d7SAndreas Gohrclass GuiExtension extends Gui 6*4fd6a1d7SAndreas Gohr{ 7*4fd6a1d7SAndreas Gohr const THUMB_WIDTH = 120; 8*4fd6a1d7SAndreas Gohr const THUMB_HEIGHT = 70; 9*4fd6a1d7SAndreas Gohr 10*4fd6a1d7SAndreas Gohr 11*4fd6a1d7SAndreas Gohr protected Extension $extension; 12*4fd6a1d7SAndreas Gohr 13*4fd6a1d7SAndreas Gohr public function __construct(Extension $extension) 14*4fd6a1d7SAndreas Gohr { 15*4fd6a1d7SAndreas Gohr parent::__construct(); 16*4fd6a1d7SAndreas Gohr $this->extension = $extension; 17*4fd6a1d7SAndreas Gohr } 18*4fd6a1d7SAndreas Gohr 19*4fd6a1d7SAndreas Gohr 20*4fd6a1d7SAndreas Gohr public function render() 21*4fd6a1d7SAndreas Gohr { 22*4fd6a1d7SAndreas Gohr 23*4fd6a1d7SAndreas Gohr $classes = $this->getClasses(); 24*4fd6a1d7SAndreas Gohr 25*4fd6a1d7SAndreas Gohr $html = "<section class=\"$classes\">"; 26*4fd6a1d7SAndreas Gohr $html .= $this->thumbnail(); 27*4fd6a1d7SAndreas Gohr $html .= $this->popularity(); 28*4fd6a1d7SAndreas Gohr $html .= $this->info(); 29*4fd6a1d7SAndreas Gohr $html .= $this->notices(); 30*4fd6a1d7SAndreas Gohr $html .= $this->mainLinks(); 31*4fd6a1d7SAndreas Gohr $html .= $this->details(); 32*4fd6a1d7SAndreas Gohr $html .= $this->actions(); 33*4fd6a1d7SAndreas Gohr 34*4fd6a1d7SAndreas Gohr 35*4fd6a1d7SAndreas Gohr $html .= '</section>'; 36*4fd6a1d7SAndreas Gohr 37*4fd6a1d7SAndreas Gohr return $html; 38*4fd6a1d7SAndreas Gohr } 39*4fd6a1d7SAndreas Gohr 40*4fd6a1d7SAndreas Gohr // region sections 41*4fd6a1d7SAndreas Gohr 42*4fd6a1d7SAndreas Gohr /** 43*4fd6a1d7SAndreas Gohr * Get the link and image tag for the screenshot/thumbnail 44*4fd6a1d7SAndreas Gohr * 45*4fd6a1d7SAndreas Gohr * @return string The HTML code 46*4fd6a1d7SAndreas Gohr */ 47*4fd6a1d7SAndreas Gohr protected function thumbnail() 48*4fd6a1d7SAndreas Gohr { 49*4fd6a1d7SAndreas Gohr $screen = $this->extension->getScreenshotURL(); 50*4fd6a1d7SAndreas Gohr $thumb = $this->extension->getThumbnailURL(); 51*4fd6a1d7SAndreas Gohr 52*4fd6a1d7SAndreas Gohr $link = []; 53*4fd6a1d7SAndreas Gohr $img = [ 54*4fd6a1d7SAndreas Gohr 'width' => self::THUMB_WIDTH, 55*4fd6a1d7SAndreas Gohr 'height' => self::THUMB_HEIGHT, 56*4fd6a1d7SAndreas Gohr 'alt' => '', 57*4fd6a1d7SAndreas Gohr ]; 58*4fd6a1d7SAndreas Gohr 59*4fd6a1d7SAndreas Gohr if ($screen) { 60*4fd6a1d7SAndreas Gohr $link = [ 61*4fd6a1d7SAndreas Gohr 'href' => $screen, 62*4fd6a1d7SAndreas Gohr 'target' => '_blank', 63*4fd6a1d7SAndreas Gohr 'class' => 'extension_screenshot', 64*4fd6a1d7SAndreas Gohr 'title' => sprintf($this->getLang('screenshot'), $this->extension->getDisplayName()) 65*4fd6a1d7SAndreas Gohr ]; 66*4fd6a1d7SAndreas Gohr 67*4fd6a1d7SAndreas Gohr $img['src'] = $thumb; 68*4fd6a1d7SAndreas Gohr $img['alt'] = $link['title']; 69*4fd6a1d7SAndreas Gohr } elseif ($this->extension->isTemplate()) { 70*4fd6a1d7SAndreas Gohr $img['src'] = DOKU_BASE . 'lib/plugins/extension/images/template.png'; 71*4fd6a1d7SAndreas Gohr } else { 72*4fd6a1d7SAndreas Gohr $img['src'] = DOKU_BASE . 'lib/plugins/extension/images/plugin.png'; 73*4fd6a1d7SAndreas Gohr } 74*4fd6a1d7SAndreas Gohr 75*4fd6a1d7SAndreas Gohr $html = '<div class="screenshot">'; 76*4fd6a1d7SAndreas Gohr if ($link) $html .= '<a ' . buildAttributes($link) . '>'; 77*4fd6a1d7SAndreas Gohr $html .= '<img ' . buildAttributes($img) . ' />'; 78*4fd6a1d7SAndreas Gohr if ($link) $html .= '</a>'; 79*4fd6a1d7SAndreas Gohr $html .= '</div>'; 80*4fd6a1d7SAndreas Gohr 81*4fd6a1d7SAndreas Gohr return $html; 82*4fd6a1d7SAndreas Gohr 83*4fd6a1d7SAndreas Gohr } 84*4fd6a1d7SAndreas Gohr 85*4fd6a1d7SAndreas Gohr /** 86*4fd6a1d7SAndreas Gohr * The main information about the extension 87*4fd6a1d7SAndreas Gohr * 88*4fd6a1d7SAndreas Gohr * @return string 89*4fd6a1d7SAndreas Gohr */ 90*4fd6a1d7SAndreas Gohr protected function info() 91*4fd6a1d7SAndreas Gohr { 92*4fd6a1d7SAndreas Gohr $html = '<h2>'; 93*4fd6a1d7SAndreas Gohr $html .= '<bdi>' . hsc($this->extension->getDisplayName()) . '</bdi>'; 94*4fd6a1d7SAndreas Gohr if($this->extension->isBundled()) { 95*4fd6a1d7SAndreas Gohr $html .= ' <span class="version">' . hsc('<'.$this->getLang('status_bundled').'>') . '</span>'; 96*4fd6a1d7SAndreas Gohr } elseif($this->extension->getInstalledVersion()) { 97*4fd6a1d7SAndreas Gohr $html .= ' <span class="version">' . hsc($this->extension->getInstalledVersion()) . '</span>'; 98*4fd6a1d7SAndreas Gohr } 99*4fd6a1d7SAndreas Gohr $html .= '</h2>'; 100*4fd6a1d7SAndreas Gohr 101*4fd6a1d7SAndreas Gohr $html .= $this->author(); 102*4fd6a1d7SAndreas Gohr 103*4fd6a1d7SAndreas Gohr $html .= '<p>' . hsc($this->extension->getDescription()) . '</p>'; 104*4fd6a1d7SAndreas Gohr 105*4fd6a1d7SAndreas Gohr return $html; 106*4fd6a1d7SAndreas Gohr } 107*4fd6a1d7SAndreas Gohr 108*4fd6a1d7SAndreas Gohr /** 109*4fd6a1d7SAndreas Gohr * Display the available notices for the extension 110*4fd6a1d7SAndreas Gohr * 111*4fd6a1d7SAndreas Gohr * @return string 112*4fd6a1d7SAndreas Gohr */ 113*4fd6a1d7SAndreas Gohr protected function notices() 114*4fd6a1d7SAndreas Gohr { 115*4fd6a1d7SAndreas Gohr $notices = Notice::list($this->extension); 116*4fd6a1d7SAndreas Gohr 117*4fd6a1d7SAndreas Gohr $html = ''; 118*4fd6a1d7SAndreas Gohr foreach ($notices as $type => $messages) { 119*4fd6a1d7SAndreas Gohr foreach ($messages as $message) { 120*4fd6a1d7SAndreas Gohr $message = hsc($message); 121*4fd6a1d7SAndreas Gohr $message = preg_replace('/`([^`]+)`/', '<bdi>$1</bdi>', $message); 122*4fd6a1d7SAndreas Gohr $html .= '<div class="msg ' . $type . '">' . $message . '</div>'; 123*4fd6a1d7SAndreas Gohr } 124*4fd6a1d7SAndreas Gohr } 125*4fd6a1d7SAndreas Gohr return $html; 126*4fd6a1d7SAndreas Gohr } 127*4fd6a1d7SAndreas Gohr 128*4fd6a1d7SAndreas Gohr /** 129*4fd6a1d7SAndreas Gohr * Generate the link bar HTML code 130*4fd6a1d7SAndreas Gohr * 131*4fd6a1d7SAndreas Gohr * @return string The HTML code 132*4fd6a1d7SAndreas Gohr */ 133*4fd6a1d7SAndreas Gohr public function mainLinks() 134*4fd6a1d7SAndreas Gohr { 135*4fd6a1d7SAndreas Gohr $html = '<div class="linkbar">'; 136*4fd6a1d7SAndreas Gohr 137*4fd6a1d7SAndreas Gohr 138*4fd6a1d7SAndreas Gohr $homepage = $this->extension->getURL(); 139*4fd6a1d7SAndreas Gohr if ($homepage) { 140*4fd6a1d7SAndreas Gohr $params = $this->prepareLinkAttributes($homepage, 'homepage'); 141*4fd6a1d7SAndreas Gohr $html .= ' <a ' . buildAttributes($params, true) . '>' . $this->getLang('homepage_link') . '</a>'; 142*4fd6a1d7SAndreas Gohr } 143*4fd6a1d7SAndreas Gohr 144*4fd6a1d7SAndreas Gohr $bugtracker = $this->extension->getBugtrackerURL(); 145*4fd6a1d7SAndreas Gohr if ($bugtracker) { 146*4fd6a1d7SAndreas Gohr $params = $this->prepareLinkAttributes($bugtracker, 'bugs'); 147*4fd6a1d7SAndreas Gohr $html .= ' <a ' . buildAttributes($params, true) . '>' . $this->getLang('bugs_features') . '</a>'; 148*4fd6a1d7SAndreas Gohr } 149*4fd6a1d7SAndreas Gohr 150*4fd6a1d7SAndreas Gohr if ($this->extension->getDonationURL()) { 151*4fd6a1d7SAndreas Gohr $params = $this->prepareLinkAttributes($this->extension->getDonationURL(), 'donate'); 152*4fd6a1d7SAndreas Gohr $html .= ' <a ' . buildAttributes($params, true) . '>' . $this->getLang('donate_action') . '</a>'; 153*4fd6a1d7SAndreas Gohr } 154*4fd6a1d7SAndreas Gohr 155*4fd6a1d7SAndreas Gohr 156*4fd6a1d7SAndreas Gohr 157*4fd6a1d7SAndreas Gohr $html .= '</div>'; 158*4fd6a1d7SAndreas Gohr 159*4fd6a1d7SAndreas Gohr return $html; 160*4fd6a1d7SAndreas Gohr } 161*4fd6a1d7SAndreas Gohr 162*4fd6a1d7SAndreas Gohr /** 163*4fd6a1d7SAndreas Gohr * Create the details section 164*4fd6a1d7SAndreas Gohr * 165*4fd6a1d7SAndreas Gohr * @return string 166*4fd6a1d7SAndreas Gohr */ 167*4fd6a1d7SAndreas Gohr protected function details() 168*4fd6a1d7SAndreas Gohr { 169*4fd6a1d7SAndreas Gohr $html = '<details>'; 170*4fd6a1d7SAndreas Gohr $html .= '<summary>' . 'FIXME label' . '</summary>'; 171*4fd6a1d7SAndreas Gohr 172*4fd6a1d7SAndreas Gohr 173*4fd6a1d7SAndreas Gohr $default = $this->getLang('unknown'); 174*4fd6a1d7SAndreas Gohr $list = []; 175*4fd6a1d7SAndreas Gohr 176*4fd6a1d7SAndreas Gohr if (!$this->extension->isBundled()) { 177*4fd6a1d7SAndreas Gohr $list['downloadurl'] = $this->shortlink($this->extension->getDownloadURL(), 'download', $default); 178*4fd6a1d7SAndreas Gohr $list['repository'] = $this->shortlink($this->extension->getSourcerepoURL(), 'repo', $default); 179*4fd6a1d7SAndreas Gohr } 180*4fd6a1d7SAndreas Gohr 181*4fd6a1d7SAndreas Gohr if ($this->extension->isInstalled()) { 182*4fd6a1d7SAndreas Gohr if ($this->extension->isBundled()) { 183*4fd6a1d7SAndreas Gohr $list['installed_version'] = $this->getLang('status_bundled'); 184*4fd6a1d7SAndreas Gohr } else { 185*4fd6a1d7SAndreas Gohr if ($this->extension->getInstalledVersion()) { 186*4fd6a1d7SAndreas Gohr $list['installed_version'] = hsc($this->extension->getInstalledVersion()); 187*4fd6a1d7SAndreas Gohr } 188*4fd6a1d7SAndreas Gohr if (!$this->extension->isBundled()) { 189*4fd6a1d7SAndreas Gohr $updateDate = $this->extension->getManager()->getLastUpdate(); 190*4fd6a1d7SAndreas Gohr $list['install_date'] = $updateDate ? hsc($updateDate) : $default; 191*4fd6a1d7SAndreas Gohr } 192*4fd6a1d7SAndreas Gohr } 193*4fd6a1d7SAndreas Gohr } 194*4fd6a1d7SAndreas Gohr 195*4fd6a1d7SAndreas Gohr if (!$this->extension->isInstalled() || $this->extension->isUpdateAvailable()) { 196*4fd6a1d7SAndreas Gohr $list['available_version'] = $this->extension->getLastUpdate() 197*4fd6a1d7SAndreas Gohr ? hsc($this->extension->getLastUpdate()) 198*4fd6a1d7SAndreas Gohr : $default; 199*4fd6a1d7SAndreas Gohr } 200*4fd6a1d7SAndreas Gohr 201*4fd6a1d7SAndreas Gohr 202*4fd6a1d7SAndreas Gohr if (!$this->extension->isBundled() && $this->extension->getCompatibleVersions()) { 203*4fd6a1d7SAndreas Gohr $list['compatible'] = join(', ', array_map( 204*4fd6a1d7SAndreas Gohr function ($date, $version) { 205*4fd6a1d7SAndreas Gohr return '<bdi>' . $version['label'] . ' (' . $date . ')</bdi>'; 206*4fd6a1d7SAndreas Gohr }, 207*4fd6a1d7SAndreas Gohr array_keys($this->extension->getCompatibleVersions()), 208*4fd6a1d7SAndreas Gohr array_values($this->extension->getCompatibleVersions()) 209*4fd6a1d7SAndreas Gohr )); 210*4fd6a1d7SAndreas Gohr } 211*4fd6a1d7SAndreas Gohr 212*4fd6a1d7SAndreas Gohr $tags = $this->extension->getTags(); 213*4fd6a1d7SAndreas Gohr if ($tags) { 214*4fd6a1d7SAndreas Gohr $list['tags'] = join(', ', array_map(function ($tag) { 215*4fd6a1d7SAndreas Gohr $url = $this->tabURL('search', ['q' => 'tag:' . $tag]); 216*4fd6a1d7SAndreas Gohr return '<bdi><a href="' . $url . '">' . hsc($tag) . '</a></bdi>'; 217*4fd6a1d7SAndreas Gohr }, $tags)); 218*4fd6a1d7SAndreas Gohr } 219*4fd6a1d7SAndreas Gohr 220*4fd6a1d7SAndreas Gohr if ($this->extension->getDependencyList()) { 221*4fd6a1d7SAndreas Gohr $list['depends'] = $this->linkExtensions($this->extension->getDependencyList()); 222*4fd6a1d7SAndreas Gohr } 223*4fd6a1d7SAndreas Gohr 224*4fd6a1d7SAndreas Gohr if ($this->extension->getSimilarList()) { 225*4fd6a1d7SAndreas Gohr $list['similar'] = $this->linkExtensions($this->extension->getSimilarList()); 226*4fd6a1d7SAndreas Gohr } 227*4fd6a1d7SAndreas Gohr 228*4fd6a1d7SAndreas Gohr if ($this->extension->getConflictList()) { 229*4fd6a1d7SAndreas Gohr $list['conflicts'] = $this->linkExtensions($this->extension->getConflictList()); 230*4fd6a1d7SAndreas Gohr } 231*4fd6a1d7SAndreas Gohr 232*4fd6a1d7SAndreas Gohr foreach ($list as $key => $value) { 233*4fd6a1d7SAndreas Gohr $html .= '<dt>' . $this->getLang($key) . '</dt>'; 234*4fd6a1d7SAndreas Gohr $html .= '<dd>' . $value . '</dd>'; 235*4fd6a1d7SAndreas Gohr } 236*4fd6a1d7SAndreas Gohr 237*4fd6a1d7SAndreas Gohr $html .= '</details>'; 238*4fd6a1d7SAndreas Gohr return $html; 239*4fd6a1d7SAndreas Gohr } 240*4fd6a1d7SAndreas Gohr 241*4fd6a1d7SAndreas Gohr /** 242*4fd6a1d7SAndreas Gohr * Generate a link to the author of the extension 243*4fd6a1d7SAndreas Gohr * 244*4fd6a1d7SAndreas Gohr * @return string The HTML code of the link 245*4fd6a1d7SAndreas Gohr */ 246*4fd6a1d7SAndreas Gohr protected function author() 247*4fd6a1d7SAndreas Gohr { 248*4fd6a1d7SAndreas Gohr if (!$this->extension->getAuthor()) { 249*4fd6a1d7SAndreas Gohr return '<em class="author">' . $this->getLang('unknown_author') . '</em>'; 250*4fd6a1d7SAndreas Gohr } 251*4fd6a1d7SAndreas Gohr 252*4fd6a1d7SAndreas Gohr $mailid = $this->extension->getEmailID(); 253*4fd6a1d7SAndreas Gohr if ($mailid) { 254*4fd6a1d7SAndreas Gohr $url = $this->tabURL('search', ['q' => 'authorid:' . $mailid]); 255*4fd6a1d7SAndreas Gohr $html = '<a href="' . $url . '" class="author" title="' . $this->getLang('author_hint') . '" >' . 256*4fd6a1d7SAndreas Gohr '<img src="//www.gravatar.com/avatar/' . $mailid . 257*4fd6a1d7SAndreas Gohr '?s=60&d=mm" width="20" height="20" alt="" /> ' . 258*4fd6a1d7SAndreas Gohr hsc($this->extension->getAuthor()) . '</a>'; 259*4fd6a1d7SAndreas Gohr } else { 260*4fd6a1d7SAndreas Gohr $html = '<span class="author">' . hsc($this->extension->getAuthor()) . '</span>'; 261*4fd6a1d7SAndreas Gohr } 262*4fd6a1d7SAndreas Gohr return '<bdi>' . $html . '</bdi>'; 263*4fd6a1d7SAndreas Gohr } 264*4fd6a1d7SAndreas Gohr 265*4fd6a1d7SAndreas Gohr /** 266*4fd6a1d7SAndreas Gohr * The popularity bar 267*4fd6a1d7SAndreas Gohr * 268*4fd6a1d7SAndreas Gohr * @return string 269*4fd6a1d7SAndreas Gohr */ 270*4fd6a1d7SAndreas Gohr protected function popularity() 271*4fd6a1d7SAndreas Gohr { 272*4fd6a1d7SAndreas Gohr $popularity = $this->extension->getPopularity(); 273*4fd6a1d7SAndreas Gohr if (!$popularity) return ''; 274*4fd6a1d7SAndreas Gohr if ($this->extension->isBundled()) return ''; 275*4fd6a1d7SAndreas Gohr 276*4fd6a1d7SAndreas Gohr $popularityText = sprintf($this->getLang('popularity'), round($popularity * 100, 2)); 277*4fd6a1d7SAndreas Gohr return '<div class="popularity" title="' . $popularityText . '">' . 278*4fd6a1d7SAndreas Gohr '<div style="width: ' . ($popularity * 100) . '%;">' . 279*4fd6a1d7SAndreas Gohr '<span class="a11y">' . $popularityText . '</span>' . 280*4fd6a1d7SAndreas Gohr '</div></div>'; 281*4fd6a1d7SAndreas Gohr 282*4fd6a1d7SAndreas Gohr } 283*4fd6a1d7SAndreas Gohr 284*4fd6a1d7SAndreas Gohr protected function actions() 285*4fd6a1d7SAndreas Gohr { 286*4fd6a1d7SAndreas Gohr global $conf; 287*4fd6a1d7SAndreas Gohr 288*4fd6a1d7SAndreas Gohr $html = ''; 289*4fd6a1d7SAndreas Gohr $actions = []; 290*4fd6a1d7SAndreas Gohr $errors = []; 291*4fd6a1d7SAndreas Gohr 292*4fd6a1d7SAndreas Gohr // show the available version if there is one 293*4fd6a1d7SAndreas Gohr if ($this->extension->getDownloadURL() && $this->extension->getLastUpdate()) { 294*4fd6a1d7SAndreas Gohr $html .= ' <span class="version">' . $this->getLang('available_version') . ' ' . 295*4fd6a1d7SAndreas Gohr hsc($this->extension->getLastUpdate()) . '</span>'; 296*4fd6a1d7SAndreas Gohr } 297*4fd6a1d7SAndreas Gohr 298*4fd6a1d7SAndreas Gohr // gather available actions and possible errors to show 299*4fd6a1d7SAndreas Gohr try { 300*4fd6a1d7SAndreas Gohr Installer::ensurePermissions($this->extension); 301*4fd6a1d7SAndreas Gohr 302*4fd6a1d7SAndreas Gohr if ($this->extension->isInstalled()) { 303*4fd6a1d7SAndreas Gohr 304*4fd6a1d7SAndreas Gohr if (!$this->extension->isProtected()) $actions[] = 'uninstall'; 305*4fd6a1d7SAndreas Gohr if ($this->extension->getDownloadURL()) { 306*4fd6a1d7SAndreas Gohr $actions[] = $this->extension->isUpdateAvailable() ? 'update' : 'reinstall'; 307*4fd6a1d7SAndreas Gohr 308*4fd6a1d7SAndreas Gohr if ($this->extension->isGitControlled()) { 309*4fd6a1d7SAndreas Gohr $errors[] = $this->getLang('git'); 310*4fd6a1d7SAndreas Gohr } 311*4fd6a1d7SAndreas Gohr } 312*4fd6a1d7SAndreas Gohr 313*4fd6a1d7SAndreas Gohr if (!$this->extension->isProtected() && !$this->extension->isTemplate()) { // no enable/disable for templates 314*4fd6a1d7SAndreas Gohr $actions[] = $this->extension->isEnabled() ? 'disable' : 'enable'; 315*4fd6a1d7SAndreas Gohr 316*4fd6a1d7SAndreas Gohr if ( 317*4fd6a1d7SAndreas Gohr $this->extension->isEnabled() && 318*4fd6a1d7SAndreas Gohr in_array('Auth', $this->extension->getComponentTypes()) && 319*4fd6a1d7SAndreas Gohr $conf['authtype'] != $this->extension->getID() 320*4fd6a1d7SAndreas Gohr ) { 321*4fd6a1d7SAndreas Gohr $errors[] = $this->getLang('auth'); 322*4fd6a1d7SAndreas Gohr } 323*4fd6a1d7SAndreas Gohr } 324*4fd6a1d7SAndreas Gohr } else { 325*4fd6a1d7SAndreas Gohr if ($this->extension->getDownloadURL()) { 326*4fd6a1d7SAndreas Gohr $actions[] = 'install'; 327*4fd6a1d7SAndreas Gohr } 328*4fd6a1d7SAndreas Gohr } 329*4fd6a1d7SAndreas Gohr } catch (\Exception $e) { 330*4fd6a1d7SAndreas Gohr $errors[] = $e->getMessage(); 331*4fd6a1d7SAndreas Gohr } 332*4fd6a1d7SAndreas Gohr 333*4fd6a1d7SAndreas Gohr foreach ($actions as $action) { 334*4fd6a1d7SAndreas Gohr $html .= '<button name="fn[' . $action . '][' . $this->extension->getID() . ']" class="button" type="submit">' . 335*4fd6a1d7SAndreas Gohr $this->getLang('btn_' . $action) . '</button>'; 336*4fd6a1d7SAndreas Gohr } 337*4fd6a1d7SAndreas Gohr 338*4fd6a1d7SAndreas Gohr foreach ($errors as $error) { 339*4fd6a1d7SAndreas Gohr $html .= '<div class="msg error">' . hsc($error) . '</div>'; 340*4fd6a1d7SAndreas Gohr } 341*4fd6a1d7SAndreas Gohr 342*4fd6a1d7SAndreas Gohr return $html; 343*4fd6a1d7SAndreas Gohr } 344*4fd6a1d7SAndreas Gohr 345*4fd6a1d7SAndreas Gohr 346*4fd6a1d7SAndreas Gohr // endregion 347*4fd6a1d7SAndreas Gohr // region utility functions 348*4fd6a1d7SAndreas Gohr 349*4fd6a1d7SAndreas Gohr /** 350*4fd6a1d7SAndreas Gohr * Create the classes representing the state of the extension 351*4fd6a1d7SAndreas Gohr * 352*4fd6a1d7SAndreas Gohr * @return string 353*4fd6a1d7SAndreas Gohr */ 354*4fd6a1d7SAndreas Gohr protected function getClasses() 355*4fd6a1d7SAndreas Gohr { 356*4fd6a1d7SAndreas Gohr $classes = ['extension', $this->extension->getType()]; 357*4fd6a1d7SAndreas Gohr if ($this->extension->isInstalled()) $classes[] = 'installed'; 358*4fd6a1d7SAndreas Gohr if ($this->extension->isUpdateAvailable()) $classes[] = 'update'; 359*4fd6a1d7SAndreas Gohr $classes[] = $this->extension->isEnabled() ? 'enabled' : 'disabled'; 360*4fd6a1d7SAndreas Gohr return implode(' ', $classes); 361*4fd6a1d7SAndreas Gohr } 362*4fd6a1d7SAndreas Gohr 363*4fd6a1d7SAndreas Gohr /** 364*4fd6a1d7SAndreas Gohr * Create an attributes array for a link 365*4fd6a1d7SAndreas Gohr * 366*4fd6a1d7SAndreas Gohr * Handles interwiki links to dokuwiki.org 367*4fd6a1d7SAndreas Gohr * 368*4fd6a1d7SAndreas Gohr * @param string $url The URL to link to 369*4fd6a1d7SAndreas Gohr * @param string $class Additional classes to add 370*4fd6a1d7SAndreas Gohr * @return array 371*4fd6a1d7SAndreas Gohr */ 372*4fd6a1d7SAndreas Gohr protected function prepareLinkAttributes($url, $class) 373*4fd6a1d7SAndreas Gohr { 374*4fd6a1d7SAndreas Gohr global $conf; 375*4fd6a1d7SAndreas Gohr 376*4fd6a1d7SAndreas Gohr $attributes = [ 377*4fd6a1d7SAndreas Gohr 'href' => $url, 378*4fd6a1d7SAndreas Gohr 'class' => 'urlextern', 379*4fd6a1d7SAndreas Gohr 'target' => $conf['target']['extern'], 380*4fd6a1d7SAndreas Gohr 'rel' => 'noopener', 381*4fd6a1d7SAndreas Gohr 'title' => $url, 382*4fd6a1d7SAndreas Gohr ]; 383*4fd6a1d7SAndreas Gohr 384*4fd6a1d7SAndreas Gohr if ($conf['relnofollow']) { 385*4fd6a1d7SAndreas Gohr $attributes['rel'] .= ' ugc nofollow'; 386*4fd6a1d7SAndreas Gohr } 387*4fd6a1d7SAndreas Gohr 388*4fd6a1d7SAndreas Gohr if (preg_match('/^https?:\/\/(www\.)?dokuwiki\.org\//i', $url)) { 389*4fd6a1d7SAndreas Gohr $attributes['class'] = 'interwiki iw_doku'; 390*4fd6a1d7SAndreas Gohr $attributes['target'] = $conf['target']['interwiki']; 391*4fd6a1d7SAndreas Gohr $attributes['rel'] = ''; 392*4fd6a1d7SAndreas Gohr } 393*4fd6a1d7SAndreas Gohr 394*4fd6a1d7SAndreas Gohr $attributes['class'] .= ' ' . $class; 395*4fd6a1d7SAndreas Gohr return $attributes; 396*4fd6a1d7SAndreas Gohr } 397*4fd6a1d7SAndreas Gohr 398*4fd6a1d7SAndreas Gohr /** 399*4fd6a1d7SAndreas Gohr * Create a link from the given URL 400*4fd6a1d7SAndreas Gohr * 401*4fd6a1d7SAndreas Gohr * Shortens the URL for display 402*4fd6a1d7SAndreas Gohr * 403*4fd6a1d7SAndreas Gohr * @param string $url 404*4fd6a1d7SAndreas Gohr * @param string $class Additional classes to add 405*4fd6a1d7SAndreas Gohr * @param string $fallback If URL is empty return this fallback 406*4fd6a1d7SAndreas Gohr * @return string HTML link 407*4fd6a1d7SAndreas Gohr */ 408*4fd6a1d7SAndreas Gohr protected function shortlink($url, $class, $fallback = '') 409*4fd6a1d7SAndreas Gohr { 410*4fd6a1d7SAndreas Gohr if (!$url) return hsc($fallback); 411*4fd6a1d7SAndreas Gohr 412*4fd6a1d7SAndreas Gohr $link = parse_url($url); 413*4fd6a1d7SAndreas Gohr $base = $link['host']; 414*4fd6a1d7SAndreas Gohr if (!empty($link['port'])) $base .= $base . ':' . $link['port']; 415*4fd6a1d7SAndreas Gohr $long = $link['path']; 416*4fd6a1d7SAndreas Gohr if (!empty($link['query'])) $long .= $link['query']; 417*4fd6a1d7SAndreas Gohr 418*4fd6a1d7SAndreas Gohr $name = shorten($base, $long, 55); 419*4fd6a1d7SAndreas Gohr 420*4fd6a1d7SAndreas Gohr $params = $this->prepareLinkAttributes($url, $class); 421*4fd6a1d7SAndreas Gohr $html = '<a ' . buildAttributes($params, true) . '>' . hsc($name) . '</a>'; 422*4fd6a1d7SAndreas Gohr return $html; 423*4fd6a1d7SAndreas Gohr } 424*4fd6a1d7SAndreas Gohr 425*4fd6a1d7SAndreas Gohr /** 426*4fd6a1d7SAndreas Gohr * Generate a list of links for extensions 427*4fd6a1d7SAndreas Gohr * 428*4fd6a1d7SAndreas Gohr * Links to the search tab with the extension name 429*4fd6a1d7SAndreas Gohr * 430*4fd6a1d7SAndreas Gohr * @param array $extensions The extension names 431*4fd6a1d7SAndreas Gohr * @return string The HTML code 432*4fd6a1d7SAndreas Gohr */ 433*4fd6a1d7SAndreas Gohr public function linkExtensions($extensions) 434*4fd6a1d7SAndreas Gohr { 435*4fd6a1d7SAndreas Gohr $html = ''; 436*4fd6a1d7SAndreas Gohr foreach ($extensions as $link) { 437*4fd6a1d7SAndreas Gohr $html .= '<bdi><a href="' . 438*4fd6a1d7SAndreas Gohr $this->tabURL('search', ['q' => 'ext:' . $link]) . '">' . 439*4fd6a1d7SAndreas Gohr hsc($link) . '</a></bdi>, '; 440*4fd6a1d7SAndreas Gohr } 441*4fd6a1d7SAndreas Gohr return rtrim($html, ', '); 442*4fd6a1d7SAndreas Gohr } 443*4fd6a1d7SAndreas Gohr 444*4fd6a1d7SAndreas Gohr // endregion 445*4fd6a1d7SAndreas Gohr 446*4fd6a1d7SAndreas Gohr 447*4fd6a1d7SAndreas Gohr /** 448*4fd6a1d7SAndreas Gohr * Extension main description 449*4fd6a1d7SAndreas Gohr * 450*4fd6a1d7SAndreas Gohr * @return string The HTML code 451*4fd6a1d7SAndreas Gohr */ 452*4fd6a1d7SAndreas Gohr public function makeLegend() 453*4fd6a1d7SAndreas Gohr { 454*4fd6a1d7SAndreas Gohr $html = '<div>'; 455*4fd6a1d7SAndreas Gohr $html .= '<h2>'; 456*4fd6a1d7SAndreas Gohr $html .= sprintf( 457*4fd6a1d7SAndreas Gohr $this->getLang('extensionby'), 458*4fd6a1d7SAndreas Gohr '<bdi>' . hsc($this->extension->getDisplayName()) . '</bdi>', 459*4fd6a1d7SAndreas Gohr $this->author() 460*4fd6a1d7SAndreas Gohr ); 461*4fd6a1d7SAndreas Gohr $html .= '</h2>' . DOKU_LF; 462*4fd6a1d7SAndreas Gohr 463*4fd6a1d7SAndreas Gohr $html .= $this->makeScreenshot(); 464*4fd6a1d7SAndreas Gohr 465*4fd6a1d7SAndreas Gohr $popularity = $this->extension->getPopularity(); 466*4fd6a1d7SAndreas Gohr if ($popularity !== false && !$this->extension->isBundled()) { 467*4fd6a1d7SAndreas Gohr $popularityText = sprintf($this->getLang('popularity'), round($popularity * 100, 2)); 468*4fd6a1d7SAndreas Gohr $html .= '<div class="popularity" title="' . $popularityText . '">' . 469*4fd6a1d7SAndreas Gohr '<div style="width: ' . ($popularity * 100) . '%;">' . 470*4fd6a1d7SAndreas Gohr '<span class="a11y">' . $popularityText . '</span>' . 471*4fd6a1d7SAndreas Gohr '</div></div>' . DOKU_LF; 472*4fd6a1d7SAndreas Gohr } 473*4fd6a1d7SAndreas Gohr 474*4fd6a1d7SAndreas Gohr if ($this->extension->getDescription()) { 475*4fd6a1d7SAndreas Gohr $html .= '<p><bdi>'; 476*4fd6a1d7SAndreas Gohr $html .= hsc($this->extension->getDescription()) . ' '; 477*4fd6a1d7SAndreas Gohr $html .= '</bdi></p>' . DOKU_LF; 478*4fd6a1d7SAndreas Gohr } 479*4fd6a1d7SAndreas Gohr 480*4fd6a1d7SAndreas Gohr $html .= $this->makeLinkbar(); 481*4fd6a1d7SAndreas Gohr $html .= $this->makeInfo(); 482*4fd6a1d7SAndreas Gohr $html .= $this->makeNoticeArea(); 483*4fd6a1d7SAndreas Gohr $html .= '</div>' . DOKU_LF; 484*4fd6a1d7SAndreas Gohr return $html; 485*4fd6a1d7SAndreas Gohr } 486*4fd6a1d7SAndreas Gohr 487*4fd6a1d7SAndreas Gohr 488*4fd6a1d7SAndreas Gohr /** 489*4fd6a1d7SAndreas Gohr * Plugin/template details 490*4fd6a1d7SAndreas Gohr * 491*4fd6a1d7SAndreas Gohr * @return string The HTML code 492*4fd6a1d7SAndreas Gohr */ 493*4fd6a1d7SAndreas Gohr public 494*4fd6a1d7SAndreas Gohr function makeInfo() 495*4fd6a1d7SAndreas Gohr { 496*4fd6a1d7SAndreas Gohr $default = $this->getLang('unknown'); 497*4fd6a1d7SAndreas Gohr 498*4fd6a1d7SAndreas Gohr 499*4fd6a1d7SAndreas Gohr $list = []; 500*4fd6a1d7SAndreas Gohr 501*4fd6a1d7SAndreas Gohr $list['status'] = $this->makeStatus(); 502*4fd6a1d7SAndreas Gohr 503*4fd6a1d7SAndreas Gohr 504*4fd6a1d7SAndreas Gohr if ($this->extension->getDonationURL()) { 505*4fd6a1d7SAndreas Gohr $list['donate'] = '<a href="' . $this->extension->getDonationURL() . '" class="donate">' . 506*4fd6a1d7SAndreas Gohr $this->getLang('donate_action') . '</a>'; 507*4fd6a1d7SAndreas Gohr } 508*4fd6a1d7SAndreas Gohr 509*4fd6a1d7SAndreas Gohr if (!$this->extension->isBundled()) { 510*4fd6a1d7SAndreas Gohr $list['downloadurl'] = $this->shortlink($this->extension->getDownloadURL(), $default); 511*4fd6a1d7SAndreas Gohr $list['repository'] = $this->shortlink($this->extension->getSourcerepoURL(), $default); 512*4fd6a1d7SAndreas Gohr } 513*4fd6a1d7SAndreas Gohr 514*4fd6a1d7SAndreas Gohr if ($this->extension->isInstalled()) { 515*4fd6a1d7SAndreas Gohr if ($this->extension->getInstalledVersion()) { 516*4fd6a1d7SAndreas Gohr $list['installed_version'] = hsc($this->extension->getInstalledVersion()); 517*4fd6a1d7SAndreas Gohr } 518*4fd6a1d7SAndreas Gohr if (!$this->extension->isBundled()) { 519*4fd6a1d7SAndreas Gohr $updateDate = $this->extension->getManager()->getLastUpdate(); 520*4fd6a1d7SAndreas Gohr $list['install_date'] = $updateDate ? hsc($updateDate) : $default; 521*4fd6a1d7SAndreas Gohr } 522*4fd6a1d7SAndreas Gohr } 523*4fd6a1d7SAndreas Gohr 524*4fd6a1d7SAndreas Gohr if (!$this->extension->isInstalled() || $this->extension->isUpdateAvailable()) { 525*4fd6a1d7SAndreas Gohr $list['available_version'] = $this->extension->getLastUpdate() 526*4fd6a1d7SAndreas Gohr ? hsc($this->extension->getLastUpdate()) 527*4fd6a1d7SAndreas Gohr : $default; 528*4fd6a1d7SAndreas Gohr } 529*4fd6a1d7SAndreas Gohr 530*4fd6a1d7SAndreas Gohr 531*4fd6a1d7SAndreas Gohr if (!$this->extension->isBundled() && $this->extension->getCompatibleVersions()) { 532*4fd6a1d7SAndreas Gohr $html .= '<dt>' . $this->getLang('compatible') . '</dt>'; 533*4fd6a1d7SAndreas Gohr $html .= '<dd>'; 534*4fd6a1d7SAndreas Gohr foreach ($this->extension->getCompatibleVersions() as $date => $version) { 535*4fd6a1d7SAndreas Gohr $html .= '<bdi>' . $version['label'] . ' (' . $date . ')</bdi>, '; 536*4fd6a1d7SAndreas Gohr } 537*4fd6a1d7SAndreas Gohr $html = rtrim($html, ', '); 538*4fd6a1d7SAndreas Gohr $html .= '</dd>'; 539*4fd6a1d7SAndreas Gohr } 540*4fd6a1d7SAndreas Gohr if ($this->extension->getDependencyList()) { 541*4fd6a1d7SAndreas Gohr $html .= '<dt>' . $this->getLang('depends') . '</dt>'; 542*4fd6a1d7SAndreas Gohr $html .= '<dd>'; 543*4fd6a1d7SAndreas Gohr $html .= $this->makeLinkList($extension->getDependencies()); 544*4fd6a1d7SAndreas Gohr $html .= '</dd>'; 545*4fd6a1d7SAndreas Gohr } 546*4fd6a1d7SAndreas Gohr 547*4fd6a1d7SAndreas Gohr if ($this->extension->getSimilarExtensions()) { 548*4fd6a1d7SAndreas Gohr $html .= '<dt>' . $this->getLang('similar') . '</dt>'; 549*4fd6a1d7SAndreas Gohr $html .= '<dd>'; 550*4fd6a1d7SAndreas Gohr $html .= $this->makeLinkList($extension->getSimilarExtensions()); 551*4fd6a1d7SAndreas Gohr $html .= '</dd>'; 552*4fd6a1d7SAndreas Gohr } 553*4fd6a1d7SAndreas Gohr 554*4fd6a1d7SAndreas Gohr if ($this->extension->getConflicts()) { 555*4fd6a1d7SAndreas Gohr $html .= '<dt>' . $this->getLang('conflicts') . '</dt>'; 556*4fd6a1d7SAndreas Gohr $html .= '<dd>'; 557*4fd6a1d7SAndreas Gohr $html .= $this->makeLinkList($extension->getConflicts()); 558*4fd6a1d7SAndreas Gohr $html .= '</dd>'; 559*4fd6a1d7SAndreas Gohr } 560*4fd6a1d7SAndreas Gohr $html .= '</dl>' . DOKU_LF; 561*4fd6a1d7SAndreas Gohr return $html; 562*4fd6a1d7SAndreas Gohr } 563*4fd6a1d7SAndreas Gohr 564*4fd6a1d7SAndreas Gohr 565*4fd6a1d7SAndreas Gohr} 566