aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2015-07-15 11:42:15 +0200
committerArthurHoaro <arthur@hoa.ro>2015-11-07 15:27:17 +0100
commit6fc14d530369740d27d6bd641369d4f5f5f04080 (patch)
tree2da553378e8f0ff367dcb677d6f519d1fb3e803c
parent38bedfbbcdd2a40e9f04f5753e0fd6f4fd513c21 (diff)
downloadShaarli-6fc14d530369740d27d6bd641369d4f5f5f04080.tar.gz
Shaarli-6fc14d530369740d27d6bd641369d4f5f5f04080.tar.zst
Shaarli-6fc14d530369740d27d6bd641369d4f5f5f04080.zip
Plugin system - CORE
see shaarli/Shaarli#275
-rw-r--r--.gitignore3
-rw-r--r--[-rwxr-xr-x]application/Config.php263
-rw-r--r--application/PluginManager.php184
-rw-r--r--application/Router.php105
-rwxr-xr-xindex.php230
-rwxr-xr-xtests/PluginManagerTest.php66
-rwxr-xr-xtests/RouterTest.php515
-rwxr-xr-xtests/plugins/test/test.php21
8 files changed, 1190 insertions, 197 deletions
diff --git a/.gitignore b/.gitignore
index 3ffedb31..5a6b9241 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,6 @@ coverage
21tests/datastore.php 21tests/datastore.php
22tests/dummycache/ 22tests/dummycache/
23phpmd.html 23phpmd.html
24
25# Ignore user plugin configuration
26plugins/*/config.php \ No newline at end of file
diff --git a/application/Config.php b/application/Config.php
index ec799d7f..c71ef68c 100755..100644
--- a/application/Config.php
+++ b/application/Config.php
@@ -1,129 +1,134 @@
1<?php 1<?php
2/** 2/**
3 * Functions related to configuration management. 3 * Functions related to configuration management.
4 */ 4 */
5 5
6/** 6/**
7 * Re-write configuration file according to given array. 7 * Re-write configuration file according to given array.
8 * Requires mandatory fields listed in $MANDATORY_FIELDS. 8 * Requires mandatory fields listed in $MANDATORY_FIELDS.
9 * 9 *
10 * @param array $config contains all configuration fields. 10 * @param array $config contains all configuration fields.
11 * @param bool $isLoggedIn true if user is logged in. 11 * @param bool $isLoggedIn true if user is logged in.
12 * 12 *
13 * @return void 13 * @return void
14 * 14 *
15 * @throws MissingFieldConfigException: a mandatory field has not been provided in $config. 15 * @throws MissingFieldConfigException: a mandatory field has not been provided in $config.
16 * @throws UnauthorizedConfigException: user is not authorize to change configuration. 16 * @throws UnauthorizedConfigException: user is not authorize to change configuration.
17 * @throws Exception: an error occured while writing the new config file. 17 * @throws Exception: an error occured while writing the new config file.
18 */ 18 */
19function writeConfig($config, $isLoggedIn) 19function writeConfig($config, $isLoggedIn)
20{ 20{
21 // These fields are required in configuration. 21 // These fields are required in configuration.
22 $MANDATORY_FIELDS = array( 22 $MANDATORY_FIELDS = array(
23 'login', 'hash', 'salt', 'timezone', 'title', 'titleLink', 23 'login', 'hash', 'salt', 'timezone', 'title', 'titleLink',
24 'redirector', 'disablesessionprotection', 'privateLinkByDefault' 24 'redirector', 'disablesessionprotection', 'privateLinkByDefault'
25 ); 25 );
26 26
27 if (!isset($config['config']['CONFIG_FILE'])) { 27 if (!isset($config['config']['CONFIG_FILE'])) {
28 throw new MissingFieldConfigException('CONFIG_FILE'); 28 throw new MissingFieldConfigException('CONFIG_FILE');
29 } 29 }
30 30
31 // Only logged in user can alter config. 31 // Only logged in user can alter config.
32 if (is_file($config['config']['CONFIG_FILE']) && !$isLoggedIn) { 32 if (is_file($config['config']['CONFIG_FILE']) && !$isLoggedIn) {
33 throw new UnauthorizedConfigException(); 33 throw new UnauthorizedConfigException();
34 } 34 }
35 35
36 // Check that all mandatory fields are provided in $config. 36 // Check that all mandatory fields are provided in $config.
37 foreach ($MANDATORY_FIELDS as $field) { 37 foreach ($MANDATORY_FIELDS as $field) {
38 if (!isset($config[$field])) { 38 if (!isset($config[$field])) {
39 throw new MissingFieldConfigException($field); 39 throw new MissingFieldConfigException($field);
40 } 40 }
41 } 41 }
42 42
43 $configStr = '<?php '. PHP_EOL; 43 $configStr = '<?php '. PHP_EOL;
44 $configStr .= '$GLOBALS[\'login\'] = '.var_export($config['login'], true).';'. PHP_EOL; 44 $configStr .= '$GLOBALS[\'login\'] = '.var_export($config['login'], true).';'. PHP_EOL;
45 $configStr .= '$GLOBALS[\'hash\'] = '.var_export($config['hash'], true).';'. PHP_EOL; 45 $configStr .= '$GLOBALS[\'hash\'] = '.var_export($config['hash'], true).';'. PHP_EOL;
46 $configStr .= '$GLOBALS[\'salt\'] = '.var_export($config['salt'], true).'; '. PHP_EOL; 46 $configStr .= '$GLOBALS[\'salt\'] = '.var_export($config['salt'], true).'; '. PHP_EOL;
47 $configStr .= '$GLOBALS[\'timezone\'] = '.var_export($config['timezone'], true).';'. PHP_EOL; 47 $configStr .= '$GLOBALS[\'timezone\'] = '.var_export($config['timezone'], true).';'. PHP_EOL;
48 $configStr .= 'date_default_timezone_set('.var_export($config['timezone'], true).');'. PHP_EOL; 48 $configStr .= 'date_default_timezone_set('.var_export($config['timezone'], true).');'. PHP_EOL;
49 $configStr .= '$GLOBALS[\'title\'] = '.var_export($config['title'], true).';'. PHP_EOL; 49 $configStr .= '$GLOBALS[\'title\'] = '.var_export($config['title'], true).';'. PHP_EOL;
50 $configStr .= '$GLOBALS[\'titleLink\'] = '.var_export($config['titleLink'], true).'; '. PHP_EOL; 50 $configStr .= '$GLOBALS[\'titleLink\'] = '.var_export($config['titleLink'], true).'; '. PHP_EOL;
51 $configStr .= '$GLOBALS[\'redirector\'] = '.var_export($config['redirector'], true).'; '. PHP_EOL; 51 $configStr .= '$GLOBALS[\'redirector\'] = '.var_export($config['redirector'], true).'; '. PHP_EOL;
52 $configStr .= '$GLOBALS[\'disablesessionprotection\'] = '.var_export($config['disablesessionprotection'], true).'; '. PHP_EOL; 52 $configStr .= '$GLOBALS[\'disablesessionprotection\'] = '.var_export($config['disablesessionprotection'], true).'; '. PHP_EOL;
53 $configStr .= '$GLOBALS[\'privateLinkByDefault\'] = '.var_export($config['privateLinkByDefault'], true).'; '. PHP_EOL; 53 $configStr .= '$GLOBALS[\'privateLinkByDefault\'] = '.var_export($config['privateLinkByDefault'], true).'; '. PHP_EOL;
54 54
55 // Store all $config['config'] 55 // Store all $config['config']
56 foreach ($config['config'] as $key => $value) { 56 foreach ($config['config'] as $key => $value) {
57 $configStr .= '$GLOBALS[\'config\'][\''. $key .'\'] = '.var_export($config['config'][$key], true).';'. PHP_EOL; 57 $configStr .= '$GLOBALS[\'config\'][\''. $key .'\'] = '.var_export($config['config'][$key], true).';'. PHP_EOL;
58 } 58 }
59 $configStr .= '?>'; 59
60 60 if (isset($config['plugins'])) {
61 if (!file_put_contents($config['config']['CONFIG_FILE'], $configStr) 61 foreach ($config['plugins'] as $key => $value) {
62 || strcmp(file_get_contents($config['config']['CONFIG_FILE']), $configStr) != 0 62 $configStr .= '$GLOBALS[\'plugins\'][\''. $key .'\'] = '.var_export($config['plugins'][$key], true).';'. PHP_EOL;
63 ) { 63 }
64 throw new Exception( 64 }
65 'Shaarli could not create the config file. 65
66 Please make sure Shaarli has the right to write in the folder is it installed in.' 66 if (!file_put_contents($config['config']['CONFIG_FILE'], $configStr)
67 ); 67 || strcmp(file_get_contents($config['config']['CONFIG_FILE']), $configStr) != 0
68 } 68 ) {
69} 69 throw new Exception(
70 70 'Shaarli could not create the config file.
71/** 71 Please make sure Shaarli has the right to write in the folder is it installed in.'
72 * Milestone 0.9 - shaarli/Shaarli#41: options.php is not supported anymore. 72 );
73 * ==> if user is loggedIn, merge its content with config.php, then delete options.php. 73 }
74 * 74}
75 * @param array $config contains all configuration fields. 75
76 * @param bool $isLoggedIn true if user is logged in. 76/**
77 * 77 * Milestone 0.9 - shaarli/Shaarli#41: options.php is not supported anymore.
78 * @return void 78 * ==> if user is loggedIn, merge its content with config.php, then delete options.php.
79 */ 79 *
80function mergeDeprecatedConfig($config, $isLoggedIn) 80 * @param array $config contains all configuration fields.
81{ 81 * @param bool $isLoggedIn true if user is logged in.
82 $config_file = $config['config']['CONFIG_FILE']; 82 *
83 83 * @return void
84 if (is_file($config['config']['DATADIR'].'/options.php') && $isLoggedIn) { 84 */
85 include $config['config']['DATADIR'].'/options.php'; 85function mergeDeprecatedConfig($config, $isLoggedIn)
86 86{
87 // Load GLOBALS into config 87 $config_file = $config['config']['CONFIG_FILE'];
88 foreach ($GLOBALS as $key => $value) { 88
89 $config[$key] = $value; 89 if (is_file($config['config']['DATADIR'].'/options.php') && $isLoggedIn) {
90 } 90 include $config['config']['DATADIR'].'/options.php';
91 $config['config']['CONFIG_FILE'] = $config_file; 91
92 writeConfig($config, $isLoggedIn); 92 // Load GLOBALS into config
93 93 foreach ($GLOBALS as $key => $value) {
94 unlink($config['config']['DATADIR'].'/options.php'); 94 $config[$key] = $value;
95 } 95 }
96} 96 $config['config']['CONFIG_FILE'] = $config_file;
97 97 writeConfig($config, $isLoggedIn);
98/** 98
99 * Exception used if a mandatory field is missing in given configuration. 99 unlink($config['config']['DATADIR'].'/options.php');
100 */ 100 }
101class MissingFieldConfigException extends Exception 101}
102{ 102
103 public $field; 103/**
104 104 * Exception used if a mandatory field is missing in given configuration.
105 /** 105 */
106 * Construct exception. 106class MissingFieldConfigException extends Exception
107 * 107{
108 * @param string $field field name missing. 108 public $field;
109 */ 109
110 public function __construct($field) 110 /**
111 { 111 * Construct exception.
112 $this->field = $field; 112 *
113 $this->message = 'Configuration value is required for '. $this->field; 113 * @param string $field field name missing.
114 } 114 */
115} 115 public function __construct($field)
116 116 {
117/** 117 $this->field = $field;
118 * Exception used if an unauthorized attempt to edit configuration has been made. 118 $this->message = 'Configuration value is required for '. $this->field;
119 */ 119 }
120class UnauthorizedConfigException extends Exception 120}
121{ 121
122 /** 122/**
123 * Construct exception. 123 * Exception used if an unauthorized attempt to edit configuration has been made.
124 */ 124 */
125 public function __construct() 125class UnauthorizedConfigException extends Exception
126 { 126{
127 $this->message = 'You are not authorized to alter config.'; 127 /**
128 } 128 * Construct exception.
129} 129 */
130 public function __construct()
131 {
132 $this->message = 'You are not authorized to alter config.';
133 }
134}
diff --git a/application/PluginManager.php b/application/PluginManager.php
new file mode 100644
index 00000000..e572ff7c
--- /dev/null
+++ b/application/PluginManager.php
@@ -0,0 +1,184 @@
1<?php
2
3/**
4 * Class PluginManager
5 *
6 * Use to manage, load and execute plugins.
7 *
8 * Using Singleton design pattern.
9 */
10class PluginManager
11{
12 /**
13 * PluginManager singleton instance.
14 * @var PluginManager $instance
15 */
16 private static $instance;
17
18 /**
19 * List of authorized plugins from configuration file.
20 * @var array $authorizedPlugins
21 */
22 private $authorizedPlugins;
23
24 /**
25 * List of loaded plugins.
26 * @var array $loadedPlugins
27 */
28 private $loadedPlugins = array();
29
30 /**
31 * Plugins subdirectory.
32 * @var string $PLUGINS_PATH
33 */
34 public static $PLUGINS_PATH = 'plugins';
35
36 /**
37 * Private constructor: new instances not allowed.
38 */
39 private function __construct()
40 {
41 }
42
43 /**
44 * Cloning isn't allowed either.
45 *
46 * @return void
47 */
48 private function __clone()
49 {
50 }
51
52 /**
53 * Return existing instance of PluginManager, or create it.
54 *
55 * @return PluginManager instance.
56 */
57 public static function getInstance()
58 {
59 if (!(self::$instance instanceof self)) {
60 self::$instance = new self();
61 }
62
63 return self::$instance;
64 }
65
66 /**
67 * Load plugins listed in $authorizedPlugins.
68 *
69 * @param array $authorizedPlugins Names of plugin authorized to be loaded.
70 *
71 * @return void
72 */
73 public function load($authorizedPlugins)
74 {
75 $this->authorizedPlugins = $authorizedPlugins;
76
77 $dirs = glob(self::$PLUGINS_PATH . '/*', GLOB_ONLYDIR);
78 $dirnames = array_map('basename', $dirs);
79 foreach ($this->authorizedPlugins as $plugin) {
80 $index = array_search($plugin, $dirnames);
81
82 // plugin authorized, but its folder isn't listed
83 if ($index === false) {
84 continue;
85 }
86
87 try {
88 $this->loadPlugin($dirs[$index], $plugin);
89 }
90 catch (PluginFileNotFoundException $e) {
91 error_log($e->getMessage());
92 }
93 }
94 }
95
96 /**
97 * Execute all plugins registered hook.
98 *
99 * @param string $hook name of the hook to trigger.
100 * @param array $data list of data to manipulate passed by reference.
101 * @param array $params additional parameters such as page target.
102 *
103 * @return void
104 */
105 public function executeHooks($hook, &$data, $params = array())
106 {
107 if (!empty($params['target'])) {
108 $data['_PAGE_'] = $params['target'];
109 }
110
111 if (isset($params['loggedin'])) {
112 $data['_LOGGEDIN_'] = $params['loggedin'];
113 }
114
115 foreach ($this->loadedPlugins as $plugin) {
116 $hookFunction = $this->buildHookName($hook, $plugin);
117
118 if (function_exists($hookFunction)) {
119 $data = call_user_func($hookFunction, $data);
120 }
121 }
122 }
123
124 /**
125 * Load a single plugin from its files.
126 * Add them in $loadedPlugins if successful.
127 *
128 * @param string $dir plugin's directory.
129 * @param string $pluginName plugin's name.
130 *
131 * @return void
132 * @throws PluginFileNotFoundException - plugin files not found.
133 */
134 private function loadPlugin($dir, $pluginName)
135 {
136 if (!is_dir($dir)) {
137 throw new PluginFileNotFoundException($pluginName);
138 }
139
140 $pluginFilePath = $dir . '/' . $pluginName . '.php';
141 if (!is_file($pluginFilePath)) {
142 throw new PluginFileNotFoundException($pluginName);
143 }
144
145 include_once $pluginFilePath;
146
147 $this->loadedPlugins[] = $pluginName;
148 }
149
150 /**
151 * Construct normalize hook name for a specific plugin.
152 *
153 * Format:
154 * hook_<plugin_name>_<hook_name>
155 *
156 * @param string $hook hook name.
157 * @param string $pluginName plugin name.
158 *
159 * @return string - plugin's hook name.
160 */
161 public function buildHookName($hook, $pluginName)
162 {
163 return 'hook_' . $pluginName . '_' . $hook;
164 }
165}
166
167/**
168 * Class PluginFileNotFoundException
169 *
170 * Raise when plugin files can't be found.
171 */
172class PluginFileNotFoundException extends Exception
173{
174 /**
175 * Construct exception with plugin name.
176 * Generate message.
177 *
178 * @param string $pluginName name of the plugin not found
179 */
180 public function __construct($pluginName)
181 {
182 $this->message = 'Plugin "'. $pluginName .'" files not found.';
183 }
184} \ No newline at end of file
diff --git a/application/Router.php b/application/Router.php
new file mode 100644
index 00000000..82b2b858
--- /dev/null
+++ b/application/Router.php
@@ -0,0 +1,105 @@
1<?php
2
3/**
4 * Class Router
5 *
6 * (only displayable pages here)
7 */
8class Router
9{
10 public static $PAGE_LOGIN = 'login';
11
12 public static $PAGE_PICWALL = 'picwall';
13
14 public static $PAGE_TAGCLOUD = 'tagcloud';
15
16 public static $PAGE_TOOLS = 'tools';
17
18 public static $PAGE_CHANGEPASSWORD = 'changepasswd';
19
20 public static $PAGE_CONFIGURE = 'configure';
21
22 public static $PAGE_CHANGETAG = 'changetag';
23
24 public static $PAGE_ADDLINK = 'addlink';
25
26 public static $PAGE_EDITLINK = 'edit_link';
27
28 public static $PAGE_EXPORT = 'export';
29
30 public static $PAGE_IMPORT = 'import';
31
32 public static $PAGE_LINKLIST = 'linklist';
33
34 /**
35 * Reproducing renderPage() if hell, to avoid regression.
36 *
37 * This highlights how bad this needs to be rewrite,
38 * but let's focus on plugins for now.
39 *
40 * @param string $query $_SERVER['QUERY_STRING'].
41 * @param array $get $_SERVER['GET'].
42 * @param bool $loggedIn true if authenticated user.
43 *
44 * @return self::page found.
45 */
46 public static function findPage($query, $get, $loggedIn)
47 {
48 $loggedIn = ($loggedIn === true) ? true : false;
49
50 if (empty($query) && !isset($get['edit_link']) && !isset($get['post'])) {
51 return self::$PAGE_LINKLIST;
52 }
53
54 if (startswith($query, 'do='. self::$PAGE_LOGIN) && $loggedIn === false) {
55 return self::$PAGE_LOGIN;
56 }
57
58 if (startswith($query, 'do='. self::$PAGE_PICWALL)) {
59 return self::$PAGE_PICWALL;
60 }
61
62 if (startswith($query, 'do='. self::$PAGE_TAGCLOUD)) {
63 return self::$PAGE_TAGCLOUD;
64 }
65
66 // At this point, only loggedin pages.
67 if (!$loggedIn) {
68 return self::$PAGE_LINKLIST;
69 }
70
71 if (startswith($query, 'do='. self::$PAGE_TOOLS)) {
72 return self::$PAGE_TOOLS;
73 }
74
75 if (startswith($query, 'do='. self::$PAGE_CHANGEPASSWORD)) {
76 return self::$PAGE_CHANGEPASSWORD;
77 }
78
79 if (startswith($query, 'do='. self::$PAGE_CONFIGURE)) {
80 return self::$PAGE_CONFIGURE;
81 }
82
83 if (startswith($query, 'do='. self::$PAGE_CHANGETAG)) {
84 return self::$PAGE_CHANGETAG;
85 }
86
87 if (startswith($query, 'do='. self::$PAGE_ADDLINK)) {
88 return self::$PAGE_ADDLINK;
89 }
90
91 if (isset($get['edit_link']) || isset($get['post'])) {
92 return self::$PAGE_EDITLINK;
93 }
94
95 if (startswith($query, 'do='. self::$PAGE_EXPORT)) {
96 return self::$PAGE_EXPORT;
97 }
98
99 if (startswith($query, 'do='. self::$PAGE_IMPORT)) {
100 return self::$PAGE_IMPORT;
101 }
102
103 return self::$PAGE_LINKLIST;
104 }
105} \ No newline at end of file
diff --git a/index.php b/index.php
index c430a200..c82735ca 100755
--- a/index.php
+++ b/index.php
@@ -45,9 +45,18 @@ $GLOBALS['config']['RAINTPL_TPL'] = 'tpl/' ; // Raintpl template directory (keep
45$GLOBALS['config']['UPDATECHECK_FILENAME'] = $GLOBALS['config']['DATADIR'].'/lastupdatecheck.txt'; // For updates check of Shaarli. 45$GLOBALS['config']['UPDATECHECK_FILENAME'] = $GLOBALS['config']['DATADIR'].'/lastupdatecheck.txt'; // For updates check of Shaarli.
46$GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400 ; // Updates check frequency for Shaarli. 86400 seconds=24 hours 46$GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400 ; // Updates check frequency for Shaarli. 86400 seconds=24 hours
47 // Note: You must have publisher.php in the same directory as Shaarli index.php 47 // Note: You must have publisher.php in the same directory as Shaarli index.php
48$GLOBALS['config']['ARCHIVE_ORG'] = false; // For each link, add a link to an archived version on archive.org
49$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. 48$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.
50$GLOBALS['config']['HIDE_PUBLIC_LINKS'] = false; 49$GLOBALS['config']['HIDE_PUBLIC_LINKS'] = false;
50//$GLOBALS['config']['ENABLED_PLUGINS'] = array(
51// 'qrcode', 'archiveorg', 'readityourself', 'demo_plugin', 'playvideos',
52// 'wallabag', 'markdown', 'addlink_toolbar',
53//);
54// Warning: order matters.
55$GLOBALS['config']['ENABLED_PLUGINS'] = array('qrcode');
56
57// Default plugins, default config - will be overriden by config.php and then plugin's config.php file.
58$GLOBALS['plugins']['READITYOUSELF_URL'] = 'http://someurl.com';
59$GLOBALS['plugins']['WALLABAG_URL'] = 'https://demo.wallabag.org/';
51// ----------------------------------------------------------------------------------------------- 60// -----------------------------------------------------------------------------------------------
52define('shaarli_version', '0.5.4'); 61define('shaarli_version', '0.5.4');
53// http://server.com/x/shaarli --> /shaarli/ 62// http://server.com/x/shaarli --> /shaarli/
@@ -75,6 +84,8 @@ require_once 'application/TimeZone.php';
75require_once 'application/Url.php'; 84require_once 'application/Url.php';
76require_once 'application/Utils.php'; 85require_once 'application/Utils.php';
77require_once 'application/Config.php'; 86require_once 'application/Config.php';
87require_once 'application/PluginManager.php';
88require_once 'application/Router.php';
78 89
79// Ensure the PHP version is supported 90// Ensure the PHP version is supported
80try { 91try {
@@ -119,6 +130,9 @@ include "inc/rain.tpl.class.php"; //include Rain TPL
119raintpl::$tpl_dir = $GLOBALS['config']['RAINTPL_TPL']; // template directory 130raintpl::$tpl_dir = $GLOBALS['config']['RAINTPL_TPL']; // template directory
120raintpl::$cache_dir = $GLOBALS['config']['RAINTPL_TMP']; // cache directory 131raintpl::$cache_dir = $GLOBALS['config']['RAINTPL_TMP']; // cache directory
121 132
133$pluginManager = PluginManager::getInstance();
134$pluginManager->load($GLOBALS['config']['ENABLED_PLUGINS']);
135
122ob_start(); // Output buffering for the page cache. 136ob_start(); // Output buffering for the page cache.
123 137
124 138
@@ -962,16 +976,31 @@ function showDaily()
962 $fill[$index]+=$length; 976 $fill[$index]+=$length;
963 } 977 }
964 $PAGE = new pageBuilder; 978 $PAGE = new pageBuilder;
965 $PAGE->assign('linksToDisplay',$linksToDisplay); 979 $data = array(
966 $PAGE->assign('linkcount',count($LINKSDB)); 980 'linksToDisplay' => $linksToDisplay,
967 $PAGE->assign('cols', $columns); 981 'linkcount' => count($LINKSDB),
968 $PAGE->assign('day',linkdate2timestamp($day.'_000000')); 982 'cols' => $columns,
969 $PAGE->assign('previousday',$previousday); 983 'day' => linkdate2timestamp($day.'_000000'),
970 $PAGE->assign('nextday',$nextday); 984 'previousday' => $previousday,
985 'nextday' => $nextday,
986 );
987 $pluginManager = PluginManager::getInstance();
988 $pluginManager->executeHooks('render_daily', $data, array('loggedin' => isLoggedIn()));
989
990 foreach ($data as $key => $value) {
991 $PAGE->assign($key, $value);
992 }
993
971 $PAGE->renderPage('daily'); 994 $PAGE->renderPage('daily');
972 exit; 995 exit;
973} 996}
974 997
998// Renders the linklist
999function showLinkList($PAGE, $LINKSDB) {
1000 buildLinkList($PAGE,$LINKSDB); // Compute list of links to display
1001 $PAGE->renderPage('linklist');
1002}
1003
975 1004
976// ------------------------------------------------------------------------------------------ 1005// ------------------------------------------------------------------------------------------
977// Render HTML page (according to URL parameters and user rights) 1006// Render HTML page (according to URL parameters and user rights)
@@ -983,12 +1012,36 @@ function renderPage()
983 $GLOBALS['config']['HIDE_PUBLIC_LINKS'] 1012 $GLOBALS['config']['HIDE_PUBLIC_LINKS']
984 ); 1013 );
985 1014
1015 $PAGE = new pageBuilder;
1016
1017 // Determine which page will be rendered.
1018 $query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : '';
1019 $targetPage = Router::findPage($query, $_GET, isLoggedIn());
1020
1021 // Call plugin hooks for header, footer and includes, specifying which page will be rendered.
1022 // Then assign generated data to RainTPL.
1023 $common_hooks = array(
1024 'header',
1025 'footer',
1026 'includes',
1027 );
1028 $pluginManager = PluginManager::getInstance();
1029 foreach($common_hooks as $name) {
1030 $plugin_data = array();
1031 $pluginManager->executeHooks('render_' . $name, $plugin_data,
1032 array(
1033 'target' => $targetPage,
1034 'loggedin' => isLoggedIn()
1035 )
1036 );
1037 $PAGE->assign('plugins_' . $name, $plugin_data);
1038 }
1039
986 // -------- Display login form. 1040 // -------- Display login form.
987 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=login')) 1041 if ($targetPage == Router::$PAGE_LOGIN)
988 { 1042 {
989 if ($GLOBALS['config']['OPEN_SHAARLI']) { header('Location: ?'); exit; } // No need to login for open Shaarli 1043 if ($GLOBALS['config']['OPEN_SHAARLI']) { header('Location: ?'); exit; } // No need to login for open Shaarli
990 $token=''; if (ban_canLogin()) $token=getToken(); // Do not waste token generation if not useful. 1044 $token=''; if (ban_canLogin()) $token=getToken(); // Do not waste token generation if not useful.
991 $PAGE = new pageBuilder;
992 $PAGE->assign('token',$token); 1045 $PAGE->assign('token',$token);
993 $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):'')); 1046 $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):''));
994 $PAGE->renderPage('loginform'); 1047 $PAGE->renderPage('loginform');
@@ -1004,7 +1057,7 @@ function renderPage()
1004 } 1057 }
1005 1058
1006 // -------- Picture wall 1059 // -------- Picture wall
1007 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=picwall')) 1060 if ($targetPage == Router::$PAGE_PICWALL)
1008 { 1061 {
1009 // Optionally filter the results: 1062 // Optionally filter the results:
1010 $links=array(); 1063 $links=array();
@@ -1027,15 +1080,22 @@ function renderPage()
1027 } 1080 }
1028 } 1081 }
1029 1082
1030 $PAGE = new pageBuilder; 1083 $data = array(
1031 $PAGE->assign('linkcount',count($LINKSDB)); 1084 'linkcount' => count($LINKSDB),
1032 $PAGE->assign('linksToDisplay',$linksToDisplay); 1085 'linksToDisplay' => $linksToDisplay,
1086 );
1087 $pluginManager->executeHooks('render_picwall', $data, array('loggedin' => isLoggedIn()));
1088
1089 foreach ($data as $key => $value) {
1090 $PAGE->assign($key, $value);
1091 }
1092
1033 $PAGE->renderPage('picwall'); 1093 $PAGE->renderPage('picwall');
1034 exit; 1094 exit;
1035 } 1095 }
1036 1096
1037 // -------- Tag cloud 1097 // -------- Tag cloud
1038 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=tagcloud')) 1098 if ($targetPage == Router::$PAGE_TAGCLOUD)
1039 { 1099 {
1040 $tags= $LINKSDB->allTags(); 1100 $tags= $LINKSDB->allTags();
1041 1101
@@ -1049,9 +1109,17 @@ function renderPage()
1049 { 1109 {
1050 $tagList[$key] = array('count'=>$value,'size'=>log($value, 15) / log($maxcount, 30) * (22-6) + 6); 1110 $tagList[$key] = array('count'=>$value,'size'=>log($value, 15) / log($maxcount, 30) * (22-6) + 6);
1051 } 1111 }
1052 $PAGE = new pageBuilder; 1112
1053 $PAGE->assign('linkcount',count($LINKSDB)); 1113 $data = array(
1054 $PAGE->assign('tags',$tagList); 1114 'linkcount' => count($LINKSDB),
1115 'tags' => $tagList,
1116 );
1117 $pluginManager->executeHooks('render_tagcloud', $data, array('loggedin' => isLoggedIn()));
1118
1119 foreach ($data as $key => $value) {
1120 $PAGE->assign($key, $value);
1121 }
1122
1055 $PAGE->renderPage('tagcloud'); 1123 $PAGE->renderPage('tagcloud');
1056 exit; 1124 exit;
1057 } 1125 }
@@ -1164,32 +1232,36 @@ function renderPage()
1164 header('Location: ?do=login&post='); 1232 header('Location: ?do=login&post=');
1165 exit; 1233 exit;
1166 } 1234 }
1167 1235 showLinkList($PAGE, $LINKSDB);
1168 if (isset($_GET['edit_link'])) { 1236 if (isset($_GET['edit_link'])) {
1169 header('Location: ?do=login&edit_link='. escape($_GET['edit_link'])); 1237 header('Location: ?do=login&edit_link='. escape($_GET['edit_link']));
1170 exit; 1238 exit;
1171 } 1239 }
1172 1240
1173 $PAGE = new pageBuilder;
1174 buildLinkList($PAGE,$LINKSDB); // Compute list of links to display
1175 $PAGE->renderPage('linklist');
1176 exit; // Never remove this one! All operations below are reserved for logged in user. 1241 exit; // Never remove this one! All operations below are reserved for logged in user.
1177 } 1242 }
1178 1243
1179 // -------- All other functions are reserved for the registered user: 1244 // -------- All other functions are reserved for the registered user:
1180 1245
1181 // -------- Display the Tools menu if requested (import/export/bookmarklet...) 1246 // -------- Display the Tools menu if requested (import/export/bookmarklet...)
1182 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=tools')) 1247 if ($targetPage == Router::$PAGE_TOOLS)
1183 { 1248 {
1184 $PAGE = new pageBuilder; 1249 $data = array(
1185 $PAGE->assign('linkcount',count($LINKSDB)); 1250 'linkcount' => count($LINKSDB),
1186 $PAGE->assign('pageabsaddr',index_url($_SERVER)); 1251 'pageabsaddr' => index_url($_SERVER),
1252 );
1253 $pluginManager->executeHooks('render_tools', $data);
1254
1255 foreach ($data as $key => $value) {
1256 $PAGE->assign($key, $value);
1257 }
1258
1187 $PAGE->renderPage('tools'); 1259 $PAGE->renderPage('tools');
1188 exit; 1260 exit;
1189 } 1261 }
1190 1262
1191 // -------- User wants to change his/her password. 1263 // -------- User wants to change his/her password.
1192 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=changepasswd')) 1264 if ($targetPage == Router::$PAGE_CHANGEPASSWORD)
1193 { 1265 {
1194 if ($GLOBALS['config']['OPEN_SHAARLI']) die('You are not supposed to change a password on an Open Shaarli.'); 1266 if ($GLOBALS['config']['OPEN_SHAARLI']) die('You are not supposed to change a password on an Open Shaarli.');
1195 if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) 1267 if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword']))
@@ -1220,7 +1292,6 @@ function renderPage()
1220 } 1292 }
1221 else // show the change password form. 1293 else // show the change password form.
1222 { 1294 {
1223 $PAGE = new pageBuilder;
1224 $PAGE->assign('linkcount',count($LINKSDB)); 1295 $PAGE->assign('linkcount',count($LINKSDB));
1225 $PAGE->assign('token',getToken()); 1296 $PAGE->assign('token',getToken());
1226 $PAGE->renderPage('changepassword'); 1297 $PAGE->renderPage('changepassword');
@@ -1229,7 +1300,7 @@ function renderPage()
1229 } 1300 }
1230 1301
1231 // -------- User wants to change configuration 1302 // -------- User wants to change configuration
1232 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=configure')) 1303 if ($targetPage == Router::$PAGE_CONFIGURE)
1233 { 1304 {
1234 if (!empty($_POST['title']) ) 1305 if (!empty($_POST['title']) )
1235 { 1306 {
@@ -1265,7 +1336,6 @@ function renderPage()
1265 } 1336 }
1266 else // Show the configuration form. 1337 else // Show the configuration form.
1267 { 1338 {
1268 $PAGE = new pageBuilder;
1269 $PAGE->assign('linkcount',count($LINKSDB)); 1339 $PAGE->assign('linkcount',count($LINKSDB));
1270 $PAGE->assign('token',getToken()); 1340 $PAGE->assign('token',getToken());
1271 $PAGE->assign('title', empty($GLOBALS['title']) ? '' : $GLOBALS['title'] ); 1341 $PAGE->assign('title', empty($GLOBALS['title']) ? '' : $GLOBALS['title'] );
@@ -1279,11 +1349,10 @@ function renderPage()
1279 } 1349 }
1280 1350
1281 // -------- User wants to rename a tag or delete it 1351 // -------- User wants to rename a tag or delete it
1282 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=changetag')) 1352 if ($targetPage == Router::$PAGE_CHANGETAG)
1283 { 1353 {
1284 if (empty($_POST['fromtag'])) 1354 if (empty($_POST['fromtag']))
1285 { 1355 {
1286 $PAGE = new pageBuilder;
1287 $PAGE->assign('linkcount',count($LINKSDB)); 1356 $PAGE->assign('linkcount',count($LINKSDB));
1288 $PAGE->assign('token',getToken()); 1357 $PAGE->assign('token',getToken());
1289 $PAGE->assign('tags', $LINKSDB->allTags()); 1358 $PAGE->assign('tags', $LINKSDB->allTags());
@@ -1328,9 +1397,8 @@ function renderPage()
1328 } 1397 }
1329 1398
1330 // -------- User wants to add a link without using the bookmarklet: Show form. 1399 // -------- User wants to add a link without using the bookmarklet: Show form.
1331 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=addlink')) 1400 if ($targetPage == Router::$PAGE_ADDLINK)
1332 { 1401 {
1333 $PAGE = new pageBuilder;
1334 $PAGE->assign('linkcount',count($LINKSDB)); 1402 $PAGE->assign('linkcount',count($LINKSDB));
1335 $PAGE->renderPage('addlink'); 1403 $PAGE->renderPage('addlink');
1336 exit; 1404 exit;
@@ -1349,6 +1417,9 @@ function renderPage()
1349 $link = array('title'=>trim($_POST['lf_title']),'url'=>$url,'description'=>trim($_POST['lf_description']),'private'=>(isset($_POST['lf_private']) ? 1 : 0), 1417 $link = array('title'=>trim($_POST['lf_title']),'url'=>$url,'description'=>trim($_POST['lf_description']),'private'=>(isset($_POST['lf_private']) ? 1 : 0),
1350 'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags)); 1418 'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags));
1351 if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title. 1419 if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title.
1420
1421 $pluginManager->executeHooks('save_link', $link);
1422
1352 $LINKSDB[$linkdate] = $link; 1423 $LINKSDB[$linkdate] = $link;
1353 $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // Save to disk. 1424 $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // Save to disk.
1354 pubsubhub(); 1425 pubsubhub();
@@ -1382,6 +1453,9 @@ function renderPage()
1382 // - confirmation is handled by JavaScript 1453 // - confirmation is handled by JavaScript
1383 // - we are protected from XSRF by the token. 1454 // - we are protected from XSRF by the token.
1384 $linkdate=$_POST['lf_linkdate']; 1455 $linkdate=$_POST['lf_linkdate'];
1456
1457 $pluginManager->executeHooks('delete_link', $LINKSDB[$linkdate]);
1458
1385 unset($LINKSDB[$linkdate]); 1459 unset($LINKSDB[$linkdate]);
1386 $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // save to disk 1460 $LINKSDB->savedb($GLOBALS['config']['PAGECACHE']); // save to disk
1387 1461
@@ -1423,13 +1497,20 @@ function renderPage()
1423 { 1497 {
1424 $link = $LINKSDB[$_GET['edit_link']]; // Read database 1498 $link = $LINKSDB[$_GET['edit_link']]; // Read database
1425 if (!$link) { header('Location: ?'); exit; } // Link not found in database. 1499 if (!$link) { header('Location: ?'); exit; } // Link not found in database.
1426 $PAGE = new pageBuilder; 1500 $data = array(
1427 $PAGE->assign('linkcount',count($LINKSDB)); 1501 'linkcount' => count($LINKSDB),
1428 $PAGE->assign('link',$link); 1502 'link' => $link,
1429 $PAGE->assign('link_is_new',false); 1503 'link_is_new' => false,
1430 $PAGE->assign('token',getToken()); // XSRF protection. 1504 'token' => getToken(),
1431 $PAGE->assign('http_referer',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : '')); 1505 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''),
1432 $PAGE->assign('tags', $LINKSDB->allTags()); 1506 'tags' => $LINKSDB->allTags(),
1507 );
1508 $pluginManager->executeHooks('render_editlink', $data);
1509
1510 foreach ($data as $key => $value) {
1511 $PAGE->assign($key, $value);
1512 }
1513
1433 $PAGE->renderPage('editlink'); 1514 $PAGE->renderPage('editlink');
1434 exit; 1515 exit;
1435 } 1516 }
@@ -1493,24 +1574,30 @@ function renderPage()
1493 ); 1574 );
1494 } 1575 }
1495 1576
1496 $PAGE = new pageBuilder; 1577 $data = array(
1497 $PAGE->assign('linkcount',count($LINKSDB)); 1578 'linkcount' => count($LINKSDB),
1498 $PAGE->assign('link',$link); 1579 'link' => $link,
1499 $PAGE->assign('link_is_new',$link_is_new); 1580 'link_is_new' => $link_is_new,
1500 $PAGE->assign('token',getToken()); // XSRF protection. 1581 'token' => getToken(), // XSRF protection.
1501 $PAGE->assign('http_referer',(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '')); 1582 'http_referer' => (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']) : ''),
1502 $PAGE->assign('source',(isset($_GET['source']) ? $_GET['source'] : '')); 1583 'source' => (isset($_GET['source']) ? $_GET['source'] : ''),
1503 $PAGE->assign('tags', $LINKSDB->allTags()); 1584 'tags' => $LINKSDB->allTags(),
1585 );
1586 $pluginManager->executeHooks('render_editlink', $data);
1587
1588 foreach ($data as $key => $value) {
1589 $PAGE->assign($key, $value);
1590 }
1591
1504 $PAGE->renderPage('editlink'); 1592 $PAGE->renderPage('editlink');
1505 exit; 1593 exit;
1506 } 1594 }
1507 1595
1508 // -------- Export as Netscape Bookmarks HTML file. 1596 // -------- Export as Netscape Bookmarks HTML file.
1509 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=export')) 1597 if ($targetPage == Router::$PAGE_EXPORT)
1510 { 1598 {
1511 if (empty($_GET['what'])) 1599 if (empty($_GET['what']))
1512 { 1600 {
1513 $PAGE = new pageBuilder;
1514 $PAGE->assign('linkcount',count($LINKSDB)); 1601 $PAGE->assign('linkcount',count($LINKSDB));
1515 $PAGE->renderPage('export'); 1602 $PAGE->renderPage('export');
1516 exit; 1603 exit;
@@ -1562,9 +1649,8 @@ HTML;
1562 } 1649 }
1563 1650
1564 // -------- Show upload/import dialog: 1651 // -------- Show upload/import dialog:
1565 if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=import')) 1652 if ($targetPage == Router::$PAGE_IMPORT)
1566 { 1653 {
1567 $PAGE = new pageBuilder;
1568 $PAGE->assign('linkcount',count($LINKSDB)); 1654 $PAGE->assign('linkcount',count($LINKSDB));
1569 $PAGE->assign('token',getToken()); 1655 $PAGE->assign('token',getToken());
1570 $PAGE->assign('maxfilesize',getMaxFileSize()); 1656 $PAGE->assign('maxfilesize',getMaxFileSize());
@@ -1573,9 +1659,7 @@ HTML;
1573 } 1659 }
1574 1660
1575 // -------- Otherwise, simply display search form and links: 1661 // -------- Otherwise, simply display search form and links:
1576 $PAGE = new pageBuilder; 1662 showLinkList($PAGE, $LINKSDB);
1577 buildLinkList($PAGE,$LINKSDB); // Compute list of links to display
1578 $PAGE->renderPage('linklist');
1579 exit; 1663 exit;
1580} 1664}
1581 1665
@@ -1746,7 +1830,7 @@ function buildLinkList($PAGE,$LINKSDB)
1746 $taglist = explode(' ',$link['tags']); 1830 $taglist = explode(' ',$link['tags']);
1747 uasort($taglist, 'strcasecmp'); 1831 uasort($taglist, 'strcasecmp');
1748 $link['taglist']=$taglist; 1832 $link['taglist']=$taglist;
1749 1833 $link['shorturl'] = smallHash($link['linkdate']);
1750 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. 1834 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.
1751 strlen($link["url"]) === 7) { 1835 strlen($link["url"]) === 7) {
1752 $link["url"] = index_url($_SERVER) . $link["url"]; 1836 $link["url"] = index_url($_SERVER) . $link["url"];
@@ -1766,18 +1850,28 @@ function buildLinkList($PAGE,$LINKSDB)
1766 $token = ''; if (isLoggedIn()) $token=getToken(); 1850 $token = ''; if (isLoggedIn()) $token=getToken();
1767 1851
1768 // Fill all template fields. 1852 // Fill all template fields.
1769 $PAGE->assign('linkcount',count($LINKSDB)); 1853 $data = array(
1770 $PAGE->assign('previous_page_url',$previous_page_url); 1854 'linkcount' => count($LINKSDB),
1771 $PAGE->assign('next_page_url',$next_page_url); 1855 'previous_page_url' => $previous_page_url,
1772 $PAGE->assign('page_current',$page); 1856 'next_page_url' => $next_page_url,
1773 $PAGE->assign('page_max',$pagecount); 1857 'page_current' => $page,
1774 $PAGE->assign('result_count',count($linksToDisplay)); 1858 'page_max' => $pagecount,
1775 $PAGE->assign('search_type',$search_type); 1859 'result_count' => count($linksToDisplay),
1776 $PAGE->assign('search_crits',$search_crits); 1860 'search_type' => $search_type,
1777 $PAGE->assign('redirector',empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector']); // Optional redirector URL. 1861 'search_crits' => $search_crits,
1778 $PAGE->assign('token',$token); 1862 'redirector' => empty($GLOBALS['redirector']) ? '' : $GLOBALS['redirector'], // Optional redirector URL.
1779 $PAGE->assign('links',$linkDisp); 1863 'token' => $token,
1780 $PAGE->assign('tags', $LINKSDB->allTags()); 1864 'links' => $linkDisp,
1865 'tags' => $LINKSDB->allTags(),
1866 );
1867
1868 $pluginManager = PluginManager::getInstance();
1869 $pluginManager->executeHooks('render_linklist', $data, array('loggedin' => isLoggedIn()));
1870
1871 foreach ($data as $key => $value) {
1872 $PAGE->assign($key, $value);
1873 }
1874
1781 return; 1875 return;
1782} 1876}
1783 1877
diff --git a/tests/PluginManagerTest.php b/tests/PluginManagerTest.php
new file mode 100755
index 00000000..749ce2b5
--- /dev/null
+++ b/tests/PluginManagerTest.php
@@ -0,0 +1,66 @@
1<?php
2
3/**
4 * Plugin Manager tests
5 */
6
7require_once 'application/PluginManager.php';
8
9/**
10 * Unit tests for Plugins
11 */
12class PluginManagerTest extends PHPUnit_Framework_TestCase
13{
14 /**
15 * Path to tests plugin.
16 * @var string $_PLUGIN_PATH
17 */
18 private static $_PLUGIN_PATH = 'tests/plugins';
19
20 /**
21 * Test plugin.
22 * @var string $_PLUGIN_NAME
23 */
24 private static $_PLUGIN_NAME = 'test';
25
26 /**
27 * Test plugin loading and hook execution.
28 *
29 * @return void
30 */
31 public function testPlugin()
32 {
33 $pluginManager = PluginManager::getInstance();
34
35 PluginManager::$PLUGINS_PATH = self::$_PLUGIN_PATH;
36 $pluginManager->load(array(self::$_PLUGIN_NAME));
37
38 $this->assertTrue(function_exists('hook_test_random'));
39
40 $data = array(0 => 'woot');
41 $pluginManager->executeHooks('random', $data);
42 $this->assertEquals('woot', $data[1]);
43
44 $data = array(0 => 'woot');
45 $pluginManager->executeHooks('random', $data, array('target' => 'test'));
46 $this->assertEquals('page test', $data[1]);
47
48 $data = array(0 => 'woot');
49 $pluginManager->executeHooks('random', $data, array('loggedin' => true));
50 $this->assertEquals('loggedin', $data[1]);
51 }
52
53 /**
54 * Test missing plugin loading.
55 *
56 * @return void
57 */
58 public function testPluginNotFound()
59 {
60 $pluginManager = PluginManager::getInstance();
61
62 $pluginManager->load(array());
63
64 $pluginManager->load(array('nope', 'renope'));
65 }
66} \ No newline at end of file
diff --git a/tests/RouterTest.php b/tests/RouterTest.php
new file mode 100755
index 00000000..8838bc8d
--- /dev/null
+++ b/tests/RouterTest.php
@@ -0,0 +1,515 @@
1<?php
2
3/**
4 * Router tests
5 */
6
7require_once 'application/Router.php';
8
9/**
10 * Unit tests for Router
11 */
12class RouterTest extends PHPUnit_Framework_TestCase
13{
14 /**
15 * Test findPage: login page output.
16 * Valid: page should be return.
17 *
18 * @return void
19 */
20 public function testFindPageLoginValid()
21 {
22 $this->assertEquals(
23 Router::$PAGE_LOGIN,
24 Router::findPage('do=login', array(), false)
25 );
26
27 $this->assertEquals(
28 Router::$PAGE_LOGIN,
29 Router::findPage('do=login', array(), 1)
30 );
31
32 $this->assertEquals(
33 Router::$PAGE_LOGIN,
34 Router::findPage('do=login&stuff', array(), false)
35 );
36 }
37
38 /**
39 * Test findPage: login page output.
40 * Invalid: page shouldn't be return.
41 *
42 * @return void
43 */
44 public function testFindPageLoginInvalid()
45 {
46 $this->assertNotEquals(
47 Router::$PAGE_LOGIN,
48 Router::findPage('do=login', array(), true)
49 );
50
51 $this->assertNotEquals(
52 Router::$PAGE_LOGIN,
53 Router::findPage('do=other', array(), false)
54 );
55 }
56
57 /**
58 * Test findPage: picwall page output.
59 * Valid: page should be return.
60 *
61 * @return void
62 */
63 public function testFindPagePicwallValid()
64 {
65 $this->assertEquals(
66 Router::$PAGE_PICWALL,
67 Router::findPage('do=picwall', array(), false)
68 );
69
70 $this->assertEquals(
71 Router::$PAGE_PICWALL,
72 Router::findPage('do=picwall', array(), true)
73 );
74 }
75
76 /**
77 * Test findPage: picwall page output.
78 * Invalid: page shouldn't be return.
79 *
80 * @return void
81 */
82 public function testFindPagePicwallInvalid()
83 {
84 $this->assertEquals(
85 Router::$PAGE_PICWALL,
86 Router::findPage('do=picwall&stuff', array(), false)
87 );
88
89 $this->assertNotEquals(
90 Router::$PAGE_PICWALL,
91 Router::findPage('do=other', array(), false)
92 );
93 }
94
95 /**
96 * Test findPage: tagcloud page output.
97 * Valid: page should be return.
98 *
99 * @return void
100 */
101 public function testFindPageTagcloudValid()
102 {
103 $this->assertEquals(
104 Router::$PAGE_TAGCLOUD,
105 Router::findPage('do=tagcloud', array(), false)
106 );
107
108 $this->assertEquals(
109 Router::$PAGE_TAGCLOUD,
110 Router::findPage('do=tagcloud', array(), true)
111 );
112
113 $this->assertEquals(
114 Router::$PAGE_TAGCLOUD,
115 Router::findPage('do=tagcloud&stuff', array(), false)
116 );
117 }
118
119 /**
120 * Test findPage: tagcloud page output.
121 * Invalid: page shouldn't be return.
122 *
123 * @return void
124 */
125 public function testFindPageTagcloudInvalid()
126 {
127 $this->assertNotEquals(
128 Router::$PAGE_TAGCLOUD,
129 Router::findPage('do=other', array(), false)
130 );
131 }
132
133 /**
134 * Test findPage: linklist page output.
135 * Valid: page should be return.
136 *
137 * @return void
138 */
139 public function testFindPageLinklistValid()
140 {
141 $this->assertEquals(
142 Router::$PAGE_LINKLIST,
143 Router::findPage('', array(), true)
144 );
145
146 $this->assertEquals(
147 Router::$PAGE_LINKLIST,
148 Router::findPage('whatever', array(), true)
149 );
150
151 $this->assertEquals(
152 Router::$PAGE_LINKLIST,
153 Router::findPage('whatever', array(), false)
154 );
155
156 $this->assertEquals(
157 Router::$PAGE_LINKLIST,
158 Router::findPage('do=tools', array(), false)
159 );
160 }
161
162 /**
163 * Test findPage: tools page output.
164 * Valid: page should be return.
165 *
166 * @return void
167 */
168 public function testFindPageToolsValid()
169 {
170 $this->assertEquals(
171 Router::$PAGE_TOOLS,
172 Router::findPage('do=tools', array(), true)
173 );
174
175 $this->assertEquals(
176 Router::$PAGE_TOOLS,
177 Router::findPage('do=tools&stuff', array(), true)
178 );
179 }
180
181 /**
182 * Test findPage: tools page output.
183 * Invalid: page shouldn't be return.
184 *
185 * @return void
186 */
187 public function testFindPageToolsInvalid()
188 {
189 $this->assertNotEquals(
190 Router::$PAGE_TOOLS,
191 Router::findPage('do=tools', array(), 1)
192 );
193
194 $this->assertNotEquals(
195 Router::$PAGE_TOOLS,
196 Router::findPage('do=tools', array(), false)
197 );
198
199 $this->assertNotEquals(
200 Router::$PAGE_TOOLS,
201 Router::findPage('do=other', array(), true)
202 );
203 }
204
205 /**
206 * Test findPage: changepasswd page output.
207 * Valid: page should be return.
208 *
209 * @return void
210 */
211 public function testFindPageChangepasswdValid()
212 {
213 $this->assertEquals(
214 Router::$PAGE_CHANGEPASSWORD,
215 Router::findPage('do=changepasswd', array(), true)
216 );
217 $this->assertEquals(
218 Router::$PAGE_CHANGEPASSWORD,
219 Router::findPage('do=changepasswd&stuff', array(), true)
220 );
221
222 }
223
224 /**
225 * Test findPage: changepasswd page output.
226 * Invalid: page shouldn't be return.
227 *
228 * @return void
229 */
230 public function testFindPageChangepasswdInvalid()
231 {
232 $this->assertNotEquals(
233 Router::$PAGE_CHANGEPASSWORD,
234 Router::findPage('do=changepasswd', array(), 1)
235 );
236
237 $this->assertNotEquals(
238 Router::$PAGE_CHANGEPASSWORD,
239 Router::findPage('do=changepasswd', array(), false)
240 );
241
242 $this->assertNotEquals(
243 Router::$PAGE_CHANGEPASSWORD,
244 Router::findPage('do=other', array(), true)
245 );
246 }
247 /**
248 * Test findPage: configure page output.
249 * Valid: page should be return.
250 *
251 * @return void
252 */
253 public function testFindPageConfigureValid()
254 {
255 $this->assertEquals(
256 Router::$PAGE_CONFIGURE,
257 Router::findPage('do=configure', array(), true)
258 );
259
260 $this->assertEquals(
261 Router::$PAGE_CONFIGURE,
262 Router::findPage('do=configure&stuff', array(), true)
263 );
264 }
265
266 /**
267 * Test findPage: configure page output.
268 * Invalid: page shouldn't be return.
269 *
270 * @return void
271 */
272 public function testFindPageConfigureInvalid()
273 {
274 $this->assertNotEquals(
275 Router::$PAGE_CONFIGURE,
276 Router::findPage('do=configure', array(), 1)
277 );
278
279 $this->assertNotEquals(
280 Router::$PAGE_CONFIGURE,
281 Router::findPage('do=configure', array(), false)
282 );
283
284 $this->assertNotEquals(
285 Router::$PAGE_CONFIGURE,
286 Router::findPage('do=other', array(), true)
287 );
288 }
289
290 /**
291 * Test findPage: changetag page output.
292 * Valid: page should be return.
293 *
294 * @return void
295 */
296 public function testFindPageChangetagValid()
297 {
298 $this->assertEquals(
299 Router::$PAGE_CHANGETAG,
300 Router::findPage('do=changetag', array(), true)
301 );
302
303 $this->assertEquals(
304 Router::$PAGE_CHANGETAG,
305 Router::findPage('do=changetag&stuff', array(), true)
306 );
307 }
308
309 /**
310 * Test findPage: changetag page output.
311 * Invalid: page shouldn't be return.
312 *
313 * @return void
314 */
315 public function testFindPageChangetagInvalid()
316 {
317 $this->assertNotEquals(
318 Router::$PAGE_CHANGETAG,
319 Router::findPage('do=changetag', array(), 1)
320 );
321
322 $this->assertNotEquals(
323 Router::$PAGE_CHANGETAG,
324 Router::findPage('do=changetag', array(), false)
325 );
326
327 $this->assertNotEquals(
328 Router::$PAGE_CHANGETAG,
329 Router::findPage('do=other', array(), true)
330 );
331 }
332
333 /**
334 * Test findPage: addlink page output.
335 * Valid: page should be return.
336 *
337 * @return void
338 */
339 public function testFindPageAddlinkValid()
340 {
341 $this->assertEquals(
342 Router::$PAGE_ADDLINK,
343 Router::findPage('do=addlink', array(), true)
344 );
345
346 $this->assertEquals(
347 Router::$PAGE_ADDLINK,
348 Router::findPage('do=addlink&stuff', array(), true)
349 );
350 }
351
352 /**
353 * Test findPage: addlink page output.
354 * Invalid: page shouldn't be return.
355 *
356 * @return void
357 */
358 public function testFindPageAddlinkInvalid()
359 {
360 $this->assertNotEquals(
361 Router::$PAGE_ADDLINK,
362 Router::findPage('do=addlink', array(), 1)
363 );
364
365 $this->assertNotEquals(
366 Router::$PAGE_ADDLINK,
367 Router::findPage('do=addlink', array(), false)
368 );
369
370 $this->assertNotEquals(
371 Router::$PAGE_ADDLINK,
372 Router::findPage('do=other', array(), true)
373 );
374 }
375
376 /**
377 * Test findPage: export page output.
378 * Valid: page should be return.
379 *
380 * @return void
381 */
382 public function testFindPageExportValid()
383 {
384 $this->assertEquals(
385 Router::$PAGE_EXPORT,
386 Router::findPage('do=export', array(), true)
387 );
388
389 $this->assertEquals(
390 Router::$PAGE_EXPORT,
391 Router::findPage('do=export&stuff', array(), true)
392 );
393 }
394
395 /**
396 * Test findPage: export page output.
397 * Invalid: page shouldn't be return.
398 *
399 * @return void
400 */
401 public function testFindPageExportInvalid()
402 {
403 $this->assertNotEquals(
404 Router::$PAGE_EXPORT,
405 Router::findPage('do=export', array(), 1)
406 );
407
408 $this->assertNotEquals(
409 Router::$PAGE_EXPORT,
410 Router::findPage('do=export', array(), false)
411 );
412
413 $this->assertNotEquals(
414 Router::$PAGE_EXPORT,
415 Router::findPage('do=other', array(), true)
416 );
417 }
418
419 /**
420 * Test findPage: import page output.
421 * Valid: page should be return.
422 *
423 * @return void
424 */
425 public function testFindPageImportValid()
426 {
427 $this->assertEquals(
428 Router::$PAGE_IMPORT,
429 Router::findPage('do=import', array(), true)
430 );
431
432 $this->assertEquals(
433 Router::$PAGE_IMPORT,
434 Router::findPage('do=import&stuff', array(), true)
435 );
436 }
437
438 /**
439 * Test findPage: import page output.
440 * Invalid: page shouldn't be return.
441 *
442 * @return void
443 */
444 public function testFindPageImportInvalid()
445 {
446 $this->assertNotEquals(
447 Router::$PAGE_IMPORT,
448 Router::findPage('do=import', array(), 1)
449 );
450
451 $this->assertNotEquals(
452 Router::$PAGE_IMPORT,
453 Router::findPage('do=import', array(), false)
454 );
455
456 $this->assertNotEquals(
457 Router::$PAGE_IMPORT,
458 Router::findPage('do=other', array(), true)
459 );
460 }
461
462 /**
463 * Test findPage: editlink page output.
464 * Valid: page should be return.
465 *
466 * @return void
467 */
468 public function testFindPageEditlinkValid()
469 {
470 $this->assertEquals(
471 Router::$PAGE_EDITLINK,
472 Router::findPage('whatever', array('edit_link' => 1), true)
473 );
474
475 $this->assertEquals(
476 Router::$PAGE_EDITLINK,
477 Router::findPage('', array('edit_link' => 1), true)
478 );
479
480
481 $this->assertEquals(
482 Router::$PAGE_EDITLINK,
483 Router::findPage('whatever', array('post' => 1), true)
484 );
485
486 $this->assertEquals(
487 Router::$PAGE_EDITLINK,
488 Router::findPage('whatever', array('post' => 1, 'edit_link' => 1), true)
489 );
490 }
491
492 /**
493 * Test findPage: editlink page output.
494 * Invalid: page shouldn't be return.
495 *
496 * @return void
497 */
498 public function testFindPageEditlinkInvalid()
499 {
500 $this->assertNotEquals(
501 Router::$PAGE_EDITLINK,
502 Router::findPage('whatever', array('edit_link' => 1), false)
503 );
504
505 $this->assertNotEquals(
506 Router::$PAGE_EDITLINK,
507 Router::findPage('whatever', array('edit_link' => 1), 1)
508 );
509
510 $this->assertNotEquals(
511 Router::$PAGE_EDITLINK,
512 Router::findPage('whatever', array(), true)
513 );
514 }
515} \ No newline at end of file
diff --git a/tests/plugins/test/test.php b/tests/plugins/test/test.php
new file mode 100755
index 00000000..3d750c90
--- /dev/null
+++ b/tests/plugins/test/test.php
@@ -0,0 +1,21 @@
1<?php
2
3/**
4 * Hook for test.
5 *
6 * @param array $data - data passed to plugin.
7 *
8 * @return mixed altered data.
9 */
10function hook_test_random($data)
11{
12 if (isset($data['_PAGE_']) && $data['_PAGE_'] == 'test') {
13 $data[1] = 'page test';
14 } else if (isset($data['_LOGGEDIN_']) && $data['_LOGGEDIN_'] === true) {
15 $data[1] = 'loggedin';
16 } else {
17 $data[1] = $data[0];
18 }
19
20 return $data;
21}