From: ArthurHoaro Date: Sat, 6 May 2017 15:12:06 +0000 (+0200) Subject: Merge pull request #764 from ArthurHoaro/feature/history X-Git-Tag: v0.9.0~11 X-Git-Url: https://git.immae.eu/?a=commitdiff_plain;h=f9ff7f1b69f19b42569ffa67280807ba56f5d48a;hp=-c;p=github%2Fshaarli%2FShaarli.git Merge pull request #764 from ArthurHoaro/feature/history History mechanism --- f9ff7f1b69f19b42569ffa67280807ba56f5d48a diff --combined application/LinkDB.php index 1e4d7ce8,2fb15040..0d3c85bd --- a/application/LinkDB.php +++ b/application/LinkDB.php @@@ -50,12 -50,6 +50,6 @@@ class LinkDB implements Iterator, Count // Link date storage format const LINK_DATE_FORMAT = 'Ymd_His'; - // Datastore PHP prefix - protected static $phpPrefix = ''; - // List of links (associative array) // - key: link date (e.g. "20110823_124546"), // - value: associative array (keys: title, description...) @@@ -144,10 -138,10 +138,10 @@@ if (!isset($value['id']) || empty($value['url'])) { die('Internal Error: A link should always have an id and URL.'); } - if ((! empty($offset) && ! is_int($offset)) || ! is_int($value['id'])) { + if (($offset !== null && ! is_int($offset)) || ! is_int($value['id'])) { die('You must specify an integer as a key.'); } - if (! empty($offset) && $offset !== $value['id']) { + if ($offset !== null && $offset !== $value['id']) { die('Array offset and link ID must be equal.'); } @@@ -295,16 -289,7 +289,7 @@@ You use the community supported versio return; } - // Read data - // Note that gzinflate is faster than gzuncompress. - // See: http://www.php.net/manual/en/function.gzdeflate.php#96439 - $this->links = array(); - - if (file_exists($this->datastore)) { - $this->links = unserialize(gzinflate(base64_decode( - substr(file_get_contents($this->datastore), - strlen(self::$phpPrefix), -strlen(self::$phpSuffix))))); - } + $this->links = FileUtils::readFlatDB($this->datastore, []); $toremove = array(); foreach ($this->links as $key => &$link) { @@@ -361,19 -346,7 +346,7 @@@ */ private function write() { - if (is_file($this->datastore) && !is_writeable($this->datastore)) { - // The datastore exists but is not writeable - throw new IOException($this->datastore); - } else if (!is_file($this->datastore) && !is_writeable(dirname($this->datastore))) { - // The datastore does not exist and its parent directory is not writeable - throw new IOException(dirname($this->datastore)); - } - - file_put_contents( - $this->datastore, - self::$phpPrefix.base64_encode(gzdeflate(serialize($this->links))).self::$phpSuffix - ); - + FileUtils::writeFlatDB($this->datastore, $this->links); } /** diff --combined index.php index e392e501,7f357c69..76aa1ae0 --- a/index.php +++ b/index.php @@@ -1,6 -1,8 +1,6 @@@ /shaarli/ define('WEB_PATH', substr($_SERVER['REQUEST_URI'], 0, 1+strrpos($_SERVER['REQUEST_URI'], '/', 0))); @@@ -62,6 -65,7 +62,7 @@@ require_once 'application/CachedPage.ph 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'; @@@ -87,8 -91,6 +88,8 @@@ try exit; } +define('shaarli_version', ApplicationUtils::getVersion(__DIR__ .'/'. ApplicationUtils::$VERSION_FILE)); + // Force cookie path (but do not change lifetime) $cookie = session_get_cookie_params(); $cookiedir = ''; @@@ -432,7 -434,7 +433,7 @@@ if (isset($_POST['login']) // Optional redirect after login: if (isset($_GET['post'])) { $uri = '?post='. urlencode($_GET['post']); - foreach (array('description', 'source', 'title') as $param) { + foreach (array('description', 'source', 'title', 'tags') as $param) { if (!empty($_GET[$param])) { $uri .= '&'.$param.'='.urlencode($_GET[$param]); } @@@ -461,7 -463,7 +462,7 @@@ $redir = '&username='. $_POST['login']; if (isset($_GET['post'])) { $redir .= '&post=' . urlencode($_GET['post']); - foreach (array('description', 'source', 'title') as $param) { + foreach (array('description', 'source', 'title', 'tags') as $param) { if (!empty($_GET[$param])) { $redir .= '&' . $param . '=' . urlencode($_GET[$param]); } @@@ -472,6 -474,34 +473,6 @@@ } } -// ------------------------------------------------------------------------------------------ -// 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...). @@@ -667,11 -697,9 +668,11 @@@ function showDaily($pageBuilder, $LINKS $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, ); @@@ -727,6 -755,12 +728,12 @@@ function renderPage($conf, $pluginManag die($e->getMessage()); } + try { + $history = new History($conf->get('resource.history')); + } catch(Exception $e) { + die($e->getMessage()); + } + $PAGE = new PageBuilder($conf); $PAGE->assign('linkcount', count($LINKSDB)); $PAGE->assign('privateLinkcount', count_private($LINKSDB)); @@@ -1018,13 -1052,7 +1025,13 @@@ // 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['description'])?'&description='.urlencode($_GET['description']):'').(!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'')); // Redirect to login page, then back to post link. + header( // Redirect to login page, then back to post link. + 'Location: ?do=login&post='.urlencode($_GET['post']). + (!empty($_GET['title'])?'&title='.urlencode($_GET['title']):''). + (!empty($_GET['description'])?'&description='.urlencode($_GET['description']):''). + (!empty($_GET['tags'])?'&tags='.urlencode($_GET['tags']):''). + (!empty($_GET['source'])?'&source='.urlencode($_GET['source']):'') + ); exit; } @@@ -1121,10 -1149,11 +1128,11 @@@ $conf->set('feed.rss_permalinks', !empty($_POST['enableRssPermalinks'])); $conf->set('updates.check_updates', !empty($_POST['updateCheck'])); $conf->set('privacy.hide_public_links', !empty($_POST['hidePublicLinks'])); - $conf->set('api.enabled', !empty($_POST['apiEnabled'])); + $conf->set('api.enabled', !empty($_POST['enableApi'])); $conf->set('api.secret', escape($_POST['apiSecret'])); try { $conf->write(isLoggedIn()); + $history->updateSettings(); invalidateCaches($conf->get('resource.page_cache')); } catch(Exception $e) { @@@ -1146,12 -1175,9 +1154,12 @@@ $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)); @@@ -1159,6 -1185,7 +1167,7 @@@ $PAGE->assign('hide_public_links', $conf->get('privacy.hide_public_links', false)); $PAGE->assign('api_enabled', $conf->get('api.enabled', true)); $PAGE->assign('api_secret', $conf->get('api.secret')); + $history->updateSettings(); $PAGE->renderPage('configure'); exit; } @@@ -1188,6 -1215,7 +1197,7 @@@ 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 ''; @@@ -1205,6 -1233,7 +1215,7 @@@ $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 ''; @@@ -1228,7 -1257,7 +1239,7 @@@ } // lf_id should only be present if the link exists. - $id = !empty($_POST['lf_id']) ? intval(escape($_POST['lf_id'])) : $LINKSDB->getNextId(); + $id = isset($_POST['lf_id']) ? intval(escape($_POST['lf_id'])) : $LINKSDB->getNextId(); // Linkdate is kept here to: // - use the same permalink for notes as they're displayed when creating them // - let users hack creation date of their posts @@@ -1239,11 -1268,13 +1250,13 @@@ $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. @@@ -1282,6 -1313,11 +1295,11 @@@ $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')) { @@@ -1301,13 -1337,9 +1319,13 @@@ // -------- User clicked the "Cancel" button when editing a link. if (isset($_POST['cancel_edit'])) { + $id = isset($_POST['lf_id']) ? (int) escape($_POST['lf_id']) : false; + if (! isset($LINKSDB[$id])) { + header('Location: ?'); + } // If we are called from the bookmarklet, we must close the popup: if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo ''; exit; } - $link = $LINKSDB[(int) escape($_POST['lf_id'])]; + $link = $LINKSDB[$id]; $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' ); // Scroll to the link which has been edited. $returnurl .= '#'. $link['shorturl']; @@@ -1332,6 -1364,7 +1350,7 @@@ $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; } @@@ -1492,22 -1525,7 +1511,22 @@@ 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; } @@@ -1517,7 -1535,7 +1536,7 @@@ // The file is too big or some form field may be missing. echo ''; exit; @@@ -1529,7 -1547,8 +1548,8 @@@ $_POST, $_FILES, $LINKSDB, - $conf + $conf, + $history ); echo ''; @@@ -1558,6 -1577,7 +1578,7 @@@ // Plugin administration form action if ($targetPage == Router::$PAGE_SAVE_PLUGINSADMIN) { + $history->updateSettings(); try { if (isset($_POST['parameters_form'])) { unset($_POST['parameters_form']); @@@ -1972,10 -1992,16 +1993,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; } @@@ -2226,10 -2252,9 +2247,10 @@@ $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'); })->add('\Shaarli\Api\ApiMiddleware'); $response = $app->run(true);