<?php

use dokuwiki\Extension\Plugin;

/**
 * DokuWiki Plugin extension (Helper Component)
 *
 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
 * @author  Michael Hamann <michael@content-space.de>
 */
/**
 * Class helper_plugin_extension_list takes care of creating a HTML list of extensions
 */
class helper_plugin_extension_list extends Plugin
{
    protected $form = '';
    /** @var  helper_plugin_extension_gui */
    protected $gui;

    /**
     * Constructor
     *
     * loads additional helpers
     */
    public function __construct()
    {
        $this->gui = plugin_load('helper', 'extension_gui');
    }

    /**
     * Initialize the extension table form
     */
    public function startForm()
    {
        $this->form .= '<ul class="extensionList">';
    }

    /**
     * Build single row of extension table
     *
     * @param helper_plugin_extension_extension  $extension The extension that shall be added
     * @param bool                               $showinfo  Show the info area
     */
    public function addRow(helper_plugin_extension_extension $extension, $showinfo = false)
    {
        $this->startRow($extension);
        $this->populateColumn('legend', $this->makeLegend($extension, $showinfo));
        $this->populateColumn('actions', $this->makeActions($extension));
        $this->endRow();
    }

    /**
     * Adds a header to the form
     *
     * @param string $id     The id of the header
     * @param string $header The content of the header
     * @param int    $level  The level of the header
     */
    public function addHeader($id, $header, $level = 2)
    {
        $this->form .= '<h' . $level . ' id="' . $id . '">' . hsc($header) . '</h' . $level . '>' . DOKU_LF;
    }

    /**
     * Adds a paragraph to the form
     *
     * @param string $data The content
     */
    public function addParagraph($data)
    {
        $this->form .= '<p>' . hsc($data) . '</p>' . DOKU_LF;
    }

    /**
     * Add hidden fields to the form with the given data
     *
     * @param array $data key-value list of fields and their values to add
     */
    public function addHidden(array $data)
    {
        $this->form .= '<div class="no">';
        foreach ($data as $key => $value) {
            $this->form .= '<input type="hidden" name="' . hsc($key) . '" value="' . hsc($value) . '" />';
        }
        $this->form .= '</div>' . DOKU_LF;
    }

    /**
     * Add closing tags
     */
    public function endForm()
    {
        $this->form .= '</ul>';
    }

    /**
     * Show message when no results are found
     */
    public function nothingFound()
    {
        global $lang;
        $this->form .= '<li class="notfound">' . $lang['nothingfound'] . '</li>';
    }

    /**
     * Print the form
     *
     * @param bool $returnonly whether to return html or print
     */
    public function render($returnonly = false)
    {
        if ($returnonly) return $this->form;
        echo $this->form;
    }

    /**
     * Start the HTML for the row for the extension
     *
     * @param helper_plugin_extension_extension $extension The extension
     */
    private function startRow(helper_plugin_extension_extension $extension)
    {
        $this->form .= '<li id="extensionplugin__' . hsc($extension->getID()) .
            '" class="' . $this->makeClass($extension) . '">';
    }

    /**
     * Add a column with the given class and content
     * @param string $class The class name
     * @param string $html  The content
     */
    private function populateColumn($class, $html)
    {
        $this->form .= '<div class="' . $class . ' col">' . $html . '</div>' . DOKU_LF;
    }

    /**
     * End the row
     */
    private function endRow()
    {
        $this->form .= '</li>' . DOKU_LF;
    }

    /**
     * Generate the link to the plugin homepage
     *
     * @param helper_plugin_extension_extension $extension The extension
     * @return string The HTML code
     */
    public function makeHomepageLink(helper_plugin_extension_extension $extension)
    {
        global $conf;
        $url = $extension->getURL();
        if (strtolower(parse_url($url, PHP_URL_HOST)) == 'www.dokuwiki.org') {
            $linktype = 'interwiki';
        } else {
            $linktype = 'extern';
        }
        $param = [
            'href'   => $url,
            'title'  => $url,
            'class'  => ($linktype == 'extern') ? 'urlextern' : 'interwiki iw_doku',
            'target' => $conf['target'][$linktype],
            'rel'    => ($linktype == 'extern') ? 'noopener' : ''
        ];
        if ($linktype == 'extern' && $conf['relnofollow']) {
            $param['rel'] = implode(' ', [$param['rel'], 'ugc nofollow']);
        }
        $html = ' <a ' . buildAttributes($param, true) . '>' .
            $this->getLang('homepage_link') . '</a>';
        return $html;
    }

    /**
     * Generate the class name for the row of the extension
     *
     * @param helper_plugin_extension_extension $extension The extension object
     * @return string The class name
     */
    public function makeClass(helper_plugin_extension_extension $extension)
    {
        $class = ($extension->isTemplate()) ? 'template' : 'plugin';
        if ($extension->isInstalled()) {
            $class .= ' installed';
            $class .= ($extension->isEnabled()) ? ' enabled' : ' disabled';
            if ($extension->updateAvailable()) $class .= ' updatable';
        }
        if (!$extension->canModify()) $class .= ' notselect';
        if ($extension->isProtected()) $class .=  ' protected';
        //if($this->showinfo) $class.= ' showinfo';
        return $class;
    }

    /**
     * Generate a link to the author of the extension
     *
     * @param helper_plugin_extension_extension $extension The extension object
     * @return string The HTML code of the link
     */
    public function makeAuthor(helper_plugin_extension_extension $extension)
    {
        if ($extension->getAuthor()) {
            $mailid = $extension->getEmailID();
            if ($mailid) {
                $url = $this->gui->tabURL('search', ['q' => 'authorid:' . $mailid]);
                $html = '<a href="' . $url . '" class="author" title="' . $this->getLang('author_hint') . '" >' .
                    '<img src="//www.gravatar.com/avatar/' . $mailid .
                    '?s=20&amp;d=mm" width="20" height="20" alt="" /> ' .
                    hsc($extension->getAuthor()) . '</a>';
            } else {
                $html = '<span class="author">' . hsc($extension->getAuthor()) . '</span>';
            }
            $html = '<bdi>' . $html . '</bdi>';
        } else {
            $html = '<em class="author">' . $this->getLang('unknown_author') . '</em>' . DOKU_LF;
        }
        return $html;
    }

    /**
     * Get the link and image tag for the screenshot/thumbnail
     *
     * @param helper_plugin_extension_extension $extension The extension object
     * @return string The HTML code
     */
    public function makeScreenshot(helper_plugin_extension_extension $extension)
    {
        $screen = $extension->getScreenshotURL();
        $thumb = $extension->getThumbnailURL();

        if ($screen) {
            // use protocol independent URLs for images coming from us #595
            $screen = str_replace('http://www.dokuwiki.org', '//www.dokuwiki.org', $screen);
            $thumb = str_replace('http://www.dokuwiki.org', '//www.dokuwiki.org', $thumb);

            $title = sprintf($this->getLang('screenshot'), hsc($extension->getDisplayName()));
            $img = '<a href="' . hsc($screen) . '" target="_blank" class="extension_screenshot">' .
                '<img alt="' . $title . '" width="120" height="70" src="' . hsc($thumb) . '" />' .
                '</a>';
        } elseif ($extension->isTemplate()) {
            $img = '<img alt="" width="120" height="70" src="' . DOKU_BASE .
                'lib/plugins/extension/images/template.png" />';
        } else {
            $img = '<img alt="" width="120" height="70" src="' . DOKU_BASE .
                'lib/plugins/extension/images/plugin.png" />';
        }
        $html = '<div class="screenshot" >' . $img . '<span></span></div>' . DOKU_LF;
        return $html;
    }

    /**
     * Extension main description
     *
     * @param helper_plugin_extension_extension $extension The extension object
     * @param bool                              $showinfo  Show the info section
     * @return string The HTML code
     */
    public function makeLegend(helper_plugin_extension_extension $extension, $showinfo = false)
    {
        $html  = '<div>';
        $html .= '<h2>';
        $html .= sprintf(
            $this->getLang('extensionby'),
            '<bdi>' . hsc($extension->getDisplayName()) . '</bdi>',
            $this->makeAuthor($extension)
        );
        $html .= '</h2>' . DOKU_LF;

        $html .= $this->makeScreenshot($extension);

        $popularity = $extension->getPopularity();
        if ($popularity !== false && !$extension->isBundled()) {
            $popularityText = sprintf($this->getLang('popularity'), round($popularity * 100, 2));
            $html .= '<div class="popularity" title="' . $popularityText . '">' .
                '<div style="width: ' . ($popularity * 100) . '%;">' .
                '<span class="a11y">' . $popularityText . '</span>' .
                '</div></div>' . DOKU_LF;
        }

        if ($extension->getDescription()) {
            $html .= '<p><bdi>';
            $html .=  hsc($extension->getDescription()) . ' ';
            $html .= '</bdi></p>' . DOKU_LF;
        }

        $html .= $this->makeLinkbar($extension);

        if ($showinfo) {
            $url = $this->gui->tabURL('');
            $class = 'close';
        } else {
            $url = $this->gui->tabURL('', ['info' => $extension->getID()]);
            $class = '';
        }
        $html .= ' <a href="' . $url . '#extensionplugin__' . $extension->getID() .
            '" class="info ' . $class . '" title="' . $this->getLang('btn_info') .
            '" data-extid="' . $extension->getID() . '">' . $this->getLang('btn_info') . '</a>';

        if ($showinfo) {
            $html .= $this->makeInfo($extension);
        }
        $html .= $this->makeNoticeArea($extension);
        $html .= '</div>' . DOKU_LF;
        return $html;
    }

    /**
     * Generate the link bar HTML code
     *
     * @param helper_plugin_extension_extension $extension The extension instance
     * @return string The HTML code
     */
    public function makeLinkbar(helper_plugin_extension_extension $extension)
    {
        global $conf;
        $html  = '<div class="linkbar">';
        $html .= $this->makeHomepageLink($extension);

        $bugtrackerURL = $extension->getBugtrackerURL();
        if ($bugtrackerURL) {
            if (strtolower(parse_url($bugtrackerURL, PHP_URL_HOST)) == 'www.dokuwiki.org') {
                $linktype = 'interwiki';
            } else {
                $linktype = 'extern';
            }
            $param = [
                'href'   => $bugtrackerURL,
                'title'  => $bugtrackerURL,
                'class'  => 'bugs',
                'target' => $conf['target'][$linktype],
                'rel'    => ($linktype == 'extern') ? 'noopener' : ''
            ];
            if ($conf['relnofollow']) {
                $param['rel'] = implode(' ', [$param['rel'], 'ugc nofollow']);
            }
            $html .= ' <a ' . buildAttributes($param, true) . '>' .
                  $this->getLang('bugs_features') . '</a>';
        }
        if ($extension->getTags()) {
            $first = true;
            $html .= ' <span class="tags">' . $this->getLang('tags') . ' ';
            foreach ($extension->getTags() as $tag) {
                if (!$first) {
                    $html .= ', ';
                } else {
                    $first = false;
                }
                $url = $this->gui->tabURL('search', ['q' => 'tag:' . $tag]);
                $html .= '<bdi><a href="' . $url . '">' . hsc($tag) . '</a></bdi>';
            }
            $html .= '</span>';
        }
        $html .= '</div>' . DOKU_LF;
        return $html;
    }

    /**
     * Notice area
     *
     * @param helper_plugin_extension_extension $extension The extension
     * @return string The HTML code
     */
    public function makeNoticeArea(helper_plugin_extension_extension $extension)
    {
        $html = '';
        $missing_dependencies = $extension->getMissingDependencies();
        if (!empty($missing_dependencies)) {
            $html .= '<div class="msg error">' .
                sprintf(
                    $this->getLang('missing_dependency'),
                    '<bdi>' . implode(', ', $missing_dependencies) . '</bdi>'
                ) .
                '</div>';
        }
        if ($extension->isInWrongFolder()) {
            $html .= '<div class="msg error">' .
                sprintf(
                    $this->getLang('wrong_folder'),
                    '<bdi>' . hsc($extension->getInstallName()) . '</bdi>',
                    '<bdi>' . hsc($extension->getBase()) . '</bdi>'
                ) .
                '</div>';
        }
        if (($securityissue = $extension->getSecurityIssue()) !== false) {
            $html .= '<div class="msg error">' .
                sprintf($this->getLang('security_issue'), '<bdi>' . hsc($securityissue) . '</bdi>') .
                '</div>';
        }
        if (($securitywarning = $extension->getSecurityWarning()) !== false) {
            $html .= '<div class="msg notify">' .
                sprintf($this->getLang('security_warning'), '<bdi>' . hsc($securitywarning) . '</bdi>') .
                '</div>';
        }
        if (($updateMessage = $extension->getUpdateMessage()) !== false) {
            $html .=  '<div class="msg notify">' .
                sprintf($this->getLang('update_message'), '<bdi>' . hsc($updateMessage) . '</bdi>') .
                '</div>';
        }
        if ($extension->updateAvailable()) {
            $html .=  '<div class="msg notify">' .
                sprintf($this->getLang('update_available'), hsc($extension->getLastUpdate())) .
                '</div>';
        }
        if ($extension->hasDownloadURLChanged()) {
            $html .= '<div class="msg notify">' .
                sprintf(
                    $this->getLang('url_change'),
                    '<bdi>' . hsc($extension->getDownloadURL()) . '</bdi>',
                    '<bdi>' . hsc($extension->getLastDownloadURL()) . '</bdi>'
                ) .
                '</div>';
        }
        return $html . DOKU_LF;
    }

    /**
     * Create a link from the given URL
     *
     * Shortens the URL for display
     *
     * @param string $url
     * @return string  HTML link
     */
    public function shortlink($url)
    {
        $link = parse_url($url);

        $base = $link['host'];
        if (!empty($link['port'])) $base .= $base . ':' . $link['port'];
        $long = $link['path'];
        if (!empty($link['query'])) $long .= $link['query'];

        $name = shorten($base, $long, 55);

        $html = '<a href="' . hsc($url) . '" class="urlextern">' . hsc($name) . '</a>';
        return $html;
    }

    /**
     * Plugin/template details
     *
     * @param helper_plugin_extension_extension $extension The extension
     * @return string The HTML code
     */
    public function makeInfo(helper_plugin_extension_extension $extension)
    {
        $default = $this->getLang('unknown');
        $html = '<dl class="details">';

        $html .= '<dt>' . $this->getLang('status') . '</dt>';
        $html .= '<dd>' . $this->makeStatus($extension) . '</dd>';

        if ($extension->getDonationURL()) {
            $html .= '<dt>' . $this->getLang('donate') . '</dt>';
            $html .= '<dd>';
            $html .= '<a href="' . $extension->getDonationURL() . '" class="donate">' .
                $this->getLang('donate_action') . '</a>';
            $html .= '</dd>';
        }

        if (!$extension->isBundled()) {
            $html .= '<dt>' . $this->getLang('downloadurl') . '</dt>';
            $html .= '<dd><bdi>';
            $html .= ($extension->getDownloadURL()
                ? $this->shortlink($extension->getDownloadURL())
                : $default);
            $html .= '</bdi></dd>';

            $html .= '<dt>' . $this->getLang('repository') . '</dt>';
            $html .= '<dd><bdi>';
            $html .= ($extension->getSourcerepoURL()
                ? $this->shortlink($extension->getSourcerepoURL())
                : $default);
            $html .= '</bdi></dd>';
        }

        if ($extension->isInstalled()) {
            if ($extension->getInstalledVersion()) {
                $html .= '<dt>' . $this->getLang('installed_version') . '</dt>';
                $html .= '<dd>';
                $html .= hsc($extension->getInstalledVersion());
                $html .= '</dd>';
            }
            if (!$extension->isBundled()) {
                $html .= '<dt>' . $this->getLang('install_date') . '</dt>';
                $html .= '<dd>';
                $html .= ($extension->getUpdateDate()
                    ? hsc($extension->getUpdateDate())
                    : $this->getLang('unknown'));
                $html .= '</dd>';
            }
        }
        if (!$extension->isInstalled() || $extension->updateAvailable()) {
            $html .= '<dt>' . $this->getLang('available_version') . '</dt>';
            $html .= '<dd>';
            $html .= ($extension->getLastUpdate()
                ? hsc($extension->getLastUpdate())
                : $this->getLang('unknown'));
            $html .= '</dd>';
        }

        $html .= '<dt>' . $this->getLang('provides') . '</dt>';
        $html .= '<dd><bdi>';
        $html .= ($extension->getTypes()
            ? hsc(implode(', ', $extension->getTypes()))
            : $default);
        $html .= '</bdi></dd>';

        if (!$extension->isBundled() && $extension->getCompatibleVersions()) {
            $html .= '<dt>' . $this->getLang('compatible') . '</dt>';
            $html .= '<dd>';
            foreach ($extension->getCompatibleVersions() as $date => $version) {
                $html .= '<bdi>' . $version['label'] . ' (' . $date . ')</bdi>, ';
            }
            $html = rtrim($html, ', ');
            $html .= '</dd>';
        }
        if ($extension->getDependencies()) {
            $html .= '<dt>' . $this->getLang('depends') . '</dt>';
            $html .= '<dd>';
            $html .= $this->makeLinkList($extension->getDependencies());
            $html .= '</dd>';
        }

        if ($extension->getSimilarExtensions()) {
            $html .= '<dt>' . $this->getLang('similar') . '</dt>';
            $html .= '<dd>';
            $html .= $this->makeLinkList($extension->getSimilarExtensions());
            $html .= '</dd>';
        }

        if ($extension->getConflicts()) {
            $html .= '<dt>' . $this->getLang('conflicts') . '</dt>';
            $html .= '<dd>';
            $html .= $this->makeLinkList($extension->getConflicts());
            $html .= '</dd>';
        }
        $html .= '</dl>' . DOKU_LF;
        return $html;
    }

    /**
     * Generate a list of links for extensions
     *
     * @param array $ext The extensions
     * @return string The HTML code
     */
    public function makeLinkList($ext)
    {
        $html = '';
        foreach ($ext as $link) {
            $html .= '<bdi><a href="' .
                $this->gui->tabURL('search', ['q' => 'ext:' . $link]) . '">' .
                hsc($link) . '</a></bdi>, ';
        }
        return rtrim($html, ', ');
    }

    /**
     * Display the action buttons if they are possible
     *
     * @param helper_plugin_extension_extension $extension The extension
     * @return string The HTML code
     */
    public function makeActions(helper_plugin_extension_extension $extension)
    {
        global $conf;
        $html   = '';
        $errors = '';

        if ($extension->isInstalled()) {
            if (($canmod = $extension->canModify()) === true) {
                if (!$extension->isProtected()) {
                    $html .= $this->makeAction('uninstall', $extension);
                }
                if ($extension->getDownloadURL()) {
                    if ($extension->updateAvailable()) {
                        $html .= $this->makeAction('update', $extension);
                    } else {
                        $html .= $this->makeAction('reinstall', $extension);
                    }
                }
            } else {
                $errors .= '<p class="permerror">' . $this->getLang($canmod) . '</p>';
            }
            if (!$extension->isProtected() && !$extension->isTemplate()) { // no enable/disable for templates
                if ($extension->isEnabled()) {
                    $html .= $this->makeAction('disable', $extension);
                } else {
                    $html .= $this->makeAction('enable', $extension);
                }
            }
            if ($extension->isGitControlled()) {
                $errors .= '<p class="permerror">' . $this->getLang('git') . '</p>';
            }
            if (
                $extension->isEnabled() &&
                in_array('Auth', $extension->getTypes()) &&
                $conf['authtype'] != $extension->getID()
            ) {
                $errors .= '<p class="permerror">' . $this->getLang('auth') . '</p>';
            }
        } elseif (($canmod = $extension->canModify()) === true) {
            if ($extension->getDownloadURL()) {
                $html .= $this->makeAction('install', $extension);
            }
        } else {
            $errors .= '<div class="permerror">' . $this->getLang($canmod) . '</div>';
        }

        if (!$extension->isInstalled() && $extension->getDownloadURL()) {
            $html .= ' <span class="version">' . $this->getLang('available_version') . ' ';
            $html .= ($extension->getLastUpdate()
                    ? hsc($extension->getLastUpdate())
                    : $this->getLang('unknown')) . '</span>';
        }

        return $html . ' ' . $errors . DOKU_LF;
    }

    /**
     * Display an action button for an extension
     *
     * @param string                            $action    The action
     * @param helper_plugin_extension_extension $extension The extension
     * @return string The HTML code
     */
    public function makeAction($action, $extension)
    {
        $title = '';

        if ($action == 'install' || $action == 'reinstall') {
            $title = 'title="' . hsc($extension->getDownloadURL()) . '"';
        }

        $classes = 'button ' . $action;
        $name    = 'fn[' . $action . '][' . hsc($extension->getID()) . ']';

        $html = '<button class="' . $classes . '" name="' . $name . '" type="submit" ' . $title . '>' .
            $this->getLang('btn_' . $action) . '</button> ';
        return $html;
    }

    /**
     * Plugin/template status
     *
     * @param helper_plugin_extension_extension $extension The extension
     * @return string The description of all relevant statusses
     */
    public function makeStatus(helper_plugin_extension_extension $extension)
    {
        $status = [];

        if ($extension->isInstalled()) {
            $status[] = $this->getLang('status_installed');
            if ($extension->isProtected()) {
                $status[] = $this->getLang('status_protected');
            } else {
                $status[] = $extension->isEnabled()
                    ? $this->getLang('status_enabled')
                    : $this->getLang('status_disabled');
            }
        } else {
            $status[] = $this->getLang('status_not_installed');
        }
        if (!$extension->canModify()) $status[] = $this->getLang('status_unmodifiable');
        if ($extension->isBundled()) $status[] = $this->getLang('status_bundled');
        $status[] = $extension->isTemplate()
            ? $this->getLang('status_template')
            : $this->getLang('status_plugin');
        return implode(', ', $status);
    }
}
