The Urlaube CMS is a flat-file content management system. It has been developed with modularity in mind. The core consists of the files contained in ./system/
while all user-provided files are located in ./user/
.
Config SHALL be located in the ./user/config/config.php
file.
Content SHALL be located in the ./user/content/
directory and MAY be located in subdirectories. Each content file has to end with the extension .md
otherwise it is not considered to be a content file. Content files consist of header lines in the form of <Key>: <Value>
which are used to provide further information about the content to handlers, plugins and themes. The actual content of the file starts after a blank line and ends at the end of the file.
An example content file might look like this:
Category: myCategory examplePages
Date: 21.05.2018
Title: Example Page
This is an example page.
The core are the minimal files that make up the CMS. The following files belong to the core:
./index.php
is the main entrypoint into the CMS, no other source file SHOULD be directly callable./router.php
is the router file that can be used to test Urlaube locally by callingphp -S localhost:8080 ./router.php
./system/derived.php
contains constants that are derived from other values./system/init.php
is the place where all core files are included./system/recommended.php
contains constants that SHOULD be used by handlers, plugins and themes for interoperability./system/static.php
contains constants that are static./system/system.php
constains core functions that SHOULD NOT be used by handlers, plugins and themes./system/user.php
constains core functions that MAY be used by handlers, plugins and themes./system/core/BaseConfig.class.php
contains a base class with getters and setters for dynamic values./system/core/BaseHandler.class.php
contains a standard handler implementation./system/core/BaseSingleton.class.php
contains a simple singleton base class./system/core/Content.class.php
contains the content class that is used to represent content files./system/core/Handler.interface.php
contains the handler interface that SHOULD be used by all handler developers./system/core/Handlers.class.php
contains the handler management class./system/core/Logging.class.php
contains a simple logging class./system/core/Main.class.php
contains the main class that controls the workflow of the core./system/core/Plugin.interface.php
contains the plugin interface that SHOULD be used by all plugin developers./system/core/Plugins.class.php
contains the plugin management class./system/core/Theme.interface.php
contains the theme interface that SHOULD be used by all theme developers./system/core/Themes.class.php
contains the theme management class./system/core/Translate.class.php
containts a simple translation engine class
Handlers extend the core and react on certain URLs by matching a handler's regular expression against the relative URI that has been called by the user.
Handlers can be registered in the core by calling the following method:
Handlers::register($entity, $function, $regex, $methods = [GET], $priority = 0);
The register method has the following parameters:
$entity
is eithernull
, a class name or an object$function
is either the name of a function (when$entity
isnull
) or the name of a method of$entity
$regex
is the regular expression that is matched against the URI relative to the configured root URI$methods
is either a string or an array of strings denoting the HTTP methods the handler shall react on$priority
is the priority value of the handler to allow it to be called earlier
The following priority values SHOULD be used for now:
FIXURL
is the earliest stage when the URI has not even been normalizedADDSLASH
is the stage when the URI has been normalized but the trailing slash has not been added (useful for handling URIs denoting file names)USER
is the stage when most handlers run after the URI has been fully normalizedERROR
is the stage before the system's error handler collects all unhandled URIs
When a handler has successfully handled a URI, it MUST return true
to abort the further handling of the URI by other handlers.
At the moment the Urlaube CMS consists of the following system handlers that are located in ./system/handlers/
:
fixurl
normalizes the URI to discard garbagefaviconico
handles accesses to thefavicon.ico
file by returning a204 No Content
indexphp
handles accesses to theindex.php
file by redirecting to the configured root URIrobotstxt
handles accesses to therobots.txt
file by generating a minimal filesitemapxml
handles access to thesitemap.xml
file by generating a minimal fileaddslash
adds a trailing slash to the URIarchive
provides a paginated archive feature based on the content'sDate
fieldauthor
provides a paginated author feature based on the content'sAuthor
fieldcategory
provides a paginated category feature based on the content'sCategory
fieldfeed
provides an RSS feed for the archive, author, category and search featuresearch
provides a paginated search featurepage
handles access to single pages by converting the URI to the corresponding content file patherror
handles all URIs that have not been handled by another handler
Users MAY add their own handlers by putting them in the ./user/handlers/
directory.
User handlers MAY need additional configuration. To do this the handler management class provides the following method:
Handlers::set($name, $value);
The method has the following parameters:
$name
is the name of the configuration value$value
is the contents of the configuration value
Plugins extend the core and react on certain events.
Plugins MAY be registered in the core by calling the following method:
Plugins::register($entity, $function, $event);
The register method has the following parameters:
$entity
is eithernull
, a class name or an object$function
is either the name of a function (when$entity
isnull
) or the name of a method of$entity
$event
is the event name the plugin shall react on
The following core trigger events are available for now:
BEFORE_MAIN
is called before the core is runBEFORE_HANDLER
is called before the handlers are runBEFORE_THEME
is called before the theme is runAFTER_THEME
is called after the theme has finished runningAFTER_HANDLER
is called after the handlers have finished runningAFTER_MAIN
is called after the core has finished running
The following theme trigger events SHOULD be used for now:
BEFORE_HEAD
should be called by a theme before the head is generatedAFTER_HEAD
should be called by a theme after the head is generatedBEFORE_BODY
should be called by a theme before the body is generatedBEFORE_SIDEBAR
should be called by a theme before the sidebar is generatedAFTER_SIDEBAR
should be called by a theme after the sidebar is generatedAFTER_BODY
should be called by a theme after the body is generatedBEFORE_FOOTER
should be called by a theme before the footer is generatedAFTER_FOOTER
should be called by a theme after the footer is generated
The following core filter events are available for now:
FILTER_CONTENT
is called after theON_CONTENT
plugins have been called incallcontent()
FILTER_HANDLERS
is called after the handlers have been registeredFILTER_OUTPUT
is called beforeMain::run()
exitsFILTER_PAGINATE
is called before pagination is applied inpaginate()
FILTER_PLUGINS
is called after the plugins have been registeredFILTER_THEMES
is called after the themes have been registeredFILTER_WIDGETS
is called after theON_WIDGETS
plugins incallwidgets()
The following core content events are available for now:
ON_CONTENT
is called incallcontent()
ON_WIDGETS
is called incallwidgets()
The following cache events are available for now:
GET_CACHE
is called ingetcache()
SET_CACHE
is called insetcache()
- Trigger plugins just get executed and don't have to provide a certain behaviour.
- Filter plugins are provided with a content argument and SHOULD return content.
- Content plugins SHOULD return content.
At the moment the Urlaube CMS consists of the following system plugins that are located in ./system/plugins/
:
cache
is used to provide a file-based caching feature that uses serialize/unserializefile
is used by system handlers to load content fileshide
is used to provide a feature to hide content from certain pagesmarkdown
is used to provide markdown support which can be disables through thenomarkdown
fieldrelocate
is used to provide a relocation feature through theRelocate
andRelocateType
fieldssticky
is used to provide a stickiness feature through theSticky
field
Users MAY add their own plugins by putting them in the ./user/plugins/
directory.
User plugins MAY need additional configuration. To do this the plugin management class provides the following method:
Plugins::set($name, $value);
The method has the following parameters:
$name
is the name of the configuration value$value
is the contents of the configuration value
Themes extend the core and are called by a handler.
Themes can be registered in the core by calling the following method:
Themes::register($entity, $function, $name);
The register method has the following parameters:
$entity
is eithernull
, a class name or an object$function
is either the name of a function (when$entity
isnull
) or the name of a method of$entity
$name
is the name of the theme to be used in the config file
To use a theme its name SHOULD be configured in the config file located at ./user/config/config.php
:
Main::set(THEMENAME, "<NAME OF THE THEME>");
Users MUST add their own theme by putting it in the ./user/themes/
directory.
User themes MAY need additional configuration. To do this the theme management class provides the following method:
Themes::set($name, $value);
The method has the following parameters:
$name
is the name of the configuration value$value
is the contents of the configuration value
The Translate
class provides a translation feature based on simple JSON files.
New translations can be registered by calling the following method:
Translate::register($folder, $name = null);
The register method has the following parameters:
$folder
is the path to a folder that contains files named after potential LANGUAGE values (e.g.de_de
)$name
is an optional namespace for the registered translation, if no name is given, the translations are loaded into the global space
An example translation file might look like this:
{
"This" : "Das",
"is" : "ist",
"an" : "eine",
"example file" : "Testdatei",
"My name is %s." : "Mein Name ist %s."
}
To translate a basic string, the following method can be used:
Translate::get($string, $name = null)
The following parameters are allowed:
$string
is the string that shall be translated, this string is searched in the JSON files within the registered folders$name
is the namespace in which the translation shall be searched for, this is helpful to separate translations of different extensions
To translate a string containing placeholders as supported by PHP's sprintf()
function the following method can be used:
Translate::format($string, $name = null, ...$values)
The following parameters are allowed:
$string
is the string that shall be translated, this string is searched in the JSON files within the registered folders$name
is the namespace in which the translation shall be searched for, this is helpful to separate translations of different extensions$values
are one or more additional values that shall be used to replace the placeholders in the translated string
There is a shortcode function in ./system/user.php
to shorten the code necessary to get a translation:
t($string, $name = null, ...$values)
The following parameters are allowed:
$string
is the string that shall be translated, this string is searched in the JSON files within the registered folders$name
is the namespace in which the translation shall be searched for, this is helpful to separate translations of different extensions$values
if left out thenTranslate::get()
is called, if at least one addition $values parameter is set, thenTranslate::format()
is called
To install the Urlaube CMS you can clone the corresponding git repository:
git clone https://github.com/urlaube/urlaube
The Urlaube CMS uses a single entrypoint scheme. A .htaccess
is already provided for your convenience. To use the Urlaube CMS in combination with NGINX you have to implement the single entrypoint yourself:
if (!-f $request_filename) {
rewrite ^.*$ /index.php last;
}
The configuration takes place in the configuration file located at ./user/config/config.php
.
The following configuration values are currently supported:
Main::set(CACHE, false);
is the activation or deactivation of the cacheMain::set(CACHEAGE, 60*60);
is the number of seconds a cached value is considered to be fresh by defaultMain::set(CHARSET, "UTF-8");
is the charset used by the systemMain::set(CONTENTTYPE, "text/html");
is the default content type set by the systemMain::set(DEBUGMODE, false);
actives printing of warning and error messages
(Important: You SHOULD set this tofalse
in production.)Main::set(HOSTNAME, _getDefaultHostname());
is the hostname as taken from the URL
(Important: You SHOULD configure this value as the default is considered to be insure. The default is taken from$_SERVER["SERVER_NAME"]
,$_SERVER["HTTP_HOST"]
or$_SERVER["SERVER_ADDR"]
and islocalhost
as a fallback.)Main::set(LANGUAGE, "de_DE");
is the language used by the systemMain::set(LOGLEVEL, Logging::NONE);
is the minimum level of log entries that get printed
(Important: You SHOULD set this toLogging::NONE
in production or set a filename forLOGTARGET
.)Main::set(LOGTARGET, Logging::OUTPUT);
is the target of log entries (eitherLogging::OUTPUT
for direct output or a filename)Main::set(METHOD, _getDefaultMethod());
is the HTTP method used to call the system
(The default is derived from$_SERVER["REQUEST_METHOD"]
.)Main::set(PAGESIZE, 5);
is the number of entries per page displayed during paginationMain::set(PORT, _getDefaultPort());
is the port number as taken from the URL
(Important: You SHOULD configure this value as the default is considered to be insecure. The default is taken from$_SERVER["SERVER_PORT"]
.)Main::set(PROTOCOL, _getDefaultProtocol());
is the protocol as taken from the URL
(Important: You SHOULD configure this value as the default is considered to be unreliable. The default is derived from$_SERVER["HTTPS"]
.)Main::set(RESPONSECODE, "200");
is the default HTTP response code set by the systemMain::set(ROOTURI, _getDefaultRootUri());
is the root URI the system is reachable at
(Important: You SHOULD configure this value as the default is considered to be unreliable. The default is derived from$_SERVER["SCRIPT_NAME"]
.)Main::set(THEMENAME, null);
is the name of the active themeMain::set(TIMEFORMAT, "c");
is the time format used for log entriesMain::set(TIMEZONE, "Europe/Berlin");
is the time zones used by the systemMain::set(URI, _getDefaultUri());
is the URI as taken from the URL
(The default is derived from$_SERVER["REQUEST_URI"]
.)
The following log levels SHALL be used:
Logging::NONE
- do not logLogging::DEBUG
- something might help when debuggingLogging::INFO
- something might be interestingLogging::WARN
- something shouldn't be doneLogging::ERROR
- something went wrong
The following values are set during runtime to provide status information to plugins and themes:
Main::set(CONTENT, null);
is the content provided to plugins and themesMain::set(METADATA, null);
are the metadata provided to plugins and themesMain::set(PAGE, 1);
is the page number that is displayed during paginationMain::set(PAGECOUNT, 1);
is the maximum number of pages available during pagination