示例#1
0
function buildLinkList($PAGE, $LINKSDB)
{
    // ---- Filter link database according to parameters
    $search_type = '';
    $search_crits = '';
    $privateonly = !empty($_SESSION['privateonly']) ? true : false;
    // Fulltext search
    if (isset($_GET['searchterm'])) {
        $search_crits = escape(trim($_GET['searchterm']));
        $search_type = LinkFilter::$FILTER_TEXT;
        $linksToDisplay = $LINKSDB->filter($search_type, $search_crits, false, $privateonly);
    } elseif (isset($_GET['searchtags'])) {
        $search_crits = explode(' ', escape(trim($_GET['searchtags'])));
        $search_type = LinkFilter::$FILTER_TAG;
        $linksToDisplay = $LINKSDB->filter($search_type, $search_crits, false, $privateonly);
    } elseif (isset($_SERVER['QUERY_STRING']) && preg_match('/[a-zA-Z0-9-_@]{6}(&.+?)?/', $_SERVER['QUERY_STRING'])) {
        $search_type = LinkFilter::$FILTER_HASH;
        $search_crits = substr(trim($_SERVER["QUERY_STRING"], '/'), 0, 6);
        $linksToDisplay = $LINKSDB->filter($search_type, $search_crits);
        if (count($linksToDisplay) == 0) {
            header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
            echo '<h1>404 Not found.</h1>Oh crap.
                  The link you are trying to reach does not exist or has been deleted.';
            echo '<br>Would you mind <a href="?">clicking here</a>?';
            exit;
        }
    } else {
        $linksToDisplay = $LINKSDB->filter('', '', false, $privateonly);
    }
    // ---- Handle paging.
    $keys = array();
    foreach ($linksToDisplay as $key => $value) {
        $keys[] = $key;
    }
    // If there is only a single link, we change on-the-fly the title of the page.
    if (count($linksToDisplay) == 1) {
        $GLOBALS['pagetitle'] = $linksToDisplay[$keys[0]]['title'] . ' - ' . $GLOBALS['title'];
    }
    // Select articles according to paging.
    $pagecount = ceil(count($keys) / $_SESSION['LINKS_PER_PAGE']);
    $pagecount = $pagecount == 0 ? 1 : $pagecount;
    $page = empty($_GET['page']) ? 1 : intval($_GET['page']);
    $page = $page < 1 ? 1 : $page;
    $page = $page > $pagecount ? $pagecount : $page;
    // Start index.
    $i = ($page - 1) * $_SESSION['LINKS_PER_PAGE'];
    $end = $i + $_SESSION['LINKS_PER_PAGE'];
    $linkDisp = array();
    while ($i < $end && $i < count($keys)) {
        $link = $linksToDisplay[$keys[$i]];
        $link['description'] = format_description($link['description'], $GLOBALS['redirector']);
        $classLi = $i % 2 != 0 ? '' : 'publicLinkHightLight';
        $link['class'] = $link['private'] == 0 ? $classLi : 'private';
        $link['timestamp'] = linkdate2timestamp($link['linkdate']);
        $taglist = explode(' ', $link['tags']);
        uasort($taglist, 'strcasecmp');
        $link['taglist'] = $taglist;
        $link['shorturl'] = smallHash($link['linkdate']);
        // Check for both signs of a note: starting with ? and 7 chars long.
        if ($link['url'][0] === '?' && strlen($link['url']) === 7) {
            $link['url'] = index_url($_SERVER) . $link['url'];
        }
        $linkDisp[$keys[$i]] = $link;
        $i++;
    }
    // Compute paging navigation
    $searchterm = empty($_GET['searchterm']) ? '' : '&searchterm=' . $_GET['searchterm'];
    $searchtags = empty($_GET['searchtags']) ? '' : '&searchtags=' . $_GET['searchtags'];
    $previous_page_url = '';
    if ($i != count($keys)) {
        $previous_page_url = '?page=' . ($page + 1) . $searchterm . $searchtags;
    }
    $next_page_url = '';
    if ($page > 1) {
        $next_page_url = '?page=' . ($page - 1) . $searchterm . $searchtags;
    }
    $token = '';
    if (isLoggedIn()) {
        $token = getToken();
    }
    // Fill all template fields.
    $data = array('linkcount' => count($LINKSDB), 'previous_page_url' => $previous_page_url, 'next_page_url' => $next_page_url, 'page_current' => $page, 'page_max' => $pagecount, 'result_count' => count($linksToDisplay), 'search_type' => $search_type, 'search_crits' => $search_crits, 'redirector' => empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'], 'token' => $token, 'links' => $linkDisp, 'tags' => $LINKSDB->allTags());
    // FIXME! temporary fix - see #399.
    if (!empty($GLOBALS['pagetitle']) && count($linkDisp) == 1) {
        $data['pagetitle'] = $GLOBALS['pagetitle'];
    }
    $pluginManager = PluginManager::getInstance();
    $pluginManager->executeHooks('render_linklist', $data, array('loggedin' => isLoggedIn()));
    foreach ($data as $key => $value) {
        $PAGE->assign($key, $value);
    }
    return;
}
示例#2
0
function renderPage()
{
    $LINKSDB = new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']);
    // Read links from database (and filter private links if used it not logged in).
    // -------- Display login form.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=login')) {
        if ($GLOBALS['config']['OPEN_SHAARLI']) {
            header('Location: ?');
            exit;
        }
        // No need to login for open Shaarli
        $token = '';
        if (ban_canLogin()) {
            $token = getToken();
        }
        // Do not waste token generation if not useful.
        $PAGE = new pageBuilder();
        $PAGE->assign('token', $token);
        $PAGE->assign('returnurl', isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '');
        $PAGE->renderPage('loginform');
        exit;
    }
    // -------- User wants to logout.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=logout')) {
        invalidateCaches();
        logout();
        header('Location: ?');
        exit;
    }
    // -------- Picture wall
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=picwall')) {
        // Optionnaly filter the results:
        $links = array();
        if (!empty($_GET['searchterm'])) {
            $links = $LINKSDB->filterFulltext($_GET['searchterm']);
        } elseif (!empty($_GET['searchtags'])) {
            $links = $LINKSDB->filterTags(trim($_GET['searchtags']));
        } else {
            $links = $LINKSDB;
        }
        $body = '';
        $linksToDisplay = array();
        // Get only links which have a thumbnail.
        foreach ($links as $link) {
            $permalink = '?' . htmlspecialchars(smallhash($link['linkdate']), ENT_QUOTES);
            $thumb = lazyThumbnail($link['url'], $permalink);
            if ($thumb != '') {
                $link['thumbnail'] = $thumb;
                // Thumbnail HTML code.
                $link['permalink'] = $permalink;
                $linksToDisplay[] = $link;
                // Add to array.
            }
        }
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('linksToDisplay', $linksToDisplay);
        $PAGE->renderPage('picwall');
        exit;
    }
    // -------- Tag cloud
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=tagcloud')) {
        $tags = $LINKSDB->allTags();
        // We sort tags alphabetically, then choose a font size according to count.
        // First, find max value.
        $maxcount = 0;
        foreach ($tags as $key => $value) {
            $maxcount = max($maxcount, $value);
        }
        ksort($tags);
        $tagList = array();
        foreach ($tags as $key => $value) {
            $tagList[$key] = array('count' => $value, 'size' => max(40 * $value / $maxcount, 8));
        }
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('tags', $tagList);
        $PAGE->renderPage('tagcloud');
        exit;
    }
    // -------- User clicks on a tag in a link: The tag is added to the list of searched tags (searchtags=...)
    if (isset($_GET['addtag'])) {
        // Get previous URL (http_referer) and add the tag to the searchtags parameters in query.
        if (empty($_SERVER['HTTP_REFERER'])) {
            header('Location: ?searchtags=' . urlencode($_GET['addtag']));
            exit;
        }
        // In case browser does not send HTTP_REFERER
        parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY), $params);
        $params['searchtags'] = empty($params['searchtags']) ? trim($_GET['addtag']) : trim($params['searchtags']) . ' ' . trim($_GET['addtag']);
        unset($params['page']);
        // We also remove page (keeping the same page has no sense, since the results are different)
        header('Location: ?' . http_build_query($params));
        exit;
    }
    // -------- User clicks on a tag in result count: Remove the tag from the list of searched tags (searchtags=...)
    if (isset($_GET['removetag'])) {
        // Get previous URL (http_referer) and remove the tag from the searchtags parameters in query.
        if (empty($_SERVER['HTTP_REFERER'])) {
            header('Location: ?');
            exit;
        }
        // In case browser does not send HTTP_REFERER
        parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY), $params);
        if (isset($params['searchtags'])) {
            $tags = explode(' ', $params['searchtags']);
            $tags = array_diff($tags, array($_GET['removetag']));
            // Remove value from array $tags.
            if (count($tags) == 0) {
                unset($params['searchtags']);
            } else {
                $params['searchtags'] = implode(' ', $tags);
            }
            unset($params['page']);
            // We also remove page (keeping the same page has no sense, since the results are different)
        }
        header('Location: ?' . http_build_query($params));
        exit;
    }
    // -------- User wants to change the number of links per page (linksperpage=...)
    if (isset($_GET['linksperpage'])) {
        if (is_numeric($_GET['linksperpage'])) {
            $_SESSION['LINKS_PER_PAGE'] = abs(intval($_GET['linksperpage']));
        }
        header('Location: ' . (empty($_SERVER['HTTP_REFERER']) ? '?' : $_SERVER['HTTP_REFERER']));
        exit;
    }
    // -------- User wants to see only private links (toggle)
    if (isset($_GET['privateonly'])) {
        if (empty($_SESSION['privateonly'])) {
            $_SESSION['privateonly'] = 1;
            // See only private links
        } else {
            unset($_SESSION['privateonly']);
            // See all links
        }
        header('Location: ' . (empty($_SERVER['HTTP_REFERER']) ? '?' : $_SERVER['HTTP_REFERER']));
        exit;
    }
    // --------- Weekly (all links form a specific week) ----------------------
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=weekly')) {
        $day_to_publish = 1;
        // 1 for monday, 7 for sunday
        $day_of_the_week = Date('N') - $day_to_publish;
        $week = Date('Ymd', time() - 3600 * 24 * $day_of_the_week);
        // one week ago
        if (isset($_GET['week'])) {
            $weektime = mktime(0, 0, 0, substr($_GET['week'], 4, 2), substr($_GET['week'], 6, 2), substr($_GET['week'], 0, 4));
            $day_of_the_week = Date('N', $weektime) - $day_to_publish;
            $week = Date('Ymd', $weektime - 3600 * 24 * $day_of_the_week);
            // one week ago
        }
        $previousweek = Date('Ymd', strtotime('-7 days', strtotime($week)));
        $nextweek = Date('Ymd', strtotime('+7 days', strtotime($week)));
        $linksToDisplay = $LINKSDB->filterWeek($week);
        // We pre-format some fields for proper output.
        foreach ($linksToDisplay as $key => $link) {
            $linksToDisplay[$key]['taglist'] = explode(' ', $link['tags']);
            $linksToDisplay[$key]['formatedDescription'] = nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description']))));
            $linksToDisplay[$key]['thumbnail'] = thumbnail($link['url']);
        }
        /* We need to spread the articles on 3 columns.
              I did not want to use a javascript lib like http://masonry.desandro.com/
              so I manually spread entries with a simple method: I roughly evaluate the 
              height of a div according to title and description length.
           */
        $columns = array(array(), array(), array());
        // Entries to display, for each column.
        $fill = array(0, 0, 0);
        // Rough estimate of columns fill.
        foreach ($linksToDisplay as $key => $link) {
            // Roughly estimate length of entry (by counting characters)
            // Title: 30 chars = 1 line. 1 line is 30 pixels height.
            // Description: 836 characters gives roughly 342 pixel height.
            // This is not perfect, but it's usually ok.
            $length = strlen($link['title']) + 342 * strlen($link['description']) / 836;
            if ($link['thumbnail']) {
                $length += 100;
            }
            // 1 thumbnails roughly takes 100 pixels height.
            // Then put in column which is the less filled:
            $smallest = min($fill);
            // find smallest value in array.
            $index = array_search($smallest, $fill);
            // find index of this smallest value.
            array_push($columns[$index], $link);
            // Put entry in this column.
            $fill[$index] += $length;
        }
        $PAGE = new pageBuilder();
        $PAGE->assign('linksToDisplay', $linksToDisplay);
        $PAGE->assign('col1', $columns[0]);
        $PAGE->assign('col2', $columns[1]);
        $PAGE->assign('col3', $columns[2]);
        $PAGE->assign('week', utf8_encode(strftime('%A %d, %B %Y', linkdate2timestamp($week . '_000000'))));
        $PAGE->assign('previousweek', $previousweek);
        $PAGE->assign('nextweek', $nextweek);
        $PAGE->renderPage('weekly');
        exit;
    }
    // --------- Monthly (all links form a specific month) ----------------------
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=monthly')) {
        $month = Date('Ym', strtotime('-1 month'));
        // Previous month, in format YYYYMM.
        if (isset($_GET['month'])) {
            $month = $_GET['month'];
        }
        $months = $LINKSDB->months();
        $i = array_search($month, $months);
        if ($i === false) {
            $i = count($months) - 1;
            $month = $months[$i];
        }
        $previousmonth = '';
        $nextmonth = '';
        if ($i !== false) {
            if ($i >= 1) {
                $previousmonth = $months[$i - 1];
            }
            if ($i < count($months) - 1) {
                $nextmonth = $months[$i + 1];
            }
        }
        $linksToDisplay = $LINKSDB->filterMonth($month);
        // We pre-format some fields for proper output.
        foreach ($linksToDisplay as $key => $link) {
            $linksToDisplay[$key]['taglist'] = explode(' ', $link['tags']);
            $linksToDisplay[$key]['formatedDescription'] = nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description']))));
            $linksToDisplay[$key]['thumbnail'] = thumbnail($link['url']);
        }
        /* We need to spread the articles on 3 columns.
              I did not want to use a javascript lib like http://masonry.desandro.com/
              so I manually spread entries with a simple method: I roughly evaluate the 
              height of a div according to title and description length.
           */
        $columns = array(array(), array(), array());
        // Entries to display, for each column.
        $fill = array(0, 0, 0);
        // Rough estimate of columns fill.
        foreach ($linksToDisplay as $key => $link) {
            // Roughly estimate length of entry (by counting characters)
            // Title: 30 chars = 1 line. 1 line is 30 pixels height.
            // Description: 836 characters gives roughly 342 pixel height.
            // This is not perfect, but it's usually ok.
            $length = strlen($link['title']) + 342 * strlen($link['description']) / 836;
            if ($link['thumbnail']) {
                $length += 100;
            }
            // 1 thumbnails roughly takes 100 pixels height.
            // Then put in column which is the less filled:
            $smallest = min($fill);
            // find smallest value in array.
            $index = array_search($smallest, $fill);
            // find index of this smallest value.
            array_push($columns[$index], $link);
            // Put entry in this column.
            $fill[$index] += $length;
        }
        $PAGE = new pageBuilder();
        $PAGE->assign('linksToDisplay', $linksToDisplay);
        $PAGE->assign('col1', $columns[0]);
        $PAGE->assign('col2', $columns[1]);
        $PAGE->assign('col3', $columns[2]);
        $PAGE->assign('month', utf8_encode(strftime('%B %Y', linkdate2timestamp($month . '01_000000'))));
        $PAGE->assign('previousmonth', $previousmonth);
        $PAGE->assign('nextmonth', $nextmonth);
        $PAGE->renderPage('monthly');
        exit;
    }
    // -------- Handle other actions allowed for non-logged in users:
    if (!isLoggedIn()) {
        // User tries to post new link but is not loggedin:
        // Show login screen, then redirect to ?post=...
        if (isset($_GET['post'])) {
            header('Location: ?do=login&post=' . urlencode($_GET['post']) . (!empty($_GET['title']) ? '&title=' . urlencode($_GET['title']) : '') . (!empty($_GET['source']) ? '&source=' . urlencode($_GET['source']) : ''));
            // Redirect to login page, then back to post link.
            exit;
        }
        $PAGE = new pageBuilder();
        buildLinkList($PAGE, $LINKSDB);
        // Compute list of links to display
        $PAGE->renderPage('linklist');
        exit;
        // Never remove this one ! All operations below are reserved for logged in user.
    }
    // -------- All other functions are reserved for the registered user:
    // -------- Display the Tools menu if requested (import/export/bookmarklet...)
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=tools')) {
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('pageabsaddr', indexUrl());
        $PAGE->renderPage('tools');
        exit;
    }
    // -------- User wants to change his/her password.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=changepasswd')) {
        if ($GLOBALS['config']['OPEN_SHAARLI']) {
            die('You are not supposed to change a password on an Open Shaarli.');
        }
        if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) {
            if (!tokenOk($_POST['token'])) {
                die('Wrong token.');
            }
            // Go away !
            // Make sure old password is correct.
            $oldhash = sha1($_POST['oldpassword'] . $GLOBALS['login'] . $GLOBALS['salt']);
            if ($oldhash != $GLOBALS['hash']) {
                echo '<script language="JavaScript">alert("The old password is not correct.");document.location=\'?do=changepasswd\';</script>';
                exit;
            }
            // Save new password
            $GLOBALS['salt'] = sha1(uniqid('', true) . '_' . mt_rand());
            // Salt renders rainbow-tables attacks useless.
            $GLOBALS['hash'] = sha1($_POST['setpassword'] . $GLOBALS['login'] . $GLOBALS['salt']);
            writeConfig();
            echo '<script language="JavaScript">alert("Your password has been changed.");document.location=\'?do=tools\';</script>';
            exit;
        } else {
            $PAGE = new pageBuilder();
            $PAGE->assign('linkcount', count($LINKSDB));
            $PAGE->assign('token', getToken());
            $PAGE->renderPage('changepassword');
            exit;
        }
    }
    // -------- User wants to change configuration
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=configure')) {
        if (!empty($_POST['title'])) {
            if (!tokenOk($_POST['token'])) {
                die('Wrong token.');
            }
            // Go away !
            $tz = 'UTC';
            if (!empty($_POST['continent']) && !empty($_POST['city'])) {
                if (isTZvalid($_POST['continent'], $_POST['city'])) {
                    $tz = $_POST['continent'] . '/' . $_POST['city'];
                }
            }
            $GLOBALS['timezone'] = $tz;
            $GLOBALS['title'] = $_POST['title'];
            $GLOBALS['redirector'] = $_POST['redirector'];
            $GLOBALS['disablesessionprotection'] = !empty($_POST['disablesessionprotection']);
            writeConfig();
            echo '<script language="JavaScript">alert("Configuration was saved.");document.location=\'?do=tools\';</script>';
            exit;
        } else {
            $PAGE = new pageBuilder();
            $PAGE->assign('linkcount', count($LINKSDB));
            $PAGE->assign('token', getToken());
            $PAGE->assign('title', htmlspecialchars(empty($GLOBALS['title']) ? '' : $GLOBALS['title'], ENT_QUOTES));
            $PAGE->assign('redirector', htmlspecialchars(empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'], ENT_QUOTES));
            list($timezone_form, $timezone_js) = templateTZform($GLOBALS['timezone']);
            $PAGE->assign('timezone_form', $timezone_form);
            // FIXME: put entire tz form generation in template ?
            $PAGE->assign('timezone_js', $timezone_js);
            $PAGE->renderPage('configure');
            exit;
        }
    }
    // -------- User wants to rename a tag or delete it
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=changetag')) {
        if (empty($_POST['fromtag'])) {
            $PAGE = new pageBuilder();
            $PAGE->assign('linkcount', count($LINKSDB));
            $PAGE->assign('token', getToken());
            $PAGE->renderPage('changetag');
            exit;
        }
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        // Delete a tag:
        if (!empty($_POST['deletetag']) && !empty($_POST['fromtag'])) {
            $needle = trim($_POST['fromtag']);
            $linksToAlter = $LINKSDB->filterTags($needle, true);
            // true for case-sensitive tag search.
            foreach ($linksToAlter as $key => $value) {
                $tags = explode(' ', trim($value['tags']));
                unset($tags[array_search($needle, $tags)]);
                // Remove tag.
                $value['tags'] = trim(implode(' ', $tags));
                $LINKSDB[$key] = $value;
            }
            $LINKSDB->savedb();
            // save to disk
            echo '<script language="JavaScript">alert("Tag was removed from ' . count($linksToAlter) . ' links.");document.location=\'?\';</script>';
            exit;
        }
        // Rename a tag:
        if (!empty($_POST['renametag']) && !empty($_POST['fromtag']) && !empty($_POST['totag'])) {
            $needle = trim($_POST['fromtag']);
            $linksToAlter = $LINKSDB->filterTags($needle, true);
            // true for case-sensitive tag search.
            foreach ($linksToAlter as $key => $value) {
                $tags = explode(' ', trim($value['tags']));
                $tags[array_search($needle, $tags)] = trim($_POST['totag']);
                // Remplace tags value.
                $value['tags'] = trim(implode(' ', $tags));
                $LINKSDB[$key] = $value;
            }
            $LINKSDB->savedb();
            // save to disk
            echo '<script language="JavaScript">alert("Tag was renamed in ' . count($linksToAlter) . ' links.");document.location=\'?searchtags=' . urlencode($_POST['totag']) . '\';</script>';
            exit;
        }
    }
    // -------- User wants to add a link without using the bookmarklet: show form.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=addlink')) {
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->renderPage('addlink');
        exit;
    }
    // -------- User clicked the "Save" button when editing a link: Save link to database.
    if (isset($_POST['save_edit'])) {
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        // Go away !
        $tags = trim(preg_replace('/\\s\\s+/', ' ', $_POST['lf_tags']));
        // Remove multiple spaces.
        $linkdate = $_POST['lf_linkdate'];
        $link = array('title' => trim($_POST['lf_title']), 'url' => trim($_POST['lf_url']), 'description' => trim($_POST['lf_description']), 'private' => isset($_POST['lf_private']) ? 1 : 0, 'linkdate' => $linkdate, 'tags' => str_replace(',', ' ', $tags));
        if ($link['title'] == '') {
            $link['title'] = $link['url'];
        }
        // If title is empty, use the URL as title.
        $LINKSDB[$linkdate] = $link;
        $LINKSDB->savedb();
        // save to disk
        pubsubhub();
        // If we are called from the bookmarklet, we must close the popup:
        if (isset($_GET['source']) && $_GET['source'] == 'bookmarklet') {
            echo '<script language="JavaScript">self.close();</script>';
            exit;
        }
        $returnurl = isset($_POST['returnurl']) ? $_POST['returnurl'] : '?';
        header('Location: ' . $returnurl);
        // After saving the link, redirect to the page the user was on.
        exit;
    }
    // -------- User clicked the "Cancel" button when editing a link.
    if (isset($_POST['cancel_edit'])) {
        // If we are called from the bookmarklet, we must close the popup;
        if (isset($_GET['source']) && $_GET['source'] == 'bookmarklet') {
            echo '<script language="JavaScript">self.close();</script>';
            exit;
        }
        $returnurl = isset($_POST['returnurl']) ? $_POST['returnurl'] : '?';
        header('Location: ' . $returnurl);
        // After canceling, redirect to the page the user was on.
        exit;
    }
    // -------- User clicked the "Delete" button when editing a link : Delete link from database.
    if (isset($_POST['delete_link'])) {
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        // We do not need to ask for confirmation:
        // - confirmation is handled by javascript
        // - we are protected from XSRF by the token.
        $linkdate = $_POST['lf_linkdate'];
        unset($LINKSDB[$linkdate]);
        $LINKSDB->savedb();
        // save to disk
        // If we are called from the bookmarklet, we must close the popup:
        if (isset($_GET['source']) && $_GET['source'] == 'bookmarklet') {
            echo '<script language="JavaScript">self.close();</script>';
            exit;
        }
        $returnurl = isset($_POST['returnurl']) ? $_POST['returnurl'] : '?';
        if ($returnurl == '?') {
            $returnurl = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '?';
        }
        header('Location: ' . $returnurl);
        // After deleting the link, redirect to the page the user was on.
        exit;
    }
    // -------- User clicked the "EDIT" button on a link: Display link edit form.
    if (isset($_GET['edit_link'])) {
        $link = $LINKSDB[$_GET['edit_link']];
        // Read database
        if (!$link) {
            header('Location: ?');
            exit;
        }
        // Link not found in database.
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('link', $link);
        $PAGE->assign('link_is_new', false);
        $PAGE->assign('token', getToken());
        // XSRF protection.
        $PAGE->assign('http_referer', isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '');
        $PAGE->renderPage('editlink');
        exit;
    }
    // -------- User want to post a new link: Display link edit form.
    if (isset($_GET['post'])) {
        $url = $_GET['post'];
        // We remove the annoying parameters added by FeedBurner and GoogleFeedProxy (?utm_source=...)
        $i = strpos($url, '&utm_source=');
        if ($i !== false) {
            $url = substr($url, 0, $i);
        }
        $i = strpos($url, '?utm_source=');
        if ($i !== false) {
            $url = substr($url, 0, $i);
        }
        $i = strpos($url, '#xtor=RSS-');
        if ($i !== false) {
            $url = substr($url, 0, $i);
        }
        $link_is_new = false;
        $link = $LINKSDB->getLinkFromUrl($url);
        // Check if URL is not already in database (in this case, we will edit the existing link)
        if (!$link) {
            $link_is_new = true;
            // This is a new link
            $linkdate = strval(date('Ymd_His'));
            $title = empty($_GET['title']) ? '' : $_GET['title'];
            // Get title if it was provided in URL (by the bookmarklet).
            $description = '';
            $tags = '';
            $private = 0;
            if ($url != '' && parse_url($url, PHP_URL_SCHEME) == '') {
                $url = 'http://' . $url;
            }
            // If this is an HTTP link, we try go get the page to extact the title (otherwise we will to straight to the edit form.)
            if (empty($title) && parse_url($url, PHP_URL_SCHEME) == 'http') {
                list($status, $headers, $data) = getHTTP($url, 4);
                // Short timeout to keep the application responsive.
                // FIXME: Decode charset according to specified in either 1) HTTP response headers or 2) <head> in html
                if (strpos($status, '200 OK') !== false) {
                    $title = html_entity_decode(html_extract_title($data), ENT_QUOTES, 'UTF-8');
                }
            }
            if ($url == '') {
                $url = '?' . smallHash($linkdate);
            }
            // In case of empty URL, this is just a text (with a link that point to itself)
            $link = array('linkdate' => $linkdate, 'title' => $title, 'url' => $url, 'description' => $description, 'tags' => $tags, 'private' => 0);
        }
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('link', $link);
        $PAGE->assign('link_is_new', $link_is_new);
        $PAGE->assign('token', getToken());
        // XSRF protection.
        $PAGE->assign('http_referer', isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '');
        $PAGE->renderPage('editlink');
        exit;
    }
    // -------- Export as Netscape Bookmarks HTML file.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=export')) {
        if (empty($_GET['what'])) {
            $PAGE = new pageBuilder();
            $PAGE->assign('linkcount', count($LINKSDB));
            $PAGE->renderPage('export');
            exit;
        }
        $exportWhat = $_GET['what'];
        if (!array_intersect(array('all', 'public', 'private'), array($exportWhat))) {
            die('What are you trying to export ???');
        }
        header('Content-Type: text/html; charset=utf-8');
        header('Content-disposition: attachment; filename=bookmarks_' . $exportWhat . '_' . strval(date('Ymd_His')) . '.html');
        $currentdate = date('Y/m/d H:i:s');
        echo <<<HTML
<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
     It will be read and overwritten.
     DO NOT EDIT! -->
<!-- Shaarli {$exportWhat} bookmarks export on {$currentdate} -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
HTML;
        foreach ($LINKSDB as $link) {
            if ($exportWhat == 'all' || $exportWhat == 'private' && $link['private'] != 0 || $exportWhat == 'public' && $link['private'] == 0) {
                echo '<DT><A HREF="' . htmlspecialchars($link['url']) . '" ADD_DATE="' . linkdate2timestamp($link['linkdate']) . '" PRIVATE="' . $link['private'] . '"';
                if ($link['tags'] != '') {
                    echo ' TAGS="' . htmlspecialchars(str_replace(' ', ',', $link['tags'])) . '"';
                }
                echo '>' . htmlspecialchars($link['title']) . "</A>\n";
                if ($link['description'] != '') {
                    echo '<DD>' . htmlspecialchars($link['description']) . "\n";
                }
            }
        }
        exit;
    }
    // -------- User is uploading a file for import
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=upload')) {
        // If file is too big, some form field may be missing.
        if (!isset($_POST['token']) || !isset($_FILES) || isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size'] == 0) {
            $returnurl = empty($_SERVER['HTTP_REFERER']) ? '?' : $_SERVER['HTTP_REFERER'];
            echo '<script language="JavaScript">alert("The file you are trying to upload is probably bigger than what this webserver can accept (' . getMaxFileSize() . ' bytes). Please upload in smaller chunks.");document.location=\'' . htmlspecialchars($returnurl) . '\';</script>';
            exit;
        }
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        importFile();
        exit;
    }
    // -------- Show upload/import dialog:
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=import')) {
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('token', getToken());
        $PAGE->assign('maxfilesize', getMaxFileSize());
        $PAGE->renderPage('import');
        exit;
    }
    // -------- Otherwise, simply display search form and links:
    $PAGE = new pageBuilder();
    $PAGE->assign('linkcount', count($LINKSDB));
    buildLinkList($PAGE, $LINKSDB);
    // Compute list of links to display
    $PAGE->renderPage('linklist');
    exit;
}
示例#3
0
文件: index.php 项目: Eragos/Shaarli
function buildLinkList($PAGE, $LINKSDB)
{
    // ---- Filter link database according to parameters
    $linksToDisplay = array();
    $search_type = '';
    $search_crits = '';
    if (isset($_GET['searchterm'])) {
        $linksToDisplay = $LINKSDB->filterFulltext(trim($_GET['searchterm']));
        $search_crits = escape(trim($_GET['searchterm']));
        $search_type = 'fulltext';
    } elseif (isset($_GET['searchtags'])) {
        $linksToDisplay = $LINKSDB->filterTags(trim($_GET['searchtags']));
        $search_crits = explode(' ', escape(trim($_GET['searchtags'])));
        $search_type = 'tags';
    } elseif (isset($_SERVER['QUERY_STRING']) && preg_match('/[a-zA-Z0-9-_@]{6}(&.+?)?/', $_SERVER['QUERY_STRING'])) {
        $linksToDisplay = $LINKSDB->filterSmallHash(substr(trim($_SERVER["QUERY_STRING"], '/'), 0, 6));
        if (count($linksToDisplay) == 0) {
            header($_SERVER["SERVER_PROTOCOL"] . " 404 Not Found");
            echo '<h1>404 Not found.</h1>Oh crap. The link you are trying to reach does not exist or has been deleted.';
            echo '<br>Would you mind <a href="?">clicking here</a>?';
            exit;
        }
        $search_type = 'permalink';
    } else {
        $linksToDisplay = $LINKSDB;
    }
    // Otherwise, display without filtering.
    // Option: Show only private links
    if (!empty($_SESSION['privateonly'])) {
        $tmp = array();
        foreach ($linksToDisplay as $linkdate => $link) {
            if ($link['private'] != 0) {
                $tmp[$linkdate] = $link;
            }
        }
        $linksToDisplay = $tmp;
    }
    // ---- Handle paging.
    /* Can someone explain to me why you get the following error when using array_keys() on an object which implements the interface ArrayAccess???
          "Warning: array_keys() expects parameter 1 to be array, object given in ... "
          If my class implements ArrayAccess, why won't array_keys() accept it ?  ( $keys=array_keys($linksToDisplay); )
       */
    $keys = array();
    foreach ($linksToDisplay as $key => $value) {
        $keys[] = $key;
    }
    // Stupid and ugly. Thanks PHP.
    // If there is only a single link, we change on-the-fly the title of the page.
    if (count($linksToDisplay) == 1) {
        $GLOBALS['pagetitle'] = $linksToDisplay[$keys[0]]['title'] . ' - ' . $GLOBALS['title'];
    }
    // Select articles according to paging.
    $pagecount = ceil(count($keys) / $_SESSION['LINKS_PER_PAGE']);
    $pagecount = $pagecount == 0 ? 1 : $pagecount;
    $page = empty($_GET['page']) ? 1 : intval($_GET['page']);
    $page = $page < 1 ? 1 : $page;
    $page = $page > $pagecount ? $pagecount : $page;
    $i = ($page - 1) * $_SESSION['LINKS_PER_PAGE'];
    // Start index.
    $end = $i + $_SESSION['LINKS_PER_PAGE'];
    $linkDisp = array();
    // Links to display
    while ($i < $end && $i < count($keys)) {
        $link = $linksToDisplay[$keys[$i]];
        $link['description'] = nl2br(keepMultipleSpaces(text2clickable($link['description'])));
        $title = $link['title'];
        $classLi = $i % 2 != 0 ? '' : 'publicLinkHightLight';
        $link['class'] = $link['private'] == 0 ? $classLi : 'private';
        $link['timestamp'] = linkdate2timestamp($link['linkdate']);
        $taglist = explode(' ', $link['tags']);
        uasort($taglist, 'strcasecmp');
        $link['taglist'] = $taglist;
        if ($link["url"][0] === '?' && strlen($link["url"]) === 7) {
            $link["url"] = indexUrl() . $link["url"];
        }
        $linkDisp[$keys[$i]] = $link;
        $i++;
    }
    // Compute paging navigation
    $searchterm = empty($_GET['searchterm']) ? '' : '&searchterm=' . $_GET['searchterm'];
    $searchtags = empty($_GET['searchtags']) ? '' : '&searchtags=' . $_GET['searchtags'];
    $paging = '';
    $previous_page_url = '';
    if ($i != count($keys)) {
        $previous_page_url = '?page=' . ($page + 1) . $searchterm . $searchtags;
    }
    $next_page_url = '';
    if ($page > 1) {
        $next_page_url = '?page=' . ($page - 1) . $searchterm . $searchtags;
    }
    $token = '';
    if (isLoggedIn()) {
        $token = getToken();
    }
    // Fill all template fields.
    $PAGE->assign('linkcount', count($LINKSDB));
    $PAGE->assign('previous_page_url', $previous_page_url);
    $PAGE->assign('next_page_url', $next_page_url);
    $PAGE->assign('page_current', $page);
    $PAGE->assign('page_max', $pagecount);
    $PAGE->assign('result_count', count($linksToDisplay));
    $PAGE->assign('search_type', $search_type);
    $PAGE->assign('search_crits', $search_crits);
    $PAGE->assign('redirector', empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector']);
    // Optional redirector URL.
    $PAGE->assign('token', $token);
    $PAGE->assign('links', $linkDisp);
    $PAGE->assign('tags', $LINKSDB->allTags());
    return;
}
示例#4
0
function renderPage()
{
    if (isset($_POST['save_edit'])) {
        linkdb::$editLink = TRUE;
    }
    $LINKSDB = new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']);
    // Read links from database (and filter private links if used it not logged in).
    // -------- Display login form.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=login')) {
        if ($GLOBALS['config']['OPEN_SHAARLI']) {
            header('Location: ?');
            exit;
        }
        // No need to login for open Shaarli
        $token = '';
        if (ban_canLogin()) {
            $token = getToken();
        }
        // Do not waste token generation if not useful.
        $PAGE = new pageBuilder();
        $PAGE->assign('token', $token);
        $PAGE->assign('returnurl', isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '');
        $PAGE->renderPage('loginform');
        exit;
    }
    // -------- User wants to logout.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=logout')) {
        invalidateCaches();
        logout();
        header('Location: ?');
        exit;
    }
    // -------- Picture wall
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=picwall')) {
        // Optionnaly filter the results:
        $links = array();
        if (!empty($_GET['searchterm'])) {
            $links = $LINKSDB->filterFulltext($_GET['searchterm']);
        } elseif (!empty($_GET['searchtags'])) {
            $links = $LINKSDB->filterTags(trim($_GET['searchtags']));
        } else {
            $links = $LINKSDB;
        }
        $body = '';
        $linksToDisplay = array();
        // Get only links which have a thumbnail.
        foreach ($links as $link) {
            $permalink = '?' . htmlspecialchars(smallhash($link['linkdate']), ENT_QUOTES);
            $thumb = lazyThumbnail($link['url'], $permalink);
            if ($thumb != '') {
                $link['thumbnail'] = $thumb;
                // Thumbnail HTML code.
                $link['permalink'] = $permalink;
                $linksToDisplay[] = $link;
                // Add to array.
            }
        }
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('linksToDisplay', $linksToDisplay);
        $PAGE->renderPage('picwall');
        exit;
    }
    // -------- Tag cloud
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=tagcloud')) {
        $tags = $LINKSDB->allTags();
        // We sort tags alphabetically, then choose a font size according to count.
        // First, find max value.
        $maxcount = 0;
        foreach ($tags as $key => $value) {
            $maxcount = max($maxcount, $value);
        }
        ksort($tags);
        $tagList = array();
        foreach ($tags as $key => $value) {
            $tagList[$key] = array('count' => $value, 'size' => max(40 * $value / $maxcount, 8));
        }
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('tags', $tagList);
        $PAGE->renderPage('tagcloud');
        exit;
    }
    // -------- User clicks on a tag in a link: The tag is added to the list of searched tags (searchtags=...)
    if (isset($_GET['addtag'])) {
        // Get previous URL (http_referer) and add the tag to the searchtags parameters in query.
        if (empty($_SERVER['HTTP_REFERER'])) {
            header('Location: ?searchtags=' . urlencode($_GET['addtag']));
            exit;
        }
        // In case browser does not send HTTP_REFERER
        parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY), $params);
        $params['searchtags'] = empty($params['searchtags']) ? trim($_GET['addtag']) : trim($params['searchtags']) . ' ' . trim($_GET['addtag']);
        unset($params['page']);
        // We also remove page (keeping the same page has no sense, since the results are different)
        header('Location: ?' . http_build_query($params));
        exit;
    }
    // -------- User clicks on a tag in result count: Remove the tag from the list of searched tags (searchtags=...)
    if (isset($_GET['removetag'])) {
        // Get previous URL (http_referer) and remove the tag from the searchtags parameters in query.
        if (empty($_SERVER['HTTP_REFERER'])) {
            header('Location: ?');
            exit;
        }
        // In case browser does not send HTTP_REFERER
        parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY), $params);
        if (isset($params['searchtags'])) {
            $tags = explode(' ', $params['searchtags']);
            $tags = array_diff($tags, array($_GET['removetag']));
            // Remove value from array $tags.
            if (count($tags) == 0) {
                unset($params['searchtags']);
            } else {
                $params['searchtags'] = implode(' ', $tags);
            }
            unset($params['page']);
            // We also remove page (keeping the same page has no sense, since the results are different)
        }
        header('Location: ?' . http_build_query($params));
        exit;
    }
    // -------- User wants to change the number of links per page (linksperpage=...)
    if (isset($_GET['linksperpage'])) {
        if (is_numeric($_GET['linksperpage'])) {
            $_SESSION['LINKS_PER_PAGE'] = abs(intval($_GET['linksperpage']));
        }
        // Make sure the referer is from Shaarli itself.
        $referer = '?';
        if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST), $_SERVER['SERVER_NAME']) == 0) {
            $referer = $_SERVER['HTTP_REFERER'];
        }
        header('Location: ' . $referer);
        exit;
    }
    // -------- User wants to see only private links (toggle)
    if (isset($_GET['privateonly'])) {
        if (empty($_SESSION['privateonly'])) {
            $_SESSION['privateonly'] = 1;
            // See only private links
        } else {
            unset($_SESSION['privateonly']);
            // See all links
        }
        // Make sure the referer is from Shaarli itself.
        $referer = '?';
        if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST), $_SERVER['SERVER_NAME']) == 0) {
            $referer = $_SERVER['HTTP_REFERER'];
        }
        header('Location: ' . $referer);
        exit;
    }
    // -------- Handle other actions allowed for non-logged in users:
    if (!isLoggedIn()) {
        // User tries to post new link but is not loggedin:
        // Show login screen, then redirect to ?post=...
        if (isset($_GET['post'])) {
            header('Location: ?do=login&post=' . urlencode($_GET['post']) . (!empty($_GET['title']) ? '&title=' . urlencode($_GET['title']) : '') . (!empty($_GET['source']) ? '&source=' . urlencode($_GET['source']) : ''));
            // Redirect to login page, then back to post link.
            exit;
        }
        $PAGE = new pageBuilder();
        buildLinkList($PAGE, $LINKSDB);
        // Compute list of links to display
        $PAGE->renderPage('linklist');
        exit;
        // Never remove this one ! All operations below are reserved for logged in user.
    }
    // -------- All other functions are reserved for the registered user:
    // -------- Display the Tools menu if requested (import/export/bookmarklet...)
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=tools')) {
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('pageabsaddr', indexUrl());
        $PAGE->renderPage('tools');
        exit;
    }
    // -------- User wants to change his/her password.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=changepasswd')) {
        if ($GLOBALS['config']['OPEN_SHAARLI']) {
            die('You are not supposed to change a password on an Open Shaarli.');
        }
        if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) {
            if (!tokenOk($_POST['token'])) {
                die('Wrong token.');
            }
            // Go away !
            // Make sure old password is correct.
            $oldhash = sha1($_POST['oldpassword'] . $_SESSION['username'] . $GLOBALS['salt']);
            if ($oldhash != $GLOBALS['password'][$_SESSION['username']]) {
                echo '<script language="JavaScript">alert("The old password is not correct.");document.location=\'?do=changepasswd\';</script>';
                exit;
            }
            // Save new password
            //$GLOBALS['salt'] = sha1(uniqid('',true).'_'.mt_rand()); // Salt renders rainbow-tables attacks useless.
            $GLOBALS['password'][$_SESSION['username']] = sha1($_POST['setpassword'] . $_SESSION['username'] . $GLOBALS['salt']);
            writeConfig();
            echo '<script language="JavaScript">alert("Your password has been changed.");document.location=\'?do=tools\';</script>';
            exit;
        } else {
            $PAGE = new pageBuilder();
            $PAGE->assign('linkcount', count($LINKSDB));
            $PAGE->assign('token', getToken());
            $PAGE->renderPage('changepassword');
            exit;
        }
    }
    // -------- User wants to manage users.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=users')) {
        $PAGE = new pageBuilder();
        foreach ($GLOBALS['login'] as $key => $username) {
            if ($key === 0) {
                $users[$key]['sysAdmin'] = TRUE;
            } else {
                $users[$key]['sysAdmin'] = FALSE;
            }
            $users[$key]['username'] = $username;
            $users[$key]['level'] = $GLOBALS['level'][$username];
            $users[$key]['email'] = $GLOBALS['email'][$username];
        }
        $PAGE->assign('users', $users);
        $PAGE->renderPage('manageusers');
        exit;
    }
    // -------- Create / Editing user.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=saveUser')) {
        $saveUserMessage = 'user successfully updated !';
        if (isset($_GET['deleteUser'])) {
            if (is_array($GLOBALS['login'])) {
                if (in_array($_GET['username'], $GLOBALS['login'])) {
                    $sUkey = array_search($_GET['username'], $GLOBALS['login']);
                    if ($sUkey !== 0) {
                        unset($GLOBALS['login'][$sUkey], $GLOBALS['password'][$_GET['username']], $GLOBALS['level'][$_GET['username']], $GLOBALS['email'][$_GET['username']]);
                        $GLOBALS['login'] = array_values($GLOBALS['login']);
                        writeConfig();
                        echo '<script language="JavaScript">alert("' . $_GET['username'] . ' was deleted.");document.location=\'?do=users\';</script>';
                    }
                }
            }
            exit;
        }
        if (is_array($GLOBALS['login'])) {
            if (!in_array($_GET['username'], $GLOBALS['login'])) {
                $newUserKey = count($GLOBALS['login']);
                $newUser = array($newUserKey => $_GET['username']);
                $GLOBALS['login'] = $GLOBALS['login'] + $newUser;
                $saveUserMessage = 'user successfully created !';
            }
            if (is_array($newUser) || isset($_GET['resetPassword'])) {
                $newPassword = smallHash(sha1(uniqid('', true) . '_' . mt_rand()));
                $GLOBALS['password'][$_GET['username']] = sha1($newPassword . $_GET['username'] . $GLOBALS['salt']);
                $saveUserMessage .= ' His new password is ' . $newPassword;
            }
            $GLOBALS['level'][$_GET['username']] = (int) $_GET['userlevel'];
            $GLOBALS['email'][$_GET['username']] = $_GET['email'];
        }
        writeConfig();
        echo '<script language="JavaScript">alert("' . $saveUserMessage . '");document.location=\'?do=users\';</script>';
        exit;
    }
    // -------- User wants to change configuration
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=configure')) {
        if (!empty($_POST['title'])) {
            if (!tokenOk($_POST['token'])) {
                die('Wrong token.');
            }
            // Go away !
            $tz = 'UTC';
            if (!empty($_POST['continent']) && !empty($_POST['city'])) {
                if (isTZvalid($_POST['continent'], $_POST['city'])) {
                    $tz = $_POST['continent'] . '/' . $_POST['city'];
                }
            }
            $GLOBALS['timezone'] = $tz;
            $GLOBALS['title'] = $_POST['title'];
            $GLOBALS['redirector'] = $_POST['redirector'];
            $GLOBALS['disablesessionprotection'] = !empty($_POST['disablesessionprotection']);
            $GLOBALS['disablejquery'] = !empty($_POST['disablejquery']);
            $GLOBALS['privateLinkByDefault'] = !empty($_POST['privateLinkByDefault']);
            writeConfig();
            echo '<script language="JavaScript">alert("Configuration was saved.");document.location=\'?do=tools\';</script>';
            exit;
        } else {
            $PAGE = new pageBuilder();
            $PAGE->assign('linkcount', count($LINKSDB));
            $PAGE->assign('token', getToken());
            $PAGE->assign('title', htmlspecialchars(empty($GLOBALS['title']) ? '' : $GLOBALS['title'], ENT_QUOTES));
            $PAGE->assign('redirector', htmlspecialchars(empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'], ENT_QUOTES));
            list($timezone_form, $timezone_js) = templateTZform($GLOBALS['timezone']);
            $PAGE->assign('timezone_form', $timezone_form);
            // FIXME: put entire tz form generation in template ?
            $PAGE->assign('timezone_js', $timezone_js);
            $PAGE->renderPage('configure');
            exit;
        }
    }
    // -------- User wants to rename a tag or delete it
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=changetag')) {
        if (empty($_POST['fromtag'])) {
            $PAGE = new pageBuilder();
            $PAGE->assign('linkcount', count($LINKSDB));
            $PAGE->assign('token', getToken());
            $PAGE->renderPage('changetag');
            exit;
        }
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        // Delete a tag:
        if (!empty($_POST['deletetag']) && !empty($_POST['fromtag'])) {
            $needle = trim($_POST['fromtag']);
            $linksToAlter = $LINKSDB->filterTags($needle, true);
            // true for case-sensitive tag search.
            foreach ($linksToAlter as $key => $value) {
                $tags = explode(' ', trim($value['tags']));
                unset($tags[array_search($needle, $tags)]);
                // Remove tag.
                $value['tags'] = trim(implode(' ', $tags));
                $LINKSDB[$key] = $value;
            }
            $LINKSDB->savedb();
            // save to disk
            echo '<script language="JavaScript">alert("Tag was removed from ' . count($linksToAlter) . ' links.");document.location=\'?\';</script>';
            exit;
        }
        // Rename a tag:
        if (!empty($_POST['renametag']) && !empty($_POST['fromtag']) && !empty($_POST['totag'])) {
            $needle = trim($_POST['fromtag']);
            $linksToAlter = $LINKSDB->filterTags($needle, true);
            // true for case-sensitive tag search.
            foreach ($linksToAlter as $key => $value) {
                $tags = explode(' ', trim($value['tags']));
                $tags[array_search($needle, $tags)] = trim($_POST['totag']);
                // Remplace tags value.
                $value['tags'] = trim(implode(' ', $tags));
                $LINKSDB[$key] = $value;
            }
            $LINKSDB->savedb();
            // save to disk
            echo '<script language="JavaScript">alert("Tag was renamed in ' . count($linksToAlter) . ' links.");document.location=\'?searchtags=' . urlencode($_POST['totag']) . '\';</script>';
            exit;
        }
    }
    // -------- User wants to add a link without using the bookmarklet: show form.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=addlink')) {
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->renderPage('addlink');
        exit;
    }
    // -------- User clicked the "Save" button when editing a link: Save link to database.
    if (isset($_POST['save_edit'])) {
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        // Go away !
        $tags = trim(preg_replace('/\\s\\s+/', ' ', $_POST['lf_tags']));
        // Remove multiple spaces.
        $linkdate = $_POST['lf_linkdate'];
        // If user is not an admin and try to edit other link of him and keep author when updating link
        if (isset($LINKSDB[$linkdate])) {
            if ($_SESSION['level'] < 3 && $LINKSDB[$linkdate]['author'] !== $_SESSION['username']) {
                die('You cannot edit link of other !');
            }
            $author = $LINKSDB[$linkdate]['author'];
        } else {
            $author = $_SESSION['username'];
        }
        $url = trim($_POST['lf_url']);
        if (!startsWith($url, 'http:') && !startsWith($url, 'https:') && !startsWith($url, 'ftp:') && !startsWith($url, 'magnet:') && !startsWith($url, '?')) {
            $url = 'http://' . $url;
        }
        $link = array('title' => trim($_POST['lf_title']), 'url' => $url, 'description' => trim($_POST['lf_description']), 'private' => $_POST['lf_private'], 'author' => $author, 'linkdate' => $linkdate, 'tags' => str_replace(',', ' ', $tags));
        if ($link['title'] == '') {
            $link['title'] = $link['url'];
        }
        // If title is empty, use the URL as title.
        $LINKSDB[$linkdate] = $link;
        $LINKSDB->savedb();
        // save to disk
        pubsubhub();
        // If we are called from the bookmarklet, we must close the popup:
        if (isset($_GET['source']) && $_GET['source'] == 'bookmarklet') {
            echo '<script language="JavaScript">self.close();</script>';
            exit;
        }
        $returnurl = isset($_POST['returnurl']) ? $_POST['returnurl'] : '?';
        $returnurl .= '#' . smallHash($linkdate);
        // Scroll to the link which has been edited.
        header('Location: ' . $returnurl);
        // After saving the link, redirect to the page the user was on.
        exit;
    }
    // -------- User clicked the "Cancel" button when editing a link.
    if (isset($_POST['cancel_edit'])) {
        // If we are called from the bookmarklet, we must close the popup;
        if (isset($_GET['source']) && $_GET['source'] == 'bookmarklet') {
            echo '<script language="JavaScript">self.close();</script>';
            exit;
        }
        $returnurl = isset($_POST['returnurl']) ? $_POST['returnurl'] : '?';
        $returnurl .= '#' . smallHash($_POST['lf_linkdate']);
        // Scroll to the link which has been edited.
        header('Location: ' . $returnurl);
        // After canceling, redirect to the page the user was on.
        exit;
    }
    // -------- User clicked the "Delete" button when editing a link : Delete link from database.
    if (isset($_POST['delete_link'])) {
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        // We do not need to ask for confirmation:
        // - confirmation is handled by javascript
        // - we are protected from XSRF by the token.
        $linkdate = $_POST['lf_linkdate'];
        if ($_SESSION['level'] < 3 && $LINKSDB[$linkdate]['author'] !== $_SESSION['username']) {
            die('You cannot delete link of other !');
        }
        unset($LINKSDB[$linkdate]);
        $LINKSDB->savedb();
        // save to disk
        // If we are called from the bookmarklet, we must close the popup:
        if (isset($_GET['source']) && $_GET['source'] == 'bookmarklet') {
            echo '<script language="JavaScript">self.close();</script>';
            exit;
        }
        $returnurl = isset($_POST['returnurl']) ? $_POST['returnurl'] : '?';
        if ($returnurl == '?') {
            $returnurl = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '?';
        }
        header('Location: ' . $returnurl);
        // After deleting the link, redirect to the page the user was on.
        exit;
    }
    // -------- User clicked the "EDIT" button on a link: Display link edit form.
    if (isset($_GET['edit_link'])) {
        $link = $LINKSDB[$_GET['edit_link']];
        // Read database
        if (!$link) {
            header('Location: ?');
            exit;
        }
        // Link not found in database.
        if ($_SESSION['level'] < 3 && $LINKSDB[$_GET['edit_link']]['author'] !== $_SESSION['username']) {
            exit;
        }
        // If user is not an admin and try to edit other link of him
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('link', $link);
        $PAGE->assign('link_is_new', false);
        $PAGE->assign('token', getToken());
        // XSRF protection.
        $PAGE->assign('http_referer', isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '');
        $PAGE->renderPage('editlink');
        exit;
    }
    // -------- User want to post a new link: Display link edit form.
    if (isset($_GET['post'])) {
        $url = $_GET['post'];
        // We remove the annoying parameters added by FeedBurner and GoogleFeedProxy (?utm_source=...)
        $i = strpos($url, '&utm_source=');
        if ($i !== false) {
            $url = substr($url, 0, $i);
        }
        $i = strpos($url, '?utm_source=');
        if ($i !== false) {
            $url = substr($url, 0, $i);
        }
        $i = strpos($url, '#xtor=RSS-');
        if ($i !== false) {
            $url = substr($url, 0, $i);
        }
        $link_is_new = false;
        $link = $LINKSDB->getLinkFromUrl($url);
        // Check if URL is not already in database (in this case, we will edit the existing link)
        if (!$link) {
            $link_is_new = true;
            // This is a new link
            $linkdate = strval(date('Ymd_His'));
            $title = empty($_GET['title']) ? '' : $_GET['title'];
            // Get title if it was provided in URL (by the bookmarklet).
            $description = '';
            $tags = '';
            $private = 2;
            $author = $_SESSION['username'];
            if ($url != '' && parse_url($url, PHP_URL_SCHEME) == '') {
                $url = 'http://' . $url;
            }
            // If this is an HTTP link, we try go get the page to extact the title (otherwise we will to straight to the edit form.)
            if (empty($title) && parse_url($url, PHP_URL_SCHEME) == 'http') {
                list($status, $headers, $data) = getHTTP($url, 4);
                // Short timeout to keep the application responsive.
                // FIXME: Decode charset according to specified in either 1) HTTP response headers or 2) <head> in html
                if (strpos($status, '200 OK') !== false) {
                    $title = html_entity_decode(html_extract_title($data), ENT_QUOTES, 'UTF-8');
                }
            }
            if ($url == '') {
                $url = '?' . smallHash($linkdate);
            }
            // In case of empty URL, this is just a text (with a link that point to itself)
            $link = array('linkdate' => $linkdate, 'title' => $title, 'url' => $url, 'description' => $description, 'tags' => $tags, 'private' => 2, 'author' => $author);
        }
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('link', $link);
        $PAGE->assign('link_is_new', $link_is_new);
        $PAGE->assign('token', getToken());
        // XSRF protection.
        $PAGE->assign('http_referer', isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '');
        $PAGE->renderPage('editlink');
        exit;
    }
    // -------- Export as Netscape Bookmarks HTML file.
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=export')) {
        if (empty($_GET['what'])) {
            $PAGE = new pageBuilder();
            $PAGE->assign('linkcount', count($LINKSDB));
            $PAGE->renderPage('export');
            exit;
        }
        $exportWhat = $_GET['what'];
        if (!array_intersect(array('all', 'public', 'private'), array($exportWhat))) {
            die('What are you trying to export ???');
        }
        header('Content-Type: text/html; charset=utf-8');
        header('Content-disposition: attachment; filename=bookmarks_' . $exportWhat . '_' . strval(date('Ymd_His')) . '.html');
        $currentdate = date('Y/m/d H:i:s');
        echo <<<HTML
<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
     It will be read and overwritten.
     DO NOT EDIT! -->
<!-- Shaarli {$exportWhat} bookmarks export on {$currentdate} -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
HTML;
        foreach ($LINKSDB as $link) {
            if ($exportWhat == 'all' || $exportWhat == 'private' && $link['private'] != 0 || $exportWhat == 'public' && $link['private'] == 0) {
                echo '<DT><A HREF="' . htmlspecialchars($link['url']) . '" ADD_DATE="' . linkdate2timestamp($link['linkdate']) . '" PRIVATE="' . $link['private'] . '"';
                if ($link['tags'] != '') {
                    echo ' TAGS="' . htmlspecialchars(str_replace(' ', ',', $link['tags'])) . '"';
                }
                echo '>' . htmlspecialchars($link['title']) . "</A>\n";
                if ($link['description'] != '') {
                    echo '<DD>' . htmlspecialchars($link['description']) . "\n";
                }
            }
        }
        exit;
    }
    // -------- User is uploading a file for import
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=upload')) {
        // If file is too big, some form field may be missing.
        if (!isset($_POST['token']) || !isset($_FILES) || isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size'] == 0) {
            $returnurl = empty($_SERVER['HTTP_REFERER']) ? '?' : $_SERVER['HTTP_REFERER'];
            echo '<script language="JavaScript">alert("The file you are trying to upload is probably bigger than what this webserver can accept (' . getMaxFileSize() . ' bytes). Please upload in smaller chunks.");document.location=\'' . htmlspecialchars($returnurl) . '\';</script>';
            exit;
        }
        if (!tokenOk($_POST['token'])) {
            die('Wrong token.');
        }
        importFile();
        exit;
    }
    // -------- Show upload/import dialog:
    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"], 'do=import')) {
        $PAGE = new pageBuilder();
        $PAGE->assign('linkcount', count($LINKSDB));
        $PAGE->assign('token', getToken());
        $PAGE->assign('maxfilesize', getMaxFileSize());
        $PAGE->renderPage('import');
        exit;
    }
    // -------- Otherwise, simply display search form and links:
    $PAGE = new pageBuilder();
    $PAGE->assign('linkcount', count($LINKSDB));
    buildLinkList($PAGE, $LINKSDB);
    // Compute list of links to display
    $PAGE->renderPage('linklist');
    exit;
}