1
0
mirror of https://github.com/monstra-cms/monstra.git synced 2025-08-06 21:26:58 +02:00

Core Improvements: Next Round #79 #80

This commit is contained in:
Awilum
2013-01-06 17:33:22 +02:00
parent 32e11ada3f
commit a05aa4d379
45 changed files with 386 additions and 280 deletions

487
engine/Core.php Normal file
View File

@@ -0,0 +1,487 @@
<?php defined('MONSTRA_ACCESS') or die('No direct script access.');
/**
* Main Monstra engine module. Core module.
*
* Monstra - Content Management System.
* Site: mostra.org
* Copyright (C) 2012 Romanenko Sergey / Awilum [awilum@msn.com]
*
* @package Monstra
* @author Romanenko Sergey / Awilum
* @copyright 2012 Romanenko Sergey / Awilum
* @version $Id$
* @since 1.0.0
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* Monstra is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYING.txt for copyright notices and details.
*/
class Core
{
/**
* An instance of the Core class
*
* @var core
*/
protected static $instance = null;
/**
* Common environment type constants for consistency and convenience
*/
const PRODUCTION = 1;
const STAGING = 2;
const TESTING = 3;
const DEVELOPMENT = 4;
/**
* The version of Monstra
*/
const VERSION = '2.1.3';
/**
* Monstra environment
*
* @var string
*/
public static $environment = Core::PRODUCTION;
/**
* Protected clone method to enforce singleton behavior.
*
* @access protected
*/
protected function __clone()
{
// Nothing here.
}
/**
* Construct
*/
protected function __construct()
{
// Load core defines
Core::loadDefines();
/**
* Compress HTML with gzip
*/
if (MONSTRA_GZIP) {
if ( ! ob_start("ob_gzhandler")) ob_start();
} else {
ob_start();
}
/**
* Send default header and set internal encoding
*/
header('Content-Type: text/html; charset=UTF-8');
function_exists('mb_language') AND mb_language('uni');
function_exists('mb_regex_encoding') AND mb_regex_encoding('UTF-8');
function_exists('mb_internal_encoding') AND mb_internal_encoding('UTF-8');
/**
* Gets the current configuration setting of magic_quotes_gpc
* and kill magic quotes
*/
if (get_magic_quotes_gpc()) {
function stripslashesGPC(&$value) { $value = stripslashes($value); }
array_walk_recursive($_GET, 'stripslashesGPC');
array_walk_recursive($_POST, 'stripslashesGPC');
array_walk_recursive($_COOKIE, 'stripslashesGPC');
array_walk_recursive($_REQUEST, 'stripslashesGPC');
}
// Error handling for Developers only.
if (Core::$environment != Core::PRODUCTION) {
// Set error handler
set_error_handler('Core::errorHandler');
// Set fatal error handler
register_shutdown_function('Core::fatalErrorHandler');
// Set exception handler
set_exception_handler('Core::exceptionHandler');
}
// Start session
Session::start();
// Init ORM
if (defined('MONSTRA_DB_DSN')) {
require_once(ROOT . '/engine/Orm.php');
Orm::configure(MONSTRA_DB_DSN);
Orm::configure('username', MONSTRA_DB_USER);
Orm::configure('password', MONSTRA_DB_PASSWORD);
}
// Auto cleanup if MONSTRA_DEBUG is true
if (Core::$environment == Core::DEVELOPMENT) {
// Cleanup minify
if (count($files = File::scan(MINIFY, array('css', 'js', 'php'))) > 0) foreach ($files as $file) File::delete(MINIFY . DS . $file);
// Cleanup cache
if (count($namespaces = Dir::scan(CACHE)) > 0) foreach ($namespaces as $namespace) Dir::delete(CACHE . DS . $namespace);
}
// Set cache dir
Cache::configure('cache_dir', CACHE);
// Load Securitu module
require_once(ROOT . '/engine/Security.php');
// Load URI module
require_once(ROOT . '/engine/Uri.php');
// Load XMLDB API module
require_once(ROOT . '/engine/Xmldb.php');
// Load Options API module
require_once(ROOT . '/engine/Options.php');
// Init Options API module
Option::init();
// Set default timezone
@ini_set('date.timezone', Option::get('timezone'));
if (function_exists('date_default_timezone_set')) date_default_timezone_set(Option::get('timezone')); else putenv('TZ='.Option::get('timezone'));
// Sanitize URL to prevent XSS - Cross-site scripting
Security::runSanitizeURL();
// Load Plugins API module
require_once(ROOT . '/engine/Plugins.php');
// Load Shortcodes API module
require_once(ROOT . '/engine/Shortcodes.php');
// Load default
Core::loadPluggable();
// Init I18n
I18n::init(Option::get('language'));
// Init Plugins API
Plugin::init();
// Init Notification service
Notification::init();
// Load Site module
require_once(ROOT . '/engine/Site.php');
// Init site module
if( ! BACKEND) Site::init();
}
/**
* Load Defines
*/
protected static function loadDefines()
{
$environments = array(1 => 'production',
2 => 'staging',
3 => 'testing',
4 => 'development');
$root_defines = ROOT . DS . 'boot' . DS . 'defines.php';
$environment_defines = ROOT . DS . 'boot' . DS . $environments[Core::$environment] . DS . 'defines.php';
$monstra_defines = ROOT . DS . 'engine' . DS . 'boot' . DS . 'defines.php';
if (file_exists($root_defines)) {
include $root_defines;
} elseif (file_exists($environment_defines)) {
include $environment_defines;
} elseif (file_exists($monstra_defines)) {
include $monstra_defines;
} else {
throw new RuntimeException("The defines file does not exist.");
}
}
/**
* Load Pluggable
*/
protected static function loadPluggable()
{
$environments = array(1 => 'production',
2 => 'staging',
3 => 'testing',
4 => 'development');
$root_pluggable = ROOT . DS . 'boot';
$environment_pluggable = ROOT . DS . 'boot' . DS . $environments[Core::$environment];
$monstra_pluggable = ROOT . DS . 'engine' . DS . 'boot';
if (file_exists($root_pluggable . DS . 'filters.php')) {
include $root_pluggable . DS . 'filters.php';
} elseif (file_exists($environment_pluggable . DS . 'filters.php')) {
include $environment_pluggable . DS . 'filters.php';
} elseif (file_exists($monstra_pluggable . DS . 'filters.php')) {
include $monstra_pluggable . DS . 'filters.php';
} else {
throw new RuntimeException("The pluggable file does not exist.");
}
if (file_exists($root_pluggable . DS . 'actions.php')) {
include $root_pluggable . DS . 'actions.php';
} elseif (file_exists($environment_pluggable . DS . 'actions.php')) {
include $environment_pluggable . DS . 'actions.php';
} elseif (file_exists($monstra_pluggable . DS . 'actions.php')) {
include $monstra_pluggable . DS . 'actions.php';
} else {
throw new RuntimeException("The pluggable file does not exist.");
}
if (file_exists($root_pluggable . DS . 'shortcodes.php')) {
include $root_pluggable . DS . 'shortcodes.php';
} elseif (file_exists($environment_pluggable . DS . 'shortcodes.php')) {
include $environment_pluggable . DS . 'shortcodes.php';
} elseif (file_exists($monstra_pluggable . DS . 'shortcodes.php')) {
include $monstra_pluggable . DS . 'shortcodes.php';
} else {
throw new RuntimeException("The pluggable file does not exist.");
}
}
/**
* Exception Handler
*
* @param object $exception An exception object
*/
public static function exceptionHandler($exception)
{
// Empty output buffers
while (ob_get_level() > 0) ob_end_clean();
// Send headers and output
@header('Content-Type: text/html; charset=UTF-8');
@header('HTTP/1.1 500 Internal Server Error');
// Get highlighted code
$code = Core::highlightCode($exception->getFile(), $exception->getLine());
// Determine error type
if ($exception instanceof ErrorException) {
$error_type = 'ErrorException: ';
$codes = array (
E_ERROR => 'Fatal Error',
E_PARSE => 'Parse Error',
E_COMPILE_ERROR => 'Compile Error',
E_COMPILE_WARNING => 'Compile Warning',
E_STRICT => 'Strict Mode Error',
E_NOTICE => 'Notice',
E_WARNING => 'Warning',
E_RECOVERABLE_ERROR => 'Recoverable Error',
/*E_DEPRECATED => 'Deprecated',*/ /* PHP 5.3 */
E_USER_NOTICE => 'Notice',
E_USER_WARNING => 'Warning',
E_USER_ERROR => 'Error',
/*E_USER_DEPRECATED => 'Deprecated'*/ /* PHP 5.3 */
);
$error_type .= in_array($exception->getCode(), array_keys($codes)) ? $codes[$exception->getCode()] : 'Unknown Error';
} else {
$error_type = get_class($exception);
}
// Show exception if core environment is DEVELOPMENT
if (Core::$environment == Core::DEVELOPMENT) {
// Development
echo ("
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>Monstra</title>
<style>
* { margin: 0; padding: 0; }
body { background-color: #EEE; }
h1,h2,h3,p{font-family:Verdana;font-weight:lighter;margin:10px;}
.exception {border: 1px solid #CCC; padding: 10px; background-color: #FFF; color: #333; margin:10px;}
pre, .code {font-family: Courier, monospace; font-size:12px;margin:0px;padding:0px;}
.highlighted {background-color: #f0eb96; font-weight: bold; border-top: 1px solid #ccc; border-bottom: 1px solid #ccc;}
.code {background:#fff;border:1px solid #ccc;overflow:auto;}
.line {display: inline-block; background-color: #EFEFEF; padding: 4px 8px 4px 8px; margin-right:10px; }
</style>
</head>
<body>
<div class='exception'>
<h1>Monstra - ".$error_type."</h1>
<p>".$exception->getMessage()."</p>
<h2>Location</h2>
<p>Exception thrown on line <code>".$exception->getLine()."</code> in <code>".$exception->getFile()."</code></p>
");
if ( ! empty($code)) {
echo '<div class="code">';
foreach ($code as $line) {
echo '<pre '; if ($line['highlighted']) { echo 'class="highlighted"'; } echo '><span class="line">' . $line['number'] . '</span>' . $line['code'] . '</pre>';
}
echo '</div>';
}
echo '</div></body></html>';
} else {
// Production
echo ("
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>Monstra</title>
<style>
* { margin: 0; padding: 0; }
.exception {border: 1px solid #CCC; padding: 10px; background-color: #FFF; color: #333; margin:10px;}
body { background-color: #EEE; font-family: sans-serif; font-size: 16px; line-height: 20px; margin: 40px; }
h1,h2,h3,p{font-family:Verdana;font-weight:lighter;margin:10px;}
</style>
</head>
<body>
<div class='exception'>
<h1>Oops!</h1>
<p>An unexpected error has occurred.</p>
</div>
</body>
</html>
");
}
// Writes message to log
@file_put_contents(LOGS . DS . gmdate('Y_m_d') . '.log',
gmdate('Y/m/d H:i:s') . ' --- ' . '['.$error_type.']' . ' --- ' . $exception->getMessage() . ' --- ' . 'Exception thrown on line '.$exception->getLine().' in '.$exception->getFile() . "\n",
FILE_APPEND);
exit(1);
}
/**
* Converts errors to ErrorExceptions.
*
* @param integer $code The error code
* @param string $message The error message
* @param string $file The filename where the error occurred
* @param integer $line The line number where the error occurred
* @return boolean
*/
public static function errorHandler($code, $message, $file, $line)
{
// If isset error_reporting and $code then throw new error exception
if ((error_reporting() & $code) !== 0) {
throw new ErrorException($message, $code, 0, $file, $line);
}
// Don't execute PHP internal error handler
return true;
}
/**
* Returns an array of lines from a file.
*
* @param string $file File in which you want to highlight a line
* @param integer $line Line number to highlight
* @param integer $padding Number of padding lines
* @return array
*/
protected static function highlightCode($file, $line, $padding = 5)
{
// Is file readable ?
if ( ! is_readable($file)) {
return false;
}
// Init vars
$lines = array();
$current_line = 0;
// Open file
$handle = fopen($file, 'r');
// Read file
while ( ! feof($handle)) {
$current_line++;
$temp = fgets($handle);
if ($current_line > $line + $padding) {
break; // Exit loop after we have found what we were looking for
}
if ($current_line >= ($line - $padding) && $current_line <= ($line + $padding)) {
$lines[] = array (
'number' => str_pad($current_line, 4, ' ', STR_PAD_LEFT),
'highlighted' => ($current_line === $line),
'code' => Core::highlightString($temp),
);
}
}
// Close
fclose($handle);
// Return lines
return $lines;
}
/**
* Highlight string
*
* @param string $string String
* @return string
*/
protected static function highlightString($string)
{
return str_replace(array("\n", '<code>', '</code>', '<span style="color: #0000BB">&lt;?php&nbsp;', '#$@r4!/*'),
array('', '', '', '<span style="color: #0000BB">', '/*'),
highlight_string('<?php ' . str_replace('/*', '#$@r4!/*', $string), true));
}
/**
* Convert errors not caught by the errorHandler to ErrorExceptions.
*/
public static function fatalErrorHandler()
{
// Get last error
$error = error_get_last();
// If isset error then throw new error exception
if (isset($error) && ($error['type'] === E_ERROR)) {
Core::exceptionHandler(new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line']));
exit(1);
}
}
/**
* Initialize Monstra engine
*
* @return Core
*/
public static function init()
{
if ( ! isset(self::$instance)) self::$instance = new Core();
return self::$instance;
}
}

183
engine/Options.php Normal file
View File

@@ -0,0 +1,183 @@
<?php defined('MONSTRA_ACCESS') or die('No direct script access.');
/**
* Monstra Options API module
*
* @package Monstra
* @subpackage Engine
* @author Romanenko Sergey / Awilum
* @copyright 2012 Romanenko Sergey / Awilum
* @version $Id$
* @since 1.0.0
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* Monstra is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYING.txt for copyright notices and details.
* @filesource
*/
class Option
{
/**
* Options
*
* @var array
*/
protected static $options = null;
/**
* An instance of the Option class
*
* @var option
*/
protected static $instance = null;
/**
* Initializing options
*
* @param string $name Options file
*/
public static function init()
{
if ( ! isset(self::$instance)) self::$instance = new Option();
return self::$instance;
}
/**
* Protected clone method to enforce singleton behavior.
*
* @access protected
*/
protected function __clone()
{
// Nothing here.
}
/**
* Construct
*/
protected function __construct()
{
Option::$options = new Table('options');
}
/**
* Add a new option
*
* <code>
* Option::add('pages_limit', 10);
* Option::add(array('pages_count' => 10, 'pages_default' => 'home'));
* </code>
*
* @param mixed $option Name of option to add.
* @param mixed $value Option value.
* @return boolean
*/
public static function add($option, $value = null)
{
if (is_array($option)) {
foreach ($option as $k => $v) {
$_option = Option::$options->select('[name="'.$k.'"]', null);
if (count($_option) == 0) {
Option::$options->insert(array('name' => $k, 'value' => $v));
}
}
} else {
$_option = Option::$options->select('[name="'.$option.'"]', null);
if (count($_option) == 0) {
return Option::$options->insert(array('name' => $option, 'value' => $value));
}
}
}
/**
* Update option value
*
* <code>
* Option::update('pages_limit', 12);
* Option::update(array('pages_count' => 10, 'pages_default' => 'home'));
* </code>
*
* @param mixed $option Name of option to update.
* @param mixed $value Option value.
* @return boolean
*/
public static function update($option, $value = null)
{
if (is_array($option)) {
foreach ($option as $k => $v) {
Option::$options->updateWhere('[name="'.$k.'"]', array('value' => $v));
}
} else {
return Option::$options->updateWhere('[name="'.$option.'"]', array('value' => $value));
}
}
/**
* Get option value
*
* <code>
* $pages_limit = Option::get('pages_limit');
* if ($pages_limit == '10') {
* // do something...
* }
* </code>
*
* @param string $option Name of option to get.
* @return string
*/
public static function get($option)
{
// Redefine vars
$option = (string) $option;
// Select specific option
$option_name = Option::$options->select('[name="'.$option.'"]', null);
// Return specific option value
return isset($option_name['value']) ? $option_name['value'] : '';
}
/**
* Delete option
*
* <code>
* Option::delete('pages_limit');
* </code>
*
* @param string $option Name of option to delete.
* @return boolean
*/
public static function delete($option)
{
// Redefine vars
$option = (string) $option;
// Delete specific option
return Option::$options->deleteWhere('[name="'.$option.'"]');
}
/**
* Check if option exist
*
* <code>
* if (Option::exists('pages_limit')) {
* // do something...
* }
* </code>
*
* @param string $option Name of option to check.
* @return boolean
*/
public static function exists($option)
{
// Redefine vars
$option = (string) $option;
// Check if option exists
return (count(Option::$options->select('[name="'.$option.'"]', null)) > 0) ? true : false;
}
}

1266
engine/Orm.php Normal file

File diff suppressed because it is too large Load Diff

1228
engine/Plugins.php Normal file

File diff suppressed because it is too large Load Diff

237
engine/Security.php Normal file
View File

@@ -0,0 +1,237 @@
<?php
/**
* Monstra Library
*
* This source file is part of the Monstra Library. More information,
* documentation and tutorials can be found at http://library.monstra.org
*
* @package Monstra
*
* @author Romanenko Sergey / Awilum
* @copyright (c) 2012 - 2013 Romanenko Sergey / Awilum
* @since 1.0.0
*/
class Security
{
/**
* Key name for token storage
*
* @var string
*/
public static $token_name = 'security_token';
/**
* Protected constructor since this is a static class.
*
* @access protected
*/
protected function __construct()
{
// Nothing here
}
/**
* Generate and store a unique token which can be used to help prevent
* [CSRF](http://wikipedia.org/wiki/Cross_Site_Request_Forgery) attacks.
*
* <code>
* $token = Security::token();
* </code>
*
* You can insert this token into your forms as a hidden field:
*
* <code>
* echo Form::hidden('csrf', Security::token());
* </code>
*
* This provides a basic, but effective, method of preventing CSRF attacks.
*
* @param boolean $new force a new token to be generated?. Default is false
* @return string
*/
public static function token($new = false)
{
// Get the current token
$token = Session::get(Security::$token_name);
// Create a new unique token
if ($new === true or ! $token) {
// Generate a new unique token
$token = sha1(uniqid(mt_rand(), true));
// Store the new token
Session::set(Security::$token_name, $token);
}
// Return token
return $token;
}
/**
* Check that the given token matches the currently stored security token.
*
* <code>
* if (Security::check($token)) {
* // Pass
* }
* </code>
*
* @param string $token token to check
* @return boolean
*/
public static function check($token)
{
return Security::token() === $token;
}
/**
* Encrypt password
*
* <code>
* $encrypt_password = Security::encryptPassword('password');
* </code>
*
* @param string $password Password to encrypt
*/
public static function encryptPassword($password)
{
return md5(md5(trim($password) . MONSTRA_PASSWORD_SALT));
}
/**
* Create safe name. Use to create safe username, filename, pagename.
*
* <code>
* $safe_name = Security::safeName('hello world');
* </code>
*
* @param string $str String
* @param string $delimiter String delimiter
* @param boolean $lowercase String Lowercase
* @return string
*/
public static function safeName($str, $delimiter = '-', $lowercase = false)
{
// Redefine vars
$str = (string) $str;
$delimiter = (string) $delimiter;
$lowercase = (bool) $lowercase;
$delimiter = (string) $delimiter;
// Remove tags
$str = filter_var($str, FILTER_SANITIZE_STRING);
// Decode all entities to their simpler forms
$str = html_entity_decode($str, ENT_QUOTES, 'UTF-8');
// Reserved characters (RFC 3986)
$reserved_characters = array(
'/', '?', ':', '@', '#', '[', ']',
'!', '$', '&', '\'', '(', ')', '*',
'+', ',', ';', '='
);
// Remove reserved characters
$str = str_replace($reserved_characters, ' ', $str);
// Set locale to en_US.UTF8
setlocale(LC_ALL, 'en_US.UTF8');
// Translit ua,ru => latin
$str = Text::translitIt($str);
// Convert string
$str = iconv('UTF-8', 'ASCII//TRANSLIT', $str);
// Remove characters
$str = preg_replace("/[^a-zA-Z0-9\/_|+ -]/", '', $str );
$str = preg_replace("/[\/_|+ -]+/", $delimiter, $str );
$str = trim($str, $delimiter);
// Lowercase
if ($lowercase === true) $str = Text::lowercase($str);
// Return safe name
return $str;
}
/**
* Create safe url.
*
* <code>
* $url = Security::sanitizeURL('http://test.com');
* </code>
*
* @param string $url Url to sanitize
* @return string
*/
public static function sanitizeURL($url)
{
$url = trim($url);
$url = rawurldecode($url);
$url = str_replace(array('--','&quot;','!','@','#','$','%','^','*','(',')','+','{','}','|',':','"','<','>',
'[',']','\\',';',"'",',','*','+','~','`','laquo','raquo',']>','&#8216;','&#8217;','&#8220;','&#8221;','&#8211;','&#8212;'),
array('-','-','','','','','','','','','','','','','','','','','','','','','','','','','','',''),
$url);
$url = str_replace('--', '-', $url);
$url = rtrim($url, "-");
$url = str_replace('..', '', $url);
$url = str_replace('//', '', $url);
$url = preg_replace('/^\//', '', $url);
$url = preg_replace('/^\./', '', $url);
return $url;
}
/**
* Sanitize URL to prevent XSS - Cross-site scripting
*/
public static function runSanitizeURL()
{
$_GET = array_map('Security::sanitizeURL', $_GET);
}
/**
* That prevents null characters between ascii characters.
*
* @param string $str String
*/
public static function removeInvisibleCharacters($str)
{
// Redefine vars
$str = (string) $str;
// Thanks to ci for this tip :)
$non_displayables = array('/%0[0-8bcef]/', '/%1[0-9a-f]/', '/[\x00-\x08]/', '/\x0b/', '/\x0c/', '/[\x0e-\x1f]/');
do {
$cleaned = $str;
$str = preg_replace($non_displayables, '', $str);
} while ($cleaned != $str);
// Return safe string
return $str;
}
/**
* Sanitize data to prevent XSS - Cross-site scripting
*
* @param string $str String
*/
public static function xssClean($str)
{
// Remove invisible characters
$str = Security::removeInvisibleCharacters($str);
// Convert html to plain text
$str = Html::toText($str);
// Return safe string
return $str;
}
}

180
engine/Shortcodes.php Normal file
View File

@@ -0,0 +1,180 @@
<?php defined('MONSTRA_ACCESS') or die('No direct script access.');
/**
* Monstra Shortcodes API
*
* The Shortcode API s a simple regex based parser that allows you to replace simple bbcode-like tags
* within a HTMLText or HTMLVarchar field when rendered into a content.
*
* Examples of shortcode tags:
*
* {shortcode}
* {shortcode parameter="value"}
* {shortcode parameter="value"}Enclosed Content{/shortcode}
*
*
* Example of escaping shortcodes:
*
* {{shortcode}}
*
*
* @package Monstra
* @subpackage Engine
* @author Romanenko Sergey / Awilum
* @copyright 2012 Romanenko Sergey / Awilum
* @version $Id$
* @since 1.0.0
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* Monstra is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYING.txt for copyright notices and details.
* @filesource
*/
class Shortcode
{
/**
* Shortcode tags array
*
* @var shortcode_tags
*/
protected static $shortcode_tags = array();
/**
* Protected constructor since this is a static class.
*
* @access protected
*/
protected function __construct()
{
// Nothing here
}
/**
* Add new shortcode
*
* <code>
* function returnSiteUrl() {
* return Option::get('siteurl');
* }
*
* // Add shortcode {siteurl}
* Shortcode::add('siteurl', 'returnSiteUrl');
* </code>
*
* @param string $shortcode Shortcode tag to be searched in content.
* @param string $callback_function The callback function to replace the shortcode with.
*/
public static function add($shortcode, $callback_function)
{
// Redefine vars
$shortcode = (string) $shortcode;
// Add new shortcode
if (is_callable($callback_function)) Shortcode::$shortcode_tags[$shortcode] = $callback_function;
}
/**
* Remove a specific registered shortcode.
*
* <code>
* Shortcode::delete('shortcode_name');
* </code>
*
* @param string $shortcode Shortcode tag.
*/
public static function delete($shortcode)
{
// Redefine vars
$shortcode = (string) $shortcode;
// Delete shortcode
if (Shortcode::exists($shortcode)) unset(Shortcode::$shortcode_tags[$shortcode]);
}
/**
* Remove all registered shortcodes.
*
* <code>
* Shortcode::clear();
* </code>
*
*/
public static function clear()
{
Shortcode::$shortcode_tags = array();
}
/**
* Check if a shortcode has been registered.
*
* <code>
* if (Shortcode::exists('shortcode_name')) {
* // do something...
* }
* </code>
*
* @param string $shortcode Shortcode tag.
*/
public static function exists($shortcode)
{
// Redefine vars
$shortcode = (string) $shortcode;
// Check shortcode
return array_key_exists($shortcode, Shortcode::$shortcode_tags);
}
/**
* Parse a string, and replace any registered shortcodes within it with the result of the mapped callback.
*
* <code>
* $content = Shortcode::parse($content);
* </code>
*
* @param string $content Content
* @return string
*/
public static function parse($content)
{
if ( ! Shortcode::$shortcode_tags) return $content;
$shortcodes = implode('|', array_map('preg_quote', array_keys(Shortcode::$shortcode_tags)));
$pattern = "/(.?)\{([$shortcodes]+)(.*?)(\/)?\}(?(4)|(?:(.+?)\{\/\s*\\2\s*\}))?(.?)/s";
return preg_replace_callback($pattern, 'Shortcode::_handle', $content);
}
/**
* _handle()
*/
protected static function _handle($matches)
{
$prefix = $matches[1];
$suffix = $matches[6];
$shortcode = $matches[2];
// Allow for escaping shortcodes by enclosing them in {{shortcode}}
if ($prefix == '{' && $suffix == '}') {
return substr($matches[0], 1, -1);
}
$attributes = array(); // Parse attributes into into this array.
if (preg_match_all('/(\w+) *= *(?:([\'"])(.*?)\\2|([^ "\'>]+))/', $matches[3], $match, PREG_SET_ORDER)) {
foreach ($match as $attribute) {
if ( ! empty($attribute[4])) {
$attributes[strtolower($attribute[1])] = $attribute[4];
} elseif ( ! empty($attribute[3])) {
$attributes[strtolower($attribute[1])] = $attribute[3];
}
}
}
// Check if this shortcode realy exists then call user function else return empty string
return (isset(Shortcode::$shortcode_tags[$shortcode])) ? $prefix . call_user_func(Shortcode::$shortcode_tags[$shortcode], $attributes, $matches[5], $shortcode) . $suffix : '';
}
}

227
engine/Site.php Normal file
View File

@@ -0,0 +1,227 @@
<?php defined('MONSTRA_ACCESS') or die('No direct script access.');
/**
* Monstra Site module
*
* @package Monstra
* @subpackage Engine
* @author Romanenko Sergey / Awilum
* @copyright 2012 Romanenko Sergey / Awilum
* @version $Id$
* @since 1.0.0
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* Monstra is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYING.txt for copyright notices and details.
* @filesource
*/
class Site
{
/**
* An instance of the Site class
*
* @var site
*/
protected static $instance = null;
/**
* Initializing site
*
* @return Site
*/
public static function init()
{
if ( ! isset(self::$instance)) self::$instance = new Site();
return self::$instance;
}
/**
* Protected clone method to enforce singleton behavior.
*
* @access protected
*/
protected function __clone()
{
// Nothing here.
}
/**
* Construct
*/
protected function __construct()
{
call_user_func(ucfirst(Uri::command()).'::main');
}
/**
* Get site name
*
* <code>
* echo Site::name();
* </code>
*
* @return string
*/
public static function name()
{
return Option::get('sitename');
}
/**
* Get site theme
*
* <code>
* echo Site::theme();
* </code>
*
* @return string
*/
public static function theme()
{
return Option::get('theme_site_name');
}
/**
* Get Page title
*
* <code>
* echo Site::title();
* </code>
*
* @return string
*/
public static function title()
{
return call_user_func(ucfirst(Uri::command()).'::title');
}
/**
* Get page description
*
* <code>
* echo Site::description();
* </code>
*
* @return string
*/
public static function description()
{
return (($description = trim(call_user_func(ucfirst(Uri::command()).'::description'))) == '') ? Html::toText(Option::get('description')) : Html::toText($description);
}
/**
* Get page keywords
*
* <code>
* echo Site::keywords();
* </code>
*
* @return string
*/
public static function keywords()
{
return (($keywords = trim(call_user_func(ucfirst(Uri::command()).'::keywords'))) == '') ? Html::toText(Option::get('keywords')) : Html::toText($keywords);
}
/**
* Get site slogan
*
* <code>
* echo Site::slogan();
* </code>
*
* @return string
*/
public static function slogan()
{
return Option::get('slogan');
}
/**
* Get page content
*
* <code>
* echo Site::content();
* </code>
*
* @return string
*/
public static function content()
{
return Filter::apply('content', call_user_func(ucfirst(Uri::command()).'::content'));
}
/**
* Get compressed template
*
* <code>
* echo Site::template();
* </code>
*
* @param string $theme Theme name
* @return mixed
*/
public static function template($theme = null)
{
// Get specific theme or current theme
$current_theme = ($theme == null) ? Option::get('theme_site_name') : $theme ;
// Get template
$template = call_user_func(ucfirst(Uri::command()).'::template');
// Check whether is there such a template in the current theme
// else return default template: index
// also compress template file :)
if (File::exists(THEMES_SITE . DS . $current_theme . DS . $template . '.template.php')) {
if ( ! file_exists(MINIFY . DS . 'theme.' . $current_theme . '.minify.' . $template . '.template.php') or
filemtime(THEMES_SITE . DS . $current_theme . DS . $template .'.template.php') > filemtime(MINIFY . DS . 'theme.' . $current_theme . '.minify.' . $template . '.template.php')) {
$buffer = file_get_contents(THEMES_SITE. DS . $current_theme . DS . $template .'.template.php');
$buffer = Minify::html($buffer);
file_put_contents(MINIFY . DS . 'theme.' . $current_theme . '.minify.' . $template . '.template.php', $buffer);
}
return 'minify.'.$template;
} else {
if ( ! File::exists(MINIFY . DS . 'theme.' . $current_theme . '.' . 'minify.index.template.php') or
filemtime(THEMES_SITE . DS . $current_theme . DS . 'index.template.php') > filemtime(MINIFY . DS . 'theme.' . $current_theme . '.' . 'minify.index.template.php')) {
$buffer = file_get_contents(THEMES_SITE . DS . $current_theme . DS . 'index.template.php');
$buffer = Minify::html($buffer);
file_put_contents(MINIFY . DS . 'theme.' . $current_theme . '.' . 'minify.index.template.php', $buffer);
}
return 'minify.index';
}
}
/**
* Get site url
*
* <code>
* echo Site::url();
* </code>
*
* @return string
*/
public static function url()
{
return Option::get('siteurl');
}
/**
* Get copyright information
*
* <code>
* echo Site::powered();
* </code>
*
* @return string
*/
public static function powered()
{
return __('Powered by', 'system').' <a href="http://monstra.org" target="_blank">Monstra</a> ' . Core::VERSION;
}
}

163
engine/Uri.php Normal file
View File

@@ -0,0 +1,163 @@
<?php
/**
* Monstra Library
*
* This source file is part of the Monstra Library. More information,
* documentation and tutorials can be found at http://library.monstra.org
*
* @package Monstra
*
* @author Romanenko Sergey / Awilum
* @copyright (c) 2012 - 2013 Romanenko Sergey / Awilum
* @since 1.0.0
*/
class Uri
{
/**
* Protected constructor since this is a static class.
*
* @access protected
*/
protected function __construct()
{
// Nothing here
}
/**
* Default component
*
* @var string
*/
public static $default_component = 'pages';
/**
* Get uri and explode command/param1/param2
*
* <code>
* $segments = Uri::segments();
* </code>
*
* @return array
*/
public static function segments()
{
// Get request uri and current script path
$request_uri = explode('/', $_SERVER['REQUEST_URI']);
$script_name = explode('/', $_SERVER['SCRIPT_NAME']);
// Delete script name
for ($i = 0; $i < sizeof($script_name); $i++) {
if ($request_uri[$i] == $script_name[$i]) {
unset($request_uri[$i]);
}
}
// Get all the values of an array
$uri = array_values($request_uri);
// Ability to pass parameters
foreach ($uri as $i => $u) {
if (isset($uri[$i])) { $pos = strrpos($uri[$i], "?"); if ($pos === false) { $uri[$i] = Security::sanitizeURL($uri[$i]); } else { $uri[$i] = Security::sanitizeURL(substr($uri[$i], 0, $pos)); } }
}
// Return uri segments
return $uri;
}
/**
* Get uri segment
*
* <code>
* $segment = Uri::segment(1);
* </code>
*
* @param integer $segment Segment
* @return mixed
*/
public static function segment($segment)
{
$segments = Uri::segments();
return isset($segments[$segment]) ? $segments[$segment] : null;
}
/**
* Get command/component from registed components
*
* <code>
* $command = Uri::command();
* </code>
*
* @return array
*/
public static function command()
{
// Get uri segments
$uri = Uri::segments();
if ( ! isset($uri[0])) {
$uri[0] = Uri::$default_component;
} else {
if ( ! in_array($uri[0], Plugin::$components) ) {
$uri[0] = Uri::$default_component;
} else {
$uri[0] = $uri[0];
}
}
return $uri[0];
}
/**
* Get uri parammeters
*
* <code>
* $params = Uri::params();
* </code>
*
* @return array
*/
public static function params()
{
//Init data array
$data = array();
// Get URI
$uri = Uri::segments();
// http://site.com/ and http://site.com/index.php same main home pages
if ( ! isset($uri[0])) {
$uri[0] = '';
}
// param1/param2
if ($uri[0] !== Uri::$default_component) {
if (isset($uri[1])) {
$data[0] = $uri[0];
$data[1] = $uri[1];
// Some more uri parts :)
// site.ru/part1/part2/part3/part4/part5/part6/
if (isset($uri[2])) $data[2] = $uri[2];
if (isset($uri[3])) $data[3] = $uri[3];
if (isset($uri[4])) $data[4] = $uri[4];
if (isset($uri[5])) $data[5] = $uri[5];
} else { // default
$data[0] = $uri[0];
}
} else {
// This is good for box plugin Pages
// parent/child
if (isset($uri[2])) {
$data[0] = $uri[1];
$data[1] = $uri[2];
} else { // default
$data[0] = $uri[1];
}
}
return $data;
}
}

1036
engine/Xmldb.php Normal file

File diff suppressed because it is too large Load Diff

53
engine/_init.php Normal file
View File

@@ -0,0 +1,53 @@
<?php defined('MONSTRA_ACCESS') or die('No direct script access.');
/**
* Include engine core
*/
include ROOT . '/libraries/Gelato/Gelato.php';
include ROOT . '/engine/Core.php';
/**
* Set core environment
*
* Monstra has four predefined environments:
* Core::DEVELOPMENT - The development environment.
* Core::TESTING - The test environment.
* Core::STAGING - The staging environment.
* Core::PRODUCTION - The production environment.
*/
Core::$environment = Core::DEVELOPMENT;
/**
* Monstra requires PHP 5.2.0 or greater
*/
if (version_compare(PHP_VERSION, "5.2.0", "<")) {
exit("Monstra requires PHP 5.2.0 or greater.");
}
/**
* Report Errors
*/
if (Core::$environment == Core::PRODUCTION) {
/**
* Report All Errors
*
* By setting error reporting to -1, we essentially force PHP to report
* every error, and this is guranteed to show every error on future
* releases of PHP. This allows everything to be fixed early!
*/
error_reporting(0);
} else {
/**
* Production environment
*/
error_reporting(-1);
}
/**
* Initialize core
*/
Core::init();