Merge pull request #275 from shaarli/plugin-proposition
Plugin proposition
This commit is contained in:
commit
fd006c630b
61 changed files with 13266 additions and 484 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -21,3 +21,6 @@ coverage
|
|||
tests/datastore.php
|
||||
tests/dummycache/
|
||||
phpmd.html
|
||||
|
||||
# Ignore user plugin configuration
|
||||
plugins/*/config.php
|
18
COPYING
18
COPYING
|
@ -1,4 +1,4 @@
|
|||
Files: *
|
||||
Files: *
|
||||
License: zlib/libpng
|
||||
Copyright: (c) 2011-2015 Sébastien SAUVAGE <sebsauvage@sebsauvage.net>
|
||||
(c) 2011-2015 Alexandre Alapetite <alexandre@alapetite.fr>
|
||||
|
@ -31,9 +31,9 @@ Copyright: (c) 2011-2015 Sébastien SAUVAGE <sebsauvage@sebsauvage.net>
|
|||
|
||||
Files: inc/reset.css
|
||||
License: BSD (http://opensource.org/licenses/BSD-3-Clause)
|
||||
Copyright: (c) 2010, Yahoo! Inc.
|
||||
Copyright: (c) 2010, Yahoo! Inc.
|
||||
|
||||
Files: images/calendar.png, images/edit_icon.png, images/feed-icon-14x14.png, images/private.png, images/private_16x16.png, images/private_16x16_active.png, images/qrcode.png, images/tag_blue.png
|
||||
Files: images/calendar.png, images/edit_icon.png, images/feed-icon-14x14.png, images/private.png, images/private_16x16.png, images/private_16x16_active.png, images/tag_blue.png
|
||||
License: CC-BY (http://creativecommons.org/licenses/by/3.0/)
|
||||
Copyright: (c) 2014 Yusuke Kamiyamane
|
||||
Source: http://p.yusukekamiyamane.com/
|
||||
|
@ -59,10 +59,6 @@ Files: inc/blazy*.js
|
|||
License: MIT License (http://opensource.org/licenses/MIT)
|
||||
Copyright: (C) Bjoern Klinggaard - @bklinggaard - http://dinbror.dk/blazy
|
||||
|
||||
Files: inc/qr.js
|
||||
License: GPLv3 License (http://opensource.org/licenses/gpl-3.0)
|
||||
Copyright: (C) 2014 Alasdair Mercer, http://neocotic.com, https://github.com/neocotic/qr.js
|
||||
|
||||
Files: inc/rain.tpl.class.php
|
||||
Copyright: 2011-2012, Federico Ulfo <rainelemental@gmail.com>
|
||||
2011-2012, The Rain Team <hello@raintm.com>
|
||||
|
@ -72,6 +68,10 @@ Files: inc/awesomplete*
|
|||
License: MIT License (http://opensource.org/licenses/MIT)
|
||||
Copyright: (C) 2015 Lea Verou - https://github.com/LeaVerou/awesomplete
|
||||
|
||||
Files: plugins/wallabag/wallabag.png
|
||||
License: MIT License (http://opensource.org/licenses/MIT)
|
||||
Copyright: (C) 2015 Nicolas Lœuillet - https://github.com/wallabag/wallabag
|
||||
|
||||
----------------------------------------------------
|
||||
ZLIB/LIBPNG LICENSE
|
||||
|
||||
|
@ -80,10 +80,10 @@ In no event will the authors be held liable for any damages arising from
|
|||
the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would
|
||||
be appreciated but is not required.
|
||||
|
|
4
Makefile
4
Makefile
|
@ -13,8 +13,8 @@
|
|||
# - enable in php.ini
|
||||
|
||||
BIN = vendor/bin
|
||||
PHP_SOURCE = index.php application tests
|
||||
PHP_COMMA_SOURCE = index.php,application,tests
|
||||
PHP_SOURCE = index.php application tests plugins
|
||||
PHP_COMMA_SOURCE = index.php,application,tests,plugins
|
||||
|
||||
all: static_analysis_summary test
|
||||
|
||||
|
|
263
application/Config.php
Executable file → Normal file
263
application/Config.php
Executable file → Normal file
|
@ -1,129 +1,134 @@
|
|||
<?php
|
||||
/**
|
||||
* Functions related to configuration management.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Re-write configuration file according to given array.
|
||||
* Requires mandatory fields listed in $MANDATORY_FIELDS.
|
||||
*
|
||||
* @param array $config contains all configuration fields.
|
||||
* @param bool $isLoggedIn true if user is logged in.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MissingFieldConfigException: a mandatory field has not been provided in $config.
|
||||
* @throws UnauthorizedConfigException: user is not authorize to change configuration.
|
||||
* @throws Exception: an error occured while writing the new config file.
|
||||
*/
|
||||
function writeConfig($config, $isLoggedIn)
|
||||
{
|
||||
// These fields are required in configuration.
|
||||
$MANDATORY_FIELDS = array(
|
||||
'login', 'hash', 'salt', 'timezone', 'title', 'titleLink',
|
||||
'redirector', 'disablesessionprotection', 'privateLinkByDefault'
|
||||
);
|
||||
|
||||
if (!isset($config['config']['CONFIG_FILE'])) {
|
||||
throw new MissingFieldConfigException('CONFIG_FILE');
|
||||
}
|
||||
|
||||
// Only logged in user can alter config.
|
||||
if (is_file($config['config']['CONFIG_FILE']) && !$isLoggedIn) {
|
||||
throw new UnauthorizedConfigException();
|
||||
}
|
||||
|
||||
// Check that all mandatory fields are provided in $config.
|
||||
foreach ($MANDATORY_FIELDS as $field) {
|
||||
if (!isset($config[$field])) {
|
||||
throw new MissingFieldConfigException($field);
|
||||
}
|
||||
}
|
||||
|
||||
$configStr = '<?php '. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'login\'] = '.var_export($config['login'], true).';'. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'hash\'] = '.var_export($config['hash'], true).';'. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'salt\'] = '.var_export($config['salt'], true).'; '. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'timezone\'] = '.var_export($config['timezone'], true).';'. PHP_EOL;
|
||||
$configStr .= 'date_default_timezone_set('.var_export($config['timezone'], true).');'. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'title\'] = '.var_export($config['title'], true).';'. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'titleLink\'] = '.var_export($config['titleLink'], true).'; '. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'redirector\'] = '.var_export($config['redirector'], true).'; '. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'disablesessionprotection\'] = '.var_export($config['disablesessionprotection'], true).'; '. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'privateLinkByDefault\'] = '.var_export($config['privateLinkByDefault'], true).'; '. PHP_EOL;
|
||||
|
||||
// Store all $config['config']
|
||||
foreach ($config['config'] as $key => $value) {
|
||||
$configStr .= '$GLOBALS[\'config\'][\''. $key .'\'] = '.var_export($config['config'][$key], true).';'. PHP_EOL;
|
||||
}
|
||||
$configStr .= '?>';
|
||||
|
||||
if (!file_put_contents($config['config']['CONFIG_FILE'], $configStr)
|
||||
|| strcmp(file_get_contents($config['config']['CONFIG_FILE']), $configStr) != 0
|
||||
) {
|
||||
throw new Exception(
|
||||
'Shaarli could not create the config file.
|
||||
Please make sure Shaarli has the right to write in the folder is it installed in.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Milestone 0.9 - shaarli/Shaarli#41: options.php is not supported anymore.
|
||||
* ==> if user is loggedIn, merge its content with config.php, then delete options.php.
|
||||
*
|
||||
* @param array $config contains all configuration fields.
|
||||
* @param bool $isLoggedIn true if user is logged in.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function mergeDeprecatedConfig($config, $isLoggedIn)
|
||||
{
|
||||
$config_file = $config['config']['CONFIG_FILE'];
|
||||
|
||||
if (is_file($config['config']['DATADIR'].'/options.php') && $isLoggedIn) {
|
||||
include $config['config']['DATADIR'].'/options.php';
|
||||
|
||||
// Load GLOBALS into config
|
||||
foreach ($GLOBALS as $key => $value) {
|
||||
$config[$key] = $value;
|
||||
}
|
||||
$config['config']['CONFIG_FILE'] = $config_file;
|
||||
writeConfig($config, $isLoggedIn);
|
||||
|
||||
unlink($config['config']['DATADIR'].'/options.php');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception used if a mandatory field is missing in given configuration.
|
||||
*/
|
||||
class MissingFieldConfigException extends Exception
|
||||
{
|
||||
public $field;
|
||||
|
||||
/**
|
||||
* Construct exception.
|
||||
*
|
||||
* @param string $field field name missing.
|
||||
*/
|
||||
public function __construct($field)
|
||||
{
|
||||
$this->field = $field;
|
||||
$this->message = 'Configuration value is required for '. $this->field;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception used if an unauthorized attempt to edit configuration has been made.
|
||||
*/
|
||||
class UnauthorizedConfigException extends Exception
|
||||
{
|
||||
/**
|
||||
* Construct exception.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->message = 'You are not authorized to alter config.';
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* Functions related to configuration management.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Re-write configuration file according to given array.
|
||||
* Requires mandatory fields listed in $MANDATORY_FIELDS.
|
||||
*
|
||||
* @param array $config contains all configuration fields.
|
||||
* @param bool $isLoggedIn true if user is logged in.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws MissingFieldConfigException: a mandatory field has not been provided in $config.
|
||||
* @throws UnauthorizedConfigException: user is not authorize to change configuration.
|
||||
* @throws Exception: an error occured while writing the new config file.
|
||||
*/
|
||||
function writeConfig($config, $isLoggedIn)
|
||||
{
|
||||
// These fields are required in configuration.
|
||||
$MANDATORY_FIELDS = array(
|
||||
'login', 'hash', 'salt', 'timezone', 'title', 'titleLink',
|
||||
'redirector', 'disablesessionprotection', 'privateLinkByDefault'
|
||||
);
|
||||
|
||||
if (!isset($config['config']['CONFIG_FILE'])) {
|
||||
throw new MissingFieldConfigException('CONFIG_FILE');
|
||||
}
|
||||
|
||||
// Only logged in user can alter config.
|
||||
if (is_file($config['config']['CONFIG_FILE']) && !$isLoggedIn) {
|
||||
throw new UnauthorizedConfigException();
|
||||
}
|
||||
|
||||
// Check that all mandatory fields are provided in $config.
|
||||
foreach ($MANDATORY_FIELDS as $field) {
|
||||
if (!isset($config[$field])) {
|
||||
throw new MissingFieldConfigException($field);
|
||||
}
|
||||
}
|
||||
|
||||
$configStr = '<?php '. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'login\'] = '.var_export($config['login'], true).';'. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'hash\'] = '.var_export($config['hash'], true).';'. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'salt\'] = '.var_export($config['salt'], true).'; '. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'timezone\'] = '.var_export($config['timezone'], true).';'. PHP_EOL;
|
||||
$configStr .= 'date_default_timezone_set('.var_export($config['timezone'], true).');'. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'title\'] = '.var_export($config['title'], true).';'. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'titleLink\'] = '.var_export($config['titleLink'], true).'; '. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'redirector\'] = '.var_export($config['redirector'], true).'; '. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'disablesessionprotection\'] = '.var_export($config['disablesessionprotection'], true).'; '. PHP_EOL;
|
||||
$configStr .= '$GLOBALS[\'privateLinkByDefault\'] = '.var_export($config['privateLinkByDefault'], true).'; '. PHP_EOL;
|
||||
|
||||
// Store all $config['config']
|
||||
foreach ($config['config'] as $key => $value) {
|
||||
$configStr .= '$GLOBALS[\'config\'][\''. $key .'\'] = '.var_export($config['config'][$key], true).';'. PHP_EOL;
|
||||
}
|
||||
|
||||
if (isset($config['plugins'])) {
|
||||
foreach ($config['plugins'] as $key => $value) {
|
||||
$configStr .= '$GLOBALS[\'plugins\'][\''. $key .'\'] = '.var_export($config['plugins'][$key], true).';'. PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!file_put_contents($config['config']['CONFIG_FILE'], $configStr)
|
||||
|| strcmp(file_get_contents($config['config']['CONFIG_FILE']), $configStr) != 0
|
||||
) {
|
||||
throw new Exception(
|
||||
'Shaarli could not create the config file.
|
||||
Please make sure Shaarli has the right to write in the folder is it installed in.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Milestone 0.9 - shaarli/Shaarli#41: options.php is not supported anymore.
|
||||
* ==> if user is loggedIn, merge its content with config.php, then delete options.php.
|
||||
*
|
||||
* @param array $config contains all configuration fields.
|
||||
* @param bool $isLoggedIn true if user is logged in.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function mergeDeprecatedConfig($config, $isLoggedIn)
|
||||
{
|
||||
$config_file = $config['config']['CONFIG_FILE'];
|
||||
|
||||
if (is_file($config['config']['DATADIR'].'/options.php') && $isLoggedIn) {
|
||||
include $config['config']['DATADIR'].'/options.php';
|
||||
|
||||
// Load GLOBALS into config
|
||||
foreach ($GLOBALS as $key => $value) {
|
||||
$config[$key] = $value;
|
||||
}
|
||||
$config['config']['CONFIG_FILE'] = $config_file;
|
||||
writeConfig($config, $isLoggedIn);
|
||||
|
||||
unlink($config['config']['DATADIR'].'/options.php');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception used if a mandatory field is missing in given configuration.
|
||||
*/
|
||||
class MissingFieldConfigException extends Exception
|
||||
{
|
||||
public $field;
|
||||
|
||||
/**
|
||||
* Construct exception.
|
||||
*
|
||||
* @param string $field field name missing.
|
||||
*/
|
||||
public function __construct($field)
|
||||
{
|
||||
$this->field = $field;
|
||||
$this->message = 'Configuration value is required for '. $this->field;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception used if an unauthorized attempt to edit configuration has been made.
|
||||
*/
|
||||
class UnauthorizedConfigException extends Exception
|
||||
{
|
||||
/**
|
||||
* Construct exception.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->message = 'You are not authorized to alter config.';
|
||||
}
|
||||
}
|
||||
|
|
184
application/PluginManager.php
Normal file
184
application/PluginManager.php
Normal file
|
@ -0,0 +1,184 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class PluginManager
|
||||
*
|
||||
* Use to manage, load and execute plugins.
|
||||
*
|
||||
* Using Singleton design pattern.
|
||||
*/
|
||||
class PluginManager
|
||||
{
|
||||
/**
|
||||
* PluginManager singleton instance.
|
||||
* @var PluginManager $instance
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* List of authorized plugins from configuration file.
|
||||
* @var array $authorizedPlugins
|
||||
*/
|
||||
private $authorizedPlugins;
|
||||
|
||||
/**
|
||||
* List of loaded plugins.
|
||||
* @var array $loadedPlugins
|
||||
*/
|
||||
private $loadedPlugins = array();
|
||||
|
||||
/**
|
||||
* Plugins subdirectory.
|
||||
* @var string $PLUGINS_PATH
|
||||
*/
|
||||
public static $PLUGINS_PATH = 'plugins';
|
||||
|
||||
/**
|
||||
* Private constructor: new instances not allowed.
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Cloning isn't allowed either.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Return existing instance of PluginManager, or create it.
|
||||
*
|
||||
* @return PluginManager instance.
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (!(self::$instance instanceof self)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plugins listed in $authorizedPlugins.
|
||||
*
|
||||
* @param array $authorizedPlugins Names of plugin authorized to be loaded.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function load($authorizedPlugins)
|
||||
{
|
||||
$this->authorizedPlugins = $authorizedPlugins;
|
||||
|
||||
$dirs = glob(self::$PLUGINS_PATH . '/*', GLOB_ONLYDIR);
|
||||
$dirnames = array_map('basename', $dirs);
|
||||
foreach ($this->authorizedPlugins as $plugin) {
|
||||
$index = array_search($plugin, $dirnames);
|
||||
|
||||
// plugin authorized, but its folder isn't listed
|
||||
if ($index === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->loadPlugin($dirs[$index], $plugin);
|
||||
}
|
||||
catch (PluginFileNotFoundException $e) {
|
||||
error_log($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all plugins registered hook.
|
||||
*
|
||||
* @param string $hook name of the hook to trigger.
|
||||
* @param array $data list of data to manipulate passed by reference.
|
||||
* @param array $params additional parameters such as page target.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function executeHooks($hook, &$data, $params = array())
|
||||
{
|
||||
if (!empty($params['target'])) {
|
||||
$data['_PAGE_'] = $params['target'];
|
||||
}
|
||||
|
||||
if (isset($params['loggedin'])) {
|
||||
$data['_LOGGEDIN_'] = $params['loggedin'];
|
||||
}
|
||||
|
||||
foreach ($this->loadedPlugins as $plugin) {
|
||||
$hookFunction = $this->buildHookName($hook, $plugin);
|
||||
|
||||
if (function_exists($hookFunction)) {
|
||||
$data = call_user_func($hookFunction, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a single plugin from its files.
|
||||
* Add them in $loadedPlugins if successful.
|
||||
*
|
||||
* @param string $dir plugin's directory.
|
||||
* @param string $pluginName plugin's name.
|
||||
*
|
||||
* @return void
|
||||
* @throws PluginFileNotFoundException - plugin files not found.
|
||||
*/
|
||||
private function loadPlugin($dir, $pluginName)
|
||||
{
|
||||
if (!is_dir($dir)) {
|
||||
throw new PluginFileNotFoundException($pluginName);
|
||||
}
|
||||
|
||||
$pluginFilePath = $dir . '/' . $pluginName . '.php';
|
||||
if (!is_file($pluginFilePath)) {
|
||||
throw new PluginFileNotFoundException($pluginName);
|
||||
}
|
||||
|
||||
include_once $pluginFilePath;
|
||||
|
||||
$this->loadedPlugins[] = $pluginName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct normalize hook name for a specific plugin.
|
||||
*
|
||||
* Format:
|
||||
* hook_<plugin_name>_<hook_name>
|
||||
*
|
||||
* @param string $hook hook name.
|
||||
* @param string $pluginName plugin name.
|
||||
*
|
||||
* @return string - plugin's hook name.
|
||||
*/
|
||||
public function buildHookName($hook, $pluginName)
|
||||
{
|
||||
return 'hook_' . $pluginName . '_' . $hook;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class PluginFileNotFoundException
|
||||
*
|
||||
* Raise when plugin files can't be found.
|
||||
*/
|
||||
class PluginFileNotFoundException extends Exception
|
||||
{
|
||||
/**
|
||||
* Construct exception with plugin name.
|
||||
* Generate message.
|
||||
*
|
||||
* @param string $pluginName name of the plugin not found
|
||||
*/
|
||||
public function __construct($pluginName)
|
||||
{
|
||||
$this->message = 'Plugin "'. $pluginName .'" files not found.';
|
||||
}
|
||||
}
|
105
application/Router.php
Normal file
105
application/Router.php
Normal file
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class Router
|
||||
*
|
||||
* (only displayable pages here)
|
||||
*/
|
||||
class Router
|
||||
{
|
||||
public static $PAGE_LOGIN = 'login';
|
||||
|
||||
public static $PAGE_PICWALL = 'picwall';
|
||||
|
||||
public static $PAGE_TAGCLOUD = 'tagcloud';
|
||||
|
||||
public static $PAGE_TOOLS = 'tools';
|
||||
|
||||
public static $PAGE_CHANGEPASSWORD = 'changepasswd';
|
||||
|
||||
public static $PAGE_CONFIGURE = 'configure';
|
||||
|
||||
public static $PAGE_CHANGETAG = 'changetag';
|
||||
|
||||
public static $PAGE_ADDLINK = 'addlink';
|
||||
|
||||
public static $PAGE_EDITLINK = 'edit_link';
|
||||
|
||||
public static $PAGE_EXPORT = 'export';
|
||||
|
||||
public static $PAGE_IMPORT = 'import';
|
||||
|
||||
public static $PAGE_LINKLIST = 'linklist';
|
||||
|
||||
/**
|
||||
* Reproducing renderPage() if hell, to avoid regression.
|
||||
*
|
||||
* This highlights how bad this needs to be rewrite,
|
||||
* but let's focus on plugins for now.
|
||||
*
|
||||
* @param string $query $_SERVER['QUERY_STRING'].
|
||||
* @param array $get $_SERVER['GET'].
|
||||
* @param bool $loggedIn true if authenticated user.
|
||||
*
|
||||
* @return self::page found.
|
||||
*/
|
||||
public static function findPage($query, $get, $loggedIn)
|
||||
{
|
||||
$loggedIn = ($loggedIn === true) ? true : false;
|
||||
|
||||
if (empty($query) && !isset($get['edit_link']) && !isset($get['post'])) {
|
||||
return self::$PAGE_LINKLIST;
|
||||
}
|
||||
|
||||
if (startswith($query, 'do='. self::$PAGE_LOGIN) && $loggedIn === false) {
|
||||
return self::$PAGE_LOGIN;
|
||||
}
|
||||
|
||||
if (startswith($query, 'do='. self::$PAGE_PICWALL)) {
|
||||
return self::$PAGE_PICWALL;
|
||||
}
|
||||
|
||||
if (startswith($query, 'do='. self::$PAGE_TAGCLOUD)) {
|
||||
return self::$PAGE_TAGCLOUD;
|
||||
}
|
||||
|
||||
// At this point, only loggedin pages.
|
||||
if (!$loggedIn) {
|
||||
return self::$PAGE_LINKLIST;
|
||||
}
|
||||
|
||||
if (startswith($query, 'do='. self::$PAGE_TOOLS)) {
|
||||
return self::$PAGE_TOOLS;
|
||||
}
|
||||
|
||||
if (startswith($query, 'do='. self::$PAGE_CHANGEPASSWORD)) {
|
||||
return self::$PAGE_CHANGEPASSWORD;
|
||||
}
|
||||
|
||||
if (startswith($query, 'do='. self::$PAGE_CONFIGURE)) {
|
||||
return self::$PAGE_CONFIGURE;
|
||||
}
|
||||
|
||||
if (startswith($query, 'do='. self::$PAGE_CHANGETAG)) {
|
||||
return self::$PAGE_CHANGETAG;
|
||||
}
|
||||
|
||||
if (startswith($query, 'do='. self::$PAGE_ADDLINK)) {
|
||||
return self::$PAGE_ADDLINK;
|
||||
}
|
||||
|
||||
if (isset($get['edit_link']) || isset($get['post'])) {
|
||||
return self::$PAGE_EDITLINK;
|
||||
}
|
||||
|
||||
if (startswith($query, 'do='. self::$PAGE_EXPORT)) {
|
||||
return self::$PAGE_EXPORT;
|
||||
}
|
||||
|
||||
if (startswith($query, 'do='. self::$PAGE_IMPORT)) {
|
||||
return self::$PAGE_IMPORT;
|
||||
}
|
||||
|
||||
return self::$PAGE_LINKLIST;
|
||||
}
|
||||
}
|
|
@ -405,12 +405,12 @@ h1 {
|
|||
}
|
||||
*/
|
||||
|
||||
.linkdate, .linkarchive {
|
||||
.linkdate {
|
||||
font-size:8pt;
|
||||
color:#888;
|
||||
}
|
||||
|
||||
.linkdate a, .linkarchive a {
|
||||
.linkdate a {
|
||||
color:#E28E3F;
|
||||
}
|
||||
|
||||
|
@ -451,12 +451,12 @@ a.qrcode img {
|
|||
color: #F57900;
|
||||
}
|
||||
|
||||
.linkdate, .linkarchive {
|
||||
.linkdate {
|
||||
font-size: 8pt;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.linkdate a, .linkarchive a {
|
||||
.linkdate a {
|
||||
background-image: url('../images/calendar.png');
|
||||
padding: 2px 0 3px 20px;
|
||||
background-repeat: no-repeat;
|
||||
|
@ -1126,3 +1126,8 @@ div.dailyNoEntry {
|
|||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
ul.errors {
|
||||
color: red;
|
||||
float: left;
|
||||
}
|
278
index.php
278
index.php
|
@ -45,9 +45,17 @@
|
|||
$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.
|
||||
$GLOBALS['config']['HIDE_PUBLIC_LINKS'] = false;
|
||||
//$GLOBALS['config']['ENABLED_PLUGINS'] = array(
|
||||
// 'qrcode', 'archiveorg', 'readityourself', 'demo_plugin', 'playvideos',
|
||||
// 'wallabag', 'markdown', 'addlink_toolbar',
|
||||
//);
|
||||
// Warning: order matters.
|
||||
$GLOBALS['config']['ENABLED_PLUGINS'] = array('qrcode');
|
||||
|
||||
// Default plugins, default config - will be overriden by config.php and then plugin's config.php file.
|
||||
//$GLOBALS['plugins']['WALLABAG_URL'] = 'https://demo.wallabag.org/';
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
define('shaarli_version', '0.5.4');
|
||||
// http://server.com/x/shaarli --> /shaarli/
|
||||
|
@ -75,6 +83,8 @@
|
|||
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 {
|
||||
|
@ -119,6 +129,9 @@
|
|||
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.
|
||||
|
||||
|
||||
|
@ -566,28 +579,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)
|
||||
|
@ -962,16 +990,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)
|
||||
|
@ -983,12 +1026,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');
|
||||
|
@ -1004,7 +1071,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();
|
||||
|
@ -1027,15 +1094,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();
|
||||
|
||||
|
@ -1049,9 +1123,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;
|
||||
}
|
||||
|
@ -1164,32 +1246,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']))
|
||||
|
@ -1220,7 +1306,6 @@ function renderPage()
|
|||
}
|
||||
else // show the change password form.
|
||||
{
|
||||
$PAGE = new pageBuilder;
|
||||
$PAGE->assign('linkcount',count($LINKSDB));
|
||||
$PAGE->assign('token',getToken());
|
||||
$PAGE->renderPage('changepassword');
|
||||
|
@ -1229,7 +1314,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']) )
|
||||
{
|
||||
|
@ -1265,7 +1350,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'] );
|
||||
|
@ -1279,11 +1363,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());
|
||||
|
@ -1328,9 +1411,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;
|
||||
|
@ -1349,6 +1431,9 @@ 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();
|
||||
|
@ -1386,6 +1471,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
|
||||
|
||||
|
@ -1427,13 +1515,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;
|
||||
}
|
||||
|
@ -1497,24 +1592,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;
|
||||
|
@ -1566,9 +1667,8 @@ function renderPage()
|
|||
}
|
||||
|
||||
// -------- 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());
|
||||
|
@ -1577,9 +1677,7 @@ function renderPage()
|
|||
}
|
||||
|
||||
// -------- 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;
|
||||
}
|
||||
|
||||
|
@ -1750,7 +1848,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"];
|
||||
|
@ -1770,18 +1868,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;
|
||||
}
|
||||
|
||||
|
|
28
plugins/TODO.md
Normal file
28
plugins/TODO.md
Normal file
|
@ -0,0 +1,28 @@
|
|||
https://github.com/shaarli/Shaarli/issues/181 - Add Disqus or Isso comments box on a permalink page
|
||||
|
||||
* http://posativ.org/isso/
|
||||
* install debian package https://packages.debian.org/sid/isso
|
||||
* configure server http://posativ.org/isso/docs/configuration/server/
|
||||
* configure client http://posativ.org/isso/docs/configuration/client/
|
||||
* http://posativ.org/isso/docs/quickstart/ and add `<script data-isso="//comments.example.tld/" src="//comments.example.tld/js/embed.min.js"></script>` to includes.html template; then add `<section id="isso-thread"></section>` in the linklist template where you want the comments (in the linklist_plugins loop for example)
|
||||
|
||||
|
||||
Problem: by default, Isso thread ID is guessed from the current url (only one thread per page).
|
||||
if we want multiple threads on a single page (shaarli linklist), we must use : the `data-isso-id` client config,
|
||||
with data-isso-id being the permalink of an item.
|
||||
|
||||
`<section data-isso-id="aH7klxW" id="isso-thread"></section>`
|
||||
`data-isso-id: Set a custom thread id, defaults to current URI.`
|
||||
|
||||
Problem: feature is currently broken https://github.com/posativ/isso/issues/27
|
||||
|
||||
Another option, only display isso threads when current URL is a permalink (`\?(A-Z|a-z|0-9|-){7}`) (only show thread
|
||||
when displaying only this link), and just display a "comments" button on each linklist item. Optionally show the comment
|
||||
count on each item using the API (http://posativ.org/isso/docs/extras/api/#get-comment-count). API requests can be done
|
||||
by raintpl `{function` or client-side with js. The former should be faster if isso and shaarli are on ther same server.
|
||||
|
||||
Showing all full isso threads in the linklist would destroy layout
|
||||
|
||||
-----------------------------------------------------------
|
||||
|
||||
http://www.git-attitude.fr/2014/11/04/git-rerere/ for the merge
|
4
plugins/addlink_toolbar/addlink_toolbar.css
Executable file
4
plugins/addlink_toolbar/addlink_toolbar.css
Executable file
|
@ -0,0 +1,4 @@
|
|||
#addlink_toolbar {
|
||||
display: inline;
|
||||
margin: 0 0 0 25px;
|
||||
}
|
6
plugins/addlink_toolbar/addlink_toolbar.html
Executable file
6
plugins/addlink_toolbar/addlink_toolbar.html
Executable file
|
@ -0,0 +1,6 @@
|
|||
<div id="addlink_toolbar">
|
||||
<form method="GET" action="" name="addform" class="addform">
|
||||
<input type="text" name="post" placeholder="URI">
|
||||
<input type="submit" value="Add link" class="bigbutton">
|
||||
</form>
|
||||
</div>
|
38
plugins/addlink_toolbar/addlink_toolbar.php
Executable file
38
plugins/addlink_toolbar/addlink_toolbar.php
Executable file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Plugin addlink_toolbar.
|
||||
* Adds the addlink input on the linklist page.
|
||||
*/
|
||||
|
||||
/**
|
||||
* When linklist is displayed, add play videos to header's toolbar.
|
||||
*
|
||||
* @param array $data - header data.
|
||||
*
|
||||
* @return mixed - header data with addlink toolbar item.
|
||||
*/
|
||||
function hook_addlink_toolbar_render_header($data)
|
||||
{
|
||||
if ($data['_PAGE_'] == Router::$PAGE_LINKLIST && $data['_LOGGEDIN_'] === true) {
|
||||
$data['fields_toolbar'][] = file_get_contents(PluginManager::$PLUGINS_PATH . '/addlink_toolbar/addlink_toolbar.html');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* When link list is displayed, include markdown CSS.
|
||||
*
|
||||
* @param array $data - includes data.
|
||||
*
|
||||
* @return mixed - includes data with markdown CSS file added.
|
||||
*/
|
||||
function hook_addlink_toolbar_render_includes($data)
|
||||
{
|
||||
if ($data['_PAGE_'] == Router::$PAGE_LINKLIST && $data['_LOGGEDIN_'] === true) {
|
||||
$data['css_files'][] = PluginManager::$PLUGINS_PATH . '/addlink_toolbar/addlink_toolbar.css';
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
1
plugins/archiveorg/archiveorg.html
Normal file
1
plugins/archiveorg/archiveorg.html
Normal file
|
@ -0,0 +1 @@
|
|||
<span><a href="https://web.archive.org/web/%s"><img width="13" height="13" src="plugins/archiveorg/internetarchive.png" title="View on archive.org" /></a></span>
|
25
plugins/archiveorg/archiveorg.php
Normal file
25
plugins/archiveorg/archiveorg.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
/**
|
||||
* Plugin Archive.org.
|
||||
*
|
||||
* Add an icon in the link list for archive.org.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add archive.org icon to link_plugin when rendering linklist.
|
||||
*
|
||||
* @param mixed $data - linklist data.
|
||||
*
|
||||
* @return mixed - linklist data with archive.org plugin.
|
||||
*/
|
||||
function hook_archiveorg_render_linklist($data)
|
||||
{
|
||||
$archive_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/archiveorg/archiveorg.html');
|
||||
|
||||
foreach ($data['links'] as &$value) {
|
||||
$archive = sprintf($archive_html, $value['url']);
|
||||
$value['link_plugin'][] = $archive;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
BIN
plugins/archiveorg/internetarchive.png
Normal file
BIN
plugins/archiveorg/internetarchive.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 878 B |
7
plugins/demo_plugin/custom_demo.css
Executable file
7
plugins/demo_plugin/custom_demo.css
Executable file
|
@ -0,0 +1,7 @@
|
|||
.linktitle a {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.upper_plugin_demo {
|
||||
float: left;
|
||||
}
|
1
plugins/demo_plugin/demo_plugin.js
Executable file
1
plugins/demo_plugin/demo_plugin.js
Executable file
|
@ -0,0 +1 @@
|
|||
console.log("I love the smell of napalm in the morning.");
|
317
plugins/demo_plugin/demo_plugin.php
Executable file
317
plugins/demo_plugin/demo_plugin.php
Executable file
|
@ -0,0 +1,317 @@
|
|||
<?php
|
||||
/**
|
||||
* Demo Plugin.
|
||||
*
|
||||
* This plugin try to cover Shaarli's plugin API entirely.
|
||||
* Can be used by plugin developper to make their own.
|
||||
*/
|
||||
|
||||
/*
|
||||
* RENDER HEADER, INCLUDES, FOOTER
|
||||
*
|
||||
* Those hooks are called at every page rendering.
|
||||
* You can filter its execution by checking _PAGE_ value
|
||||
* and check user status with _LOGGEDIN_.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Hook render_header.
|
||||
* Executed on every page redering.
|
||||
*
|
||||
* Template placeholders:
|
||||
* - buttons_toolbar
|
||||
* - fields_toolbar
|
||||
*
|
||||
* @param array $data data passed to plugin
|
||||
*
|
||||
* @return array altered $data.
|
||||
*/
|
||||
function hook_demo_plugin_render_header($data)
|
||||
{
|
||||
// Only execute when linklist is rendered.
|
||||
if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) {
|
||||
|
||||
// If loggedin
|
||||
if ($data['_LOGGEDIN_'] === true) {
|
||||
// Buttons in toolbar
|
||||
$data['buttons_toolbar'][] = '<li><a href="#">DEMO_buttons_toolbar</a></li>';
|
||||
}
|
||||
|
||||
// Fields in toolbar
|
||||
$data['fields_toolbar'][] = 'DEMO_fields_toolbar';
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook render_includes.
|
||||
* Executed on every page redering.
|
||||
*
|
||||
* Template placeholders:
|
||||
* - css_files
|
||||
*
|
||||
* Data:
|
||||
* - _PAGE_: current page
|
||||
* - _LOGGEDIN_: true/false
|
||||
*
|
||||
* @param array $data data passed to plugin
|
||||
*
|
||||
* @return array altered $data.
|
||||
*/
|
||||
function hook_demo_plugin_render_includes($data)
|
||||
{
|
||||
// List of plugin's CSS files.
|
||||
// Note that you just need to specify CSS path.
|
||||
$data['css_files'][] = PluginManager::$PLUGINS_PATH . '/demo_plugin/custom_demo.css';
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook render_footer.
|
||||
* Executed on every page redering.
|
||||
*
|
||||
* Template placeholders:
|
||||
* - text
|
||||
* - js_files
|
||||
*
|
||||
* Data:
|
||||
* - _PAGE_: current page
|
||||
* - _LOGGEDIN_: true/false
|
||||
*
|
||||
* @param array $data data passed to plugin
|
||||
*
|
||||
* @return array altered $data.
|
||||
*/
|
||||
function hook_demo_plugin_render_footer($data)
|
||||
{
|
||||
// footer text
|
||||
$data['text'][] = 'Shaarli is now enhanced by the awesome demo_plugin.';
|
||||
|
||||
// List of plugin's JS files.
|
||||
// Note that you just need to specify CSS path.
|
||||
$data['js_files'][] = PluginManager::$PLUGINS_PATH . '/demo_plugin/demo_plugin.js';
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/*
|
||||
* SPECIFIC PAGES
|
||||
*/
|
||||
|
||||
/**
|
||||
* Hook render_linklist.
|
||||
*
|
||||
* Template placeholders:
|
||||
* - action_plugin: next to 'private only' button.
|
||||
* - plugin_start_zone: page start
|
||||
* - plugin_end_zone: page end
|
||||
* - link_plugin: icons below each links.
|
||||
*
|
||||
* Data:
|
||||
* - _LOGGEDIN_: true/false
|
||||
*
|
||||
* @param array $data data passed to plugin
|
||||
*
|
||||
* @return array altered $data.
|
||||
*/
|
||||
function hook_demo_plugin_render_linklist($data)
|
||||
{
|
||||
// action_plugin
|
||||
$data['action_plugin'][] = '<div class="upper_plugin_demo"><a href="?up" title="Uppercase!">←</a></div>';
|
||||
|
||||
if (isset($_GET['up'])) {
|
||||
// Manipulate link data
|
||||
foreach ($data['links'] as &$value) {
|
||||
$value['description'] = strtoupper($value['description']);
|
||||
$value['title'] = strtoupper($value['title']);
|
||||
}
|
||||
}
|
||||
|
||||
// link_plugin (for each link)
|
||||
foreach ($data['links'] as &$value) {
|
||||
$value['link_plugin'][] = ' DEMO \o/';
|
||||
}
|
||||
|
||||
// plugin_start_zone
|
||||
$data['plugin_start_zone'][] = '<center>BEFORE</center>';
|
||||
// plugin_start_zone
|
||||
$data['plugin_end_zone'][] = '<center>AFTER</center>';
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook render_editlink.
|
||||
*
|
||||
* Template placeholders:
|
||||
* - field_plugin: add link fields after tags.
|
||||
*
|
||||
* @param array $data data passed to plugin
|
||||
*
|
||||
* @return array altered $data.
|
||||
*/
|
||||
function hook_demo_plugin_render_editlink($data)
|
||||
{
|
||||
// Load HTML into a string
|
||||
$html = file_get_contents(PluginManager::$PLUGINS_PATH .'/demo_plugin/field.html');
|
||||
|
||||
// replace value in HTML if it exists in $data
|
||||
if (!empty($data['link']['stuff'])) {
|
||||
$html = sprintf($html, $data['link']['stuff']);
|
||||
} else {
|
||||
$html = sprintf($html, '');
|
||||
}
|
||||
|
||||
// field_plugin
|
||||
$data['edit_link_plugin'][] = $html;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook render_tools.
|
||||
*
|
||||
* Template placeholders:
|
||||
* - tools_plugin: after other tools.
|
||||
*
|
||||
* @param array $data data passed to plugin
|
||||
*
|
||||
* @return array altered $data.
|
||||
*/
|
||||
function hook_demo_plugin_render_tools($data)
|
||||
{
|
||||
// field_plugin
|
||||
$data['tools_plugin'][] = 'tools_plugin';
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook render_picwall.
|
||||
*
|
||||
* Template placeholders:
|
||||
* - plugin_start_zone: page start.
|
||||
* - plugin_end_zone: page end.
|
||||
*
|
||||
* Data:
|
||||
* - _LOGGEDIN_: true/false
|
||||
*
|
||||
* @param array $data data passed to plugin
|
||||
*
|
||||
* @return array altered $data.
|
||||
*/
|
||||
function hook_demo_plugin_render_picwall($data)
|
||||
{
|
||||
// plugin_start_zone
|
||||
$data['plugin_start_zone'][] = '<center>BEFORE</center>';
|
||||
// plugin_end_zone
|
||||
$data['plugin_end_zone'][] = '<center>AFTER</center>';
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook render_tagcloud.
|
||||
*
|
||||
* Template placeholders:
|
||||
* - plugin_start_zone: page start.
|
||||
* - plugin_end_zone: page end.
|
||||
*
|
||||
* Data:
|
||||
* - _LOGGEDIN_: true/false
|
||||
*
|
||||
* @param array $data data passed to plugin
|
||||
*
|
||||
* @return array altered $data.
|
||||
*/
|
||||
function hook_demo_plugin_render_tagcloud($data)
|
||||
{
|
||||
// plugin_start_zone
|
||||
$data['plugin_start_zone'][] = '<center>BEFORE</center>';
|
||||
// plugin_end_zone
|
||||
$data['plugin_end_zone'][] = '<center>AFTER</center>';
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook render_daily.
|
||||
*
|
||||
* Template placeholders:
|
||||
* - plugin_start_zone: page start.
|
||||
* - plugin_end_zone: page end.
|
||||
*
|
||||
* Data:
|
||||
* - _LOGGEDIN_: true/false
|
||||
*
|
||||
* @param array $data data passed to plugin
|
||||
*
|
||||
* @return array altered $data.
|
||||
*/
|
||||
function hook_demo_plugin_render_daily($data)
|
||||
{
|
||||
// plugin_start_zone
|
||||
$data['plugin_start_zone'][] = '<center>BEFORE</center>';
|
||||
// plugin_end_zone
|
||||
$data['plugin_end_zone'][] = '<center>AFTER</center>';
|
||||
|
||||
|
||||
// Manipulate columns data
|
||||
foreach ($data['cols'] as &$value) {
|
||||
foreach ($value as &$value2) {
|
||||
$value2['formatedDescription'] .= ' ಠ_ಠ';
|
||||
}
|
||||
}
|
||||
|
||||
// Add plugin content at the end of each link
|
||||
foreach ($data['cols'] as &$value) {
|
||||
foreach ($value as &$value2) {
|
||||
$value2['link_plugin'][] = 'DEMO';
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/*
|
||||
* DATA SAVING HOOK.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Hook savelink.
|
||||
*
|
||||
* Triggered when a link is save (new or edit).
|
||||
* All new links now contain a 'stuff' value.
|
||||
*
|
||||
* @param array $data contains the new link data.
|
||||
*
|
||||
* @return array altered $data.
|
||||
*/
|
||||
function hook_demo_plugin_save_link($data)
|
||||
{
|
||||
|
||||
// Save stuff added in editlink field
|
||||
if (!empty($_POST['lf_stuff'])) {
|
||||
$data['stuff'] = escape($_POST['lf_stuff']);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook delete_link.
|
||||
*
|
||||
* Triggered when a link is deleted.
|
||||
*
|
||||
* @param array $data contains the link to be deleted.
|
||||
*
|
||||
* @return array altered data.
|
||||
*/
|
||||
function hook_demo_plugin_delete_link($data)
|
||||
{
|
||||
if (strpos($data['url'], 'youtube.com') !== false) {
|
||||
exit('You can not delete a YouTube link. Don\'t ask.');
|
||||
}
|
||||
}
|
2
plugins/demo_plugin/field.html
Executable file
2
plugins/demo_plugin/field.html
Executable file
|
@ -0,0 +1,2 @@
|
|||
<label for="lf_stuff"><i>Demo Stuff</i></label><br>
|
||||
<input type="text" name="lf_stuff" id="lf_stuff" value="%s" class="lf_input"><br>
|
71
plugins/playvideos/README.md
Normal file
71
plugins/playvideos/README.md
Normal file
|
@ -0,0 +1,71 @@
|
|||
### ► Play Videos plugin for Shaarli
|
||||
This plugin adds a `► Play Videos` button to [Shaarli](https://github.com/shaarli/Shaarli)'s toolbar. Click this button to play all videos on the page in an overlay HTML5 player. Nice for continuous stream of music, documentaries, talks...
|
||||
|
||||
This uses code from https://zaius.github.io/youtube_playlist/ and is currently only compatible with Youtube videos.
|
||||
|
||||
![](https://cdn.mediacru.sh/D_izf0zjAtxy.png)
|
||||
|
||||
#### Installation and setup
|
||||
Place the files in the `tpl/plugins/playvideos/` directory of your Shaarli.
|
||||
This is a default Shaarli plugin, you just have to enable it.
|
||||
|
||||
To enable the plugin, add `playvideos` to the `TOOLBAR_PLUGINS` config option in your `index.php` or `data/options.php`. Example:
|
||||
|
||||
$GLOBALS['config']['TOOLBAR_PLUGINS'] = array('aplugins', 'anotherone', 'playvideos');
|
||||
|
||||
#### Troubleshooting
|
||||
If your server has [Content Security Policy](http://content-security-policy.com/) headers enabled, this may prevent the script from loading fully. You should relax the CSP in your server settings. Example CSP rule for apache2:
|
||||
`Header set Content-Security-Policy "script-src 'self' 'unsafe-inline' https://www.youtube.com https://s.ytimg.com 'unsafe-eval'"`
|
||||
|
||||
### License
|
||||
```
|
||||
File: youtube_playlist.js
|
||||
Copyright: (c) 2010-2014, David Kelso <david@kelso.id.au>
|
||||
License: The ISC License (http://opensource.org/licenses/ISC)
|
||||
|
||||
Files: jquery*.js
|
||||
License: MIT License (http://opensource.org/licenses/MIT)
|
||||
Copyright: (C) jQuery Foundation and other contributors, https://jquery.com/download/
|
||||
|
||||
-----------------------------------------------------
|
||||
|
||||
The ISC License (http://opensource.org/licenses/ISC)
|
||||
|
||||
Copyright (c) 2010-2014, David Kelso (david at kelso dot id dot au)
|
||||
Copyright (c) 2010-2014, nodiscc (nodiscc at gmail dot com)
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE
|
||||
|
||||
----------------------------------------------------
|
||||
MIT LICENSE
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
----------------------------------------------------
|
||||
```
|
10346
plugins/playvideos/jquery-1.11.2.js
vendored
Normal file
10346
plugins/playvideos/jquery-1.11.2.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
BIN
plugins/playvideos/jquery-1.11.2.min.js
vendored
Normal file
BIN
plugins/playvideos/jquery-1.11.2.min.js
vendored
Normal file
Binary file not shown.
1
plugins/playvideos/playvideos.html
Normal file
1
plugins/playvideos/playvideos.html
Normal file
|
@ -0,0 +1 @@
|
|||
<a href="#" id="playvideos">► Play Videos</a>
|
40
plugins/playvideos/playvideos.php
Normal file
40
plugins/playvideos/playvideos.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
/**
|
||||
* Plugin PlayVideos
|
||||
*
|
||||
* Add a button in the toolbar allowing to watch all videos.
|
||||
* Note: this plugin adds jQuery.
|
||||
*/
|
||||
|
||||
/**
|
||||
* When linklist is displayed, add play videos to header's toolbar.
|
||||
*
|
||||
* @param array $data - header data.
|
||||
*
|
||||
* @return mixed - header data with playvideos toolbar item.
|
||||
*/
|
||||
function hook_playvideos_render_header($data)
|
||||
{
|
||||
if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) {
|
||||
$data['buttons_toolbar'][] = file_get_contents(PluginManager::$PLUGINS_PATH . '/playvideos/playvideos.html');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* When linklist is displayed, include playvideos JS files.
|
||||
*
|
||||
* @param array $data - footer data.
|
||||
*
|
||||
* @return mixed - footer data with playvideos JS files added.
|
||||
*/
|
||||
function hook_playvideos_render_footer($data)
|
||||
{
|
||||
if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) {
|
||||
$data['js_files'][] = PluginManager::$PLUGINS_PATH . '/playvideos/jquery-1.11.2.min.js';
|
||||
$data['js_files'][] = PluginManager::$PLUGINS_PATH . '/playvideos/youtube_playlist.js';
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
196
plugins/playvideos/youtube_playlist.js
Normal file
196
plugins/playvideos/youtube_playlist.js
Normal file
|
@ -0,0 +1,196 @@
|
|||
var run_playideos = (function () {
|
||||
var e, n, t, o, r, i = [].indexOf || function (e) {
|
||||
for (var n = 0, t = this.length; n < t; n++) {
|
||||
if (n in this && this[n] === e) return n
|
||||
}
|
||||
return -1
|
||||
};
|
||||
if (!window.console) {
|
||||
window.console = {
|
||||
log: function () {}
|
||||
}
|
||||
}
|
||||
n = {
|
||||
shadow: {
|
||||
"background-color": "black",
|
||||
position: "fixed",
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
"z-index": 1e3,
|
||||
opacity: .8
|
||||
},
|
||||
player_box: {
|
||||
position: "fixed",
|
||||
left: "50%",
|
||||
top: "50%",
|
||||
width: 640,
|
||||
height: 480,
|
||||
"margin-left": -320,
|
||||
"margin-top": -240,
|
||||
"z-index": 1001
|
||||
},
|
||||
prev_button: {
|
||||
"float": "left"
|
||||
},
|
||||
next_button: {
|
||||
"float": "right"
|
||||
}
|
||||
};
|
||||
t = function (e, n) {
|
||||
var t, o, r;
|
||||
r = document.createElement("script");
|
||||
r.src = e;
|
||||
o = document.getElementsByTagName("head")[0];
|
||||
t = false;
|
||||
r.onload = r.onreadystatechange = function () {
|
||||
var e, i;
|
||||
e = !this.readyState || (i = this.readyState) === "loaded" || i === "complete";
|
||||
if (!t && e) {
|
||||
t = true;
|
||||
n();
|
||||
r.onload = r.onreadystatechange = null;
|
||||
return o.removeChild(r)
|
||||
}
|
||||
};
|
||||
return o.appendChild(r)
|
||||
};
|
||||
e = function (e) {
|
||||
var t, o, r, a, u, l, d, c, f, p, s, y, h, g, v, m, w;
|
||||
e.getScript("//www.youtube.com/iframe_api");
|
||||
d = [];
|
||||
w = new RegExp("https?://(www.)?youtube.com/");
|
||||
e('a[href^="http"]').each(function () {
|
||||
var n;
|
||||
if (!e(this).attr("href").match(w)) {
|
||||
return
|
||||
}
|
||||
n = this.href.replace(/^.*v=/, "").replace(/\&.*$/, "");
|
||||
if (i.call(d, n) < 0) {
|
||||
return d.push(n)
|
||||
}
|
||||
});
|
||||
console.log("video ids", d);
|
||||
c = 0;
|
||||
y = null;
|
||||
g = "playlist_player";
|
||||
f = function () {
|
||||
console.log("Playing", c, d[c]);
|
||||
return y.loadVideoById(d[c])
|
||||
};
|
||||
p = function () {
|
||||
c++;
|
||||
if (c >= d.length) {
|
||||
c -= d.length
|
||||
}
|
||||
return f()
|
||||
};
|
||||
s = function () {
|
||||
c--;
|
||||
if (c < 0) {
|
||||
c += d.length
|
||||
}
|
||||
return f()
|
||||
};
|
||||
l = function () {
|
||||
e("#shadow, #player_box").remove();
|
||||
return e(document).unbind("keyup.player")
|
||||
};
|
||||
e(document).bind("keyup.player", function (e) {
|
||||
if (e.keyCode === 27) {
|
||||
l()
|
||||
}
|
||||
if (e.keyCode === 39) {
|
||||
p()
|
||||
}
|
||||
if (e.keyCode === 37) {
|
||||
return s()
|
||||
}
|
||||
});
|
||||
u = e("<div />", {
|
||||
id: "shadow",
|
||||
css: n.shadow,
|
||||
click: l
|
||||
});
|
||||
r = e("<div />", {
|
||||
id: "player_box",
|
||||
css: n.player_box
|
||||
});
|
||||
o = e("<div />", {
|
||||
id: g
|
||||
});
|
||||
a = e("<a />", {
|
||||
href: "javascript:;",
|
||||
text: "previous",
|
||||
css: n.prev_button,
|
||||
click: s
|
||||
});
|
||||
t = e("<a />", {
|
||||
href: "javascript:;",
|
||||
text: "next",
|
||||
css: n.next_button,
|
||||
click: p
|
||||
});
|
||||
r.append(o).append(a).append(t);
|
||||
e("body").append(u).append(r);
|
||||
v = function (e) {
|
||||
console.log("player ready");
|
||||
return e.target.playVideo()
|
||||
};
|
||||
h = function (e) {
|
||||
var n, t;
|
||||
n = {
|
||||
2: "invalid video id",
|
||||
5: "video not supported in html5",
|
||||
100: "video removed or private",
|
||||
101: "video not embedable",
|
||||
150: "video not embedable"
|
||||
};
|
||||
t = n[e.data] || "unknown error";
|
||||
console.log("Error", t);
|
||||
d.splice(c, 1);
|
||||
if (c >= d.length) {
|
||||
c = 0
|
||||
}
|
||||
return f()
|
||||
};
|
||||
m = function (e) {
|
||||
if (e.data === YT.PlayerState.ENDED) {
|
||||
return p()
|
||||
}
|
||||
};
|
||||
return window.onYouTubeIframeAPIReady = function () {
|
||||
return y = new YT.Player(g, {
|
||||
height: "390",
|
||||
width: "640",
|
||||
videoId: d[0],
|
||||
events: {
|
||||
onReady: v,
|
||||
onError: h,
|
||||
onStateChange: m
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
o = false;
|
||||
if (typeof jQuery !== "undefined" && jQuery !== null && jQuery.fn && jQuery.fn.jquery) {
|
||||
r = jQuery.fn.jquery.split(".");
|
||||
if (r.length === 3 && parseInt(r[1]) > 3) {
|
||||
console.log("using in page jquery version", jQuery.fn.jquery);
|
||||
e(jQuery);
|
||||
o = true
|
||||
}
|
||||
}
|
||||
if (!o) {
|
||||
t("plugins/playvideos/jquery-1.11.2.min.js", function () {
|
||||
return e(jQuery.noConflict(true))
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
var input = document.querySelector('#playvideos');
|
||||
input.addEventListener('click', function()
|
||||
{
|
||||
run_playideos();
|
||||
});
|
5
plugins/qrcode/qrcode.html
Normal file
5
plugins/qrcode/qrcode.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<div class="linkqrcode">
|
||||
<a href="http://qrfree.kaywa.com/?l=1&s=8&d=%s" onclick="showQrCode(this); return false;" class="qrcode" data-permalink="%s">
|
||||
<img src="%s/qrcode/qrcode.png" width="13" height="13" title="QR-Code">
|
||||
</a>
|
||||
</div>
|
41
plugins/qrcode/qrcode.php
Normal file
41
plugins/qrcode/qrcode.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
/**
|
||||
* Plugin qrcode
|
||||
* Add QRCode containing URL for each links.
|
||||
* Display a QRCode icon in link list.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add qrcode icon to link_plugin when rendering linklist.
|
||||
*
|
||||
* @param array $data - linklist data.
|
||||
*
|
||||
* @return mixed - linklist data with qrcode plugin.
|
||||
*/
|
||||
function hook_qrcode_render_linklist($data)
|
||||
{
|
||||
$qrcode_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/qrcode/qrcode.html');
|
||||
|
||||
foreach ($data['links'] as &$value) {
|
||||
$qrcode = sprintf($qrcode_html, $value['url'], $value['url'], PluginManager::$PLUGINS_PATH);
|
||||
$value['link_plugin'][] = $qrcode;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* When linklist is displayed, include qrcode JS files.
|
||||
*
|
||||
* @param array $data - footer data.
|
||||
*
|
||||
* @return mixed - footer data with qrcode JS files added.
|
||||
*/
|
||||
function hook_qrcode_render_footer($data)
|
||||
{
|
||||
if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) {
|
||||
$data['js_files'][] = PluginManager::$PLUGINS_PATH . '/qrcode/shaarli-qrcode.js';
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 321 B |
56
plugins/qrcode/shaarli-qrcode.js
Normal file
56
plugins/qrcode/shaarli-qrcode.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Show the QR-Code of a permalink (when the QR-Code icon is clicked).
|
||||
function showQrCode(caller,loading)
|
||||
{
|
||||
// Dynamic javascript lib loading: We only load qr.js if the QR code icon is clicked:
|
||||
if (typeof(qr) == 'undefined') // Load qr.js only if not present.
|
||||
{
|
||||
if (!loading) // If javascript lib is still loading, do not append script to body.
|
||||
{
|
||||
var element = document.createElement("script");
|
||||
element.src = "plugins/qrcode/qr-1.1.3.min.js";
|
||||
document.body.appendChild(element);
|
||||
}
|
||||
setTimeout(function() { showQrCode(caller,true);}, 200); // Retry in 200 milliseconds.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove previous qrcode if present.
|
||||
removeQrcode();
|
||||
|
||||
// Build the div which contains the QR-Code:
|
||||
var element = document.createElement('div');
|
||||
element.id="permalinkQrcode";
|
||||
|
||||
// Make QR-Code div commit sepuku when clicked:
|
||||
if ( element.attachEvent ){
|
||||
element.attachEvent('onclick', 'this.parentNode.removeChild(this);' );
|
||||
|
||||
} else {
|
||||
// Damn IE
|
||||
element.setAttribute('onclick', 'this.parentNode.removeChild(this);' );
|
||||
}
|
||||
|
||||
// Build the QR-Code:
|
||||
var image = qr.image({size: 8,value: caller.dataset.permalink});
|
||||
if (image)
|
||||
{
|
||||
element.appendChild(image);
|
||||
element.innerHTML += "<br>Click to close";
|
||||
caller.parentNode.appendChild(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
element.innerHTML = "Your browser does not seem to be HTML5 compatible.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove any displayed QR-Code
|
||||
function removeQrcode()
|
||||
{
|
||||
var elem = document.getElementById("permalinkQrcode");
|
||||
if (elem) {
|
||||
elem.parentNode.removeChild(elem);
|
||||
}
|
||||
return false;
|
||||
}
|
BIN
plugins/readityourself/book-open.png
Normal file
BIN
plugins/readityourself/book-open.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 568 B |
3
plugins/readityourself/config.php.dist
Normal file
3
plugins/readityourself/config.php.dist
Normal file
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
|
||||
$GLOBALS['plugins']['READITYOUSELF_URL'] = 'http://someurl.com';
|
1
plugins/readityourself/readityourself.html
Normal file
1
plugins/readityourself/readityourself.html
Normal file
|
@ -0,0 +1 @@
|
|||
<span><a href="%s?url=%s"><img width="13" height="13" src="%s/readityourself/book-open.png" title="Read with Readityourself" /></a></span>
|
43
plugins/readityourself/readityourself.php
Normal file
43
plugins/readityourself/readityourself.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Plugin readityourself
|
||||
*/
|
||||
|
||||
// If we're talking about https://github.com/memiks/readityourself
|
||||
// it seems kinda dead.
|
||||
// Not tested.
|
||||
|
||||
// don't raise unnecessary warnings
|
||||
if (is_file(PluginManager::$PLUGINS_PATH . '/readityourself/config.php')) {
|
||||
include PluginManager::$PLUGINS_PATH . '/readityourself/config.php';
|
||||
}
|
||||
|
||||
if (!isset($GLOBALS['plugins']['READITYOUSELF_URL'])) {
|
||||
$GLOBALS['plugins']['errors'][] = 'Readityourself plugin error: '.
|
||||
'Please define "$GLOBALS[\'plugins\'][\'READITYOUSELF_URL\']" '.
|
||||
'in "plugins/readityourself/config.php" or in your Shaarli config.php file.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add readityourself icon to link_plugin when rendering linklist.
|
||||
*
|
||||
* @param mixed $data - linklist data.
|
||||
*
|
||||
* @return mixed - linklist data with readityourself plugin.
|
||||
*/
|
||||
function hook_readityourself_render_linklist($data)
|
||||
{
|
||||
if (!isset($GLOBALS['plugins']['READITYOUSELF_URL'])) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$readityourself_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/readityourself/readityourself.html');
|
||||
|
||||
foreach ($data['links'] as &$value) {
|
||||
$readityourself = sprintf($readityourself_html, $GLOBALS['plugins']['READITYOUSELF_URL'], $value['url'], PluginManager::$PLUGINS_PATH);
|
||||
$value['link_plugin'][] = $readityourself;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
29
plugins/wallabag/README.md
Normal file
29
plugins/wallabag/README.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
## Save to Wallabag plugin for Shaarli
|
||||
|
||||
For each link in your Shaarli, adds a button to save the target page in your [wallabag](https://www.wallabag.org/).
|
||||
|
||||
### Installation/configuration
|
||||
Clone this repository inside your `tpl/plugins/` directory, or download the archive and unpack it there.
|
||||
The directory structure should look like:
|
||||
|
||||
```
|
||||
└── tpl
|
||||
└── plugins
|
||||
└── wallabag
|
||||
├── README.md
|
||||
├── wallabag.html
|
||||
└── wallabag.png
|
||||
```
|
||||
|
||||
To enable the plugin, add `'wallabag'` to your list of enabled plugins in `data/options.php` (`PLUGINS` array)
|
||||
. This should look like:
|
||||
|
||||
```
|
||||
$GLOBALS['config']['PLUGINS'] = array('qrcode', 'any_other_plugin', 'wallabag')
|
||||
```
|
||||
|
||||
Then, set the `WALLABAG_URL` variable in `data/options.php` pointing to your wallabag URL. Example:
|
||||
|
||||
```
|
||||
$GLOBALS['config']['WALLABAG_URL'] = 'http://demo.wallabag.org' ; //Base URL of your wallabag installation
|
||||
```
|
3
plugins/wallabag/config.php.dist
Normal file
3
plugins/wallabag/config.php.dist
Normal file
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
|
||||
$GLOBALS['plugins']['WALLABAG_URL'] = 'https://demo.wallabag.org/';
|
1
plugins/wallabag/wallabag.html
Normal file
1
plugins/wallabag/wallabag.html
Normal file
|
@ -0,0 +1 @@
|
|||
<span><a href="%s/?plainurl=%s" target="_blank"><img width="13" height="13" src="%s/wallabag/wallabag.png" title="Save to wallabag" /></a></span>
|
39
plugins/wallabag/wallabag.php
Normal file
39
plugins/wallabag/wallabag.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Plugin Wallabag.
|
||||
*/
|
||||
|
||||
// don't raise unnecessary warnings
|
||||
if (is_file(PluginManager::$PLUGINS_PATH . '/wallabag/config.php')) {
|
||||
include PluginManager::$PLUGINS_PATH . '/wallabag/config.php';
|
||||
}
|
||||
|
||||
if (!isset($GLOBALS['plugins']['WALLABAG_URL'])) {
|
||||
$GLOBALS['plugins']['errors'][] = 'Wallabag plugin error: '.
|
||||
'Please define "$GLOBALS[\'plugins\'][\'WALLABAG_URL\']" '.
|
||||
'in "plugins/wallabag/config.php" or in your Shaarli config.php file.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add wallabag icon to link_plugin when rendering linklist.
|
||||
*
|
||||
* @param mixed $data - linklist data.
|
||||
*
|
||||
* @return mixed - linklist data with wallabag plugin.
|
||||
*/
|
||||
function hook_wallabag_render_linklist($data)
|
||||
{
|
||||
if (!isset($GLOBALS['plugins']['WALLABAG_URL'])) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$wallabag_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/wallabag/wallabag.html');
|
||||
|
||||
foreach ($data['links'] as &$value) {
|
||||
$wallabag = sprintf($wallabag_html, $GLOBALS['plugins']['WALLABAG_URL'], $value['url'], PluginManager::$PLUGINS_PATH);
|
||||
$value['link_plugin'][] = $wallabag;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
BIN
plugins/wallabag/wallabag.png
Normal file
BIN
plugins/wallabag/wallabag.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 369 B |
354
tests/ConfigTest.php
Executable file → Normal file
354
tests/ConfigTest.php
Executable file → Normal file
|
@ -1,177 +1,177 @@
|
|||
<?php
|
||||
/**
|
||||
* Config' tests
|
||||
*/
|
||||
|
||||
require_once 'application/Config.php';
|
||||
|
||||
/**
|
||||
* Unitary tests for Shaarli config related functions
|
||||
*/
|
||||
class ConfigTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
// Configuration input set.
|
||||
private static $_configFields;
|
||||
|
||||
/**
|
||||
* Executed before each test.
|
||||
*/
|
||||
public function setUp()
|
||||
{
|
||||
self::$_configFields = array(
|
||||
'login' => 'login',
|
||||
'hash' => 'hash',
|
||||
'salt' => 'salt',
|
||||
'timezone' => 'Europe/Paris',
|
||||
'title' => 'title',
|
||||
'titleLink' => 'titleLink',
|
||||
'redirector' => '',
|
||||
'disablesessionprotection' => false,
|
||||
'privateLinkByDefault' => false,
|
||||
'config' => array(
|
||||
'CONFIG_FILE' => 'tests/config.php',
|
||||
'DATADIR' => 'tests',
|
||||
'config1' => 'config1data',
|
||||
'config2' => 'config2data',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed after each test.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function tearDown()
|
||||
{
|
||||
if (is_file(self::$_configFields['config']['CONFIG_FILE'])) {
|
||||
unlink(self::$_configFields['config']['CONFIG_FILE']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test writeConfig function, valid use case, while being logged in.
|
||||
*/
|
||||
public function testWriteConfig()
|
||||
{
|
||||
writeConfig(self::$_configFields, true);
|
||||
|
||||
include self::$_configFields['config']['CONFIG_FILE'];
|
||||
$this->assertEquals(self::$_configFields['login'], $GLOBALS['login']);
|
||||
$this->assertEquals(self::$_configFields['hash'], $GLOBALS['hash']);
|
||||
$this->assertEquals(self::$_configFields['salt'], $GLOBALS['salt']);
|
||||
$this->assertEquals(self::$_configFields['timezone'], $GLOBALS['timezone']);
|
||||
$this->assertEquals(self::$_configFields['title'], $GLOBALS['title']);
|
||||
$this->assertEquals(self::$_configFields['titleLink'], $GLOBALS['titleLink']);
|
||||
$this->assertEquals(self::$_configFields['redirector'], $GLOBALS['redirector']);
|
||||
$this->assertEquals(self::$_configFields['disablesessionprotection'], $GLOBALS['disablesessionprotection']);
|
||||
$this->assertEquals(self::$_configFields['privateLinkByDefault'], $GLOBALS['privateLinkByDefault']);
|
||||
$this->assertEquals(self::$_configFields['config']['config1'], $GLOBALS['config']['config1']);
|
||||
$this->assertEquals(self::$_configFields['config']['config2'], $GLOBALS['config']['config2']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test writeConfig option while logged in:
|
||||
* 1. init fields.
|
||||
* 2. update fields, add new sub config, add new root config.
|
||||
* 3. rewrite config.
|
||||
* 4. check result.
|
||||
*/
|
||||
public function testWriteConfigFieldUpdate()
|
||||
{
|
||||
writeConfig(self::$_configFields, true);
|
||||
self::$_configFields['title'] = 'ok';
|
||||
self::$_configFields['config']['config1'] = 'ok';
|
||||
self::$_configFields['config']['config_new'] = 'ok';
|
||||
self::$_configFields['new'] = 'should not be saved';
|
||||
writeConfig(self::$_configFields, true);
|
||||
|
||||
include self::$_configFields['config']['CONFIG_FILE'];
|
||||
$this->assertEquals('ok', $GLOBALS['title']);
|
||||
$this->assertEquals('ok', $GLOBALS['config']['config1']);
|
||||
$this->assertEquals('ok', $GLOBALS['config']['config_new']);
|
||||
$this->assertFalse(isset($GLOBALS['new']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test writeConfig function with an empty array.
|
||||
*
|
||||
* @expectedException MissingFieldConfigException
|
||||
*/
|
||||
public function testWriteConfigEmpty()
|
||||
{
|
||||
writeConfig(array(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test writeConfig function with a missing mandatory field.
|
||||
*
|
||||
* @expectedException MissingFieldConfigException
|
||||
*/
|
||||
public function testWriteConfigMissingField()
|
||||
{
|
||||
unset(self::$_configFields['login']);
|
||||
writeConfig(self::$_configFields, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test writeConfig function while being logged out, and there is no config file existing.
|
||||
*/
|
||||
public function testWriteConfigLoggedOutNoFile()
|
||||
{
|
||||
writeConfig(self::$_configFields, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test writeConfig function while being logged out, and a config file already exists.
|
||||
*
|
||||
* @expectedException UnauthorizedConfigException
|
||||
*/
|
||||
public function testWriteConfigLoggedOutWithFile()
|
||||
{
|
||||
file_put_contents(self::$_configFields['config']['CONFIG_FILE'], '');
|
||||
writeConfig(self::$_configFields, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test mergeDeprecatedConfig while being logged in:
|
||||
* 1. init a config file.
|
||||
* 2. init a options.php file with update value.
|
||||
* 3. merge.
|
||||
* 4. check updated value in config file.
|
||||
*/
|
||||
public function testMergeDeprecatedConfig()
|
||||
{
|
||||
// init
|
||||
writeConfig(self::$_configFields, true);
|
||||
$configCopy = self::$_configFields;
|
||||
$invert = !$configCopy['privateLinkByDefault'];
|
||||
$configCopy['privateLinkByDefault'] = $invert;
|
||||
|
||||
// Use writeConfig to create a options.php
|
||||
$configCopy['config']['CONFIG_FILE'] = 'tests/options.php';
|
||||
writeConfig($configCopy, true);
|
||||
|
||||
$this->assertTrue(is_file($configCopy['config']['CONFIG_FILE']));
|
||||
|
||||
// merge configs
|
||||
mergeDeprecatedConfig(self::$_configFields, true);
|
||||
|
||||
// make sure updated field is changed
|
||||
include self::$_configFields['config']['CONFIG_FILE'];
|
||||
$this->assertEquals($invert, $GLOBALS['privateLinkByDefault']);
|
||||
$this->assertFalse(is_file($configCopy['config']['CONFIG_FILE']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test mergeDeprecatedConfig while being logged in without options file.
|
||||
*/
|
||||
public function testMergeDeprecatedConfigNoFile()
|
||||
{
|
||||
writeConfig(self::$_configFields, true);
|
||||
mergeDeprecatedConfig(self::$_configFields, true);
|
||||
|
||||
include self::$_configFields['config']['CONFIG_FILE'];
|
||||
$this->assertEquals(self::$_configFields['login'], $GLOBALS['login']);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
/**
|
||||
* Config' tests
|
||||
*/
|
||||
|
||||
require_once 'application/Config.php';
|
||||
|
||||
/**
|
||||
* Unitary tests for Shaarli config related functions
|
||||
*/
|
||||
class ConfigTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
// Configuration input set.
|
||||
private static $configFields;
|
||||
|
||||
/**
|
||||
* Executed before each test.
|
||||
*/
|
||||
public function setUp()
|
||||
{
|
||||
self::$configFields = array(
|
||||
'login' => 'login',
|
||||
'hash' => 'hash',
|
||||
'salt' => 'salt',
|
||||
'timezone' => 'Europe/Paris',
|
||||
'title' => 'title',
|
||||
'titleLink' => 'titleLink',
|
||||
'redirector' => '',
|
||||
'disablesessionprotection' => false,
|
||||
'privateLinkByDefault' => false,
|
||||
'config' => array(
|
||||
'CONFIG_FILE' => 'tests/config.php',
|
||||
'DATADIR' => 'tests',
|
||||
'config1' => 'config1data',
|
||||
'config2' => 'config2data',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed after each test.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function tearDown()
|
||||
{
|
||||
if (is_file(self::$configFields['config']['CONFIG_FILE'])) {
|
||||
unlink(self::$configFields['config']['CONFIG_FILE']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test writeConfig function, valid use case, while being logged in.
|
||||
*/
|
||||
public function testWriteConfig()
|
||||
{
|
||||
writeConfig(self::$configFields, true);
|
||||
|
||||
include self::$configFields['config']['CONFIG_FILE'];
|
||||
$this->assertEquals(self::$configFields['login'], $GLOBALS['login']);
|
||||
$this->assertEquals(self::$configFields['hash'], $GLOBALS['hash']);
|
||||
$this->assertEquals(self::$configFields['salt'], $GLOBALS['salt']);
|
||||
$this->assertEquals(self::$configFields['timezone'], $GLOBALS['timezone']);
|
||||
$this->assertEquals(self::$configFields['title'], $GLOBALS['title']);
|
||||
$this->assertEquals(self::$configFields['titleLink'], $GLOBALS['titleLink']);
|
||||
$this->assertEquals(self::$configFields['redirector'], $GLOBALS['redirector']);
|
||||
$this->assertEquals(self::$configFields['disablesessionprotection'], $GLOBALS['disablesessionprotection']);
|
||||
$this->assertEquals(self::$configFields['privateLinkByDefault'], $GLOBALS['privateLinkByDefault']);
|
||||
$this->assertEquals(self::$configFields['config']['config1'], $GLOBALS['config']['config1']);
|
||||
$this->assertEquals(self::$configFields['config']['config2'], $GLOBALS['config']['config2']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test writeConfig option while logged in:
|
||||
* 1. init fields.
|
||||
* 2. update fields, add new sub config, add new root config.
|
||||
* 3. rewrite config.
|
||||
* 4. check result.
|
||||
*/
|
||||
public function testWriteConfigFieldUpdate()
|
||||
{
|
||||
writeConfig(self::$configFields, true);
|
||||
self::$configFields['title'] = 'ok';
|
||||
self::$configFields['config']['config1'] = 'ok';
|
||||
self::$configFields['config']['config_new'] = 'ok';
|
||||
self::$configFields['new'] = 'should not be saved';
|
||||
writeConfig(self::$configFields, true);
|
||||
|
||||
include self::$configFields['config']['CONFIG_FILE'];
|
||||
$this->assertEquals('ok', $GLOBALS['title']);
|
||||
$this->assertEquals('ok', $GLOBALS['config']['config1']);
|
||||
$this->assertEquals('ok', $GLOBALS['config']['config_new']);
|
||||
$this->assertFalse(isset($GLOBALS['new']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test writeConfig function with an empty array.
|
||||
*
|
||||
* @expectedException MissingFieldConfigException
|
||||
*/
|
||||
public function testWriteConfigEmpty()
|
||||
{
|
||||
writeConfig(array(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test writeConfig function with a missing mandatory field.
|
||||
*
|
||||
* @expectedException MissingFieldConfigException
|
||||
*/
|
||||
public function testWriteConfigMissingField()
|
||||
{
|
||||
unset(self::$configFields['login']);
|
||||
writeConfig(self::$configFields, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test writeConfig function while being logged out, and there is no config file existing.
|
||||
*/
|
||||
public function testWriteConfigLoggedOutNoFile()
|
||||
{
|
||||
writeConfig(self::$configFields, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test writeConfig function while being logged out, and a config file already exists.
|
||||
*
|
||||
* @expectedException UnauthorizedConfigException
|
||||
*/
|
||||
public function testWriteConfigLoggedOutWithFile()
|
||||
{
|
||||
file_put_contents(self::$configFields['config']['CONFIG_FILE'], '');
|
||||
writeConfig(self::$configFields, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test mergeDeprecatedConfig while being logged in:
|
||||
* 1. init a config file.
|
||||
* 2. init a options.php file with update value.
|
||||
* 3. merge.
|
||||
* 4. check updated value in config file.
|
||||
*/
|
||||
public function testMergeDeprecatedConfig()
|
||||
{
|
||||
// init
|
||||
writeConfig(self::$configFields, true);
|
||||
$configCopy = self::$configFields;
|
||||
$invert = !$configCopy['privateLinkByDefault'];
|
||||
$configCopy['privateLinkByDefault'] = $invert;
|
||||
|
||||
// Use writeConfig to create a options.php
|
||||
$configCopy['config']['CONFIG_FILE'] = 'tests/options.php';
|
||||
writeConfig($configCopy, true);
|
||||
|
||||
$this->assertTrue(is_file($configCopy['config']['CONFIG_FILE']));
|
||||
|
||||
// merge configs
|
||||
mergeDeprecatedConfig(self::$configFields, true);
|
||||
|
||||
// make sure updated field is changed
|
||||
include self::$configFields['config']['CONFIG_FILE'];
|
||||
$this->assertEquals($invert, $GLOBALS['privateLinkByDefault']);
|
||||
$this->assertFalse(is_file($configCopy['config']['CONFIG_FILE']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test mergeDeprecatedConfig while being logged in without options file.
|
||||
*/
|
||||
public function testMergeDeprecatedConfigNoFile()
|
||||
{
|
||||
writeConfig(self::$configFields, true);
|
||||
mergeDeprecatedConfig(self::$configFields, true);
|
||||
|
||||
include self::$configFields['config']['CONFIG_FILE'];
|
||||
$this->assertEquals(self::$configFields['login'], $GLOBALS['login']);
|
||||
}
|
||||
}
|
||||
|
|
66
tests/PluginManagerTest.php
Normal file
66
tests/PluginManagerTest.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Plugin Manager tests
|
||||
*/
|
||||
|
||||
require_once 'application/PluginManager.php';
|
||||
|
||||
/**
|
||||
* Unit tests for Plugins
|
||||
*/
|
||||
class PluginManagerTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* Path to tests plugin.
|
||||
* @var string $pluginPath
|
||||
*/
|
||||
private static $pluginPath = 'tests/plugins';
|
||||
|
||||
/**
|
||||
* Test plugin.
|
||||
* @var string $pluginName
|
||||
*/
|
||||
private static $pluginName = 'test';
|
||||
|
||||
/**
|
||||
* Test plugin loading and hook execution.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testPlugin()
|
||||
{
|
||||
$pluginManager = PluginManager::getInstance();
|
||||
|
||||
PluginManager::$PLUGINS_PATH = self::$pluginPath;
|
||||
$pluginManager->load(array(self::$pluginName));
|
||||
|
||||
$this->assertTrue(function_exists('hook_test_random'));
|
||||
|
||||
$data = array(0 => 'woot');
|
||||
$pluginManager->executeHooks('random', $data);
|
||||
$this->assertEquals('woot', $data[1]);
|
||||
|
||||
$data = array(0 => 'woot');
|
||||
$pluginManager->executeHooks('random', $data, array('target' => 'test'));
|
||||
$this->assertEquals('page test', $data[1]);
|
||||
|
||||
$data = array(0 => 'woot');
|
||||
$pluginManager->executeHooks('random', $data, array('loggedin' => true));
|
||||
$this->assertEquals('loggedin', $data[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test missing plugin loading.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testPluginNotFound()
|
||||
{
|
||||
$pluginManager = PluginManager::getInstance();
|
||||
|
||||
$pluginManager->load(array());
|
||||
|
||||
$pluginManager->load(array('nope', 'renope'));
|
||||
}
|
||||
}
|
515
tests/RouterTest.php
Normal file
515
tests/RouterTest.php
Normal file
|
@ -0,0 +1,515 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Router tests
|
||||
*/
|
||||
|
||||
require_once 'application/Router.php';
|
||||
|
||||
/**
|
||||
* Unit tests for Router
|
||||
*/
|
||||
class RouterTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* Test findPage: login page output.
|
||||
* Valid: page should be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageLoginValid()
|
||||
{
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_LOGIN,
|
||||
Router::findPage('do=login', array(), false)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_LOGIN,
|
||||
Router::findPage('do=login', array(), 1)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_LOGIN,
|
||||
Router::findPage('do=login&stuff', array(), false)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: login page output.
|
||||
* Invalid: page shouldn't be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageLoginInvalid()
|
||||
{
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_LOGIN,
|
||||
Router::findPage('do=login', array(), true)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_LOGIN,
|
||||
Router::findPage('do=other', array(), false)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: picwall page output.
|
||||
* Valid: page should be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPagePicwallValid()
|
||||
{
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_PICWALL,
|
||||
Router::findPage('do=picwall', array(), false)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_PICWALL,
|
||||
Router::findPage('do=picwall', array(), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: picwall page output.
|
||||
* Invalid: page shouldn't be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPagePicwallInvalid()
|
||||
{
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_PICWALL,
|
||||
Router::findPage('do=picwall&stuff', array(), false)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_PICWALL,
|
||||
Router::findPage('do=other', array(), false)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: tagcloud page output.
|
||||
* Valid: page should be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageTagcloudValid()
|
||||
{
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_TAGCLOUD,
|
||||
Router::findPage('do=tagcloud', array(), false)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_TAGCLOUD,
|
||||
Router::findPage('do=tagcloud', array(), true)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_TAGCLOUD,
|
||||
Router::findPage('do=tagcloud&stuff', array(), false)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: tagcloud page output.
|
||||
* Invalid: page shouldn't be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageTagcloudInvalid()
|
||||
{
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_TAGCLOUD,
|
||||
Router::findPage('do=other', array(), false)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: linklist page output.
|
||||
* Valid: page should be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageLinklistValid()
|
||||
{
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_LINKLIST,
|
||||
Router::findPage('', array(), true)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_LINKLIST,
|
||||
Router::findPage('whatever', array(), true)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_LINKLIST,
|
||||
Router::findPage('whatever', array(), false)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_LINKLIST,
|
||||
Router::findPage('do=tools', array(), false)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: tools page output.
|
||||
* Valid: page should be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageToolsValid()
|
||||
{
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_TOOLS,
|
||||
Router::findPage('do=tools', array(), true)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_TOOLS,
|
||||
Router::findPage('do=tools&stuff', array(), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: tools page output.
|
||||
* Invalid: page shouldn't be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageToolsInvalid()
|
||||
{
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_TOOLS,
|
||||
Router::findPage('do=tools', array(), 1)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_TOOLS,
|
||||
Router::findPage('do=tools', array(), false)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_TOOLS,
|
||||
Router::findPage('do=other', array(), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: changepasswd page output.
|
||||
* Valid: page should be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageChangepasswdValid()
|
||||
{
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_CHANGEPASSWORD,
|
||||
Router::findPage('do=changepasswd', array(), true)
|
||||
);
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_CHANGEPASSWORD,
|
||||
Router::findPage('do=changepasswd&stuff', array(), true)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: changepasswd page output.
|
||||
* Invalid: page shouldn't be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageChangepasswdInvalid()
|
||||
{
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_CHANGEPASSWORD,
|
||||
Router::findPage('do=changepasswd', array(), 1)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_CHANGEPASSWORD,
|
||||
Router::findPage('do=changepasswd', array(), false)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_CHANGEPASSWORD,
|
||||
Router::findPage('do=other', array(), true)
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Test findPage: configure page output.
|
||||
* Valid: page should be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageConfigureValid()
|
||||
{
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_CONFIGURE,
|
||||
Router::findPage('do=configure', array(), true)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_CONFIGURE,
|
||||
Router::findPage('do=configure&stuff', array(), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: configure page output.
|
||||
* Invalid: page shouldn't be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageConfigureInvalid()
|
||||
{
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_CONFIGURE,
|
||||
Router::findPage('do=configure', array(), 1)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_CONFIGURE,
|
||||
Router::findPage('do=configure', array(), false)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_CONFIGURE,
|
||||
Router::findPage('do=other', array(), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: changetag page output.
|
||||
* Valid: page should be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageChangetagValid()
|
||||
{
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_CHANGETAG,
|
||||
Router::findPage('do=changetag', array(), true)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_CHANGETAG,
|
||||
Router::findPage('do=changetag&stuff', array(), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: changetag page output.
|
||||
* Invalid: page shouldn't be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageChangetagInvalid()
|
||||
{
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_CHANGETAG,
|
||||
Router::findPage('do=changetag', array(), 1)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_CHANGETAG,
|
||||
Router::findPage('do=changetag', array(), false)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_CHANGETAG,
|
||||
Router::findPage('do=other', array(), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: addlink page output.
|
||||
* Valid: page should be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageAddlinkValid()
|
||||
{
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_ADDLINK,
|
||||
Router::findPage('do=addlink', array(), true)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_ADDLINK,
|
||||
Router::findPage('do=addlink&stuff', array(), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: addlink page output.
|
||||
* Invalid: page shouldn't be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageAddlinkInvalid()
|
||||
{
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_ADDLINK,
|
||||
Router::findPage('do=addlink', array(), 1)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_ADDLINK,
|
||||
Router::findPage('do=addlink', array(), false)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_ADDLINK,
|
||||
Router::findPage('do=other', array(), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: export page output.
|
||||
* Valid: page should be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageExportValid()
|
||||
{
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_EXPORT,
|
||||
Router::findPage('do=export', array(), true)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_EXPORT,
|
||||
Router::findPage('do=export&stuff', array(), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: export page output.
|
||||
* Invalid: page shouldn't be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageExportInvalid()
|
||||
{
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_EXPORT,
|
||||
Router::findPage('do=export', array(), 1)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_EXPORT,
|
||||
Router::findPage('do=export', array(), false)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_EXPORT,
|
||||
Router::findPage('do=other', array(), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: import page output.
|
||||
* Valid: page should be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageImportValid()
|
||||
{
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_IMPORT,
|
||||
Router::findPage('do=import', array(), true)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_IMPORT,
|
||||
Router::findPage('do=import&stuff', array(), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: import page output.
|
||||
* Invalid: page shouldn't be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageImportInvalid()
|
||||
{
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_IMPORT,
|
||||
Router::findPage('do=import', array(), 1)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_IMPORT,
|
||||
Router::findPage('do=import', array(), false)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_IMPORT,
|
||||
Router::findPage('do=other', array(), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: editlink page output.
|
||||
* Valid: page should be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageEditlinkValid()
|
||||
{
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_EDITLINK,
|
||||
Router::findPage('whatever', array('edit_link' => 1), true)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_EDITLINK,
|
||||
Router::findPage('', array('edit_link' => 1), true)
|
||||
);
|
||||
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_EDITLINK,
|
||||
Router::findPage('whatever', array('post' => 1), true)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Router::$PAGE_EDITLINK,
|
||||
Router::findPage('whatever', array('post' => 1, 'edit_link' => 1), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test findPage: editlink page output.
|
||||
* Invalid: page shouldn't be return.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testFindPageEditlinkInvalid()
|
||||
{
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_EDITLINK,
|
||||
Router::findPage('whatever', array('edit_link' => 1), false)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_EDITLINK,
|
||||
Router::findPage('whatever', array('edit_link' => 1), 1)
|
||||
);
|
||||
|
||||
$this->assertNotEquals(
|
||||
Router::$PAGE_EDITLINK,
|
||||
Router::findPage('whatever', array(), true)
|
||||
);
|
||||
}
|
||||
}
|
67
tests/plugins/PlugQrcodeTest.php
Normal file
67
tests/plugins/PlugQrcodeTest.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PlugQrcodeTest.php
|
||||
*/
|
||||
|
||||
require_once 'plugins/qrcode/qrcode.php';
|
||||
require_once 'application/Router.php';
|
||||
|
||||
/**
|
||||
* Class PlugQrcodeTest
|
||||
* Unit test for the QR-Code plugin
|
||||
*/
|
||||
class PlugQrcodeTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* Reset plugin path
|
||||
*/
|
||||
function setUp() {
|
||||
PluginManager::$PLUGINS_PATH = 'plugins';
|
||||
}
|
||||
|
||||
/**
|
||||
* Test render_linklist hook.
|
||||
*/
|
||||
function testQrcodeLinklist()
|
||||
{
|
||||
$str = 'http://randomstr.com/test';
|
||||
$data = array(
|
||||
'title' => $str,
|
||||
'links' => array(
|
||||
array(
|
||||
'url' => $str,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$data = hook_qrcode_render_linklist($data);
|
||||
$link = $data['links'][0];
|
||||
// data shouldn't be altered
|
||||
$this->assertEquals($str, $data['title']);
|
||||
$this->assertEquals($str, $link['url']);
|
||||
|
||||
// plugin data
|
||||
$this->assertEquals(1, count($link['link_plugin']));
|
||||
$this->assertNotFalse(strpos($link['link_plugin'][0], $str));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test render_footer hook.
|
||||
*/
|
||||
function testQrcodeFooter()
|
||||
{
|
||||
$str = 'stuff';
|
||||
$data = array($str => $str);
|
||||
$data['_PAGE_'] = Router::$PAGE_LINKLIST;
|
||||
|
||||
$data = hook_qrcode_render_footer($data);
|
||||
$this->assertEquals($str, $data[$str]);
|
||||
$this->assertEquals(1, count($data['js_files']));
|
||||
|
||||
$data = array($str => $str);
|
||||
$data['_PAGE_'] = $str;
|
||||
$this->assertEquals($str, $data[$str]);
|
||||
$this->assertArrayNotHasKey('js_files', $data);
|
||||
}
|
||||
}
|
100
tests/plugins/PluginAddlinkTest.php
Normal file
100
tests/plugins/PluginAddlinkTest.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PluginPlayvideosTest.php
|
||||
*/
|
||||
|
||||
require_once 'plugins/addlink_toolbar/addlink_toolbar.php';
|
||||
require_once 'application/Router.php';
|
||||
|
||||
/**
|
||||
* Class PluginAddlinkTest
|
||||
* Unit test for the Addlink toolbar plugin
|
||||
*/
|
||||
class PluginAddlinkTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* Reset plugin path.
|
||||
*/
|
||||
function setUp()
|
||||
{
|
||||
PluginManager::$PLUGINS_PATH = 'plugins';
|
||||
}
|
||||
|
||||
/**
|
||||
* Test render_header hook while logged in.
|
||||
*/
|
||||
function testAddlinkHeaderLoggedIn()
|
||||
{
|
||||
$str = 'stuff';
|
||||
$data = array($str => $str);
|
||||
$data['_PAGE_'] = Router::$PAGE_LINKLIST;
|
||||
$data['_LOGGEDIN_'] = true;
|
||||
|
||||
$data = hook_addlink_toolbar_render_header($data);
|
||||
$this->assertEquals($str, $data[$str]);
|
||||
$this->assertEquals(1, count($data['fields_toolbar']));
|
||||
|
||||
$data = array($str => $str);
|
||||
$data['_PAGE_'] = $str;
|
||||
$data['_LOGGEDIN_'] = true;
|
||||
$data = hook_addlink_toolbar_render_header($data);
|
||||
$this->assertEquals($str, $data[$str]);
|
||||
$this->assertArrayNotHasKey('fields_toolbar', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test render_header hook while logged out.
|
||||
*/
|
||||
function testAddlinkHeaderLoggedOut()
|
||||
{
|
||||
$str = 'stuff';
|
||||
$data = array($str => $str);
|
||||
$data['_PAGE_'] = Router::$PAGE_LINKLIST;
|
||||
$data['_LOGGEDIN_'] = false;
|
||||
|
||||
$data = hook_addlink_toolbar_render_header($data);
|
||||
$this->assertEquals($str, $data[$str]);
|
||||
$this->assertArrayNotHasKey('fields_toolbar', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test render_includes hook while logged in.
|
||||
*/
|
||||
function testAddlinkIncludesLoggedIn()
|
||||
{
|
||||
$str = 'stuff';
|
||||
$data = array($str => $str);
|
||||
$data['_PAGE_'] = Router::$PAGE_LINKLIST;
|
||||
$data['_LOGGEDIN_'] = true;
|
||||
|
||||
$data = hook_addlink_toolbar_render_includes($data);
|
||||
$this->assertEquals($str, $data[$str]);
|
||||
$this->assertEquals(1, count($data['css_files']));
|
||||
|
||||
$str = 'stuff';
|
||||
$data = array($str => $str);
|
||||
$data['_PAGE_'] = $str;
|
||||
$data['_LOGGEDIN_'] = true;
|
||||
|
||||
$data = hook_addlink_toolbar_render_includes($data);
|
||||
$this->assertEquals($str, $data[$str]);
|
||||
$this->assertArrayNotHasKey('css_files', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test render_includes hook.
|
||||
* Should not affect css files while logged out.
|
||||
*/
|
||||
function testAddlinkIncludesLoggedOut()
|
||||
{
|
||||
$str = 'stuff';
|
||||
$data = array($str => $str);
|
||||
$data['_PAGE_'] = Router::$PAGE_LINKLIST;
|
||||
$data['_LOGGEDIN_'] = false;
|
||||
|
||||
$data = hook_addlink_toolbar_render_includes($data);
|
||||
$this->assertEquals($str, $data[$str]);
|
||||
$this->assertArrayNotHasKey('css_files', $data);
|
||||
}
|
||||
}
|
48
tests/plugins/PluginArchiveorgTest.php
Normal file
48
tests/plugins/PluginArchiveorgTest.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PluginArchiveorgTest.php
|
||||
*/
|
||||
|
||||
require_once 'plugins/archiveorg/archiveorg.php';
|
||||
|
||||
/**
|
||||
* Class PlugQrcodeTest
|
||||
* Unit test for the QR-Code plugin
|
||||
*/
|
||||
class PluginArchiveorgTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* Reset plugin path
|
||||
*/
|
||||
function setUp()
|
||||
{
|
||||
PluginManager::$PLUGINS_PATH = 'plugins';
|
||||
}
|
||||
|
||||
/**
|
||||
* Test render_linklist hook.
|
||||
*/
|
||||
function testArchiveorgLinklist()
|
||||
{
|
||||
$str = 'http://randomstr.com/test';
|
||||
$data = array(
|
||||
'title' => $str,
|
||||
'links' => array(
|
||||
array(
|
||||
'url' => $str,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$data = hook_archiveorg_render_linklist($data);
|
||||
$link = $data['links'][0];
|
||||
// data shouldn't be altered
|
||||
$this->assertEquals($str, $data['title']);
|
||||
$this->assertEquals($str, $link['url']);
|
||||
|
||||
// plugin data
|
||||
$this->assertEquals(1, count($link['link_plugin']));
|
||||
$this->assertNotFalse(strpos($link['link_plugin'][0], $str));
|
||||
}
|
||||
}
|
61
tests/plugins/PluginPlayvideosTest.php
Normal file
61
tests/plugins/PluginPlayvideosTest.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PluginPlayvideosTest.php
|
||||
*/
|
||||
|
||||
require_once 'plugins/playvideos/playvideos.php';
|
||||
require_once 'application/Router.php';
|
||||
|
||||
/**
|
||||
* Class PluginPlayvideosTest
|
||||
* Unit test for the PlayVideos plugin
|
||||
*/
|
||||
class PluginPlayvideosTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* Reset plugin path
|
||||
*/
|
||||
function setUp()
|
||||
{
|
||||
PluginManager::$PLUGINS_PATH = 'plugins';
|
||||
}
|
||||
|
||||
/**
|
||||
* Test render_linklist hook.
|
||||
*/
|
||||
function testPlayvideosHeader()
|
||||
{
|
||||
$str = 'stuff';
|
||||
$data = array($str => $str);
|
||||
$data['_PAGE_'] = Router::$PAGE_LINKLIST;
|
||||
|
||||
$data = hook_playvideos_render_header($data);
|
||||
$this->assertEquals($str, $data[$str]);
|
||||
$this->assertEquals(1, count($data['buttons_toolbar']));
|
||||
|
||||
$data = array($str => $str);
|
||||
$data['_PAGE_'] = $str;
|
||||
$this->assertEquals($str, $data[$str]);
|
||||
$this->assertArrayNotHasKey('buttons_toolbar', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test render_footer hook.
|
||||
*/
|
||||
function testPlayvideosFooter()
|
||||
{
|
||||
$str = 'stuff';
|
||||
$data = array($str => $str);
|
||||
$data['_PAGE_'] = Router::$PAGE_LINKLIST;
|
||||
|
||||
$data = hook_playvideos_render_footer($data);
|
||||
$this->assertEquals($str, $data[$str]);
|
||||
$this->assertEquals(2, count($data['js_files']));
|
||||
|
||||
$data = array($str => $str);
|
||||
$data['_PAGE_'] = $str;
|
||||
$this->assertEquals($str, $data[$str]);
|
||||
$this->assertArrayNotHasKey('js_files', $data);
|
||||
}
|
||||
}
|
75
tests/plugins/PluginReadityourselfTest.php
Normal file
75
tests/plugins/PluginReadityourselfTest.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PluginReadityourselfTest.php.php
|
||||
*/
|
||||
|
||||
require_once 'plugins/readityourself/readityourself.php';
|
||||
|
||||
/**
|
||||
* Class PluginWallabagTest
|
||||
* Unit test for the Wallabag plugin
|
||||
*/
|
||||
class PluginReadityourselfTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* Reset plugin path
|
||||
*/
|
||||
function setUp()
|
||||
{
|
||||
PluginManager::$PLUGINS_PATH = 'plugins';
|
||||
}
|
||||
|
||||
/**
|
||||
* Test render_linklist hook.
|
||||
*/
|
||||
function testReadityourselfLinklist()
|
||||
{
|
||||
$GLOBALS['plugins']['READITYOUSELF_URL'] = 'value';
|
||||
$str = 'http://randomstr.com/test';
|
||||
$data = array(
|
||||
'title' => $str,
|
||||
'links' => array(
|
||||
array(
|
||||
'url' => $str,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$data = hook_readityourself_render_linklist($data);
|
||||
$link = $data['links'][0];
|
||||
// data shouldn't be altered
|
||||
$this->assertEquals($str, $data['title']);
|
||||
$this->assertEquals($str, $link['url']);
|
||||
|
||||
// plugin data
|
||||
$this->assertEquals(1, count($link['link_plugin']));
|
||||
$this->assertNotFalse(strpos($link['link_plugin'][0], $str));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test without config: nothing should happened.
|
||||
*/
|
||||
function testReadityourselfLinklistWithoutConfig()
|
||||
{
|
||||
unset($GLOBALS['plugins']['READITYOUSELF_URL']);
|
||||
$str = 'http://randomstr.com/test';
|
||||
$data = array(
|
||||
'title' => $str,
|
||||
'links' => array(
|
||||
array(
|
||||
'url' => $str,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$data = hook_readityourself_render_linklist($data);
|
||||
$link = $data['links'][0];
|
||||
// data shouldn't be altered
|
||||
$this->assertEquals($str, $data['title']);
|
||||
$this->assertEquals($str, $link['url']);
|
||||
|
||||
// plugin data
|
||||
$this->assertArrayNotHasKey('link_plugin', $link);
|
||||
}
|
||||
}
|
49
tests/plugins/PluginWallabagTest.php
Normal file
49
tests/plugins/PluginWallabagTest.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* PluginWallabagTest.php.php
|
||||
*/
|
||||
|
||||
require_once 'plugins/wallabag/wallabag.php';
|
||||
|
||||
/**
|
||||
* Class PluginWallabagTest
|
||||
* Unit test for the Wallabag plugin
|
||||
*/
|
||||
class PluginWallabagTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* Reset plugin path
|
||||
*/
|
||||
function setUp()
|
||||
{
|
||||
PluginManager::$PLUGINS_PATH = 'plugins';
|
||||
}
|
||||
|
||||
/**
|
||||
* Test render_linklist hook.
|
||||
*/
|
||||
function testWallabagLinklist()
|
||||
{
|
||||
$GLOBALS['plugins']['WALLABAG_URL'] = 'value';
|
||||
$str = 'http://randomstr.com/test';
|
||||
$data = array(
|
||||
'title' => $str,
|
||||
'links' => array(
|
||||
array(
|
||||
'url' => $str,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$data = hook_wallabag_render_linklist($data);
|
||||
$link = $data['links'][0];
|
||||
// data shouldn't be altered
|
||||
$this->assertEquals($str, $data['title']);
|
||||
$this->assertEquals($str, $link['url']);
|
||||
|
||||
// plugin data
|
||||
$this->assertEquals(1, count($link['link_plugin']));
|
||||
$this->assertNotFalse(strpos($link['link_plugin'][0], $str));
|
||||
}
|
||||
}
|
21
tests/plugins/test/test.php
Normal file
21
tests/plugins/test/test.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Hook for test.
|
||||
*
|
||||
* @param array $data - data passed to plugin.
|
||||
*
|
||||
* @return mixed altered data.
|
||||
*/
|
||||
function hook_test_random($data)
|
||||
{
|
||||
if (isset($data['_PAGE_']) && $data['_PAGE_'] == 'test') {
|
||||
$data[1] = 'page test';
|
||||
} else if (isset($data['_LOGGEDIN_']) && $data['_LOGGEDIN_'] === true) {
|
||||
$data[1] = 'loggedin';
|
||||
} else {
|
||||
$data[1] = $data[0];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
|
@ -38,6 +38,7 @@
|
|||
<input type="checkbox" name="updateCheck" id="updateCheck" {if="!empty($GLOBALS['config']['ENABLE_UPDATECHECK'])"}checked{/if}/>
|
||||
<label for="updateCheck"> Notify me when a new release is ready</label></td>
|
||||
</tr>
|
||||
|
||||
<tr><td></td><td class="right"><input type="submit" name="Save" value="Save config" class="bigbutton"></td></tr>
|
||||
</table>
|
||||
</form>
|
||||
|
|
|
@ -2,18 +2,43 @@
|
|||
<html>
|
||||
<head>{include="includes"}</head>
|
||||
<body>
|
||||
<div id="pageheader">{include="page.header"}</div>
|
||||
<div id="pageheader">
|
||||
{include="page.header"}
|
||||
</div>
|
||||
<div class="daily">
|
||||
<div class="dailyAbout">
|
||||
All links of one day<br>in a single page.<br>
|
||||
{if="$previousday"} <a href="?do=daily&day={$previousday}"><b><</b>Previous day</a>{else}<b><</b>Previous day{/if}
|
||||
-
|
||||
{if="$nextday"}<a href="?do=daily&day={$nextday}">Next day<b>></b></a>{else}Next day<b>></b>{/if}
|
||||
<br><br>
|
||||
<a href="?do=dailyrss" title="1 RSS entry per day"><img src="images/feed-icon-14x14.png#" alt="rss_feed">Daily RSS Feed</a>
|
||||
<div id="plugin_zone_start_picwall" class="plugin_zone">
|
||||
{loop="$plugin_start_zone"}
|
||||
{$value}
|
||||
{/loop}
|
||||
</div>
|
||||
<div class="dailyTitle"><img src="../images/floral_left.png" width="51" height="50" class="nomobile" alt="floral_left"> The Daily Shaarli <img src="../images/floral_right.png" width="51" height="50" class="nomobile" alt="floral_right"></div>
|
||||
<div class="dailyDate"><span class="nomobile">———————————</span> {function="strftime('%A %d, %B %Y', $day)"} <span class="nomobile">———————————</span></div>
|
||||
|
||||
<div class="dailyAbout">
|
||||
All links of one day<br>in a single page.<br>
|
||||
{if="$previousday"} <a href="?do=daily&day={$previousday}"><b><</b>Previous day</a>{else}<b><</b>Previous day{/if}
|
||||
-
|
||||
{if="$nextday"}<a href="?do=daily&day={$nextday}">Next day<b>></b></a>{else}Next day<b>></b>{/if}
|
||||
<br>
|
||||
|
||||
{loop="$daily_about_plugin"}
|
||||
{$value}
|
||||
{/loop}
|
||||
|
||||
<br>
|
||||
<a href="?do=dailyrss" title="1 RSS entry per day"><img src="images/feed-icon-14x14.png#" alt="rss_feed">Daily RSS Feed</a>
|
||||
</div>
|
||||
|
||||
<div class="dailyTitle">
|
||||
<img src="../images/floral_left.png" width="51" height="50" class="nomobile" alt="floral_left">
|
||||
The Daily Shaarli
|
||||
<img src="../images/floral_right.png" width="51" height="50" class="nomobile" alt="floral_right">
|
||||
</div>
|
||||
|
||||
<div class="dailyDate">
|
||||
<span class="nomobile">———————————</span>
|
||||
{function="strftime('%A %d, %B %Y', $day)"}
|
||||
<span class="nomobile">———————————</span>
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
|
||||
{if="$linksToDisplay"}
|
||||
|
@ -47,6 +72,12 @@
|
|||
<div class="dailyEntryThumbnail">{$link.thumbnail}</div>
|
||||
{/if}
|
||||
<div class="dailyEntryDescription">{$link.formatedDescription}</div>
|
||||
|
||||
<div class="dailyEntryFooter">
|
||||
{loop="$link.link_plugin"}
|
||||
{$value}
|
||||
{/loop}
|
||||
</div>
|
||||
</div>
|
||||
{/loop}
|
||||
</div>
|
||||
|
@ -55,6 +86,14 @@
|
|||
{else}
|
||||
<div class="dailyNoEntry">No articles on this day.</div>
|
||||
{/if}
|
||||
|
||||
<div class="clear"></div>
|
||||
|
||||
<div id="plugin_zone_end_picwall" class="plugin_zone">
|
||||
{loop="$plugin_end_zone"}
|
||||
{$value}
|
||||
{/loop}
|
||||
</div>
|
||||
<div id="closing"><img src="../images/squiggle_closing.png" width="66" height="61" alt="-"></div>
|
||||
</div>
|
||||
{include="page.footer"}
|
||||
|
|
|
@ -21,6 +21,11 @@
|
|||
<label for="lf_tags"><i>Tags</i></label><br>
|
||||
<input type="text" name="lf_tags" id="lf_tags" value="{$link.tags}" class="lf_input"
|
||||
data-list="{loop="$tags"}{$key}, {/loop}" data-multiple autocomplete="off" ><br>
|
||||
|
||||
{loop="$edit_link_plugin"}
|
||||
{$value}
|
||||
{/loop}
|
||||
|
||||
{if="($link_is_new && $GLOBALS['privateLinkByDefault']==true) || $link.private == true"}
|
||||
<input type="checkbox" checked="checked" name="lf_private" id="lf_private">
|
||||
<label for="lf_private"><i>Private</i></label><br>
|
||||
|
|
|
@ -8,3 +8,6 @@
|
|||
<link type="text/css" rel="stylesheet" href="../inc/reset.css" />
|
||||
<link type="text/css" rel="stylesheet" href="../inc/shaarli.css" />
|
||||
{if="is_file('inc/user.css')"}<link type="text/css" rel="stylesheet" href="../inc/user.css" />{/if}
|
||||
{loop="$plugins_includes.css_files"}
|
||||
<link type="text/css" rel="stylesheet" href="{$value}#"/>
|
||||
{/loop}
|
|
@ -17,6 +17,9 @@
|
|||
</datalist>
|
||||
<input type="submit" value="Search" class="bigbutton">
|
||||
</form>
|
||||
{loop="$plugins_header.fields_toolbar"}
|
||||
{$value}
|
||||
{/loop}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -24,6 +27,12 @@
|
|||
|
||||
{include="linklist.paging"}
|
||||
|
||||
<div id="plugin_zone_start_linklist" class="plugin_zone">
|
||||
{loop="$plugin_start_zone"}
|
||||
{$value}
|
||||
{/loop}
|
||||
</div>
|
||||
|
||||
{if="count($links)==0"}
|
||||
<div id="searchcriteria">Nothing found.</i></div>
|
||||
{else}
|
||||
|
@ -40,7 +49,7 @@
|
|||
<ul>
|
||||
{loop="links"}
|
||||
<li{if="$value.class"} class="{$value.class}"{/if}>
|
||||
<a id="{$value.linkdate|smallHash}"></a>
|
||||
<a id="{$value.shorturl}"></a>
|
||||
<div class="thumbnail">{$value.url|thumbnail}</div>
|
||||
<div class="linkcontainer">
|
||||
{if="isLoggedIn()"}
|
||||
|
@ -56,89 +65,38 @@
|
|||
{if="!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()"}
|
||||
<span class="linkdate" title="Permalink"><a href="?{$value.linkdate|smallHash}">{function="strftime('%c', $value.timestamp)"} - permalink</a> - </span>
|
||||
{else}
|
||||
<span class="linkdate" title="Short link here"><a href="?{$value.linkdate|smallHash}">permalink</a> - </span>
|
||||
<span class="linkdate" title="Short link here"><a href="?{$value.shorturl}">permalink</a> - </span>
|
||||
{/if}
|
||||
{if="$GLOBALS['config']['ARCHIVE_ORG']"}
|
||||
<span class="linkarchive"><a href="https://web.archive.org/web/{$value.url}">archive</a> - </span>
|
||||
{/if}
|
||||
<div class="linkqrcode"><a href="http://qrfree.kaywa.com/?l=1&s=8&d={$scripturl|urlencode}%3F{$value.linkdate|smallHash}"
|
||||
onclick="return showQrCode(this);" class="qrcode" data-permalink="{$scripturl}?{$value.linkdate|smallHash}">
|
||||
<img src="images/qrcode.png#" alt="QR-Code" title="{function="strftime('%c', $value.timestamp)"}"></a></div> -
|
||||
|
||||
{loop="$value.link_plugin"}
|
||||
<span>{$value}</span> -
|
||||
{/loop}
|
||||
|
||||
<a href="{$value.url}"><span class="linkurl" title="Short link">{$value.url}</span></a><br>
|
||||
{if="$value.tags"}
|
||||
<div class="linktaglist">
|
||||
{loop="value.taglist"}<span class="linktag" title="Add tag"><a href="?addtag={$value|urlencode}">{$value}</a></span> {/loop}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
</div>
|
||||
</li>
|
||||
{/loop}
|
||||
</ul>
|
||||
|
||||
<div id="plugin_zone_end_linklist" class="plugin_zone">
|
||||
{loop="$plugin_end_zone"}
|
||||
{$value}
|
||||
{/loop}
|
||||
</div>
|
||||
|
||||
{include="linklist.paging"}
|
||||
|
||||
</div>
|
||||
|
||||
{include="page.footer"}
|
||||
|
||||
<script>
|
||||
// Remove any displayed QR-Code
|
||||
function remove_qrcode()
|
||||
{
|
||||
var elem = document.getElementById("permalinkQrcode");
|
||||
if (elem) elem.parentNode.removeChild(elem);
|
||||
return false;
|
||||
}
|
||||
|
||||
function isCanvasSupported(){
|
||||
var elem = document.createElement('canvas');
|
||||
return !!(elem.getContext && elem.getContext('2d'));
|
||||
}
|
||||
|
||||
// Show the QR-Code of a permalink (when the QR-Code icon is clicked).
|
||||
function showQrCode(caller,loading)
|
||||
{
|
||||
if( !isCanvasSupported() ) return true;
|
||||
|
||||
// Dynamic javascript lib loading: We only load qr.js if the QR code icon is clicked:
|
||||
if (typeof(qr)=='undefined') // Load qr.js only if not present.
|
||||
{
|
||||
loading = typeof loading !== 'undefined' ? loading : false;
|
||||
if (!loading) // If javascript lib is still loading, do not append script to body.
|
||||
{
|
||||
var element = document.createElement("script");
|
||||
element.src = "inc/qr-1.1.3.min.js";
|
||||
document.body.appendChild(element);
|
||||
}
|
||||
setTimeout(function() { showQrCode(caller,true);}, 200); // Retry in 200 milliseconds.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove previous qrcode if present.
|
||||
remove_qrcode();
|
||||
|
||||
// Build the div which contains the QR-Code:
|
||||
var element = document.createElement('div');
|
||||
element.id="permalinkQrcode";
|
||||
|
||||
// Make QR-Code div commit sepuku when clicked:
|
||||
element.addEventListener('click', remove_qrcode ); // Works on every canvas supported browser
|
||||
|
||||
// Build the QR-Code:
|
||||
var image = qr.image({size: 8,value: caller.getAttribute('data-permalink')});
|
||||
if (image)
|
||||
{
|
||||
element.appendChild(image);
|
||||
element.innerHTML+= "<br>Click to close";
|
||||
caller.parentNode.appendChild(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
element.innerHTML="Your browser does not seem to be HTML5 compatible.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
<script src="inc/awesomplete.min.js#"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -8,8 +8,13 @@
|
|||
<img src="images/private_16x16.png#" width="16" height="16" title="Click to see only private links" alt="Click to see only private links">
|
||||
{/if}
|
||||
</a>
|
||||
|
||||
|
||||
</div>
|
||||
{/if}
|
||||
{loop="$action_plugin"}
|
||||
{$value}
|
||||
{/loop}
|
||||
<div class="paging_linksperpage">
|
||||
Links per page: <a href="?linksperpage=20">20</a> <a href="?linksperpage=50">50</a> <a href="?linksperpage=100">100</a>
|
||||
<form method="GET" class="linksperpage"><input type="text" name="linksperpage" size="2"></form>
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<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>
|
||||
{loop="$plugins_footer.text"}
|
||||
{$value}
|
||||
{/loop}
|
||||
</div>
|
||||
{if="$newversion"}
|
||||
<div id="newversion"><span id="version_id">●</span> Shaarli {$newversion} is <a href="https://github.com/shaarli/Shaarli/releases">available</a>.</div>
|
||||
|
@ -7,3 +10,7 @@
|
|||
{if="isLoggedIn()"}
|
||||
<script>function confirmDeleteLink() { var agree=confirm("Are you sure you want to delete this link ?"); if (agree) return true ; else return false ; }</script>
|
||||
{/if}
|
||||
|
||||
{loop="$plugins_footer.js_files"}
|
||||
<script src="{$value}#"></script>
|
||||
{/loop}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<a href="{$titleLink}">{$shaarlititle}</a>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
|
||||
{if="!empty($_GET['source']) && $_GET['source']=='bookmarklet'"}
|
||||
{ignore} When called as a popup from bookmarklet, do not display menu. {/ignore}
|
||||
{else}
|
||||
|
@ -33,10 +33,20 @@
|
|||
<li><a href="?do=tagcloud">Tag cloud</a></li>
|
||||
<li><a href="?do=picwall{$searchcrits}">Picture wall</a></li>
|
||||
<li><a href="?do=daily">Daily</a></li>
|
||||
{loop="$plugins_header.buttons_toolbar"}
|
||||
{$value}
|
||||
{/loop}
|
||||
{/if}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{if="!empty($plugin_errors) && isLoggedIn()"}
|
||||
<ul class="errors">
|
||||
{loop="plugin_errors"}
|
||||
<li>{$value}</li>
|
||||
{/loop}
|
||||
</ul>
|
||||
{/if}
|
||||
|
||||
<div class="clear"></div>
|
||||
|
||||
|
|
|
@ -5,15 +5,34 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="pageheader">{include="page.header"}</div>
|
||||
|
||||
<div id="plugin_zone_start_picwall" class="plugin_zone">
|
||||
{loop="$plugin_start_zone"}
|
||||
{$value}
|
||||
{/loop}
|
||||
</div>
|
||||
|
||||
<div class="center">
|
||||
<div id="picwall_container">
|
||||
{loop="linksToDisplay"}
|
||||
<div class="picwall_pictureframe">
|
||||
{$value.thumbnail}<a href="{$value.url}"><span class="info">{$value.title}</span></a>
|
||||
{loop="$value.picwall_plugin"}
|
||||
{$value}
|
||||
{/loop}
|
||||
</div>
|
||||
{/loop}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
|
||||
<div id="plugin_zone_end_picwall" class="plugin_zone">
|
||||
{loop="$plugin_end_zone"}
|
||||
{$value}
|
||||
{/loop}
|
||||
</div>
|
||||
|
||||
{include="page.footer"}
|
||||
|
||||
<script>
|
||||
|
|
|
@ -4,9 +4,25 @@
|
|||
<body>
|
||||
<div id="pageheader">{include="page.header"}</div>
|
||||
<div class="center">
|
||||
<div id="plugin_zone_start_tagcloud" class="plugin_zone">
|
||||
{loop="$plugin_start_zone"}
|
||||
{$value}
|
||||
{/loop}
|
||||
</div>
|
||||
|
||||
<div id="cloudtag">
|
||||
{loop="tags"}
|
||||
<span class="count">{$value.count}</span><a href="?searchtags={$key|urlencode}" style="font-size:{$value.size}pt;">{$key}</a>
|
||||
<span class="count">{$value.count}</span>
|
||||
<a href="?searchtags={$key|urlencode}" style="font-size:{$value.size}pt;">{$key}</a>
|
||||
{loop="$value.tag_plugin"}
|
||||
{$value}
|
||||
{/loop}
|
||||
{/loop}
|
||||
</div>
|
||||
|
||||
<div id="plugin_zone_end_tagcloud" class="plugin_zone">
|
||||
{loop="$plugin_end_zone"}
|
||||
{$value}
|
||||
{/loop}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
<a class="smallbutton" onclick="alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...');return false;" href="javascript:javascript:(function(){var%20url%20=%20location.href;var%20title%20=%20document.title%20||%20url;window.open('{$pageabsaddr}?post='%20+%20encodeURIComponent(url)+'&title='%20+%20encodeURIComponent(title)+'&description='%20+%20encodeURIComponent(document.getSelection())+'&source=bookmarklet','_blank','menubar=no,height=390,width=600,toolbar=no,scrollbars=no,status=no,dialog=1');})();"><b>✚Shaare link</b></a> <a href="#" style="clear:none;"><span>⇐ Drag this link to your bookmarks toolbar (or right-click it and choose Bookmark This Link....).<br> Then click "✚Shaare link" button in any page you want to share.</span></a><br><br>
|
||||
<a class="smallbutton" onclick="alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...');return false;" href="?private=1&post="><b>✚Add Note</b></a> <a href="#" style="clear:none;"><span>⇐ Drag this link to your bookmarks toolbar (or right-click it and choose Bookmark This Link....).<br> Then click "✚Add Note" button anytime to start composing a (default private) Note (text post) to your Shaarli.</span></a><br><br>
|
||||
<a class="smallbutton" onclick="activateFirefoxSocial(this)"><b>✚Add to Firefox social</b></a> <a href="#" style="clear:none;"><span>⇐ Click on this button to add Shaarli to the "Share this page" button in Firefox.</span></a><br><br>
|
||||
{loop="$tools_plugin"}
|
||||
{$value}
|
||||
{/loop}
|
||||
<div class="clear"></div>
|
||||
|
||||
<script>
|
||||
|
|
Loading…
Reference in a new issue