diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..753e230
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,26 @@
+#
+# Monstra CMS :: php & apache settings
+#
+
+# Set default charset utf-8
+AddDefaultCharset UTF-8
+
+# Don't show directory listings for URLs which map to a directory.
+Options -Indexes
+
+# PHP 5, Apache 1 and 2.
+
+
".$exception->getMessage()."
+Exception thrown on line ".$exception->getLine()."
in ".$exception->getFile()."
' . $line['number'] . '' . $line['code'] . '
';
+ }
+ echo 'An unexpected error has occurred.
+', '
', '<?php ', '#$@r4!/*'),
+ array('', '', '', '', '/*'),
+ highlight_string('
+ * Option::add('pages_limit', 10);
+ * Option::add(array('pages_count' => 10, 'pages_default' => 'home'));
+ *
+ *
+ * @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
+ *
+ *
+ * Option::update('pages_limit', 12);
+ * Option::update(array('pages_count' => 10, 'pages_default' => 'home'));
+ *
+ *
+ * @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
+ *
+ *
+ * $pages_limit = Option::get('pages_limit');
+ * if ($pages_limit == '10') {
+ * // do something...
+ * }
+ *
+ *
+ * @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
+ *
+ *
+ * Option::delete('pages_limit');
+ *
+ *
+ * @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.'"]');
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/engine/plugins.php b/monstra/engine/plugins.php
new file mode 100644
index 0000000..3269bb2
--- /dev/null
+++ b/monstra/engine/plugins.php
@@ -0,0 +1,1206 @@
+select(null, 'all', null, array('location', 'frontend', 'backend', 'status', 'priority'), 'priority', 'ASC');
+
+ // Now include plugins from $records plugins array
+ // If plugin is active then load it to the system.
+ foreach ($records as $record) {
+ if ($record['status'] == 'active') {
+ include_once ROOT . DS . $record['location'];
+ }
+ }
+ }
+
+
+ /**
+ * Get plugin admin
+ *
+ *
+ * // Get admin for Blog plugin
+ * Plugin::admin('blog');
+ *
+ *
+ * @param string $plug Plugin Name
+ * @param string $alt_folder Alternative plugin folder
+ */
+ public static function admin($plug, $alt_folder = null) {
+
+ // Redefine arguments
+ $plug = (string) $plug;
+
+ // Plugin admin extension
+ $ext = '.admin.php';
+
+ // Plugin admin can be loaded only in backend
+ if (BACKEND) {
+
+ // Plugin admin folder
+ if ( ! empty($alt_folder)) {
+ $folder = $alt_folder . DS . strtolower($plug);
+ } else {
+ $folder = strtolower($plug);
+ }
+
+ // Path to plugin admin file
+ $path = PLUGINS . DS . $folder . DS . $plug . $ext;
+
+ // Load plugin admin
+ if (File::exists($path)) {
+ include $path;
+ }
+ }
+ }
+
+
+
+ /**
+ * Register new plugin in system
+ *
+ *
+ * // Register plugin
+ * Plugin::register( __FILE__,
+ * __('Blog'),
+ * __('Blog plugin'),
+ * '1.0.0',
+ * 'Awilum',
+ * 'http://example.org/',
+ * 'blog');
+ *
+ *
+ * @param string $file Plugin file
+ * @param string $title Plugin title
+ * @param string $description Plugin description
+ * @param string $version Plugin version
+ * @param string $author Plugin author
+ * @param string $author_uri Plugin author uri
+ * @param string $component Plugin as component
+ * @param boolean $box Plugin as box
+ */
+ public static function register($file, $title, $description = null, $version = null, $author = null, $author_uri = null, $component = null, $box = false) {
+
+ // Redefine arguments
+ $file = (string) $file;
+ $title = (string) $title;
+ $description = ($description === null) ? null : (string) $description;
+ $version = ($version === null) ? null : (string) $version;
+ $author = ($author === null) ? null : (string) $author;
+ $author_uri = ($author_uri === null) ? null : (string) $author_uri;
+ $component = ($component === null) ? null : (string) $component;
+ $box = (bool) $box;
+
+
+ // Get plugin id from name.plugin.php
+ $id = strtolower(basename($file, '.plugin.php'));
+
+ // Set plugin privilege 'box' if $box is true
+ if ($box) $privilege = 'box'; else $privilege = '';
+
+ // Register plugin in global plugins array.
+ Plugin::$plugins[$id] = array(
+ 'id' => $id,
+ 'title' => $title,
+ 'privilege' => $privilege,
+ 'version' => $version,
+ 'description' => $description,
+ 'author' => $author,
+ 'author_uri' => $author_uri,
+ );
+
+ // Add plugin as a component
+ // Plugin - component will be available at the link sitename/component_name
+ // Example:
+ // www.example.org/guestbook
+ // www.example.org/news
+ if ( ! empty($component)) {
+ Plugin::$components[] = $component;
+ }
+ }
+
+ }
+
+
+ /**
+ * Frontend class
+ */
+ class Frontend {
+
+ public static function main() { }
+ public static function title() { return ''; }
+ public static function description() { return ''; }
+ public static function keywords() { return ''; }
+ public static function template() { return 'index'; }
+ public static function content() { return ''; }
+
+ }
+
+
+ /**
+ * Backend class
+ */
+ class Backend {
+
+ public static function main() { }
+
+ }
+
+
+ /**
+ * View class
+ */
+ class View {
+
+
+ /**
+ * Path to view file.
+ *
+ * @var string
+ */
+ protected $view_file;
+
+
+ /**
+ * View variables.
+ *
+ * @var array
+ */
+ protected $vars = array();
+
+
+ /**
+ * Global view variables.
+ *
+ * @var array
+ */
+ protected static $global_vars = array();
+
+
+ /**
+ * The output.
+ *
+ * @var string
+ */
+ protected $output;
+
+
+ /**
+ * Create a new view object.
+ *
+ *
+ * // Create new view object
+ * $view = new View('blog/views/backend/index');
+ *
+ * // Assign some new variables
+ * $view->assign('msg', 'Some message...');
+ *
+ * // Get view
+ * $output = $view->render();
+ *
+ * // Display view
+ * echo $output;
+ *
+ *
+ * @param string $view Name of the view file
+ * @param array $variables Array of view variables
+ */
+ public function __construct($view, array $variables = array()) {
+
+ // Set view file
+ $this->view_file = PLUGINS . DS . $view . '.view.php';
+
+ // Is view file exists ?
+ if (file_exists($this->view_file) === false) {
+ throw new RuntimeException(vsprintf("%s(): The '%s' view does not exist.", array(__METHOD__, $view)));
+ }
+
+ // Set view variables
+ $this->vars = $variables;
+ }
+
+
+ /**
+ * View factory
+ *
+ *
+ * // Create new view object, assign some variables
+ * // and displays the rendered view in the browser.
+ * View::factory('blog/views/backend/index')
+ * ->assign('msg', 'Some message...')
+ * ->display();
+ *
+ *
+ * @param string $view Name of the view file
+ * @param array $variables Array of view variables
+ * @return View
+ */
+ public static function factory($view, array $variables = array()) {
+ return new View($view, $variables);
+ }
+
+
+ /**
+ * Assign a view variable.
+ *
+ *
+ * $view->assign('msg', 'Some message...');
+ *
+ *
+ * @param string $key Variable name
+ * @param mixed $value Variable value
+ * @param boolean $global Set variable available in all views
+ * @return View
+ */
+ public function assign($key, $value, $global = false) {
+
+ // Assign a new view variable (global or locale)
+ if ($global === false) {
+ $this->vars[$key] = $value;
+ } else {
+ View::$global_vars[$key] = $value;
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * Include the view file and extracts the view variables before returning the generated output.
+ *
+ *
+ * // Get view
+ * $output = $view->render();
+ *
+ * // Display output
+ * echo $output;
+ *
+ *
+ * @param string $filter Callback function used to filter output
+ * @return string
+ */
+ public function render($filter = null) {
+
+ // Is output empty ?
+ if (empty($this->output)) {
+
+ // Extract variables as references
+ extract(array_merge($this->vars, View::$global_vars), EXTR_REFS);
+
+ // Turn on output buffering
+ ob_start();
+
+ // Include view file
+ include($this->view_file);
+
+ // Output...
+ $this->output = ob_get_clean();
+ }
+
+ // Filter output ?
+ if ($filter !== null) {
+ $this->output = call_user_func($filter, $this->output);
+ }
+
+ // Return output
+ return $this->output;
+ }
+
+
+ /**
+ * Displays the rendered view in the browser.
+ *
+ *
+ * $view->display();
+ *
+ *
+ */
+ public function display() {
+ echo $this->render();
+ }
+
+
+ /**
+ * Magic setter method that assigns a view variable.
+ *
+ * @param string $key Variable name
+ * @param mixed $value Variable value
+ */
+ public function __set($key, $value) {
+ $this->vars[$key] = $value;
+ }
+
+
+ /**
+ * Magic getter method that returns a view variable.
+ *
+ * @param string $key Variable name
+ * @return mixed
+ */
+ public function __get($key) {
+
+ if (isset($this->vars[$key])) {
+ return $this->vars[$key];
+ }
+ }
+
+
+ /**
+ * Magic isset method that checks if a view variable is set.
+ *
+ * @param string $key Variable name
+ * @return boolean
+ */
+ public function __isset($key) {
+ return isset($this->vars[$key]);
+ }
+
+
+ /**
+ * Magic unset method that unsets a view variable.
+ *
+ * @param string $key Variable name
+ */
+ public function __unset($key) {
+ unset($this->vars[$key]);
+ }
+
+
+ /**
+ * Method that magically converts the view object into a string.
+ *
+ * @return string
+ */
+ public function __toString() {
+ return $this->render();
+ }
+ }
+
+
+ /**
+ * I18n class
+ */
+ class I18n {
+
+
+ /**
+ * Dictionary
+ *
+ * @var array
+ */
+ public static $dictionary = array();
+
+
+ /**
+ * An instance of the I18n class
+ *
+ * @var I18n
+ */
+ protected static $instance = null;
+
+
+ /**
+ * Initializing I18n
+ *
+ * @param string $dir Plugins directory
+ */
+ public static function init($locale) {
+ if ( ! isset(self::$instance)) self::$instance = new I18n($locale);
+ return self::$instance;
+ }
+
+
+ /**
+ * Protected clone method to enforce singleton behavior.
+ *
+ * @access protected
+ */
+ protected function __clone() {
+ // Nothing here.
+ }
+
+
+ /**
+ * Construct
+ */
+ protected function __construct($locale) {
+
+ // Redefine arguments
+ $locale = (string) $locale;
+
+ // Get lang table for current locale
+ $lang_table = Cache::get('i18n', $locale);
+
+ // If lang_table is empty then create new
+ if ( ! $lang_table) {
+
+ // Get plugins Table
+ $plugins = new Table('plugins');
+
+ // Get all plugins
+ $records = $plugins->select(null, 'all', null, array('location', 'priority'), 'priority', 'ASC');
+
+ // Init var
+ $lang_table = array();
+
+ // Loop through each installed plugin
+ foreach ($records as $record) {
+
+ if (is_dir(ROOT . DS . dirname($record['location']) . DS . 'languages')) {
+
+ // Init var
+ $t = array();
+
+ // Check lang file
+ if (file_exists(ROOT . DS . dirname($record['location']) . DS . 'languages' . DS . $locale . '.lang.php')) {
+
+ // Merge the language strings into the sub table
+ $t = array_merge($t, include ROOT . DS . dirname($record['location']) . DS . 'languages' . DS . $locale . '.lang.php');
+
+ }
+
+ // Append the sub table, preventing less specific language files from overloading more specific files
+ $lang_table += $t;
+ }
+ }
+
+ // Save lang table for current locale
+ Cache::put('i18n', $locale, $lang_table);
+
+ // Update dictionary
+ I18n::$dictionary = $lang_table;
+ }
+
+ // Update dictionary
+ I18n::$dictionary = $lang_table;
+ }
+
+
+ /**
+ * Returns translation of a string. If no translation exists, the original
+ * string will be returned. No parameters are replaced.
+ *
+ *
+ * $hello = I18n::find('Hello friends, my name is :name', 'namespace');
+ *
+ *
+ * @param string $string Text to translate
+ * @param string $namespace Namespace
+ * @return string
+ */
+ public static function find($string, $namespace = null) {
+
+ // Redefine arguments
+ $string = (string) $string;
+
+ // Return string
+ if (isset(I18n::$dictionary[$namespace][$string])) return I18n::$dictionary[$namespace][$string]; else return $string;
+ }
+
+ }
+
+
+ /**
+ * Global Translation/internationalization function.
+ * Accepts an English string and returns its translation
+ * to the active system language. If the given string is not available in the
+ * current dictionary the original English string will be returned.
+ *
+ *
+ * // Display a translated message
+ * echo __('Hello, world', 'namespace');
+ *
+ * // With parameter replacement
+ * echo __('Hello, :user', 'namespace', array(':user' => $username));
+ *
+ *
+ * @global array $dictionary Dictionary
+ * @param string $string String to translate
+ * @param array $values Values to replace in the translated text
+ * @param string $namespace Namespace
+ * @return string
+ */
+ function __($string, $namespace = null, array $values = null) {
+
+ // Redefine arguments
+ $string = (string) $string;
+
+ // Find string in dictionary
+ $string = I18n::find($string, $namespace);
+
+ // Return string
+ return empty($values) ? $string : strtr($string, $values);
+ }
+
+
+ /**
+ * Action class
+ */
+ class Action {
+
+
+ /**
+ * Actions
+ *
+ * @var array
+ */
+ public static $actions = array();
+
+
+ /**
+ * Protected constructor since this is a static class.
+ *
+ * @access protected
+ */
+ protected function __construct() {
+ // Nothing here
+ }
+
+
+ /**
+ * Hooks a function on to a specific action.
+ *
+ *
+ * // Hooks a function "newLink" on to a "footer" action.
+ * Action::add('footer', 'newLink', 10);
+ *
+ * function newLink() {
+ * echo 'My link';
+ * }
+ *
+ *
+ * @param string $action_name Action name
+ * @param string $added_function Added function
+ * @param integer $priority Priority. Default is 10
+ * @param array $args Arguments
+ */
+ public static function add($action_name, $added_function, $priority = 10, array $args = null) {
+
+ // Hooks a function on to a specific action.
+ Action::$actions[] = array(
+ 'action_name' => (string)$action_name,
+ 'function' => (string)$added_function,
+ 'priority' => (int)$priority,
+ 'args' => $args
+ );
+ }
+
+
+ /**
+ * Run functions hooked on a specific action hook.
+ *
+ *
+ * // Run functions hooked on a "footer" action hook.
+ * Action::run('footer');
+ *
+ *
+ * @param string $action_name Action name
+ * @param array $args Arguments
+ * @param boolean $return Return data or not. Default is false
+ * @return mixed
+ */
+ public static function run($action_name, $args = array(), $return = false) {
+
+ // Redefine arguments
+ $action_name = (string) $action_name;
+ $return = (bool) $return;
+
+ // Run action
+ if (count(Action::$actions) > 0) {
+
+ // Sort actions by priority
+ $actions = Arr::subvalSort(Action::$actions, 'priority');
+
+ // Loop through $actions array
+ foreach ($actions as $action) {
+
+ // Execute specific action
+ if ($action['action_name'] == $action_name) {
+
+ // isset arguments ?
+ if (isset($args)) {
+
+ // Return or Render specific action results ?
+ if ($return) {
+ return call_user_func_array($action['function'], $args);
+ } else {
+ call_user_func_array($action['function'], $args);
+ }
+
+ } else {
+
+ if ($return) {
+ return call_user_func_array($action['function'], $action['args']);
+ } else {
+ call_user_func_array($action['function'], $action['args']);
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+
+ /**
+ * Filter class
+ */
+ class Filter {
+
+
+ /**
+ * Filters
+ *
+ * @var array
+ */
+ public static $filters = array();
+
+
+ /**
+ * Protected constructor since this is a static class.
+ *
+ * @access protected
+ */
+ protected function __construct() {
+ // Nothing here
+ }
+
+
+ /**
+ * Apply filters
+ *
+ *
+ * Filter::apply('content', $content);
+ *
+ *
+ * @param string $filter_name The name of the filter hook.
+ * @param mixed $value The value on which the filters hooked.
+ * @return mixed
+ */
+ public static function apply($filter_name, $value) {
+
+ // Redefine arguments
+ $filter_name = (string) $filter_name;
+
+ $args = array_slice(func_get_args(), 2);
+
+ if ( ! isset(Filter::$filters[$filter_name])) {
+ return $value;
+ }
+
+ foreach (Filter::$filters[$filter_name] as $priority => $functions) {
+ if ( ! is_null($functions)) {
+ foreach ($functions as $function) {
+ $all_args = array_merge(array($value), $args);
+ $function_name = $function['function'];
+ $accepted_args = $function['accepted_args'];
+ if ($accepted_args == 1) {
+ $the_args = array($value);
+ } elseif ($accepted_args > 1) {
+ $the_args = array_slice($all_args, 0, $accepted_args);
+ } elseif ($accepted_args == 0) {
+ $the_args = null;
+ } else {
+ $the_args = $all_args;
+ }
+ $value = call_user_func_array($function_name, $the_args);
+ }
+ }
+ }
+ return $value;
+ }
+
+
+ /**
+ * Add filter
+ *
+ *
+ * Filter::add('content', 'replacer');
+ *
+ * function replacer($content) {
+ * return preg_replace(array('/\[b\](.*?)\[\/b\]/ms'), array('\1'), $content);
+ * }
+ *
+ *
+ * @param string $filter_name The name of the filter to hook the $function_to_add to.
+ * @param string $function_to_add The name of the function to be called when the filter is applied.
+ * @param integer $priority Function to add priority - default is 10.
+ * @param integer $accepted_args The number of arguments the function accept default is 1.
+ * @return boolean
+ */
+ public static function add($filter_name, $function_to_add, $priority = 10, $accepted_args = 1) {
+
+ // Redefine arguments
+ $filter_name = (string) $filter_name;
+ $function_to_add = (string) $function_to_add;
+ $priority = (int) $priority;
+ $accepted_args = (int) $accepted_args;
+
+ // Check that we don't already have the same filter at the same priority. Thanks to WP :)
+ if (isset(Filter::$filters[$filter_name]["$priority"])) {
+ foreach (Filter::$filters[$filter_name]["$priority"] as $filter) {
+ if ($filter['function'] == $function_to_add) {
+ return true;
+ }
+ }
+ }
+
+ Filter::$filters[$filter_name]["$priority"][] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
+
+ // Sort
+ ksort(Filter::$filters[$filter_name]["$priority"]);
+
+ return true;
+ }
+
+ }
+
+
+ /**
+ * Stylesheet class
+ */
+ class Stylesheet {
+
+
+ /**
+ * Stylesheets
+ *
+ * @var array
+ */
+ public static $stylesheets = array();
+
+
+ /**
+ * Protected constructor since this is a static class.
+ *
+ * @access protected
+ */
+ protected function __construct() {
+ // Nothing here
+ }
+
+
+ /**
+ * Add stylesheet
+ *
+ *
+ * Stylesheet::add('path/to/my/stylesheet1.css');
+ * Stylesheet::add('path/to/my/stylesheet2.css', 'frontend', 11);
+ * Stylesheet::add('path/to/my/stylesheet3.css', 'backend',12);
+ *
+ *
+ * @param string $file File path
+ * @param string $load Load stylesheet on frontend, backend or both
+ * @param integer $priority Priority. Default is 10
+ */
+ public static function add($file, $load = 'frontend', $priority = 10) {
+ Stylesheet::$stylesheets[] = array(
+ 'file' => (string)$file,
+ 'load' => (string)$load,
+ 'priority' => (int)$priority,
+ );
+ }
+
+
+ /**
+ * Minify, combine and load site stylesheet
+ */
+ public static function load() {
+
+ $backend_site_css_path = MINIFY . DS . 'backend_site.minify.css';
+ $frontend_site_css_path = MINIFY . DS . 'frontend_site.minify.css';
+
+ // Load stylesheets
+ if (count(Stylesheet::$stylesheets) > 0) {
+
+ $backend_buffer = '';
+ $backend_regenerate = false;
+
+ $frontend_buffer = '';
+ $frontend_regenerate = false;
+
+
+ // Sort stylesheets by priority
+ $stylesheets = Arr::subvalSort(Stylesheet::$stylesheets, 'priority');
+
+ // Build backend site stylesheets
+ foreach ($stylesheets as $stylesheet) {
+ if ((file_exists(ROOT . DS . $stylesheet['file'])) and (($stylesheet['load'] == 'backend') or ($stylesheet['load'] == 'both')) ) {
+ if ( ! file_exists($backend_site_css_path) or filemtime(ROOT . DS . $stylesheet['file']) > filemtime($backend_site_css_path)) {
+ $backend_regenerate = true;
+ break;
+ }
+ }
+ }
+
+ // Regenerate site stylesheet
+ if ($backend_regenerate) {
+ foreach ($stylesheets as $stylesheet) {
+ if ((file_exists(ROOT . DS . $stylesheet['file'])) and (($stylesheet['load'] == 'backend') or ($stylesheet['load'] == 'both')) ) {
+ $backend_buffer .= file_get_contents(ROOT . DS . $stylesheet['file']);
+ }
+ }
+ $backend_buffer = Stylesheet::parseVariables($backend_buffer);
+ file_put_contents($backend_site_css_path, Minify::css($backend_buffer));
+ $backend_regenerate = false;
+ }
+
+
+ // Build frontend site stylesheets
+ foreach ($stylesheets as $stylesheet) {
+ if ((file_exists(ROOT . DS . $stylesheet['file'])) and (($stylesheet['load'] == 'frontend') or ($stylesheet['load'] == 'both')) ) {
+ if ( ! file_exists($frontend_site_css_path) or filemtime(ROOT . DS . $stylesheet['file']) > filemtime($frontend_site_css_path)) {
+ $frontend_regenerate = true;
+ break;
+ }
+ }
+ }
+
+ // Regenerate site stylesheet
+ if ($frontend_regenerate) {
+ foreach ($stylesheets as $stylesheet) {
+ if ((file_exists(ROOT . DS . $stylesheet['file'])) and (($stylesheet['load'] == 'frontend') or ($stylesheet['load'] == 'both')) ) {
+ $frontend_buffer .= file_get_contents(ROOT . DS . $stylesheet['file']);
+ }
+ }
+ $frontend_buffer = Stylesheet::parseVariables($frontend_buffer);
+ file_put_contents($frontend_site_css_path, Minify::css($frontend_buffer));
+ $frontend_regenerate = false;
+ }
+
+ // Render
+ if (BACKEND) {
+ echo '';
+ } else {
+ echo '';
+ }
+ }
+ }
+
+
+ /**
+ * CSS Parser
+ */
+ public static function parseVariables($frontend_buffer) {
+ return str_replace(array('@site_url',
+ '@theme_site_url',
+ '@theme_admin_url'),
+ array(Option::get('siteurl'),
+ Option::get('siteurl').'public/themes/'.Option::get('theme_site_name'),
+ Option::get('siteurl').'admin/themes/'.Option::get('theme_admin_name')),
+ $frontend_buffer);
+ }
+
+
+ }
+
+
+ /**
+ * Javascript class
+ */
+ class Javascript {
+
+
+ /**
+ * Javascripts
+ *
+ * @var array
+ */
+ public static $javascripts = array();
+
+ /**
+ * Protected constructor since this is a static class.
+ *
+ * @access protected
+ */
+ protected function __construct() {
+ // Nothing here
+ }
+
+
+ /**
+ * Add javascript
+ *
+ *
+ * Javascript::add('path/to/my/script1.js');
+ * Javascript::add('path/to/my/script2.js', 'frontend', 11);
+ * Javascript::add('path/to/my/script3.js', 'backend', 12);
+ *
+ *
+ * @param string $file File path
+ * @param string $load Load script on frontend, backend or both
+ * @param inteeer $priority Priority default is 10
+ */
+ public static function add($file, $load = 'frontend', $priority = 10) {
+ Javascript::$javascripts[] = array(
+ 'file' => (string)$file,
+ 'load' => (string)$load,
+ 'priority' => (int)$priority,
+ );
+ }
+
+
+ /**
+ * Combine and load site javascript
+ */
+ public static function load() {
+
+ $backend_site_js_path = MINIFY . DS . 'backend_site.minify.js';
+ $frontend_site_js_path = MINIFY . DS . 'frontend_site.minify.js';
+
+ // Load javascripts
+ if (count(Javascript::$javascripts) > 0) {
+
+ $backend_buffer = '';
+ $backend_regenerate = false;
+
+ $frontend_buffer = '';
+ $frontend_regenerate = false;
+
+
+ // Sort javascripts by priority
+ $javascripts = Arr::subvalSort(Javascript::$javascripts, 'priority');
+
+ // Build backend site javascript
+ foreach ($javascripts as $javascript) {
+ if ((file_exists(ROOT . DS . $javascript['file'])) and (($javascript['load'] == 'backend') or ($javascript['load'] == 'both')) ) {
+ if ( ! file_exists($backend_site_js_path) or filemtime(ROOT . DS . $javascript['file']) > filemtime($backend_site_js_path)) {
+ $backend_regenerate = true;
+ break;
+ }
+ }
+ }
+
+ // Regenerate site javascript
+ if ($backend_regenerate) {
+ foreach ($javascripts as $javascript) {
+ if ((file_exists(ROOT . DS . $javascript['file'])) and (($javascript['load'] == 'backend') or ($javascript['load'] == 'both')) ) {
+ $backend_buffer .= file_get_contents(ROOT . DS . $javascript['file']);
+ }
+ }
+ file_put_contents($backend_site_js_path, $backend_buffer);
+ $backend_regenerate = false;
+ }
+
+
+ // Build frontend site javascript
+ foreach ($javascripts as $javascript) {
+ if ((file_exists(ROOT . DS . $javascript['file'])) and (($javascript['load'] == 'frontend') or ($javascript['load'] == 'both')) ) {
+ if ( ! file_exists($frontend_site_js_path) or filemtime(ROOT . DS . $javascript['file']) > filemtime($frontend_site_js_path)) {
+ $frontend_regenerate = true;
+ break;
+ }
+ }
+ }
+
+ // Regenerate site javascript
+ if ($frontend_regenerate) {
+ foreach ($javascripts as $javascript) {
+ if ((file_exists(ROOT . DS . $javascript['file'])) and (($javascript['load'] == 'frontend') or ($javascript['load'] == 'both')) ) {
+ $frontend_buffer .= file_get_contents(ROOT . DS . $javascript['file']);
+ }
+ }
+ file_put_contents($frontend_site_js_path, $frontend_buffer);
+ $frontend_regenerate = false;
+ }
+
+ // Render
+ if (BACKEND) {
+ echo '';
+ } else {
+ echo '';
+ }
+ }
+ }
+
+ }
+
+
+ /**
+ * Navigation class
+ */
+ class Navigation {
+
+
+ /**
+ * Items
+ *
+ * @var array
+ */
+ public static $items = array();
+
+
+ /**
+ * Navigation types
+ */
+ const LEFT = 1;
+ const TOP = 2;
+
+
+ /**
+ * Add new item
+ *
+ *
+ * // Add link for left navigation
+ * Navigation::add(__('Blog'), 'content', 'blog', 11);
+ *
+ * // Add link for top navigation
+ * Navigation::add(__('View site'), 'top', 'http://site.com/', 11, Navigation::TOP, true);
+ *
+ *
+ * @param string $name Name
+ * @param string $category Category
+ * @param stirng $link Link
+ * @param integer $priority Priority. Default is 10
+ * @param integer $type Type. Default is LEFT
+ * @param bool $external External or not. Default is false
+ */
+ public static function add($name, $category, $id, $priority = 10, $type = Navigation::LEFT, $external = false) {
+ Navigation::$items[] = array(
+ 'name' => (string)$name,
+ 'category' => (string)$category,
+ 'id' => (string)$id,
+ 'priority' => (int)$priority,
+ 'type' => (int)$type,
+ 'external' => (bool)$external,
+ );
+ }
+
+
+ /**
+ * Draw items
+ *
+ *
+ * Navigation::draw('content');
+ * Navigation::draw('top', Navigation::TOP);
+ *
+ *
+ * @param string $category Category
+ * @param integer $type Type. Default is LEFT
+ */
+ public static function draw($category, $type = Navigation::LEFT) {
+
+ // Sort items by priority
+ $items = Arr::subvalSort(Navigation::$items, 'priority');
+
+ // Draw left navigation
+ if ($type == Navigation::LEFT) {
+
+ // Loop trough the items
+ foreach ($items as $item) {
+
+ // If current plugin id == selected item id then set class to current
+ if (Request::get('id') == $item['id'] && $item['external'] == false) $class = 'class = "current" '; else $class = '';
+
+ // If current category == item category and navigation type is left them draw this item
+ if ($item['category'] == $category && $item['type'] == Navigation::LEFT) {
+
+ // Is external item id or not ?
+ if ($item['external'] == false) {
+ echo ''.$item['name'].' ';
+ } else {
+ echo ''.$item['name'].' ';
+ }
+ }
+ }
+ } elseif ($type == Navigation::TOP) {
+ // Draw top navigation
+ foreach ($items as $item) {
+ if ($item['category'] == $category && $item['type'] == Navigation::TOP) {
+ if ($item['external'] == false) {
+ echo ''.$item['name'].''.Html::nbsp(2);
+ } else {
+ echo ''.$item['name'].''.Html::nbsp(2);
+ }
+ }
+ }
+ }
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/engine/shortcodes.php b/monstra/engine/shortcodes.php
new file mode 100644
index 0000000..9222f28
--- /dev/null
+++ b/monstra/engine/shortcodes.php
@@ -0,0 +1,133 @@
+
+ * function returnSiteUrl() {
+ * return Option::get('siteurl');
+ * }
+ *
+ * // Add shortcode {siteurl}
+ * Shortcode::add('siteurl', 'returnSiteUrl');
+ *
+ *
+ * @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;
+ }
+
+
+ /**
+ * Parse a string, and replace any registered shortcodes within it with the result of the mapped callback.
+ *
+ *
+ * $content = Shortcode::parse($content);
+ *
+ *
+ * @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];
+ }
+ }
+ }
+
+ return $prefix . call_user_func(Shortcode::$shortcode_tags[$shortcode], $attributes, $matches[5], $shortcode) . $suffix;
+ }
+
+}
\ No newline at end of file
diff --git a/monstra/engine/site.php b/monstra/engine/site.php
new file mode 100644
index 0000000..f46d901
--- /dev/null
+++ b/monstra/engine/site.php
@@ -0,0 +1,257 @@
+
+ * echo Site::name();
+ *
+ *
+ * @return string
+ */
+ public static function name() {
+ return Option::get('sitename');
+ }
+
+
+ /**
+ * Get site theme
+ *
+ *
+ * echo Site::theme();
+ *
+ *
+ * @return string
+ */
+ public static function theme() {
+ return Option::get('theme_site_name');
+ }
+
+
+ /**
+ * Get Page title
+ *
+ *
+ * echo Site::title();
+ *
+ *
+ * @return string
+ */
+ public static function title() {
+
+ // Get title
+ $title = call_user_func(ucfirst(Uri::command()).'::title');
+
+ return $title;
+ }
+
+
+ /**
+ * Get page description
+ *
+ *
+ * echo Site::description();
+ *
+ *
+ * @return string
+ */
+ public static function description() {
+
+ // Get description
+ $description = call_user_func(ucfirst(Uri::command()).'::description');
+
+ if (trim($description) !== '') {
+ return Html::toText($description);
+ } else {
+ return Html::toText(Option::get('description'));
+ }
+ }
+
+
+ /**
+ * Get page keywords
+ *
+ *
+ * echo Site::keywords();
+ *
+ *
+ * @return string
+ */
+ public static function keywords() {
+
+ // Get keywords
+ $keywords = call_user_func(ucfirst(Uri::command()).'::keywords');
+
+ if (trim($keywords) !== '') {
+ return Html::toText($keywords);
+ } else {
+ return Html::toText(Option::get('keywords'));
+ }
+
+ }
+
+
+ /**
+ * Get site slogan
+ *
+ *
+ * echo Site::slogan();
+ *
+ *
+ * @return string
+ */
+ public static function slogan() {
+ return Option::get('slogan');
+ }
+
+
+ /**
+ * Get page content
+ *
+ *
+ * echo Site::content();
+ *
+ *
+ * @return string
+ */
+ public static function content() {
+
+ // Get content
+ $content = call_user_func(ucfirst(Uri::command()).'::content');
+
+ return Filter::apply('content', $content);
+ }
+
+
+ /**
+ * Get compressed template
+ *
+ *
+ * echo Site::template();
+ *
+ *
+ * @return mixed
+ */
+ public static function template() {
+
+ // Get current theme
+ $current_theme = Option::get('theme_site_name');
+
+ // 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
+ *
+ *
+ * echo Site::url();
+ *
+ *
+ * @return string
+ */
+ public static function url() {
+ return Option::get('siteurl');
+ }
+
+
+ /**
+ * Get copyright information
+ *
+ *
+ * echo Site::powered();
+ *
+ *
+ * @return string
+ */
+ public static function powered() {
+ return __('Powered by', 'system').' Monstra ' . MONSTRA_VERSION;
+ }
+
+ }
+
+
+ // Add new shortcode {siteurl}
+ Shortcode::add('siteurl', 'returnSiteUrl');
+ function returnSiteUrl() { return Option::get('siteurl'); }
\ No newline at end of file
diff --git a/monstra/engine/xmldb.php b/monstra/engine/xmldb.php
new file mode 100644
index 0000000..48c877a
--- /dev/null
+++ b/monstra/engine/xmldb.php
@@ -0,0 +1,1008 @@
+
+ * $xml_safe = XML::safe($xml_unsafe);
+ *
+ *
+ * @param string $str String
+ * @param boolean $flag Flag
+ * @return string
+ */
+ public static function safe($str, $flag = true) {
+
+ // Redefine vars
+ $str = (string) $str;
+ $flag = (bool) $flag;
+
+ // Remove invisible chars
+ $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);
+
+ // htmlspecialchars
+ if ($flag) $str = htmlspecialchars($str, ENT_QUOTES, 'utf-8');
+
+ // Return safe string
+ return $str;
+ }
+
+
+ /**
+ * Get XML file
+ *
+ *
+ * $xml_file = XML::loadFile('path/to/file.xml');
+ *
+ *
+ * @param string $file File name
+ * @param boolean $force Method
+ * @return array
+ */
+ public static function loadFile($file, $force = false) {
+
+ // Redefine vars
+ $file = (string) $file;
+ $force = (bool) $force;
+
+ // For CMS API XML file force method
+ if ($force) {
+ $xml = file_get_contents($file);
+ $data = simplexml_load_string($xml);
+ return $data;
+ } else {
+ if (file_exists($file) && is_file($file)) {
+ $xml = file_get_contents($file);
+ $data = simplexml_load_string($xml);
+ return $data;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ }
+
+
+ /**
+ * DB Class
+ */
+ class DB {
+
+
+ /**
+ * XMLDB directory
+ *
+ * @var string
+ */
+ public static $db_dir = STORAGE;
+
+
+ /**
+ * Protected constructor since this is a static class.
+ *
+ * @access protected
+ */
+ protected function __construct() {
+ // Nothing here
+ }
+
+
+ /**
+ * Configure the settings of XMLDB
+ *
+ * @param mixed $setting Setting name
+ * @param mixed $value Setting value
+ */
+ public static function configure($setting, $value){
+ if (property_exists("db", $setting)) DB::$$setting = $value;
+ }
+
+
+ /**
+ * Create new database
+ *
+ * @param string $db_name Database name
+ * @param integer $mode Mode
+ * @return boolean
+ */
+ public static function create($db_name, $chmod = 0775) {
+
+ // Redefine vars
+ $db_name = (string) $db_name;
+
+ // Create
+ if (is_dir(DB::$db_dir . '/' . $db_name)) return false;
+ return mkdir(DB::$db_dir . '/' . $db_name, $chmod);
+ }
+
+
+ /**
+ * Drop database
+ *
+ * @param string $db_name Database name
+ * @return boolean
+ */
+ public static function drop($db_name) {
+
+ // Redefine vars
+ $db_name = (string) $db_name;
+
+ // Drop
+ if (is_dir(DB::$db_dir . '/' . $db_name)){$ob=scandir(DB::$db_dir . '/' . $db_name); foreach ($ob as $o){if($o!='.'&&$o!='..'){if(filetype(DB::$db_dir . '/' . $db_name.'/'.$o)=='dir')DB::drop(DB::$db_dir . '/' . $db_name.'/'.$o); else unlink(DB::$db_dir . '/' . $db_name.'/'.$o);}}}
+ reset($ob); rmdir(DB::$db_dir . '/' . $db_name);
+ }
+
+ }
+
+
+ /**
+ * Table class
+ */
+ class Table {
+
+
+ /**
+ * XMLDB Tables directory
+ *
+ * @var string
+ */
+ public static $tables_dir = XMLDB;
+
+
+ /**
+ * Table
+ *
+ * @var object
+ */
+ private $table;
+
+
+ /**
+ * Table name
+ *
+ * @var string
+ */
+ private $name;
+
+
+ /**
+ * Configure the settings of XMLDB Tables
+ *
+ * @param mixed $setting Setting name
+ * @param mixed $value Setting value
+ */
+ public static function configure($setting, $value){
+ if (property_exists("table", $setting)) Table::$$setting = $value;
+ }
+
+
+ /**
+ * Table construct
+ *
+ *
+ * $users = new Table('table_name');
+ *
+ *
+ * @param string $table_name Table name
+ */
+ function __construct($table_name) {
+
+ // Redefine vars
+ $table_name = (string) $table_name;
+
+ $this->table = Table::get($table_name);
+ $this->name = $table_name;
+ }
+
+
+ /**
+ * Create new table
+ *
+ * XMLDB Table structure:
+ *
+ *
+ *
+ * 0
+ *
+ *
+ *
+ *
+ *
+ * value
+ * value
+ *
+ *
+ *
+ *
+ * Table::create('table_name', array('field1', 'field2'));
+ *
+ *
+ * @param string $table_name Table name
+ * @param array $fields Fields
+ * @return boolean
+ */
+ public static function create($table_name, $fields) {
+
+ // Redefine vars
+ $table_name = (string) $table_name;
+
+ if ( ! file_exists(Table::$tables_dir . '/' . $table_name . '.table.xml') &&
+ is_dir(dirname(Table::$tables_dir)) &&
+ is_writable(dirname(Table::$tables_dir)) &&
+ isset($fields) &&
+ is_array($fields)) {
+
+ // Create table fields
+ $_fields = '';
+ foreach ($fields as $field) $_fields .= "<$field/>";
+ $_fields .= ' ';
+
+ // Create new table
+ return file_put_contents(Table::$tables_dir . '/' . $table_name . '.table.xml','0 '.$_fields.' ', LOCK_EX);
+
+ } else {
+
+ // Something wrong... return false
+ return false;
+ }
+ }
+
+
+ /**
+ * Delete table
+ *
+ *
+ * Table::drop('table_name');
+ *
+ *
+ * @param string $table_name Table name
+ * @return boolean
+ */
+ public static function drop($table_name) {
+
+ // Redefine vars
+ $table_name = (string) $table_name;
+
+ // Drop
+ if ( ! is_dir(Table::$tables_dir . '/' . $table_name . '.table.xml')) {
+ return unlink(Table::$tables_dir . '/' . $table_name . '.table.xml');
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Get table
+ *
+ *
+ * $table = Table::get('table_name');
+ *
+ *
+ * @param array $table_name Table name
+ * @return mixed
+ */
+ public static function get($table_name) {
+
+ // Redefine vars
+ $table_name = (string) $table_name;
+
+ // Load table
+ if (file_exists(Table::$tables_dir . '/' . $table_name.'.table.xml') && is_file(Table::$tables_dir . '/' . $table_name.'.table.xml')) {
+ $data = array('xml_object' => XML::loadFile(Table::$tables_dir . '/' . $table_name.'.table.xml'),
+ 'xml_filename' => Table::$tables_dir . '/' . $table_name.'.table.xml');
+ return $data;
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * Get information about table
+ *
+ *
+ * var_dump($users->info());
+ *
+ *
+ * @return array
+ */
+ public function info() {
+ return array(
+ 'table_name' => basename($this->table['xml_filename'], '.table.xml'),
+ 'table_size' => filesize($this->table['xml_filename']),
+ 'table_last_change' => filemtime($this->table['xml_filename']),
+ 'table_last_access' => fileatime($this->table['xml_filename']),
+ 'table_fields' => $this->fields(),
+ 'records_count' => $this->count(),
+ 'records_last_id' => $this->lastId()
+ );
+ }
+
+
+ /**
+ * Get table fields
+ *
+ *
+ * var_dump($users->fields());
+ *
+ *
+ * @return array
+ */
+ public function fields() {
+
+ // Select fields
+ $fields_obj = Table::_selectOne($this->table, "fields");
+
+ // Create fields array
+ foreach ($fields_obj as $key => $field) {
+ $fields[] = $key;
+ }
+
+ // Return array of fields
+ return $fields;
+ }
+
+
+ /**
+ * Add new field
+ *
+ *
+ * $users->addField('test');
+ *
+ *
+ * @param string $name Field name
+ * @return boolean
+ */
+ public function addField($name) {
+
+ // Redefine vars
+ $name = (string) $name;
+
+ // Get table
+ $table = $this->table;
+
+ // Select all fields
+ $fields = Table::_selectOne($this->table, "fields");
+
+ // Select current field
+ $field = Table::_selectOne($this->table, "fields/{$name}");
+
+ // If field dosnt exists than create new field
+ if ($field == null) {
+
+ // Create new field
+ $fields->addChild($name, '');
+
+ // Save table
+ return Table::_save($table);
+
+ } else {
+
+ return false;
+
+ }
+
+ }
+
+
+ /**
+ * Delete field
+ *
+ *
+ * $users->deleteField('test');
+ *
+ *
+ * @param string $name Field name
+ * @return boolean
+ */
+ public function deleteField($name) {
+
+ // Redefine vars
+ $name = (string) $name;
+
+ // Get table
+ $table = $this->table;
+
+ // Select field
+ $field = Table::_selectOne($this->table, "fields/{$name}");
+
+ // If field exist than delete it
+ if ($field != null) {
+
+ // Delete field
+ unset($field[0]);
+
+ // Save table
+ return Table::_save($table);
+
+ } else {
+
+ return false;
+
+ }
+ }
+
+
+ /**
+ * Update field
+ *
+ *
+ * $users->updateField('login', 'username');
+ *
+ *
+ * @param string $old_name Old field name
+ * @param string $new_name New field name
+ * @return boolean
+ */
+ public function updateField($old_name, $new_name) {
+ if (file_exists(Table::$tables_dir . '/' . $this->name.'.table.xml') && is_file(Table::$tables_dir . '/' . $this->name.'.table.xml')) {
+ $table = strtr(file_get_contents(Table::$tables_dir . '/' . $this->name.'.table.xml'), array('<'.$old_name.'>' => '<'.$new_name.'>',
+ ''.$old_name.'>' => ''.$new_name.'>',
+ '<'.$old_name.'/>' => '<'.$new_name.'/>'));
+ if (file_put_contents(Table::$tables_dir . '/' . $this->name.'.table.xml', $table)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+
+ /**
+ * Add new record
+ *
+ *
+ * $users->insert(array('login'=>'admin', 'password'=>'pass'));
+ *
+ *
+ * @param array $fields Record fields to insert
+ * @return boolean
+ */
+ public function insert(array $fields = null) {
+
+ // Set save flag to true
+ $save = true;
+
+ // Foreach fields check is current field alredy exists
+ if (count($fields) !== 0) {
+ foreach ($fields as $key => $value) {
+ if (Table::_selectOne($this->table, "fields/{$key}") == null) {
+ $save = false;
+ break;
+ }
+ }
+ }
+
+ // Get table fields and create fields names array
+ $_fields = Table::_selectOne($this->table, "fields");
+ foreach ($_fields as $key => $value) {
+ $field_names[(string)$key] = (string)$key;
+ }
+
+ // Save record
+ if ($save) {
+
+ // Find autoincrement option
+ $inc = Table::_selectOne($this->table, "options/autoincrement");
+
+ // Increment
+ $inc_upd = $inc + 1;
+
+ // Add record
+ $node = $this->table['xml_object']->addChild(XML::safe($this->name));
+
+ // Update autoincrement
+ Table::_updateWhere($this->table, "options", array('autoincrement' => $inc_upd));
+
+ // Add common record fields: id and uid
+ $node->addChild('id', $inc_upd);
+ $node->addChild('uid', Table::_generateUID());
+
+ // If exists fields to insert then insert them
+ if (count($fields) !== 0) {
+
+ $table_fields = array_diff_key($field_names, $fields);
+
+ // Defined fields
+ foreach ($table_fields as $table_field) {
+ $node->addChild($table_field, '');
+ }
+
+ // User fields
+ foreach ($fields as $key => $value) {
+ $node->addChild($key, XML::safe($value));
+ }
+ }
+
+ // Save table
+ return Table::_save($this->table);
+
+ } else {
+
+ return false;
+
+ }
+ }
+
+
+ /**
+ * Select record(s) in table
+ *
+ *
+ * $records = $users->select('[id=2]');
+ * $records = $users->select(null, 'all');
+ * $records = $users->select(null, 'all', null, array('login'));
+ * $records = $users->select(null, 2, 1);
+ *
+ *
+ * @param string $query XPath query
+ * @param integer $row_count Row count. To select all records write 'all'
+ * @param integer $offset Offset
+ * @param array $fields Fields
+ * @param string $order_by Order by
+ * @param string $order Order type
+ * @return array
+ */
+ public function select($query = null, $row_count = 'all', $offset = null, array $fields = null, $order_by = 'id', $order = 'ASC') {
+
+ // Redefine vars
+ $query = ($query === null) ? null : (string) $query;
+ $offset = ($offset === null) ? null : (int) $offset;
+ $order_by = (string) $order_by;
+ $order = (string) $order;
+
+ // Execute query
+ if ($query !== null) {
+ $tmp = $this->table['xml_object']->xpath('//'.$this->name.$query);
+ } else {
+ $tmp = $this->table['xml_object']->xpath($this->name);
+ }
+
+ // Init vars
+ $data = array();
+ $records = array();
+ $_records = array();
+
+ $one_record = false;
+
+ // If row count is null then select only one record
+ // eg: $users->select('[login="admin"]', null);
+ if ($row_count == null) {
+
+ if (isset($tmp[0])) {
+ $_records = $tmp[0];
+ $one_record = true;
+ }
+
+ } else {
+
+ // If row count is 'all' then select all records
+ // eg:
+ // $users->select('[status="active"]', 'all');
+ // or
+ // $users->select('[status="active"]');
+ if ($row_count == 'all') {
+
+ foreach ($tmp as $record) {
+ $data[] = $record;
+ }
+
+ $_records = $data;
+
+ } else {
+
+ // Else select records like
+ // eg: $users->select(null, 2, 1);
+
+ foreach($tmp as $record) {
+ $data[] = $record;
+ }
+
+ // If offset is null slice array from end else from begin
+ if ($offset === null) {
+ $_records = array_slice($data, -$row_count, $row_count);
+ } else {
+ $_records = array_slice($data, $offset, $row_count);
+ }
+
+ }
+ }
+
+ // If array of fields is exits then get records with this fields only
+ if (count($fields) > 0) {
+
+ if (count($_records) > 0) {
+
+ $count = 0;
+ foreach ($_records as $key => $record) {
+
+ foreach ($fields as $field) {
+ $record_array[$count][$field] = (string)$record->$field;
+ }
+
+ //$record_array[$count]['id'] = (int)$record['id'];
+ $record_array[$count]['id'] = (int)$record->id;
+
+ if ($order_by == 'id') {
+ $record_array[$count]['sort'] = (int)$record->$order_by;
+ } else {
+ $record_array[$count]['sort'] = (string)$record->$order_by;
+ }
+
+ $count++;
+
+ }
+ $records = Table::subvalSort($record_array, 'sort', $order);
+ }
+
+ } else {
+
+ // Convert from XML object to array
+
+ if ( ! $one_record) {
+ $count = 0;
+ foreach ($_records as $xml_objects) {
+ $vars = get_object_vars($xml_objects);
+ foreach ($vars as $key => $value) {
+ $records[$count][$key] = (string)$value;
+ }
+ $count++;
+ }
+ } else {
+ $vars = get_object_vars($_records[0]);
+ foreach ($vars as $key => $value) {
+ $records[$key] = (string)$value;
+ }
+ }
+
+ }
+
+ // Return records
+ return $records;
+
+ }
+
+
+ /**
+ * Delete current record in table
+ *
+ *
+ * $users->delete(2);
+ *
+ *
+ * @param integer $id Record ID
+ * @return boolean
+ */
+ public function delete($id) {
+
+ // Redefine vars
+ $id = (int) $id;
+
+ // Find record to delete
+ $xml_arr = Table::_selectOne($this->table, "//".$this->name."[id='".$id."']");
+
+ // If its exists then delete it
+ if (count($xml_arr) !== 0) {
+
+ // Delete
+ unset($xml_arr[0]);
+
+ }
+
+ // Save table
+ return Table::_save($this->table);
+ }
+
+
+ /**
+ * Delete with xPath query record in xml file
+ *
+ *
+ * $users->deleteWhere('[id=2]');
+ *
+ *
+ * @param string $query xPath query
+ * @return boolean
+ */
+ public function deleteWhere($query) {
+
+ // Redefine vars
+ $query = (string) $query;
+
+ // Find record to delete
+ $xml_arr = Table::_selectOne($this->table, '//'.$this->name.$query);
+
+ // If its exists then delete it
+ if (count($xml_arr) !== 0) {
+
+ // Delete
+ unset($xml_arr[0]);
+
+ }
+
+ // Save table
+ return Table::_save($this->table);
+ }
+
+
+ /**
+ * Update record with xPath query in XML file
+ *
+ *
+ * $users->updateWhere('[id=2]', array('login'=>'Admin', 'password'=>'new pass'));
+ *
+ *
+ * @param string $query XPath query
+ * @param array $fields Record fields to udpate
+ * @return boolean
+ */
+ public function updateWhere($query, array $fields = null) {
+
+ // Redefine vars
+ $query = (string) $query;
+
+ // Set save flag to true
+ $save = true;
+
+ // Foreach fields check is current field alredy exists
+ if (count($fields) !== 0) {
+ foreach ($fields as $key => $value) {
+ if (Table::_selectOne($this->table, "fields/{$key}") == null) {
+ $save = false;
+ break;
+ }
+ }
+ }
+
+ // Get table fields and create fields names array
+ $_fields = Table::_selectOne($this->table, "fields");
+ foreach ($_fields as $key => $value) {
+ $field_names[(string)$key] = (string)$key;
+ }
+
+ // Save record
+ if ($save) {
+
+ // Find record
+ $xml_arr = Table::_selectOne($this->table, '//'.$this->name.$query);
+
+ // If its exists then delete it
+ if (count($fields) !== 0) {
+ foreach ($fields as $key => $value) {
+ // Else: Strict Mode Error
+ // Creating default object from empty value
+ @$xml_arr->$key = XML::safe($value, false);
+ }
+ }
+
+ // Save table
+ return Table::_save($this->table);
+
+ } else {
+
+ return false;
+
+ }
+ }
+
+
+ /**
+ * Update current record in table
+ *
+ *
+ * $users->update(1, array('login'=>'Admin','password'=>'new pass'));
+ *
+ *
+ * @param integer $id Record ID
+ * @param array $fields Record fields to udpate
+ * @return boolean
+ */
+ public function update($id, array $fields = null) {
+
+ // Redefine vars
+ $id = (int) $id;
+
+ // Set save flag to true
+ $save = true;
+
+ // Foreach fields check is current field alredy exists
+ if (count($fields) !== 0) {
+ foreach ($fields as $key => $value) {
+ if (Table::_selectOne($this->table, "fields/{$key}") == null) {
+ $save = false;
+ break;
+ }
+ }
+ }
+
+ // Get table fields and create fields names array
+ $_fields = Table::_selectOne($this->table, "fields");
+ foreach ($_fields as $key => $value) {
+ $field_names[(string)$key] = (string)$key;
+ }
+
+ // Save record
+ if ($save) {
+
+ // Find record to delete
+ $xml_arr = Table::_selectOne($this->table, "//".$this->name."[id='".(int)$id."']");
+
+ // If its exists then update it
+ if (count($fields) !== 0) {
+ foreach ($fields as $key => $value) {
+
+ // Delete current
+ unset($xml_arr->$key);
+
+ // And add new one
+ $xml_arr->addChild($key, XML::safe($value, false));
+
+ }
+ }
+
+ // Save table
+ return Table::_save($this->table);
+
+ } else {
+
+ return false;
+
+ }
+ }
+
+
+ /**
+ * Get last record id
+ *
+ *
+ * echo $users->lastId();
+ *
+ *
+ * @return integer
+ */
+ public function lastId() {
+ $data = $this->table['xml_object']->xpath("//root/node()[last()]");
+ return (int)$data[0]->id;
+ }
+
+
+ /**
+ * Get count of records
+ *
+ *
+ * echo $users->count();
+ *
+ *
+ * @return integer
+ */
+ public function count() {
+ return count($this->table['xml_object'])-2;
+ }
+
+
+
+ /**
+ * Subval sort
+ *
+ * @param array $a Array
+ * @param string $subkey Key
+ * @param string $order Order type DESC or ASC
+ * @return array
+ */
+ protected static function subvalSort($a, $subkey, $order = null) {
+ if (count($a) != 0 || (!empty($a))) {
+ foreach ($a as $k=>$v) $b[$k] = function_exists('mb_strtolower') ? mb_strtolower($v[$subkey]) : strtolower($v[$subkey]);
+ if ($order==null || $order== 'ASC') asort($b); else if ($order == 'DESC') arsort($b);
+ foreach ($b as $key=>$val) $c[] = $a[$key];
+ return $c;
+ }
+ }
+
+
+ /**
+ * _selectOne
+ */
+ protected static function _selectOne($table, $query) {
+ $tmp = $table['xml_object']->xpath($query);
+ return isset($tmp[0])? $tmp[0]: null;
+ }
+
+
+ /**
+ * _updateWhere
+ */
+ protected static function _updateWhere($table, $query, $fields = array()) {
+
+ // Find record to delete
+ $xml_arr = Table::_selectOne($table, $query);
+
+ // If its exists then delete it
+ if (count($fields) !== 0) {
+ foreach ($fields as $key => $value) {
+ $xml_arr->$key = XML::safe($value, false);
+ }
+ }
+
+ // Save table
+ Table::_save($table);
+ }
+
+
+ /**
+ * _generateUID
+ */
+ protected static function _generateUID() {
+ return substr(md5(uniqid(rand(), true)), 0, 10);
+ }
+
+
+ /**
+ * Format XML and save
+ *
+ * @param array $table Array of database name and XML object
+ */
+ protected static function _save($table) {
+ $dom = new DOMDocument('1.0');
+ $dom->preserveWhiteSpace = false;
+
+ // Save new xml data to xml file only if loadXML successful.
+ // Preventing the destruction of the database by unsafe data.
+ // note: If loadXML !successful then _save() add&save empty record.
+ // This record cant be removed by delete[Where]() Problem solved by hand removing...
+ // Possible solution: modify delete[Where]() or prevent add&saving of such records.
+ // the result now: database cant be destroyed :)
+ if ($dom->loadXML($table['xml_object']->asXML())) {
+ $dom->save($table['xml_filename']);
+ return true;
+ } else {
+ return false;
+ // report about errors...
+ }
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/agent.php b/monstra/helpers/agent.php
new file mode 100644
index 0000000..f9ee9cc
--- /dev/null
+++ b/monstra/helpers/agent.php
@@ -0,0 +1,171 @@
+
+ * if (Agent::isMobile()) {
+ * // Do something...
+ * }
+ *
+ *
+ * @return boolean
+ */
+ public static function isMobile() {
+ return Agent::find(Agent::$mobiles);
+ }
+
+
+ /**
+ * Returns true if the user agent that made the request is identified as a robot/crawler.
+ *
+ *
+ * if (Agent::isRobot()) {
+ * // Do something...
+ * }
+ *
+ *
+ * @return boolean
+ */
+ public static function isRobot() {
+ return Agent::find(Agent::$robots);
+ }
+
+
+ /**
+ * Returns TRUE if the string you're looking for exists in the user agent string and FALSE if not.
+ *
+ *
+ * if (Agent::is('iphone')) {
+ * // Do something...
+ * }
+ *
+ * if (Agent::is(array('iphone', 'ipod'))) {
+ * // Do something...
+ * }
+ *
+ *
+ * @param mixed $device String or array of strings you're looking for
+ * @return boolean
+ */
+ public static function is($device) {
+ return Agent::find((array) $device);
+ }
+
+}
\ No newline at end of file
diff --git a/monstra/helpers/alert.php b/monstra/helpers/alert.php
new file mode 100644
index 0000000..316ad6d
--- /dev/null
+++ b/monstra/helpers/alert.php
@@ -0,0 +1,97 @@
+
+ * Alert::success('Message here...');
+ *
+ *
+ * @param string $message Message
+ * @param integer $time Time
+ */
+ public static function success($message, $time = 3000) {
+
+ // Redefine vars
+ $message = (string) $message;
+ $time = (int) $time;
+
+ echo ''.$message.'
+ ';
+ }
+
+
+ /**
+ * Show warning message
+ *
+ *
+ * Alert::warning('Message here...');
+ *
+ *
+ * @param string $message Message
+ * @param integer $time Time
+ */
+ public static function warning($message, $time = 3000) {
+
+ // Redefine vars
+ $message = (string) $message;
+ $time = (int) $time;
+
+ echo ''.$message.'
+ ';
+ }
+
+
+ /**
+ * Show error message
+ *
+ *
+ * Alert::error('Message here...');
+ *
+ *
+ * @param string $message Message
+ * @param integer $time Time
+ */
+ public static function error($message, $time = 3000) {
+
+ // Redefine vars
+ $message = (string) $message;
+ $time = (int) $time;
+
+ echo ''.$message.'
+ ';
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/arr.php b/monstra/helpers/arr.php
new file mode 100644
index 0000000..456d000
--- /dev/null
+++ b/monstra/helpers/arr.php
@@ -0,0 +1,193 @@
+
+ * $new_array = Arr::subvalSort($old_array, 'sort');
+ *
+ *
+ * @param array $a Array
+ * @param string $subkey Key
+ * @param string $order Order type DESC or ASC
+ * @return array
+ */
+ public static function subvalSort($a, $subkey, $order = null) {
+ if (count($a) != 0 || (!empty($a))) {
+ foreach ($a as $k => $v) $b[$k] = function_exists('mb_strtolower') ? mb_strtolower($v[$subkey]) : strtolower($v[$subkey]);
+ if ($order == null || $order == 'ASC') asort($b); else if ($order == 'DESC') arsort($b);
+ foreach ($b as $key => $val) $c[] = $a[$key];
+ return $c;
+ }
+ }
+
+
+ /**
+ * Returns value from array using "dot notation".
+ * If the key does not exist in the array, the default value will be returned instead.
+ *
+ *
+ * $login = Arr::get($_POST, 'login');
+ *
+ * $array = array('foo' => 'bar');
+ * $foo = Arr::get($array, 'foo');
+ *
+ * $array = array('test' => array('foo' => 'bar'));
+ * $foo = Arr::get($array, 'test.foo');
+ *
+ *
+ * @param array $array Array to extract from
+ * @param string $path Array path
+ * @param mixed $default Default value
+ * @return mixed
+ */
+ public static function get($array, $path, $default = null) {
+
+ // Get segments from path
+ $segments = explode('.', $path);
+
+ // Loop through segments
+ foreach($segments as $segment) {
+
+ // Check
+ if ( ! is_array($array) || !isset($array[$segment])) {
+ return $default;
+ }
+
+ // Write
+ $array = $array[$segment];
+ }
+
+ // Return
+ return $array;
+ }
+
+
+ /**
+ * Deletes an array value using "dot notation".
+ *
+ *
+ * Arr::delete($array, 'foo.bar');
+ *
+ *
+ * @access public
+ * @param array $array Array you want to modify
+ * @param string $path Array path
+ * @return boolean
+ */
+ public static function delete(&$array, $path) {
+
+ // Get segments from path
+ $segments = explode('.', $path);
+
+ // Loop through segments
+ while(count($segments) > 1) {
+
+ $segment = array_shift($segments);
+
+ if ( ! isset($array[$segment]) || !is_array($array[$segment])) {
+ return false;
+ }
+
+ $array =& $array[$segment];
+ }
+
+ unset($array[array_shift($segments)]);
+
+ return true;
+ }
+
+
+ /**
+ * Checks if the given dot-notated key exists in the array.
+ *
+ *
+ * if (Arr::keyExists($array, 'foo.bar')) {
+ * // Do something...
+ * }
+ *
+ *
+ * @param array $array The search array
+ * @param mixed $path Array path
+ * @return boolean
+ */
+ public static function keyExists($array, $path) {
+
+ foreach (explode('.', $path) as $segment) {
+
+ if ( ! is_array($array) or ! array_key_exists($segment, $array)) {
+ return false;
+ }
+
+ $array = $array[$segment];
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Returns a random value from an array.
+ *
+ *
+ * Arr::random(array('php', 'js', 'css', 'html'));
+ *
+ *
+ * @access public
+ * @param array $array Array path
+ * @return mixed
+ */
+ public static function random($array) {
+ return $array[array_rand($array)];
+ }
+
+
+ /**
+ * Returns TRUE if the array is associative and FALSE if not.
+ *
+ *
+ * if (Arr::isAssoc($array)) {
+ * // Do something...
+ * }
+ *
+ *
+ * @param array $array Array to check
+ * @return boolean
+ */
+ public static function isAssoc($array) {
+ return (bool) count(array_filter(array_keys($array), 'is_string'));
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/cache.php b/monstra/helpers/cache.php
new file mode 100644
index 0000000..34022e0
--- /dev/null
+++ b/monstra/helpers/cache.php
@@ -0,0 +1,225 @@
+
+ * Cache::configure('cache_dir', 'path/to/cache/dir');
+ *
+ *
+ * @param mixed $setting Setting name
+ * @param mixed $value Setting value
+ */
+ public static function configure($setting, $value){
+ if (property_exists("cache", $setting)) Cache::$$setting = $value;
+ }
+
+
+ /**
+ * Get data from cache
+ *
+ *
+ * $profile = Cache::get('profiles', 'profile');
+ *
+ *
+ * @param string $namespace Namespace
+ * @param string $key Cache key
+ * @return boolean
+ */
+ public static function get($namespace, $key){
+
+ // Redefine vars
+ $namespace = (string) $namespace;
+
+ // Get cache file id
+ $cache_file_id = Cache::getCacheFileID($namespace, $key);
+
+ // Is cache file exists ?
+ if (file_exists($cache_file_id)) {
+
+ // If cache file has not expired then fetch it
+ if ((time() - filemtime($cache_file_id)) < Cache::$cache_time) {
+
+ $handle = fopen($cache_file_id, 'r');
+
+ $cache = '';
+
+ while ( ! feof($handle)) {
+ $cache .= fgets($handle);
+ }
+
+ fclose($handle);
+
+ return unserialize($cache);
+
+ } else {
+ unlink($cache_file_id);
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * Create new cache file $key in namescapce $namespace with the given data $data
+ *
+ *
+ * $profile = array('login' => 'Awilum',
+ * 'email' => 'awilum@msn.com');
+ * Cache::put('profiles', 'profile', $profile);
+ *
+ *
+ * @param string $namespace Namespace
+ * @param string $key Cache key
+ * @param mixed $data The variable to store
+ * @return boolean
+ */
+ public static function put($namespace, $key, $data) {
+
+ // Redefine vars
+ $namespace = (string) $namespace;
+
+ // Is CACHE directory writable ?
+ if (file_exists(CACHE) === false || is_readable(CACHE) === false || is_writable(CACHE) === false) {
+ throw new RuntimeException(vsprintf("%s(): Cache directory ('%s') is not writable.", array(__METHOD__, CACHE)));
+ }
+
+ // Create namespace
+ if ( ! file_exists(Cache::getNamespaceID($namespace))) {
+ mkdir(Cache::getNamespaceID($namespace), 0775, true);
+ }
+
+ // Write cache to specific namespace
+ return file_put_contents(Cache::getCacheFileID($namespace, $key), serialize($data), LOCK_EX);
+ }
+
+
+ /**
+ * Deletes a cache in specific namespace
+ *
+ *
+ * Cache::delete('profiles', 'profile');
+ *
+ *
+ * @param string $namespace Namespace
+ * @param string $key Cache key
+ * @return boolean
+ */
+ public static function delete($namespace, $key) {
+
+ // Redefine vars
+ $namespace = (string) $namespace;
+
+ if (file_exists(Cache::getCacheFileID($namespace, $key))) unlink(Cache::getCacheFileID($namespace, $key)); else return false;
+ }
+
+
+ /**
+ * Clean specific cache namespace.
+ *
+ *
+ * Cache::clean('profiles');
+ *
+ *
+ * @param string $namespace Namespace
+ * @return null
+ */
+ public static function clean($namespace) {
+
+ // Redefine vars
+ $namespace = (string) $namespace;
+
+ array_map("unlink", glob(Cache::$cache_dir . DS . md5($namespace) . DS . "*." . Cache::$cache_file_ext));
+ }
+
+
+ /**
+ * Get cache file ID
+ *
+ * @param string $namespace Namespace
+ * @param string $key Cache key
+ * @return string
+ */
+ protected static function getCacheFileID($namespace, $key) {
+
+ // Redefine vars
+ $namespace = (string) $namespace;
+
+ return Cache::$cache_dir . DS . md5($namespace) . DS . md5($key) . '.' . Cache::$cache_file_ext;
+ }
+
+
+ /**
+ * Get namespace ID
+ *
+ * @param string $namespace Namespace
+ * @return string
+ */
+ protected static function getNamespaceID($namespace) {
+
+ // Redefine vars
+ $namespace = (string) $namespace;
+
+ return Cache::$cache_dir . DS . md5($namespace);
+ }
+
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/captcha.php b/monstra/helpers/captcha.php
new file mode 100644
index 0000000..903cf5d
--- /dev/null
+++ b/monstra/helpers/captcha.php
@@ -0,0 +1,84 @@
+
+ *
+ *
+ *
+ * @return string
+ */
+ public static function getMathQuestion() {
+
+ if ( ! isset($_SESSION["math_question_v1"]) && ! isset($_SESSION["math_question_v2"])) {
+ $v1 = rand(1,9);
+ $v2 = rand(1,9);
+ $_SESSION["math_question_v1"] = $v1;
+ $_SESSION["math_question_v2"] = $v2;
+ } else {
+ $v1 = $_SESSION["math_question_v1"];
+ $v2 = $_SESSION["math_question_v2"];
+ }
+
+ return sprintf("%s + %s = ", $v1, $v2);
+ }
+
+
+ /**
+ * Checks the given answer if it matches the addition of the saved session variables.
+ *
+ *
+ * if (isset($_POST['submit'])) {
+ * if (Captcha::correctAnswer($_POST['answer'])) {
+ * // Do something...
+ * }
+ * }
+ *
+ *
+ * @param integer $answer User answer
+ */
+ public static function correctAnswer($answer){
+
+ $v1 = $_SESSION["math_question_v1"];
+ $v2 = $_SESSION["math_question_v2"];
+
+ unset($_SESSION['math_question_v1']);
+ unset($_SESSION['math_question_v2']);
+
+ if (($v1 + $v2) == $answer) {
+ return true;
+ }
+
+ return false;
+
+ }
+
+ }
diff --git a/monstra/helpers/cookie.php b/monstra/helpers/cookie.php
new file mode 100644
index 0000000..29289f8
--- /dev/null
+++ b/monstra/helpers/cookie.php
@@ -0,0 +1,113 @@
+
+ * Cookie::set('limit', 10);
+ *
+ *
+ * @param string $key A name for the cookie.
+ * @param mixed $value The value to be stored. Keep in mind that they will be serialized.
+ * @param integer $expire The number of seconds that this cookie will be available.
+ * @param string $path The path on the server in which the cookie will be availabe. Use / for the entire domain, /foo if you just want it to be available in /foo.
+ * @param string $domain The domain that the cookie is available on. Use .example.com to make it available on all subdomains of example.com.
+ * @param boolean $secure Should the cookie be transmitted over a HTTPS-connection? If true, make sure you use a secure connection, otherwise the cookie won't be set.
+ * @param boolean $httpOnly Should the cookie only be available through HTTP-protocol? If true, the cookie can't be accessed by Javascript, ...
+ * @return boolean
+ */
+ public static function set($key, $value, $expire = 86400, $domain = '', $path = '/', $secure = false, $httpOnly = false) {
+
+ // Redefine vars
+ $key = (string) $key;
+ $value = serialize($value);
+ $expire = time() + (int) $expire;
+ $path = (string) $path;
+ $domain = (string) $domain;
+ $secure = (bool) $secure;
+ $httpOnly = (bool) $httpOnly;
+
+ // Set cookie
+ return setcookie($key, $value, $expire, $path, $domain, $secure, $httpOnly);
+ }
+
+
+ /**
+ * Get a cookie
+ *
+ *
+ * $limit = Cookie::get('limit');
+ *
+ *
+ * @param string $key The name of the cookie that should be retrieved.
+ * @return mixed
+ */
+ public static function get($key) {
+
+ // Redefine key
+ $key = (string) $key;
+
+ // Cookie doesn't exist
+ if( ! isset($_COOKIE[$key])) return false;
+
+ // Fetch base value
+ $value = (get_magic_quotes_gpc()) ? stripslashes($_COOKIE[$key]) : $_COOKIE[$key];
+
+ // Unserialize
+ $actual_value = @unserialize($value);
+
+ // If unserialize failed
+ if($actual_value === false && serialize(false) != $value) return false;
+
+ // Everything is fine
+ return $actual_value;
+
+ }
+
+
+ /**
+ * Delete a cookie
+ *
+ *
+ * Cookie::delete('limit');
+ *
+ *
+ * @param string $name The name of the cookie that should be deleted.
+ */
+ public static function delete($key) {
+ unset($_COOKIE[$key]);
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/curl.php b/monstra/helpers/curl.php
new file mode 100644
index 0000000..015167d
--- /dev/null
+++ b/monstra/helpers/curl.php
@@ -0,0 +1,153 @@
+ 'Mozilla/5.0 (compatible; Monstra CMS; +http://monstra.org)',
+ CURLOPT_RETURNTRANSFER => true
+ );
+
+
+ /**
+ * Information about the last transfer.
+ *
+ * @var array
+ */
+ protected static $info;
+
+
+ /**
+ * Performs a curl GET request.
+ *
+ *
+ * $res = Curl::get('http://site.com/');
+ *
+ *
+ * @param string $url The URL to fetch
+ * @param array $options An array specifying which options to set and their values
+ * @return string
+ */
+ public static function get($url, array $options = null) {
+
+ // Redefine vars
+ $url = (string) $url;
+
+ // Check if curl is available
+ if ( ! function_exists('curl_init')) throw new RuntimeException(vsprintf("%s(): This method requires cURL (http://php.net/curl), it seems like the extension isn't installed.", array(__METHOD__)));
+
+ // Initialize a cURL session
+ $handle = curl_init($url);
+
+ // Merge options
+ $options = (array) $options + Curl::$default_options;
+
+ // Set multiple options for a cURL transfer
+ curl_setopt_array($handle, $options);
+
+ // Perform a cURL session
+ $response = curl_exec($handle);
+
+ // Set information regarding a specific transfer
+ Curl::$info = curl_getinfo($handle);
+
+ // Close a cURL session
+ curl_close($handle);
+
+ // Return response
+ return $response;
+ }
+
+
+ /**
+ * Performs a curl POST request.
+ *
+ *
+ * $res = Curl::post('http://site.com/login');
+ *
+ *
+ * @param string $url The URL to fetch
+ * @param array $data An array with the field name as key and field data as value
+ * @param boolean $multipart True to send data as multipart/form-data and false to send as application/x-www-form-urlencoded
+ * @param array $options An array specifying which options to set and their values
+ * @return string
+ */
+ public static function post($url, array $data = null, $multipart = false, array $options = null) {
+
+ // Redefine vars
+ $url = (string) $url;
+
+ // Check if curl is available
+ if ( ! function_exists('curl_init')) throw new RuntimeException(vsprintf("%s(): This method requires cURL (http://php.net/curl), it seems like the extension isn't installed.", array(__METHOD__)));
+
+ // Initialize a cURL session
+ $handle = curl_init($url);
+
+ // Merge options
+ $options = (array) $options + Curl::$default_options;
+
+ // Add options
+ $options[CURLOPT_POST] = true;
+ $options[CURLOPT_POSTFIELDS] = ($multipart === true) ? (array) $data : http_build_query((array) $data);
+
+ // Set multiple options for a cURL transfer
+ curl_setopt_array($handle, $options);
+
+ // Perform a cURL session
+ $response = curl_exec($handle);
+
+ // Set information regarding a specific transfer
+ Curl::$info = curl_getinfo($handle);
+
+ // Close a cURL session
+ curl_close($handle);
+
+ // Return response
+ return $response;
+ }
+
+
+ /**
+ * Gets information about the last transfer.
+ *
+ *
+ * $res = Curl::getInfo();
+ *
+ *
+ * @param string $value Array key of the array returned by curl_getinfo()
+ * @return mixed
+ */
+ public static function getInfo($value = null) {
+
+ if (empty(Curl::$info)) {
+ return false;
+ }
+
+ return ($value === null) ? Curl::$info : Curl::$info[$value];
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/date.php b/monstra/helpers/date.php
new file mode 100644
index 0000000..0adf332
--- /dev/null
+++ b/monstra/helpers/date.php
@@ -0,0 +1,432 @@
+
+ * echo Date::format($date, 'j.n.Y');
+ *
+ *
+ * @param integer $date Unix timestamp
+ * @param string $format Date format
+ * @return integer
+ */
+ public static function format($date, $format = '') {
+
+ // Redefine vars
+ $format = (string) $format;
+ $date = (int) $date;
+
+ if ($format != '') { return date($format, $date); } else { return date(MONSTRA_DATE_FORMAT, $date); }
+ }
+
+
+ /**
+ * Get number of seconds in a minute, incrementing by a step.
+ *
+ *
+ * $seconds = Date::seconds();
+ *
+ *
+ * @param integer $step Amount to increment each step by, 1 to 30
+ * @param integer $start Start value
+ * @param integer $end End value
+ * @return array
+ */
+ public static function seconds($step = 1, $start = 0, $end = 60) {
+
+ // Redefine vars
+ $step = (int) $step;
+ $start = (int) $start;
+ $end = (int) $end;
+
+ return Date::_range($step, $start, $end);
+ }
+
+
+ /**
+ * Get number of minutes in a hour, incrementing by a step.
+ *
+ *
+ * $minutes = Date::minutes();
+ *
+ *
+ * @param integer $step Amount to increment each step by, 1 to 30
+ * @param integer $start Start value
+ * @param integer $end End value
+ * @return array
+ */
+ public static function minutes($step = 5, $start = 0, $end = 60) {
+
+ // Redefine vars
+ $step = (int) $step;
+ $start = (int) $start;
+ $end = (int) $end;
+
+ return Date::_range($step, $start, $end);
+ }
+
+
+ /**
+ * Get number of hours, incrementing by a step.
+ *
+ *
+ * $hours = Date::hours();
+ *
+ *
+ * @param integer $step Amount to increment each step by, 1 to 30
+ * @param integer $long Start value
+ * @param integer $start End value
+ * @return array
+ */
+ public static function hours($step = 1, $long = false, $start = null) {
+
+ // Redefine vars
+ $step = (int) $step;
+ $long = (bool) $long;
+
+ if ($start === null) $start = ($long === FALSE) ? 1 : 0;
+ $end = ($long === true) ? 23 : 12;
+
+ return Date::_range($step, $start, $end, true);
+ }
+
+
+ /**
+ * Get number of months.
+ *
+ *
+ * $months = Date::months();
+ *
+ *
+ * @return array
+ */
+ public static function months() {
+ return Date::_range(1, 1, 12, true);
+ }
+
+
+ /**
+ * Get number of days.
+ *
+ *
+ * $months = Date::days();
+ *
+ *
+ * @return array
+ */
+ public static function days() {
+ return Date::_range(1, 1, Date::daysInMonth((int)date('M')), true);
+ }
+
+
+ /**
+ * Returns the number of days in the requested month
+ *
+ *
+ * $days = Date::daysInMonth(1);
+ *
+ *
+ * @param integer $month Month as a number (1-12)
+ * @param integer $year The year
+ * @return integer
+ */
+ public static function daysInMonth($month, $year = null) {
+
+ // Redefine vars
+ $month = (int) $month;
+ $year = ! empty($year) ? (int) $year : (int) date('Y');
+
+ if ($month < 1 or $month > 12) {
+ return false;
+ } elseif ($month == 2) {
+ if ($year % 400 == 0 or ($year % 4 == 0 and $year % 100 != 0)) {
+ return 29;
+ }
+ }
+
+ $days_in_month = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
+ return $days_in_month[$month-1];
+ }
+
+
+ /**
+ * Get number of years.
+ *
+ *
+ * $years = Date::years();
+ *
+ *
+ * @param integer $long Start value
+ * @param integer $start End value
+ * @return array
+ */
+ public static function years($start = 1980, $end = 2024) {
+
+ // Redefine vars
+ $start = (int) $start;
+ $end = (int) $end;
+
+ return Date::_range(1, $start, $end, true);
+ }
+
+
+ /**
+ * Get current season name
+ *
+ *
+ * echo Date::season();
+ *
+ *
+ * @return string
+ */
+ public static function season() {
+ $seasons = array("Winter", "Spring", "Summer", "Autumn");
+ return $seasons[(int)((date("n") %12)/3)];
+ }
+
+
+ /**
+ * Get today date
+ *
+ *
+ * echo Date::today();
+ *
+ *
+ * @param string $format Date format
+ * @return string
+ */
+ public static function today($format = '') {
+
+ // Redefine vars
+ $format = (string) $format;
+
+ if ($format != '') { return date($format); } else { return date(MONSTRA_DATE_FORMAT); }
+ }
+
+
+ /**
+ * Get yesterday date
+ *
+ *
+ * echo Date::yesterday();
+ *
+ *
+ * @param string $format Date format
+ * @return string
+ */
+ public static function yesterday($format = '') {
+
+ // Redefine vars
+ $format = (string) $format;
+
+ if ($format != '') { return date($format, strtotime("-1 day")); } else { return date(MONSTRA_DATE_FORMAT, strtotime("-1 day")); }
+ }
+
+
+ /**
+ * Get tomorrow date
+ *
+ *
+ * echo Date::tomorrow();
+ *
+ *
+ * @param string $format Date format
+ * @return string
+ */
+ public static function tomorrow($format = '') {
+
+ // Redefine vars
+ $format = (string) $format;
+
+ if ($format != '') { return date($format, strtotime("+1 day")); } else { return date(MONSTRA_DATE_FORMAT, strtotime("-1 day")); }
+ }
+
+
+ /**
+ * Converts a UNIX timestamp to DOS format.
+ *
+ *
+ * $dos = Date::unix2dos($unix);
+ *
+ *
+ * @param integer $timestamp UNIX timestamp
+ * @return integer
+ */
+ public static function unix2dos($timestamp = 0) {
+
+ $timestamp = ($_timestamp == 0) ? getdate() : getdate($_timestamp);
+
+ if ($timestamp['year'] < 1980) return (1 << 21 | 1 << 16);
+
+ $timestamp['year'] -= 1980;
+
+ return ($timestamp['year'] << 25 | $timestamp['mon'] << 21 |
+ $timestamp['mday'] << 16 | $timestamp['hours'] << 11 |
+ $timestamp['minutes'] << 5 | $timestamp['seconds'] >> 1);
+ }
+
+
+ /**
+ * Converts a DOS timestamp to UNIX format.
+ *
+ *
+ * $unix = Date::dos2unix($dos);
+ *
+ *
+ * @param integer $timestamp DOS timestamp
+ * @return integer
+ */
+ public static function dos2unix($timestamp) {
+ $sec = 2 * ($timestamp & 0x1f);
+ $min = ($timestamp >> 5) & 0x3f;
+ $hrs = ($timestamp >> 11) & 0x1f;
+ $day = ($timestamp >> 16) & 0x1f;
+ $mon = (($timestamp >> 21) & 0x0f);
+ $year = (($timestamp >> 25) & 0x7f) + 1980;
+ return mktime($hrs, $min, $sec, $mon, $day, $year);
+ }
+
+
+ /**
+ * Get Time zones
+ *
+ * @return array
+ */
+ public static function timezones() {
+ return array('Kwajalein'=>'(GMT-12:00) International Date Line West',
+ 'Pacific/Samoa'=>'(GMT-11:00) Midway Island, Samoa',
+ 'Pacific/Honolulu'=>'(GMT-10:00) Hawaii',
+ 'America/Anchorage'=>'(GMT-09:00) Alaska',
+ 'America/Los_Angeles'=>'(GMT-08:00) Pacific Time (US & Canada)',
+ 'America/Tijuana'=>'(GMT-08:00) Tijuana, Baja California',
+ 'America/Denver'=>'(GMT-07:00) Mountain Time (US & Canada)',
+ 'America/Chihuahua'=>'(GMT-07:00) Chihuahua, La Paz, Mazatlan',
+ 'America/Phoenix'=>'(GMT-07:00) Arizona',
+ 'America/Regina'=>'(GMT-06:00) Saskatchewan',
+ 'America/Tegucigalpa'=>'(GMT-06:00) Central America',
+ 'America/Chicago'=>'(GMT-06:00) Central Time (US & Canada)',
+ 'America/Mexico_City'=>'(GMT-06:00) Guadalajara, Mexico City, Monterrey',
+ 'America/New_York'=>'(GMT-05:00) Eastern Time (US & Canada)',
+ 'America/Bogota'=>'(GMT-05:00) Bogota, Lima, Quito, Rio Branco',
+ 'America/Indiana/Indianapolis'=>'(GMT-05:00) Indiana (East)',
+ 'America/Caracas'=>'(GMT-04:30) Caracas',
+ 'America/Halifax'=>'(GMT-04:00) Atlantic Time (Canada)',
+ 'America/Manaus'=>'(GMT-04:00) Manaus',
+ 'America/Santiago'=>'(GMT-04:00) Santiago',
+ 'America/La_Paz'=>'(GMT-04:00) La Paz',
+ 'America/St_Johns'=>'(GMT-03:30) Newfoundland',
+ 'America/Argentina/Buenos_Aires'=>'(GMT-03:00) Buenos Aires',
+ 'America/Sao_Paulo'=>'(GMT-03:00) Brasilia',
+ 'America/Godthab'=>'(GMT-03:00) Greenland',
+ 'America/Montevideo'=>'(GMT-03:00) Montevideo',
+ 'America/Argentina/Buenos_Aires'=>'(GMT-03:00) Georgetown',
+ 'Atlantic/South_Georgia'=>'(GMT-02:00) Mid-Atlantic',
+ 'Atlantic/Azores'=>'(GMT-01:00) Azores',
+ 'Atlantic/Cape_Verde'=>'(GMT-01:00) Cape Verde Is.',
+ 'Europe/London'=>'(GMT) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London',
+ 'Atlantic/Reykjavik'=>'(GMT) Monrovia, Reykjavik',
+ 'Africa/Casablanca'=>'(GMT) Casablanca',
+ 'Europe/Belgrade'=>'(GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague',
+ 'Europe/Sarajevo'=>'(GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb',
+ 'Europe/Brussels'=>'(GMT+01:00) Brussels, Copenhagen, Madrid, Paris',
+ 'Africa/Algiers'=>'(GMT+01:00) West Central Africa',
+ 'Europe/Amsterdam'=>'(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna',
+ 'Africa/Cairo'=>'(GMT+02:00) Cairo',
+ 'Europe/Helsinki'=>'(GMT+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius',
+ 'Europe/Athens'=>'(GMT+02:00) Athens, Bucharest, Istanbul',
+ 'Asia/Jerusalem'=>'(GMT+02:00) Jerusalem',
+ 'Asia/Amman'=>'(GMT+02:00) Amman',
+ 'Asia/Beirut'=>'(GMT+02:00) Beirut',
+ 'Africa/Windhoek'=>'(GMT+02:00) Windhoek',
+ 'Africa/Harare'=>'(GMT+02:00) Harare, Pretoria',
+ 'Asia/Kuwait'=>'(GMT+03:00) Kuwait, Riyadh',
+ 'Asia/Baghdad'=>'(GMT+03:00) Baghdad',
+ 'Europe/Minsk'=>'(GMT+03:00) Minsk',
+ 'Africa/Nairobi'=>'(GMT+03:00) Nairobi',
+ 'Asia/Tbilisi'=>'(GMT+03:00) Tbilisi',
+ 'Asia/Tehran'=>'(GMT+03:30) Tehran',
+ 'Asia/Muscat'=>'(GMT+04:00) Abu Dhabi, Muscat',
+ 'Asia/Baku'=>'(GMT+04:00) Baku',
+ 'Europe/Moscow'=>'(GMT+04:00) Moscow, St. Petersburg, Volgograd',
+ 'Asia/Yerevan'=>'(GMT+04:00) Yerevan',
+ 'Asia/Karachi'=>'(GMT+05:00) Islamabad, Karachi',
+ 'Asia/Tashkent'=>'(GMT+05:00) Tashkent',
+ 'Asia/Kolkata'=>'(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi',
+ 'Asia/Colombo'=>'(GMT+05:30) Sri Jayawardenepura',
+ 'Asia/Katmandu'=>'(GMT+05:45) Kathmandu',
+ 'Asia/Dhaka'=>'(GMT+06:00) Astana, Dhaka',
+ 'Asia/Yekaterinburg'=>'(GMT+06:00) Ekaterinburg',
+ 'Asia/Rangoon'=>'(GMT+06:30) Yangon (Rangoon)',
+ 'Asia/Novosibirsk'=>'(GMT+07:00) Almaty, Novosibirsk',
+ 'Asia/Bangkok'=>'(GMT+07:00) Bangkok, Hanoi, Jakarta',
+ 'Asia/Beijing'=>'(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi',
+ 'Asia/Ulaanbaatar'=>'(GMT+08:00) Irkutsk, Ulaan Bataar',
+ 'Asia/Krasnoyarsk'=>'(GMT+08:00) Krasnoyarsk',
+ 'Asia/Kuala_Lumpur'=>'(GMT+08:00) Kuala Lumpur, Singapore',
+ 'Asia/Taipei'=>'(GMT+08:00) Taipei',
+ 'Australia/Perth'=>'(GMT+08:00) Perth',
+ 'Asia/Seoul'=>'(GMT+09:00) Seoul',
+ 'Asia/Tokyo'=>'(GMT+09:00) Osaka, Sapporo, Tokyo',
+ 'Australia/Darwin'=>'(GMT+09:30) Darwin',
+ 'Australia/Adelaide'=>'(GMT+09:30) Adelaide',
+ 'Australia/Sydney'=>'(GMT+10:00) Canberra, Melbourne, Sydney',
+ 'Australia/Brisbane'=>'(GMT+10:00) Brisbane',
+ 'Australia/Hobart'=>'(GMT+10:00) Hobart',
+ 'Asia/Yakutsk'=>'(GMT+10:00) Yakutsk',
+ 'Pacific/Guam'=>'(GMT+10:00) Guam, Port Moresby',
+ 'Asia/Vladivostok'=>'(GMT+11:00) Vladivostok',
+ 'Pacific/Fiji'=>'(GMT+12:00) Fiji, Kamchatka, Marshall Is.',
+ 'Asia/Magadan'=>'(GMT+12:00) Magadan, Solomon Is., New Caledonia',
+ 'Pacific/Auckland'=>'(GMT+12:00) Auckland, Wellington',
+ 'Pacific/Tongatapu'=>'(GMT+13:00) Nukualofa'
+ );
+ }
+
+
+ /**
+ * _range()
+ */
+ protected static function _range($step, $start, $end, $flag = false) {
+ $result = array();
+ if ($flag) {
+ for ($i = $start; $i <= $end; $i += $step) $result[$i] = (string)$i;
+ } else {
+ for ($i = $start; $i < $end; $i += $step) $result[$i] = sprintf('%02d', $i);
+ }
+ return $result;
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/debug.php b/monstra/helpers/debug.php
new file mode 100644
index 0000000..71f886f
--- /dev/null
+++ b/monstra/helpers/debug.php
@@ -0,0 +1,124 @@
+
+ * Debug::elapsedTimeSetPoint('point_name');
+ *
+ *
+ * @param string $point_name Point name
+ */
+ public static function elapsedTimeSetPoint($point_name) {
+ Debug::$time[$point_name] = microtime(true);
+ }
+
+
+ /**
+ * Get elapsed time for current point
+ *
+ *
+ * echo Debug::elapsedTime('point_name');
+ *
+ *
+ * @param string $point_name Point name
+ * @return string
+ */
+ public static function elapsedTime($point_name) {
+ if (isset(Debug::$time[$point_name])) return sprintf("%01.4f", microtime(true) - Debug::$time[$point_name]);
+ }
+
+
+ /**
+ * Save current memory for current point
+ *
+ *
+ * Debug::memoryUsageSetPoint('point_name');
+ *
+ *
+ * @param string $point_name Point name
+ */
+ public static function memoryUsageSetPoint($point_name) {
+ Debug::$memory[$point_name] = memory_get_usage();
+ }
+
+
+ /**
+ * Get memory usage for current point
+ *
+ *
+ * echo Debug::memoryUsage('point_name');
+ *
+ *
+ * @param string $point_name Point name
+ * @return string
+ */
+ public static function memoryUsage($point_name) {
+ if (isset(Debug::$memory[$point_name])) return Number::byteFormat(memory_get_usage() - Debug::$memory[$point_name]);
+ }
+
+
+ /**
+ * Print the variable $data and exit if exit = true
+ *
+ *
+ * Debug::dump($data);
+ *
+ *
+ * @param mixed $data Data
+ * @param boolean $exit Exit
+ */
+ public static function dump($data, $exit = false){
+ echo "dump \n---------------------- \n\n" . print_r($data, true) . "\n----------------------"; + if ($exit) exit; + } + + } \ No newline at end of file diff --git a/monstra/helpers/dir.php b/monstra/helpers/dir.php new file mode 100644 index 0000000..a101102 --- /dev/null +++ b/monstra/helpers/dir.php @@ -0,0 +1,215 @@ + + * Dir::create('folder1'); + * + * + * @param string $dir Name of directory to create + * @param integer $chmod Chmod + * @return boolean + */ + public static function create($dir, $chmod = 0775) { + + // Redefine vars + $dir = (string) $dir; + + // Create new dir if $dir !exists + return ( ! Dir::exists($dir)) ? @mkdir($dir, $chmod, true) : true; + } + + + /** + * Checks if this directory exists. + * + *
+ * if (Dir::exists('folder1')) {
+ * // Do something...
+ * }
+ *
+ *
+ * @param string $dir Full path of the directory to check.
+ * @return boolean
+ */
+ public static function exists($dir) {
+
+ // Redefine vars
+ $dir = (string) $dir;
+
+ // Directory exists
+ if (file_exists($dir) && is_dir($dir)) return true;
+
+ // Doesn't exist
+ return false;
+ }
+
+
+ /**
+ * Check dir permission
+ *
+ *
+ * echo Dir::checkPerm('folder1');
+ *
+ *
+ * @param string $dir Directory to check
+ * @return string
+ */
+ public static function checkPerm($dir) {
+
+ // Redefine vars
+ $dir = (string) $dir;
+
+ // Clear stat cache
+ clearstatcache();
+
+ // Return perm
+ return substr(sprintf('%o', fileperms($dir)), -4);
+ }
+
+
+ /**
+ * Delete directory
+ *
+ *
+ * Dir::delete('folder1');
+ *
+ *
+ * @param string $dir Name of directory to delete
+ */
+ public static function delete($dir) {
+
+ // Redefine vars
+ $dir = (string) $dir;
+
+ // Delete dir
+ if (is_dir($dir)){$ob=scandir($dir);foreach ($ob as $o){if($o!='.'&&$o!='..'){if(filetype($dir.'/'.$o)=='dir')Dir::delete($dir.'/'.$o); else unlink($dir.'/'.$o);}}}
+ reset($ob); rmdir($dir);
+ }
+
+
+ /**
+ * Get list of directories
+ *
+ *
+ * $dirs = Dir::scan('folders');
+ *
+ *
+ * @param string $dir Directory
+ */
+ public static function scan($dir){
+
+ // Redefine vars
+ $dir = (string) $dir;
+
+ // Scan dir
+ if (is_dir($dir)&&$dh=opendir($dir)){$f=array();while ($fn=readdir($dh)){if($fn!='.'&&$fn!='..'&&is_dir($dir.DS.$fn))$f[]=$fn;}return$f;}
+ }
+
+
+ /**
+ * Check if a directory is writable.
+ *
+ *
+ * if (Dir::writable('folder1')) {
+ * // Do something...
+ * }
+ *
+ *
+ * @param string $path The path to check.
+ * @return booleans
+ */
+ public static function writable($path) {
+
+ // Redefine vars
+ $path = (string) $path;
+
+ // Create temporary file
+ $file = tempnam($path, 'writable');
+
+ // File has been created
+ if($file !== false) {
+
+ // Remove temporary file
+ File::delete($file);
+
+ // Writable
+ return true;
+ }
+
+ // Else not writable
+ return false;
+ }
+
+
+ /**
+ * Get directory size.
+ *
+ *
+ * echo Dir::size('folder1');
+ *
+ *
+ * @param string $path The path to directory.
+ * @return integer
+ */
+ public static function size($path) {
+
+ // Redefine vars
+ $path = (string) $path;
+
+ $total_size = 0;
+ $files = scandir($path);
+ $clean_path = rtrim($path, '/') . '/';
+
+ foreach ($files as $t) {
+ if ( $t <> "." && $t <> "..") {
+ $current_file = $clean_path . $t;
+ if (is_dir($current_file)) {
+ $total_size += Dir::size($current_file);
+ } else {
+ $total_size += filesize($current_file);
+ }
+ }
+ }
+
+ // Return total size
+ return $total_size;
+ }
+
+ }
diff --git a/monstra/helpers/file.php b/monstra/helpers/file.php
new file mode 100644
index 0000000..eb23942
--- /dev/null
+++ b/monstra/helpers/file.php
@@ -0,0 +1,596 @@
+ 'audio/aac',
+ 'atom' => 'application/atom+xml',
+ 'avi' => 'video/avi',
+ 'bmp' => 'image/x-ms-bmp',
+ 'c' => 'text/x-c',
+ 'class' => 'application/octet-stream',
+ 'css' => 'text/css',
+ 'csv' => 'text/csv',
+ 'deb' => 'application/x-deb',
+ 'dll' => 'application/x-msdownload',
+ 'dmg' => 'application/x-apple-diskimage',
+ 'doc' => 'application/msword',
+ 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ 'exe' => 'application/octet-stream',
+ 'flv' => 'video/x-flv',
+ 'gif' => 'image/gif',
+ 'gz' => 'application/x-gzip',
+ 'h' => 'text/x-c',
+ 'htm' => 'text/html',
+ 'html' => 'text/html',
+ 'ini' => 'text/plain',
+ 'jar' => 'application/java-archive',
+ 'java' => 'text/x-java',
+ 'jpeg' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'js' => 'text/javascript',
+ 'json' => 'application/json',
+ 'mid' => 'audio/midi',
+ 'midi' => 'audio/midi',
+ 'mka' => 'audio/x-matroska',
+ 'mkv' => 'video/x-matroska',
+ 'mp3' => 'audio/mpeg',
+ 'mp4' => 'application/mp4',
+ 'mpeg' => 'video/mpeg',
+ 'mpg' => 'video/mpeg',
+ 'odt' => 'application/vnd.oasis.opendocument.text',
+ 'ogg' => 'audio/ogg',
+ 'pdf' => 'application/pdf',
+ 'php' => 'text/x-php',
+ 'png' => 'image/png',
+ 'psd' => 'image/vnd.adobe.photoshop',
+ 'py' => 'application/x-python',
+ 'ra' => 'audio/vnd.rn-realaudio',
+ 'ram' => 'audio/vnd.rn-realaudio',
+ 'rar' => 'application/x-rar-compressed',
+ 'rss' => 'application/rss+xml',
+ 'safariextz' => 'application/x-safari-extension',
+ 'sh' => 'text/x-shellscript',
+ 'shtml' => 'text/html',
+ 'swf' => 'application/x-shockwave-flash',
+ 'tar' => 'application/x-tar',
+ 'tif' => 'image/tiff',
+ 'tiff' => 'image/tiff',
+ 'torrent' => 'application/x-bittorrent',
+ 'txt' => 'text/plain',
+ 'wav' => 'audio/wav',
+ 'webp' => 'image/webp',
+ 'wma' => 'audio/x-ms-wma',
+ 'xls' => 'application/vnd.ms-excel',
+ 'xml' => 'text/xml',
+ 'zip' => 'application/zip',
+ );
+
+
+ /**
+ * Protected constructor since this is a static class.
+ *
+ * @access protected
+ */
+ protected function __construct() {
+ // Nothing here
+ }
+
+
+ /**
+ * Returns true if the File exists.
+ *
+ *
+ * if (File::exists('filename.txt')) {
+ * // Do something...
+ * }
+ *
+ *
+ * @param string $filename The file name
+ * @return boolean
+ */
+ public static function exists($filename) {
+
+ // Redefine vars
+ $filename = (string) $filename;
+
+ // Return
+ return (file_exists($filename) && is_file($filename));
+ }
+
+
+ /**
+ * Delete file
+ *
+ *
+ * File::delete('filename.txt');
+ *
+ *
+ * @param mixed $filename The file name or array of files
+ * @return boolean
+ */
+ public static function delete($filename) {
+
+ // Is array
+ if (is_array($filename)) {
+
+ // Delete each file in $filename array
+ foreach($filename as $file) {
+ @unlink((string) $file);
+ }
+
+ } else {
+ // Is string
+ return @unlink((string) $filename);
+ }
+
+ }
+
+
+ /**
+ * Rename file
+ *
+ *
+ * File::rename('filename1.txt', 'filename2.txt');
+ *
+ *
+ * @param string $from Original file location
+ * @param string $to Desitination location of the file
+ * @return boolean
+ */
+ public static function rename($from, $to) {
+
+ // Redefine vars
+ $from = (string) $from;
+ $to = (string) $to;
+
+ // If file exists $to than rename it
+ if ( ! File::exists($to)) return rename($from, $to);
+
+ // Else return false
+ return false;
+ }
+
+
+ /**
+ * Copy file
+ *
+ *
+ * File::copy('folder1/filename.txt', 'folder2/filename.txt');
+ *
+ *
+ * @param string $from Original file location
+ * @param string $to Desitination location of the file
+ * @return boolean
+ */
+ public static function copy($from, $to) {
+
+ // Redefine vars
+ $from = (string) $from;
+ $to = (string) $to;
+
+ // If file !exists $from and exists $to then return false
+ if ( ! File::exists($from) || File::exists($to)) return false;
+
+ // Else copy file
+ return copy($from, $to);
+ }
+
+
+ /**
+ * Get the File extension.
+ *
+ *
+ * echo File::ext('filename.txt');
+ *
+ *
+ * @param string $filename The file name
+ * @return string
+ */
+ public static function ext($filename){
+
+ // Redefine vars
+ $filename = (string) $filename;
+
+ // Return file extension
+ return substr(strrchr($filename, '.'), 1);
+ }
+
+
+ /**
+ * Get the File name
+ *
+ *
+ * echo File::name('filename.txt');
+ *
+ *
+ * @param string $filename The file name
+ * @return string
+ */
+ public static function name($filename) {
+
+ // Redefine vars
+ $filename = (string) $filename;
+
+ // Return filename
+ return basename($filename, '.'.File::ext($filename));
+ }
+
+
+ /**
+ * Get list of files in directory recursive
+ *
+ *
+ * $files = File::scan('folder');
+ * $files = File::scan('folder', 'txt');
+ * $files = File::scan('folder', array('txt', 'log'));
+ *
+ *
+ * @param string $folder Folder
+ * @param mixed $type Files types
+ * @return array
+ */
+ public static function scan($folder, $type = null) {
+ $data = array();
+ if (is_dir($folder)) {
+ $iterator = new RecursiveDirectoryIterator($folder);
+ foreach (new RecursiveIteratorIterator($iterator) as $file) {
+ if ($type !== null) {
+ if (is_array($type)) {
+ $file_ext = substr(strrchr($file->getFilename(), '.'), 1);
+ if (in_array($file_ext, $type)) {
+ if (strpos($file->getFilename(), $file_ext, 1)) {
+ $data[] = $file->getFilename();
+ }
+ }
+ } else {
+ if (strpos($file->getFilename(), $type, 1)) {
+ $data[] = $file->getFilename();
+ }
+ }
+ } else {
+ if ($file->getFilename() !== '.' && $file->getFilename() !== '..') $data[] = $file->getFilename();
+ }
+ }
+ return $data;
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * Fetch the content from a file or URL.
+ *
+ *
+ * echo File::getContent('filename.txt');
+ *
+ *
+ * @param string $filename The file name
+ * @return boolean
+ */
+ public static function getContent($filename) {
+
+ // Redefine vars
+ $filename = (string) $filename;
+
+ // If file exists load it
+ if (File::exists($filename)) {
+ return file_get_contents($filename);
+ }
+ }
+
+
+ /**
+ * Writes a string to a file.
+ *
+ * @param string $filename The path of the file.
+ * @param string $content The content that should be written.
+ * @param boolean $createFile Should the file be created if it doesn't exists?
+ * @param boolean $append Should the content be appended if the file already exists?
+ * @param integer $chmod Mode that should be applied on the file.
+ * @return boolean
+ */
+ public static function setContent($filename, $content, $create_file = true, $append = false, $chmod = 0666) {
+
+ // Redefine vars
+ $filename = (string) $filename;
+ $content = (string) $content;
+ $create_file = (bool) $create_file;
+ $append = (bool) $append;
+
+ // File may not be created, but it doesn't exist either
+ if ( ! $create_file && File::exists($filename)) throw new RuntimeException(vsprintf("%s(): The file '{$filename}' doesn't exist", array(__METHOD__)));
+
+ // Create directory recursively if needed
+ Dir::create(dirname($filename));
+
+ // Create file & open for writing
+ $handler = ($append) ? @fopen($filename, 'a') : @fopen($filename, 'w');
+
+ // Something went wrong
+ if ($handler === false) throw new RuntimeException(vsprintf("%s(): The file '{$filename}' could not be created. Check if PHP has enough permissions.", array(__METHOD__)));
+
+ // Store error reporting level
+ $level = error_reporting();
+
+ // Disable errors
+ error_reporting(0);
+
+ // Write to file
+ $write = fwrite($handler, $content);
+
+ // Validate write
+ if($write === false) throw new RuntimeException(vsprintf("%s(): The file '{$filename}' could not be created. Check if PHP has enough permissions.", array(__METHOD__)));
+
+ // Close the file
+ fclose($handler);
+
+ // Chmod file
+ chmod($filename, $chmod);
+
+ // Restore error reporting level
+ error_reporting($level);
+
+ // Return
+ return true;
+ }
+
+
+ /**
+ * Get time(in Unix timestamp) the file was last changed
+ *
+ *
+ * echo File::lastChange('filename.txt');
+ *
+ *
+ * @param string $filename The file name
+ * @return boolean
+ */
+ public static function lastChange($filename) {
+
+ // Redefine vars
+ $filename = (string) $filename;
+
+ // If file exists return filemtime
+ if (File::exists($filename)) {
+ return filemtime($filename);
+ }
+
+ // Return
+ return false;
+
+ }
+
+
+ /**
+ * Get last access time
+ *
+ *
+ * echo File::lastAccess('filename.txt');
+ *
+ *
+ * @param string $filename The file name
+ * @return boolean
+ */
+ public static function lastAccess($filename) {
+
+ // Redefine vars
+ $filename = (string) $filename;
+
+ // If file exists return fileatime
+ if (File::exists($filename)) {
+ return fileatime($filename);
+ }
+
+ // Return
+ return false;
+ }
+
+
+ /**
+ * Returns the mime type of a file. Returns false if the mime type is not found.
+ *
+ *
+ * echo File::mime('filename.txt');
+ *
+ *
+ * @param string $file Full path to the file
+ * @param boolean $guess Set to false to disable mime type guessing
+ * @return string
+ */
+ public static function mime($file, $guess = true) {
+
+ // Redefine vars
+ $file = (string) $file;
+ $guess = (bool) $guess;
+
+ // Get mime using the file information functions
+ if (function_exists('finfo_open')) {
+
+ $info = finfo_open(FILEINFO_MIME_TYPE);
+
+ $mime = finfo_file($info, $file);
+
+ finfo_close($info);
+
+ return $mime;
+
+ } else {
+
+ // Just guess mime by using the file extension
+ if ($guess === true) {
+
+ $mime_types = File::$mime_types;
+
+ $extension = pathinfo($file, PATHINFO_EXTENSION);
+
+ return isset($mime_types[$extension]) ? $mime_types[$extension] : false;
+ } else {
+ return false;
+ }
+ }
+ }
+
+
+ /**
+ * Forces a file to be downloaded.
+ *
+ *
+ * File::download('filename.txt');
+ *
+ *
+ * @param string $file Full path to file
+ * @param string $content_type Content type of the file
+ * @param string $filename Filename of the download
+ * @param integer $kbps Max download speed in KiB/s
+ */
+ public static function download($file, $content_type = null, $filename = null, $kbps = 0) {
+
+ // Redefine vars
+ $file = (string) $file;
+ $content_type = ($content_type === null) ? null : (string) $content_type;
+ $filename = ($filename === null) ? null : (string) $filename;
+ $kbps = (int) $kbps;
+
+ // Check that the file exists and that its readable
+ if (file_exists($file) === false || is_readable($file) === false) {
+ throw new RuntimeException(vsprintf("%s(): Failed to open stream.", array(__METHOD__)));
+ }
+
+ // Empty output buffers
+ while (ob_get_level() > 0) ob_end_clean();
+
+ // Send headers
+ if ($content_type === null) $content_type = File::mime($file);
+
+ if ($filename === null) $filename = basename($file);
+
+
+ header('Content-type: ' . $content_type);
+ header('Content-Disposition: attachment; filename="' . $filename . '"');
+ header('Content-Length: ' . filesize($file));
+
+ // Read file and write it to the output
+ @set_time_limit(0);
+
+ if ($kbps === 0) {
+
+ readfile($file);
+
+ } else {
+
+ $handle = fopen($file, 'r');
+
+ while ( ! feof($handle) && !connection_aborted()) {
+
+ $s = microtime(true);
+
+ echo fread($handle, round($kbps * 1024));
+
+ if (($wait = 1e6 - (microtime(true) - $s)) > 0) usleep($wait);
+
+ }
+
+ fclose($handle);
+ }
+
+ exit();
+ }
+
+
+ /**
+ * Display a file in the browser.
+ *
+ *
+ * File::display('filename.txt');
+ *
+ *
+ * @param string $file Full path to file
+ * @param string $content_type Content type of the file
+ * @param string $filename Filename of the download
+ */
+ public static function display($file, $content_type = null, $filename = null) {
+
+ // Redefine vars
+ $file = (string) $file;
+ $content_type = ($content_type === null) ? null : (string) $content_type;
+ $filename = ($filename === null) ? null : (string) $filename;
+
+ // Check that the file exists and that its readable
+ if (file_exists($file) === false || is_readable($file) === false) {
+ throw new RuntimeException(vsprintf("%s(): Failed to open stream.", array(__METHOD__)));
+ }
+
+ // Empty output buffers
+ while (ob_get_level() > 0) ob_end_clean();
+
+ // Send headers
+ if ($content_type === null) $content_type = File::mime($file);
+
+ if($filename === null) $filename = basename($file);
+
+ header('Content-type: ' . $content_type);
+ header('Content-Disposition: inline; filename="' . $filename . '"');
+ header('Content-Length: ' . filesize($file));
+
+ // Read file and write to output
+ readfile($file);
+
+ exit();
+ }
+
+
+ /**
+ * Tests whether a file is writable for anyone.
+ *
+ *
+ * if (File::writable('filename.txt')) {
+ * // do something...
+ * }
+ *
+ *
+ * @param string $file File to check
+ * @return boolean
+ */
+ public static function writable($file) {
+
+ // Redefine vars
+ $file = (string) $file;
+
+ // Is file exists ?
+ if ( ! file_exists($file)) throw new RuntimeException(vsprintf("%s(): The file '{$file}' doesn't exist", array(__METHOD__)));
+
+ // Gets file permissions
+ $perms = fileperms($file);
+
+ // Is writable ?
+ if (is_writable($file) || ($perms & 0x0080) || ($perms & 0x0010) || ($perms & 0x0002)) return true;
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/form.php b/monstra/helpers/form.php
new file mode 100644
index 0000000..1f473a5
--- /dev/null
+++ b/monstra/helpers/form.php
@@ -0,0 +1,363 @@
+
+ * // Form will submit back to the current page using POST
+ * echo Form::open();
+ *
+ * // Form will submit to 'search' using GET
+ * echo Form::open('search', array('method' => 'get'));
+ *
+ * // When "file" inputs are present, you must include the "enctype"
+ * echo Form::open(null, array('enctype' => 'multipart/form-data'));
+ *
+ *
+ * @param mixed $action Form action, defaults to the current request URI.
+ * @param array $attributes HTML attributes.
+ * @uses Url::base
+ * @uses Html::attributes
+ * @return string
+ */
+ public static function open($action = null, array $attributes = null) {
+
+ if ( ! $action) {
+
+ // Submits back to the current url
+ $action = '';
+
+ } elseif (strpos($action, '://') === false) {
+
+ // Make the URI absolute
+ $action = Url::base() . '/' . $action;
+ }
+
+ // Add the form action to the attributes
+ $attributes['action'] = $action;
+
+ if ( ! isset($attributes['method'])) {
+
+ // Use POST method
+ $attributes['method'] = 'post';
+ }
+
+ return '';
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/html.php b/monstra/helpers/html.php
new file mode 100644
index 0000000..04c84d6
--- /dev/null
+++ b/monstra/helpers/html.php
@@ -0,0 +1,277 @@
+
+ * echo Html::chars($username);
+ *
+ *
+ * @param string $value String to convert
+ * @param boolean $double_encode Encode existing entities
+ * @return string
+ */
+ public static function chars($value, $double_encode = true) {
+ return htmlspecialchars((string)$value, ENT_QUOTES, 'utf-8', $double_encode);
+ }
+
+
+ /**
+ * Compiles an array of HTML attributes into an attribute string.
+ * Attributes will be sorted using Html::$attribute_order for consistency.
+ *
+ *
+ * echo ''.$content.'';
+ *
+ *
+ * @param array $attributes Attribute list
+ * @return string
+ */
+ public static function attributes(array $attributes = null) {
+
+ if (empty($attributes)) return '';
+
+ // Init var
+ $sorted = array();
+
+ foreach (Html::$attribute_order as $key) {
+
+ if (isset($attributes[$key])) {
+ // Add the attribute to the sorted list
+ $sorted[$key] = $attributes[$key];
+ }
+
+ }
+
+ // Combine the sorted attributes
+ $attributes = $sorted + $attributes;
+
+ $compiled = '';
+ foreach ($attributes as $key => $value) {
+
+ if ($value === NULL) {
+ // Skip attributes that have NULL values
+ continue;
+ }
+
+ if (is_int($key)) {
+ // Assume non-associative keys are mirrored attributes
+ $key = $value;
+ }
+
+ // Add the attribute value
+ $compiled .= ' '.$key.'="'.Html::chars($value).'"';
+ }
+
+ return $compiled;
+ }
+
+
+ /**
+ * Create br tags
+ *
+ *
+ * echo Html::br(2);
+ *
+ *
+ * @param integer $num Count of line break tag
+ * @return string
+ */
+ public static function br($num = 1) {
+ return str_repeat("
+ * echo Html::nbsp(2);
+ *
+ *
+ * @param integer $num Count of
+ * @return string
+ */
+ public static function nbsp($num = 1) {
+ return str_repeat(" ", (int)$num);
+ }
+
+
+ /**
+ * Create an arrow
+ *
+ *
+ * echo Html::arrow('right');
+ *
+ *
+ * @param string $direction Arrow direction [up,down,left,right]
+ * @param boolean $render If this option is true then render html object else return it
+ * @return string
+ */
+ public static function arrow($direction) {
+ switch ($direction) {
+ case "up": $output = '↑'; break;
+ case "down": $output = '↓'; break;
+ case "left": $output = '←'; break;
+ case "right": $output = '→'; break;
+ }
+ return $output;
+ }
+
+
+ /**
+ * Create HTML link anchor.
+ *
+ *
+ * echo Html::anchor('About', 'http://sitename.com/about');
+ *
+ *
+ * @param string $title Anchor title
+ * @param string $url Anchor url
+ * @param array $attributes Anchor attributes
+ * @uses Html::attributes
+ * @return string
+ */
+ public static function anchor($title, $url = null, array $attributes = null) {
+
+ // Add link
+ if ($url !== null) $attributes['href'] = $url;
+
+ return ''.$title.'';
+ }
+
+
+ /**
+ * Create HTML
+ * echo Html::heading('Title', 1);
+ *
+ *
+ * @param string $title Text
+ * @param integer $h Number [1-6]
+ * @param array $attributes Heading attributes
+ * @uses Html::attributes
+ * @return string
+ */
+ public static function heading($title, $h = 1, array $attributes = null) {
+
+ $output = '
+ * echo Html::doctype('html5');
+ *
+ *
+ * @param string $type Doctype to generated
+ * @return mixed
+ */
+ public static function doctype($type = 'html5') {
+
+ $doctypes = array('xhtml11' => '',
+ 'xhtml1-strict' => '',
+ 'xhtml1-trans' => '',
+ 'xhtml1-frame' => '',
+ 'html5' => '',
+ 'html4-strict' => '',
+ 'html4-trans' => '',
+ 'html4-frame' => '');
+
+ if (isset($doctypes[$type])) return $doctypes[$type]; else return false;
+
+ }
+
+
+ /**
+ * Create image
+ *
+ *
+ * echo Html::image('data/files/pic1.jpg');
+ *
+ *
+ * @param array $attributes Image attributes
+ * @param string $file File
+ * @uses Url::base
+ * @return string
+ */
+ public static function image($file, array $attributes = null) {
+
+ if (strpos($file, '://') === FALSE) {
+ $file = Url::base().'/'.$file;
+ }
+
+ // Add the image link
+ $attributes['src'] = $file;
+
+ return '
+ * echo Html::toText('test');
+ *
+ *
+ * @param string $str String
+ * @return string
+ */
+ public static function toText($str) {
+ return htmlspecialchars($str, ENT_QUOTES, 'utf-8');
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/image.php b/monstra/helpers/image.php
new file mode 100644
index 0000000..619df77
--- /dev/null
+++ b/monstra/helpers/image.php
@@ -0,0 +1,673 @@
+image_info)) return $this->image_info[$key];
+ }
+
+
+ /**
+ * Set value for specific key
+ *
+ * @param string $key Key
+ * @param mixed $value Value
+ */
+ public function __set($key, $value) {
+ $this->image_info[$key] = $value;
+ }
+
+
+ /**
+ * Image factory.
+ *
+ *
+ * $image = Image::factory('original.png');
+ *
+ *
+ * @param string $filename Filename
+ * @return Image
+ */
+ public static function factory($filename) {
+ return new Image($filename);
+ }
+
+
+ /**
+ * Construct
+ *
+ * @param string $file Filename
+ */
+ public function __construct($file) {
+
+ // Redefine vars
+ $file = (string) $file;
+
+ // Check if the file exists
+ if (file_exists($file)) {
+
+ // Extract attributes of the image file
+ list($this->width, $this->height, $type, $a) = getimagesize($file);
+
+ // Save image type
+ $this->type = $type;
+
+ // Create a new image
+ $this->image = $this->createImage($file, $type);
+ } else {
+ throw new RuntimeException(vsprintf("%s(): The file '{$file}' doesn't exist", array(__METHOD__)));
+ }
+ }
+
+
+ /**
+ * Create a new image from file.
+ *
+ * @param string $file Path to the image file
+ * @param integer $type Image type
+ * @return resource
+ */
+ protected function createImage($file, $type) {
+
+ // Create image from file
+ switch($type) {
+ case IMAGETYPE_JPEG:
+ return imagecreatefromjpeg($file);
+ break;
+ case IMAGETYPE_GIF:
+ return imagecreatefromgif($file);
+ break;
+ case IMAGETYPE_PNG:
+ return imagecreatefrompng($file);
+ break;
+ default:
+ throw new RuntimeException(vsprintf("%s(): Unable to open '%s'. Unsupported image type.", array(__METHOD__, $type)));
+ }
+ }
+
+
+ /**
+ * Resizes the image to the chosen size.
+ *
+ *
+ * Image::factory('original.png')->resize(800, 600)->save('edited.png');
+ *
+ *
+ * @param integer $width Width of the image
+ * @param integer $height Height of the image
+ * @param integer $aspect_ratio Aspect ratio (Image::AUTO Image::WIDTH Image::HEIGHT)
+ * @return Image
+ */
+ public function resize($width, $height = null, $aspect_ratio = null) {
+
+ // Redefine vars
+ $width = (int) $width;
+ $height = ($height === null) ? null : (int) $height;
+ $aspect_ratio = ($aspect_ratio === null) ? null : (int) $aspect_ratio;
+
+ // Resizes the image to {$width}% of the original size
+ if ($height === null) {
+
+ $new_width = round($this->width * ($width / 100));
+ $new_height = round($this->height * ($width / 100));
+
+ } else {
+
+ // Resizes the image to the smalles possible dimension while maintaining aspect ratio
+ if ($aspect_ratio === Image::AUTO) {
+
+ // Calculate smallest size based on given height and width while maintaining aspect ratio
+ $percentage = min(($width / $this->width), ($height / $this->height));
+
+ $new_width = round($this->width * $percentage);
+ $new_height = round($this->height * $percentage);
+
+ // Resizes the image using the width to maintain aspect ratio
+ } else if ($aspect_ratio === Image::WIDTH) {
+
+ // Base new size on given width while maintaining aspect ratio
+ $new_width = $width;
+ $new_height = round($this->height * ($width / $this->width));
+
+ // Resizes the image using the height to maintain aspect ratio
+ } else if($aspect_ratio === Image::HEIGHT) {
+
+ // Base new size on given height while maintaining aspect ratio
+ $new_width = round($this->width * ($height / $this->height));
+ $new_height = $height;
+
+ // Resizes the image to a dimension of {$width}x{$height} pixels while ignoring the aspect ratio
+ } else {
+
+ $new_width = $width;
+ $new_height = $height;
+ }
+ }
+
+ // Create a new true color image width new width and height
+ $resized = imagecreatetruecolor($new_width, $new_height);
+
+ // Copy and resize part of an image with resampling
+ imagecopyresampled($resized, $this->image, 0, 0, 0, 0, $new_width, $new_height, $this->width, $this->height);
+
+ // Destroy an image
+ imagedestroy($this->image);
+
+ // Create a new true color image width new width and height
+ $this->image = imagecreatetruecolor($new_width, $new_height);
+
+ // Copy and resize part of an image with resampling
+ imagecopyresampled($this->image, $resized, 0, 0, 0, 0, $new_width, $new_height, $new_width, $new_height);
+
+ // Destroy an image
+ imagedestroy($resized);
+
+ // Save new width and height
+ $this->width = $new_width;
+ $this->height = $new_height;
+
+ return $this;
+ }
+
+
+ /**
+ * Crops the image
+ *
+ *
+ * Image::factory('original.png')->crop(800, 600, 0, 0)->save('edited.png');
+ *
+ *
+ * @param integer $width Width of the crop
+ * @param integer $height Height of the crop
+ * @param integer $x The X coordinate of the cropped region's top left corner
+ * @param integer $y The Y coordinate of the cropped region's top left corner
+ * @return Image
+ */
+ public function crop($width, $height, $x, $y) {
+
+ // Redefine vars
+ $width = (int) $width;
+ $height = (int) $height;
+ $x = (int) $x;
+ $y = (int) $y;
+
+ // Calculate
+ if ($x + $width > $this->width) $width = $this->width - $x;
+ if ($y + $height > $this->height) $height = $this->height - $y;
+ if ($width <= 0 || $height <= 0) return false;
+
+ // Create a new true color image
+ $crop = imagecreatetruecolor($width, $height);
+
+ // Copy and resize part of an image with resampling
+ imagecopyresampled($crop, $this->image, 0, 0, $x, $y, $this->width, $this->height, $this->width, $this->height);
+
+ // Destroy an image
+ imagedestroy($this->image);
+
+ // Create a new true color image
+ $this->image = imagecreatetruecolor($width, $height);
+
+ // Copy and resize part of an image with resampling
+ imagecopyresampled($this->image, $crop, 0, 0, 0, 0, $width, $height, $width, $height);
+
+ // Destroy an image
+ imagedestroy($crop);
+
+ // Save new width and height
+ $this->width = $width;
+ $this->height = $height;
+
+ return $this;
+ }
+
+
+ /**
+ * Adds a watermark to the image.
+ *
+ * @param string $file Path to the image file
+ * @param integer $position Position of the watermark
+ * @param integer $opacity Opacity of the watermark in percent
+ * @return Image
+ */
+ public function watermark($file, $position = null, $opacity = 100) {
+
+ // Check if the image exists
+ if ( ! file_exists($file)) {
+ throw new RuntimeException(vsprintf("%s(): The image file ('%s') does not exist.", array(__METHOD__, $file)));
+ }
+
+ $watermark = $this->createImage($file, $this->type);
+
+ $watermarkW = imagesx($watermark);
+ $watermarkH = imagesy($watermark);
+
+ // Make sure that opacity is between 0 and 100
+ $opacity = max(min((int) $opacity, 100), 0);
+
+ if($opacity < 100) {
+
+ if(GD_BUNDLED === 0) {
+ throw new RuntimeException(vsprintf("%s(): Setting watermark opacity requires the 'imagelayereffect' function which is only available in the bundled version of GD.", array(__METHOD__)));
+ }
+
+ // Convert alpha to 0-127
+ $alpha = min(round(abs(($opacity * 127 / 100) - 127)), 127);
+
+ $transparent = imagecolorallocatealpha($watermark, 0, 0, 0, $alpha);
+
+ imagelayereffect($watermark, IMG_EFFECT_OVERLAY);
+
+ imagefilledrectangle($watermark, 0, 0, $watermarkW, $watermarkH, $transparent);
+ }
+
+ // Position the watermark.
+ switch($position) {
+ case Image::TOP_RIGHT:
+ $x = imagesx($this->image) - $watermarkW;
+ $y = 0;
+ break;
+ case Image::BOTTOM_LEFT:
+ $x = 0;
+ $y = imagesy($this->image) - $watermarkH;
+ break;
+ case Image::BOTTOM_RIGHT:
+ $x = imagesx($this->image) - $watermarkW;
+ $y = imagesy($this->image) - $watermarkH;
+ break;
+ case Image::CENTER:
+ $x = (imagesx($this->image) / 2) - ($watermarkW / 2);
+ $y = (imagesy($this->image) / 2) - ($watermarkH / 2);
+ break;
+ default:
+ $x = 0;
+ $y = 0;
+ }
+
+ imagealphablending($this->image, true);
+
+ imagecopy($this->image, $watermark, $x, $y, 0, 0, $watermarkW, $watermarkH);
+
+ imagedestroy($watermark);
+
+ // Return Image
+ return $this;
+ }
+
+
+ /**
+ * Convert image into grayscale
+ *
+ *
+ * Image::factory('original.png')->grayscale()->save('edited.png');
+ *
+ *
+ * @return Image
+ */
+ public function grayscale() {
+ imagefilter($this->image, IMG_FILTER_GRAYSCALE);
+ return $this;
+ }
+
+
+ /**
+ * Convert image into sepia
+ *
+ *
+ * Image::factory('original.png')->sepia()->save('edited.png');
+ *
+ *
+ * @return Image
+ */
+ public function sepia() {
+ imagefilter($this->image, IMG_FILTER_GRAYSCALE);
+ imagefilter($this->image, IMG_FILTER_COLORIZE, 112, 66, 20);
+ return $this;
+ }
+
+
+ /**
+ * Convert image into brightness
+ *
+ *
+ * Image::factory('original.png')->brightness(60)->save('edited.png');
+ *
+ *
+ * @param integer $level Level. From -255(min) to 255(max)
+ * @return Image
+ */
+ public function brightness($level = 0) {
+ imagefilter($this->image, IMG_FILTER_BRIGHTNESS, (int)$level);
+ return $this;
+ }
+
+
+ /**
+ * Convert image into colorize
+ *
+ *
+ * Image::factory('original.png')->colorize(60, 0, 0)->save('edited.png');
+ *
+ *
+ * @param integer $red Red
+ * @param integer $green Green
+ * @param integer $blue Blue
+ * @return Image
+ */
+ public function colorize($red, $green, $blue) {
+ imagefilter($this->image, IMG_FILTER_COLORIZE, (int)$red, (int)$green, (int)$blue);
+ return $this;
+ }
+
+
+ /**
+ * Convert image into contrast
+ *
+ *
+ * Image::factory('original.png')->contrast(60)->save('edited.png');
+ *
+ *
+ * @param integer $level Level. From -100(max) to 100(min) note the direction!
+ * @return Image
+ */
+ public function contrast($level) {
+ imagefilter($this->image, IMG_FILTER_CONTRAST, (int)$level);
+ return $this;
+ }
+
+
+ /**
+ * Creates a color based on a hex value.
+ *
+ * @param string $hex Hex code of the color
+ * @param integer $alpha Alpha. Default is 100
+ * @param boolean $returnRGB FALSE returns a color identifier, TRUE returns a RGB array
+ * @return integer
+ */
+ protected function createColor($hex, $alpha = 100, $return_rgb = false) {
+
+ // Redefine vars
+ $hex = (string) $hex;
+ $alpha = (int) $alpha;
+ $return_rgb = (bool) $return_rgb;
+
+ $hex = str_replace('#', '', $hex);
+
+ if (preg_match('/^([a-f0-9]{3}){1,2}$/i', $hex) === 0) {
+ throw new RuntimeException(vsprintf("%s(): Invalid color code ('%s').", array(__METHOD__, $hex)));
+ }
+
+ if (strlen($hex) === 3) {
+
+ $r = hexdec(str_repeat(substr($hex, 0, 1), 2));
+ $g = hexdec(str_repeat(substr($hex, 1, 1), 2));
+ $b = hexdec(str_repeat(substr($hex, 2, 1), 2));
+
+ } else {
+
+ $r = hexdec(substr($hex, 0, 2));
+ $g = hexdec(substr($hex, 2, 2));
+ $b = hexdec(substr($hex, 4, 2));
+
+ }
+
+ if ($return_rgb === true) {
+
+ return array('r' => $r, 'g' => $g, 'b' => $b);
+
+ } else {
+
+ // Convert alpha to 0-127
+ $alpha = min(round(abs(($alpha * 127 / 100) - 127)), 127);
+
+ return imagecolorallocatealpha($this->image, $r, $g, $b, $alpha);
+ }
+ }
+
+
+ /**
+ * Rotates the image using the given angle in degrees.
+ *
+ *
+ * Image::factory('original.png')->rotate(90)->save('edited.png');
+ *
+ *
+ * @param integer $degrees Degrees to rotate the image
+ * @return Image
+ */
+ public function rotate($degrees) {
+
+ if (GD_BUNDLED === 0) {
+ throw new RuntimeException(vsprintf("%s(): This method requires the 'imagerotate' function which is only available in the bundled version of GD.", array(__METHOD__)));
+ }
+
+ // Redefine vars
+ $degrees = (int) $degrees;
+
+ // Get image width and height
+ $width = imagesx($this->image);
+ $height = imagesy($this->image);
+
+ // Allocate a color for an image
+ $transparent = imagecolorallocatealpha($this->image, 0, 0, 0, 127);
+
+ // Rotate gif image
+ if ($this->image_info['type'] === IMAGETYPE_GIF) {
+
+ // Create a new true color image
+ $temp = imagecreatetruecolor($width, $height);
+
+ // Flood fill
+ imagefill($temp, 0, 0, $transparent);
+
+ // Copy part of an image
+ imagecopy($temp, $this->image, 0, 0, 0, 0, $width, $height);
+
+ // Destroy an image
+ imagedestroy($this->image);
+
+ // Save temp image
+ $this->image = $temp;
+ }
+
+ // Rotate an image with a given angle
+ $this->image = imagerotate($this->image, (360 - $degrees), $transparent);
+
+ // Define a color as transparent
+ imagecolortransparent($this->image, $transparent);
+
+ return $this;
+ }
+
+
+ /**
+ * Adds a border to the image.
+ *
+ *
+ * Image::factory('original.png')->border('#000', 5)->save('edited.png');
+ *
+ *
+ * @param string $color Hex code for the color
+ * @param integer $thickness Thickness of the frame in pixels
+ * @return Image
+ */
+ public function border($color = '#000', $thickness = 5) {
+
+ // Redefine vars
+ $color = (string) $color;
+ $thickness = (int) $thickness;
+
+ // Get image width and height
+ $width = imagesx($this->image);
+ $height = imagesy($this->image);
+
+ // Creates a color based on a hex value
+ $color = $this->createColor($color);
+
+ // Create border
+ for ($i = 0; $i < $thickness; $i++) {
+
+ if ($i < 0) {
+
+ $x = $width + 1;
+ $y = $hidth + 1;
+
+ } else {
+
+ $x = --$width;
+ $y = --$height;
+
+ }
+
+ imagerectangle($this->image, $i, $i, $x, $y, $color);
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * Save image
+ *
+ *
+ * Image::factory('original.png')->save('edited.png');
+ *
+ *
+ * @param string $dest Desitination location of the file
+ * @param integer $quality Image quality. Default is 100
+ * @return Image
+ */
+ public function save($file, $quality = 100) {
+
+ // Redefine vars
+ $file = (string) $file;
+ $quality = (int) $quality;
+
+ $path_info = pathinfo($file);
+
+ if ( ! is_writable($path_info['dirname'])) {
+ throw new RuntimeException(vsprintf("%s(): '%s' is not writable.", array(__METHOD__, $path_info['dirname'])));
+ }
+
+ // Make sure that quality is between 0 and 100
+ $quality = max(min((int) $quality, 100), 0);
+
+ // Save image
+ switch ($path_info['extension']) {
+ case 'jpg':
+ case 'jpeg':
+ imagejpeg($this->image, $file, $quality);
+ break;
+ case 'gif':
+ imagegif($this->image, $file);
+ break;
+ case 'png':
+ imagealphablending($this->image, true);
+ imagesavealpha($this->image, true);
+ imagepng($this->image, $file, (9 - (round(($quality / 100) * 9))));
+ break;
+ default:
+ throw new RuntimeException(vsprintf("%s(): Unable to save to '%s'. Unsupported image format.", array(__METHOD__, $path_info['extension'])));
+ }
+
+ // Return Image
+ return $this;
+ }
+
+
+ /**
+ * Destructor
+ */
+ public function __destruct() {
+ imagedestroy($this->image);
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/inflector.php b/monstra/helpers/inflector.php
new file mode 100644
index 0000000..bb5cfc7
--- /dev/null
+++ b/monstra/helpers/inflector.php
@@ -0,0 +1,238 @@
+ '\1\2en', // ox
+ '/([m|l])ouse$/' => '\1ice', // mouse, louse
+ '/(matr|vert|ind)ix|ex$/' => '\1ices', // matrix, vertex, index
+ '/(x|ch|ss|sh)$/' => '\1es', // search, switch, fix, box, process, address
+ '/([^aeiouy]|qu)y$/' => '\1ies', // query, ability, agency
+ '/(hive)$/' => '\1s', // archive, hive
+ '/(?:([^f])fe|([lr])f)$/' => '\1\2ves', // half, safe, wife
+ '/sis$/' => 'ses', // basis, diagnosis
+ '/([ti])um$/' => '\1a', // datum, medium
+ '/(p)erson$/' => '\1eople', // person, salesperson
+ '/(m)an$/' => '\1en', // man, woman, spokesman
+ '/(c)hild$/' => '\1hildren', // child
+ '/(buffal|tomat)o$/' => '\1\2oes', // buffalo, tomato
+ '/(bu|campu)s$/' => '\1\2ses', // bus, campus
+ '/(alias|status|virus)$/' => '\1es', // alias
+ '/(octop)us$/' => '\1i', // octopus
+ '/(ax|cris|test)is$/' => '\1es', // axis, crisis
+ '/s$/' => 's', // no change (compatibility)
+ '/$/' => 's',
+ );
+
+
+ /**
+ * Singular rules
+ *
+ * @var array
+ */
+ protected static $singular_rules = array(
+ '/(matr)ices$/' => '\1ix',
+ '/(vert|ind)ices$/' => '\1ex',
+ '/^(ox)en/' => '\1',
+ '/(alias)es$/' => '\1',
+ '/([octop|vir])i$/' => '\1us',
+ '/(cris|ax|test)es$/' => '\1is',
+ '/(shoe)s$/' => '\1',
+ '/(o)es$/' => '\1',
+ '/(bus|campus)es$/' => '\1',
+ '/([m|l])ice$/' => '\1ouse',
+ '/(x|ch|ss|sh)es$/' => '\1',
+ '/(m)ovies$/' => '\1\2ovie',
+ '/(s)eries$/' => '\1\2eries',
+ '/([^aeiouy]|qu)ies$/' => '\1y',
+ '/([lr])ves$/' => '\1f',
+ '/(tive)s$/' => '\1',
+ '/(hive)s$/' => '\1',
+ '/([^f])ves$/' => '\1fe',
+ '/(^analy)ses$/' => '\1sis',
+ '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/' => '\1\2sis',
+ '/([ti])a$/' => '\1um',
+ '/(p)eople$/' => '\1\2erson',
+ '/(m)en$/' => '\1an',
+ '/(s)tatuses$/' => '\1\2tatus',
+ '/(c)hildren$/' => '\1\2hild',
+ '/(n)ews$/' => '\1\2ews',
+ '/([^us])s$/' => '\1',
+ );
+
+
+ /**
+ * Protected constructor since this is a static class.
+ *
+ * @access protected
+ */
+ protected function __construct() {
+ // Nothing here
+ }
+
+
+ /**
+ * Returns a camelized string from a string using underscore syntax.
+ *
+ *
+ * // "some_text_here" becomes "SomeTextHere"
+ * echo Inflector::camelize('some_text_here');
+ *
+ *
+ * @param string $string Word to camelize.
+ * @return string Camelized word.
+ */
+ public static function camelize($string) {
+
+ // Redefine vars
+ $string = (string) $string;
+
+ return str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
+ }
+
+
+ /**
+ * Returns a string using underscore syntax from a camelized string.
+ *
+ *
+ * // "SomeTextHere" becomes "some_text_here"
+ * echo Inflector::underscore('SomeTextHere');
+ *
+ *
+ * @param string $string CamelCased word
+ * @return string Underscored version of the $string
+ */
+ public static function underscore($string) {
+
+ // Redefine vars
+ $string = (string) $string;
+
+ return strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $string));
+ }
+
+
+ /**
+ * Returns a humanized string from a string using underscore syntax.
+ *
+ *
+ * // "some_text_here" becomes "Some text here"
+ * echo Inflector::humanize('some_text_here');
+ *
+ *
+ * @param string $string String using underscore syntax.
+ * @return string Humanized version of the $string
+ */
+ public static function humanize($string) {
+
+ // Redefine vars
+ $string = (string) $string;
+
+ return ucfirst(strtolower(str_replace('_', ' ', $string)));
+ }
+
+
+ /**
+ * Returns ordinalize number.
+ *
+ *
+ * // 1 becomes 1st
+ * echo Inflector::ordinalize(1);
+ *
+ *
+ * @param integer $number Number to ordinalize
+ * @return string
+ */
+ public static function ordinalize($number) {
+
+ if ( ! is_numeric($number)) {
+ return $number;
+ }
+
+ if (in_array(($number % 100), range(11, 13))) {
+ return $number . 'th';
+ } else {
+ switch ($number % 10) {
+ case 1: return $number . 'st'; break;
+ case 2: return $number . 'nd'; break;
+ case 3: return $number . 'rd'; break;
+ default: return $number . 'th'; break;
+ }
+ }
+ }
+
+
+ /**
+ * Returns the plural version of the given word
+ *
+ *
+ * echo Inflector::pluralize('cat');
+ *
+ *
+ * @param string $word Word to pluralize
+ * @return string
+ */
+ public static function pluralize($word) {
+
+ $result = (string) $word;
+
+ foreach (Inflector::$plural_rules as $rule => $replacement) {
+ if (preg_match($rule, $result)) {
+ $result = preg_replace($rule, $replacement, $result);
+ break;
+ }
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Returns the singular version of the given word
+ *
+ *
+ * echo Inflector::singularize('cats');
+ *
+ *
+ * @param string $word Word to singularize
+ * @return string
+ */
+ public static function singularize($word) {
+
+ $result = (string) $word;
+
+ foreach (Inflector::$singular_rules as $rule => $replacement) {
+ if (preg_match($rule, $result)) {
+ $result = preg_replace($rule, $replacement, $result);
+ break;
+ }
+ }
+
+ return $result;
+ }
+
+ }
diff --git a/monstra/helpers/minify.php b/monstra/helpers/minify.php
new file mode 100644
index 0000000..c90a292
--- /dev/null
+++ b/monstra/helpers/minify.php
@@ -0,0 +1,98 @@
+
+ * echo Minify::html($buffer);
+ *
+ *
+ * @param string $buffer html
+ * @return string
+ */
+ public static function html($buffer) {
+ return preg_replace('/^\\s+|\\s+$/m', '', $buffer);
+ }
+
+
+ /**
+ * Minify css
+ *
+ *
+ * echo Minify::css($buffer);
+ *
+ *
+ * @param string $buffer css
+ * @return string
+ */
+ public static function css($buffer) {
+
+ // Remove comments
+ $buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer);
+
+ // Remove tabs, spaces, newlines, etc.
+ $buffer = str_replace(array("\r\n", "\r", "\n", "\t", ' ', ' ', ' '), '', $buffer);
+
+ // Preserve empty comment after '>' http://www.webdevout.net/css-hacks#in_css-selectors
+ $buffer = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $buffer);
+
+ // Preserve empty comment between property and value
+ // http://css-discuss.incutio.com/?page=BoxModelHack
+ $buffer = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $buffer);
+ $buffer = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $buffer);
+
+ // Remove ws around { } and last semicolon in declaration block
+ $buffer = preg_replace('/\\s*{\\s*/', '{', $buffer);
+ $buffer = preg_replace('/;?\\s*}\\s*/', '}', $buffer);
+
+ // Remove ws surrounding semicolons
+ $buffer = preg_replace('/\\s*;\\s*/', ';', $buffer);
+
+ // Remove ws around urls
+ $buffer = preg_replace('/url\\(\\s*([^\\)]+?)\\s*\\)/x', 'url($1)', $buffer);
+
+ // Remove ws between rules and colons
+ $buffer = preg_replace('/\\s*([{;])\\s*([\\*_]?[\\w\\-]+)\\s*:\\s*(\\b|[#\'"])/x', '$1$2:$3', $buffer);
+
+ // Minimize hex colors
+ $buffer = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i', '$1#$2$3$4$5', $buffer);
+
+ // Replace any ws involving newlines with a single newline
+ $buffer = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $buffer);
+
+ return $buffer;
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/notification.php b/monstra/helpers/notification.php
new file mode 100644
index 0000000..34bc064
--- /dev/null
+++ b/monstra/helpers/notification.php
@@ -0,0 +1,150 @@
+
+ * echo Notification::get('success');
+ * echo Notification::get('errors');
+ *
+ *
+ * @param string $key Variable name
+ * @return mixed
+ */
+ public static function get($key) {
+
+ // Redefine arguments
+ $key = (string) $key;
+
+ // Return specific variable from the Notifications array
+ return isset(Notification::$notifications[$key]) ? Notification::$notifications[$key] : null;
+ }
+
+
+ /**
+ * Adds specific variable to the Notifications array.
+ *
+ *
+ * Notification::set('success', 'Data has been saved with success!');
+ * Notification::set('errors', 'Data not saved!');
+ *
+ *
+ * @param string $key Variable name
+ * @param mixed $value Variable value
+ */
+ public static function set($key, $value) {
+
+ // Redefine arguments
+ $key = (string) $key;
+
+ // Save specific variable to the Notifications array
+ $_SESSION[Notification::SESSION_KEY][$key] = $value;
+ }
+
+
+ /**
+ * Adds specific variable to the Notifications array for current page.
+ *
+ *
+ * Notification::setNow('success', 'Success!');
+ *
+ *
+ * @param string $var Variable name
+ * @param mixed $value Variable value
+ */
+ public static function setNow($key, $value) {
+
+ // Redefine arguments
+ $key = (string) $key;
+
+ // Save specific variable for current page only
+ Notification::$notifications[$key] = $value;
+ }
+
+
+ /**
+ * Clears the Notifications array.
+ *
+ *
+ * Notification::clean();
+ *
+ *
+ * Data that previous pages stored will not be deleted, just the data that
+ * this page stored itself.
+ */
+ public static function clean() {
+ $_SESSION[Notification::SESSION_KEY] = array();
+ }
+
+
+ /**
+ * Initializes the Notification service.
+ *
+ *
+ * Notification::init();
+ *
+ *
+ * This will read notification/flash data from the $_SESSION variable and load it into
+ * the $this->previous array.
+ */
+ public static function init() {
+
+ // Get notification/flash data...
+
+ if ( ! empty($_SESSION[Notification::SESSION_KEY]) && is_array($_SESSION[Notification::SESSION_KEY])) {
+ Notification::$notifications = $_SESSION[Notification::SESSION_KEY];
+ }
+
+ $_SESSION[Notification::SESSION_KEY] = array();
+
+ }
+
+ }
diff --git a/monstra/helpers/number.php b/monstra/helpers/number.php
new file mode 100644
index 0000000..389a367
--- /dev/null
+++ b/monstra/helpers/number.php
@@ -0,0 +1,213 @@
+
+ * echo Number::byteFormat(10000);
+ *
+ *
+ * @param integer $size Data to convert
+ * @return string
+ */
+ public static function byteFormat($size) {
+
+ // Redefine vars
+ $size = (int) $size;
+
+ $unit = array('b', 'kb', 'mb', 'gb', 'tb', 'pb');
+
+ return @round($size/pow(1024, ($i=floor(log($size, 1024)))), 2).' '.$unit[$i];
+ }
+
+
+ /**
+ * Converts a number into a more readable human-type number.
+ *
+ *
+ * echo Number::quantity(7000); // 7K
+ * echo Number::quantity(7500); // 8K
+ * echo Number::quantity(7500, 1); // 7.5K
+ *
+ *
+ * @param integer $num Num to convert
+ * @param integer $decimals Decimals
+ * @return string
+ */
+ public static function quantity($num, $decimals = 0) {
+
+ // Redefine vars
+ $num = (int) $num;
+ $decimals = (int) $decimals;
+
+ if ($num >= 1000 && $num < 1000000) {
+ return sprintf('%01.'.$decimals.'f', (sprintf('%01.0f', $num) / 1000)).'K';
+ } elseif ($num >= 1000000 && $num < 1000000000) {
+ return sprintf('%01.'.$decimals.'f', (sprintf('%01.0f', $num) / 1000000)).'M';
+ } elseif ($num >= 1000000000) {
+ return sprintf('%01.'.$decimals.'f', (sprintf('%01.0f', $num) / 1000000000)).'B';
+ }
+
+ return $num;
+ }
+
+
+ /**
+ * Checks if the value is between the minimum and maximum (min & max included).
+ *
+ *
+ * if (Number::between(2, 10, 5)) {
+ * // do something...
+ * }
+ *
+ *
+ * @param float $minimum The minimum.
+ * @param float $maximum The maximum.
+ * @param float $value The value to validate.
+ * @return boolean
+ */
+ public static function between($minimum, $maximum, $value) {
+ return ((float) $value >= (float) $minimum && (float) $value <= (float) $maximum);
+ }
+
+
+ /**
+ * Checks the value for an even number.
+ *
+ *
+ * if (Number::even(2)) {
+ * // do something...
+ * }
+ *
+ *
+ * @param integer $value The value to validate.
+ * @return boolean
+ */
+ public static function even($value) {
+ return (((int) $value % 2) == 0);
+ }
+
+
+ /**
+ * Checks if the value is greather than a given minimum.
+ *
+ *
+ * if (Number::greaterThan(2, 10)) {
+ * // do something...
+ * }
+ *
+ *
+ * @param float $minimum The minimum as a float.
+ * @param float $value The value to validate.
+ * @return boolean
+ */
+ public static function greaterThan($minimum, $value) {
+ return ((float) $value > (float) $minimum);
+ }
+
+
+ /**
+ * Checks if the value is smaller than a given maximum.
+ *
+ *
+ * if (Number::smallerThan(2, 10)) {
+ * // do something...
+ * }
+ *
+ *
+ * @param integer $maximum The maximum.
+ * @param integer $value The value to validate.
+ * @return boolean
+ */
+ public static function smallerThan($maximum, $value) {
+ return ((int) $value < (int) $maximum);
+ }
+
+
+ /**
+ * Checks if the value is not greater than or equal a given maximum.
+ *
+ *
+ * if (Number::maximum(2, 10)) {
+ * // do something...
+ * }
+ *
+ *
+ * @param integer $maximum The maximum.
+ * @param integer $value The value to validate.
+ * @return boolean
+ */
+ public static function maximum($maximum, $value) {
+ return ((int) $value <= (int) $maximum);
+ }
+
+
+ /**
+ * Checks if the value is greater than or equal to a given minimum.
+ *
+ *
+ * if (Number::minimum(2, 10)) {
+ * // do something...
+ * }
+ *
+ *
+ * @param integer $minimum The minimum.
+ * @param integer $value The value to validate.
+ * @return boolean
+ */
+ public static function minimum($minimum, $value) {
+ return ((int) $value >= (int) $minimum);
+ }
+
+
+ /**
+ * Checks the value for an odd number.
+ *
+ *
+ * if (Number::odd(2)) {
+ * // do something...
+ * }
+ *
+ *
+ * @param integer $value The value to validate.
+ * @return boolean
+ */
+ public static function odd($value) {
+ return ! Number::even((int) $value);
+ }
+
+ }
+
+
diff --git a/monstra/helpers/orm.php b/monstra/helpers/orm.php
new file mode 100644
index 0000000..005b790
--- /dev/null
+++ b/monstra/helpers/orm.php
@@ -0,0 +1,1141 @@
+ 'sqlite::memory:',
+ 'id_column' => 'id',
+ 'id_column_overrides' => array(),
+ 'error_mode' => PDO::ERRMODE_EXCEPTION,
+ 'username' => null,
+ 'password' => null,
+ 'driver_options' => null,
+ 'identifier_quote_character' => null, // if this is null, will be autodetected
+ 'logging' => false,
+ 'caching' => false,
+ );
+
+ // Database connection, instance of the PDO class
+ protected static $_db;
+
+ // Last query run, only populated if logging is enabled
+ protected static $_last_query;
+
+ // Log of all queries run, only populated if logging is enabled
+ protected static $_query_log = array();
+
+ // Query cache, only used if query caching is enabled
+ protected static $_query_cache = array();
+
+ // --------------------------- //
+ // --- INSTANCE PROPERTIES --- //
+ // --------------------------- //
+
+ // The name of the table the current ORM instance is associated with
+ protected $_table_name;
+
+ // Alias for the table to be used in SELECT queries
+ protected $_table_alias = null;
+
+ // Values to be bound to the query
+ protected $_values = array();
+
+ // Columns to select in the result
+ protected $_result_columns = array('*');
+
+ // Are we using the default result column or have these been manually changed?
+ protected $_using_default_result_columns = true;
+
+ // Join sources
+ protected $_join_sources = array();
+
+ // Should the query include a DISTINCT keyword?
+ protected $_distinct = false;
+
+ // Is this a raw query?
+ protected $_is_raw_query = false;
+
+ // The raw query
+ protected $_raw_query = '';
+
+ // The raw query parameters
+ protected $_raw_parameters = array();
+
+ // Array of WHERE clauses
+ protected $_where_conditions = array();
+
+ // LIMIT
+ protected $_limit = null;
+
+ // OFFSET
+ protected $_offset = null;
+
+ // ORDER BY
+ protected $_order_by = array();
+
+ // GROUP BY
+ protected $_group_by = array();
+
+ // The data for a hydrated instance of the class
+ protected $_data = array();
+
+ // Fields that have been modified during the
+ // lifetime of the object
+ protected $_dirty_fields = array();
+
+ // Is this a new object (has create() been called)?
+ protected $_is_new = false;
+
+ // Name of the column to use as the primary key for
+ // this instance only. Overrides the config settings.
+ protected $_instance_id_column = null;
+
+ // ---------------------- //
+ // --- STATIC METHODS --- //
+ // ---------------------- //
+
+ /**
+ * Pass configuration settings to the class in the form of
+ * key/value pairs. As a shortcut, if the second argument
+ * is omitted, the setting is assumed to be the DSN string
+ * used by PDO to connect to the database. Often, this
+ * will be the only configuration required to use Idiorm.
+ */
+ public static function configure($key, $value=null) {
+ // Shortcut: If only one argument is passed,
+ // assume it's a connection string
+ if (is_null($value)) {
+ $value = $key;
+ $key = 'connection_string';
+ }
+ self::$_config[$key] = $value;
+ }
+
+ /**
+ * Despite its slightly odd name, this is actually the factory
+ * method used to acquire instances of the class. It is named
+ * this way for the sake of a readable interface, ie
+ * ORM::for_table('table_name')->find_one()-> etc. As such,
+ * this will normally be the first method called in a chain.
+ */
+ public static function for_table($table_name) {
+ self::_setup_db();
+ return new self($table_name);
+ }
+
+ /**
+ * Set up the database connection used by the class.
+ */
+ protected static function _setup_db() {
+ if (!is_object(self::$_db)) {
+ $connection_string = self::$_config['connection_string'];
+ $username = self::$_config['username'];
+ $password = self::$_config['password'];
+ $driver_options = self::$_config['driver_options'];
+ $db = new PDO($connection_string, $username, $password, $driver_options);
+ $db->setAttribute(PDO::ATTR_ERRMODE, self::$_config['error_mode']);
+ self::set_db($db);
+ }
+ }
+
+ /**
+ * Set the PDO object used by Idiorm to communicate with the database.
+ * This is public in case the ORM should use a ready-instantiated
+ * PDO object as its database connection.
+ */
+ public static function set_db($db) {
+ self::$_db = $db;
+ self::_setup_identifier_quote_character();
+ }
+
+ /**
+ * Detect and initialise the character used to quote identifiers
+ * (table names, column names etc). If this has been specified
+ * manually using ORM::configure('identifier_quote_character', 'some-char'),
+ * this will do nothing.
+ */
+ public static function _setup_identifier_quote_character() {
+ if (is_null(self::$_config['identifier_quote_character'])) {
+ self::$_config['identifier_quote_character'] = self::_detect_identifier_quote_character();
+ }
+ }
+
+ /**
+ * Return the correct character used to quote identifiers (table
+ * names, column names etc) by looking at the driver being used by PDO.
+ */
+ protected static function _detect_identifier_quote_character() {
+ switch(self::$_db->getAttribute(PDO::ATTR_DRIVER_NAME)) {
+ case 'pgsql':
+ case 'sqlsrv':
+ case 'dblib':
+ case 'mssql':
+ case 'sybase':
+ return '"';
+ case 'mysql':
+ case 'sqlite':
+ case 'sqlite2':
+ default:
+ return '`';
+ }
+ }
+
+ /**
+ * Returns the PDO instance used by the the ORM to communicate with
+ * the database. This can be called if any low-level DB access is
+ * required outside the class.
+ */
+ public static function get_db() {
+ self::_setup_db(); // required in case this is called before Idiorm is instantiated
+ return self::$_db;
+ }
+
+ /**
+ * Add a query to the internal query log. Only works if the
+ * 'logging' config option is set to true.
+ *
+ * This works by manually binding the parameters to the query - the
+ * query isn't executed like this (PDO normally passes the query and
+ * parameters to the database which takes care of the binding) but
+ * doing it this way makes the logged queries more readable.
+ */
+ protected static function _log_query($query, $parameters) {
+ // If logging is not enabled, do nothing
+ if (!self::$_config['logging']) {
+ return false;
+ }
+
+ if (count($parameters) > 0) {
+ // Escape the parameters
+ $parameters = array_map(array(self::$_db, 'quote'), $parameters);
+
+ // Replace placeholders in the query for vsprintf
+ $query = str_replace("?", "%s", $query);
+
+ // Replace the question marks in the query with the parameters
+ $bound_query = vsprintf($query, $parameters);
+ } else {
+ $bound_query = $query;
+ }
+
+ self::$_last_query = $bound_query;
+ self::$_query_log[] = $bound_query;
+ return true;
+ }
+
+ /**
+ * Get the last query executed. Only works if the
+ * 'logging' config option is set to true. Otherwise
+ * this will return null.
+ */
+ public static function get_last_query() {
+ return self::$_last_query;
+ }
+
+ /**
+ * Get an array containing all the queries run up to
+ * now. Only works if the 'logging' config option is
+ * set to true. Otherwise returned array will be empty.
+ */
+ public static function get_query_log() {
+ return self::$_query_log;
+ }
+
+ // ------------------------ //
+ // --- INSTANCE METHODS --- //
+ // ------------------------ //
+
+ /**
+ * "Private" constructor; shouldn't be called directly.
+ * Use the ORM::for_table factory method instead.
+ */
+ protected function __construct($table_name, $data=array()) {
+ $this->_table_name = $table_name;
+ $this->_data = $data;
+ }
+
+ /**
+ * Create a new, empty instance of the class. Used
+ * to add a new row to your database. May optionally
+ * be passed an associative array of data to populate
+ * the instance. If so, all fields will be flagged as
+ * dirty so all will be saved to the database when
+ * save() is called.
+ */
+ public function create($data=null) {
+ $this->_is_new = true;
+ if (!is_null($data)) {
+ return $this->hydrate($data)->force_all_dirty();
+ }
+ return $this;
+ }
+
+ /**
+ * Specify the ID column to use for this instance or array of instances only.
+ * This overrides the id_column and id_column_overrides settings.
+ *
+ * This is mostly useful for libraries built on top of Idiorm, and will
+ * not normally be used in manually built queries. If you don't know why
+ * you would want to use this, you should probably just ignore it.
+ */
+ public function use_id_column($id_column) {
+ $this->_instance_id_column = $id_column;
+ return $this;
+ }
+
+ /**
+ * Create an ORM instance from the given row (an associative
+ * array of data fetched from the database)
+ */
+ protected function _create_instance_from_row($row) {
+ $instance = self::for_table($this->_table_name);
+ $instance->use_id_column($this->_instance_id_column);
+ $instance->hydrate($row);
+ return $instance;
+ }
+
+ /**
+ * Tell the ORM that you are expecting a single result
+ * back from your query, and execute it. Will return
+ * a single instance of the ORM class, or false if no
+ * rows were returned.
+ * As a shortcut, you may supply an ID as a parameter
+ * to this method. This will perform a primary key
+ * lookup on the table.
+ */
+ public function find_one($id=null) {
+ if (!is_null($id)) {
+ $this->where_id_is($id);
+ }
+ $this->limit(1);
+ $rows = $this->_run();
+
+ if (empty($rows)) {
+ return false;
+ }
+
+ return $this->_create_instance_from_row($rows[0]);
+ }
+
+ /**
+ * Tell the ORM that you are expecting multiple results
+ * from your query, and execute it. Will return an array
+ * of instances of the ORM class, or an empty array if
+ * no rows were returned.
+ */
+ public function find_many() {
+ $rows = $this->_run();
+ return array_map(array($this, '_create_instance_from_row'), $rows);
+ }
+
+ /**
+ * Tell the ORM that you wish to execute a COUNT query.
+ * Will return an integer representing the number of
+ * rows returned.
+ */
+ public function count() {
+ $this->select_expr('COUNT(*)', 'count');
+ $result = $this->find_one();
+ return ($result !== false && isset($result->count)) ? (int) $result->count : 0;
+ }
+
+ /**
+ * This method can be called to hydrate (populate) this
+ * instance of the class from an associative array of data.
+ * This will usually be called only from inside the class,
+ * but it's public in case you need to call it directly.
+ */
+ public function hydrate($data=array()) {
+ $this->_data = $data;
+ return $this;
+ }
+
+ /**
+ * Force the ORM to flag all the fields in the $data array
+ * as "dirty" and therefore update them when save() is called.
+ */
+ public function force_all_dirty() {
+ $this->_dirty_fields = $this->_data;
+ return $this;
+ }
+
+ /**
+ * Perform a raw query. The query should contain placeholders,
+ * in either named or question mark style, and the parameters
+ * should be an array of values which will be bound to the
+ * placeholders in the query. If this method is called, all
+ * other query building methods will be ignored.
+ */
+ public function raw_query($query, $parameters) {
+ $this->_is_raw_query = true;
+ $this->_raw_query = $query;
+ $this->_raw_parameters = $parameters;
+ return $this;
+ }
+
+ /**
+ * Add an alias for the main table to be used in SELECT queries
+ */
+ public function table_alias($alias) {
+ $this->_table_alias = $alias;
+ return $this;
+ }
+
+ /**
+ * Internal method to add an unquoted expression to the set
+ * of columns returned by the SELECT query. The second optional
+ * argument is the alias to return the expression as.
+ */
+ protected function _add_result_column($expr, $alias=null) {
+ if (!is_null($alias)) {
+ $expr .= " AS " . $this->_quote_identifier($alias);
+ }
+
+ if ($this->_using_default_result_columns) {
+ $this->_result_columns = array($expr);
+ $this->_using_default_result_columns = false;
+ } else {
+ $this->_result_columns[] = $expr;
+ }
+ return $this;
+ }
+
+ /**
+ * Add a column to the list of columns returned by the SELECT
+ * query. This defaults to '*'. The second optional argument is
+ * the alias to return the column as.
+ */
+ public function select($column, $alias=null) {
+ $column = $this->_quote_identifier($column);
+ return $this->_add_result_column($column, $alias);
+ }
+
+ /**
+ * Add an unquoted expression to the list of columns returned
+ * by the SELECT query. The second optional argument is
+ * the alias to return the column as.
+ */
+ public function select_expr($expr, $alias=null) {
+ return $this->_add_result_column($expr, $alias);
+ }
+
+ /**
+ * Add a DISTINCT keyword before the list of columns in the SELECT query
+ */
+ public function distinct() {
+ $this->_distinct = true;
+ return $this;
+ }
+
+ /**
+ * Internal method to add a JOIN source to the query.
+ *
+ * The join_operator should be one of INNER, LEFT OUTER, CROSS etc - this
+ * will be prepended to JOIN.
+ *
+ * The table should be the name of the table to join to.
+ *
+ * The constraint may be either a string or an array with three elements. If it
+ * is a string, it will be compiled into the query as-is, with no escaping. The
+ * recommended way to supply the constraint is as an array with three elements:
+ *
+ * first_column, operator, second_column
+ *
+ * Example: array('user.id', '=', 'profile.user_id')
+ *
+ * will compile to
+ *
+ * ON `user`.`id` = `profile`.`user_id`
+ *
+ * The final (optional) argument specifies an alias for the joined table.
+ */
+ protected function _add_join_source($join_operator, $table, $constraint, $table_alias=null) {
+
+ $join_operator = trim("{$join_operator} JOIN");
+
+ $table = $this->_quote_identifier($table);
+
+ // Add table alias if present
+ if (!is_null($table_alias)) {
+ $table_alias = $this->_quote_identifier($table_alias);
+ $table .= " {$table_alias}";
+ }
+
+ // Build the constraint
+ if (is_array($constraint)) {
+ list($first_column, $operator, $second_column) = $constraint;
+ $first_column = $this->_quote_identifier($first_column);
+ $second_column = $this->_quote_identifier($second_column);
+ $constraint = "{$first_column} {$operator} {$second_column}";
+ }
+
+ $this->_join_sources[] = "{$join_operator} {$table} ON {$constraint}";
+ return $this;
+ }
+
+ /**
+ * Add a simple JOIN source to the query
+ */
+ public function join($table, $constraint, $table_alias=null) {
+ return $this->_add_join_source("", $table, $constraint, $table_alias);
+ }
+
+ /**
+ * Add an INNER JOIN souce to the query
+ */
+ public function inner_join($table, $constraint, $table_alias=null) {
+ return $this->_add_join_source("INNER", $table, $constraint, $table_alias);
+ }
+
+ /**
+ * Add a LEFT OUTER JOIN souce to the query
+ */
+ public function left_outer_join($table, $constraint, $table_alias=null) {
+ return $this->_add_join_source("LEFT OUTER", $table, $constraint, $table_alias);
+ }
+
+ /**
+ * Add an RIGHT OUTER JOIN souce to the query
+ */
+ public function right_outer_join($table, $constraint, $table_alias=null) {
+ return $this->_add_join_source("RIGHT OUTER", $table, $constraint, $table_alias);
+ }
+
+ /**
+ * Add an FULL OUTER JOIN souce to the query
+ */
+ public function full_outer_join($table, $constraint, $table_alias=null) {
+ return $this->_add_join_source("FULL OUTER", $table, $constraint, $table_alias);
+ }
+
+ /**
+ * Internal method to add a WHERE condition to the query
+ */
+ protected function _add_where($fragment, $values=array()) {
+ if (!is_array($values)) {
+ $values = array($values);
+ }
+ $this->_where_conditions[] = array(
+ self::WHERE_FRAGMENT => $fragment,
+ self::WHERE_VALUES => $values,
+ );
+ return $this;
+ }
+
+ /**
+ * Helper method to compile a simple COLUMN SEPARATOR VALUE
+ * style WHERE condition into a string and value ready to
+ * be passed to the _add_where method. Avoids duplication
+ * of the call to _quote_identifier
+ */
+ protected function _add_simple_where($column_name, $separator, $value) {
+ $column_name = $this->_quote_identifier($column_name);
+ return $this->_add_where("{$column_name} {$separator} ?", $value);
+ }
+
+ /**
+ * Return a string containing the given number of question marks,
+ * separated by commas. Eg "?, ?, ?"
+ */
+ protected function _create_placeholders($number_of_placeholders) {
+ return join(", ", array_fill(0, $number_of_placeholders, "?"));
+ }
+
+ /**
+ * Add a WHERE column = value clause to your query. Each time
+ * this is called in the chain, an additional WHERE will be
+ * added, and these will be ANDed together when the final query
+ * is built.
+ */
+ public function where($column_name, $value) {
+ return $this->where_equal($column_name, $value);
+ }
+
+ /**
+ * More explicitly named version of for the where() method.
+ * Can be used if preferred.
+ */
+ public function where_equal($column_name, $value) {
+ return $this->_add_simple_where($column_name, '=', $value);
+ }
+
+ /**
+ * Add a WHERE column != value clause to your query.
+ */
+ public function where_not_equal($column_name, $value) {
+ return $this->_add_simple_where($column_name, '!=', $value);
+ }
+
+ /**
+ * Special method to query the table by its primary key
+ */
+ public function where_id_is($id) {
+ return $this->where($this->_get_id_column_name(), $id);
+ }
+
+ /**
+ * Add a WHERE ... LIKE clause to your query.
+ */
+ public function where_like($column_name, $value) {
+ return $this->_add_simple_where($column_name, 'LIKE', $value);
+ }
+
+ /**
+ * Add where WHERE ... NOT LIKE clause to your query.
+ */
+ public function where_not_like($column_name, $value) {
+ return $this->_add_simple_where($column_name, 'NOT LIKE', $value);
+ }
+
+ /**
+ * Add a WHERE ... > clause to your query
+ */
+ public function where_gt($column_name, $value) {
+ return $this->_add_simple_where($column_name, '>', $value);
+ }
+
+ /**
+ * Add a WHERE ... < clause to your query
+ */
+ public function where_lt($column_name, $value) {
+ return $this->_add_simple_where($column_name, '<', $value);
+ }
+
+ /**
+ * Add a WHERE ... >= clause to your query
+ */
+ public function where_gte($column_name, $value) {
+ return $this->_add_simple_where($column_name, '>=', $value);
+ }
+
+ /**
+ * Add a WHERE ... <= clause to your query
+ */
+ public function where_lte($column_name, $value) {
+ return $this->_add_simple_where($column_name, '<=', $value);
+ }
+
+ /**
+ * Add a WHERE ... IN clause to your query
+ */
+ public function where_in($column_name, $values) {
+ $column_name = $this->_quote_identifier($column_name);
+ $placeholders = $this->_create_placeholders(count($values));
+ return $this->_add_where("{$column_name} IN ({$placeholders})", $values);
+ }
+
+ /**
+ * Add a WHERE ... NOT IN clause to your query
+ */
+ public function where_not_in($column_name, $values) {
+ $column_name = $this->_quote_identifier($column_name);
+ $placeholders = $this->_create_placeholders(count($values));
+ return $this->_add_where("{$column_name} NOT IN ({$placeholders})", $values);
+ }
+
+ /**
+ * Add a WHERE column IS NULL clause to your query
+ */
+ public function where_null($column_name) {
+ $column_name = $this->_quote_identifier($column_name);
+ return $this->_add_where("{$column_name} IS NULL");
+ }
+
+ /**
+ * Add a WHERE column IS NOT NULL clause to your query
+ */
+ public function where_not_null($column_name) {
+ $column_name = $this->_quote_identifier($column_name);
+ return $this->_add_where("{$column_name} IS NOT NULL");
+ }
+
+ /**
+ * Add a raw WHERE clause to the query. The clause should
+ * contain question mark placeholders, which will be bound
+ * to the parameters supplied in the second argument.
+ */
+ public function where_raw($clause, $parameters=array()) {
+ return $this->_add_where($clause, $parameters);
+ }
+
+ /**
+ * Add a LIMIT to the query
+ */
+ public function limit($limit) {
+ $this->_limit = $limit;
+ return $this;
+ }
+
+ /**
+ * Add an OFFSET to the query
+ */
+ public function offset($offset) {
+ $this->_offset = $offset;
+ return $this;
+ }
+
+ /**
+ * Add an ORDER BY clause to the query
+ */
+ protected function _add_order_by($column_name, $ordering) {
+ $column_name = $this->_quote_identifier($column_name);
+ $this->_order_by[] = "{$column_name} {$ordering}";
+ return $this;
+ }
+
+ /**
+ * Add an ORDER BY column DESC clause
+ */
+ public function order_by_desc($column_name) {
+ return $this->_add_order_by($column_name, 'DESC');
+ }
+
+ /**
+ * Add an ORDER BY column ASC clause
+ */
+ public function order_by_asc($column_name) {
+ return $this->_add_order_by($column_name, 'ASC');
+ }
+
+ /**
+ * Add a column to the list of columns to GROUP BY
+ */
+ public function group_by($column_name) {
+ $column_name = $this->_quote_identifier($column_name);
+ $this->_group_by[] = $column_name;
+ return $this;
+ }
+
+ /**
+ * Build a SELECT statement based on the clauses that have
+ * been passed to this instance by chaining method calls.
+ */
+ protected function _build_select() {
+ // If the query is raw, just set the $this->_values to be
+ // the raw query parameters and return the raw query
+ if ($this->_is_raw_query) {
+ $this->_values = $this->_raw_parameters;
+ return $this->_raw_query;
+ }
+
+ // Build and return the full SELECT statement by concatenating
+ // the results of calling each separate builder method.
+ return $this->_join_if_not_empty(" ", array(
+ $this->_build_select_start(),
+ $this->_build_join(),
+ $this->_build_where(),
+ $this->_build_group_by(),
+ $this->_build_order_by(),
+ $this->_build_limit(),
+ $this->_build_offset(),
+ ));
+ }
+
+ /**
+ * Build the start of the SELECT statement
+ */
+ protected function _build_select_start() {
+ $result_columns = join(', ', $this->_result_columns);
+
+ if ($this->_distinct) {
+ $result_columns = 'DISTINCT ' . $result_columns;
+ }
+
+ $fragment = "SELECT {$result_columns} FROM " . $this->_quote_identifier($this->_table_name);
+
+ if (!is_null($this->_table_alias)) {
+ $fragment .= " " . $this->_quote_identifier($this->_table_alias);
+ }
+ return $fragment;
+ }
+
+ /**
+ * Build the JOIN sources
+ */
+ protected function _build_join() {
+ if (count($this->_join_sources) === 0) {
+ return '';
+ }
+
+ return join(" ", $this->_join_sources);
+ }
+
+ /**
+ * Build the WHERE clause(s)
+ */
+ protected function _build_where() {
+ // If there are no WHERE clauses, return empty string
+ if (count($this->_where_conditions) === 0) {
+ return '';
+ }
+
+ $where_conditions = array();
+ foreach ($this->_where_conditions as $condition) {
+ $where_conditions[] = $condition[self::WHERE_FRAGMENT];
+ $this->_values = array_merge($this->_values, $condition[self::WHERE_VALUES]);
+ }
+
+ return "WHERE " . join(" AND ", $where_conditions);
+ }
+
+ /**
+ * Build GROUP BY
+ */
+ protected function _build_group_by() {
+ if (count($this->_group_by) === 0) {
+ return '';
+ }
+ return "GROUP BY " . join(", ", $this->_group_by);
+ }
+
+ /**
+ * Build ORDER BY
+ */
+ protected function _build_order_by() {
+ if (count($this->_order_by) === 0) {
+ return '';
+ }
+ return "ORDER BY " . join(", ", $this->_order_by);
+ }
+
+ /**
+ * Build LIMIT
+ */
+ protected function _build_limit() {
+ if (!is_null($this->_limit)) {
+ return "LIMIT " . $this->_limit;
+ }
+ return '';
+ }
+
+ /**
+ * Build OFFSET
+ */
+ protected function _build_offset() {
+ if (!is_null($this->_offset)) {
+ return "OFFSET " . $this->_offset;
+ }
+ return '';
+ }
+
+ /**
+ * Wrapper around PHP's join function which
+ * only adds the pieces if they are not empty.
+ */
+ protected function _join_if_not_empty($glue, $pieces) {
+ $filtered_pieces = array();
+ foreach ($pieces as $piece) {
+ if (is_string($piece)) {
+ $piece = trim($piece);
+ }
+ if (!empty($piece)) {
+ $filtered_pieces[] = $piece;
+ }
+ }
+ return join($glue, $filtered_pieces);
+ }
+
+ /**
+ * Quote a string that is used as an identifier
+ * (table names, column names etc). This method can
+ * also deal with dot-separated identifiers eg table.column
+ */
+ protected function _quote_identifier($identifier) {
+ $parts = explode('.', $identifier);
+ $parts = array_map(array($this, '_quote_identifier_part'), $parts);
+ return join('.', $parts);
+ }
+
+ /**
+ * This method performs the actual quoting of a single
+ * part of an identifier, using the identifier quote
+ * character specified in the config (or autodetected).
+ */
+ protected function _quote_identifier_part($part) {
+ if ($part === '*') {
+ return $part;
+ }
+ $quote_character = self::$_config['identifier_quote_character'];
+ return $quote_character . $part . $quote_character;
+ }
+
+ /**
+ * Create a cache key for the given query and parameters.
+ */
+ protected static function _create_cache_key($query, $parameters) {
+ $parameter_string = join(',', $parameters);
+ $key = $query . ':' . $parameter_string;
+ return sha1($key);
+ }
+
+ /**
+ * Check the query cache for the given cache key. If a value
+ * is cached for the key, return the value. Otherwise, return false.
+ */
+ protected static function _check_query_cache($cache_key) {
+ if (isset(self::$_query_cache[$cache_key])) {
+ return self::$_query_cache[$cache_key];
+ }
+ return false;
+ }
+
+ /**
+ * Clear the query cache
+ */
+ public static function clear_cache() {
+ self::$_query_cache = array();
+ }
+
+ /**
+ * Add the given value to the query cache.
+ */
+ protected static function _cache_query_result($cache_key, $value) {
+ self::$_query_cache[$cache_key] = $value;
+ }
+
+ /**
+ * Execute the SELECT query that has been built up by chaining methods
+ * on this class. Return an array of rows as associative arrays.
+ */
+ protected function _run() {
+ $query = $this->_build_select();
+ $caching_enabled = self::$_config['caching'];
+
+ if ($caching_enabled) {
+ $cache_key = self::_create_cache_key($query, $this->_values);
+ $cached_result = self::_check_query_cache($cache_key);
+
+ if ($cached_result !== false) {
+ return $cached_result;
+ }
+ }
+
+ self::_log_query($query, $this->_values);
+ $statement = self::$_db->prepare($query);
+ $statement->execute($this->_values);
+
+ $rows = array();
+ while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
+ $rows[] = $row;
+ }
+
+ if ($caching_enabled) {
+ self::_cache_query_result($cache_key, $rows);
+ }
+
+ return $rows;
+ }
+
+ /**
+ * Return the raw data wrapped by this ORM
+ * instance as an associative array. Column
+ * names may optionally be supplied as arguments,
+ * if so, only those keys will be returned.
+ */
+ public function as_array() {
+ if (func_num_args() === 0) {
+ return $this->_data;
+ }
+ $args = func_get_args();
+ return array_intersect_key($this->_data, array_flip($args));
+ }
+
+ /**
+ * Return the value of a property of this object (database row)
+ * or null if not present.
+ */
+ public function get($key) {
+ return isset($this->_data[$key]) ? $this->_data[$key] : null;
+ }
+
+ /**
+ * Return the name of the column in the database table which contains
+ * the primary key ID of the row.
+ */
+ protected function _get_id_column_name() {
+ if (!is_null($this->_instance_id_column)) {
+ return $this->_instance_id_column;
+ }
+ if (isset(self::$_config['id_column_overrides'][$this->_table_name])) {
+ return self::$_config['id_column_overrides'][$this->_table_name];
+ } else {
+ return self::$_config['id_column'];
+ }
+ }
+
+ /**
+ * Get the primary key ID of this object.
+ */
+ public function id() {
+ return $this->get($this->_get_id_column_name());
+ }
+
+ /**
+ * Set a property to a particular value on this object.
+ * Flags that property as 'dirty' so it will be saved to the
+ * database when save() is called.
+ */
+ public function set($key, $value) {
+ $this->_data[$key] = $value;
+ $this->_dirty_fields[$key] = $value;
+ }
+
+ /**
+ * Check whether the given field has been changed since this
+ * object was saved.
+ */
+ public function is_dirty($key) {
+ return isset($this->_dirty_fields[$key]);
+ }
+
+ /**
+ * Save any fields which have been modified on this object
+ * to the database.
+ */
+ public function save() {
+ $query = array();
+ $values = array_values($this->_dirty_fields);
+
+ if (!$this->_is_new) { // UPDATE
+ // If there are no dirty values, do nothing
+ if (count($values) == 0) {
+ return true;
+ }
+ $query = $this->_build_update();
+ $values[] = $this->id();
+ } else { // INSERT
+ $query = $this->_build_insert();
+ }
+
+ self::_log_query($query, $values);
+ $statement = self::$_db->prepare($query);
+ $success = $statement->execute($values);
+
+ // If we've just inserted a new record, set the ID of this object
+ if ($this->_is_new) {
+ $this->_is_new = false;
+ if (is_null($this->id())) {
+ $this->_data[$this->_get_id_column_name()] = self::$_db->lastInsertId();
+ }
+ }
+
+ $this->_dirty_fields = array();
+ return $success;
+ }
+
+ /**
+ * Build an UPDATE query
+ */
+ protected function _build_update() {
+ $query = array();
+ $query[] = "UPDATE {$this->_quote_identifier($this->_table_name)} SET";
+
+ $field_list = array();
+ foreach ($this->_dirty_fields as $key => $value) {
+ $field_list[] = "{$this->_quote_identifier($key)} = ?";
+ }
+ $query[] = join(", ", $field_list);
+ $query[] = "WHERE";
+ $query[] = $this->_quote_identifier($this->_get_id_column_name());
+ $query[] = "= ?";
+ return join(" ", $query);
+ }
+
+ /**
+ * Build an INSERT query
+ */
+ protected function _build_insert() {
+ $query[] = "INSERT INTO";
+ $query[] = $this->_quote_identifier($this->_table_name);
+ $field_list = array_map(array($this, '_quote_identifier'), array_keys($this->_dirty_fields));
+ $query[] = "(" . join(", ", $field_list) . ")";
+ $query[] = "VALUES";
+
+ $placeholders = $this->_create_placeholders(count($this->_dirty_fields));
+ $query[] = "({$placeholders})";
+ return join(" ", $query);
+ }
+
+ /**
+ * Delete this record from the database
+ */
+ public function delete() {
+ $query = join(" ", array(
+ "DELETE FROM",
+ $this->_quote_identifier($this->_table_name),
+ "WHERE",
+ $this->_quote_identifier($this->_get_id_column_name()),
+ "= ?",
+ ));
+ $params = array($this->id());
+ self::_log_query($query, $params);
+ $statement = self::$_db->prepare($query);
+ return $statement->execute($params);
+ }
+
+ // --------------------- //
+ // --- MAGIC METHODS --- //
+ // --------------------- //
+ public function __get($key) {
+ return $this->get($key);
+ }
+
+ public function __set($key, $value) {
+ $this->set($key, $value);
+ }
+
+ public function __isset($key) {
+ return isset($this->_data[$key]);
+ }
+ }
diff --git a/monstra/helpers/request.php b/monstra/helpers/request.php
new file mode 100644
index 0000000..92d57da
--- /dev/null
+++ b/monstra/helpers/request.php
@@ -0,0 +1,161 @@
+
+ * Request::redirect('test');
+ *
+ *
+ * @param string $url The URL
+ * @param integer $status Status
+ * @param integer $delay Delay
+ */
+ public static function redirect($url, $status = 302, $delay = null){
+
+ // Redefine vars
+ $url = (string) $url;
+ $status = (int) $status;
+
+ // Status codes
+ $messages = array();
+ $messages[301] = '301 Moved Permanently';
+ $messages[302] = '302 Found';
+
+ // Is Headers sent ?
+ if (headers_sent()) {
+
+ echo "\n";
+
+ } else {
+
+ // Redirect headers
+ Request::setHeaders('HTTP/1.1 ' . $status . ' ' . Arr::get($messages, $status, 302));
+
+ // Delay execution
+ if ($delay !== null) sleep((int) $delay);
+
+ // Redirect
+ Request::setHeaders("Location: $url");
+
+ // Shutdown request
+ Request::shutdown();
+
+ }
+
+ }
+
+
+ /**
+ * Set one or multiple headers.
+ *
+ *
+ * Request::setHeaders('Location: http://site.com/');
+ *
+ *
+ * @param mixed $headers String or array with headers to send.
+ */
+ public static function setHeaders($headers) {
+
+ // Loop elements
+ foreach ((array) $headers as $header) {
+
+ // Set header
+ header((string) $header);
+
+ }
+
+ }
+
+
+ /**
+ * Get
+ *
+ *
+ * $action = Request::get('action');
+ *
+ *
+ * @param string $key Key
+ * @param mixed
+ */
+ public static function get($key) {
+ return Arr::get($_GET, $key);
+ }
+
+
+ /**
+ * Post
+ *
+ *
+ * $login = Request::post('login');
+ *
+ *
+ * @param string $key Key
+ * @param mixed
+ */
+ public static function post($key) {
+ return Arr::get($_POST, $key);
+ }
+
+
+ /**
+ * Returns whether this is an ajax request or not
+ *
+ *
+ * if (Request::isAjax()) {
+ * // do something...
+ * }
+ *
+ *
+ * @return boolean
+ */
+ public static function isAjax() {
+ return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
+ }
+
+
+ /**
+ * Terminate request
+ *
+ *
+ * Request::shutdown();
+ *
+ *
+ */
+ public static function shutdown() {
+ exit(0);
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/response.php b/monstra/helpers/response.php
new file mode 100644
index 0000000..a006deb
--- /dev/null
+++ b/monstra/helpers/response.php
@@ -0,0 +1,109 @@
+ 'Continue',
+ 101 => 'Switching Protocols',
+
+ // Success 2xx
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+
+ // Redirection 3xx
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found', // 1.1
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ // 306 is deprecated but reserved
+ 307 => 'Temporary Redirect',
+
+ // Client Error 4xx
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Timeout',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition Failed',
+ 413 => 'Request Entity Too Large',
+ 414 => 'Request-URI Too Long',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Requested Range Not Satisfiable',
+ 417 => 'Expectation Failed',
+
+ // Server Error 5xx
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Timeout',
+ 505 => 'HTTP Version Not Supported',
+ 509 => 'Bandwidth Limit Exceeded'
+ );
+
+
+ /**
+ * Protected constructor since this is a static class.
+ *
+ * @access protected
+ */
+ protected function __construct() {
+ // Nothing here
+ }
+
+
+ /**
+ * Set header status
+ *
+ *
+ * Response::status(404);
+ *
+ *
+ * @param integer $status Status code
+ */
+ public static function status($status) {
+ if (array_key_exists($status, Response::$messages)) header('HTTP/1.1 ' . $status . ' ' . Response::$messages[$status]);
+ }
+
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/security.php b/monstra/helpers/security.php
new file mode 100644
index 0000000..5446ede
--- /dev/null
+++ b/monstra/helpers/security.php
@@ -0,0 +1,250 @@
+
+ * $token = Security::token();
+ *
+ *
+ * You can insert this token into your forms as a hidden field:
+ *
+ *
+ * echo Form::hidden('csrf', Security::token());
+ *
+ *
+ * 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.
+ *
+ *
+ * if (Security::check($token)) {
+ * // Pass
+ * }
+ *
+ *
+ * @param string $token token to check
+ * @return boolean
+ */
+ public static function check($token) {
+ return Security::token() === $token;
+ }
+
+
+
+ /**
+ * Encrypt password
+ *
+ *
+ * $encrypt_password = Security::encryptPassword('password');
+ *
+ *
+ * @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.
+ *
+ *
+ * $safe_name = Security::safeName('hello world');
+ *
+ *
+ * @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.
+ *
+ *
+ * $url = Security::sanitizeURL('http://test.com');
+ *
+ *
+ * @param string $url Url to sanitize
+ * @return string
+ */
+ public static function sanitizeURL($url) {
+
+ $url = trim($url);
+ $url = rawurldecode($url);
+ $url = str_replace(array('--','"','!','@','#','$','%','^','*','(',')','+','{','}','|',':','"','<','>',
+ '[',']','\\',';',"'",',','*','+','~','`','laquo','raquo',']>','‘','’','“','”','–','—'),
+ 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;
+ }
+
+ }
diff --git a/monstra/helpers/session.php b/monstra/helpers/session.php
new file mode 100644
index 0000000..ae88f82
--- /dev/null
+++ b/monstra/helpers/session.php
@@ -0,0 +1,198 @@
+
+ * Session::start();
+ *
+ *
+ */
+ public static function start() {
+
+ // Is session already started?
+ if ( ! session_id()) {
+
+ // Start the session
+ return @session_start();
+ }
+
+ // If already started
+ return true;
+ }
+
+
+ /**
+ * Deletes one or more session variables.
+ *
+ *
+ * Session::delete('user');
+ *
+ *
+ */
+ public static function delete() {
+
+ // Loop all arguments
+ foreach (func_get_args() as $argument) {
+
+ // Array element
+ if (is_array($argument)) {
+
+ // Loop the keys
+ foreach ($argument as $key) {
+
+ // Unset session key
+ unset($_SESSION[(string) $key]);
+ }
+ } else {
+
+ // Remove from array
+ unset($_SESSION[(string) $argument]);
+ }
+ }
+ }
+
+
+ /**
+ * Destroys the session.
+ *
+ *
+ * Session::destroy();
+ *
+ *
+ */
+ public static function destroy() {
+
+ // Destroy
+ if (session_id()) {
+ session_unset();
+ session_destroy();
+ $_SESSION = array();
+ }
+
+ }
+
+
+ /**
+ * Checks if a session variable exists.
+ *
+ *
+ * if (Session::exists('user')) {
+ * // Do something...
+ * }
+ *
+ *
+ * @return boolean
+ */
+ public static function exists() {
+
+ // Start session if needed
+ if ( ! session_id()) Session::start();
+
+ // Loop all arguments
+ foreach (func_get_args() as $argument) {
+
+ // Array element
+ if (is_array($argument)) {
+
+ // Loop the keys
+ foreach ($argument as $key) {
+
+ // Does NOT exist
+ if ( ! isset($_SESSION[(string) $key])) return false;
+ }
+ } else {
+
+ // Does NOT exist
+ if ( ! isset($_SESSION[(string) $argument])) return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Gets a variable that was stored in the session.
+ *
+ *
+ * echo Session::get('user');
+ *
+ *
+ * @param string $key The key of the variable to get.
+ * @return mixed
+ */
+ public static function get($key) {
+
+ // Start session if needed
+ if ( ! session_id()) self::start();
+
+ // Redefine key
+ $key = (string) $key;
+
+ // Fetch key
+ if (Session::exists((string) $key)) return $_SESSION[(string) $key];
+
+ // Key doesn't exist
+ return null;
+ }
+
+
+ /**
+ * Returns the sessionID.
+ *
+ *
+ * echo Session::getSessionId();
+ *
+ *
+ * @return string
+ */
+ public static function getSessionId() {
+ if ( ! session_id()) Session::start();
+ return session_id();
+ }
+
+
+ /**
+ * Stores a variable in the session.
+ *
+ *
+ * Session::set('user', 'Awilum');
+ *
+ *
+ * @param string $key The key for the variable.
+ * @param mixed $value The value to store.
+ */
+ public static function set($key, $value) {
+
+ // Start session if needed
+ if ( ! session_id()) self::start();
+
+ // Set key
+ $_SESSION[(string) $key] = $value;
+ }
+
+
+ }
diff --git a/monstra/helpers/text.php b/monstra/helpers/text.php
new file mode 100644
index 0000000..67cfdb4
--- /dev/null
+++ b/monstra/helpers/text.php
@@ -0,0 +1,470 @@
+ latin
+ *
+ *
+ * echo Text::translitIt('Привет');
+ *
+ *
+ * @param string $str [ua,ru] string
+ * @return string $str
+ */
+ public static function translitIt($str) {
+
+ // Redefine vars
+ $str = (string) $str;
+
+ $patern = array(
+ "А" => "A", "Б" => "B", "В" => "V", "Г" => "G",
+ "Д" => "D", "Е" => "E", "Ж" => "J", "З" => "Z",
+ "И" => "I", "Й" => "Y", "К" => "K", "Л" => "L",
+ "М" => "M", "Н" => "N", "О" => "O", "П" => "P",
+ "Р" => "R", "С" => "S", "Т" => "T", "У" => "U",
+ "Ф" => "F", "Х" => "H", "Ц" => "TS", "Ч" => "CH",
+ "Ш" => "SH", "Щ" => "SCH", "Ъ" => "", "Ы" => "YI",
+ "Ь" => "", "Э" => "E", "Ю" => "YU", "Я" => "YA",
+ "а" => "a", "б" => "b", "в" => "v", "г" => "g",
+ "д" => "d", "е" => "e", "ж" => "j", "з" => "z",
+ "и" => "i", "й" => "y", "к" => "k", "л" => "l",
+ "м" => "m", "н" => "n", "о" => "o","п" => "p",
+ "р" => "r", "с" => "s", "т" => "t", "у" => "u",
+ "ф" => "f", "х" => "h", "ц" => "ts", "ч" => "ch",
+ "ш" => "sh", "щ" => "sch", "ъ" => "y", "ї" => "i",
+ "Ї" => "Yi", "є" => "ie", "Є" => "Ye", "ы" => "yi",
+ "ь" => "", "э" => "e", "ю" => "yu", "я" => "ya", "ё" => "yo"
+ );
+
+ return strtr($str, $patern);
+ }
+
+
+ /**
+ * Removes any leading and traling slashes from a string
+ *
+ *
+ * echo Text::trimSlashes('some text here/');
+ *
+ *
+ * @param string $str String with slashes
+ * @return string
+ */
+ public static function trimSlashes($str) {
+
+ // Redefine vars
+ $str = (string) $str;
+
+ return trim($str, '/');
+ }
+
+
+ /**
+ * Removes slashes contained in a string or in an array
+ *
+ *
+ * echo Text::strpSlashes('some \ text \ here');
+ *
+ *
+ * @param string $str String with slashes
+ * @return string
+ */
+ public static function strpSlashes($str) {
+
+ // Redefine vars
+ $str = (string) $str;
+
+ if (is_array($str)) {
+ foreach ($str as $key => $val) {
+ $str[$key] = stripslashes($val);
+ }
+ } else {
+ $str = stripslashes($str);
+ }
+ return $str;
+ }
+
+
+ /**
+ * Removes single and double quotes from a string
+ *
+ *
+ * echo Text::stripQuotes('some "text" here');
+ *
+ *
+ * @param string $str String with single and double quotes
+ * @return string
+ */
+ public static function stripQuotes($str) {
+
+ // Redefine vars
+ $str = (string) $str;
+
+ return str_replace(array('"', "'"), '', $str);
+ }
+
+
+ /**
+ * Convert single and double quotes to entities
+ *
+ *
+ * echo Text::quotesToEntities('some "text" here');
+ *
+ *
+ * @param string $str String with single and double quotes
+ * @return string
+ */
+ public static function quotesToEntities($str) {
+
+ // Redefine vars
+ $str = (string) $str;
+
+ return str_replace(array("\'", "\"", "'", '"'), array("'", """, "'", """), $str);
+ }
+
+
+ /**
+ * Creates a random string of characters
+ *
+ *
+ * echo Text::random();
+ *
+ *
+ * @param string $type The type of string. Default is 'alnum'
+ * @param integer $length The number of characters. Default is 16
+ * @return string
+ */
+ public static function random($type = 'alnum', $length = 16) {
+
+ // Redefine vars
+ $type = (string) $type;
+ $length = (int) $length;
+
+ switch($type) {
+
+ case 'basic':
+ return mt_rand();
+ break;
+
+ default:
+ case 'alnum':
+ case 'numeric':
+ case 'nozero':
+ case 'alpha':
+ case 'distinct':
+ case 'hexdec':
+ switch ($type) {
+ case 'alpha':
+ $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ break;
+
+ default:
+ case 'alnum':
+ $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ break;
+
+ case 'numeric':
+ $pool = '0123456789';
+ break;
+
+ case 'nozero':
+ $pool = '123456789';
+ break;
+
+ case 'distinct':
+ $pool = '2345679ACDEFHJKLMNPRSTUVWXYZ';
+ break;
+
+ case 'hexdec':
+ $pool = '0123456789abcdef';
+ break;
+ }
+
+ $str = '';
+ for ($i=0; $i < $length; $i++) {
+ $str .= substr($pool, mt_rand(0, strlen($pool) -1), 1);
+ }
+ return $str;
+ break;
+
+ case 'unique':
+ return md5(uniqid(mt_rand()));
+ break;
+
+ case 'sha1' :
+ return sha1(uniqid(mt_rand(), true));
+ break;
+ }
+ }
+
+
+ /**
+ * Cut string
+ *
+ *
+ * echo Text::cut('Some text here', 5);
+ *
+ *
+ * @param string $str Input string
+ * @param integer $length Length after cut
+ * @param string $cut_msg Message after cut string
+ * @return string
+ */
+ public static function cut($str, $length, $cut_msg = null) {
+
+ // Redefine vars
+ $str = (string) $str;
+ $length = (int) $length;
+
+ if (isset($cut_msg)) $msg = $cut_msg; else $msg = '...';
+
+ return function_exists('mb_substr') ? mb_substr($str, 0, $length, 'utf-8') . $msg : substr($str, 0, $length) . $msg;
+ }
+
+
+ /**
+ * Lowercase
+ *
+ *
+ * echo Text::lowercase('Some text here');
+ *
+ *
+ * @param string $str String
+ * @return string
+ */
+ public static function lowercase($str) {
+
+ // Redefine vars
+ $str = (string) $str;
+
+ return function_exists('mb_strtolower') ? mb_strtolower($str, 'utf-8') : strtolower($str);
+ }
+
+
+ /**
+ * Uppercase
+ *
+ *
+ * echo Text::uppercase('some text here');
+ *
+ *
+ * @param string $str String
+ * @return string
+ */
+ public static function uppercase($str) {
+
+ // Redefine vars
+ $str = (string) $str;
+
+ return function_exists('mb_strtoupper') ? mb_strtoupper($str, 'utf-8') : strtoupper($str);
+ }
+
+
+ /**
+ * Get length
+ *
+ *
+ * echo Text::length('Some text here');
+ *
+ *
+ * @param string $str String
+ * @return string
+ */
+ public static function length($str) {
+
+ // Redefine vars
+ $str = (string) $str;
+
+ return function_exists('mb_strlen') ? mb_strlen($str, 'utf-8') : strlen($str);
+ }
+
+
+ /**
+ * Create a lorem ipsum text
+ *
+ *
+ * echo Text::lorem(2);
+ *
+ *
+ * @param integer $num Count
+ * @return string
+ */
+ public static function lorem($num = 1) {
+
+ // Redefine vars
+ $num = (int) $num;
+
+ return str_repeat('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', (int)$num);
+ }
+
+
+ /**
+ * Extract the last `$num` characters from a string.
+ *
+ *
+ * echo Text::right('Some text here', 4);
+ *
+ *
+ * @param string $str The string to extract the characters from.
+ * @param integer $num The number of characters to extract.
+ * @return string
+ */
+ public static function right($str, $num){
+
+ // Redefine vars
+ $str = (string) $str;
+ $num = (int) $num;
+
+ return substr($str, Text::length($str)-$num, $num);
+ }
+
+
+ /**
+ * Extract the first `$num` characters from a string.
+ *
+ *
+ * echo Text::left('Some text here', 4);
+ *
+ *
+ * @param string $str The string to extract the characters from.
+ * @param integer $num The number of characters to extract.
+ * @return string
+ */
+ public static function left($str, $num){
+
+ // Redefine vars
+ $str = (string) $str;
+ $num = (int) $num;
+
+ return substr($str, 0, $num);
+ }
+
+
+ /**
+ * Replaces newline with
+ * echo Text::nl2br("Some \n text \n here");
+ *
+ *
+ * @param string $str The input string
+ * @param boolean $xhtml Xhtml or not
+ * @return string
+ */
+ public static function nl2br($str, $xhtml = true) {
+
+ // Redefine vars
+ $str = (string) $str;
+ $xhtml = (bool) $xhtml;
+
+ return str_replace(array("\r\n", "\n\r", "\n", "\r"), (($xhtml) ? '
+ * echo Text::br2nl("Some
text
here");
+ *
+ *
+ * @param string $str The input string
+ * @return string
+ */
+ public static function br2nl($str) {
+
+ // Redefine vars
+ $str = (string) $str;
+
+ return str_replace(array('
+ * echo Text::ampEncode("M&CMS");
+ *
+ *
+ * @param string $str The input string
+ * @return string
+ */
+ public static function ampEncode($str) {
+
+ // Redefine vars
+ $str = (string) $str;
+
+ return str_replace('&', '&', $str);
+ }
+
+
+ /**
+ * Converts & to &.
+ *
+ *
+ * echo Text::ampEncode("M&CMS");
+ *
+ *
+ * @param string $str The input string
+ * @return string
+ */
+ public static function ampDecode($str) {
+
+ // Redefine vars
+ $str = (string) $str;
+
+ return str_replace('&', '&', $str);
+ }
+
+
+ /**
+ * Convert plain text to html
+ *
+ *
+ * echo Text::toHtml('test');
+ *
+ *
+ * @param string $str String
+ * @return string
+ */
+ public static function toHtml($str) {
+
+ // Redefine vars
+ $str = (string) $str;
+
+ return html_entity_decode($str, ENT_QUOTES, 'utf-8');
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/uri.php b/monstra/helpers/uri.php
new file mode 100644
index 0000000..cf551f9
--- /dev/null
+++ b/monstra/helpers/uri.php
@@ -0,0 +1,171 @@
+
+ * $segments = Uri::segments();
+ *
+ *
+ * @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
+ *
+ *
+ * $segment = Uri::segment(1);
+ *
+ *
+ * @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
+ *
+ *
+ * $command = Uri::command();
+ *
+ *
+ * @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
+ *
+ *
+ * $params = Uri::params();
+ *
+ *
+ * @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;
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/url.php b/monstra/helpers/url.php
new file mode 100644
index 0000000..66a3d71
--- /dev/null
+++ b/monstra/helpers/url.php
@@ -0,0 +1,115 @@
+
+ * echo Url::tiny('http:://sitename.com');
+ *
+ *
+ * @param string $url Long url
+ * @return string
+ */
+ public static function tiny($url) {
+ return file_get_contents('http://tinyurl.com/api-create.php?url='.(string)$url);
+ }
+
+
+ /**
+ * Check is url exists
+ *
+ *
+ * if(Url::exists('http:://sitename.com')) {
+ * // Do something...
+ * }
+ *
+ *
+ * @param string $url Url
+ * @return boolean
+ */
+ public static function exists($url) {
+ $a_url = parse_url($url);
+ if ( ! isset($a_url['port'])) $a_url['port'] = 80;
+ $errno = 0;
+ $errstr = '';
+ $timeout = 30;
+ if (isset($a_url['host']) && $a_url['host']!=gethostbyname($a_url['host'])){
+ $fid = fsockopen($a_url['host'], $a_url['port'], $errno, $errstr, $timeout);
+ if ( ! $fid) return false;
+ $page = isset($a_url['path']) ? $a_url['path'] : '';
+ $page .= isset($a_url['query']) ? '?'.$a_url['query'] : '';
+ fputs($fid, 'HEAD '.$page.' HTTP/1.0'."\r\n".'Host: '.$a_url['host']."\r\n\r\n");
+ $head = fread($fid, 4096);
+ fclose($fid);
+ return preg_match('#^HTTP/.*\s+[200|302]+\s#i', $head);
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * Find url
+ *
+ *
+ * // Outputs: http://sitename.com/home
+ * echo Url::find('home');
+ *
+ *
+ * @global string $site_url Site url
+ * @param string $url URL - Uniform Resource Locator
+ * @return string
+ */
+ public static function find($url) {
+ $pos = strpos($url, 'http://');
+ if ($pos === false) { $url_output = Option::get('siteurl') . $url; } else { $url_output = $url; }
+ return $url_output;
+ }
+
+
+ /**
+ * Gets the base URL
+ *
+ *
+ * echo Url::base();
+ *
+ *
+ * @return string
+ */
+ public static function base() {
+ return 'http://' . rtrim(rtrim($_SERVER['HTTP_HOST'], '\\/') . dirname($_SERVER['PHP_SELF']), '\\/');
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/valid.php b/monstra/helpers/valid.php
new file mode 100644
index 0000000..c2db15f
--- /dev/null
+++ b/monstra/helpers/valid.php
@@ -0,0 +1,208 @@
+
+ * if (Valid::email('test@test.com')) {
+ * // Do something...
+ * }
+ *
+ *
+ * @param string $email email address
+ * @return boolean
+ */
+ public static function email($email) {
+ return (bool) preg_match('/^[-_a-z0-9\'+*$^&%=~!?{}]++(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*+@(?:(?![-.])[-a-z0-9.]+(?
+ * if (Valid::ip('127.0.0.1')) {
+ * // Do something...
+ * }
+ *
+ *
+ * @param string $ip ip address
+ * @return boolean
+ */
+ public static function ip($ip) {
+ return (bool) preg_match("^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}^", (string)$ip);
+ }
+
+
+ /**
+ * Check an credit card for correct format.
+ *
+ *
+ * if (Valid::creditCard(7711111111111111, 'Visa')) {
+ * // Do something...
+ * }
+ *
+ *
+ * @param integer $num Credit card num
+ * @param string $type Credit card type:
+ * American - American Express
+ * Dinners - Diner's Club
+ * Discover - Discover Card
+ * Master - Mastercard
+ * Visa - Visa
+ * @return boolean
+ */
+ public static function creditCard($num, $type) {
+
+ // Redefine vars
+ $num = (int) $num;
+ $type = (string) $type;
+
+ switch($type) {
+ case "American": return (bool) preg_match("/^([34|37]{2})([0-9]{13})$/", $num);
+ case "Dinners": return (bool) preg_match("/^([30|36|38]{2})([0-9]{12})$/", $num);
+ case "Discover": return (bool) preg_match("/^([6011]{4})([0-9]{12})$/", $num);
+ case "Master": return (bool) preg_match("/^([51|52|53|54|55]{2})([0-9]{14})$/", $num);
+ case "Visa": return (bool) preg_match("/^([4]{1})([0-9]{12,15})$/", $num);
+ }
+ }
+
+
+ /**
+ * Check an phone number for correct format.
+ *
+ *
+ * if (Valid::phone(0661111117)) {
+ * // Do something...
+ * }
+ *
+ *
+ * @param string $num Phone number
+ * @return boolean
+ */
+ public static function phone($num) {
+ return (bool) preg_match("/^([0-9\(\)\/\+ \-]*)$/", (string)$num);
+ }
+
+
+ /**
+ * Check an url for correct format.
+ *
+ *
+ * if (Valid::url('http://site.com/')) {
+ * // Do something...
+ * }
+ *
+ *
+ * @param string $url Url
+ * @return boolean
+ */
+ public static function url($url) {
+ return (bool) preg_match("|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i", (string)$url);
+ }
+
+
+ /**
+ * Check an date for correct format.
+ *
+ *
+ * if (Valid::date('12/12/12')) {
+ * // Do something...
+ * }
+ *
+ *
+ * @param string $str Date
+ * @return boolean
+ */
+ public static function date($str) {
+ return (strtotime($str) !== false);
+ }
+
+
+ /**
+ * Checks whether a string consists of digits only (no dots or dashes).
+ *
+ *
+ * if (Valid::digit('12')) {
+ * // Do something...
+ * }
+ *
+ *
+ * @param string $str String
+ * @return boolean
+ */
+ public static function digit($str) {
+ return (bool) preg_match ("/[^0-9]/", $str);
+ }
+
+
+ /**
+ * Checks whether a string is a valid number (negative and decimal numbers allowed).
+ *
+ *
+ * if (Valid::numeric('3.14')) {
+ * // Do something...
+ * }
+ *
+ *
+ * Uses {@link http://www.php.net/manual/en/function.localeconv.php locale conversion}
+ * to allow decimal point to be locale specific.
+ *
+ * @param string $str String
+ * @return boolean
+ */
+ public static function numeric($str) {
+ $locale = localeconv();
+ return (bool) preg_match('/^-?[0-9'.$locale['decimal_point'].']++$/D', (string)$str);
+ }
+
+
+ /**
+ * Checks if the given regex statement is valid.
+ *
+ * @param string $regexp The value to validate.
+ * @return boolean
+ */
+ public static function regexp($regexp) {
+
+ // dummy string
+ $dummy = 'Monstra - fast and simple cms';
+
+ // validate
+ return (@preg_match((string) $regexp, $dummy) !== false);
+
+ }
+
+ }
\ No newline at end of file
diff --git a/monstra/helpers/zip.php b/monstra/helpers/zip.php
new file mode 100644
index 0000000..89b78c6
--- /dev/null
+++ b/monstra/helpers/zip.php
@@ -0,0 +1,385 @@
+now = time();
+ }
+
+
+ /**
+ * Zip factory
+ *
+ *
+ * Zip::factory();
+ *
+ *
+ * @return Zip
+ */
+ public static function factory() {
+ return new Zip();
+ }
+
+
+ /**
+ * Add Directory
+ *
+ *
+ * Zip::factory()->addDir('test');
+ *
+ *
+ * @param mixed $directory The directory name. Can be string or array
+ */
+ public function addDir($directory) {
+
+ foreach ((array)$directory as $dir) {
+
+ if ( ! preg_match("|.+/$|", $dir)) {
+ $dir .= '/';
+ }
+
+ $dir_time = $this->_get_mod_time($dir);
+
+ $this->_add_dir($dir, $dir_time['file_mtime'], $dir_time['file_mdate']);
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * Get file/directory modification time
+ *
+ * @param string $dir Full path to the dir
+ * @return array
+ */
+ protected function _get_mod_time($dir) {
+
+ // If this is a newly created file/dir, we will set the time to 'now'
+ $date = (@filemtime($dir)) ? filemtime($dir) : getdate($this->now);
+
+ $time['file_mtime'] = ($date['hours'] << 11) + ($date['minutes'] << 5) + $date['seconds'] / 2;
+ $time['file_mdate'] = (($date['year'] - 1980) << 9) + ($date['mon'] << 5) + $date['mday'];
+
+ return $time;
+ }
+
+
+ /**
+ * Add Directory
+ *
+ * @param string $dir The directory name
+ * @param integer $file_mtime File mtime
+ * @param integer $file_mdate File mdate
+ */
+ private function _add_dir($dir, $file_mtime, $file_mdate) {
+
+ $dir = str_replace("\\", "/", $dir);
+
+ $this->zipdata .=
+ "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00"
+ .pack('v', $file_mtime)
+ .pack('v', $file_mdate)
+ .pack('V', 0) // crc32
+ .pack('V', 0) // compressed filesize
+ .pack('V', 0) // uncompressed filesize
+ .pack('v', strlen($dir)) // length of pathname
+ .pack('v', 0) // extra field length
+ .$dir
+ // below is "data descriptor" segment
+ .pack('V', 0) // crc32
+ .pack('V', 0) // compressed filesize
+ .pack('V', 0); // uncompressed filesize
+
+ $this->directory .=
+ "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00"
+ .pack('v', $file_mtime)
+ .pack('v', $file_mdate)
+ .pack('V',0) // crc32
+ .pack('V',0) // compressed filesize
+ .pack('V',0) // uncompressed filesize
+ .pack('v', strlen($dir)) // length of pathname
+ .pack('v', 0) // extra field length
+ .pack('v', 0) // file comment length
+ .pack('v', 0) // disk number start
+ .pack('v', 0) // internal file attributes
+ .pack('V', 16) // external file attributes - 'directory' bit set
+ .pack('V', $this->offset) // relative offset of local header
+ .$dir;
+
+ $this->offset = strlen($this->zipdata);
+ $this->entries++;
+ }
+
+
+ /**
+ * Add Data to Zip
+ *
+ *
+ * Zip::factory()->addData('test.txt', 'Some test text here');
+ *
+ *
+ * Lets you add files to the archive. If the path is included
+ * in the filename it will be placed within a directory. Make
+ * sure you use add_dir() first to create the folder.
+ *
+ * @param mixed $filepath Full path to the file
+ * @param string $data Data
+ */
+ public function addData($filepath, $data = null) {
+
+ if (is_array($filepath)) {
+ foreach ($filepath as $path => $data) {
+ $file_data = $this->_get_mod_time($path);
+ $this->_add_data($path, $data, $file_data['file_mtime'], $file_data['file_mdate']);
+ }
+ } else {
+ $file_data = $this->_get_mod_time($filepath);
+ $this->_add_data($filepath, $data, $file_data['file_mtime'], $file_data['file_mdate']);
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * Add Data to Zip
+ *
+ * @param string $filepath Full path to the file
+ * @param string $data The data to be encoded
+ * @param integer $file_mtime File mtime
+ * @param integer $file_mdate File mdate
+ */
+ private function _add_data($filepath, $data, $file_mtime, $file_mdate) {
+
+ $filepath = str_replace("\\", "/", $filepath);
+
+ $uncompressed_size = strlen($data);
+ $crc32 = crc32($data);
+
+ $gzdata = gzcompress($data);
+ $gzdata = substr($gzdata, 2, -4);
+ $compressed_size = strlen($gzdata);
+
+ $this->zipdata .=
+ "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00"
+ .pack('v', $file_mtime)
+ .pack('v', $file_mdate)
+ .pack('V', $crc32)
+ .pack('V', $compressed_size)
+ .pack('V', $uncompressed_size)
+ .pack('v', strlen($filepath)) // length of filename
+ .pack('v', 0) // extra field length
+ .$filepath
+ .$gzdata; // "file data" segment
+
+ $this->directory .=
+ "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00"
+ .pack('v', $file_mtime)
+ .pack('v', $file_mdate)
+ .pack('V', $crc32)
+ .pack('V', $compressed_size)
+ .pack('V', $uncompressed_size)
+ .pack('v', strlen($filepath)) // length of filename
+ .pack('v', 0) // extra field length
+ .pack('v', 0) // file comment length
+ .pack('v', 0) // disk number start
+ .pack('v', 0) // internal file attributes
+ .pack('V', 32) // external file attributes - 'archive' bit set
+ .pack('V', $this->offset) // relative offset of local header
+ .$filepath;
+
+ $this->offset = strlen($this->zipdata);
+ $this->entries++;
+ $this->file_num++;
+ }
+
+
+ /**
+ * Read the contents of a file and add it to the zip
+ *
+ *
+ * Zip::factory()->readFile('test.txt');
+ *
+ *
+ * @param string $path Path
+ * @param boolean $preserve_filepath Preserve filepath
+ * @return mixed
+ */
+ function readFile($path, $preserve_filepath = false) {
+
+ if ( ! file_exists($path)) {
+ return false;
+ }
+
+ if (false !== ($data = file_get_contents($path))) {
+
+ $name = str_replace("\\", "/", $path);
+
+ if ($preserve_filepath === false) {
+ $name = preg_replace("|.*/(.+)|", "\\1", $name);
+ }
+
+ $this->addData($name, $data);
+
+ return $this;
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Read a directory and add it to the zip.
+ *
+ *
+ * Zip::factory()->readDir('test/');
+ *
+ *
+ * This function recursively reads a folder and everything it contains (including
+ * sub-folders) and creates a zip based on it. Whatever directory structure
+ * is in the original file path will be recreated in the zip file.
+ *
+ * @param string $path Path to source
+ * @param boolean $preserve_filepath Preserve filepath
+ * @param string $root_path Root path
+ * @return mixed
+ */
+ function readDir($path, $preserve_filepath = true, $root_path = null) {
+
+ if ( ! $fp = @opendir($path)) {
+ return false;
+ }
+
+ // Set the original directory root for child dir's to use as relative
+ if ($root_path === null) {
+ $root_path = dirname($path) . '/';
+ }
+
+ while (false !== ($file = readdir($fp))) {
+
+ if (substr($file, 0, 1) == '.') {
+ continue;
+ }
+
+ if (@is_dir($path.$file)) {
+ $this->readDir($path.$file."/", $preserve_filepath, $root_path);
+ } else {
+ if (false !== ($data = file_get_contents($path.$file))) {
+ $name = str_replace("\\", "/", $path);
+
+ if ($preserve_filepath === false) {
+ $name = str_replace($root_path, '', $name);
+ }
+
+ $this->addData($name.$file, $data);
+ }
+ }
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * Get the Zip file
+ *
+ *
+ * Zip::factory()->getZip();
+ *
+ *
+ * @return string
+ */
+ public function getZip() {
+
+ // Is there any data to return?
+ if ($this->entries == 0) {
+ return false;
+ }
+
+ $zip_data = $this->zipdata;
+ $zip_data .= $this->directory."\x50\x4b\x05\x06\x00\x00\x00\x00";
+ $zip_data .= pack('v', $this->entries); // total # of entries "on this disk"
+ $zip_data .= pack('v', $this->entries); // total # of entries overall
+ $zip_data .= pack('V', strlen($this->directory)); // size of central dir
+ $zip_data .= pack('V', strlen($this->zipdata)); // offset to start of central dir
+ $zip_data .= "\x00\x00"; // .zip file comment length
+
+ return $zip_data;
+ }
+
+
+ /**
+ * Write File to the specified directory
+ *
+ *
+ * Zip::factory()->readDir('test1/')->readDir('test2/')->archive('test.zip');
+ *
+ *
+ * @param string $filepath The file name
+ * @return boolean
+ */
+ public function archive($filepath) {
+
+ if ( ! ($fp = @fopen($filepath, "w"))) {
+ return false;
+ }
+
+ flock($fp, LOCK_EX);
+ fwrite($fp, $this->getZip());
+ flock($fp, LOCK_UN);
+ fclose($fp);
+
+ return true;
+ }
+
+
+ /**
+ * Initialize Data
+ *
+ *
+ * Zip::factory()->clearData();
+ *
+ *
+ * Lets you clear current zip data. Useful if you need to create
+ * multiple zips with different data.
+ */
+ public function clearData() {
+ $this->zipdata = '';
+ $this->directory = '';
+ $this->entries = 0;
+ $this->file_num = 0;
+ $this->offset = 0;
+ }
+
+ }
diff --git a/plugins/.htaccess b/plugins/.htaccess
new file mode 100644
index 0000000..14249c5
--- /dev/null
+++ b/plugins/.htaccess
@@ -0,0 +1 @@
+Deny from all
\ No newline at end of file
diff --git a/plugins/box/backup/backup.admin.php b/plugins/box/backup/backup.admin.php
new file mode 100644
index 0000000..6580be5
--- /dev/null
+++ b/plugins/box/backup/backup.admin.php
@@ -0,0 +1,60 @@
+readDir(STORAGE . DS, false);
+
+ // Add public folder
+ if (Request::post('add_public_folder')) $zip->readDir(ROOT . DS . 'public' . DS, false);
+
+ // Add plugins folder
+ if (Request::post('add_plugins_folder')) $zip->readDir(PLUGINS . DS, false);
+
+ $zip->archive($backups_path . DS . Date::format(time(), "Y-m-d-H-i-s").'.zip');
+ }
+
+ // Delete backup
+ // -------------------------------------
+ if (Request::get('sub_id') == 'backup') {
+ if (Request::get('delete_file')) {
+ File::delete($backups_path . DS . Request::get('delete_file'));
+ Request::redirect(Option::get('siteurl').'admin/index.php?id=backup');
+ }
+ }
+
+ // Download backup
+ // -------------------------------------
+ if (Request::get('download')) {
+ File::download('../backups/'.Request::get('download'));
+ }
+
+ // Get backup list
+ $backups_list = File::scan($backups_path, '.zip');
+
+ // Display view
+ View::factory('box/backup/views/backend/index')
+ ->assign('backups_list', $backups_list)
+ ->display();
+ }
+ }
\ No newline at end of file
diff --git a/plugins/box/backup/backup.plugin.php b/plugins/box/backup/backup.plugin.php
new file mode 100644
index 0000000..8e03513
--- /dev/null
+++ b/plugins/box/backup/backup.plugin.php
@@ -0,0 +1,34 @@
+
++ | + | + |
+ + + | ++ | + 'btn btn-actions', 'onclick' => "return confirmDelete('".__('Delete backup: :backup', 'backup', array(':backup' => Date::format($name, 'F jS, Y - g:i A')))."')")); + ?> + | +
+ | + 'btn btn-actions')); ?> + 'btn btn-actions', 'onclick' => "return confirmDelete('".__('Delete block: :block', 'blocks', array(':block' => basename($block, '.block.html')))."')")); + ?> + | +
+ | + | + | + |
+ + | ++ + | ++ + | ++ 'btn', 'onclick' => "return confirmDelete('".__('Delete directory: :dir', 'filesmanager', array(':dir' => $dir))."')")); + ?> + | +
+ '_blank'));?> + | ++ + | ++ + | ++ 'btn btn-actions', 'onclick' => "return confirmDelete('".__('Delete file: :file', 'filesmanager', array(':file' => $file))."')")); + ?> + | +
chmod -R a-w :path
' =>
+ 'The Monstra core directory (":path") and/or files underneath it has been found to be writable. We would advise you to remove all write permissions. chmod -R a-w :path
',
+ 'The Monstra .htaccess file has been found to be writable. We would advise you to remove all write permissions. chmod a-w :path
' =>
+ 'The Monstra .htaccess file has been found to be writable. We would advise you to remove all write permissions. chmod a-w :path
',
+ 'The Monstra index.php file has been found to be writable. We would advise you to remove all write permissions. chmod a-w :path
' =>
+ 'The Monstra index.php file has been found to be writable. We would advise you to remove all write permissions. chmod a-w :path
',
+ 'Due to the type and amount of information an error might give intruders when Core::$environment = Core::DEVELOPMENT, we strongly advise setting Core::PRODUCTION in production systems.',
+ 'Due to the type and amount of information an error might give intruders when Core::$environment = Core::DEVELOPMENT, we strongly advise setting Core::PRODUCTION in production systems.',
+ )
+ );
\ No newline at end of file
diff --git a/plugins/box/information/languages/ru.lang.php b/plugins/box/information/languages/ru.lang.php
new file mode 100644
index 0000000..e89d7f9
--- /dev/null
+++ b/plugins/box/information/languages/ru.lang.php
@@ -0,0 +1,27 @@
+ array(
+ 'Information' => 'Информация',
+ 'Debuging' => 'Дебагинг',
+ 'Name' => 'Название',
+ 'Value' => 'Значение',
+ 'Security' => 'Безопасность',
+ 'System' => 'Система',
+ 'on' => 'включен',
+ 'off'=> 'выключен',
+ 'System version' => 'Версия системы',
+ 'System version ID' => 'Версия системы ID',
+ 'Security check results' => 'Результаты проверки безопасности',
+ 'The configuration file has been found to be writable. We would advise you to remove all write permissions on defines.php on production systems.' =>
+ 'Конфигурационный файл доступен для записи. Мы рекомендуем вам удалить права записи на файл defines.php на живом сайте.',
+ 'The Monstra core directory (":path") and/or files underneath it has been found to be writable. We would advise you to remove all write permissions. chmod -R a-w :path
' =>
+ 'Директория Monstra (":path") доступна для записи. Мы рекомендуем вам удалить права записи на директорию (":path") на живом сайте. chmod -R a-w :path
',
+ 'The Monstra .htaccess file has been found to be writable. We would advise you to remove all write permissions. chmod a-w :path
' =>
+ 'Главный .htaccess доступен для записи. Мы рекомендуем вам удалить права записи на главный .htaccess файл. chmod -R a-w :path
',
+ 'The Monstra index.php file has been found to be writable. We would advise you to remove all write permissions. chmod a-w :path
' =>
+ 'Главный index.php файл доступен для записи. Мы рекомендуем вам удалить права записи на главный index.php файл. chmod -R a-w :path
',
+ 'Due to the type and amount of information an error might give intruders when Core::$environment = Core::DEVELOPMENT, we strongly advise setting Core::PRODUCTION in production systems.' =>
+ 'Система работает в режиме Core::DEVELOPMENT Мы рекомендуем вам установить режим Core::PRODUCTION на живом сайте.',
+ )
+ );
\ No newline at end of file
diff --git a/plugins/box/information/views/backend/index.view.php b/plugins/box/information/views/backend/index.view.php
new file mode 100644
index 0000000..e47cc0c
--- /dev/null
+++ b/plugins/box/information/views/backend/index.view.php
@@ -0,0 +1,89 @@
+
++ | + |
+ | + |
+ | + |
+ | + |
+ | + |
+ | |
! | ++ |
! | +You can do this on unix systems with: chmod -R a-w :path ', 'information', array(':path' => MONSTRA . DS)); ?> |
+
! | +You can do this on unix systems with: chmod a-w :path ', 'information', array(':path' => ROOT . DS . '.htaccess')); ?> |
+
! | +You can do this on unix systems with: chmod a-w :path ', 'information', array(':path' => ROOT . DS . 'index.php')); ?> |
+
! | ++ |
+ * echo Page::title();
+ *
+ *
+ * @return string
+ */
+ public static function title() {
+ return Pages::$page['title'];
+ }
+
+
+ /**
+ * Get pages Description
+ *
+ *
+ * echo Page::description();
+ *
+ *
+ * @return string
+ */
+ public static function description() {
+ return Pages::$page['description'];
+ }
+
+
+ /**
+ * Get pages Keywords
+ *
+ *
+ * echo Page::keywords();
+ *
+ *
+ * @return string
+ */
+ public static function keywords() {
+ return Pages::$page['keywords'];
+ }
+
+ }
+
+
+ class Page extends Pages {
+
+
+ /**
+ * Get date of current page
+ *
+ *
+ * echo Page::date();
+ *
+ *
+ * @return string
+ */
+ public static function date() {
+ return Date::format(Pages::$page['date'], 'Y-m-d');
+ }
+
+
+ /**
+ * Get author of current page
+ *
+ *
+ * echo Page::author();
+ *
+ *
+ * @return string
+ */
+ public static function author() {
+ return Pages::$page['author'];
+ }
+
+
+ /**
+ * Get children pages for a specific parent page
+ *
+ *
+ * $pages = Page::children('page');
+ *
+ *
+ * @param string $parent Parent page
+ * @return array
+ */
+ public static function children($parent) {
+ return Pages::$pages->select('[parent="'.(string)$parent.'"]', 'all');
+ }
+
+
+ /**
+ * Get the available children pages for requested page.
+ *
+ *
+ * echo Page::available();
+ *
+ *
+ */
+ public static function available() {
+ $pages = Pages::$pages->select('[parent="'.Pages::$requested_page.'"]', 'all');
+
+ // Display view
+ View::factory('box/pages/views/frontend/available_pages')
+ ->assign('pages', $pages)
+ ->display();
+ }
+
+
+ /**
+ * Get page breadcrumbs
+ *
+ *
+ * echo Page::breadcrumbs();
+ *
+ *
+ */
+ public static function breadcrumbs() {
+ $current_page = Pages::$requested_page;
+ if ($current_page !== 'error404') {
+ $page = Pages::$pages->select('[slug="'.$current_page.'"]', null);
+ if (trim($page['parent']) !== '') {
+ $parent = true;
+ $parent_page = Pages::$pages->select('[slug="'.$page['parent'].'"]', null);
+ } else {
+ $parent = false;
+ }
+
+ // Display view
+ View::factory('box/pages/views/frontend/breadcrumbs')
+ ->assign('current_page', $current_page)
+ ->assign('page', $page)
+ ->assign('parent', $parent)
+ ->assign('parent_page', $parent_page)
+ ->display();
+ }
+ }
+
+
+ /**
+ * Get page url
+ *
+ *
+ * echo Page::url();
+ *
+ *
+ */
+ public static function url() {
+ return Option::get('siteurl').Pages::$page['slug'];
+ }
+
+
+ /**
+ * Get page slug
+ *
+ *
+ * echo Page::slug();
+ *
+ *
+ */
+ public static function slug() {
+ return Pages::$page['slug'];
+ }
+
+
+ /**
+ * Get page meta robots
+ *
+ *
+ * echo Page::robots();
+ *
+ *
+ */
+ public static function robots() {
+ return (Pages::$page !== null) ? Pages::$page['robots_index'].', '.Pages::$page['robots_follow'] : '';
+ }
+
+ }
\ No newline at end of file
diff --git a/plugins/box/pages/views/backend/add.view.php b/plugins/box/pages/views/backend/add.view.php
new file mode 100644
index 0000000..f5a4519
--- /dev/null
+++ b/plugins/box/pages/views/backend/add.view.php
@@ -0,0 +1,133 @@
++ | + | + | + | + |
+ + + '_blank')); ?> + | ++ + | ++ + | ++ + | ++ | + +
+ | + | + | + | + |
+ plugin_name; ?> + | ++ plugin_description; ?> + | ++ plugin_author; ?> + | ++ plugin_version; ?> + | ++ 'btn btn-actions')); ?> + 'btn btn-actions', 'onclick' => "return confirmDelete('".__('Delete plugin :plugin', 'plugins', array(':plugin' => Text::lowercase(basename($plug['path'],'.manifest.xml'))) )."')")); + ?> + | +
+ | + 'btn btn-actions')); ?> + 'btn btn-actions', 'onclick' => "return confirmDelete('".__('Delete snippet: :snippet', 'snippets', array(':snippet' => basename($snippet, '.snippet.php')))."')")); + ?> + | +
+ | + | + +
+ | + | + +
+ | + | + +
+ | + | + +
+ | + | + | + | + |
+ + | ++ + | ++ + | ++ + | ++ 'btn btn-actions')); ?> + 'btn btn-actions', 'onclick' => "return confirmDelete('".__('Delete user: :user', 'users', array(':user' => Html::toText($user['login'])))."')")); + ?> + | +
+ |
+ + | +
', closeWith:'
\n'}, + onTab: {keepDefault:false, openWith:' '}, + markupSet: [ + {name:'Heading 1', key:'1', openWith:'', closeWith:'
' }, + {separator:'---------------' }, + {name:'Bold', key:'B', openWith:'(!(|!|)!)', closeWith:'(!(|!|)!)' }, + {name:'Italic', key:'I', openWith:'(!(|!|)!)', closeWith:'(!(|!|)!)' }, + {name:'Stroke through', key:'S', openWith:'t |