]> git.immae.eu Git - github/shaarli/Shaarli.git/blobdiff - index.php
Bump version to v0.6.0
[github/shaarli/Shaarli.git] / index.php
old mode 100755 (executable)
new mode 100644 (file)
index c1ddf4b..872acf0
--- a/index.php
+++ b/index.php
@@ -1,10 +1,20 @@
 <?php
-// Shaarli 0.5.3 - Shaare your links...
-// The personal, minimalist, super-fast, no-database Delicious clone. By sebsauvage.net
-// http://sebsauvage.net/wiki/doku.php?id=php:shaarli
-// Licence: http://www.opensource.org/licenses/zlib-license.php
-// Requires: PHP 5.3.x
-// -----------------------------------------------------------------------------------------------
+/**
+ * Shaarli v0.6.0 - Shaare your links...
+ *
+ * The personal, minimalist, super-fast, no-database Delicious clone.
+ *
+ * Friendly fork by the Shaarli community:
+ *  - https://github.com/shaarli/Shaarli
+ *
+ * Original project by sebsauvage.net:
+ *  - http://sebsauvage.net/wiki/doku.php?id=php:shaarli
+ *  - https://github.com/sebsauvage/Shaarli
+ *
+ * Licence: http://www.opensource.org/licenses/zlib-license.php
+ *
+ * Requires: PHP 5.3.x
+ */
 
 // Set 'UTC' as the default timezone if it is not defined in php.ini
 // See http://php.net/manual/en/datetime.configuration.php#ini.date.timezone
@@ -12,46 +22,120 @@ if (date_default_timezone_get() == '') {
     date_default_timezone_set('UTC');
 }
 
-// -----------------------------------------------------------------------------------------------
-// Hardcoded parameter (These parameters can be overwritten by editing the file /data/config.php)
-// You should not touch any code below (or at your own risks!)
-$GLOBALS['config']['DATADIR'] = 'data'; // Data subdirectory
-$GLOBALS['config']['CONFIG_FILE'] = $GLOBALS['config']['DATADIR'].'/config.php'; // Configuration file (user login/password)
-$GLOBALS['config']['DATASTORE'] = $GLOBALS['config']['DATADIR'].'/datastore.php'; // Data storage file.
-$GLOBALS['config']['LINKS_PER_PAGE'] = 20; // Default links per page.
-$GLOBALS['config']['IPBANS_FILENAME'] = $GLOBALS['config']['DATADIR'].'/ipbans.php'; // File storage for failures and bans.
-$GLOBALS['config']['BAN_AFTER'] = 4;        // Ban IP after this many failures.
-$GLOBALS['config']['BAN_DURATION'] = 1800;  // Ban duration for IP address after login failures (in seconds) (1800 sec. = 30 minutes)
-$GLOBALS['config']['OPEN_SHAARLI'] = false; // If true, anyone can add/edit/delete links without having to login
-$GLOBALS['config']['HIDE_TIMESTAMPS'] = false; // If true, the moment when links were saved are not shown to users that are not logged in.
-$GLOBALS['config']['SHOW_ATOM'] = false; // If true, an extra "ATOM feed" button will be displayed in the toolbar
-$GLOBALS['config']['ENABLE_THUMBNAILS'] = true; // Enable thumbnails in links.
-$GLOBALS['config']['CACHEDIR'] = 'cache'; // Cache directory for thumbnails for SLOW services (like flickr)
-$GLOBALS['config']['PAGECACHE'] = 'pagecache'; // Page cache directory.
-$GLOBALS['config']['ENABLE_LOCALCACHE'] = true; // Enable Shaarli to store thumbnail in a local cache. Disable to reduce web space usage.
-$GLOBALS['config']['PUBSUBHUB_URL'] = ''; // PubSubHubbub support. Put an empty string to disable, or put your hub url here to enable.
-$GLOBALS['config']['RAINTPL_TMP'] = 'tmp/' ; // Raintpl cache directory  (keep the trailing slash!)
-$GLOBALS['config']['RAINTPL_TPL'] = 'tpl/' ; // Raintpl template directory (keep the trailing slash!)
-$GLOBALS['config']['UPDATECHECK_FILENAME'] = $GLOBALS['config']['DATADIR'].'/lastupdatecheck.txt'; // For updates check of Shaarli.
-$GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400 ; // Updates check frequency for Shaarli. 86400 seconds=24 hours
-                                          // Note: You must have publisher.php in the same directory as Shaarli index.php
-$GLOBALS['config']['ARCHIVE_ORG'] = false; // For each link, add a link to an archived version on archive.org
-$GLOBALS['config']['ENABLE_RSS_PERMALINKS'] = true;  // Enable RSS permalinks by default. This corresponds to the default behavior of shaarli before this was added as an option.
+/* -----------------------------------------------------------------------------
+ * Hardcoded parameters
+ * You should not touch any code below (or at your own risks!)
+ * (These parameters can be overwritten by editing the file /data/config.php)
+ * -----------------------------------------------------------------------------
+ */
+
+/*
+ * Shaarli directories & configuration files
+ */
+// Data subdirectory
+$GLOBALS['config']['DATADIR'] = 'data';
+
+// Main configuration file
+$GLOBALS['config']['CONFIG_FILE'] = $GLOBALS['config']['DATADIR'].'/config.php';
+
+// Link datastore
+$GLOBALS['config']['DATASTORE'] = $GLOBALS['config']['DATADIR'].'/datastore.php';
+
+// Banned IPs
+$GLOBALS['config']['IPBANS_FILENAME'] = $GLOBALS['config']['DATADIR'].'/ipbans.php';
+
+// For updates check of Shaarli
+$GLOBALS['config']['UPDATECHECK_FILENAME'] = $GLOBALS['config']['DATADIR'].'/lastupdatecheck.txt';
+
+// RainTPL cache directory (keep the trailing slash!)
+$GLOBALS['config']['RAINTPL_TMP'] = 'tmp/';
+// Raintpl template directory (keep the trailing slash!)
+$GLOBALS['config']['RAINTPL_TPL'] = 'tpl/';
+
+// Thuumbnail cache directory
+$GLOBALS['config']['CACHEDIR'] = 'cache';
+
+// Atom & RSS feed cache directory
+$GLOBALS['config']['PAGECACHE'] = 'pagecache';
+
+
+/*
+ * Global configuration
+ */
+// Ban IP after this many failures
+$GLOBALS['config']['BAN_AFTER'] = 4;
+// Ban duration for IP address after login failures (in seconds)
+$GLOBALS['config']['BAN_DURATION'] = 1800;
+
+// Feed options
+// Enable RSS permalinks by default.
+// This corresponds to the default behavior of shaarli before this was added as an option.
+$GLOBALS['config']['ENABLE_RSS_PERMALINKS'] = true;
+// If true, an extra "ATOM feed" button will be displayed in the toolbar
+$GLOBALS['config']['SHOW_ATOM'] = false;
+
+// Link display options
 $GLOBALS['config']['HIDE_PUBLIC_LINKS'] = false;
-// -----------------------------------------------------------------------------------------------
-define('shaarli_version','0.5.3');
+$GLOBALS['config']['HIDE_TIMESTAMPS'] = false;
+$GLOBALS['config']['LINKS_PER_PAGE'] = 20;
+
+// Open Shaarli (true): anyone can add/edit/delete links without having to login
+$GLOBALS['config']['OPEN_SHAARLI'] = false;
+
+// Thumbnails
+// Display thumbnails in links
+$GLOBALS['config']['ENABLE_THUMBNAILS'] = true;
+// Store thumbnails in a local cache
+$GLOBALS['config']['ENABLE_LOCALCACHE'] = true;
+
+// Update check frequency for Shaarli. 86400 seconds=24 hours
+$GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400 ;
+
+
+/*
+ * Plugin configuration
+ *
+ * Warning: order matters!
+ *
+ * These settings may be be overriden in:
+ *  - data/config.php
+ *  - each plugin's configuration file
+ */
+//$GLOBALS['config']['ENABLED_PLUGINS'] = array(
+//    'qrcode', 'archiveorg', 'readityourself', 'demo_plugin', 'playvideos',
+//    'wallabag', 'markdown', 'addlink_toolbar',
+//);
+$GLOBALS['config']['ENABLED_PLUGINS'] = array('qrcode');
+
+//$GLOBALS['plugins']['WALLABAG_URL'] = 'https://demo.wallabag.org/';
+
+// PubSubHubbub support. Put an empty string to disable, or put your hub url here to enable.
+$GLOBALS['config']['PUBSUBHUB_URL'] = '';
+
+/*
+ * PHP configuration
+ */
+define('shaarli_version', '0.6.0');
+
 // http://server.com/x/shaarli --> /shaarli/
 define('WEB_PATH', substr($_SERVER["REQUEST_URI"], 0, 1+strrpos($_SERVER["REQUEST_URI"], '/', 0)));
 
-// PHP Settings
-ini_set('max_input_time','60');  // High execution time in case of problematic imports/exports.
-ini_set('memory_limit', '128M');  // Try to set max upload file size and read (May not work on some hosts).
+// High execution time in case of problematic imports/exports.
+ini_set('max_input_time','60');
+
+// Try to set max upload file size and read
+ini_set('memory_limit', '128M');
 ini_set('post_max_size', '16M');
 ini_set('upload_max_filesize', '16M');
-error_reporting(E_ALL^E_WARNING);  // See all error except warnings.
-//error_reporting(-1); // See all errors (for debugging only)
 
-// User configuration
+// See all error except warnings
+error_reporting(E_ALL^E_WARNING);
+// See all errors (for debugging only)
+//error_reporting(-1);
+
+/*
+ * User configuration
+ */
 if (is_file($GLOBALS['config']['CONFIG_FILE'])) {
     require_once $GLOBALS['config']['CONFIG_FILE'];
 }
@@ -65,6 +149,8 @@ require_once 'application/TimeZone.php';
 require_once 'application/Url.php';
 require_once 'application/Utils.php';
 require_once 'application/Config.php';
+require_once 'application/PluginManager.php';
+require_once 'application/Router.php';
 
 // Ensure the PHP version is supported
 try {
@@ -109,6 +195,9 @@ include "inc/rain.tpl.class.php"; //include Rain TPL
 raintpl::$tpl_dir = $GLOBALS['config']['RAINTPL_TPL']; // template directory
 raintpl::$cache_dir = $GLOBALS['config']['RAINTPL_TMP']; // cache directory
 
+$pluginManager = PluginManager::getInstance();
+$pluginManager->load($GLOBALS['config']['ENABLED_PLUGINS']);
+
 ob_start();  // Output buffering for the page cache.
 
 
@@ -556,28 +645,43 @@ class pageBuilder
 
     function __construct()
     {
-        $this->tpl=false;
+        $this->tpl = false;
     }
 
+    /**
+     * Initialize all default tpl tags.
+     */
     private function initialize()
     {
         $this->tpl = new RainTPL;
-        $this->tpl->assign('newversion',escape(checkUpdate()));
-        $this->tpl->assign('feedurl',escape(index_url($_SERVER)));
-        $searchcrits=''; // Search criteria
-        if (!empty($_GET['searchtags'])) $searchcrits.='&searchtags='.urlencode($_GET['searchtags']);
-        elseif (!empty($_GET['searchterm'])) $searchcrits.='&searchterm='.urlencode($_GET['searchterm']);
-        $this->tpl->assign('searchcrits',$searchcrits);
-        $this->tpl->assign('source',index_url($_SERVER));
-        $this->tpl->assign('version',shaarli_version);
-        $this->tpl->assign('scripturl',index_url($_SERVER));
-        $this->tpl->assign('pagetitle','Shaarli');
-        $this->tpl->assign('privateonly',!empty($_SESSION['privateonly'])); // Show only private links?
-        if (!empty($GLOBALS['title'])) $this->tpl->assign('pagetitle',$GLOBALS['title']);
-        if (!empty($GLOBALS['titleLink'])) $this->tpl->assign('titleLink',$GLOBALS['titleLink']);
-        if (!empty($GLOBALS['pagetitle'])) $this->tpl->assign('pagetitle',$GLOBALS['pagetitle']);
-        $this->tpl->assign('shaarlititle',empty($GLOBALS['title']) ? 'Shaarli': $GLOBALS['title'] );
-        return;
+        $this->tpl->assign('newversion', escape(checkUpdate()));
+        $this->tpl->assign('feedurl', escape(index_url($_SERVER)));
+        $searchcrits = ''; // Search criteria
+        if (!empty($_GET['searchtags'])) {
+            $searchcrits .= '&searchtags=' . urlencode($_GET['searchtags']);
+        }
+        elseif (!empty($_GET['searchterm'])) {
+            $searchcrits .= '&searchterm=' . urlencode($_GET['searchterm']);
+        }
+        $this->tpl->assign('searchcrits', $searchcrits);
+        $this->tpl->assign('source', index_url($_SERVER));
+        $this->tpl->assign('version', shaarli_version);
+        $this->tpl->assign('scripturl', index_url($_SERVER));
+        $this->tpl->assign('pagetitle', 'Shaarli');
+        $this->tpl->assign('privateonly', !empty($_SESSION['privateonly'])); // Show only private links?
+        if (!empty($GLOBALS['title'])) {
+            $this->tpl->assign('pagetitle', $GLOBALS['title']);
+        }
+        if (!empty($GLOBALS['titleLink'])) {
+            $this->tpl->assign('titleLink', $GLOBALS['titleLink']);
+        }
+        if (!empty($GLOBALS['pagetitle'])) {
+            $this->tpl->assign('pagetitle', $GLOBALS['pagetitle']);
+        }
+        $this->tpl->assign('shaarlititle', empty($GLOBALS['title']) ? 'Shaarli': $GLOBALS['title']);
+        if (!empty($GLOBALS['plugins']['errors'])) {
+            $this->tpl->assign('plugin_errors', $GLOBALS['plugins']['errors']);
+        }
     }
 
     // The following assign() method is basically the same as RainTPL (except that it's lazy)
@@ -952,16 +1056,31 @@ function showDaily()
         $fill[$index]+=$length;
     }
     $PAGE = new pageBuilder;
-    $PAGE->assign('linksToDisplay',$linksToDisplay);
-    $PAGE->assign('linkcount',count($LINKSDB));
-    $PAGE->assign('cols', $columns);
-    $PAGE->assign('day',linkdate2timestamp($day.'_000000'));
-    $PAGE->assign('previousday',$previousday);
-    $PAGE->assign('nextday',$nextday);
+    $data = array(
+        'linksToDisplay' => $linksToDisplay,
+        'linkcount' => count($LINKSDB),
+        'cols' => $columns,
+        'day' => linkdate2timestamp($day.'_000000'),
+        'previousday' => $previousday,
+        'nextday' => $nextday,
+    );
+    $pluginManager = PluginManager::getInstance();
+    $pluginManager->executeHooks('render_daily', $data, array('loggedin' => isLoggedIn()));
+
+    foreach ($data as $key => $value) {
+        $PAGE->assign($key, $value);
+    }
+
     $PAGE->renderPage('daily');
     exit;
 }
 
+// Renders the linklist
+function showLinkList($PAGE, $LINKSDB) {
+    buildLinkList($PAGE,$LINKSDB); // Compute list of links to display
+    $PAGE->renderPage('linklist');
+}
+
 
 // ------------------------------------------------------------------------------------------
 // Render HTML page (according to URL parameters and user rights)
@@ -973,12 +1092,36 @@ function renderPage()
         $GLOBALS['config']['HIDE_PUBLIC_LINKS']
     );
 
+    $PAGE = new pageBuilder;
+
+    // Determine which page will be rendered.
+    $query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : '';
+    $targetPage = Router::findPage($query, $_GET, isLoggedIn());
+
+    // Call plugin hooks for header, footer and includes, specifying which page will be rendered.
+    // Then assign generated data to RainTPL.
+    $common_hooks = array(
+        'header',
+        'footer',
+        'includes',
+    );
+    $pluginManager = PluginManager::getInstance();
+    foreach($common_hooks as $name) {
+        $plugin_data = array();
+        $pluginManager->executeHooks('render_' . $name, $plugin_data,
+            array(
+                'target' => $targetPage,
+                'loggedin' => isLoggedIn()
+            )
+        );
+        $PAGE->assign('plugins_' . $name, $plugin_data);
+    }
+
     // -------- Display login form.
-    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=login'))
+    if ($targetPage == Router::$PAGE_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']) ? escape($_SERVER['HTTP_REFERER']):''));
         $PAGE->renderPage('loginform');
@@ -994,7 +1137,7 @@ function renderPage()
     }
 
     // -------- Picture wall
-    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=picwall'))
+    if ($targetPage == Router::$PAGE_PICWALL)
     {
         // Optionally filter the results:
         $links=array();
@@ -1017,15 +1160,22 @@ function renderPage()
             }
         }
 
-        $PAGE = new pageBuilder;
-        $PAGE->assign('linkcount',count($LINKSDB));
-        $PAGE->assign('linksToDisplay',$linksToDisplay);
+        $data = array(
+            'linkcount' => count($LINKSDB),
+            'linksToDisplay' => $linksToDisplay,
+        );
+        $pluginManager->executeHooks('render_picwall', $data, array('loggedin' => isLoggedIn()));
+
+        foreach ($data as $key => $value) {
+            $PAGE->assign($key, $value);
+        }
+
         $PAGE->renderPage('picwall');
         exit;
     }
 
     // -------- Tag cloud
-    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=tagcloud'))
+    if ($targetPage == Router::$PAGE_TAGCLOUD)
     {
         $tags= $LINKSDB->allTags();
 
@@ -1039,9 +1189,17 @@ function renderPage()
         {
             $tagList[$key] = array('count'=>$value,'size'=>log($value, 15) / log($maxcount, 30) * (22-6) + 6);
         }
-        $PAGE = new pageBuilder;
-        $PAGE->assign('linkcount',count($LINKSDB));
-        $PAGE->assign('tags',$tagList);
+
+        $data = array(
+            'linkcount' => count($LINKSDB),
+            'tags' => $tagList,
+        );
+        $pluginManager->executeHooks('render_tagcloud', $data, array('loggedin' => isLoggedIn()));
+
+        foreach ($data as $key => $value) {
+            $PAGE->assign($key, $value);
+        }
+
         $PAGE->renderPage('tagcloud');
         exit;
     }
@@ -1154,32 +1312,36 @@ function renderPage()
                        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;
         }
 
-        $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'))
+    if ($targetPage == Router::$PAGE_TOOLS)
     {
-        $PAGE = new pageBuilder;
-        $PAGE->assign('linkcount',count($LINKSDB));
-        $PAGE->assign('pageabsaddr',index_url($_SERVER));
+        $data = array(
+            'linkcount' => count($LINKSDB),
+            'pageabsaddr' => index_url($_SERVER),
+        );
+        $pluginManager->executeHooks('render_tools', $data);
+
+        foreach ($data as $key => $value) {
+            $PAGE->assign($key, $value);
+        }
+
         $PAGE->renderPage('tools');
         exit;
     }
 
     // -------- User wants to change his/her password.
-    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=changepasswd'))
+    if ($targetPage == Router::$PAGE_CHANGEPASSWORD)
     {
         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']))
@@ -1210,7 +1372,6 @@ function renderPage()
         }
         else // show the change password form.
         {
-            $PAGE = new pageBuilder;
             $PAGE->assign('linkcount',count($LINKSDB));
             $PAGE->assign('token',getToken());
             $PAGE->renderPage('changepassword');
@@ -1219,7 +1380,7 @@ function renderPage()
     }
 
     // -------- User wants to change configuration
-    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=configure'))
+    if ($targetPage == Router::$PAGE_CONFIGURE)
     {
         if (!empty($_POST['title']) )
         {
@@ -1255,7 +1416,6 @@ function renderPage()
         }
         else // Show the configuration form.
         {
-            $PAGE = new pageBuilder;
             $PAGE->assign('linkcount',count($LINKSDB));
             $PAGE->assign('token',getToken());
             $PAGE->assign('title', empty($GLOBALS['title']) ? '' : $GLOBALS['title'] );
@@ -1269,11 +1429,10 @@ function renderPage()
     }
 
     // -------- User wants to rename a tag or delete it
-    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=changetag'))
+    if ($targetPage == Router::$PAGE_CHANGETAG)
     {
         if (empty($_POST['fromtag']))
         {
-            $PAGE = new pageBuilder;
             $PAGE->assign('linkcount',count($LINKSDB));
             $PAGE->assign('token',getToken());
             $PAGE->assign('tags', $LINKSDB->allTags());
@@ -1318,9 +1477,8 @@ function renderPage()
     }
 
     // -------- User wants to add a link without using the bookmarklet: Show form.
-    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=addlink'))
+    if ($targetPage == Router::$PAGE_ADDLINK)
     {
-        $PAGE = new pageBuilder;
         $PAGE->assign('linkcount',count($LINKSDB));
         $PAGE->renderPage('addlink');
         exit;
@@ -1339,15 +1497,22 @@ function renderPage()
         $link = array('title'=>trim($_POST['lf_title']),'url'=>$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.
+
+        $pluginManager->executeHooks('save_link', $link);
+
         $LINKSDB[$linkdate] = $link;
         $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // Save to disk.
         pubsubhub();
 
         // If we are called from the bookmarklet, we must close the popup:
-        if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; }
-        $returnurl = ( !empty($_POST['returnurl']) ? escape($_POST['returnurl']) : '?' );
-        $returnurl .= '#'.smallHash($_POST['lf_linkdate']);  // Scroll to the link which has been edited.
+        if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) {
+            echo '<script>self.close();</script>';
+            exit;
+        }
+
+        $returnurl = !empty($_POST['returnurl']) ? escape($_POST['returnurl']): '?';
         $location = generateLocation($returnurl, $_SERVER['HTTP_HOST'], array('addlink', 'post', 'edit_link'));
+        $location .= '#'.smallHash($_POST['lf_linkdate']);  // Scroll to the link which has been edited.
         header('Location: '. $location); // After saving the link, redirect to the page the user was on.
         exit;
     }
@@ -1372,6 +1537,9 @@ function renderPage()
         // - confirmation is handled by JavaScript
         // - we are protected from XSRF by the token.
         $linkdate=$_POST['lf_linkdate'];
+
+        $pluginManager->executeHooks('delete_link', $LINKSDB[$linkdate]);
+
         unset($LINKSDB[$linkdate]);
         $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // save to disk
 
@@ -1413,13 +1581,20 @@ function renderPage()
     {
         $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']) ? escape($_SERVER['HTTP_REFERER']) : ''));
-        $PAGE->assign('tags', $LINKSDB->allTags());
+        $data = array(
+            'linkcount' => count($LINKSDB),
+            'link' => $link,
+            'link_is_new' => false,
+            'token' => getToken(),
+            'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''),
+            'tags' => $LINKSDB->allTags(),
+        );
+        $pluginManager->executeHooks('render_editlink', $data);
+
+        foreach ($data as $key => $value) {
+            $PAGE->assign($key, $value);
+        }
+
         $PAGE->renderPage('editlink');
         exit;
     }
@@ -1483,24 +1658,30 @@ function renderPage()
             );
         }
 
-        $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->assign('source',(isset($_GET['source']) ? $_GET['source'] : ''));
-        $PAGE->assign('tags', $LINKSDB->allTags());
+        $data = array(
+            'linkcount' => count($LINKSDB),
+            'link' => $link,
+            'link_is_new' => $link_is_new,
+            'token' => getToken(), // XSRF protection.
+            'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''),
+            'source' => (isset($_GET['source']) ? $_GET['source'] : ''),
+            'tags' => $LINKSDB->allTags(),
+        );
+        $pluginManager->executeHooks('render_editlink', $data);
+
+        foreach ($data as $key => $value) {
+            $PAGE->assign($key, $value);
+        }
+
         $PAGE->renderPage('editlink');
         exit;
     }
 
     // -------- Export as Netscape Bookmarks HTML file.
-    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=export'))
+    if ($targetPage == Router::$PAGE_EXPORT)
     {
         if (empty($_GET['what']))
         {
-            $PAGE = new pageBuilder;
             $PAGE->assign('linkcount',count($LINKSDB));
             $PAGE->renderPage('export');
             exit;
@@ -1552,9 +1733,8 @@ HTML;
     }
 
     // -------- Show upload/import dialog:
-    if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=import'))
+    if ($targetPage == Router::$PAGE_IMPORT)
     {
-        $PAGE = new pageBuilder;
         $PAGE->assign('linkcount',count($LINKSDB));
         $PAGE->assign('token',getToken());
         $PAGE->assign('maxfilesize',getMaxFileSize());
@@ -1563,9 +1743,7 @@ HTML;
     }
 
     // -------- Otherwise, simply display search form and links:
-    $PAGE = new pageBuilder;
-    buildLinkList($PAGE,$LINKSDB); // Compute list of links to display
-    $PAGE->renderPage('linklist');
+    showLinkList($PAGE, $LINKSDB);
     exit;
 }
 
@@ -1736,7 +1914,7 @@ function buildLinkList($PAGE,$LINKSDB)
         $taglist = explode(' ',$link['tags']);
         uasort($taglist, 'strcasecmp');
         $link['taglist']=$taglist;
-
+        $link['shorturl'] = smallHash($link['linkdate']);
         if ($link["url"][0] === '?' && // Check for both signs of a note: starting with ? and 7 chars long. I doubt that you'll post any links that look like this.
             strlen($link["url"]) === 7) {
             $link["url"] = index_url($_SERVER) . $link["url"];
@@ -1756,18 +1934,28 @@ function buildLinkList($PAGE,$LINKSDB)
     $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());
+    $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'],  // Optional redirector URL.
+        'token' => $token,
+        'links' => $linkDisp,
+        'tags' => $LINKSDB->allTags(),
+    );
+
+    $pluginManager = PluginManager::getInstance();
+    $pluginManager->executeHooks('render_linklist', $data, array('loggedin' => isLoggedIn()));
+
+    foreach ($data as $key => $value) {
+        $PAGE->assign($key, $value);
+    }
+
     return;
 }