Files
thetempusproject/bin/tempus_project.php
Joey Kimsey 35b7be92a6 bugfixes and small features
Fixed config switches not registering the correct current value
Added better ux when image uploads are disabled
Fixed an issue where uploaded files were not being handled correctly
Added the ability to disable user registrations
Fixed some variables being unintendedly protected
2025-01-26 15:13:34 -05:00

668 lines
24 KiB
PHP

<?php
/**
* bin/tempus_project.php
*
* Using the .htaccess rerouting: all traffic should be directed to/through index.php.
* In this file we initiate all models we will need, authenticate sessions, set
* template objects, and call appload to initialize the appropriate controller/method.
*
* @version 3.0
* @author Joey Kimsey <Joey@thetempusproject.com>
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
namespace TheTempusProject;
use TheTempusProject\Bedrock\Bin\Bedrock;
use TheTempusProject\Bedrock\Classes\Config;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\Bedrock\Functions\Session;
use TheTempusProject\Bedrock\Functions\Cookie;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Bedrock\Functions\Date;
use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Hermes\Functions\Redirect;
use TheTempusProject\Hermes\Functions\Route as Routes;
use TheTempusProject\Houdini\Classes\Views;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Houdini\Classes\Filters;
use TheTempusProject\Houdini\Classes\Issues;
use TheTempusProject\Houdini\Classes\Navigation;
use TheTempusProject\Classes\Installer;
use TheTempusProject\Classes\Plugin;
use TheTempusProject\Models\Sessions;
use TheTempusProject\Models\User;
use TheTempusProject\Models\Group;
use TheTempusProject\Models\Routes as RoutesModel;
class TheTempusProject extends Bedrock {
const MAIN_MENU_NAME = 'topNavLeft';
const ADMIN_MENU_NAME = 'adminMenu';
const CONTACT_FOOTER_MENU_NAME = 'contactFooterMenu';
const INFO_FOOTER_MENU_NAME = 'infoFooterMenu';
public static $plugins = [];
public static $activeGroup;
public static $activePerms;
public static $activePrefs;
public static $activeSession;
public static $activeUser;
public static $topNavRight = '';
public static $topNavRightDropdown = '';
public static $isLoggedIn = false;
public static $isMember = false;
public static $isAdmin = false;
public static $isMod = false;
private $initialized = false;
public $contact_footer_links = [];
public $info_footer_links = [
[
'text' => 'FAQs',
'url' => '{ROOT_URL}home/faq',
],
[
'text' => 'About',
'url' => '{ROOT_URL}home/about',
],
[
'text' => 'Privacy Policy',
'url' => '{ROOT_URL}home/privacy',
],
[
'text' => 'Terms of Service',
'url' => '{ROOT_URL}home/terms',
],
];
public $admin_links = [
[
'text' => '<i class="fa fa-fw fa-home"></i> Dashboard',
'url' => '{ROOT_URL}admin/index',
],
[
'text' => '<i class="fa fa-fw fa-gear"></i> Settings',
'url' => '{ROOT_URL}admin/settings',
],
[
'text' => '<i class="fa fa-fw fa-user"></i> Users',
'url' => '{ROOT_URL}admin/users',
],
[
'text' => '<i class="fa fa-fw fa-user-group"></i> Groups',
'url' => '{ROOT_URL}admin/groups',
],
[
'text' => '<i class="fa fa-fw fa-phone"></i> Contact',
'url' => '{ROOT_URL}admin/SendMail',
],
[
'text' => '<i class="fa fa-fw fa-shield-halved"></i> Tokens',
'url' => '{ROOT_URL}admin/tokens',
],
[
'text' => '<i class="fa fa-fw fa-arrow-down"></i> Modules',
'url' => [
[
'text' => '<i class="fa fa-fw fa-database"></i> Plugins',
'url' => '{ROOT_URL}admin/plugins',
],
[
'text' => '<i class="fa fa-fw fa-database"></i> Composer',
'url' => '{ROOT_URL}admin/composer',
],
],
],
[
'text' => '<i class="fa fa-fw fa-arrow-down"></i> Logs',
'url' => [
[
'text' => '<i class="fa fa-fw fa-file"></i> Logs',
'url' => '{ROOT_URL}admin/logs',
],
[
'text' => '<i class="fa fa-fw fa-lock"></i> Admin',
'url' => '{ROOT_URL}admin/admin',
],
[
'text' => '<i class="fa fa-fw fa-warning"></i> Errors',
'url' => '{ROOT_URL}admin/errors',
],
[
'text' => '<i class="fa fa-fw fa-history"></i> Logins',
'url' => '{ROOT_URL}admin/logins',
],
],
],
[
'text' => '<i class="fa fa-fw fa-external-link"></i> Redirects',
'url' => '{ROOT_URL}admin/routes',
],
];
public $main_links = [
[
'text' => 'Home',
'url' => '{ROOT_URL}home/index',
],
[
'text' => 'Admin',
'url' => '{ROOT_URL}admin/index',
'filter' => 'admin',
],
];
public $filters = [
[
'name' => 'bbCode.bold',
'find' => '#\[b\](.*?)\[/b\]#is',
'replace' => '<b>$1</b>',
'enabled' => true,
'example' => '[b]Bold Text[/b]',
],
[
'name' => 'bbCode.paragraph',
'find' => '#\[p\](.*?)\[/p\]#is',
'replace' => '<p>$1</p>',
'enabled' => true,
'example' => '[p]Paragraph[/p]',
],
[
'name' => 'bbCode.italic',
'find' => '#\[i\](.*?)\[/i\]#is',
'replace' => '<i>$1</i>',
'enabled' => true,
'example' => '[i]Italic[/i]',
],
[
'name' => 'bbCode.underline',
'find' => '#\[u\](.*?)\[/u\]#is',
'replace' => '<u>$1</u>',
'enabled' => true,
'example' => '[u]Underline[/u]',
],
[
'name' => 'bbCode.strike',
'find' => '#\[s\](.*?)\[/s\]#is',
'replace' => '<del>$1</del>',
'enabled' => true,
'example' => '[s]Strike-through[/s]',
],
[
'name' => 'bbCode.code',
'find' => '#\[code\](.*?)\[/code\]#is',
'replace' => '<code>$1</code>',
'enabled' => true,
'example' => '[code]<p>Is it a paragraph though?</p>[/code]',
],
[
'name' => 'bbCode.color',
'find' => '#\[color=(.*?)\](.*?)\[/color\]#is',
'replace' => "<font color='$1'>$2</font>",
'enabled' => true,
'example' => '[color=red]This is an alert color![/color]',
],
[
'name' => 'bbCode.image',
'find' => '#\[img\](.*?)\[/img\]#is',
'replace' => "<img src='$1'>",
'enabled' => true,
'example' => '[img]/images/logo.png[/img]',
],
[
'name' => 'bbCode.url',
'find' => '#\[url=(.*?)\](.*?)\[/url\]#is',
'replace' => "<a href='$1'>$2</a>",
'enabled' => true,
'example' => '[url=https://google.com]Bing.com[/url]',
],
[
'name' => 'bbCode.quote',
'find' => '#\[quote=(.*?)\](.*?)\[/quote\]#is',
'replace' => "<blockquote cite='$1'>$2</blockquote>",
'enabled' => true,
'example' => '[quote=WhoSaidIt]What they said.[/quote]',
],
[
'name' => 'bbCode.list',
'find' => '#\[list\](.*?)\[/list\]#is',
'replace' => '<ul>$1</ul>',
'enabled' => true,
'example' => '[list][/list]',
],
[
'name' => 'bbCode.listItem',
'find' => '#\(\.\)(.*)$#m',
'replace' => '<li>$1</li>',
'enabled' => true,
'example' => '[list](.)Item1 (.)Item2[/list]',
],
[
'name' => 'icons.check',
'find' => '#\(c\)#is',
'replace' => '&#10004;',
'enabled' => true,
'example' => '(c)',
],
[
'name' => 'icons.close',
'find' => '#\(x\)#is',
'replace' => '&#10006;',
'enabled' => true,
'example' => '(x)',
],
[
'name' => 'icons.exclaim',
'find' => '#\(!\)#is',
'replace' => '&#10069;',
'enabled' => true,
'example' => '(!)',
],
[
'name' => 'icons.question',
'find' => '#\(\?\)#is',
'replace' => '&#10068;',
'enabled' => true,
'example' => '(?)',
],
];
public static $configMatrix = [
"main" => [
"logo" => [
"type" => "file",
"pretty" => "Site Logo (Used mostly in emails)",
"default" => "images/logo.png"
],
"logoLarge" => [
"type" => "file",
"pretty" => "Large Site Logo (Used mostly when sharing images on social media)",
"default" => "images/logoLarge.jpg"
],
"name" => [
"type" => "text",
"pretty" => "Site Name",
"default" => "TTP Example"
],
"template" => [
"type" => "text",
"pretty" => "Default Site Template",
"default" => "default"
],
"tokenEnabled" => [
"type" => "radio",
"pretty" => "Enable CSRF Token for all forms.",
"default" => true
],
"registrationEnabled" => [
"type" => "radio",
"pretty" => "Allow new users to register an account.",
"default" => true
],
"loginLimit" => [
"type" => "text",
"pretty" => "Maximum Login Attempts per hour",
"default" => 5
],
"loginTimer" => [
"type" => "text",
"pretty" => "Maximum Login session length. (in seconds)",
"default" => 604800 // 60 * 60 * 24 * 7
]
],
"uploads" => [
"images" => [
"type" => "radio",
"pretty" => "Upload Images Enabled",
"default" => true,
"value" => true,
],
"maxImageSize"=> [
"type" => "text",
"pretty" => "Maximum size for image uploads",
"default" => 500000,
"value" => 500000,
]
],
"database" => [
"dbEnabled" => [
"type" => "radio",
"pretty" => "Database Enabled",
"default" => true,
"protected" => true
],
"dbHost" => [
"type" => "text",
"pretty" => "Database Host (IE: http://localhost:3306)",
"default" => "127.0.0.1",
"protected" => true
],
"dbMaxQuery" => [
"type" => "text",
"pretty" => "Maximum results per query",
"default" => 100,
"protected" => true
],
"dbName" => [
"type" => "text",
"pretty" => "Database Name",
"default" => "ttp-example",
"protected" => true
],
"dbPassword" => [
"type" => "text",
"pretty" => "Database Password",
"default" => "",
"protected" => true
],
"dbPrefix" => [
"type" => "text",
"pretty" => "Database table Prefix",
"default" => "TTP_",
"protected" => true
],
"dbUsername" => [
"type" => "text",
"pretty" => "Database Username",
"default" => "root",
"protected" => true
]
]
];
public static $userCPlinks = [];
/**
* The constructor takes care of everything that we will need before
* finally calling appload to instantiate the appropriate controller/method.
*/
public function __construct() {
// Initialize the parent app
parent::__construct();
Debug::info( 'Requested URL: ' . $this->getCurrentUrl() );
// load our own config
self::$activeConfig->load( CONFIG_JSON );
// instead of htaccess it should check which server type and use that method
if ( ! Check::isNginx() && ! file_exists( HTACCESS_LOCATION ) && ! file_exists( INSTALLER_LOCATION ) ) {
echo file_get_contents( ERRORS_DIRECTORY . '533.html' );
exit();
}
// Authenticate our session
$this->authenticate();
// Load any filter we need
$this->loadFilters();
// Notify admins if the installer is still around
if ( self::$isAdmin ) {
if ( file_exists( INSTALLER_LOCATION ) ) {
if ( Debug::status() ) {
Debug::warn( 'You have not removed the installer yet.' );
} else {
Issues::add( 'error', 'You have not removed the installer. This is a security risk that should be corrected immediately.' );
}
}
}
// Load nav links
$this->loadLinks();
// Load active plugins
$plugins = Plugin::getActivePlugins();
foreach ( $plugins as $plugin ) {
foreach ($plugin as $name => $class) {
self::$plugins[ $name ] = new $class( true );
}
}
// load the damn usercp menu n a retarded fashion
self::$userCPlinks[] = (object) [
"url" => "{ROOT_URL}usercp",
"name" => "Profile"
];
self::$userCPlinks[] = (object) [
"url" => "{ROOT_URL}usercp/settings",
"name" => "Settings"
];
self::$userCPlinks[] = (object) [
"url" => "{ROOT_URL}usercp/email",
"name" => "Change Email"
];
self::$userCPlinks[] = (object) [
"url" => "{ROOT_URL}usercp/password",
"name" => "Change Password"
];
Components::set( 'SITE_URL', Routes::getAddress() );
Components::set( 'DEBUG_EMAIL', DEBUG_EMAIL );
Debug::gend();
}
/**
* Handles the permissions, preferences, login, and group setup for any active user.
*
* @return (bool)
*/
public function authenticate() {
$user = new User;
$group = new Group;
$sessions = new Sessions;
// Set defaults
self::$activePerms = $group->getDefaultPermissions(); // PERMISSIONS_JSON
self::$activePrefs = $user->getDefaultPreferences(); // PREFERENCES_JSON
if (
!$sessions->checkSession( Session::get( 'SessionID' ) ) &&
!$sessions->checkCookie( Cookie::get( 'RememberToken' ), true )
) {
Debug::info( 'Sessions->authenticate - Could not authenticate cookie or session' );
return false;
}
self::$isLoggedIn = true;
self::$activeSession = $sessions::$activeSession;
self::$activeUser = $user->get( self::$activeSession->userID );
self::$activePrefs = self::$activeUser->prefs;
self::$activeGroup = $group->findById( self::$activeUser->userGroup );
if ( !empty( self::$activeGroup ) ) {
self::$activePerms = self::$activeGroup->perms;
self::$isAdmin = !empty(self::$activeGroup->perms['adminAccess']);
} else {
self::$isAdmin = false;
}
return true;
}
public static function dateTimeCallback( $data ) {
if ( empty( $data[2] ) ) {
return '';
}
return Date::formatTimestamp( $data[1], $data[2] );
}
public function loadFilters() {
// These Filter have to be loaded here because they have calculated values
$this->filters[] = [
'name' => 'mentions',
'find' => '#(^|\s)@([a-zA-Z_]+\w*)#',
'replace' => '$1<a href="' . Routes::getRoot() . 'home/profile/$2">@$2</a>$3',
'enabled' => true,
'example' => '@username',
];
$this->filters[] = [
'name' => 'hashtags',
'find' => '#(^|\s)\#([a-zA-Z\_\-]+\w*)#',
'replace' => '$1<a href="' . Routes::getRoot() . 'home/hashtag/$2">#$2</a>$3',
'enabled' => true,
'example' => '#hash-tag',
];
$this->filters[] = [
'name' => 'admin',
'find' => '#{ADMIN}(.*?){/ADMIN}#is',
'replace' => ( self::$isAdmin ? '$1' : '' ),
'enabled' => true,
'example' => '{ADMIN}Only visible to users who are an admin.{ADMIN}',
];
$this->filters[] = [
'name' => 'loggedin',
'find' => '#{LOGGEDIN}(.*?){/LOGGEDIN}#is',
'replace' => ( self::$isLoggedIn ? '$1' : '' ),
'enabled' => true,
'example' => '{LOGGEDIN}Only visible to users who are logged-in{LOGGEDIN}',
];
$this->filters[] = [
'name' => 'notloggedin',
'find' => '#{NOTLOGGEDIN}(.*?){/NOTLOGGEDIN}#is',
'replace' => ( self::$isLoggedIn ? '' : '$1' ),
'enabled' => true,
'example' => '{NOTLOGGEDIN}Only visible to users who are logged-in{NOTLOGGEDIN}',
];
$this->filters[] = [
'name' => 'dtc',
'find' => '#{DTC(.*?)}(.*?){/DTC}#is',
'replace' => [ __CLASS__, 'dateTimeCallback' ],
'enabled' => true,
'callback' => true,
'example' => '{DTC=date}000000000{DTC}',
];
if ( ! empty( $this->filters ) ) {
foreach( $this->filters as $filter ) {
Filters::add( $filter['name'], $filter['find'], $filter['replace'], $filter['enabled'], ( $filter['callback'] ?? false ) );
}
}
}
public function loadLinks() {
foreach ( $this->contact_footer_links as $key => $link ) {
Navigation::addLink( self::CONTACT_FOOTER_MENU_NAME, $link );
}
foreach ( $this->info_footer_links as $key => $link ) {
Navigation::addLink( self::INFO_FOOTER_MENU_NAME, $link );
}
foreach ( $this->main_links as $key => $link ) {
Navigation::addLink( self::MAIN_MENU_NAME, $link );
}
foreach ( $this->admin_links as $key => $link ) {
Navigation::addLink( self::ADMIN_MENU_NAME, $link );
}
}
public function load( $url = '' ) {
$routes = new RoutesModel;
if ( empty( $url ) ) {
$url = trim( Input::get( 'url' ), '/' );
$url = str_ireplace( '.php', '', $url );
}
$route = $routes->findByOriginalUrl($url);
if (false !== $route) {
$route = $route;
if ('internal' === $route->redirect_type) {
$this->setUrl($route->forwarded_url);
} else {
Redirect::external($route->forwarded_url);
}
}
parent::load();
}
public static function handle_shutdown() {
if ( ! Debug::status( 'console' ) ) {
return;
}
echo '<div class="container">';
echo '<div class="col-centered">';
echo '<div class="row" style="margin-bottom: 275px; padding-bottom: 25px; background: #eee;">';
echo '<h2 style="text-align: center; padding-top: 15px; padding-bottom: 15px;">';
echo 'Running the shutdown handler';
echo '</h2>';
Components::set( 'DEBUGGING_LOG', Debug::dump() );
$error = error_get_last();
echo '<p style="text-align: center;">';
if ( !empty( $error ) ) {
echo 'Looks like there was an error: <pre>' . print_r( $error, true ) . '</pre>';
// log_error( $error["type"], $error["message"], $error["file"], $error["line"] );
} else {
echo 'Shutting down without error.';
}
echo '</p>';
echo Views::simpleView( 'debug' );
echo '</div>';
echo '</div>';
echo '</div>';
}
/**
* Echos useful information about the installation.
*
* @return (null)
*/
public function printDebug() {
$autoload = '<tr><td>Vendor Autoloaded: </td><td><code>';
$autoload .= var_export( VENDOR_AUTOLOADED, true );
$autoload .= '</code></td></tr>';
if ( VENDOR_AUTOLOADED === false ) {
$autoload .= '<tr><td>Core Autoloaded: </td><td><code>';
$autoload .= var_export( defined( 'BEDROCK_AUTOLOADED' ), true );
$autoload .= '</code></td></tr>';
$autoload .= '<tr><td>Project Autoloaded: </td><td><code>';
$autoload .= var_export( defined( 'TEMPUS_PROJECT_AUTOLOADED' ), true );
$autoload .= '</code></td></tr>';
$autoload .= '<tr><td>Debugger Autoloaded: </td><td><code>';
$autoload .= var_export( defined( 'CANARY_AUTOLOADED' ), true );
$autoload .= '</code></td></tr>';
}
$autoload .= '<tr><td>Core Constants Loaded: </td><td><code>';
$autoload .= var_export( BEDROCK_CONSTANTS_LOADED, true );
$autoload .= '</code></td></tr>';
$autoload .= '<tr><td>Project Constants Loaded: </td><td><code>';
$autoload .= var_export( TEMPUS_PROJECT_CONSTANTS_LOADED, true );
$autoload .= '</code></td></tr>';
$autoload .= '<tr><td>Debugger Constants Loaded: </td><td><code>';
$autoload .= var_export( CANARY_CONSTANTS_LOADED, true );
$autoload .= '</code></td></tr>';
echo '<div style="margin: 0 auto; padding-bottom: 25px; background: #eee; width: 1000px;">';
echo '<h1 style="text-align: center;">Tempus Project Debugging Info</h1>';
echo '<table style="margin: 0 auto; padding-bottom: 25px; background: #eee; width: 950px;">';
echo '<tr><td style="text-align: center; padding-top: 25px; padding-bottom: 10px;" colspan="2"><h2>App Data</h2></td></tr>';
echo '<tr><td>Controller: </td><td><code>';
echo var_export( self::$controllerName, true );
echo '</code><br></td></tr>';
echo '<tr><td>Method: </td><td><code>';
echo var_export( self::$methodName, true );
echo '</code><br></td></tr>';
echo '<tr><td>GET: </td><td><code>';
echo var_export( $_GET, true );
echo '</code><br></td></tr>';
echo '<tr><td>POST: </td><td><code>';
echo var_export( $_POST, true );
echo '</code><br></td></tr>';
echo '<tr><td style="text-align: center; padding-top: 25px; padding-bottom: 10px;" colspan="2"><h2>Authentication</h2></td></tr>';
echo '<tr><td>isLoggedIn: </td><td><code>';
echo var_export( self::$isLoggedIn, true );
echo '</code><br></td></tr>';
echo '<tr><td>isAdmin: </td><td><code>';
echo var_export( self::$isAdmin, true );
echo '</code><br></td></tr>';
echo '<tr><td>isMod: </td><td><code>';
echo var_export( self::$isMod, true );
echo '</code><br></td></tr>';
echo '<tr><td>isMember: </td><td><code>';
echo var_export( self::$isMember, true );
echo '</code><br></td></tr>';
echo '<tr><td style="text-align: center; padding-top: 25px; padding-bottom: 10px;" colspan="2"><h2>User Data</h2></td></tr>';
echo '<tr><td>activeUser: </td><td><pre>';
echo var_export( self::$activeUser, true );
echo '</pre></td></tr>';
echo '<tr><td>activeGroup: </td><td><pre>';
echo var_export( self::$activeGroup, true );
echo '</pre></td></tr>';
echo '<tr><td>activePerms: </td><td><pre>';
echo var_export( self::$activePerms, true );
echo '</pre></td></tr>';
echo '<tr><td>activePrefs: </td><td><pre>';
echo var_export( self::$activePrefs, true );
echo '</pre></td></tr>';
echo '<tr><td style="text-align: center; padding-top: 25px; padding-bottom: 10px;" colspan="2"><h2>Autoload</h2></td></tr>';
echo $autoload;
echo '</table></div>';
parent::printDebug();
}
}