X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=index.php;h=92eb443ba6310477ed04d831a62c72d03f2bcc3c;hb=3e395a6bc63cba16b0772a382f6cbac0ce4ab906;hp=c96d01367ab1aee7b2cf4c62a1029fd6b451d078;hpb=7d86f40bdb2135655b5b4fe8cbcc1ac102114f86;p=github%2Fshaarli%2FShaarli.git diff --git a/index.php b/index.php index c96d0136..92eb443b 100644 --- a/index.php +++ b/index.php @@ -62,6 +62,7 @@ require_once 'application/CachedPage.php'; require_once 'application/config/ConfigPlugin.php'; require_once 'application/FeedBuilder.php'; require_once 'application/FileUtils.php'; +require_once 'application/History.php'; require_once 'application/HttpUtils.php'; require_once 'application/Languages.php'; require_once 'application/LinkDB.php'; @@ -224,27 +225,6 @@ function setup_login_state($conf) } $userIsLoggedIn = setup_login_state($conf); -/** - * PubSubHubbub protocol support (if enabled) [UNTESTED] - * (Source: http://aldarone.fr/les-flux-rss-shaarli-et-pubsubhubbub/ ) - * - * @param ConfigManager $conf Configuration Manager instance. - */ -function pubsubhub($conf) -{ - $pshUrl = $conf->get('config.PUBSUBHUB_URL'); - if (!empty($pshUrl)) - { - include_once './publisher.php'; - $p = new Publisher($pshUrl); - $topic_url = array ( - index_url($_SERVER).'?do=atom', - index_url($_SERVER).'?do=rss' - ); - $p->publish_update($topic_url); - } -} - // ------------------------------------------------------------------------------------------ // Session management @@ -472,34 +452,6 @@ if (isset($_POST['login'])) } } -// ------------------------------------------------------------------------------------------ -// Misc utility functions: - -// Convert post_max_size/upload_max_filesize (e.g. '16M') parameters to bytes. -function return_bytes($val) -{ - $val = trim($val); $last=strtolower($val[strlen($val)-1]); - switch($last) - { - case 'g': $val *= 1024; - case 'm': $val *= 1024; - case 'k': $val *= 1024; - } - return $val; -} - -// Try to determine max file size for uploads (POST). -// Returns an integer (in bytes) -function getMaxFileSize() -{ - $size1 = return_bytes(ini_get('post_max_size')); - $size2 = return_bytes(ini_get('upload_max_filesize')); - // Return the smaller of two: - $maxsize = min($size1,$size2); - // FIXME: Then convert back to readable notations ? (e.g. 2M instead of 2000000) - return $maxsize; -} - // ------------------------------------------------------------------------------------------ // Token management for XSRF protection // Token should be used in any form which acts on data (create,update,delete,import...). @@ -695,9 +647,11 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) $dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000'); $data = array( + 'pagetitle' => $conf->get('general.title') .' - '. format_date($dayDate, false), 'linksToDisplay' => $linksToDisplay, 'cols' => $columns, 'day' => $dayDate->getTimestamp(), + 'dayDate' => $dayDate, 'previousday' => $previousday, 'nextday' => $nextday, ); @@ -732,7 +686,7 @@ function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager) { * @param PluginManager $pluginManager Plugin Manager instance, * @param LinkDB $LINKSDB */ -function renderPage($conf, $pluginManager, $LINKSDB) +function renderPage($conf, $pluginManager, $LINKSDB, $history) { $updater = new Updater( read_updates_file($conf->get('resource.updates')), @@ -753,7 +707,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) die($e->getMessage()); } - $PAGE = new PageBuilder($conf); + $PAGE = new PageBuilder($conf, $LINKSDB); $PAGE->assign('linkcount', count($LINKSDB)); $PAGE->assign('privateLinkcount', count_private($LINKSDB)); $PAGE->assign('plugin_errors', $pluginManager->getErrors()); @@ -836,7 +790,9 @@ function renderPage($conf, $pluginManager, $LINKSDB) // -------- Tag cloud if ($targetPage == Router::$PAGE_TAGCLOUD) { - $tags= $LINKSDB->allTags(); + $visibility = ! empty($_SESSION['privateonly']) ? 'private' : 'all'; + $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : []; + $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility); // We sort tags alphabetically, then choose a font size according to count. // First, find max value. @@ -845,17 +801,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) $maxcount = max($maxcount, $value); } - // Sort tags alphabetically: case insensitive, support locale if available. - uksort($tags, function($a, $b) { - // Collator is part of PHP intl. - if (class_exists('Collator')) { - $c = new Collator(setlocale(LC_COLLATE, 0)); - if (!intl_is_failure(intl_get_error_code())) { - return $c->compare($a, $b); - } - } - return strcasecmp($a, $b); - }); + alphabetical_sort($tags, true, true); $tagList = array(); foreach($tags as $key => $value) { @@ -870,6 +816,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) } $data = array( + 'search_tags' => implode(' ', $filteringTags), 'tags' => $tagList, ); $pluginManager->executeHooks('render_tagcloud', $data, array('loggedin' => isLoggedIn())); @@ -878,7 +825,32 @@ function renderPage($conf, $pluginManager, $LINKSDB) $PAGE->assign($key, $value); } - $PAGE->renderPage('tagcloud'); + $PAGE->renderPage('tag.cloud'); + exit; + } + + // -------- Tag cloud + if ($targetPage == Router::$PAGE_TAGLIST) + { + $visibility = ! empty($_SESSION['privateonly']) ? 'private' : 'all'; + $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : []; + $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility); + + if (! empty($_GET['sort']) && $_GET['sort'] === 'alpha') { + alphabetical_sort($tags, false, true); + } + + $data = [ + 'search_tags' => implode(' ', $filteringTags), + 'tags' => $tags, + ]; + $pluginManager->executeHooks('render_taglist', $data, ['loggedin' => isLoggedIn()]); + + foreach ($data as $key => $value) { + $PAGE->assign($key, $value); + } + + $PAGE->renderPage('tag.list'); exit; } @@ -1151,6 +1123,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) $conf->set('api.secret', escape($_POST['apiSecret'])); try { $conf->write(isLoggedIn()); + $history->updateSettings(); invalidateCaches($conf->get('resource.page_cache')); } catch(Exception $e) { @@ -1172,9 +1145,12 @@ function renderPage($conf, $pluginManager, $LINKSDB) $PAGE->assign('theme', $conf->get('resource.theme')); $PAGE->assign('theme_available', ThemeUtils::getThemes($conf->get('resource.raintpl_tpl'))); $PAGE->assign('redirector', $conf->get('redirector.url')); - list($timezone_form, $timezone_js) = generateTimeZoneForm($conf->get('general.timezone')); - $PAGE->assign('timezone_form', $timezone_form); - $PAGE->assign('timezone_js',$timezone_js); + list($continents, $cities) = generateTimeZoneData( + timezone_identifiers_list(), + $conf->get('general.timezone') + ); + $PAGE->assign('continents', $continents); + $PAGE->assign('cities', $cities); $PAGE->assign('private_links_default', $conf->get('privacy.default_private_links', false)); $PAGE->assign('session_protection_disabled', $conf->get('security.session_protection_disabled', false)); $PAGE->assign('enable_rss_permalinks', $conf->get('feed.rss_permalinks', false)); @@ -1191,7 +1167,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) if ($targetPage == Router::$PAGE_CHANGETAG) { if (empty($_POST['fromtag']) || (empty($_POST['totag']) && isset($_POST['renametag']))) { - $PAGE->assign('tags', $LINKSDB->allTags()); + $PAGE->assign('fromtag', ! empty($_GET['fromtag']) ? escape($_GET['fromtag']) : ''); $PAGE->renderPage('changetag'); exit; } @@ -1211,6 +1187,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) unset($tags[array_search($needle,$tags)]); // Remove tag. $value['tags']=trim(implode(' ',$tags)); $LINKSDB[$key]=$value; + $history->updateLink($LINKSDB[$key]); } $LINKSDB->save($conf->get('resource.page_cache')); echo ''; @@ -1228,6 +1205,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) $tags[array_search($needle, $tags)] = trim($_POST['totag']); $value['tags'] = implode(' ', array_unique($tags)); $LINKSDB[$key] = $value; + $history->updateLink($LINKSDB[$key]); } $LINKSDB->save($conf->get('resource.page_cache')); // Save to disk. echo ''; @@ -1262,11 +1240,13 @@ function renderPage($conf, $pluginManager, $LINKSDB) $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate); $updated = new DateTime(); $shortUrl = $LINKSDB[$id]['shorturl']; + $new = false; } else { // New link $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate); $updated = null; $shortUrl = link_small_hash($created, $id); + $new = true; } // Remove multiple spaces. @@ -1305,6 +1285,11 @@ function renderPage($conf, $pluginManager, $LINKSDB) $LINKSDB[$id] = $link; $LINKSDB->save($conf->get('resource.page_cache')); + if ($new) { + $history->addLink($link); + } else { + $history->updateLink($link); + } // If we are called from the bookmarklet, we must close the popup: if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { @@ -1342,19 +1327,23 @@ function renderPage($conf, $pluginManager, $LINKSDB) // -------- User clicked the "Delete" button when editing a link: Delete link from database. if ($targetPage == Router::$PAGE_DELETELINK) { - // We do not need to ask for confirmation: - // - confirmation is handled by JavaScript - // - we are protected from XSRF by the token. - if (! tokenOk($_GET['token'])) { die('Wrong token.'); } - $id = intval(escape($_GET['lf_linkdate'])); - $link = $LINKSDB[$id]; - $pluginManager->executeHooks('delete_link', $link); - unset($LINKSDB[$id]); + if (strpos($_GET['lf_linkdate'], ' ') !== false) { + $ids = array_values(array_filter(preg_split('/\s+/', escape($_GET['lf_linkdate'])))); + } else { + $ids = [$_GET['lf_linkdate']]; + } + foreach ($ids as $id) { + $id = (int) escape($id); + $link = $LINKSDB[$id]; + $pluginManager->executeHooks('delete_link', $link); + unset($LINKSDB[$id]); + } $LINKSDB->save($conf->get('resource.page_cache')); // save to disk + $history->deleteLink($link); // If we are called from the bookmarklet, we must close the popup: if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo ''; exit; } @@ -1384,7 +1373,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) 'link' => $link, 'link_is_new' => false, 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''), - 'tags' => $LINKSDB->allTags(), + 'tags' => $LINKSDB->linksCountPerTag(), ); $pluginManager->executeHooks('render_editlink', $data); @@ -1453,7 +1442,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) 'link_is_new' => $link_is_new, 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''), 'source' => (isset($_GET['source']) ? $_GET['source'] : ''), - 'tags' => $LINKSDB->allTags(), + 'tags' => $LINKSDB->linksCountPerTag(), 'default_private_links' => $conf->get('privacy.default_private_links', false), ); $pluginManager->executeHooks('render_editlink', $data); @@ -1515,7 +1504,22 @@ function renderPage($conf, $pluginManager, $LINKSDB) if (! isset($_POST['token']) || ! isset($_FILES['filetoupload'])) { // Show import dialog - $PAGE->assign('maxfilesize', getMaxFileSize()); + $PAGE->assign( + 'maxfilesize', + get_max_upload_size( + ini_get('post_max_size'), + ini_get('upload_max_filesize'), + false + ) + ); + $PAGE->assign( + 'maxfilesizeHuman', + get_max_upload_size( + ini_get('post_max_size'), + ini_get('upload_max_filesize'), + true + ) + ); $PAGE->renderPage('import'); exit; } @@ -1525,7 +1529,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) // The file is too big or some form field may be missing. echo ''; exit; @@ -1537,7 +1541,8 @@ function renderPage($conf, $pluginManager, $LINKSDB) $_POST, $_FILES, $LINKSDB, - $conf + $conf, + $history ); echo ''; @@ -1577,6 +1582,7 @@ function renderPage($conf, $pluginManager, $LINKSDB) $conf->set('general.enabled_plugins', save_plugin_config($_POST)); } $conf->write(isLoggedIn()); + $history->updateSettings(); } catch (Exception $e) { error_log( @@ -1592,6 +1598,13 @@ function renderPage($conf, $pluginManager, $LINKSDB) exit; } + // Get a fresh token + if ($targetPage == Router::$GET_TOKEN) { + header('Content-Type:text/plain'); + echo getToken($conf); + exit; + } + // -------- Otherwise, simply display search form and links: showLinkList($PAGE, $LINKSDB, $conf, $pluginManager); exit; @@ -1706,7 +1719,6 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) 'visibility' => ! empty($_SESSION['privateonly']) ? 'private' : '', 'redirector' => $conf->get('redirector.url'), // Optional redirector URL. 'links' => $linkDisp, - 'tags' => $LINKSDB->allTags(), ); // If there is only a single link, we change on-the-fly the title of the page. @@ -1992,16 +2004,10 @@ function install($conf) exit; } - // Display config form: - list($timezone_form, $timezone_js) = generateTimeZoneForm(); - $timezone_html = ''; - if ($timezone_form != '') { - $timezone_html = 'Timezone:'.$timezone_form.''; - } - $PAGE = new PageBuilder($conf); - $PAGE->assign('timezone_html',$timezone_html); - $PAGE->assign('timezone_js',$timezone_js); + list($continents, $cities) = generateTimeZoneData(timezone_identifiers_list(), date_default_timezone_get()); + $PAGE->assign('continents', $continents); + $PAGE->assign('cities', $cities); $PAGE->renderPage('install'); exit; } @@ -2245,16 +2251,27 @@ $linkDb = new LinkDB( $conf->get('redirector.encode_url') ); +try { + $history = new History($conf->get('resource.history')); +} catch(Exception $e) { + die($e->getMessage()); +} + $container = new \Slim\Container(); $container['conf'] = $conf; $container['plugins'] = $pluginManager; +$container['history'] = $history; $app = new \Slim\App($container); // REST API routes $app->group('/api/v1', function() { - $this->get('/info', '\Shaarli\Api\Controllers\Info:getInfo'); - $this->get('/links', '\Shaarli\Api\Controllers\Links:getLinks'); - $this->get('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:getLink'); + $this->get('/info', '\Shaarli\Api\Controllers\Info:getInfo')->setName('getInfo'); + $this->get('/links', '\Shaarli\Api\Controllers\Links:getLinks')->setName('getLinks'); + $this->get('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:getLink')->setName('getLink'); + $this->post('/links', '\Shaarli\Api\Controllers\Links:postLink')->setName('postLink'); + $this->put('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:putLink')->setName('putLink'); + $this->delete('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:deleteLink')->setName('deleteLink'); + $this->get('/history', '\Shaarli\Api\Controllers\History:getHistory')->setName('getHistory'); })->add('\Shaarli\Api\ApiMiddleware'); $response = $app->run(true); @@ -2263,7 +2280,7 @@ $response = $app->run(true); if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v1') === false) { // We use UTF-8 for proper international characters handling. header('Content-Type: text/html; charset=utf-8'); - renderPage($conf, $pluginManager, $linkDb); + renderPage($conf, $pluginManager, $linkDb, $history); } else { $app->respond($response); }