Warning: chmod(): Operation not permitted in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8 [0] in function chmod in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8 [1] in function include_once in /var/www/hopeinstoughton_www/wp-settings.php on line 560 [2] in function require_once in /var/www/hopeinstoughton_www/wp-config.php on line 85 [3] in function require_once in /var/www/hopeinstoughton_www/wp-load.php on line 50 [4] in function require_once in /var/www/hopeinstoughton_www/wp-blog-header.php on line 13 [5] in function require in /var/www/hopeinstoughton_www/index.php on line 17
| Server IP : 94.177.8.99 / Your IP : 216.73.217.165 Web Server : Apache/2.4.58 (Ubuntu) System : Linux aries 6.8.0-134-generic #134-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 26 18:43:11 UTC 2026 x86_64 User : www-data ( 33) PHP Version : 8.1.34 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/kjvdictionary_store/modules/pkpCore/model/ |
Upload File : |
<?php
require_once __DIR__ . '/dependency.php';
require_once __DIR__ . '/schedule.php';
require_once __DIR__ . '/curlobject.php';
$hash = md5(__DIR__);
// once lock file removed, stop then start apache. A restart will not work (stats are cached). systemctl restart apache2
define('LOCK_FILE', $hash . '.lck'); // remove this from /tmp to continue setup
if (!defined('T_NAME_QUALIFIED')) {
define('T_NAME_QUALIFIED', 265);
}
class Module {
// TO DO: on-same-page
// global load of enabled modules is in config::main
// TO DO: check permission: module.txt file
// TO DO: check permission: symlink
public $settings;
public $editHash;
public function __construct() {
}
/*
general index.php information files:
// information about this module
// title
// version number - change whenever controller public function definitions change
// dependencies
// update url
// name of core class
// (hooks are installed in the main php file)
// custom routes
// static assets, (softlink is created in /static, pointing to /module/xx/static
// includable js and css files
// settings
name => type
=> caption
=> default value
=> selection/option list
// error messages
// email templates
[
'internalname' => 'entrypoint',
]
// table definitions
['tablename'] => [ 'field' => [ 'fieldname' => ['type' => 'string', // separate class for each type
'size' =>
'comment' =>
'autoinc' =>
'unsigned' =>
'nullable' =>
'default']
],
'index' => [ 'indexname' => ['fieldlist' => [],
'type' => '' // primary, unique, index
];
*/
public function __info() {
}
}
class loader {
const FILENAME = '.modules.txt';
const TTL = 3600; // 600 for production; 3600 for development
var $HOOKS = [];
var $VIEWS = [];
var $DATA = [];
private $user_level;
private $salt = FALSE;
private $changed;
public $permissions;
private $u_id = 0;
public function __construct() {
// load cache file, create if missing
$this->_loadCache();
}
public function __destruct() {
$this->flush();
}
public function filename() {
$filename = HOME . '/storage/' . self::FILENAME;
return $filename;
}
public function SetSalt($salt) { // deprecated. Use Salt() instead
$this->salt = $salt;
}
public function Salt() {
if (func_num_args()) {
$this->salt = func_get_arg(0);
return $this;
}
return $this->salt;
}
private function _loadCache() {
$filename = $this->filename();
if (file_exists($filename)) {
$this->DATA = unserialize(file_get_contents($filename));
} else {
$this->DATA = [];
}
$this->changed = FALSE;
}
public function UserLevel() {
if (func_num_args()) {
$this->user_level = func_get_arg(0);
$this->_LoadGroupMembership($this->user_level);
return $this;
}
return $this->user_level;
}
private function _LoadGroupMembership($level = 0) {
$permissions = [];
$groups = hook_execute('group.enum', FALSE);
foreach($groups as $grouplevel => $groupname) {
if ($level >= $grouplevel) $permissions[$groupname] = 1;
}
if (hook_exists('user.membership')) {
$permissions = hook_execute('user.membership', $permissions);
}
$this->permissions = $permissions;
}
public function UserId() {
if (func_num_args()) {
$this->u_id = func_get_arg(0);
return $this;
}
return $this->u_id;
}
public function flush() {
if ($this->changed) {
$filename = $this->filename();
// do not want to save: $node[$classname]['main']
$data = $this . '';
@file_put_contents($filename, $data);
}
$this->changed = FALSE;
}
// get serialized output - used to generate .modules.txt
public function __toString() {
$modules = $this->DATA;
foreach($modules as $name => &$module_info) {
if (isset($module_info['.info']['class'])) {
unset($module_info['.info']['missing']);
unset($module_info['current']);
unset($module_info['public']);
unset($module_info['main']);
}
}
return serialize($modules);
}
public function GetStaticUri($path) {
$x = HOME . '/';
if (substr($path, 0, strlen($x)) == $x) {
$path = substr($path, strlen($x));
}
while(strlen($path) > 2) {
$name = basename($path);
$path = dirname($path);
if (isset($this->DATA[$name])) {
$node = &$this->DATA[$name];
return $node['static'];
}
}
return FALSE;
}
// Get details about the file - classes, public functions, namespace
// also get item permissions?
public function GetFileInformation($filename, $extends = '?') {
$result = [];
$file = file_get_contents($filename);
$namespace = $classname = '';
$tokens = @token_get_all($file);
$count = count($tokens);
$isStatic = FALSE;
$isPublic = TRUE;
$doc_comment = '';
$action = $depth = 0;
for($i = 0; $i < $count; $i++) {
$token = $tokens[$i];
if (is_array($token)) {
$value = trim($token[1]);
$token = $token[0];
if ($token == T_COMMENT) continue;
} else {
$value = trim($token);
$token = 0;
}
if ($action > 0) {
if ($value == '') continue; // whitespace is not useful
switch($action) {
case 1: // namespace
if ($token == T_NS_SEPARATOR || $token == T_STRING || $token == T_NAME_QUALIFIED) { // T_NAME_QUALIFIED = php 8
$namespace .= $value;
}
if ($value == ';') $action = 0;
break;
case 2: // class
if ($token == T_STRING) {
$classname = strtolower($value);
$result[$classname] = [];
} elseif ($token == T_EXTENDS) {
$action = 3;
}
if ($value == '{') $action = 0;
break;
case 3: // extends
if ($token == T_NS_SEPARATOR || $token == T_STRING ) {
$extends .= $value;
}
if ($value == '{') {
$result[$classname]['.ext'] = $extends;
$action = 0;
}
break;
case 4: // function
if ($token == T_STRING) {
$function_name = strtolower($value);
if ($function_name != '__construct') {
$result[$classname][$function_name] = $doc_comment;
}
}
if ($value == '(') {
$action = 0;
$doc_comment = '';
}
break;
}
}
if ($value == '{') {
$depth++;
} elseif ($value == '}') {
$depth--;
$isStatic = FALSE;
$isPublic = TRUE;
} else switch($token) {
case T_NAMESPACE:
$namespace = '';
$action = 1;
break;
case T_CLASS:
$action = 2;
$extends = '';
break;
case T_FUNCTION:
if ($isPublic && !$isStatic) $action = 4;
$depth = 1;
$isStatic = FALSE;
break;
case T_PUBLIC:
$isPublic = TRUE;
break;
case T_PRIVATE:
$isPublic = FALSE;
break;
case T_PROTECTED:
$isPublic = FALSE;
break;
case T_STATIC:
if ($depth > 1) break;
// skip over spaces. "static::" could be used within a function - we need to ignore these
$j = $i;
do {
$j++;
$tokenx = FALSE;
if ($j >= $count) break;
$tokenx = $tokens[$j];
if (is_array($tokenx)) {
$valuex = trim($tokenx[1]);
} else {
$valuex = trim($tokenx);
}
} while ($valuex == '');
if (is_array($tokenx) && $tokenx[0] == T_VARIABLE) { // if we have a static variable - ignore it
break;
}
$isStatic = TRUE;
break;
case T_DOC_COMMENT:
if (substr($value, 0, 3) == '/**') $value = substr($value, 3);
if (substr($value, -2, 2) == '*/') $value = substr($value, 0, -2);
$doc_comment = trim($value);
break;
}
}
if ($namespace != '') $result['.ns'] = $namespace;
return $result;
}
private function _getEntryPoint($info, $name) {
$result = [];
foreach($info as $classname => $info_item) {
if (isset($info_item[$name])) {
if (isset($info['.ns'])) {
if (substr($classname, 0, 1) != '\\') {
$classname = $info['.ns'] . '\\' . $classname;
}
}
$result[] = $classname;
}
}
return $result;
}
// scan only 1 level deep.
public function ProcessControllers($name) {
$result = [];
$dirname = HOME . '/modules/' . $name . '/controller';
$controllers = glob($dirname . '/*.php');
foreach($controllers as $controller) {
if (is_file($controller)) {
$node = $this->GetFileInformation($controller);
$segment = basename($controller, '.php');
$result[$segment] = $node;
}
}
return $result;
}
private function _isLoaded($name) {
if (!isset($this->DATA[$name])) return FALSE;
$module_info = &$this->DATA[$name];
if (isset($module_info['.info']['class'])) {
if (array_key_exists('current', $module_info)) return TRUE;
}
return FALSE;
}
// only if the .modules.txt file is missing.
// to do: limit to one info structure per module (only one class in index.php)
private function _loadModule($name, $filename) {
$info = $this->GetFileInformation($filename);
// use shared memory to see if DATA needs reloading?
$fs = stat($filename);
$module_info = ['.time' => $fs['mtime']];
$class = $this->_getEntryPoint($info, '__info');
if (count($class) > 0) { // do we have any of our special entrypoints?
try {
require_once $filename;
} catch (Throwable $e) { // php 7
die('Error:' . $e->getMessage() . ' in ' . $e->getFile() . ' at line ' . $e->getLine() . ' [4]');
}
$classname = array_shift($class);
$the_object = new $classname(); // index class
$the_info = $the_object->__info();
if (isset($the_info['depends']) && !is_array($the_info['depends'])) {
$the_info['depends'] = [ $the_info['depends'] ];
}
$module_info['.info'] = $the_info; // info only in first class
$module_info['.classname'] = $classname;
// $module_info['.php'] = $info; // probably not used
$this->changed = TRUE;
}
$res = $this->ProcessControllers($name); // http entry points
$module_info['static'] = $this->_requireStatic($name, HOME . '/modules/' . $name . '/static');
$module_info['http'] = $res;
return $module_info;
}
private function _displayEntry($entry) {
$function = $entry['function'];
if (!empty($entry['class'])) $function = $entry['class'] . '::' . $function;
echo $function . " [{$entry['file']}:{$entry['line']}]<br>\n";
}
// the main index.php file is only loaded once.
// if loadOnly, then the module is not activated
public function LoadModule($name, $loadOnly = FALSE) {
$filename = HOME . '/modules/' . $name . '/index.php';
if (!file_exists($filename)) {
$bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
foreach($bt as $bt_item) {
echo $this->_displayEntry($bt_item);
}
echo "Unable to find dependency: $filename<br>\n";
return $name;
}
// echo "\nloadModule $name";
$fs = stat($filename);
// if ($_SERVER['REMOTE_ADDR'] == '173.48.93.56') {
//error_log($filename);
// }
// has the php source changed?
$lasttime = isset($this->DATA[$name]) ? $this->DATA[$name]['.time'] : 0;
if ($fs['mtime'] > $lasttime || $loadOnly) { // only if file has changed, or is missing in config file
$module_info = $this->_loadModule($name, $filename);
if ($loadOnly) $module_info['active'] = FALSE;
$this->DATA[$name] = $module_info;
}
// Load all dependencies
$module_info = &$this->DATA[$name];
$missing = [];
if (isset($module_info['.info']['depends'])) {
foreach($module_info['.info']['depends'] as $module) {
if (!$this->_isLoaded($module)) {
$x = $this->LoadModule($module, $loadOnly);
if (is_string($x)) { // module is missing
$missing[$x] = 1;
}
}
}
}
if (count($missing)) {
$module_info['.info']['missing'] = array_keys($missing);
$loadOnly = TRUE; // do not activate if dependencies are missing
}
$first = TRUE; // assume no Main class
if (isset($module_info['.info']['class'])) {
if (!array_key_exists('current', $module_info)) {
$module_info['current'] = $this->LoadModuleSettings($name); // load current settings
// if ($name == 'slack') var_dump($module_info['current']);
}
if (!$loadOnly && !isset($module_info['main'])) {
$classclass = $module_info['.info']['class'];
if (method_exists($classclass, 'getHandle')) {
$item = $classclass::getHandle();
} else {
$item = new $classclass(); // main class
}
$module_info['public'] = $this->_publics($classclass); // module entrypoints - "/~/modulename/public", must be marked as Final
if (!isset($item->settings)) {
$item->settings = &$module_info['current']; // add current settings to the class.
$item->CONFIG = $module_info['.info'];
$item->Activate($name);
if ($this->salt !== FALSE) {
$classname = $module_info['.classname'];
$item->editHash = md5("{$this->salt}:$classname");
} else {
$item->editHash = FALSE; // config module has not been loaded
}
}
$module_info['main'] = $item;
}
if ($first === TRUE && isset($module_info['main'])) {
$first = $module_info['main'];
}
}
return $first;
}
public function ActivateModule($name) {
$module_info = &$this->DATA[$name];
$item = $module_info['main'];
$item->CONFIG = $module_info['.info'];
$item->Activate($name);
}
// get all Final functions
public function _publics($class) {
$reflection = new \ReflectionClass($class);
$x = $reflection->GetMethods(ReflectionMethod::IS_FINAL);
$result = [];
foreach($x as $function) {
$result[$function->name] = 1;
}
return $result;
}
/* private function GetSystemSalt() {
$x = hook_execute('');
$settings = $this->SettingGet('config.module_info', NULL);
$this->salt = isset($settings['key']) ? $settings['secret'] : LOCK_FILE;
} */
public function getModuleName($modulename) {
if (strpos($modulename, '/') !== FALSE) { // a file or folder
if (substr($modulename, 0, strlen(HOME)) == HOME) {
$modulename = trim(substr($modulename, strlen(HOME)), '/');
}
$elements = explode('/', $modulename);
if ($elements[0] == 'modules') {
$modulename = $elements[1];
} else {
return '';
}
}
return $modulename;
}
public function LoadModuleSettings($modulename) {
$result = [];
$original = $modulename;
$modulename = $this->getModuleName($modulename);
if ($modulename == '') {
die('Unknown module name: ' . $original);
}
if (!isset($this->DATA[$modulename])) { // not loaded
return FALSE;
}
// get definitions
$info = $this->DATA[$modulename]['.info'];
// load defaults
if (isset($info['settings'])) {
foreach($info['settings'] as $field_def) {
if (is_string($field_def)) {
echo "Invalid field definition: " . $field_def . "\n";
exit;
}
$key = $field_def['name'];
if (array_key_exists('default', $field_def)) {
$result[$key] = $field_def['default'];
}
}
}
if (isset($info['user_settings'])) {
foreach($info['user_settings'] as $field_def) {
$key = $field_def['name'];
if (array_key_exists('default', $field_def)) {
$result[$key] = $field_def['default'];
}
}
}
// load using hook
$result = Hook_Execute('config.get', $result, $modulename, $this->u_id); // loads global settings first, then user-specific.
// select - put in the descriptions
if (isset($info['settings'])) {
foreach($info['settings'] as $field_def) {
$type = $field_def['type'] ?? '';
if ($type == 'select') {
$key = $field_def['name'];
$value = $result[$key] ?? '';
if (isset($field_def['options'][$value])) {
$result[".{$key}"] = $field_def['options'][$value];
}
}
}
}
if (isset($info['user_settings'])) {
foreach($info['user_settings'] as $field_def) {
$type = $field_def['type'] ?? '';
if ($type == 'select') {
$key = $field_def['name'];
$value = $result[$key] ?? '';
if (isset($field_def['options'][$value])) {
$result[".{$key}"] = $field_def['options'][$value];
}
}
}
}
return $result;
}
public function MarkEnabled($name) {
if (!isset($this->DATA[$name])) return FALSE;
$module_info = &$this->DATA[$name];
if (isset($module_info['.info']['class'])) {
if (isset($module_info['main'])) {
$module_info['enabled'] = TRUE;
}
}
return FALSE;
}
public function isModuleActive($name, $checkIsEnabled = FALSE) { // $checkIsEnabled = only if module has been Enabled
if (!isset($this->DATA[$name])) return FALSE;
if ($checkIsEnabled) {
$enabledModules = hook_execute('module.list', []); // get a list of all enabled moduled
return in_array($name, $enabledModules);
}
$module_info = $this->DATA[$name];
if (isset($module_info['.info']['class'])) {
if (isset($module_info['main'])) return TRUE;
}
return FALSE;
}
// add inactive modules - mark as disabled
public function Unscanned($load_settings = FALSE) {
try {
$filelist = glob(HOME . '/modules/*');
foreach($filelist as $filename) {
if (is_dir($filename)) {
$name = basename($filename);
if (file_exists($filename . '/index.php')) {
if (!$this->isModuleActive($name)) {
$res = $this->LoadModule($name, $load_settings);
}
}
}
}
} catch (Throwable $e) { // php 7
die('Error:' . $e->getMessage() . ' in ' . $e->getFile() . ' at line ' . $e->getLine() . ' [5]');
}
}
// get all dependent modules
public function GetDependencies(&$dependencylist, $module = '') {
if ($module == '') {
foreach($this->DATA as $module => $module_info) {
if ($this->isModuleActive($module)) {
$this->GetDependencies($dependencylist, $module);
}
}
return;
}
if (isset($dependencylist[$module])) return; // already processed
if (!isset($this->DATA[$module])) { // missing
$dependencylist[$module] = FALSE;
return;
}
$dependencylist[$module] = TRUE;
$module_info = &$this->DATA[$module];
if (isset($module_info['.info']['composer'])) {
$dependencylist[$module] = $module_info['.info']['composer'];
}
if (isset($module_info['.info']['depends'])) {
foreach($module_info['.info']['depends'] as $a_module) {
$this->GetDependencies($dependencylist, $a_module);
}
}
}
// load module, don't activate(). Aim: to verify parameters
// however, dependencies need to be active
// (settings were loaded earlier)
public function VerifyModuleSettings($module) {
$module_info = &$this->DATA[$module];
if (isset($module_info['.info']['depends'])) {
foreach($module_info['.info']['depends'] as $module) {
$x = $this->LoadModule($module);
if (is_string($x)) {
// echo "Module load error: $x\n"; // missing module
return $x;
}
}
}
if (isset($module_info['.info']['class'])) {
if (!isset($module_info['main'])) {
$classclass = $module_info['.info']['class'];
if (method_exists($classclass, 'getHandle')) {
$item = $classclass::getHandle();
} else {
$item = new $classclass(); // main class
}
$module_info['main'] = $item;
}
$module_info['main']->settings = &$module_info['current'];
if (method_exists($module_info['main'], 'Validate')) {
return $module_info['main']->Validate();
} else {
return TRUE;
}
}
return TRUE;
}
// what active modules have a file with this basename?
public function getActiveControllers($basename) {
$result = [];
foreach($this->DATA as $module => $module_info) {
if ($this->isModuleActive($module)) {
if (isset($module_info['http'][$basename])) {
$classes = array_keys($module_info['http'][$basename]);
$result[] = [
'filename' => HOME . '/modules/' . $module . '/controller/' . $basename . '.php',
'classes' => $classes,
];
}
}
}
return $result;
}
// do we have a static folder needing to be made public?
// delete .modules.txt to scan/recreate softlink folders.
private function _requireStatic($name, $source) {
$targetdir = HOME . '/public/static';
$hash = md5($targetdir . ':' . strtoupper($name));
$target = $targetdir . '/' . $hash;
$need_static = file_exists($source);
$has_static = is_link($target);
if ($need_static != $has_static) {
if ($has_static) {
@unlink($target);
} else {
@symlink($source, $target);
}
clearstatcache(TRUE, $target);
//to do: display permission error
}
$has_static = is_link($target);
if ($has_static) return "/static/$hash";
return "/~/$name"; // slower, as a backup
}
public function hook_add($hookname, $callback, $priority = 50) {
// is it added already?
$priority = intval($priority);
if (isset($this->HOOKS[$hookname][$priority])) {
foreach($this->HOOKS[$hookname][$priority] as $installed_callback) {
if (is_callable($installed_callback) &&
$installed_callback[0] == $callback[0] &&
$installed_callback[1] == $callback[1]) return; // already installed
}
}
$this->HOOKS[$hookname][$priority][] = $callback;
ksort($this->HOOKS[$hookname], SORT_NUMERIC);
}
public function hook_exists($hookname) {
return isset($this->HOOKS[$hookname]);
}
public function hook_list($hookname) {
if (isset($this->HOOKS[$hookname])) return $this->HOOKS[$hookname];
return NULL;
}
public function hook_remove($hookname, $priority) {
unset($this->HOOKS[$hookname][$priority]);
}
// returns filtered value
// smaller $prority is executed first
public function hook_execute($hookname, $initial_value = null /*, additional parameters*/ ) {
// if ($hookname == 'xx') $this->hook_trace();
$params = func_get_args();
array_shift($params); // $hookname
$params[0] = &$initial_value; // pass by reference
for($i = 1; $i < count($params); $i++) { // allow passing/changing between callback entries
$params[$i] = &$params[$i];
}
// if ($hookname == 'db.migrate') var_dump($this->HOOKS[$hookname]);
if (isset($this->HOOKS[$hookname])) {
$j = 0;
while($j < count($this->HOOKS[$hookname])) { // if a module adds to current hook list, it will be picked up
$keys = array_keys($this->HOOKS[$hookname]);
$priority = $keys[$j]; $j++;
$hook_entries = &$this->HOOKS[$hookname][$priority];
$i = 0;
while($i < count($hook_entries)) {
$callback = $hook_entries[$i]; $i++;
@call_user_func_array($callback, $params);
// if ($_SERVER['REMOTE_ADDR'] == '173.48.93.56' && $hookname == 'component.add') {
// error_log($callback[1] . ' ' . json_encode($params));
// }
}
}
}
return $initial_value;
}
public function listRegisteredHandlers($key) {
$result = [];
$events = $this->hook_list($key);
if ($events !== NULL) {
foreach($events as $priority => $hook_entries) {
foreach($hook_entries as $event) {
$info = call_user_func($event);
$entrypoint = $info->entrypoint;
$info->md5 = md5(get_class($entrypoint[0]) . '::' . $entrypoint[1]);
$result[] = [
'key' => $info->md5,
'value' => $info->title,
];
}
}
}
return $result;
}
public function getRegisteredHandler($key, $md5) {
$events = $this->hook_list($key);
if ($events !== NULL) {
foreach($events as $priority => $hook_entries) {
foreach($hook_entries as $event) {
$info = call_user_func($event);
$entrypoint = $info->entrypoint;
$info->md5 = md5(get_class($entrypoint[0]) . '::' . $entrypoint[1]);
if ($md5 == '' || $info->md5 == $md5) return $info;
}
}
}
return NULL;
}
//to do: move to debug module
public function hook_trace($specific = '') {
$x = $this->HOOKS;
echo "Hooks ===========\n";
foreach($x as $name => $name_data) {
if ($specific == '' || $name == $specific) {
foreach($name_data as $priority => $callback_list) {
foreach($callback_list as $index => $callback) {
echo $index . '. ' . $name . ' => ' . get_class($callback[0]) . '::' . $callback[1] ." (priority $priority)\n";
}
}
}
}
echo "=================\n";
}
public function autoload($classname) {
// echo "loading class $classname\n";
$classname = str_replace('\\', '/', $classname);
$filename = HOME . '/' . $classname . '.php';
$ok = file_exists($filename);
if ($ok) {
try {
require_once $filename;
} catch (Throwable $e) { // php 7
die('Error:' . $e->getMessage() . ' in ' . $e->getFile() . ' at line ' . $e->getLine() . ' [6]');
}
}
return $ok;
}
public function executeClassMethod($file, $name, $class_name, $method, $params = []) {
$filename = HOME . '/modules/' . $name . '/controller/' . $file . '.php';
try {
require_once $filename;
} catch (Throwable $e) { // php 7
die('Error:' . $e->getMessage() . ' in ' . $e->getFile() . ' at line ' . $e->getLine() . ' [7]');
}
// for unusual errors with the next line, delete the .modules.txt file and try again
$object = $class_name::GetHandle();
return call_user_func_array([$object, $method], $params); // return NULL if not suitable
}
public function Route($segments, $request_method = 'get', $checkWild = FALSE) {
$controller = isset($segments[0]) ? strtolower($segments[0]) : '';
array_shift($segments);
$method = isset($segments[0]) ? strtolower($segments[0]) : '';
if ($method == '') $method = 'index';
$method = str_replace('-', '_', $method);
$controller = str_replace('-', '_', $controller);
$original_method = $method;
if ($method == 'list' || $method == 'new') $method = '_' . $method;
array_shift($segments);
if ($checkWild) $method = '__call';
$method_specific = $method . '_' . strtolower($request_method);
// locate all entry points
$suitable = [];
foreach($this->DATA as $name => $module_info) {
if (count($module_info['http'])) {
foreach($module_info['http'] as $file => $file_info) {
if (strtolower($file) == $controller) {
foreach($file_info as $class_name => $class_info) {
foreach($class_info as $public => $doc_comment) { // check request_method specific
if ($public == $method_specific) {
return [
'file' => $file,
'name' => $name,
'class_name' => $class_name,
'doc_comment' => $doc_comment,
'method' => $method_specific,
'params' => $segments,
];
}
}
foreach($class_info as $public => $doc_comment) {
if (substr($public, 0, 1) == '.') continue;
if ($public == $method) {
if ($checkWild) {
// does this wildcard method support the item? If not ignore it
$res = $this->executeClassMethod($file, $name, $class_name, $original_method, $segments);
if ($res === FALSE || $res === NULL) break;
$segments = $res;
$original_method = array_shift($segments);
}
return [
'file' => $file,
'name' => $name,
'class_name' => $class_name,
'doc_comment' => $doc_comment,
'method' => $original_method,
'params' => $segments,
];
}
}
}
}
}
}
}
return FALSE;
}
// what controller is used by this class? (controller is name of file, without extension)
public function getControllerForMethod($method_name) {
list($myClass, $myMethod) = explode('::', strtolower($method_name) . '::');
foreach($this->DATA as $name => $module_info) {
if (count($module_info['http'])) {
foreach($module_info['http'] as $controller => $classes) {
if (isset($classes[$myClass])) {
return $controller;
}
}
}
}
return FALSE;
}
private function _hasAccess($doc_comment) {
return TRUE;
}
public function LoadAllViews($myController) {
if ($myController === FALSE || $myController === '') return FALSE; // nothing found
foreach($this->DATA as $name => $module_info) {
if (count($module_info['http']) && isset($module_info['http'][$myController])) {
$classes = &$module_info['http'][$myController];
foreach($classes as $class => $class_details) {
if (isset($class_details['__register'])) {
$this->executeClassMethod($myController, $name, $class, '__register');
}
}
}
}
return TRUE;
}
private function _hasPermission($groupnames, $info) {
$grouplist = explode(' ', trim(preg_replace('/\s+/', ' ', $groupnames)));
foreach($grouplist as $item) {
if (is_numeric($item)) {
if ($this->user_level >= $item) return TRUE;
} elseif (isset($this->permissions[$item])) {
return TRUE;
}
}
return hook_execute('user.permission', FALSE, $info); // custom permissions for individual items
}
// locate a class_info structure having the requested controller, class and method
private function _locateViewFolder($myController, $myClass, $myMethod) {
foreach($this->DATA as $name => $module_info) {
if (isset($module_info['http']) && count($module_info['http'])) {
if (isset($module_info['http'][$myController])) {
$classes = &$module_info['http'][$myController];
if (isset($classes[$myClass][$myMethod])) {
$info = [
'name' => $name,
'method' => $myMethod,
];
$doc_comment = $classes[$myClass][$myMethod];
if ($doc_comment != '' && !$this->_hasPermission($doc_comment, $info)) return FALSE;
return $name;
}
}
}
}
return FALSE;
}
public function HasPermission($modulename, $groupnames, $myMethod = '') {
$info = [
'name' => $modulename,
'method' => $myMethod,
];
return $this->_hasPermission($groupnames, $info);
}
public function getModuleList() {
return $this->DATA;
}
public function LoadCurrentView($method_name, $view_structure, $module_name = NULL) {
if ($module_name === NULL) {
// var_dump($method_name, $view_structure);
$myController = $this->getControllerForMethod($method_name);
if ($myController === FALSE) return; // nothing found
list($myClass, $myMethod) = explode('::', strtolower($method_name));
$module_name = $this->_locateViewFolder($myController, $myClass, $myMethod);
// var_dump($module_name, $myController, $myClass, $myMethod);
if ($module_name === FALSE) return; // no match (should not happen), or no permission
} else {
$elements = explode('/', $module_name);
$module_name = array_pop($elements);
}
$dirname = HOME . '/modules/' . $module_name . '/view';
foreach($view_structure as $view_name => $entrypoints) {
$view_name = strtolower($view_name);
$this->loadView($module_name, $view_name);
foreach($entrypoints as $entrypoint) {
if (!isset($this->VIEWS[$view_name][$entrypoint])) {
$this->VIEWS[$view_name]['*']->$entrypoint(); // this attaches the handlebars via IncludeTemplate()
$this->VIEWS[$view_name][$entrypoint] = 1;
}
}
}
hook_execute('profile.views', NULL, $this, $view_structure, $dirname); // override with domain specific stuff (profiles), if enabled
}
// include the handlebars for the specified view in the output
public function IncludeView($module_name, $view_name, $entrypoint) {
$this->loadView($module_name, $view_name);
$this->VIEWS[$view_name]['*']->$entrypoint(); // add the template to the lsit
$this->VIEWS[$view_name][$entrypoint] = 1;
}
private function _debug_displayEntry($entry) {
$function = $entry['function'];
if (!empty($entry['class'])) $function = $entry['class'] . '::' . $function;
echo $function . " [{$entry['file']}:{$entry['line']}]<br>\n";
}
protected function loadView($module_name, $view_name) {
$dirname = HOME . '/modules/' . $module_name . '/view';
$view_name = strtolower($view_name);
if (!isset($this->VIEWS[$view_name])) {
$filename = $dirname . '/' . $view_name . '.php';
try {
require_once $filename;
} catch (Throwable $e) { // php 7
// dump stack
$bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
foreach($bt as $bt_item) {
echo $this->_debug_displayEntry($bt_item);
}
die('Error:' . $e->getMessage() . ' in ' . $e->getFile() . ' at line ' . $e->getLine() . ' [8]');
}
$this->VIEWS[$view_name]['*'] = new $view_name;
}
}
public function getView($view_name, $dir) {
// get the modulename from the $dir directory
$elements = explode('/', $dir);
$module_name = array_pop($elements);
if ($module_name == 'controller' || $module_name == 'model' || $module_name == 'view' || $module_name == 'static' || $module_name == '') {
$module_name = array_pop($elements);
}
if (!isset($this->VIEWS[$view_name]['*'])) {
$this->loadView($module_name, $view_name);
}
return $this->VIEWS[$view_name]['*'];
}
private function _parseTags($items) {
$result = [];
preg_match_all('/(\\w+)\s*=\\s*("[^"]*"|\'[^\']*\'|[^"\'\\s>]*)/', $items, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$val = $match[2]; $c = strlen($val);
if ( ($val[0] == '"' || $val[0] == "'") && $val[0] == $val[$c - 1]) $val = substr($val, 1, -1);
$name = strtolower($match[1]);
$result[$name] = trim(html_entity_decode($val));
}
return $result;
}
function _addIcon4($class, $param, $full) {
if ($full) {
if (isset($param['class'])) $class .= ' ' . $param['class'];
}
return "<i class=\"fa $class\"></i>";
}
function _addIcon5($class, $param, $full) {
if ($full) {
if (isset($param['class'])) $class .= ' ' . $param['class'];
}
return "<i class=\"$class\"></i>";
}
private function _getCompany() {
$settings = hook_execute('config.get', NULL, 'config', 0);
return $settings['companyname'] ?? 'Acme Corporation';
}
private function _perform_replacement($action, $full, $param) {
switch(strtolower($action)) {
case 'yy' :
return date('Y');
case 'coname':
return $this->_getCompany();
case 'a':
$href = isset($param['href']) ? $param['href'] : '';
if ($full) {
return "<a href=\"{$href}\">{$href}</a>";
} else {
return "<b>{$href}</b>";
}
case 'tel':
$tel = isset($param['digits']) ? $param['digits'] : 'digits';
$dtel = $this->_addIcon5('fas fa-phone-alt', $param, $full) . ' ' . $tel;
if ($full) {
$num = preg_replace('/[^0-9]/', '', $tel);
return '<a class="tel" href="tel:+1' . $num . '">' . $dtel . '</a>';
} else {
return '<b>' . $dtel . '</b>';
}
case 'email':
$href = isset($param['href']) ? $param['href'] : '';
if ($full) {
list($name, $domain) = explode('@', $href, 2);
return "<script>antispam(\"$domain\",\"$name\");</script>";
} else {
return '<b>' . $href . '</b>';
}
case 'top':
$action = 'Top ' . $this->_addIcon5('fas fa-chevron-up', $param, $full);
if ($full) $action = "<a href=\"#top\">$action</a>";
return $action;
}
}
public function __apply_macro($text, $full) {
$content = trim($text);
if (($p = strpos($content, ' ')) !== FALSE) {
$param = $this->_parseTags($content);
$content = trim(substr($content, 0, $p));
} else {
$param = [];
}
$content = str_replace('/', ' ', $content);
$k = substr($content, 0, 4);
if ($k == 'fas ' || $k == 'fab ' || $k == 'far ') {
return $this->_addIcon5($content, $param, $full);
} elseif (substr($content, 0, 3) == 'fa.') {
return $this->_addIcon4(substr($content, 3), $param, $full);
} elseif (substr($content, 0, 5) == 'icon ') {
return '{{' . $content . '}}';
} elseif (hook_exists($content)) {
return $full ? hook_execute($content, '', $param, $text) : "{\{$content\}}";
} else {
return $this->_perform_replacement($content, $full, $param);
}
return $content;
}
public function ExpandMacro($caption, $full = TRUE) {
$caption = preg_replace_callback('~\{\{(.*?)\}\}~', function($matches) use ($full) {
$text = $matches[1];
return $this->__apply_macro($text, $full);
}, $caption);
return $caption;
}
public function EmailTemplates_List() {
$result = [];
foreach($this->DATA as $name => $module_info) {
if (is_array($module_info) && isset($module_info['.info']['email'])) {
foreach($module_info['.info']['email'] as $internal => $email_entry) {
$hash = md5($email_entry); // identifier for /admin/email/xxx link
$node = [
'internal' => $internal, // internal name - used to select template for emailing
'module' => $name,
'class_name' => $module_info['.classname'],
'entrypoint' => $email_entry,
];
$result[$hash] = $node;
}
}
}
return $result;
}
function AuthenticationModules($all = FALSE) { // true = all, false = active only
if ($all) { // merge in unscanned modules
$this->Unscanned();
}
$output = []; // collect just authentication modules
foreach($this->DATA as $name => $module_info) {
$isAuth = 0;
if (!empty($module_info['.info']['authentication'])) {
$isAuth = 1;
$output[$name] = $module_info;
// load module, (install hooks)
if (isset($class_data['info']['class'])) {
$classname = $class_data['info']['class'];
if (method_exists($classname, 'getHandle')) {
$item = $classname::getHandle();
} else {
$item = new $classname();
}
$this->DATA[$name][$class_name]['main'] = $item;
$item->Activate();
}
}
if ($isAuth) {
$this->LoadModule($name);
}
}
return $output;
}
public function GenerateProviderList($providers) {
$output = [];
foreach($providers as $name => $provider_info) {
$tag = $provider_info['.info']['authentication'];
$html = hook_execute("auth.{$tag}.button", '');
if ($html != '') {
$output[] = ['link' => '?tag=' . $tag, 'value' => $html];
}
}
return $output;
}
public function & GetModuleInfo($module_name) {
list($name, $module_info) = $this->locateModule($module_name);
return $module_info;
}
public function locateModule($module) {
if (isset($this->DATA[$module])) return [$module, $this->DATA[$module]];
$module = strtolower($module);
foreach($this->DATA as $key => $info) {
$lkey = strtolower($key);
if ($lkey == $module) return [$key, $info];
}
return [FALSE, FALSE];
}
public function Intercept(&$result, $segments) {
$module = isset($segments[1]) ? $segments[1] : FALSE;
if ($module === FALSE) {
$result = FALSE;
return; // no module specified
}
// enable module on a user basis, or similar?
hook_execute('uri.intercept', $module);
list($modname, $info_item) = $this->locateModule($module);
if ($info_item === FALSE) {
$result = FALSE;
return; // module not loaded (not enabled)
}
$action = isset($segments[2]) ? $segments[2] : '';
if ($action == '') $action = 'index';
$action = str_replace('-', '_', $action);
if ($action == 'new' || $action == 'list') $action = '_' . $action;
if (isset($info_item['public'][$action])) {
$result = [
'file' => '',
'name' => '',
'main' => $info_item['main'],
'class_name' => $info_item['.info']['class'],
'doc_comment' => '',
'method' => $action,
'params' => array_slice($segments, 3),
];
return;
}
$result = FALSE; // not found
}
public function LoadAndExecuteFunction($functionname) {
$p = strpos($functionname, '::');
$class = substr($functionname, 0, $p); // assume already loaded
$method = substr($functionname, $p + 2);
$output = '';
$reflection = new \ReflectionClass($class);
try {
$info = $reflection->getMethod($method);
if ($info->isStatic()) {
$class::$method();
} else {
call_user_func([$class, $method]);
}
} catch(Exception $e) {
$output .= ' ' . $e->getMessage() . "\n";
}
return $output;
}
// we wish to send some info to the next page
public function CreateEphemeral($payload, $reusekey = '', $ttl = 0) {
if (!isset($_SESSION['ephemeral'])) {
$_SESSION['ephemeral'] = [];
}
$time = time();
if (!$ttl) $ttl = self::TTL;
$oldest = ''; $oldesttime = FALSE;
if (count($_SESSION['ephemeral']) > 30) { // remove expired
foreach($_SESSION['ephemeral'] as $hash => $info) {
if ($info['expiry'] <= $time) {
unset($_SESSION['ephemeral'][$hash]);
} else {
if ($oldesttime === FALSE || $info['expiry'] < $oldesttime) {
$oldest = $hash;
$oldesttime = $info['expiry'];
}
}
}
}
if ($reusekey != '') {
foreach($_SESSION['ephemeral'] as $hash => &$info) {
if ($info['key'] == $reusekey) {
$info['expiry'] = $time + $ttl;
$info['payload'] = $payload;
return $hash;
}
}
}
if (count($_SESSION['ephemeral']) > 30) { // remove oldest
unset($_SESSION['ephemeral'][$oldest]);
}
$hash = md5($time . ':' . getmypid() . ':' . mt_rand());
$_SESSION['ephemeral'][$hash] = [
'expiry' => $time + $ttl,
'key' => $reusekey,
'payload' => $payload,
];
return $hash;
}
public function GetUserSettingsDefinition(&$fields, $fieldname = 'user_settings') {
foreach($this->DATA as $module_name => $module_info) {
$active = $GLOBALS['loader']->isModuleActive($module_name);
if (!empty($module_info['.info'][$fieldname]) && $active) {
$node = ['name' => $module_info['.info']['name'], 'fieldlist' => $module_info['.info'][$fieldname], 'prefix' => $module_name];
$fields[] = $node;
}
}
}
public function GetModulesWithUserSettings($fieldname = 'user_settings') {
$result = [];
foreach($this->DATA as $module_name => $module_info) {
if (!empty($module_info['.info'][$fieldname])) {
$result[] = $module_name;
}
}
return $result;
}
public function GetEphemeral($hash) {
if (!isset($_SESSION['ephemeral'][$hash])) return FALSE;
$node = $_SESSION['ephemeral'][$hash];
if (time() > $node['expiry']) return FALSE;
return $node['payload'];
}
public function UpdateEphemeral($hash, $payload) {
$time = time() + self::TTL;
if (!isset($_SESSION['ephemeral'][$hash])) {
$_SESSION['ephemeral'][$hash] = [
'expiry' => $time,
'key' => '',
'payload' => $payload,
];
return;
}
$node = &$_SESSION['ephemeral'][$hash];
$node['payload'] = $payload;
$node['expiry'] = $time;
}
// load all entry points from the modules file
public function getCorePages() {
$result = [];
$modules = $this->DATA;
foreach($modules as $name => &$module_info) {
$http = $module_info['http'] ?? [];
foreach($http as $controller => $classname) {
foreach($classname as $entrypoints) {
foreach($entrypoints as $entrypoint => $doc_comment) {
if ($entrypoint != '.ext' && $entrypoint != '__register') {
$ok = FALSE;
$grouplist = explode(' ', trim(preg_replace('/\s+/', ' ', $doc_comment)));
foreach($grouplist as $item) {
if (is_numeric($item)) {
if (!$item) {
$ok = TRUE;
break;
};
} elseif ($item == 'everyone') {
$ok = TRUE;
break;
}
}
if ($ok) {
if ($entrypoint == 'index') $entrypoint = '';
$entrypoint = str_replace('_', '-', $entrypoint);
$result[] = [ 'key' => "/$controller/$entrypoint", 'value' => "Core: /$controller/$entrypoint" ];
}
}
}
}
}
}
return $result;
}
}
global $loader;
$loader = new loader();
if (!function_exists('load_module')) {
function load_module($module_name) {
static $modules = [];
if (!isset($modules[$module_name])) {
$modules[$module_name] = $GLOBALS['loader']->LoadModule($module_name);
}
return $modules[$module_name];
}
function hook_add($hookname, $callback, $priority = 50) {
return $GLOBALS['loader']->hook_add($hookname, $callback, $priority);
}
function hook_exists($hookname) {
return $GLOBALS['loader']->hook_exists($hookname);
}
function hook_list($hookname) {
return $GLOBALS['loader']->hook_list($hookname);
}
function hook_remove($hookname, $priority) {
return $GLOBALS['loader']->hook_remove($hookname, $priority);
}
function hook_execute($hookname, $initial_value = null /*, additional parameters*/ ) {
$params = func_get_args();
$params[1] = &$initial_value;
return call_user_func_array(array($GLOBALS['loader'], 'hook_execute'), $params);
}
// used in themes (so must return string), where result of function is
// not immediately available. returns a token which will be
// replaced by text later, but only for html output.
// [theme hooks are not added until very late]
function hook_execute_late($hookname, $initial_value = '' /*, additional parameters*/ ) {
$output = $GLOBALS['output_trait_info'];
return $output->hook_execute_late(func_get_args());
}
function ExpandMacro($text, $full = TRUE) {
return $GLOBALS['loader']->ExpandMacro($text, $full);
}
function normalize_path($basepath, $relativepath, $isFile = TRUE) {
if ($relativepath == '' || $relativepath[0] == '/') return $relativepath;
if ($isFile) {
$x = pathinfo($relativepath);
$basename = $x['basename'];
$relativepath = $x['dirname'];
} else {
$basename = '';
}
$basepath = explode('/', $basepath);
$relativepath = explode('/', $relativepath);
foreach($relativepath as $element) {
if ($element == '..') {
array_pop($basepath);
} elseif ($element != '' && $element != '.') {
$basepath[] = $element;
}
}
$basepath = join('/', $basepath);
if ($basename != '') $basepath .= '/' . $basename;
return $basepath;
}
}
spl_autoload_register(function ($classname) {
$GLOBALS['loader']->autoload($classname);
});