[
'start' => '^(!{1,4})([^\n]+)\n*',
'callback' => 'StudipCoreFormat::markupHeading'
],
// horizontal rule
'hrule' => [
'start' => '^--(\d?)$',
'callback' => 'StudipCoreFormat::markupHorizontalRule'
],
// list and table
'list' => [
'start' => '(^[=-]+ [^\n]+\n?)+',
'callback' => 'StudipCoreFormat::markupList'
],
'table' => [
'start' => '(^\|[^\n]*\|[^\n]*\n?)+',
'callback' => 'StudipCoreFormat::markupTable'
],
// block indent
'indent' => [
'start' => '(^ [^\n]+\n?)+',
'callback' => 'StudipCoreFormat::markupIndent'
],
// basic text formatting
'bold' => [
'start' => '\*\*',
'end' => '\*\*',
'callback' => 'StudipCoreFormat::markupText'
],
'italics' => [
'start' => '%%',
'end' => '%%',
'callback' => 'StudipCoreFormat::markupText'
],
'underline' => [
'start' => '__',
'end' => '__',
'callback' => 'StudipCoreFormat::markupText'
],
'verb' => [
'start' => '##',
'end' => '##',
'callback' => 'StudipCoreFormat::markupText'
],
'big' => [
'start' => '(\+\+)+',
'end' => '(\+\+)+',
'callback' => 'StudipCoreFormat::markupTextSize'
],
'small' => [
'start' => '(--)+',
'end' => '(--)+',
'callback' => 'StudipCoreFormat::markupTextSize'
],
'super' => [
'start' => '>>',
'end' => '>>',
'callback' => 'StudipCoreFormat::markupText'
],
'sub' => [
'start' => '<<',
'end' => '<<',
'callback' => 'StudipCoreFormat::markupText'
],
'strike' => [
'start' => '\{-',
'end' => '-\}',
'callback' => 'StudipCoreFormat::markupText'
],
// basic text formatting (simple form)
'simple_bold' => [
'start' => '(?<=\s|^)\*(\S+)\*(?=\s|$)',
'callback' => 'StudipCoreFormat::markupTextSimple'
],
'simple_italics' => [
'start' => '(?<=\s|^)%(\S+)%(?=\s|$)',
'callback' => 'StudipCoreFormat::markupTextSimple'
],
'simple_underline' => [
'start' => '(?<=\s|^)_(\S+)_(?=\s|$)',
'callback' => 'StudipCoreFormat::markupTextSimple'
],
'simple_verb' => [
'start' => '(?<=\s|^)#(\S+)#(?=\s|$)',
'callback' => 'StudipCoreFormat::markupTextSimple'
],
'simple_big' => [
'start' => '(?<=\s|^)\+(\S+)\+(?=\s|$)',
'callback' => 'StudipCoreFormat::markupTextSimple'
],
'simple_small' => [
'start' => '(?<=\s|^)-(\S+)-(?=\s|$)',
'callback' => 'StudipCoreFormat::markupTextSimple'
],
'simple_super' => [
'start' => '(?<=\s|^)>(\S+)>(?=\s|$)',
'callback' => 'StudipCoreFormat::markupTextSimple'
],
'simple_sub' => [
'start' => '(?<=\s|^)<(\S+)<(?=\s|$)',
'callback' => 'StudipCoreFormat::markupTextSimple'
],
// preformatted text, quote, nop and code
'pre' => [
'start' => '\[pre\]',
'end' => '\[\/pre\]',
'callback' => 'StudipCoreFormat::markupPreformat'
],
'quote' => [
'start' => '\[quote(=.*?)?\]',
'end' => '\[\/quote\]\s*',
'callback' => 'StudipCoreFormat::markupQuote'
],
'nop' => [
'start' => '\[nop\](.*?)\[\/nop\]',
'callback' => 'StudipCoreFormat::markupNoFormat'
],
'code' => [
'start' => '\[code(=.*?)?\](.*?)\[\/code\]',
'callback' => 'StudipCoreFormat::markupCode'
],
'media' => [
'start' => '\[(img|audio|video)(.*?)\](.*?)(?=\s|$)',
'callback' => 'StudipCoreFormat::markupMedia'
],
'emails' => [
'start' => '(?xi:
# ensure block is preceded by whitspace, line start or closing
# tag
(?<=\s|^|\>)
# capture displayed text
(?:\[([^\n\f\]]+?)\])?
# capture actual email address
([\w.!#%+\-]+@([[:alnum:]\-.]+\.[[:alnum:]\-.]{2,}))
# ensure block is succeeded by white space or line end
(?=\s|$)
)',
'callback' => 'StudipCoreFormat::markupEmails'
],
'htmlAnchor' => [
# avoid replacing html links by studip's links-markup
# match ...
'start' => '(?xi: <\s* a (?:\s(?:\s* \w+ \s*=\s* "[^"]*" )*)? \s*>)',
'end' => '(?xi: <\s* \/\s* a \s*>)',
'callback' => 'StudipCoreFormat::htmlAnchor'
],
'htmlImg' => [
# avoid replacing img links by studip's links-markup
# match
'start' => '(?xi:
# match tag start:
\s*\/?\s*>
)',
'callback' => 'StudipCoreFormat::htmlImg'
],
'links' => [
// markup: [text]url
//
// To set URLs apart from normal text, scheme and host are
// obligatory. URLs are based on, but allow more than RFC3986 in
// order to simplify the regular expression.
//
// http://tools.ietf.org/html/rfc3986
'start' => '(?xi:
# capture 1: displayed text
(?:\[( [^\n\f\]]+ )\])?
# capture 2: URL
\b(
# scheme
[a-z][a-z0-9\+\-\.]*:\/\/
# user:password@host:port\/path?query#fragment
[a-zA-Z0-9\x{00a0}-\x{d7ff}\x{f900}-\x{fdcf}\x{fdf0}-\x{ffef}_\-\.~!$&\'\(\)\[\]*+,;=%:@\/?#]+
)
)',
'callback' => 'StudipCoreFormat::markupLinks'
],
'tex' => [
'start' => '\[tex\]',
'end' => '\[\/tex\]',
'callback' => 'StudipCoreFormat::markupTexFormat'
],
];
/**
* Returns the list of global Stud.IP markup rules as an array.
* Each entry has the following attributes: 'start', 'end' and
* 'callback'. The rule name is used as the entry's array key.
*
* @return array list of all markup rules
*/
public static function getStudipMarkups()
{
return self::$studip_rules;
}
/**
* Adds a new markup rule to the global Stud.IP markup set. This can
* also be used to replace an existing markup rule. The end regular
* expression is optional (i.e. may be NULL) to indicate that this
* rule has an empty content model. The callback is called whenever
* the rule matches and is passed the following arguments:
*
* - $markup the markup parser object
* - $matches match results of preg_match for $start
* - $contents (parsed) contents of this markup rule
*
* Sometimes you may want your rule to apply before another specific rule
* will apply. For this case the parameter $before defines a rulename of
* existing markup, before which your rule should apply.
*
* @param string $name name of this rule
* @param string $start start regular expression
* @param string $end end regular expression (optional)
* @param callback $callback function generating output of this rule
* @param string $before mark before which rule this rule should be appended
*/
public static function addStudipMarkup($name, $start, $end, $callback, $before = null)
{
$inserted = false;
foreach (self::$studip_rules as $rule_name => $rule) {
if ($rule_name === $before) {
self::$studip_rules[$name] = compact('start', 'end', 'callback');
$inserted = true;
}
if ($inserted) {
unset(self::$studip_rules[$rule_name]);
self::$studip_rules[$rule_name] = $rule;
}
}
if (!$inserted) {
self::$studip_rules[$name] = $end
? compact('start', 'end', 'callback')
: compact('start', 'callback');
}
}
/**
* Returns a single markup-rule if it exists.
* @return array: array('start' => "...", 'end' => "...", 'callback' => "...")
*/
public static function getStudipMarkup($name) {
return self::$studip_rules[$name];
}
/**
* Removes a markup rule from the global Stud.IP markup set.
*
* @param string $name name of the rule
*/
public static function removeStudipMarkup($name)
{
unset(self::$studip_rules[$name]);
}
/**
* Initializes a new StudipCoreFormat instance.
*/
public function __construct()
{
parent::__construct(self::getStudipMarkups());
}
/**
* Stud.IP markup for headings
*/
protected static function markupHeading($markup, $matches)
{
$level = max(1, 5 - mb_strlen($matches[1]));
$text = $markup->format($matches[2]);
return sprintf('
| '; $result .= $markup->format(trim($cell)); $result .= ' | '; } $result .= '
%s', trim($contents)); } /** * Stud.IP markup for quoted text */ protected static function markupQuote($markup, $matches, $contents) { if (isset($matches[1]) && mb_strlen($matches[1]) > 1) { $title = sprintf(_('%s hat geschrieben:'), $markup->format(mb_substr($matches[1], 1))); return sprintf('
%s', $title, trim($contents)); } else { return sprintf('
%s', trim($contents)); } } /** * Stud.IP markup for unformatted text */ protected static function markupNoFormat($markup, $matches) { return $markup->quote($matches[1]); } /** * Stud.IP markup for (PHP) source code */ protected static function markupCode($markup, $matches) { $codetype = ""; if (mb_strlen($matches[1])) { $codetype = " ".decodeHTML(trim(mb_substr($matches[1], 1)), ENT_QUOTES); } $code = decodeHTML(trim($matches[2]), ENT_QUOTES); return sprintf('
%2$s',
htmlReady($codetype),
htmlReady($code));
}
/**
* Stud.IP markup for email-adresses
*/
protected static function markupEmails($markup, $matches)
{
$link_text = $matches[1] ?: $matches[2];
$email = $matches[2];
$domain = $matches[3];
return sprintf('%2$s',
$email,
$link_text
);
}
/**
* Stud.IP markup for images, audio and video
*/
protected static function markupMedia($markup, $matches)
{
$tag = $matches[1];
$params = explode(":",$matches[2]);
$url = $matches[3];
$whitespace = $matches[4] ?? null;
$title = null;
$link = null;
$position = null;
$width = null;
$virtual_url = null;
foreach ($params as $key => $param) {
if ($param) {
if (is_numeric($param)) {
$width = $param;
} elseif(in_array($param, words("left center right"))) {
$position = $param;
} elseif($key === 0 && $param[0] === "=") {
$title = mb_substr($param, 1);
} elseif($key < count($params) - 1) {
$virtual_url = $param.":".$params[$key + 1];
if (isURL($virtual_url)) {
$link = $virtual_url;
}
}
}
}
$format_strings = [
'img' => '