![Shaarli logo](doc/images/doc-logo.png)
-The personal, minimalist, super-fast, no-database delicious clone.
+The personal, minimalist, super-fast, database free, bookmarking service.
_Do you want to share the links you discover?_
_Shaarli is a minimalist delicious clone that you can install on your own server._
function index_url($server)
{
$scriptname = $server['SCRIPT_NAME'];
- if (endswith($scriptname, 'index.php')) {
+ if (endsWith($scriptname, 'index.php')) {
$scriptname = substr($scriptname, 0, -9);
}
return server_url($server) . $scriptname;
* - timestamp link addition date, using the Unix epoch format
* - taglist comma-separated tag list
*
- * @param LinkDB $linkDb The link datastore
- * @param string $selection Which links to export: (all|private|public)
+ * @param LinkDB $linkDb Link datastore
+ * @param string $selection Which links to export: (all|private|public)
+ * @param bool $prependNoteUrl Prepend note permalinks with the server's URL
+ * @param string $indexUrl Absolute URL of the Shaarli index page
*
* @throws Exception Invalid export selection
*
* @return array The links to be exported, with additional fields
*/
- public static function filterAndFormat($linkDb, $selection)
+ public static function filterAndFormat($linkDb, $selection, $prependNoteUrl, $indexUrl)
{
// see tpl/export.html for possible values
- if (! in_array($selection, array('all','public','private'))) {
+ if (! in_array($selection, array('all', 'public', 'private'))) {
throw new Exception('Invalid export selection: "'.$selection.'"');
}
$date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
$link['timestamp'] = $date->getTimestamp();
$link['taglist'] = str_replace(' ', ',', $link['tags']);
+
+ if (startsWith($link['url'], '?') && $prependNoteUrl) {
+ $link['url'] = $indexUrl . $link['url'];
+ }
+
$bookmarkLinks[] = $link;
}
return self::$PAGE_LINKLIST;
}
- if (startswith($query, 'do='. self::$PAGE_LOGIN) && $loggedIn === false) {
+ if (startsWith($query, 'do='. self::$PAGE_LOGIN) && $loggedIn === false) {
return self::$PAGE_LOGIN;
}
- if (startswith($query, 'do='. self::$PAGE_PICWALL)) {
+ if (startsWith($query, 'do='. self::$PAGE_PICWALL)) {
return self::$PAGE_PICWALL;
}
- if (startswith($query, 'do='. self::$PAGE_TAGCLOUD)) {
+ if (startsWith($query, 'do='. self::$PAGE_TAGCLOUD)) {
return self::$PAGE_TAGCLOUD;
}
- if (startswith($query, 'do='. self::$PAGE_OPENSEARCH)) {
+ if (startsWith($query, 'do='. self::$PAGE_OPENSEARCH)) {
return self::$PAGE_OPENSEARCH;
}
return self::$PAGE_LINKLIST;
}
- if (startswith($query, 'do='. self::$PAGE_TOOLS)) {
+ if (startsWith($query, 'do='. self::$PAGE_TOOLS)) {
return self::$PAGE_TOOLS;
}
- if (startswith($query, 'do='. self::$PAGE_CHANGEPASSWORD)) {
+ if (startsWith($query, 'do='. self::$PAGE_CHANGEPASSWORD)) {
return self::$PAGE_CHANGEPASSWORD;
}
- if (startswith($query, 'do='. self::$PAGE_CONFIGURE)) {
+ if (startsWith($query, 'do='. self::$PAGE_CONFIGURE)) {
return self::$PAGE_CONFIGURE;
}
- if (startswith($query, 'do='. self::$PAGE_CHANGETAG)) {
+ if (startsWith($query, 'do='. self::$PAGE_CHANGETAG)) {
return self::$PAGE_CHANGETAG;
}
- if (startswith($query, 'do='. self::$PAGE_ADDLINK)) {
+ if (startsWith($query, 'do='. self::$PAGE_ADDLINK)) {
return self::$PAGE_ADDLINK;
}
return self::$PAGE_EDITLINK;
}
- if (startswith($query, 'do='. self::$PAGE_EXPORT)) {
+ if (startsWith($query, 'do='. self::$PAGE_EXPORT)) {
return self::$PAGE_EXPORT;
}
- if (startswith($query, 'do='. self::$PAGE_IMPORT)) {
+ if (startsWith($query, 'do='. self::$PAGE_IMPORT)) {
return self::$PAGE_IMPORT;
}
- if (startswith($query, 'do='. self::$PAGE_PLUGINSADMIN)) {
+ if (startsWith($query, 'do='. self::$PAGE_PLUGINSADMIN)) {
return self::$PAGE_PLUGINSADMIN;
}
- if (startswith($query, 'do='. self::$PAGE_SAVE_PLUGINSADMIN)) {
+ if (startsWith($query, 'do='. self::$PAGE_SAVE_PLUGINSADMIN)) {
return self::$PAGE_SAVE_PLUGINSADMIN;
}
/**
* Tells if a string start with a substring
+ *
+ * @param string $haystack Given string.
+ * @param string $needle String to search at the beginning of $haystack.
+ * @param bool $case Case sensitive.
+ *
+ * @return bool True if $haystack starts with $needle.
*/
-function startsWith($haystack, $needle, $case=true)
+function startsWith($haystack, $needle, $case = true)
{
if ($case) {
return (strcmp(substr($haystack, 0, strlen($needle)), $needle) === 0);
/**
* Tells if a string ends with a substring
+ *
+ * @param string $haystack Given string.
+ * @param string $needle String to search at the end of $haystack.
+ * @param bool $case Case sensitive.
+ *
+ * @return bool True if $haystack ends with $needle.
*/
-function endsWith($haystack, $needle, $case=true)
+function endsWith($haystack, $needle, $case = true)
{
if ($case) {
return (strcmp(substr($haystack, strlen($haystack) - strlen($needle)), $needle) === 0);
// This RSS feed cannot be filtered.
function showDailyRSS() {
// Cache system
- $query = $_SERVER["QUERY_STRING"];
+ $query = $_SERVER['QUERY_STRING'];
$cache = new CachedPage(
$GLOBALS['config']['PAGECACHE'],
page_url($_SERVER),
exit;
}
// -------- User wants to logout.
- if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=logout'))
+ if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=logout'))
{
invalidateCaches($GLOBALS['config']['PAGECACHE']);
logout();
exit;
}
- // Same case as above except that user tried to access ?do=addlink without being logged in
- // Note: passing empty parameters makes Shaarli generate default URLs and descriptions.
- if (isset($_GET['do']) && $_GET['do'] === 'addlink') {
- header('Location: ?do=login&post=');
- exit;
- }
showLinkList($PAGE, $LINKSDB);
if (isset($_GET['edit_link'])) {
header('Location: ?do=login&edit_link='. escape($_GET['edit_link']));
exit;
}
- // -------- Export as Netscape Bookmarks HTML file.
if ($targetPage == Router::$PAGE_EXPORT) {
+ // Export links as a Netscape Bookmarks file
+
if (empty($_GET['selection'])) {
$PAGE->assign('linkcount',count($LINKSDB));
$PAGE->renderPage('export');
// export as bookmarks_(all|private|public)_YYYYmmdd_HHMMSS.html
$selection = $_GET['selection'];
+ if (isset($_GET['prepend_note_url'])) {
+ $prependNoteUrl = $_GET['prepend_note_url'];
+ } else {
+ $prependNoteUrl = false;
+ }
+
try {
$PAGE->assign(
'links',
- NetscapeBookmarkUtils::filterAndFormat($LINKSDB, $selection)
+ NetscapeBookmarkUtils::filterAndFormat(
+ $LINKSDB,
+ $selection,
+ $prependNoteUrl,
+ index_url($_SERVER)
+ )
);
} catch (Exception $exc) {
header('Content-Type: text/plain; charset=utf-8');
}
// -------- User is uploading a file for import
- if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=upload'))
+ 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))
{
$link = array('linkdate'=>'','title'=>'','url'=>'','description'=>'','tags'=>'','private'=>0);
$d = explode('<DD>',$html);
- if (startswith($d[0],'<A '))
+ if (startsWith($d[0], '<A '))
{
$link['description'] = (isset($d[1]) ? html_entity_decode(trim($d[1]),ENT_QUOTES,'UTF-8') : ''); // Get description (optional)
preg_match('!<A .*?>(.*?)</A>!i',$d[0],$matches); $link['title'] = (isset($matches[1]) ? trim($matches[1]) : ''); // Get title
// Is this a link to an image, or to a flickr page ?
$imageurl='';
- if (endswith(parse_url($url,PHP_URL_PATH),'.jpg'))
+ if (endsWith(parse_url($url, PHP_URL_PATH), '.jpg'))
{ // This is a direct link to an image. e.g. http://farm1.staticflickr.com/5/5921913_ac83ed27bd_o.jpg
preg_match('!(http://farm\d+\.staticflickr\.com/\d+/\d+_\w+_)\w.jpg!',$url,$matches);
if (!empty($matches[1])) $imageurl=$matches[1].'m.jpg';
return true;
}
-if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=genthumbnail')) { genThumbnail(); exit; } // Thumbnail generation/cache does not need the link database.
-if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=dailyrss')) { showDailyRSS(); exit; }
+if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=genthumbnail')) { genThumbnail(); exit; } // Thumbnail generation/cache does not need the link database.
+if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=dailyrss')) { showDailyRSS(); exit; }
if (!isset($_SESSION['LINKS_PER_PAGE'])) $_SESSION['LINKS_PER_PAGE']=$GLOBALS['config']['LINKS_PER_PAGE'];
renderPage();
?>
hyphens: none;
}
+.markdown :not(pre) code {
+ background-color: #eee;
+ padding: 1px 3px;
+ border-radius: 1px;
+ box-shadow: 0 -1px 0 #e5e5e5,0 0 1px rgba(0,0,0,0.12),0 1px 1px rgba(0,0,0,0.24);
+}
+
.md_help {
color: white;
}
*/
public function testFilterAndFormatInvalid()
{
- NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'derp');
+ NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'derp', false, '');
}
/**
*/
public function testFilterAndFormatAll()
{
- $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'all');
+ $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'all', false, '');
$this->assertEquals(self::$refDb->countLinks(), sizeof($links));
foreach ($links as $link) {
$date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
*/
public function testFilterAndFormatPrivate()
{
- $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'private');
+ $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'private', false, '');
$this->assertEquals(self::$refDb->countPrivateLinks(), sizeof($links));
foreach ($links as $link) {
$date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
*/
public function testFilterAndFormatPublic()
{
- $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'public');
+ $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'public', false, '');
$this->assertEquals(self::$refDb->countPublicLinks(), sizeof($links));
foreach ($links as $link) {
$date = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $link['linkdate']);
);
}
}
+
+ /**
+ * Do not prepend notes with the Shaarli index's URL
+ */
+ public function testFilterAndFormatDoNotPrependNoteUrl()
+ {
+ $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'public', false, '');
+ $this->assertEquals(
+ '?WDWyig',
+ $links[0]['url']
+ );
+ }
+
+ /**
+ * Prepend notes with the Shaarli index's URL
+ */
+ public function testFilterAndFormatPrependNoteUrl()
+ {
+ $indexUrl = 'http://localhost:7469/shaarli/';
+ $links = NetscapeBookmarkUtils::filterAndFormat(
+ self::$linkDb,
+ 'public',
+ true,
+ $indexUrl
+ );
+ $this->assertEquals(
+ $indexUrl . '?WDWyig',
+ $links[0]['url']
+ );
+ }
}
<div id="pageheader">
{include="page.header"}
<div id="toolsdiv">
- <a href="?do=export&selection=all">
- <b>Export all</b><span>: Export all links</span>
- </a><br>
- <a href="?do=export&selection=public">
- <b>Export public</b><span>: Only export public links</span>
- </a><br>
- <a href="?do=export&selection=private">
- <b>Export private</b><span>: Only export private links</span>
- </a>
+ <form method="GET">
+ <input type="hidden" name="do" value="export">
+ Selection:<br>
+ <input type="radio" name="selection" value="all" checked="true"> All<br>
+ <input type="radio" name="selection" value="private"> Private<br>
+ <input type="radio" name="selection" value="public"> Public<br>
+ <br>
+ <input type="checkbox" name="prepend_note_url" id="prepend_note_url">
+ <label for="prepend_note_url">
+ Prepend note permalinks with this Shaarli instance's URL
+ <em>(useful to import bookmarks in a web browser)</em>
+ </label>
+ <br><br>
+ <input class="bigbutton" type="submit" value="Export">
+ </form>
<div class="clear"></div>
</div>
</div>
<div id="footer">
- <b><a href="https://github.com/shaarli/Shaarli">Shaarli</a></b> - The personal, minimalist, super-fast, no-database delicious clone by the <a href="https://github.com/shaarli/Shaarli">Shaarli</a> community - <a href="doc/Home.html">Help/documentation</a>
+ <strong><a href="https://github.com/shaarli/Shaarli">Shaarli</a></strong>
+ - The personal, minimalist, super-fast, database free, bookmarking service by the Shaarli community
+ - <a href="doc/Home.html" rel="nofollow">Help/documentation</a>
{loop="$plugins_footer.text"}
{$value}
{/loop}