1
0
mirror of https://github.com/monstra-cms/monstra.git synced 2025-07-31 18:30:20 +02:00

#431 initial commit for MONSTRA 4

This commit is contained in:
Awilum
2018-02-27 23:31:11 +03:00
parent 9a19078870
commit acfa870de9
46 changed files with 1760 additions and 0 deletions

BIN
.DS_Store vendored Normal file → Executable file

Binary file not shown.

6
.gitignore vendored Executable file
View File

@@ -0,0 +1,6 @@
.idea
.DS_Store
composer.phar
composer.lock
vendor
site-katerinasitnikova

63
.htaccess Executable file
View File

@@ -0,0 +1,63 @@
#
# This file is part of the Monstra.
#
# (c) Romanenko Sergey / Awilum <awilum@msn.com>
#
# For the full copyright and license information, please view the LICENSE
# file that was distributed with this source code.
#
# Set default charset utf-8
AddDefaultCharset UTF-8
# PHP 5, Apache 1 and 2.
<IfModule mod_php5.c>
php_flag magic_quotes_gpc off
php_flag magic_quotes_sybase off
php_flag register_globals off
</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine on
## Begin - Rewrite rules to block out some common exploits.
# If you experience problems on your site block out the operations listed below
# This attempts to block the most common type of exploit `attempts` to Monstra
#
# Block out any script trying to base64_encode data within the URL.
RewriteCond %{QUERY_STRING} base64_encode[^(]*\([^)]*\) [OR]
# Block out any script that includes a <script> tag in URL.
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
# Block out any script trying to set a PHP GLOBALS variable via URL.
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
# Block out any script trying to modify a _REQUEST variable via URL.
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
# Return 403 Forbidden header and show the content of the root homepage
RewriteRule .* index.php [F]
#
## End - Rewrite rules to block out some common exploits.
## Begin - Rewrite rules for Monstra
RewriteBase /monstra/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
## End - Rewrite rules for Monstra
## Begin - Rewrite rules for SEO improvements.
# RewriteCond %{HTTP_HOST} ^www.example.org [NC]
# RewriteRule ^(.*)$ http://example.org/$1 [R=301,L]
# Redirect 301 /index http://example.org/
## End - Rewrite rules for SEO improvements.
</IfModule>
# Prevent visitors from viewing files directly.
<FilesMatch "(^#.*#|\.(md|txt|html|tpl|yml|yaml)|~)$">
Order allow,deny
Deny from all
Satisfy All
</FilesMatch>
# Don't show directory listings for URLs which map to a directory.
Options -Indexes

2
CHANGELOG.md Executable file
View File

@@ -0,0 +1,2 @@
# Monstra 4.0.0, 2018-03-30
*

21
LICENSE.md Executable file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018 Monstra Content Management
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

47
README.md Executable file
View File

@@ -0,0 +1,47 @@
# Monstra
[![Join the chat at https://gitter.im/Monstra/Monstra](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Monstra/Monstra?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Monstra is Modern Open Source Flat-File Content Management site.
Content in Monstra is just a simple files written with markdown syntax in pages folder.
You simply create markdown files in the pages folder and that becomes a page.
## Requirements
PHP 5.5.9 or higher with PHP's [Multibyte String module](http://php.net/mbstring)
Apache with [Mod Rewrite](http://httpd.apache.org/docs/current/mod/mod_rewrite.html)
## Installation
#### Using (S)FTP
[Download the latest version.](http://monstra.org/download)
Unzip the contents to a new folder on your local computer, and upload to your webhost using the (S)FTP client of your choice. After youve done this, be sure to chmod the following directories (with containing files) to 777, so they are readable and writable by Monstra:
* `cache/`
* `config/`
* `storage/`
* `themes/`
* `plugins/`
#### Using Composer
You can easily install Monstra with Composer.
```
composer create-project monstra-cms/monstra
```
## Contributing
1. Help on the [Forum.](http://forum.Monstra.org)
2. Develop a new plugin.
3. Create a new theme.
4. Find and [report issues.](https://github.com/Monstra/Monstra/issues)
5. Link back to [Monstra](http://monstra.org).
## Links
- [Site](http://monstra.org)
- [Forum](http://forum.Monstra.org)
- [Documentation](http://monstra.org/documentation)
- [Github Repository](https://github.com/Monstra/Monstra)
## License
See [LICENSE](https://github.com/Monstra/Monstra/blob/master/LICENSE.md)

43
composer.json Executable file
View File

@@ -0,0 +1,43 @@
{
"name": "monstra-cms/monstra",
"type": "library",
"description": "Monstra is Modern Open Source Flat-File Content Management System",
"keywords": ["Monstra", "php", "cms", "flat-file", "markdown"],
"homepage": "http://monstra.org",
"license": "MIT",
"authors": [
{
"name": "Sergey Romanenko",
"email": "awilum@msn.com",
"homepage": "https://github.com/Awilum"
}
],
"support": {
"issues": "https://github.com/monstra-cms/monstra/issues"
},
"require": {
"php": ">=5.5.9",
"erusev/parsedown-extra": "0.7.*",
"mustangostang/spyc" : "0.5.*",
"doctrine/cache": "1.6.*",
"doctrine/collections": "1.3",
"force/session" : "*",
"force/filesystem" : "*",
"force/arr" : "*",
"force/http" : "*",
"force/shortcode" : "*",
"force/token" : "*",
"force/url" : "*"
},
"autoload": {
"classmap": [
"Monstra"
],
"files": [
"Monstra/boot/defines.php",
"Monstra/boot/shortcodes.php",
"Monstra/boot/filters.php",
"Monstra/boot/actions.php"
]
}
}

BIN
favicon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

10
index.php Executable file
View File

@@ -0,0 +1,10 @@
<?php
// Monstra requires PHP 5.5.9 or greater
version_compare(PHP_VERSION, "5.5.9", "<") and exit("Monstra requires PHP 5.5.9 or greater.");
// Register the auto-loader.
require_once __DIR__ . '/vendor/autoload.php';
// Initialize Monstra Application
Monstra::init();

113
monstra/Action.php Executable file
View File

@@ -0,0 +1,113 @@
<?php
/**
* This file is part of the Monstra.
*
* (c) Romanenko Sergey / Awilum <awilum@msn.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Action
{
/**
* Actions
*
* @var array
* @access protected
*/
protected static $actions = [];
/**
* Protected constructor since this is a static class.
*
* @access protected
*/
protected function __construct()
{
// Nothing here
}
/**
* Hooks a function on to a specific action.
*
* <code>
* // Hooks a function "newLink" on to a "footer" action.
* Action::add('footer', 'newLink', 10);
*
* function newLink() {
* echo '<a href="#">My link</a>';
* }
* </code>
*
* @access public
* @param string $action_name Action name
* @param mixed $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.
static::$actions[] = array(
'action_name' => (string) $action_name,
'function' => $added_function,
'priority' => (int) $priority,
'args' => $args
);
}
/**
* Run functions hooked on a specific action hook.
*
* <code>
* // Run functions hooked on a "footer" action hook.
* Action::run('footer');
* </code>
*
* @access public
* @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 = [], $return = false)
{
// Redefine arguments
$action_name = (string) $action_name;
$return = (bool) $return;
// Run action
if (count(static::$actions) > 0) {
// Sort actions by priority
$actions = Arr::subvalSort(static::$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']);
}
}
}
}
}
}
}

102
monstra/Blocks.php Executable file
View File

@@ -0,0 +1,102 @@
<?php
/**
* This file is part of the Monstra.
*
* (c) Romanenko Sergey / Awilum <awilum@msn.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Blocks
{
/**
* An instance of the Blocks class
*
* @var object
* @access protected
*/
protected static $instance = null;
/**
* Protected clone method to enforce singleton behavior.
*
* @access protected
*/
protected function __clone()
{
// Nothing here.
}
/**
* Constructor.
*
* @access protected
*/
protected function __construct()
{
$blocks_cache_id = '';
$blocks = File::scan(STORAGE_PATH . '/blocks', 'md');
if ($blocks) {
foreach ($blocks as $block) {
$blocks_cache_id .= filemtime($block);
}
// Create Unique Cache ID for Block
$blocks_cache_id = md5('blocks' . ROOT_DIR . $blocks_cache_id);
}
if (Cache::driver()->contains($blocks_cache_id)) {
Cache::driver()->fetch($blocks_cache_id);
} else {
Config::set('site.pages.flush_cache', true);
Cache::driver()->save($blocks_cache_id, $blocks_cache_id);
}
}
/**
* Get Page Block
*
* <code>
* $block = Blocks::get('my-block');
* </code>
*
* @access public
* @param string $name Block name
* @return string Formatted Block content
*/
public static function get($name)
{
if (File::exists($block_path = STORAGE_PATH .'/blocks/' . $name . '.md')) {
// Create Unique Cache ID for Block
$block_cache_id = md5('block' . ROOT_DIR . $block_path . filemtime($block_path));
if (Cache::driver()->contains($block_cache_id)) {
return Cache::driver()->fetch($block_cache_id);
} else {
Cache::driver()->save($block_cache_id, $block = Filter::apply('content', file_get_contents($block_path)));
return $block;
}
} else {
return 'Block '.$name.' is not found!';
}
}
/**
* Initialize Monstra Blocks
*
* <code>
* Blocks::init();
* </code>
*
* @access public
*/
public static function init()
{
return !isset(self::$instance) and self::$instance = new Blocks();
}
}

248
monstra/Cache.php Executable file
View File

@@ -0,0 +1,248 @@
<?php
/**
* This file is part of the Monstra.
*
* (c) Romanenko Sergey / Awilum <awilum@msn.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Cache
{
/**
* An instance of the Cache class
*
* @var object
*/
protected static $instance = null;
/**
* Unique cache key
*
* @var string Cache key.
*/
protected static $key;
/**
* Lifetime
*
* @var int Lifetime.
*/
protected static $lifetime;
/**
* Current time
*
* @var int Current time.
*/
protected static $now;
/**
* Cache Driver
*
* @var DoctrineCache
*/
protected static $driver;
/**
* Protected clone method to enforce singleton behavior.
*
* @access protected
*/
protected function __clone()
{
// Nothing here.
}
/**
* Constructor.
*
* @access protected
*/
protected function __construct()
{
// Set current time
static::$now = time();
// Cache key allows us to invalidate all cache on configuration changes.
static::$key = (Config::get('site.cache.prefix') ? Config::get('site.cache.prefix') : 'Monstra') . '-' . md5(ROOT_DIR . Monstra::VERSION);
// Get Cache Driver
static::$driver = static::getCacheDriver();
// Set the cache namespace to our unique key
static::$driver->setNamespace(static::$key);
}
/**
* Get Cache Driver
*
* @access public
* @return object
*/
public static function getCacheDriver()
{
$driver_name = Config::get('site.cache.driver');
if (!$driver_name || $driver_name == 'auto') {
if (extension_loaded('apc')) {
$driver_name = 'apc';
} elseif (extension_loaded('wincache')) {
$driver_name = 'wincache';
} elseif (extension_loaded('xcache')) {
$driver_name = 'xcache';
}
} else {
$driver_name = 'file';
}
switch ($driver_name) {
case 'apc':
$driver = new \Doctrine\Common\Cache\ApcCache();
break;
case 'wincache':
$driver = new \Doctrine\Common\Cache\WinCacheCache();
break;
case 'xcache':
$driver = new \Doctrine\Common\Cache\XcacheCache();
break;
case 'memcache':
$memcache = new \Memcache();
$memcache->connect(Config::get('site.cache.memcache.server', 'localhost'),
Config::get('site.cache.memcache.port', 11211));
$driver = new \Doctrine\Common\Cache\MemcacheCache();
$driver->setMemcache($memcache);
break;
case 'redis':
$redis = new \Redis();
$redis->connect(Config::get('site.cache.redis.server', 'localhost'),
Config::get('site.cache.redis.port', 6379));
$driver = new \Doctrine\Common\Cache\RedisCache();
$driver->setRedis($redis);
break;
default:
// Create doctrine cache directory if its not exists
!Dir::exists($cache_directory = CACHE_PATH . '/doctrine/') and Dir::create($cache_directory);
$driver = new \Doctrine\Common\Cache\FilesystemCache($cache_directory);
break;
}
return $driver;
}
/**
* Returns driver variable
*
* @access public
* @return object
*/
public static function driver()
{
return static::$driver;
}
/**
* Get cache key.
*
* @access public
* @return string
*/
public static function getKey()
{
return static::$key;
}
/**
* Fetches an entry from the cache.
*
* @access public
* @param string $id The id of the cache entry to fetch.
* @return mixed The cached data or FALSE, if no cache entry exists for the given id.
*/
public function fetch($id)
{
if (Config::get('site.cache.enabled')) {
return static::$driver->fetch($id);
} else {
return false;
}
}
/**
* Puts data into the cache.
*
* @access public
* @param string $id The cache id.
* @param mixed $data The cache entry/data.
* @param int $lifeTime The lifetime in number of seconds for this cache entry.
* If zero (the default), the entry never expires (although it may be deleted from the cache
* to make place for other entries).
*/
public function save($id, $data, $lifetime = null)
{
if (Config::get('site.cache.enabled')) {
if ($lifetime === null) {
$lifetime = static::getLifetime();
}
static::$driver->save($id, $data, $lifetime);
}
}
/**
* Clear Cache
*/
public static function clear()
{
Dir::delete(CACHE_PATH . '/doctrine/');
}
/**
* Set the cache lifetime.
*
* @access public
* @param int $future timestamp
*/
public static function setLifetime($future)
{
if (!$future) {
return;
}
$interval = $future - $this->now;
if ($interval > 0 && $interval < static::getLifetime()) {
static::$lifetime = $interval;
}
}
/**
* Retrieve the cache lifetime (in seconds)
*
* @access public
* @return mixed
*/
public static function getLifetime()
{
if (static::$lifetime === null) {
static::$lifetime = Config::get('site.cache.lifetime') ?: 604800;
}
return static::$lifetime;
}
/**
* Initialize Monstra Cache
*
* <code>
* Cache::init();
* </code>
*
* @access public
* @return object
*/
public static function init()
{
return !isset(self::$instance) and self::$instance = new Cache();
}
}

113
monstra/Config.php Executable file
View File

@@ -0,0 +1,113 @@
<?php
/**
* This file is part of the Monstra.
*
* (c) Romanenko Sergey / Awilum <awilum@msn.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Config
{
/**
* An instance of the Config class
*
* @var object
* @access protected
*/
protected static $instance = null;
/**
* Config
*
* @var array
* @access protected
*/
protected static $config = [];
/**
* Protected clone method to enforce singleton behavior.
*
* @access protected
*/
protected function __clone()
{
// Nothing here.
}
/**
* Constructor.
*
* @access protected
*/
protected function __construct()
{
static::$config['site'] = Yaml::parseFile(CONFIG_PATH . '/' . 'site.yml');
}
/**
* Set new or update existing config variable
*
* <code>
* Config::set('site.title', 'value');
* </code>
*
* @access public
* @param string $key Key
* @param mixed $value Value
*/
public static function set($key, $value)
{
Arr::set(static::$config, $key, $value);
}
/**
* Get config variable
*
* <code>
* Config::get('site');
* Config::get('site.title');
* Config::get('site.title', 'Default title');
* </code>
*
* @access public
* @param string $key Key
* @param mixed $default Default value
* @return mixed
*/
public static function get($key, $default = null)
{
return Arr::get(static::$config, $key, $default);
}
/**
* Get config array
*
* <code>
* $config = Config::getConfig();
* </code>
*
* @access public
* @return array
*/
public static function getConfig()
{
return static::$config;
}
/**
* Initialize Monstra Config
*
* <code>
* Config::init();
* </code>
*
* @access public
*/
public static function init()
{
return !isset(self::$instance) and self::$instance = new Config();
}
}

120
monstra/Filter.php Executable file
View File

@@ -0,0 +1,120 @@
<?php
/**
* This file is part of the Monstra.
*
* (c) Romanenko Sergey / Awilum <awilum@msn.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Filter
{
/**
* Filters
*
* @var array
* @access protected
*/
protected static $filters = [];
/**
* Protected constructor since this is a static class.
*
* @access protected
*/
protected function __construct()
{
// Nothing here
}
/**
* Apply filters
*
* <code>
* Filter::apply('content', $content);
* </code>
*
* @access public
* @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(static::$filters[$filter_name])) {
return $value;
}
foreach (static::$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
*
* <code>
* Filter::add('content', 'replacer');
*
* function replacer($content) {
* return preg_replace(array('/\[b\](.*?)\[\/b\]/ms'), array('<strong>\1</strong>'), $content);
* }
* </code>
*
* @access public
* @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 = $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(static::$filters[$filter_name]["$priority"])) {
foreach (static::$filters[$filter_name]["$priority"] as $filter) {
if ($filter['function'] == $function_to_add) {
return true;
}
}
}
static::$filters[$filter_name]["$priority"][] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
// Sort
ksort(static::$filters[$filter_name]["$priority"]);
return true;
}
}

39
monstra/Markdown.php Executable file
View File

@@ -0,0 +1,39 @@
<?php
/**
* This file is part of the Monstra.
*
* (c) Romanenko Sergey / Awilum <awilum@msn.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Markdown
{
/**
* Parsedown Extra Object
*
* @var object
* @access protected
*/
protected static $markdown;
/**
* Markdown parser
*
* <code>
* $content = Markdown::parse($content);
* </code>
*
* @access public
* @param string $content Content to parse
* @return string Formatted content
*/
public static function parse($content)
{
!static::$markdown and static::$markdown = new ParsedownExtra();
return static::$markdown->text($content);
}
}

98
monstra/Monstra.php Executable file
View File

@@ -0,0 +1,98 @@
<?php
/**
* Monstra
*
* @package Monstra
* @author Romanenko Sergey / Awilum <awilum@msn.com>
* @link http://monstra.org
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Monstra
{
/**
* An instance of the Monstra class
*
* @var object
* @access protected
*/
protected static $instance = null;
/**
* The version of Monstra
*
* @var string
*/
const VERSION = '4.0.0 alpha';
/**
* Protected clone method to enforce singleton behavior.
*
* @access protected
*/
protected function __clone()
{
// Nothing here.
}
/**
* Constructor.
*
* @access protected
*/
protected function __construct()
{
// Init Config
Config::init();
// Turn on output buffering
ob_start();
// Display Errors
Config::get('site.errors.display') and error_reporting(-1);
// Set internal encoding
function_exists('mb_language') and mb_language('uni');
function_exists('mb_regex_encoding') and mb_regex_encoding(Config::get('site.charset'));
function_exists('mb_internal_encoding') and mb_internal_encoding(Config::get('site.charset'));
// Set default timezone
date_default_timezone_set(Config::get('site.timezone'));
// Start the session
Session::start();
// Init Cache
Cache::init();
// Init Plugins
Plugins::init();
// Init Blocks
Blocks::init();
// Init Pages
Pages::init();
// Flush (send) the output buffer and turn off output buffering
ob_end_flush();
}
/**
* Initialize Monstra Application
*
* <code>
* Monstra::init();
* </code>
*
* @access public
* @return object
*/
public static function init()
{
return !isset(self::$instance) and self::$instance = new Monstra();
}
}

309
monstra/Pages.php Executable file
View File

@@ -0,0 +1,309 @@
<?php
/**
* This file is part of the Monstra.
*
* (c) Romanenko Sergey / Awilum <awilum@msn.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Pages
{
/**
* An instance of the Pages class
*
* @var object
* @access protected
*/
protected static $instance = null;
/**
* Current page.
*
* @var array
* @access protected
*/
protected static $current_page;
/**
* Current page template.
*
* @var object
* @access protected
*/
protected static $current_template;
/**
* Protected clone method to enforce singleton behavior.
*
* @access protected
*/
protected function __clone()
{
// Nothing here.
}
/**
* Constructor.
*
* @access protected
*/
protected function __construct()
{
// Get Current Page
static::$current_page = static::getPage(Url::getUriString());
// Get Theme Templates
static::$current_template = ((!empty(static::$current_page['template'])) ? static::$current_page['template'] : 'index');
// Send default header
header('Content-Type: text/html; charset='.Config::get('site.charset'));
// Run actions before page rendered
Action::run('before_page_rendered');
// Display page for current requested url
static::display(static::$current_page);
// Run actions after page rendered
Action::run('after_page_rendered');
}
/**
* Get pages
*
* <code>
* $pages = Pages::getPages('blog');
* </code>
*
* @access public
* @param string $url Url
* @param string $order_by Order by
* @param string $order_type Order type
* @param array $ignore Pages to ignore
* @param int $limit Limit of pages
* @return array
*/
public static function getPages($url = '', $order_by = 'date', $order_type = 'DESC', $ignore = array('404'), $limit = null)
{
$pages = File::scan(STORAGE_PATH . '/pages/' . $url, 'md');
if ($pages) {
foreach ($pages as $page) {
$pages_cache_id .= filemtime($page);
}
// Create Unique Cache ID for Pages
$pages_cache_id = md5('pages' . ROOT_DIR . $url . $order_by . $order_type . implode(",", $ignore) . (($limit === null) ? 'null' : $limit) . $pages_cache_id);
}
if (Cache::driver()->contains($pages_cache_id)) {
return Cache::driver()->fetch($pages_cache_id);
} else {
foreach ($pages as $key => $page) {
if (!in_array(basename($page, '.md'), $ignore)) {
$content = file_get_contents($page);
$_page = explode('---', $content, 3);
$_pages[$key] = Yaml::parse($_page[1]);
$url = str_replace(STORAGE_PATH . '/pages', Url::getBase(), $page);
$url = str_replace('index.md', '', $url);
$url = str_replace('.md', '', $url);
$url = str_replace('\\', '/', $url);
$url = rtrim($url, '/');
$_pages[$key]['url'] = $url;
$_content = $_page[2];
// Parse page for summary <!--more-->
if (($pos = strpos($_content, "<!--more-->")) === false) {
$_content = Filter::apply('content', $_content);
} else {
$_content = explode("<!--more-->", $_content);
$_content['summary'] = Filter::apply('content', $_content[0]);
$_content['content'] = Filter::apply('content', $_content[0].$_content[1]);
}
if (is_array($_content)) {
$_pages[$key]['summary'] = $_content['summary'];
$_pages[$key]['content'] = $_content['content'];
} else {
$_pages[$key]['summary'] = $_content;
$_pages[$key]['content'] = $_content;
}
$_pages[$key]['slug'] = basename($page, '.md');
}
}
$_pages = Arr::subvalSort($_pages, $order_by, $order_type);
if ($limit != null) {
$_pages = array_slice($_pages, null, $limit);
}
Cache::driver()->save($pages_cache_id, $_pages);
return $_pages;
}
}
/**
* Get page
*
* <code>
* $page = Pages::getPage('downloads');
* </code>
*
* @access public
* @param string $url Url
* @return array
*/
public static function getPage($url)
{
// If url is empty that its a homepage
if ($url) {
$file = STORAGE_PATH . '/pages/' . $url;
} else {
$file = STORAGE_PATH . '/pages/' . 'index';
}
// Select the file
if (is_dir($file)) {
$file = STORAGE_PATH . '/pages/' . $url .'/index.md';
} else {
$file .= '.md';
}
// Get 404 page if file not exists
if (!file_exists($file)) {
$file = STORAGE_PATH . '/pages/' . '404.md';
Response::status(404);
}
// Create Unique Cache ID for requested page
$page_cache_id = md5('page' . ROOT_DIR . $file . filemtime($file));
if (Cache::driver()->contains($page_cache_id) && Config::get('site.pages.flush_cache') == false) {
return Cache::driver()->fetch($page_cache_id);
} else {
$content = file_get_contents($file);
$_page = explode('---', $content, 3);
$page = Yaml::parse($_page[1]);
$url = str_replace(STORAGE_PATH . '/pages', Url::getBase(), $file);
$url = str_replace('index.md', '', $url);
$url = str_replace('.md', '', $url);
$url = str_replace('\\', '/', $url);
$url = rtrim($url, '/');
$page['url'] = $url;
$_content = $_page[2];
// Parse page for summary <!--more-->
if (($pos = strpos($_content, "<!--more-->")) === false) {
$_content = Filter::apply('content', $_content);
} else {
$_content = explode("<!--more-->", $_content);
$_content['summary'] = Filter::apply('content', $_content[0]);
$_content['content'] = Filter::apply('content', $_content[0].$_content[1]);
}
if (is_array($_content)) {
$page['summary'] = $_content['summary'];
$page['content'] = $_content['content'];
} else {
$page['content'] = $_content;
}
$page['slug'] = basename($file, '.md');
// Overload page title, keywords and description if needed
empty($page['title']) and $page['title'] = Config::get('site.title');
empty($page['keywords']) and $page['keywords'] = Config::get('site.keywords');
empty($page['description']) and $page['description'] = Config::get('site.description');
Cache::driver()->save($page_cache_id, $page);
return $page;
}
}
/**
* Get Current Page
*
* <code>
* $page = Pages::getCurrentPage();
* </code>
*
* @return array
*/
public static function getCurrentPage()
{
return static::$current_page;
}
/**
* Update Current Page
*
* <code>
* Pages::updateCurrentPage('title', 'My new Page Title');
* </code>
*
* @return array
*/
public static function updateCurrentPage($path, $value)
{
Arr::set(static::$current_page, $path, $value);
}
/**
* Display Page
*
* <code>
* Pages::display($page);
* </code>
*
* @access public
* @param array $page Page array
* @return string
*/
public static function display($page)
{
Theme::getTemplate(((!empty($page['template'])) ? $page['template'] : 'index'));
}
/**
* Get Current Template
*
* <code>
* $template = Pages::getCurrentTemplate();
* </code>
*
* @access public
* @return object
*/
public static function getCurrentTemplate()
{
return static::$current_template;
}
/**
* Initialize Monstra Pages
*
* <code>
* Pages::init();
* </code>
*
* @access public
*/
public static function init()
{
return !isset(self::$instance) and self::$instance = new Pages();
}
}

116
monstra/Plugins.php Executable file
View File

@@ -0,0 +1,116 @@
<?php
/**
* This file is part of the Monstra.
*
* (c) Romanenko Sergey / Awilum <awilum@msn.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Plugins
{
/**
* An instance of the Plugins class
*
* @var object
* @access protected
*/
protected static $instance = null;
/**
* Protected clone method to enforce singleton behavior.
*
* @access protected
*/
protected function __clone()
{
// Nothing here.
}
/**
* Constructor.
*
* @access protected
*/
protected function __construct()
{
$plugins_cache_id = '';
$plugin_manifest = [];
$plugin_settings = [];
// Get Plugins List
$plugins_list = Config::get('site.plugins');
// If Plugins List isnt empty then create plugin cache ID
if (is_array($plugins_list) && count($plugins_list) > 0) {
// Go through...
foreach ($plugins_list as $plugin) {
if (File::exists($_plugin = PLUGINS_PATH . '/' . $plugin . '/' . $plugin . '.yml')) {
$plugins_cache_id .= filemtime($_plugin);
}
}
// Create Unique Cache ID for Plugins
$plugins_cache_id = md5('plugins' . ROOT_DIR . PLUGINS_PATH . $plugins_cache_id);
}
// Get plugins list from cache or scan plugins folder and create new plugins cache item
if (Cache::driver()->contains($plugins_cache_id)) {
Config::set('plugins', Cache::driver()->fetch($plugins_cache_id));
} else {
// If Plugins List isnt empty
if (is_array($plugins_list) && count($plugins_list) > 0) {
// Go through...
foreach ($plugins_list as $plugin) {
if (File::exists($_plugin_manifest = PLUGINS_PATH . '/' . $plugin . '/' . $plugin . '.yml')) {
$plugin_manifest = Yaml::parseFile($_plugin_manifest);
}
if (File::exists($_plugin_settings = PLUGINS_PATH . '/' . $plugin . '/settings.yml')) {
$plugin_settings = Yaml::parseFile($_plugin_settings);
}
$_plugins_config[File::name($_plugin_manifest)] = array_merge($plugin_manifest, $plugin_settings);
}
Config::set('plugins', $_plugins_config);
Cache::driver()->save($plugins_cache_id, $_plugins_config);
}
}
// Include enabled plugins
if (is_array(Config::get('plugins')) && count(Config::get('plugins')) > 0) {
foreach (Config::get('plugins') as $plugin_name => $plugin) {
if (Config::get('plugins.'.$plugin_name.'.enabled')) {
include_once PLUGINS_PATH .'/'. $plugin_name .'/'. $plugin_name . '.php';
}
}
}
// Run Actions on plugins_loaded
Action::run('plugins_loaded');
}
/**
* Initialize Monstra Plugins
*
* <code>
* Plugins::init();
* </code>
*
* @access public
*/
public static function init()
{
if (! isset(self::$instance)) {
self::$instance = new Plugins();
}
return self::$instance;
}
}

9
monstra/Theme.php Executable file
View File

@@ -0,0 +1,9 @@
<?php
class Theme
{
public static function getTemplate($template = null)
{
include THEMES_PATH . '/' . Config::get('site.theme') . '/' . $template . '.php';
}
}

59
monstra/Yaml.php Executable file
View File

@@ -0,0 +1,59 @@
<?php
/**
* This file is part of the Monstra.
*
* (c) Romanenko Sergey / Awilum <awilum@msn.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Yaml
{
/**
* Parses YAML to array.
*
* <code>
* $array = Yaml::parseFile('file.yml');
* </code>
*
* @access public
* @param string $file Path to YAML file.
* @return array
*/
public static function parseFile($file)
{
return Spyc::YAMLLoad($file);
}
/**
* Parses YAML to array.
*
* <code>
* $array = Yaml::parse('title: My title');
* </code>
*
* @param string $string YAML string.
* @return array
*/
public static function parse($string)
{
return Spyc::YAMLLoadString($string);
}
/**
* Dumps array to YAML.
*
* <code>
* $yaml = Yaml::dump($data);
* </code>
*
* @param array $data Array.
* @return string
*/
public static function dump($data)
{
return Spyc::YAMLDump($data, false, false, true);
}
}

15
monstra/boot/actions.php Executable file
View File

@@ -0,0 +1,15 @@
<?php
/**
* This file is part of the Monstra.
*
* (c) Romanenko Sergey / Awilum <awilum@msn.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// Set Monstra Meta Generator
Action::add('theme_meta', function () {
echo('<meta name="generator" content="Powered by Monstra" />');
});

34
monstra/boot/defines.php Executable file
View File

@@ -0,0 +1,34 @@
<?php
/**
* This file is part of the Monstra.
*
* (c) Romanenko Sergey / Awilum <awilum@msn.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// Define the path to the root directory (without trailing slash).
define('ROOT_DIR', str_replace(DIRECTORY_SEPARATOR, '/', getcwd()));
// Define the path to the storage directory (without trailing slash).
define('SITE_PATH', ROOT_DIR . '/site');
// Define the path to the storage directory (without trailing slash).
define('STORAGE_PATH', SITE_PATH . '/storage');
// Define the path to the themes directory (without trailing slash).
define('THEMES_PATH', SITE_PATH . '/themes');
// Define the path to the plugins directory (without trailing slash).
define('PLUGINS_PATH', SITE_PATH . '/plugins');
// Define the path to the config directory (without trailing slash).
define('CONFIG_PATH', SITE_PATH . '/config');
// Define the path to the cache directory (without trailing slash).
define('CACHE_PATH', SITE_PATH . '/cache');
// Define the path to the cache directory (without trailing slash).
define('ACCOUNTS_PATH', SITE_PATH . '/cache');

16
monstra/boot/filters.php Executable file
View File

@@ -0,0 +1,16 @@
<?php
/**
* This file is part of the Monstra.
*
* (c) Romanenko Sergey / Awilum <awilum@msn.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// Add Shortcode parser filter
Filter::add('content', 'Shortcode::parse', 1);
// Add Parsedown parser filter
Filter::add('content', 'Markdown::parse', 2);

22
monstra/boot/shortcodes.php Executable file
View File

@@ -0,0 +1,22 @@
<?php
/**
* This file is part of the Monstra.
*
* (c) Romanenko Sergey / Awilum <awilum@msn.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
// Add {block name=block-name} shortcode
Shortcode::add('block', function ($attributes) {
if (isset($attributes['name'])) {
return Blocks::get($attributes['name']);
}
});
// Add {site_url} shortcode
Shortcode::add('site_url', function () {
return Url::getBase();
});

5
robots.txt Executable file
View File

@@ -0,0 +1,5 @@
User-agent: *
Disallow: /monstra/
Disallow: /site/plugins/
Disallow: /site/config/
Disallow: /vendor/

0
site/accounts/.gitkeep Executable file
View File

0
site/cache/.gitkeep vendored Executable file
View File

View File

@@ -0,0 +1,3 @@
0
a:7:{s:5:"title";s:9:"Error 404";s:6:"robots";s:16:"noindex,nofollow";s:3:"url";s:33:"http://localhost:8888/monstra/404";s:7:"content";s:95:"<h2>Error 404</h2>
<p>We're sorry but the page you are looking for doesn't appear to exist!</p>";s:4:"slug";s:3:"404";s:8:"keywords";N;s:11:"description";s:54:"Modern Open Source Flat-File Content Management System";}

View File

@@ -0,0 +1,2 @@
0
a:6:{s:3:"url";s:33:"http://localhost:8888/monstra/404";s:7:"content";s:0:"";s:4:"slug";s:3:"404";s:5:"title";N;s:8:"keywords";N;s:11:"description";N;}

View File

@@ -0,0 +1,19 @@
0
a:7:{s:5:"title";s:7:"Welcome";s:11:"description";s:64:"Monstra is a simple and light-weighted Content Management System";s:8:"template";s:5:"index";s:3:"url";s:29:"http://localhost:8888/monstra";s:7:"content";s:823:"<h2>Monstra is succesfully installed!</h2>
<p>You can start editing the content and customising your site.</p>
<h3>Edit this Page</h3>
<p>To edit this page, simply go to the folder you installed Monstra, and then browse to the <code>/storage/pages/</code> folder and open the <code>index.md</code> file in your editor.</p>
<h3>Create a New page</h3>
<p>Creating a new page is very simple in Monstra. </p>
<ol>
<li>
<p>Launch your text editor and paste this sample text:</p>
<pre><code>---
title: My New Page
---
My new page body.</code></pre>
</li>
<li>Save this file in the <code>/storage/pages/</code> folder as <code>my-new-page.md</code> and its will be available by this url: <a href="http://yoursite/my-new-page">http://yoursite/my-new-page</a></li>
</ol>
<p>That is it! </p>
<p>Block Monstra-docs is not found!</p>";s:4:"slug";s:5:"index";s:8:"keywords";N;}

28
site/config/site.yml Executable file
View File

@@ -0,0 +1,28 @@
# Site configuration
title: 'Monstra'
description: 'Modern Open Source Flat-File Content Management System'
author:
email: ''
# System Configuration
timezone: UTC
charset: UTF-8
theme: default
plugins:
pages:
flush_cache: false
errors:
display: false
cache:
enabled: false
prefix: Monstra
driver: auto
lifetime: 604800

0
site/plugins/.gitkeep Executable file
View File

View File

@@ -0,0 +1 @@
Check **Monstra** documentation for more details: http://monstra.org/documentation

7
site/storage/pages/404.md Executable file
View File

@@ -0,0 +1,7 @@
---
title: Error 404
robots: noindex,nofollow
---
## Error 404
We're sorry but the page you are looking for doesn't appear to exist!

15
site/storage/pages/contact.md Executable file
View File

@@ -0,0 +1,15 @@
---
title: Contact
description: Monstra is a simple and light-weighted Content Management System
---
## Stay in touch
[Monstra official site](http://monstra.org/)
[Monstra forum](http://forum.Monstra.org/)
[Monstra on Github](https://github.com/Monstra/Monstra)
[Monstra Gitter chat room](https://gitter.im/Monstra/Monstra)
## Follow us on Twitter
Follow Monstra on Twitter [@Monstra_cms](https://twitter.com/Monstra_cms)

28
site/storage/pages/index.md Executable file
View File

@@ -0,0 +1,28 @@
---
title: Welcome
description: Monstra is a simple and light-weighted Content Management System
template: index
---
## Monstra is succesfully installed!
You can start editing the content and customising your site.
### Edit this Page
To edit this page, simply go to the folder you installed Monstra, and then browse to the `/storage/pages/` folder and open the `index.md` file in your editor.
### Create a New page
Creating a new page is very simple in Monstra.
1. Launch your text editor and paste this sample text:
```
---
title: My New Page
---
My new page body.
```
2. Save this file in the `/storage/pages/` folder as `my-new-page.md` and its will be available by this url: http://yoursite/my-new-page
That is it!
{block name=Monstra-docs}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

13
site/themes/default/default.yml Executable file
View File

@@ -0,0 +1,13 @@
name: Default
version: 1.0.0
description: Default Monstra theme
author:
name: Sergey Romanenko
email: awilum@msn.com
url: https://github.com/Awilum
homepage: https://github.com/Monstra/Monstra
bugs: https://github.com/Monstra/Monstra/issues
license: MIT
# Theme settings
enabled: true

3
site/themes/default/index.php Executable file
View File

@@ -0,0 +1,3 @@
<?php Theme::getTemplate('partials/head'); ?>
index
<?php Theme::getTemplate('partials/footer'); ?>

View File

@@ -0,0 +1,3 @@
<?php Theme::getTemplate('tail'); ?>
</body>
</html>

View File

@@ -0,0 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="../../../../favicon.ico">
<title>Cover Template for Bootstrap</title>
<!-- Bootstrap core CSS -->
<link href="../../../../dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="cover.css" rel="stylesheet">
</head>
<body>

View File

@@ -0,0 +1 @@
navigation here

View File

@@ -0,0 +1,5 @@
{* Bootstrap core JavaScript *}
{* Placed at the end of the document so the pages load faster *}
<script src="<?php echo Url::getBase(); ?>/themes/<?php echo Config::get('site.theme'); ?>/bower_components/jquery/dist/jquery.min.js"></script>
<script src="<?php echo Url::getBase(); ?>}/themes/<?php echo Config::get('site.theme'); ?>/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<?php Action::run('theme_footer'); ?>